mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-18 09:44:18 +08:00
069b747931
Since internal to v4l2 the ioctl prototype is the same regardless of it being called through .ioctl or .unlocked_ioctl, we need to convert it all to the long return type of unlocked_ioctl. Thanks to Jean-Francois Moine for posting an initial patch for this and thus bringing it to our attention. Cc: Jean-Francois Moine <moinejf@free.fr> Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
4044 lines
112 KiB
C
4044 lines
112 KiB
C
/*
|
|
* 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)
|
|
cpia_proc_root->owner = THIS_MODULE;
|
|
else
|
|
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;
|
|
}
|
|
}
|
|
|
|
framerate = cam->fps;
|
|
if(framerate > 30 || framerate < 1)
|
|
framerate = 1;
|
|
|
|
if(!cam->params.flickerControl.disabled) {
|
|
/* Flicker control on */
|
|
if((cam->exposure_status == EXPOSURE_VERY_DARK ||
|
|
cam->exposure_status == EXPOSURE_DARK) &&
|
|
cam->exposure_count >= DARK_TIME*framerate &&
|
|
cam->params.sensorFps.divisor < 3) {
|
|
|
|
/* dark for too long */
|
|
++cam->params.sensorFps.divisor;
|
|
cam->cmd_queue |= COMMAND_SETSENSORFPS;
|
|
|
|
cam->params.flickerControl.coarseJump =
|
|
flicker_jumps[cam->mainsFreq]
|
|
[cam->params.sensorFps.baserate]
|
|
[cam->params.sensorFps.divisor];
|
|
cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
|
|
|
|
new_exposure = cam->params.flickerControl.coarseJump-1;
|
|
while(new_exposure < old_exposure/2)
|
|
new_exposure += cam->params.flickerControl.coarseJump;
|
|
cam->params.exposure.coarseExpLo = new_exposure & 0xff;
|
|
cam->params.exposure.coarseExpHi = new_exposure >> 8;
|
|
cam->cmd_queue |= COMMAND_SETEXPOSURE;
|
|
cam->exposure_status = EXPOSURE_NORMAL;
|
|
LOG("Automatically decreasing sensor_fps\n");
|
|
|
|
} else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
|
|
cam->exposure_status == EXPOSURE_LIGHT) &&
|
|
cam->exposure_count >= LIGHT_TIME*framerate &&
|
|
cam->params.sensorFps.divisor > 0) {
|
|
|
|
/* light for too long */
|
|
int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
|
|
|
|
--cam->params.sensorFps.divisor;
|
|
cam->cmd_queue |= COMMAND_SETSENSORFPS;
|
|
|
|
cam->params.flickerControl.coarseJump =
|
|
flicker_jumps[cam->mainsFreq]
|
|
[cam->params.sensorFps.baserate]
|
|
[cam->params.sensorFps.divisor];
|
|
cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
|
|
|
|
new_exposure = cam->params.flickerControl.coarseJump-1;
|
|
while(new_exposure < 2*old_exposure &&
|
|
new_exposure+
|
|
cam->params.flickerControl.coarseJump < max_exp)
|
|
new_exposure += cam->params.flickerControl.coarseJump;
|
|
cam->params.exposure.coarseExpLo = new_exposure & 0xff;
|
|
cam->params.exposure.coarseExpHi = new_exposure >> 8;
|
|
cam->cmd_queue |= COMMAND_SETEXPOSURE;
|
|
cam->exposure_status = EXPOSURE_NORMAL;
|
|
LOG("Automatically increasing sensor_fps\n");
|
|
}
|
|
} else {
|
|
/* Flicker control off */
|
|
if((cam->exposure_status == EXPOSURE_VERY_DARK ||
|
|
cam->exposure_status == EXPOSURE_DARK) &&
|
|
cam->exposure_count >= DARK_TIME*framerate &&
|
|
cam->params.sensorFps.divisor < 3) {
|
|
|
|
/* dark for too long */
|
|
++cam->params.sensorFps.divisor;
|
|
cam->cmd_queue |= COMMAND_SETSENSORFPS;
|
|
|
|
if(cam->params.exposure.gain > 0) {
|
|
--cam->params.exposure.gain;
|
|
cam->cmd_queue |= COMMAND_SETEXPOSURE;
|
|
}
|
|
cam->exposure_status = EXPOSURE_NORMAL;
|
|
LOG("Automatically decreasing sensor_fps\n");
|
|
|
|
} else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
|
|
cam->exposure_status == EXPOSURE_LIGHT) &&
|
|
cam->exposure_count >= LIGHT_TIME*framerate &&
|
|
cam->params.sensorFps.divisor > 0) {
|
|
|
|
/* light for too long */
|
|
--cam->params.sensorFps.divisor;
|
|
cam->cmd_queue |= COMMAND_SETSENSORFPS;
|
|
|
|
if(cam->params.exposure.gain <
|
|
cam->params.exposure.gainMode-1) {
|
|
++cam->params.exposure.gain;
|
|
cam->cmd_queue |= COMMAND_SETEXPOSURE;
|
|
}
|
|
cam->exposure_status = EXPOSURE_NORMAL;
|
|
LOG("Automatically increasing sensor_fps\n");
|
|
}
|
|
}
|
|
mutex_unlock(&cam->param_lock);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* if flicker is switched off, this function switches it back on.It checks,
|
|
however, that conditions are suitable before restarting it.
|
|
This should only be called for firmware version 1.2.
|
|
|
|
It also adjust the colour balance when an exposure step is detected - as
|
|
long as flicker is running
|
|
*/
|
|
static void restart_flicker(struct cam_data *cam)
|
|
{
|
|
int cam_exposure, old_exp;
|
|
if(!FIRMWARE_VERSION(1,2))
|
|
return;
|
|
mutex_lock(&cam->param_lock);
|
|
if(cam->params.flickerControl.flickerMode == 0 ||
|
|
cam->raw_image[39] == 0) {
|
|
mutex_unlock(&cam->param_lock);
|
|
return;
|
|
}
|
|
cam_exposure = cam->raw_image[39]*2;
|
|
old_exp = cam->params.exposure.coarseExpLo +
|
|
cam->params.exposure.coarseExpHi*256;
|
|
/*
|
|
see how far away camera exposure is from a valid
|
|
flicker exposure value
|
|
*/
|
|
cam_exposure %= cam->params.flickerControl.coarseJump;
|
|
if(!cam->params.flickerControl.disabled &&
|
|
cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
|
|
/* Flicker control auto-disabled */
|
|
cam->params.flickerControl.disabled = 1;
|
|
}
|
|
|
|
if(cam->params.flickerControl.disabled &&
|
|
cam->params.flickerControl.flickerMode &&
|
|
old_exp > cam->params.flickerControl.coarseJump +
|
|
ROUND_UP_EXP_FOR_FLICKER) {
|
|
/* exposure is now high enough to switch
|
|
flicker control back on */
|
|
set_flicker(&cam->params, &cam->cmd_queue, 1);
|
|
if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
|
|
cam->params.exposure.expMode == 2)
|
|
cam->exposure_status = EXPOSURE_NORMAL;
|
|
|
|
}
|
|
mutex_unlock(&cam->param_lock);
|
|
}
|
|
#undef FIRMWARE_VERSION
|
|
|
|
static int clear_stall(struct cam_data *cam)
|
|
{
|
|
/* FIXME: Does this actually work? */
|
|
LOG("Clearing stall\n");
|
|
|
|
cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
|
|
do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
|
|
return cam->params.status.streamState != STREAM_PAUSED;
|
|
}
|
|
|
|
/* kernel thread function to read image from camera */
|
|
static int fetch_frame(void *data)
|
|
{
|
|
int image_size, retry;
|
|
struct cam_data *cam = (struct cam_data *)data;
|
|
unsigned long oldjif, rate, diff;
|
|
|
|
/* Allow up to two bad images in a row to be read and
|
|
* ignored before an error is reported */
|
|
for (retry = 0; retry < 3; ++retry) {
|
|
if (retry)
|
|
DBG("retry=%d\n", retry);
|
|
|
|
if (!cam->ops)
|
|
continue;
|
|
|
|
/* load first frame always uncompressed */
|
|
if (cam->first_frame &&
|
|
cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
|
|
do_command(cam, CPIA_COMMAND_SetCompression,
|
|
CPIA_COMPRESSION_NONE,
|
|
NO_DECIMATION, 0, 0);
|
|
/* Trial & error - Discarding a frame prevents the
|
|
first frame from having an error in the data. */
|
|
do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
|
|
}
|
|
|
|
/* init camera upload */
|
|
if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
|
|
cam->params.streamStartLine, 0, 0))
|
|
continue;
|
|
|
|
if (cam->ops->wait_for_stream_ready) {
|
|
/* loop until image ready */
|
|
int count = 0;
|
|
do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
|
|
while (cam->params.status.streamState != STREAM_READY) {
|
|
if(++count > READY_TIMEOUT)
|
|
break;
|
|
if(cam->params.status.streamState ==
|
|
STREAM_PAUSED) {
|
|
/* Bad news */
|
|
if(!clear_stall(cam))
|
|
return -EIO;
|
|
}
|
|
|
|
cond_resched();
|
|
|
|
/* sleep for 10 ms, hopefully ;) */
|
|
msleep_interruptible(10);
|
|
if (signal_pending(current))
|
|
return -EINTR;
|
|
|
|
do_command(cam, CPIA_COMMAND_GetCameraStatus,
|
|
0, 0, 0, 0);
|
|
}
|
|
if(cam->params.status.streamState != STREAM_READY) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
cond_resched();
|
|
|
|
/* grab image from camera */
|
|
oldjif = jiffies;
|
|
image_size = cam->ops->streamRead(cam->lowlevel_data,
|
|
cam->raw_image, 0);
|
|
if (image_size <= 0) {
|
|
DBG("streamRead failed: %d\n", image_size);
|
|
continue;
|
|
}
|
|
|
|
rate = image_size * HZ / 1024;
|
|
diff = jiffies-oldjif;
|
|
cam->transfer_rate = diff==0 ? rate : rate/diff;
|
|
/* diff==0 ? unlikely but possible */
|
|
|
|
/* Switch flicker control back on if it got turned off */
|
|
restart_flicker(cam);
|
|
|
|
/* If AEC is enabled, monitor the exposure and
|
|
adjust the sensor frame rate if needed */
|
|
if(cam->params.exposure.expMode == 2)
|
|
monitor_exposure(cam);
|
|
|
|
/* camera idle now so dispatch queued commands */
|
|
dispatch_commands(cam);
|
|
|
|
/* Update our knowledge of the camera state */
|
|
do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
|
|
do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
|
|
do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
|
|
|
|
/* decompress and convert image to by copying it from
|
|
* raw_image to decompressed_frame
|
|
*/
|
|
|
|
cond_resched();
|
|
|
|
cam->image_size = parse_picture(cam, image_size);
|
|
if (cam->image_size <= 0) {
|
|
DBG("parse_picture failed %d\n", cam->image_size);
|
|
if(cam->params.compression.mode !=
|
|
CPIA_COMPRESSION_NONE) {
|
|
/* Compression may not work right if we
|
|
had a bad frame, get the next one
|
|
uncompressed. */
|
|
cam->first_frame = 1;
|
|
do_command(cam, CPIA_COMMAND_SetGrabMode,
|
|
CPIA_GRAB_SINGLE, 0, 0, 0);
|
|
/* FIXME: Trial & error - need up to 70ms for
|
|
the grab mode change to complete ? */
|
|
msleep_interruptible(70);
|
|
if (signal_pending(current))
|
|
return -EINTR;
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (retry < 3) {
|
|
/* FIXME: this only works for double buffering */
|
|
if (cam->frame[cam->curframe].state == FRAME_READY) {
|
|
memcpy(cam->frame[cam->curframe].data,
|
|
cam->decompressed_frame.data,
|
|
cam->decompressed_frame.count);
|
|
cam->frame[cam->curframe].state = FRAME_DONE;
|
|
} else
|
|
cam->decompressed_frame.state = FRAME_DONE;
|
|
|
|
if (cam->first_frame) {
|
|
cam->first_frame = 0;
|
|
do_command(cam, CPIA_COMMAND_SetCompression,
|
|
cam->params.compression.mode,
|
|
cam->params.compression.decimation, 0, 0);
|
|
|
|
/* Switch from single-grab to continuous grab */
|
|
do_command(cam, CPIA_COMMAND_SetGrabMode,
|
|
CPIA_GRAB_CONTINUOUS, 0, 0, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
return -EIO;
|
|
}
|
|
|
|
static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
|
|
{
|
|
if (!cam->frame_buf) {
|
|
/* we do lazy allocation */
|
|
int err;
|
|
if ((err = allocate_frame_buf(cam)))
|
|
return err;
|
|
}
|
|
|
|
cam->curframe = vm->frame;
|
|
cam->frame[cam->curframe].state = FRAME_READY;
|
|
return fetch_frame(cam);
|
|
}
|
|
|
|
static int goto_high_power(struct cam_data *cam)
|
|
{
|
|
if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
|
|
return -EIO;
|
|
msleep_interruptible(40); /* windows driver does it too */
|
|
if(signal_pending(current))
|
|
return -EINTR;
|
|
if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
|
|
return -EIO;
|
|
if (cam->params.status.systemState == HI_POWER_STATE) {
|
|
DBG("camera now in HIGH power state\n");
|
|
return 0;
|
|
}
|
|
printstatus(cam);
|
|
return -EIO;
|
|
}
|
|
|
|
static int goto_low_power(struct cam_data *cam)
|
|
{
|
|
if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
|
|
return -1;
|
|
if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
|
|
return -1;
|
|
if (cam->params.status.systemState == LO_POWER_STATE) {
|
|
DBG("camera now in LOW power state\n");
|
|
return 0;
|
|
}
|
|
printstatus(cam);
|
|
return -1;
|
|
}
|
|
|
|
static void save_camera_state(struct cam_data *cam)
|
|
{
|
|
if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
|
|
do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
|
|
if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
|
|
do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
|
|
|
|
DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
|
|
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);
|
|
DBG("%d/%d/%d\n",
|
|
cam->params.colourBalance.redGain,
|
|
cam->params.colourBalance.greenGain,
|
|
cam->params.colourBalance.blueGain);
|
|
}
|
|
|
|
static int set_camera_state(struct cam_data *cam)
|
|
{
|
|
cam->cmd_queue = COMMAND_SETCOMPRESSION |
|
|
COMMAND_SETCOMPRESSIONTARGET |
|
|
COMMAND_SETCOLOURPARAMS |
|
|
COMMAND_SETFORMAT |
|
|
COMMAND_SETYUVTHRESH |
|
|
COMMAND_SETECPTIMING |
|
|
COMMAND_SETCOMPRESSIONPARAMS |
|
|
COMMAND_SETEXPOSURE |
|
|
COMMAND_SETCOLOURBALANCE |
|
|
COMMAND_SETSENSORFPS |
|
|
COMMAND_SETAPCOR |
|
|
COMMAND_SETFLICKERCTRL |
|
|
COMMAND_SETVLOFFSET;
|
|
|
|
do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
|
|
dispatch_commands(cam);
|
|
|
|
/* Wait 6 frames for the sensor to get all settings and
|
|
AEC/ACB to settle */
|
|
msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
|
|
(1 << cam->params.sensorFps.divisor) + 10);
|
|
|
|
if(signal_pending(current))
|
|
return -EINTR;
|
|
|
|
save_camera_state(cam);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void get_version_information(struct cam_data *cam)
|
|
{
|
|
/* GetCPIAVersion */
|
|
do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
|
|
|
|
/* GetPnPID */
|
|
do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
|
|
}
|
|
|
|
/* initialize camera */
|
|
static int reset_camera(struct cam_data *cam)
|
|
{
|
|
int err;
|
|
/* Start the camera in low power mode */
|
|
if (goto_low_power(cam)) {
|
|
if (cam->params.status.systemState != WARM_BOOT_STATE)
|
|
return -ENODEV;
|
|
|
|
/* FIXME: this is just dirty trial and error */
|
|
err = goto_high_power(cam);
|
|
if(err)
|
|
return err;
|
|
do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
|
|
if (goto_low_power(cam))
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* procedure described in developer's guide p3-28 */
|
|
|
|
/* Check the firmware version. */
|
|
cam->params.version.firmwareVersion = 0;
|
|
get_version_information(cam);
|
|
if (cam->params.version.firmwareVersion != 1)
|
|
return -ENODEV;
|
|
|
|
/* A bug in firmware 1-02 limits gainMode to 2 */
|
|
if(cam->params.version.firmwareRevision <= 2 &&
|
|
cam->params.exposure.gainMode > 2) {
|
|
cam->params.exposure.gainMode = 2;
|
|
}
|
|
|
|
/* set QX3 detected flag */
|
|
cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
|
|
cam->params.pnpID.product == 0x0001);
|
|
|
|
/* The fatal error checking should be done after
|
|
* the camera powers up (developer's guide p 3-38) */
|
|
|
|
/* Set streamState before transition to high power to avoid bug
|
|
* in firmware 1-02 */
|
|
do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
|
|
STREAM_NOT_READY, 0);
|
|
|
|
/* GotoHiPower */
|
|
err = goto_high_power(cam);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Check the camera status */
|
|
if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
|
|
return -EIO;
|
|
|
|
if (cam->params.status.fatalError) {
|
|
DBG("fatal_error: %#04x\n",
|
|
cam->params.status.fatalError);
|
|
DBG("vp_status: %#04x\n",
|
|
cam->params.status.vpStatus);
|
|
if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
|
|
/* Fatal error in camera */
|
|
return -EIO;
|
|
} else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
|
|
/* Firmware 1-02 may do this for parallel port cameras,
|
|
* just clear the flags (developer's guide p 3-38) */
|
|
do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
|
|
FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
|
|
}
|
|
}
|
|
|
|
/* Check the camera status again */
|
|
if (cam->params.status.fatalError) {
|
|
if (cam->params.status.fatalError)
|
|
return -EIO;
|
|
}
|
|
|
|
/* VPVersion can't be retrieved before the camera is in HiPower,
|
|
* so get it here instead of in get_version_information. */
|
|
do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
|
|
|
|
/* set camera to a known state */
|
|
return set_camera_state(cam);
|
|
}
|
|
|
|
static void put_cam(struct cpia_camera_ops* ops)
|
|
{
|
|
module_put(ops->owner);
|
|
}
|
|
|
|
/* ------------------------- V4L interface --------------------- */
|
|
static int cpia_open(struct file *file)
|
|
{
|
|
struct video_device *dev = video_devdata(file);
|
|
struct cam_data *cam = video_get_drvdata(dev);
|
|
int err;
|
|
|
|
if (!cam) {
|
|
DBG("Internal error, cam_data not found!\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (cam->open_count > 0) {
|
|
DBG("Camera already open\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (!try_module_get(cam->ops->owner))
|
|
return -ENODEV;
|
|
|
|
mutex_lock(&cam->busy_lock);
|
|
err = -ENOMEM;
|
|
if (!cam->raw_image) {
|
|
cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
|
|
if (!cam->raw_image)
|
|
goto oops;
|
|
}
|
|
|
|
if (!cam->decompressed_frame.data) {
|
|
cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
|
|
if (!cam->decompressed_frame.data)
|
|
goto oops;
|
|
}
|
|
|
|
/* open cpia */
|
|
err = -ENODEV;
|
|
if (cam->ops->open(cam->lowlevel_data))
|
|
goto oops;
|
|
|
|
/* reset the camera */
|
|
if ((err = reset_camera(cam)) != 0) {
|
|
cam->ops->close(cam->lowlevel_data);
|
|
goto oops;
|
|
}
|
|
|
|
err = -EINTR;
|
|
if(signal_pending(current))
|
|
goto oops;
|
|
|
|
/* Set ownership of /proc/cpia/videoX to current user */
|
|
if(cam->proc_entry)
|
|
cam->proc_entry->uid = current_uid();
|
|
|
|
/* set mark for loading first frame uncompressed */
|
|
cam->first_frame = 1;
|
|
|
|
/* init it to something */
|
|
cam->mmap_kludge = 0;
|
|
|
|
++cam->open_count;
|
|
file->private_data = dev;
|
|
mutex_unlock(&cam->busy_lock);
|
|
return 0;
|
|
|
|
oops:
|
|
if (cam->decompressed_frame.data) {
|
|
rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
|
|
cam->decompressed_frame.data = NULL;
|
|
}
|
|
if (cam->raw_image) {
|
|
rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
|
|
cam->raw_image = NULL;
|
|
}
|
|
mutex_unlock(&cam->busy_lock);
|
|
put_cam(cam->ops);
|
|
return err;
|
|
}
|
|
|
|
static int cpia_close(struct file *file)
|
|
{
|
|
struct video_device *dev = file->private_data;
|
|
struct cam_data *cam = video_get_drvdata(dev);
|
|
|
|
if (cam->ops) {
|
|
/* Return ownership of /proc/cpia/videoX to root */
|
|
if(cam->proc_entry)
|
|
cam->proc_entry->uid = 0;
|
|
|
|
/* save camera state for later open (developers guide ch 3.5.3) */
|
|
save_camera_state(cam);
|
|
|
|
/* GotoLoPower */
|
|
goto_low_power(cam);
|
|
|
|
/* Update the camera status */
|
|
do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
|
|
|
|
/* cleanup internal state stuff */
|
|
free_frames(cam->frame);
|
|
|
|
/* close cpia */
|
|
cam->ops->close(cam->lowlevel_data);
|
|
|
|
put_cam(cam->ops);
|
|
}
|
|
|
|
if (--cam->open_count == 0) {
|
|
/* clean up capture-buffers */
|
|
if (cam->raw_image) {
|
|
rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
|
|
cam->raw_image = NULL;
|
|
}
|
|
|
|
if (cam->decompressed_frame.data) {
|
|
rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
|
|
cam->decompressed_frame.data = NULL;
|
|
}
|
|
|
|
if (cam->frame_buf)
|
|
free_frame_buf(cam);
|
|
|
|
if (!cam->ops)
|
|
kfree(cam);
|
|
}
|
|
file->private_data = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t cpia_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
struct video_device *dev = file->private_data;
|
|
struct cam_data *cam = video_get_drvdata(dev);
|
|
int err;
|
|
|
|
/* make this _really_ smp and multithread-safe */
|
|
if (mutex_lock_interruptible(&cam->busy_lock))
|
|
return -EINTR;
|
|
|
|
if (!buf) {
|
|
DBG("buf NULL\n");
|
|
mutex_unlock(&cam->busy_lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!count) {
|
|
DBG("count 0\n");
|
|
mutex_unlock(&cam->busy_lock);
|
|
return 0;
|
|
}
|
|
|
|
if (!cam->ops) {
|
|
DBG("ops NULL\n");
|
|
mutex_unlock(&cam->busy_lock);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* upload frame */
|
|
cam->decompressed_frame.state = FRAME_READY;
|
|
cam->mmap_kludge=0;
|
|
if((err = fetch_frame(cam)) != 0) {
|
|
DBG("ERROR from fetch_frame: %d\n", err);
|
|
mutex_unlock(&cam->busy_lock);
|
|
return err;
|
|
}
|
|
cam->decompressed_frame.state = FRAME_UNUSED;
|
|
|
|
/* copy data to user space */
|
|
if (cam->decompressed_frame.count > count) {
|
|
DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
|
|
(unsigned long) count);
|
|
mutex_unlock(&cam->busy_lock);
|
|
return -EFAULT;
|
|
}
|
|
if (copy_to_user(buf, cam->decompressed_frame.data,
|
|
cam->decompressed_frame.count)) {
|
|
DBG("copy_to_user failed\n");
|
|
mutex_unlock(&cam->busy_lock);
|
|
return -EFAULT;
|
|
}
|
|
|
|
mutex_unlock(&cam->busy_lock);
|
|
return cam->decompressed_frame.count;
|
|
}
|
|
|
|
static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg)
|
|
{
|
|
struct video_device *dev = file->private_data;
|
|
struct cam_data *cam = video_get_drvdata(dev);
|
|
int retval = 0;
|
|
|
|
if (!cam || !cam->ops)
|
|
return -ENODEV;
|
|
|
|
/* make this _really_ smp-safe */
|
|
if (mutex_lock_interruptible(&cam->busy_lock))
|
|
return -EINTR;
|
|
|
|
/* DBG("cpia_ioctl: %u\n", cmd); */
|
|
|
|
switch (cmd) {
|
|
/* query capabilities */
|
|
case VIDIOCGCAP:
|
|
{
|
|
struct video_capability *b = arg;
|
|
|
|
DBG("VIDIOCGCAP\n");
|
|
strcpy(b->name, "CPiA Camera");
|
|
b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
|
|
b->channels = 1;
|
|
b->audios = 0;
|
|
b->maxwidth = 352; /* VIDEOSIZE_CIF */
|
|
b->maxheight = 288;
|
|
b->minwidth = 48; /* VIDEOSIZE_48_48 */
|
|
b->minheight = 48;
|
|
break;
|
|
}
|
|
|
|
/* get/set video source - we are a camera and nothing else */
|
|
case VIDIOCGCHAN:
|
|
{
|
|
struct video_channel *v = arg;
|
|
|
|
DBG("VIDIOCGCHAN\n");
|
|
if (v->channel != 0) {
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
v->channel = 0;
|
|
strcpy(v->name, "Camera");
|
|
v->tuners = 0;
|
|
v->flags = 0;
|
|
v->type = VIDEO_TYPE_CAMERA;
|
|
v->norm = 0;
|
|
break;
|
|
}
|
|
|
|
case VIDIOCSCHAN:
|
|
{
|
|
struct video_channel *v = arg;
|
|
|
|
DBG("VIDIOCSCHAN\n");
|
|
if (v->channel != 0)
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* image properties */
|
|
case VIDIOCGPICT:
|
|
{
|
|
struct video_picture *pic = arg;
|
|
DBG("VIDIOCGPICT\n");
|
|
*pic = cam->vp;
|
|
break;
|
|
}
|
|
|
|
case VIDIOCSPICT:
|
|
{
|
|
struct video_picture *vp = arg;
|
|
|
|
DBG("VIDIOCSPICT\n");
|
|
|
|
/* check validity */
|
|
DBG("palette: %d\n", vp->palette);
|
|
DBG("depth: %d\n", vp->depth);
|
|
if (!valid_mode(vp->palette, vp->depth)) {
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
mutex_lock(&cam->param_lock);
|
|
/* brightness, colour, contrast need no check 0-65535 */
|
|
cam->vp = *vp;
|
|
/* update cam->params.colourParams */
|
|
cam->params.colourParams.brightness = vp->brightness*100/65535;
|
|
cam->params.colourParams.contrast = vp->contrast*100/65535;
|
|
cam->params.colourParams.saturation = vp->colour*100/65535;
|
|
/* contrast is in steps of 8, so round */
|
|
cam->params.colourParams.contrast =
|
|
((cam->params.colourParams.contrast + 3) / 8) * 8;
|
|
if (cam->params.version.firmwareVersion == 1 &&
|
|
cam->params.version.firmwareRevision == 2 &&
|
|
cam->params.colourParams.contrast > 80) {
|
|
/* 1-02 firmware limits contrast to 80 */
|
|
cam->params.colourParams.contrast = 80;
|
|
}
|
|
|
|
/* Adjust flicker control if necessary */
|
|
if(cam->params.flickerControl.allowableOverExposure < 0)
|
|
cam->params.flickerControl.allowableOverExposure =
|
|
-find_over_exposure(cam->params.colourParams.brightness);
|
|
if(cam->params.flickerControl.flickerMode != 0)
|
|
cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
|
|
|
|
|
|
/* queue command to update camera */
|
|
cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
|
|
mutex_unlock(&cam->param_lock);
|
|
DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
|
|
vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
|
|
vp->contrast);
|
|
break;
|
|
}
|
|
|
|
/* get/set capture window */
|
|
case VIDIOCGWIN:
|
|
{
|
|
struct video_window *vw = arg;
|
|
DBG("VIDIOCGWIN\n");
|
|
|
|
*vw = cam->vw;
|
|
break;
|
|
}
|
|
|
|
case VIDIOCSWIN:
|
|
{
|
|
/* copy_from_user, check validity, copy to internal structure */
|
|
struct video_window *vw = arg;
|
|
DBG("VIDIOCSWIN\n");
|
|
|
|
if (vw->clipcount != 0) { /* clipping not supported */
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
if (vw->clips != NULL) { /* clipping not supported */
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* we set the video window to something smaller or equal to what
|
|
* is requested by the user???
|
|
*/
|
|
mutex_lock(&cam->param_lock);
|
|
if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
|
|
int video_size = match_videosize(vw->width, vw->height);
|
|
|
|
if (video_size < 0) {
|
|
retval = -EINVAL;
|
|
mutex_unlock(&cam->param_lock);
|
|
break;
|
|
}
|
|
cam->video_size = video_size;
|
|
|
|
/* video size is changing, reset the subcapture area */
|
|
memset(&cam->vc, 0, sizeof(cam->vc));
|
|
|
|
set_vw_size(cam);
|
|
DBG("%d / %d\n", cam->vw.width, cam->vw.height);
|
|
cam->cmd_queue |= COMMAND_SETFORMAT;
|
|
}
|
|
|
|
mutex_unlock(&cam->param_lock);
|
|
|
|
/* setformat ignored by camera during streaming,
|
|
* so stop/dispatch/start */
|
|
if (cam->cmd_queue & COMMAND_SETFORMAT) {
|
|
DBG("\n");
|
|
dispatch_commands(cam);
|
|
}
|
|
DBG("%d/%d:%d\n", cam->video_size,
|
|
cam->vw.width, cam->vw.height);
|
|
break;
|
|
}
|
|
|
|
/* mmap interface */
|
|
case VIDIOCGMBUF:
|
|
{
|
|
struct video_mbuf *vm = arg;
|
|
int i;
|
|
|
|
DBG("VIDIOCGMBUF\n");
|
|
memset(vm, 0, sizeof(*vm));
|
|
vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
|
|
vm->frames = FRAME_NUM;
|
|
for (i = 0; i < FRAME_NUM; i++)
|
|
vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
|
|
break;
|
|
}
|
|
|
|
case VIDIOCMCAPTURE:
|
|
{
|
|
struct video_mmap *vm = arg;
|
|
int video_size;
|
|
|
|
DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
|
|
vm->width, vm->height);
|
|
if (vm->frame<0||vm->frame>=FRAME_NUM) {
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* set video format */
|
|
cam->vp.palette = vm->format;
|
|
switch(vm->format) {
|
|
case VIDEO_PALETTE_GREY:
|
|
cam->vp.depth=8;
|
|
break;
|
|
case VIDEO_PALETTE_RGB555:
|
|
case VIDEO_PALETTE_RGB565:
|
|
case VIDEO_PALETTE_YUV422:
|
|
case VIDEO_PALETTE_YUYV:
|
|
case VIDEO_PALETTE_UYVY:
|
|
cam->vp.depth = 16;
|
|
break;
|
|
case VIDEO_PALETTE_RGB24:
|
|
cam->vp.depth = 24;
|
|
break;
|
|
case VIDEO_PALETTE_RGB32:
|
|
cam->vp.depth = 32;
|
|
break;
|
|
default:
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
if (retval)
|
|
break;
|
|
|
|
/* set video size */
|
|
video_size = match_videosize(vm->width, vm->height);
|
|
if (video_size < 0) {
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
if (video_size != cam->video_size) {
|
|
cam->video_size = video_size;
|
|
|
|
/* video size is changing, reset the subcapture area */
|
|
memset(&cam->vc, 0, sizeof(cam->vc));
|
|
|
|
set_vw_size(cam);
|
|
cam->cmd_queue |= COMMAND_SETFORMAT;
|
|
dispatch_commands(cam);
|
|
}
|
|
/* according to v4l-spec we must start streaming here */
|
|
cam->mmap_kludge = 1;
|
|
retval = capture_frame(cam, vm);
|
|
|
|
break;
|
|
}
|
|
|
|
case VIDIOCSYNC:
|
|
{
|
|
int *frame = arg;
|
|
|
|
//DBG("VIDIOCSYNC: %d\n", *frame);
|
|
|
|
if (*frame<0 || *frame >= FRAME_NUM) {
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
switch (cam->frame[*frame].state) {
|
|
case FRAME_UNUSED:
|
|
case FRAME_READY:
|
|
case FRAME_GRABBING:
|
|
DBG("sync to unused frame %d\n", *frame);
|
|
retval = -EINVAL;
|
|
break;
|
|
|
|
case FRAME_DONE:
|
|
cam->frame[*frame].state = FRAME_UNUSED;
|
|
//DBG("VIDIOCSYNC: %d synced\n", *frame);
|
|
break;
|
|
}
|
|
if (retval == -EINTR) {
|
|
/* FIXME - xawtv does not handle this nice */
|
|
retval = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case VIDIOCGCAPTURE:
|
|
{
|
|
struct video_capture *vc = arg;
|
|
|
|
DBG("VIDIOCGCAPTURE\n");
|
|
|
|
*vc = cam->vc;
|
|
|
|
break;
|
|
}
|
|
|
|
case VIDIOCSCAPTURE:
|
|
{
|
|
struct video_capture *vc = arg;
|
|
|
|
DBG("VIDIOCSCAPTURE\n");
|
|
|
|
if (vc->decimation != 0) { /* How should this be used? */
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
if (vc->flags != 0) { /* Even/odd grab not supported */
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* Clip to the resolution we can set for the ROI
|
|
(every 8 columns and 4 rows) */
|
|
vc->x = vc->x & ~(__u32)7;
|
|
vc->y = vc->y & ~(__u32)3;
|
|
vc->width = vc->width & ~(__u32)7;
|
|
vc->height = vc->height & ~(__u32)3;
|
|
|
|
if(vc->width == 0 || vc->height == 0 ||
|
|
vc->x + vc->width > cam->vw.width ||
|
|
vc->y + vc->height > cam->vw.height) {
|
|
retval = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
|
|
|
|
mutex_lock(&cam->param_lock);
|
|
|
|
cam->vc.x = vc->x;
|
|
cam->vc.y = vc->y;
|
|
cam->vc.width = vc->width;
|
|
cam->vc.height = vc->height;
|
|
|
|
set_vw_size(cam);
|
|
cam->cmd_queue |= COMMAND_SETFORMAT;
|
|
|
|
mutex_unlock(&cam->param_lock);
|
|
|
|
/* setformat ignored by camera during streaming,
|
|
* so stop/dispatch/start */
|
|
dispatch_commands(cam);
|
|
break;
|
|
}
|
|
|
|
case VIDIOCGUNIT:
|
|
{
|
|
struct video_unit *vu = arg;
|
|
|
|
DBG("VIDIOCGUNIT\n");
|
|
|
|
vu->video = cam->vdev.minor;
|
|
vu->vbi = VIDEO_NO_UNIT;
|
|
vu->radio = VIDEO_NO_UNIT;
|
|
vu->audio = VIDEO_NO_UNIT;
|
|
vu->teletext = VIDEO_NO_UNIT;
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
/* pointless to implement overlay with this camera */
|
|
case VIDIOCCAPTURE:
|
|
case VIDIOCGFBUF:
|
|
case VIDIOCSFBUF:
|
|
case VIDIOCKEY:
|
|
/* tuner interface - we have none */
|
|
case VIDIOCGTUNER:
|
|
case VIDIOCSTUNER:
|
|
case VIDIOCGFREQ:
|
|
case VIDIOCSFREQ:
|
|
/* audio interface - we have none */
|
|
case VIDIOCGAUDIO:
|
|
case VIDIOCSAUDIO:
|
|
retval = -EINVAL;
|
|
break;
|
|
default:
|
|
retval = -ENOIOCTLCMD;
|
|
break;
|
|
}
|
|
|
|
mutex_unlock(&cam->busy_lock);
|
|
return retval;
|
|
}
|
|
|
|
static long cpia_ioctl(struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
return video_usercopy(file, cmd, arg, cpia_do_ioctl);
|
|
}
|
|
|
|
|
|
/* FIXME */
|
|
static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
|
|
{
|
|
struct video_device *dev = file->private_data;
|
|
unsigned long start = vma->vm_start;
|
|
unsigned long size = vma->vm_end - vma->vm_start;
|
|
unsigned long page, pos;
|
|
struct cam_data *cam = video_get_drvdata(dev);
|
|
int retval;
|
|
|
|
if (!cam || !cam->ops)
|
|
return -ENODEV;
|
|
|
|
DBG("cpia_mmap: %ld\n", size);
|
|
|
|
if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
|
|
return -EINVAL;
|
|
|
|
if (!cam || !cam->ops)
|
|
return -ENODEV;
|
|
|
|
/* make this _really_ smp-safe */
|
|
if (mutex_lock_interruptible(&cam->busy_lock))
|
|
return -EINTR;
|
|
|
|
if (!cam->frame_buf) { /* we do lazy allocation */
|
|
if ((retval = allocate_frame_buf(cam))) {
|
|
mutex_unlock(&cam->busy_lock);
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
pos = (unsigned long)(cam->frame_buf);
|
|
while (size > 0) {
|
|
page = vmalloc_to_pfn((void *)pos);
|
|
if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
|
|
mutex_unlock(&cam->busy_lock);
|
|
return -EAGAIN;
|
|
}
|
|
start += PAGE_SIZE;
|
|
pos += PAGE_SIZE;
|
|
if (size > PAGE_SIZE)
|
|
size -= PAGE_SIZE;
|
|
else
|
|
size = 0;
|
|
}
|
|
|
|
DBG("cpia_mmap: %ld\n", size);
|
|
mutex_unlock(&cam->busy_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct v4l2_file_operations cpia_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = cpia_open,
|
|
.release = cpia_close,
|
|
.read = cpia_read,
|
|
.mmap = cpia_mmap,
|
|
.ioctl = cpia_ioctl,
|
|
};
|
|
|
|
static struct video_device cpia_template = {
|
|
.name = "CPiA Camera",
|
|
.fops = &cpia_fops,
|
|
.release = video_device_release_empty,
|
|
};
|
|
|
|
/* initialise cam_data structure */
|
|
static void reset_camera_struct(struct cam_data *cam)
|
|
{
|
|
/* The following parameter values are the defaults from
|
|
* "Software Developer's Guide for CPiA Cameras". Any changes
|
|
* to the defaults are noted in comments. */
|
|
cam->params.colourParams.brightness = 50;
|
|
cam->params.colourParams.contrast = 48;
|
|
cam->params.colourParams.saturation = 50;
|
|
cam->params.exposure.gainMode = 4;
|
|
cam->params.exposure.expMode = 2; /* AEC */
|
|
cam->params.exposure.compMode = 1;
|
|
cam->params.exposure.centreWeight = 1;
|
|
cam->params.exposure.gain = 0;
|
|
cam->params.exposure.fineExp = 0;
|
|
cam->params.exposure.coarseExpLo = 185;
|
|
cam->params.exposure.coarseExpHi = 0;
|
|
cam->params.exposure.redComp = COMP_RED;
|
|
cam->params.exposure.green1Comp = COMP_GREEN1;
|
|
cam->params.exposure.green2Comp = COMP_GREEN2;
|
|
cam->params.exposure.blueComp = COMP_BLUE;
|
|
cam->params.colourBalance.balanceMode = 2; /* ACB */
|
|
cam->params.colourBalance.redGain = 32;
|
|
cam->params.colourBalance.greenGain = 6;
|
|
cam->params.colourBalance.blueGain = 92;
|
|
cam->params.apcor.gain1 = 0x18;
|
|
cam->params.apcor.gain2 = 0x16;
|
|
cam->params.apcor.gain4 = 0x24;
|
|
cam->params.apcor.gain8 = 0x34;
|
|
cam->params.flickerControl.flickerMode = 0;
|
|
cam->params.flickerControl.disabled = 1;
|
|
|
|
cam->params.flickerControl.coarseJump =
|
|
flicker_jumps[cam->mainsFreq]
|
|
[cam->params.sensorFps.baserate]
|
|
[cam->params.sensorFps.divisor];
|
|
cam->params.flickerControl.allowableOverExposure =
|
|
-find_over_exposure(cam->params.colourParams.brightness);
|
|
cam->params.vlOffset.gain1 = 20;
|
|
cam->params.vlOffset.gain2 = 24;
|
|
cam->params.vlOffset.gain4 = 26;
|
|
cam->params.vlOffset.gain8 = 26;
|
|
cam->params.compressionParams.hysteresis = 3;
|
|
cam->params.compressionParams.threshMax = 11;
|
|
cam->params.compressionParams.smallStep = 1;
|
|
cam->params.compressionParams.largeStep = 3;
|
|
cam->params.compressionParams.decimationHysteresis = 2;
|
|
cam->params.compressionParams.frDiffStepThresh = 5;
|
|
cam->params.compressionParams.qDiffStepThresh = 3;
|
|
cam->params.compressionParams.decimationThreshMod = 2;
|
|
/* End of default values from Software Developer's Guide */
|
|
|
|
cam->transfer_rate = 0;
|
|
cam->exposure_status = EXPOSURE_NORMAL;
|
|
|
|
/* Set Sensor FPS to 15fps. This seems better than 30fps
|
|
* for indoor lighting. */
|
|
cam->params.sensorFps.divisor = 1;
|
|
cam->params.sensorFps.baserate = 1;
|
|
|
|
cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
|
|
cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
|
|
|
|
cam->params.format.subSample = SUBSAMPLE_422;
|
|
cam->params.format.yuvOrder = YUVORDER_YUYV;
|
|
|
|
cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
|
|
cam->params.compressionTarget.frTargeting =
|
|
CPIA_COMPRESSION_TARGET_QUALITY;
|
|
cam->params.compressionTarget.targetFR = 15; /* From windows driver */
|
|
cam->params.compressionTarget.targetQ = 5; /* From windows driver */
|
|
|
|
cam->params.qx3.qx3_detected = 0;
|
|
cam->params.qx3.toplight = 0;
|
|
cam->params.qx3.bottomlight = 0;
|
|
cam->params.qx3.button = 0;
|
|
cam->params.qx3.cradled = 0;
|
|
|
|
cam->video_size = VIDEOSIZE_CIF;
|
|
|
|
cam->vp.colour = 32768; /* 50% */
|
|
cam->vp.hue = 32768; /* 50% */
|
|
cam->vp.brightness = 32768; /* 50% */
|
|
cam->vp.contrast = 32768; /* 50% */
|
|
cam->vp.whiteness = 0; /* not used -> grayscale only */
|
|
cam->vp.depth = 24; /* to be set by user */
|
|
cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
|
|
|
|
cam->vc.x = 0;
|
|
cam->vc.y = 0;
|
|
cam->vc.width = 0;
|
|
cam->vc.height = 0;
|
|
|
|
cam->vw.x = 0;
|
|
cam->vw.y = 0;
|
|
set_vw_size(cam);
|
|
cam->vw.chromakey = 0;
|
|
cam->vw.flags = 0;
|
|
cam->vw.clipcount = 0;
|
|
cam->vw.clips = NULL;
|
|
|
|
cam->cmd_queue = COMMAND_NONE;
|
|
cam->first_frame = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
/* initialize cam_data structure */
|
|
static void init_camera_struct(struct cam_data *cam,
|
|
struct cpia_camera_ops *ops )
|
|
{
|
|
int i;
|
|
|
|
/* Default everything to 0 */
|
|
memset(cam, 0, sizeof(struct cam_data));
|
|
|
|
cam->ops = ops;
|
|
mutex_init(&cam->param_lock);
|
|
mutex_init(&cam->busy_lock);
|
|
|
|
reset_camera_struct(cam);
|
|
|
|
cam->proc_entry = NULL;
|
|
|
|
memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
|
|
video_set_drvdata(&cam->vdev, cam);
|
|
|
|
cam->curframe = 0;
|
|
for (i = 0; i < FRAME_NUM; i++) {
|
|
cam->frame[i].width = 0;
|
|
cam->frame[i].height = 0;
|
|
cam->frame[i].state = FRAME_UNUSED;
|
|
cam->frame[i].data = NULL;
|
|
}
|
|
cam->decompressed_frame.width = 0;
|
|
cam->decompressed_frame.height = 0;
|
|
cam->decompressed_frame.state = FRAME_UNUSED;
|
|
cam->decompressed_frame.data = NULL;
|
|
}
|
|
|
|
struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
|
|
{
|
|
struct cam_data *camera;
|
|
|
|
if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
|
|
return NULL;
|
|
|
|
|
|
init_camera_struct( camera, ops );
|
|
camera->lowlevel_data = lowlevel;
|
|
|
|
/* register v4l device */
|
|
if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
|
|
kfree(camera);
|
|
printk(KERN_DEBUG "video_register_device failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* get version information from camera: open/reset/close */
|
|
|
|
/* open cpia */
|
|
if (camera->ops->open(camera->lowlevel_data))
|
|
return camera;
|
|
|
|
/* reset the camera */
|
|
if (reset_camera(camera) != 0) {
|
|
camera->ops->close(camera->lowlevel_data);
|
|
return camera;
|
|
}
|
|
|
|
/* close cpia */
|
|
camera->ops->close(camera->lowlevel_data);
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
create_proc_cpia_cam(camera);
|
|
#endif
|
|
|
|
printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
|
|
camera->params.version.firmwareVersion,
|
|
camera->params.version.firmwareRevision,
|
|
camera->params.version.vcVersion,
|
|
camera->params.version.vcRevision);
|
|
printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
|
|
camera->params.pnpID.vendor,
|
|
camera->params.pnpID.product,
|
|
camera->params.pnpID.deviceRevision);
|
|
printk(KERN_INFO " VP-Version: %d.%d %04x\n",
|
|
camera->params.vpVersion.vpVersion,
|
|
camera->params.vpVersion.vpRevision,
|
|
camera->params.vpVersion.cameraHeadID);
|
|
|
|
return camera;
|
|
}
|
|
|
|
void cpia_unregister_camera(struct cam_data *cam)
|
|
{
|
|
DBG("unregistering video\n");
|
|
video_unregister_device(&cam->vdev);
|
|
if (cam->open_count) {
|
|
put_cam(cam->ops);
|
|
DBG("camera open -- setting ops to NULL\n");
|
|
cam->ops = NULL;
|
|
}
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
DBG("destroying /proc/cpia/video%d\n", cam->vdev.num);
|
|
destroy_proc_cpia_cam(cam);
|
|
#endif
|
|
if (!cam->open_count) {
|
|
DBG("freeing camera\n");
|
|
kfree(cam);
|
|
}
|
|
}
|
|
|
|
static int __init cpia_init(void)
|
|
{
|
|
printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
|
|
CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
|
|
|
|
printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
|
|
"allowed, it is disabled by default now. Users should fix the "
|
|
"applications in case they don't work without conversion "
|
|
"reenabled by setting the 'colorspace_conv' module "
|
|
"parameter to 1\n");
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
proc_cpia_create();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit cpia_exit(void)
|
|
{
|
|
#ifdef CONFIG_PROC_FS
|
|
proc_cpia_destroy();
|
|
#endif
|
|
}
|
|
|
|
module_init(cpia_init);
|
|
module_exit(cpia_exit);
|
|
|
|
/* Exported symbols for modules. */
|
|
|
|
EXPORT_SYMBOL(cpia_register_camera);
|
|
EXPORT_SYMBOL(cpia_unregister_camera);
|