xref: /linux/drivers/media/usb/gspca/cpia1.c (revision 8c96f0a207bedb6f06089fde9adc7abe8136a087)
154e8bc5dSHans de Goede /*
254e8bc5dSHans de Goede  * cpia CPiA (1) gspca driver
354e8bc5dSHans de Goede  *
476fafe78SHans de Goede  * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>
554e8bc5dSHans de Goede  *
654e8bc5dSHans de Goede  * This module is adapted from the in kernel v4l1 cpia driver which is :
754e8bc5dSHans de Goede  *
854e8bc5dSHans de Goede  * (C) Copyright 1999-2000 Peter Pregler
954e8bc5dSHans de Goede  * (C) Copyright 1999-2000 Scott J. Bertin
1054e8bc5dSHans de Goede  * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
1154e8bc5dSHans de Goede  * (C) Copyright 2000 STMicroelectronics
1254e8bc5dSHans de Goede  *
1354e8bc5dSHans de Goede  * This program is free software; you can redistribute it and/or modify
1454e8bc5dSHans de Goede  * it under the terms of the GNU General Public License as published by
1554e8bc5dSHans de Goede  * the Free Software Foundation; either version 2 of the License, or
1654e8bc5dSHans de Goede  * (at your option) any later version.
1754e8bc5dSHans de Goede  *
1854e8bc5dSHans de Goede  * This program is distributed in the hope that it will be useful,
1954e8bc5dSHans de Goede  * but WITHOUT ANY WARRANTY; without even the implied warranty of
2054e8bc5dSHans de Goede  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2154e8bc5dSHans de Goede  * GNU General Public License for more details.
2254e8bc5dSHans de Goede  *
2354e8bc5dSHans de Goede  * You should have received a copy of the GNU General Public License
2454e8bc5dSHans de Goede  * along with this program; if not, write to the Free Software
2554e8bc5dSHans de Goede  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
2654e8bc5dSHans de Goede  *
2754e8bc5dSHans de Goede  */
2854e8bc5dSHans de Goede 
29133a9fe9SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
30133a9fe9SJoe Perches 
3154e8bc5dSHans de Goede #define MODULE_NAME "cpia1"
3254e8bc5dSHans de Goede 
33c2f644aeSHans de Goede #include <linux/input.h>
3454e8bc5dSHans de Goede #include "gspca.h"
3554e8bc5dSHans de Goede 
361fddcf0eSHans de Goede MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
3754e8bc5dSHans de Goede MODULE_DESCRIPTION("Vision CPiA");
3854e8bc5dSHans de Goede MODULE_LICENSE("GPL");
3954e8bc5dSHans de Goede 
4054e8bc5dSHans de Goede /* constant value's */
4154e8bc5dSHans de Goede #define MAGIC_0		0x19
4254e8bc5dSHans de Goede #define MAGIC_1		0x68
431d00d6c1SJean-François Moine #define DATA_IN		0xc0
4454e8bc5dSHans de Goede #define DATA_OUT	0x40
4554e8bc5dSHans de Goede #define VIDEOSIZE_QCIF	0	/* 176x144 */
4654e8bc5dSHans de Goede #define VIDEOSIZE_CIF	1	/* 352x288 */
4754e8bc5dSHans de Goede #define SUBSAMPLE_420	0
4854e8bc5dSHans de Goede #define SUBSAMPLE_422	1
4954e8bc5dSHans de Goede #define YUVORDER_YUYV	0
5054e8bc5dSHans de Goede #define YUVORDER_UYVY	1
5154e8bc5dSHans de Goede #define NOT_COMPRESSED	0
5254e8bc5dSHans de Goede #define COMPRESSED	1
5354e8bc5dSHans de Goede #define NO_DECIMATION	0
5454e8bc5dSHans de Goede #define DECIMATION_ENAB	1
5554e8bc5dSHans de Goede #define EOI		0xff	/* End Of Image */
5654e8bc5dSHans de Goede #define EOL		0xfd	/* End Of Line */
5754e8bc5dSHans de Goede #define FRAME_HEADER_SIZE	64
5854e8bc5dSHans de Goede 
5954e8bc5dSHans de Goede /* Image grab modes */
6054e8bc5dSHans de Goede #define CPIA_GRAB_SINGLE	0
6154e8bc5dSHans de Goede #define CPIA_GRAB_CONTINEOUS	1
6254e8bc5dSHans de Goede 
6354e8bc5dSHans de Goede /* Compression parameters */
6454e8bc5dSHans de Goede #define CPIA_COMPRESSION_NONE	0
6554e8bc5dSHans de Goede #define CPIA_COMPRESSION_AUTO	1
6654e8bc5dSHans de Goede #define CPIA_COMPRESSION_MANUAL	2
6754e8bc5dSHans de Goede #define CPIA_COMPRESSION_TARGET_QUALITY         0
6854e8bc5dSHans de Goede #define CPIA_COMPRESSION_TARGET_FRAMERATE       1
6954e8bc5dSHans de Goede 
7054e8bc5dSHans de Goede /* Return offsets for GetCameraState */
7154e8bc5dSHans de Goede #define SYSTEMSTATE	0
7254e8bc5dSHans de Goede #define GRABSTATE	1
7354e8bc5dSHans de Goede #define STREAMSTATE	2
7454e8bc5dSHans de Goede #define FATALERROR	3
7554e8bc5dSHans de Goede #define CMDERROR	4
7654e8bc5dSHans de Goede #define DEBUGFLAGS	5
7754e8bc5dSHans de Goede #define VPSTATUS	6
7854e8bc5dSHans de Goede #define ERRORCODE	7
7954e8bc5dSHans de Goede 
8054e8bc5dSHans de Goede /* SystemState */
8154e8bc5dSHans de Goede #define UNINITIALISED_STATE	0
8254e8bc5dSHans de Goede #define PASS_THROUGH_STATE	1
8354e8bc5dSHans de Goede #define LO_POWER_STATE		2
8454e8bc5dSHans de Goede #define HI_POWER_STATE		3
8554e8bc5dSHans de Goede #define WARM_BOOT_STATE		4
8654e8bc5dSHans de Goede 
8754e8bc5dSHans de Goede /* GrabState */
8854e8bc5dSHans de Goede #define GRAB_IDLE		0
8954e8bc5dSHans de Goede #define GRAB_ACTIVE		1
9054e8bc5dSHans de Goede #define GRAB_DONE		2
9154e8bc5dSHans de Goede 
9254e8bc5dSHans de Goede /* StreamState */
9354e8bc5dSHans de Goede #define STREAM_NOT_READY	0
9454e8bc5dSHans de Goede #define STREAM_READY		1
9554e8bc5dSHans de Goede #define STREAM_OPEN		2
9654e8bc5dSHans de Goede #define STREAM_PAUSED		3
9754e8bc5dSHans de Goede #define STREAM_FINISHED		4
9854e8bc5dSHans de Goede 
9954e8bc5dSHans de Goede /* Fatal Error, CmdError, and DebugFlags */
10054e8bc5dSHans de Goede #define CPIA_FLAG	  1
10154e8bc5dSHans de Goede #define SYSTEM_FLAG	  2
10254e8bc5dSHans de Goede #define INT_CTRL_FLAG	  4
10354e8bc5dSHans de Goede #define PROCESS_FLAG	  8
10454e8bc5dSHans de Goede #define COM_FLAG	 16
10554e8bc5dSHans de Goede #define VP_CTRL_FLAG	 32
10654e8bc5dSHans de Goede #define CAPTURE_FLAG	 64
10754e8bc5dSHans de Goede #define DEBUG_FLAG	128
10854e8bc5dSHans de Goede 
10954e8bc5dSHans de Goede /* VPStatus */
11054e8bc5dSHans de Goede #define VP_STATE_OK			0x00
11154e8bc5dSHans de Goede 
11254e8bc5dSHans de Goede #define VP_STATE_FAILED_VIDEOINIT	0x01
11354e8bc5dSHans de Goede #define VP_STATE_FAILED_AECACBINIT	0x02
11454e8bc5dSHans de Goede #define VP_STATE_AEC_MAX		0x04
11554e8bc5dSHans de Goede #define VP_STATE_ACB_BMAX		0x08
11654e8bc5dSHans de Goede 
11754e8bc5dSHans de Goede #define VP_STATE_ACB_RMIN		0x10
11854e8bc5dSHans de Goede #define VP_STATE_ACB_GMIN		0x20
11954e8bc5dSHans de Goede #define VP_STATE_ACB_RMAX		0x40
12054e8bc5dSHans de Goede #define VP_STATE_ACB_GMAX		0x80
12154e8bc5dSHans de Goede 
12254e8bc5dSHans de Goede /* default (minimum) compensation values */
12354e8bc5dSHans de Goede #define COMP_RED        220
12454e8bc5dSHans de Goede #define COMP_GREEN1     214
12554e8bc5dSHans de Goede #define COMP_GREEN2     COMP_GREEN1
12654e8bc5dSHans de Goede #define COMP_BLUE       230
12754e8bc5dSHans de Goede 
12854e8bc5dSHans de Goede /* exposure status */
12954e8bc5dSHans de Goede #define EXPOSURE_VERY_LIGHT 0
13054e8bc5dSHans de Goede #define EXPOSURE_LIGHT      1
13154e8bc5dSHans de Goede #define EXPOSURE_NORMAL     2
13254e8bc5dSHans de Goede #define EXPOSURE_DARK       3
13354e8bc5dSHans de Goede #define EXPOSURE_VERY_DARK  4
13454e8bc5dSHans de Goede 
13554e8bc5dSHans de Goede #define CPIA_MODULE_CPIA			(0 << 5)
13654e8bc5dSHans de Goede #define CPIA_MODULE_SYSTEM			(1 << 5)
13754e8bc5dSHans de Goede #define CPIA_MODULE_VP_CTRL			(5 << 5)
13854e8bc5dSHans de Goede #define CPIA_MODULE_CAPTURE			(6 << 5)
13954e8bc5dSHans de Goede #define CPIA_MODULE_DEBUG			(7 << 5)
14054e8bc5dSHans de Goede 
14154e8bc5dSHans de Goede #define INPUT (DATA_IN << 8)
14254e8bc5dSHans de Goede #define OUTPUT (DATA_OUT << 8)
14354e8bc5dSHans de Goede 
14454e8bc5dSHans de Goede #define CPIA_COMMAND_GetCPIAVersion	(INPUT | CPIA_MODULE_CPIA | 1)
14554e8bc5dSHans de Goede #define CPIA_COMMAND_GetPnPID		(INPUT | CPIA_MODULE_CPIA | 2)
14654e8bc5dSHans de Goede #define CPIA_COMMAND_GetCameraStatus	(INPUT | CPIA_MODULE_CPIA | 3)
14754e8bc5dSHans de Goede #define CPIA_COMMAND_GotoHiPower	(OUTPUT | CPIA_MODULE_CPIA | 4)
14854e8bc5dSHans de Goede #define CPIA_COMMAND_GotoLoPower	(OUTPUT | CPIA_MODULE_CPIA | 5)
14954e8bc5dSHans de Goede #define CPIA_COMMAND_GotoSuspend	(OUTPUT | CPIA_MODULE_CPIA | 7)
15054e8bc5dSHans de Goede #define CPIA_COMMAND_GotoPassThrough	(OUTPUT | CPIA_MODULE_CPIA | 8)
15154e8bc5dSHans de Goede #define CPIA_COMMAND_ModifyCameraStatus	(OUTPUT | CPIA_MODULE_CPIA | 10)
15254e8bc5dSHans de Goede 
15354e8bc5dSHans de Goede #define CPIA_COMMAND_ReadVCRegs		(INPUT | CPIA_MODULE_SYSTEM | 1)
15454e8bc5dSHans de Goede #define CPIA_COMMAND_WriteVCReg		(OUTPUT | CPIA_MODULE_SYSTEM | 2)
15554e8bc5dSHans de Goede #define CPIA_COMMAND_ReadMCPorts	(INPUT | CPIA_MODULE_SYSTEM | 3)
15654e8bc5dSHans de Goede #define CPIA_COMMAND_WriteMCPort	(OUTPUT | CPIA_MODULE_SYSTEM | 4)
15754e8bc5dSHans de Goede #define CPIA_COMMAND_SetBaudRate	(OUTPUT | CPIA_MODULE_SYSTEM | 5)
15854e8bc5dSHans de Goede #define CPIA_COMMAND_SetECPTiming	(OUTPUT | CPIA_MODULE_SYSTEM | 6)
15954e8bc5dSHans de Goede #define CPIA_COMMAND_ReadIDATA		(INPUT | CPIA_MODULE_SYSTEM | 7)
16054e8bc5dSHans de Goede #define CPIA_COMMAND_WriteIDATA		(OUTPUT | CPIA_MODULE_SYSTEM | 8)
16154e8bc5dSHans de Goede #define CPIA_COMMAND_GenericCall	(OUTPUT | CPIA_MODULE_SYSTEM | 9)
16254e8bc5dSHans de Goede #define CPIA_COMMAND_I2CStart		(OUTPUT | CPIA_MODULE_SYSTEM | 10)
16354e8bc5dSHans de Goede #define CPIA_COMMAND_I2CStop		(OUTPUT | CPIA_MODULE_SYSTEM | 11)
16454e8bc5dSHans de Goede #define CPIA_COMMAND_I2CWrite		(OUTPUT | CPIA_MODULE_SYSTEM | 12)
16554e8bc5dSHans de Goede #define CPIA_COMMAND_I2CRead		(INPUT | CPIA_MODULE_SYSTEM | 13)
16654e8bc5dSHans de Goede 
16754e8bc5dSHans de Goede #define CPIA_COMMAND_GetVPVersion	(INPUT | CPIA_MODULE_VP_CTRL | 1)
16854e8bc5dSHans de Goede #define CPIA_COMMAND_ResetFrameCounter	(INPUT | CPIA_MODULE_VP_CTRL | 2)
16954e8bc5dSHans de Goede #define CPIA_COMMAND_SetColourParams	(OUTPUT | CPIA_MODULE_VP_CTRL | 3)
17054e8bc5dSHans de Goede #define CPIA_COMMAND_SetExposure	(OUTPUT | CPIA_MODULE_VP_CTRL | 4)
17154e8bc5dSHans de Goede #define CPIA_COMMAND_SetColourBalance	(OUTPUT | CPIA_MODULE_VP_CTRL | 6)
17254e8bc5dSHans de Goede #define CPIA_COMMAND_SetSensorFPS	(OUTPUT | CPIA_MODULE_VP_CTRL | 7)
17354e8bc5dSHans de Goede #define CPIA_COMMAND_SetVPDefaults	(OUTPUT | CPIA_MODULE_VP_CTRL | 8)
17454e8bc5dSHans de Goede #define CPIA_COMMAND_SetApcor		(OUTPUT | CPIA_MODULE_VP_CTRL | 9)
17554e8bc5dSHans de Goede #define CPIA_COMMAND_SetFlickerCtrl	(OUTPUT | CPIA_MODULE_VP_CTRL | 10)
17654e8bc5dSHans de Goede #define CPIA_COMMAND_SetVLOffset	(OUTPUT | CPIA_MODULE_VP_CTRL | 11)
17754e8bc5dSHans de Goede #define CPIA_COMMAND_GetColourParams	(INPUT | CPIA_MODULE_VP_CTRL | 16)
17854e8bc5dSHans de Goede #define CPIA_COMMAND_GetColourBalance	(INPUT | CPIA_MODULE_VP_CTRL | 17)
17954e8bc5dSHans de Goede #define CPIA_COMMAND_GetExposure	(INPUT | CPIA_MODULE_VP_CTRL | 18)
18054e8bc5dSHans de Goede #define CPIA_COMMAND_SetSensorMatrix	(OUTPUT | CPIA_MODULE_VP_CTRL | 19)
18154e8bc5dSHans de Goede #define CPIA_COMMAND_ColourBars		(OUTPUT | CPIA_MODULE_VP_CTRL | 25)
18254e8bc5dSHans de Goede #define CPIA_COMMAND_ReadVPRegs		(INPUT | CPIA_MODULE_VP_CTRL | 30)
18354e8bc5dSHans de Goede #define CPIA_COMMAND_WriteVPReg		(OUTPUT | CPIA_MODULE_VP_CTRL | 31)
18454e8bc5dSHans de Goede 
18554e8bc5dSHans de Goede #define CPIA_COMMAND_GrabFrame		(OUTPUT | CPIA_MODULE_CAPTURE | 1)
18654e8bc5dSHans de Goede #define CPIA_COMMAND_UploadFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 2)
18754e8bc5dSHans de Goede #define CPIA_COMMAND_SetGrabMode	(OUTPUT | CPIA_MODULE_CAPTURE | 3)
18854e8bc5dSHans de Goede #define CPIA_COMMAND_InitStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 4)
18954e8bc5dSHans de Goede #define CPIA_COMMAND_FiniStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 5)
19054e8bc5dSHans de Goede #define CPIA_COMMAND_StartStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 6)
19154e8bc5dSHans de Goede #define CPIA_COMMAND_EndStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 7)
19254e8bc5dSHans de Goede #define CPIA_COMMAND_SetFormat		(OUTPUT | CPIA_MODULE_CAPTURE | 8)
19354e8bc5dSHans de Goede #define CPIA_COMMAND_SetROI		(OUTPUT | CPIA_MODULE_CAPTURE | 9)
19454e8bc5dSHans de Goede #define CPIA_COMMAND_SetCompression	(OUTPUT | CPIA_MODULE_CAPTURE | 10)
19554e8bc5dSHans de Goede #define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
19654e8bc5dSHans de Goede #define CPIA_COMMAND_SetYUVThresh	(OUTPUT | CPIA_MODULE_CAPTURE | 12)
19754e8bc5dSHans de Goede #define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
19854e8bc5dSHans de Goede #define CPIA_COMMAND_DiscardFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 14)
19954e8bc5dSHans de Goede #define CPIA_COMMAND_GrabReset		(OUTPUT | CPIA_MODULE_CAPTURE | 15)
20054e8bc5dSHans de Goede 
20154e8bc5dSHans de Goede #define CPIA_COMMAND_OutputRS232	(OUTPUT | CPIA_MODULE_DEBUG | 1)
20254e8bc5dSHans de Goede #define CPIA_COMMAND_AbortProcess	(OUTPUT | CPIA_MODULE_DEBUG | 4)
20354e8bc5dSHans de Goede #define CPIA_COMMAND_SetDramPage	(OUTPUT | CPIA_MODULE_DEBUG | 5)
20454e8bc5dSHans de Goede #define CPIA_COMMAND_StartDramUpload	(OUTPUT | CPIA_MODULE_DEBUG | 6)
20554e8bc5dSHans de Goede #define CPIA_COMMAND_StartDummyDtream	(OUTPUT | CPIA_MODULE_DEBUG | 8)
20654e8bc5dSHans de Goede #define CPIA_COMMAND_AbortStream	(OUTPUT | CPIA_MODULE_DEBUG | 9)
20754e8bc5dSHans de Goede #define CPIA_COMMAND_DownloadDRAM	(OUTPUT | CPIA_MODULE_DEBUG | 10)
20854e8bc5dSHans de Goede #define CPIA_COMMAND_Null		(OUTPUT | CPIA_MODULE_DEBUG | 11)
20954e8bc5dSHans de Goede 
21054e8bc5dSHans de Goede #define ROUND_UP_EXP_FOR_FLICKER 15
21154e8bc5dSHans de Goede 
21254e8bc5dSHans de Goede /* Constants for automatic frame rate adjustment */
21354e8bc5dSHans de Goede #define MAX_EXP       302
21454e8bc5dSHans de Goede #define MAX_EXP_102   255
21554e8bc5dSHans de Goede #define LOW_EXP       140
21654e8bc5dSHans de Goede #define VERY_LOW_EXP   70
21754e8bc5dSHans de Goede #define TC             94
21854e8bc5dSHans de Goede #define	EXP_ACC_DARK   50
21954e8bc5dSHans de Goede #define	EXP_ACC_LIGHT  90
22054e8bc5dSHans de Goede #define HIGH_COMP_102 160
22154e8bc5dSHans de Goede #define MAX_COMP      239
22254e8bc5dSHans de Goede #define DARK_TIME       3
22354e8bc5dSHans de Goede #define LIGHT_TIME      3
22454e8bc5dSHans de Goede 
22554e8bc5dSHans de Goede #define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
22654e8bc5dSHans de Goede 				sd->params.version.firmwareRevision == (y))
22754e8bc5dSHans de Goede 
2281bfea3e4SHans Verkuil #define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000)
2291bfea3e4SHans Verkuil #define BRIGHTNESS_DEF 50
2301bfea3e4SHans Verkuil #define CONTRAST_DEF 48
2311bfea3e4SHans Verkuil #define SATURATION_DEF 50
2321bfea3e4SHans Verkuil #define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ
2331bfea3e4SHans Verkuil #define ILLUMINATORS_1_DEF 0
2341bfea3e4SHans Verkuil #define ILLUMINATORS_2_DEF 0
2351bfea3e4SHans Verkuil #define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
2361bfea3e4SHans Verkuil 
23754e8bc5dSHans de Goede /* Developer's Guide Table 5 p 3-34
23854e8bc5dSHans de Goede  * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
23954e8bc5dSHans de Goede static u8 flicker_jumps[2][2][4] =
24054e8bc5dSHans de Goede { { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
24154e8bc5dSHans de Goede   { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
24254e8bc5dSHans de Goede };
24354e8bc5dSHans de Goede 
24454e8bc5dSHans de Goede struct cam_params {
24554e8bc5dSHans de Goede 	struct {
24654e8bc5dSHans de Goede 		u8 firmwareVersion;
24754e8bc5dSHans de Goede 		u8 firmwareRevision;
24854e8bc5dSHans de Goede 		u8 vcVersion;
24954e8bc5dSHans de Goede 		u8 vcRevision;
25054e8bc5dSHans de Goede 	} version;
25154e8bc5dSHans de Goede 	struct {
25254e8bc5dSHans de Goede 		u16 vendor;
25354e8bc5dSHans de Goede 		u16 product;
25454e8bc5dSHans de Goede 		u16 deviceRevision;
25554e8bc5dSHans de Goede 	} pnpID;
25654e8bc5dSHans de Goede 	struct {
25754e8bc5dSHans de Goede 		u8 vpVersion;
25854e8bc5dSHans de Goede 		u8 vpRevision;
25954e8bc5dSHans de Goede 		u16 cameraHeadID;
26054e8bc5dSHans de Goede 	} vpVersion;
26154e8bc5dSHans de Goede 	struct {
26254e8bc5dSHans de Goede 		u8 systemState;
26354e8bc5dSHans de Goede 		u8 grabState;
26454e8bc5dSHans de Goede 		u8 streamState;
26554e8bc5dSHans de Goede 		u8 fatalError;
26654e8bc5dSHans de Goede 		u8 cmdError;
26754e8bc5dSHans de Goede 		u8 debugFlags;
26854e8bc5dSHans de Goede 		u8 vpStatus;
26954e8bc5dSHans de Goede 		u8 errorCode;
27054e8bc5dSHans de Goede 	} status;
27154e8bc5dSHans de Goede 	struct {
27254e8bc5dSHans de Goede 		u8 brightness;
27354e8bc5dSHans de Goede 		u8 contrast;
27454e8bc5dSHans de Goede 		u8 saturation;
27554e8bc5dSHans de Goede 	} colourParams;
27654e8bc5dSHans de Goede 	struct {
27754e8bc5dSHans de Goede 		u8 gainMode;
27854e8bc5dSHans de Goede 		u8 expMode;
27954e8bc5dSHans de Goede 		u8 compMode;
28054e8bc5dSHans de Goede 		u8 centreWeight;
28154e8bc5dSHans de Goede 		u8 gain;
28254e8bc5dSHans de Goede 		u8 fineExp;
28354e8bc5dSHans de Goede 		u8 coarseExpLo;
28454e8bc5dSHans de Goede 		u8 coarseExpHi;
28554e8bc5dSHans de Goede 		u8 redComp;
28654e8bc5dSHans de Goede 		u8 green1Comp;
28754e8bc5dSHans de Goede 		u8 green2Comp;
28854e8bc5dSHans de Goede 		u8 blueComp;
28954e8bc5dSHans de Goede 	} exposure;
29054e8bc5dSHans de Goede 	struct {
29154e8bc5dSHans de Goede 		u8 balanceMode;
29254e8bc5dSHans de Goede 		u8 redGain;
29354e8bc5dSHans de Goede 		u8 greenGain;
29454e8bc5dSHans de Goede 		u8 blueGain;
29554e8bc5dSHans de Goede 	} colourBalance;
29654e8bc5dSHans de Goede 	struct {
29754e8bc5dSHans de Goede 		u8 divisor;
29854e8bc5dSHans de Goede 		u8 baserate;
29954e8bc5dSHans de Goede 	} sensorFps;
30054e8bc5dSHans de Goede 	struct {
30154e8bc5dSHans de Goede 		u8 gain1;
30254e8bc5dSHans de Goede 		u8 gain2;
30354e8bc5dSHans de Goede 		u8 gain4;
30454e8bc5dSHans de Goede 		u8 gain8;
30554e8bc5dSHans de Goede 	} apcor;
30654e8bc5dSHans de Goede 	struct {
30754e8bc5dSHans de Goede 		u8 disabled;
30854e8bc5dSHans de Goede 		u8 flickerMode;
30954e8bc5dSHans de Goede 		u8 coarseJump;
31054e8bc5dSHans de Goede 		u8 allowableOverExposure;
31154e8bc5dSHans de Goede 	} flickerControl;
31254e8bc5dSHans de Goede 	struct {
31354e8bc5dSHans de Goede 		u8 gain1;
31454e8bc5dSHans de Goede 		u8 gain2;
31554e8bc5dSHans de Goede 		u8 gain4;
31654e8bc5dSHans de Goede 		u8 gain8;
31754e8bc5dSHans de Goede 	} vlOffset;
31854e8bc5dSHans de Goede 	struct {
31954e8bc5dSHans de Goede 		u8 mode;
32054e8bc5dSHans de Goede 		u8 decimation;
32154e8bc5dSHans de Goede 	} compression;
32254e8bc5dSHans de Goede 	struct {
32354e8bc5dSHans de Goede 		u8 frTargeting;
32454e8bc5dSHans de Goede 		u8 targetFR;
32554e8bc5dSHans de Goede 		u8 targetQ;
32654e8bc5dSHans de Goede 	} compressionTarget;
32754e8bc5dSHans de Goede 	struct {
32854e8bc5dSHans de Goede 		u8 yThreshold;
32954e8bc5dSHans de Goede 		u8 uvThreshold;
33054e8bc5dSHans de Goede 	} yuvThreshold;
33154e8bc5dSHans de Goede 	struct {
33254e8bc5dSHans de Goede 		u8 hysteresis;
33354e8bc5dSHans de Goede 		u8 threshMax;
33454e8bc5dSHans de Goede 		u8 smallStep;
33554e8bc5dSHans de Goede 		u8 largeStep;
33654e8bc5dSHans de Goede 		u8 decimationHysteresis;
33754e8bc5dSHans de Goede 		u8 frDiffStepThresh;
33854e8bc5dSHans de Goede 		u8 qDiffStepThresh;
33954e8bc5dSHans de Goede 		u8 decimationThreshMod;
34054e8bc5dSHans de Goede 	} compressionParams;
34154e8bc5dSHans de Goede 	struct {
34254e8bc5dSHans de Goede 		u8 videoSize;		/* CIF/QCIF */
34354e8bc5dSHans de Goede 		u8 subSample;
34454e8bc5dSHans de Goede 		u8 yuvOrder;
34554e8bc5dSHans de Goede 	} format;
34654e8bc5dSHans de Goede 	struct {                        /* Intel QX3 specific data */
34754e8bc5dSHans de Goede 		u8 qx3_detected;        /* a QX3 is present */
34854e8bc5dSHans de Goede 		u8 toplight;            /* top light lit , R/W */
34954e8bc5dSHans de Goede 		u8 bottomlight;         /* bottom light lit, R/W */
35054e8bc5dSHans de Goede 		u8 button;              /* snapshot button pressed (R/O) */
35154e8bc5dSHans de Goede 		u8 cradled;             /* microscope is in cradle (R/O) */
35254e8bc5dSHans de Goede 	} qx3;
35354e8bc5dSHans de Goede 	struct {
35454e8bc5dSHans de Goede 		u8 colStart;		/* skip first 8*colStart pixels */
35554e8bc5dSHans de Goede 		u8 colEnd;		/* finish at 8*colEnd pixels */
35654e8bc5dSHans de Goede 		u8 rowStart;		/* skip first 4*rowStart lines */
35754e8bc5dSHans de Goede 		u8 rowEnd;		/* finish at 4*rowEnd lines */
35854e8bc5dSHans de Goede 	} roi;
35954e8bc5dSHans de Goede 	u8 ecpTiming;
36054e8bc5dSHans de Goede 	u8 streamStartLine;
36154e8bc5dSHans de Goede };
36254e8bc5dSHans de Goede 
36354e8bc5dSHans de Goede /* specific webcam descriptor */
36454e8bc5dSHans de Goede struct sd {
36554e8bc5dSHans de Goede 	struct gspca_dev gspca_dev;		/* !! must be the first item */
36654e8bc5dSHans de Goede 	struct cam_params params;		/* camera settings */
36754e8bc5dSHans de Goede 
36854e8bc5dSHans de Goede 	atomic_t cam_exposure;
36954e8bc5dSHans de Goede 	atomic_t fps;
37054e8bc5dSHans de Goede 	int exposure_count;
37154e8bc5dSHans de Goede 	u8 exposure_status;
3721bfea3e4SHans Verkuil 	struct v4l2_ctrl *freq;
37354e8bc5dSHans de Goede 	u8 mainsFreq;				/* 0 = 50hz, 1 = 60hz */
37454e8bc5dSHans de Goede 	u8 first_frame;
37554e8bc5dSHans de Goede };
37654e8bc5dSHans de Goede 
37754e8bc5dSHans de Goede static const struct v4l2_pix_format mode[] = {
37854e8bc5dSHans de Goede 	{160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
37954e8bc5dSHans de Goede 		/* The sizeimage is trial and error, as with low framerates
38054e8bc5dSHans de Goede 		   the camera will pad out usb frames, making the image
38154e8bc5dSHans de Goede 		   data larger then strictly necessary */
38254e8bc5dSHans de Goede 		.bytesperline = 160,
38354e8bc5dSHans de Goede 		.sizeimage = 65536,
38454e8bc5dSHans de Goede 		.colorspace = V4L2_COLORSPACE_SRGB,
38554e8bc5dSHans de Goede 		.priv = 3},
38654e8bc5dSHans de Goede 	{176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
38754e8bc5dSHans de Goede 		.bytesperline = 172,
38854e8bc5dSHans de Goede 		.sizeimage = 65536,
38954e8bc5dSHans de Goede 		.colorspace = V4L2_COLORSPACE_SRGB,
39054e8bc5dSHans de Goede 		.priv = 2},
39154e8bc5dSHans de Goede 	{320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
39254e8bc5dSHans de Goede 		.bytesperline = 320,
39354e8bc5dSHans de Goede 		.sizeimage = 262144,
39454e8bc5dSHans de Goede 		.colorspace = V4L2_COLORSPACE_SRGB,
39554e8bc5dSHans de Goede 		.priv = 1},
39654e8bc5dSHans de Goede 	{352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
39754e8bc5dSHans de Goede 		.bytesperline = 352,
39854e8bc5dSHans de Goede 		.sizeimage = 262144,
39954e8bc5dSHans de Goede 		.colorspace = V4L2_COLORSPACE_SRGB,
40054e8bc5dSHans de Goede 		.priv = 0},
40154e8bc5dSHans de Goede };
40254e8bc5dSHans de Goede 
40354e8bc5dSHans de Goede /**********************************************************************
40454e8bc5dSHans de Goede  *
40554e8bc5dSHans de Goede  * General functions
40654e8bc5dSHans de Goede  *
40754e8bc5dSHans de Goede  **********************************************************************/
40854e8bc5dSHans de Goede 
40954e8bc5dSHans de Goede static int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command)
41054e8bc5dSHans de Goede {
41154e8bc5dSHans de Goede 	u8 requesttype;
41254e8bc5dSHans de Goede 	unsigned int pipe;
41354e8bc5dSHans de Goede 	int ret, databytes = command[6] | (command[7] << 8);
41454e8bc5dSHans de Goede 	/* Sometimes we see spurious EPIPE errors */
41554e8bc5dSHans de Goede 	int retries = 3;
41654e8bc5dSHans de Goede 
41754e8bc5dSHans de Goede 	if (command[0] == DATA_IN) {
41854e8bc5dSHans de Goede 		pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
41954e8bc5dSHans de Goede 		requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
42054e8bc5dSHans de Goede 	} else if (command[0] == DATA_OUT) {
42154e8bc5dSHans de Goede 		pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
42254e8bc5dSHans de Goede 		requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
42354e8bc5dSHans de Goede 	} else {
42454e8bc5dSHans de Goede 		PDEBUG(D_ERR, "Unexpected first byte of command: %x",
42554e8bc5dSHans de Goede 		       command[0]);
42654e8bc5dSHans de Goede 		return -EINVAL;
42754e8bc5dSHans de Goede 	}
42854e8bc5dSHans de Goede 
42954e8bc5dSHans de Goede retry:
43054e8bc5dSHans de Goede 	ret = usb_control_msg(gspca_dev->dev, pipe,
43154e8bc5dSHans de Goede 			      command[1],
43254e8bc5dSHans de Goede 			      requesttype,
43354e8bc5dSHans de Goede 			      command[2] | (command[3] << 8),
43454e8bc5dSHans de Goede 			      command[4] | (command[5] << 8),
43554e8bc5dSHans de Goede 			      gspca_dev->usb_buf, databytes, 1000);
43654e8bc5dSHans de Goede 
43754e8bc5dSHans de Goede 	if (ret < 0)
438133a9fe9SJoe Perches 		pr_err("usb_control_msg %02x, error %d\n", command[1], ret);
43954e8bc5dSHans de Goede 
44054e8bc5dSHans de Goede 	if (ret == -EPIPE && retries > 0) {
44154e8bc5dSHans de Goede 		retries--;
44254e8bc5dSHans de Goede 		goto retry;
44354e8bc5dSHans de Goede 	}
44454e8bc5dSHans de Goede 
44554e8bc5dSHans de Goede 	return (ret < 0) ? ret : 0;
44654e8bc5dSHans de Goede }
44754e8bc5dSHans de Goede 
44854e8bc5dSHans de Goede /* send an arbitrary command to the camera */
44954e8bc5dSHans de Goede static int do_command(struct gspca_dev *gspca_dev, u16 command,
45054e8bc5dSHans de Goede 		      u8 a, u8 b, u8 c, u8 d)
45154e8bc5dSHans de Goede {
45254e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
45354e8bc5dSHans de Goede 	int ret, datasize;
45454e8bc5dSHans de Goede 	u8 cmd[8];
45554e8bc5dSHans de Goede 
45654e8bc5dSHans de Goede 	switch (command) {
45754e8bc5dSHans de Goede 	case CPIA_COMMAND_GetCPIAVersion:
45854e8bc5dSHans de Goede 	case CPIA_COMMAND_GetPnPID:
45954e8bc5dSHans de Goede 	case CPIA_COMMAND_GetCameraStatus:
46054e8bc5dSHans de Goede 	case CPIA_COMMAND_GetVPVersion:
46154e8bc5dSHans de Goede 	case CPIA_COMMAND_GetColourParams:
46254e8bc5dSHans de Goede 	case CPIA_COMMAND_GetColourBalance:
46354e8bc5dSHans de Goede 	case CPIA_COMMAND_GetExposure:
46454e8bc5dSHans de Goede 		datasize = 8;
46554e8bc5dSHans de Goede 		break;
46654e8bc5dSHans de Goede 	case CPIA_COMMAND_ReadMCPorts:
46754e8bc5dSHans de Goede 	case CPIA_COMMAND_ReadVCRegs:
46854e8bc5dSHans de Goede 		datasize = 4;
46954e8bc5dSHans de Goede 		break;
47054e8bc5dSHans de Goede 	default:
47154e8bc5dSHans de Goede 		datasize = 0;
47254e8bc5dSHans de Goede 		break;
47354e8bc5dSHans de Goede 	}
47454e8bc5dSHans de Goede 
47554e8bc5dSHans de Goede 	cmd[0] = command >> 8;
47654e8bc5dSHans de Goede 	cmd[1] = command & 0xff;
47754e8bc5dSHans de Goede 	cmd[2] = a;
47854e8bc5dSHans de Goede 	cmd[3] = b;
47954e8bc5dSHans de Goede 	cmd[4] = c;
48054e8bc5dSHans de Goede 	cmd[5] = d;
48154e8bc5dSHans de Goede 	cmd[6] = datasize;
48254e8bc5dSHans de Goede 	cmd[7] = 0;
48354e8bc5dSHans de Goede 
48454e8bc5dSHans de Goede 	ret = cpia_usb_transferCmd(gspca_dev, cmd);
48554e8bc5dSHans de Goede 	if (ret)
48654e8bc5dSHans de Goede 		return ret;
48754e8bc5dSHans de Goede 
48854e8bc5dSHans de Goede 	switch (command) {
48954e8bc5dSHans de Goede 	case CPIA_COMMAND_GetCPIAVersion:
49054e8bc5dSHans de Goede 		sd->params.version.firmwareVersion = gspca_dev->usb_buf[0];
49154e8bc5dSHans de Goede 		sd->params.version.firmwareRevision = gspca_dev->usb_buf[1];
49254e8bc5dSHans de Goede 		sd->params.version.vcVersion = gspca_dev->usb_buf[2];
49354e8bc5dSHans de Goede 		sd->params.version.vcRevision = gspca_dev->usb_buf[3];
49454e8bc5dSHans de Goede 		break;
49554e8bc5dSHans de Goede 	case CPIA_COMMAND_GetPnPID:
49654e8bc5dSHans de Goede 		sd->params.pnpID.vendor =
49754e8bc5dSHans de Goede 			gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
49854e8bc5dSHans de Goede 		sd->params.pnpID.product =
49954e8bc5dSHans de Goede 			gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
50054e8bc5dSHans de Goede 		sd->params.pnpID.deviceRevision =
50154e8bc5dSHans de Goede 			gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8);
50254e8bc5dSHans de Goede 		break;
50354e8bc5dSHans de Goede 	case CPIA_COMMAND_GetCameraStatus:
50454e8bc5dSHans de Goede 		sd->params.status.systemState = gspca_dev->usb_buf[0];
50554e8bc5dSHans de Goede 		sd->params.status.grabState = gspca_dev->usb_buf[1];
50654e8bc5dSHans de Goede 		sd->params.status.streamState = gspca_dev->usb_buf[2];
50754e8bc5dSHans de Goede 		sd->params.status.fatalError = gspca_dev->usb_buf[3];
50854e8bc5dSHans de Goede 		sd->params.status.cmdError = gspca_dev->usb_buf[4];
50954e8bc5dSHans de Goede 		sd->params.status.debugFlags = gspca_dev->usb_buf[5];
51054e8bc5dSHans de Goede 		sd->params.status.vpStatus = gspca_dev->usb_buf[6];
51154e8bc5dSHans de Goede 		sd->params.status.errorCode = gspca_dev->usb_buf[7];
51254e8bc5dSHans de Goede 		break;
51354e8bc5dSHans de Goede 	case CPIA_COMMAND_GetVPVersion:
51454e8bc5dSHans de Goede 		sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0];
51554e8bc5dSHans de Goede 		sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1];
51654e8bc5dSHans de Goede 		sd->params.vpVersion.cameraHeadID =
51754e8bc5dSHans de Goede 			gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
51854e8bc5dSHans de Goede 		break;
51954e8bc5dSHans de Goede 	case CPIA_COMMAND_GetColourParams:
52054e8bc5dSHans de Goede 		sd->params.colourParams.brightness = gspca_dev->usb_buf[0];
52154e8bc5dSHans de Goede 		sd->params.colourParams.contrast = gspca_dev->usb_buf[1];
52254e8bc5dSHans de Goede 		sd->params.colourParams.saturation = gspca_dev->usb_buf[2];
52354e8bc5dSHans de Goede 		break;
52454e8bc5dSHans de Goede 	case CPIA_COMMAND_GetColourBalance:
52554e8bc5dSHans de Goede 		sd->params.colourBalance.redGain = gspca_dev->usb_buf[0];
52654e8bc5dSHans de Goede 		sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1];
52754e8bc5dSHans de Goede 		sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2];
52854e8bc5dSHans de Goede 		break;
52954e8bc5dSHans de Goede 	case CPIA_COMMAND_GetExposure:
53054e8bc5dSHans de Goede 		sd->params.exposure.gain = gspca_dev->usb_buf[0];
53154e8bc5dSHans de Goede 		sd->params.exposure.fineExp = gspca_dev->usb_buf[1];
53254e8bc5dSHans de Goede 		sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2];
53354e8bc5dSHans de Goede 		sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3];
53454e8bc5dSHans de Goede 		sd->params.exposure.redComp = gspca_dev->usb_buf[4];
53554e8bc5dSHans de Goede 		sd->params.exposure.green1Comp = gspca_dev->usb_buf[5];
53654e8bc5dSHans de Goede 		sd->params.exposure.green2Comp = gspca_dev->usb_buf[6];
53754e8bc5dSHans de Goede 		sd->params.exposure.blueComp = gspca_dev->usb_buf[7];
53854e8bc5dSHans de Goede 		break;
53954e8bc5dSHans de Goede 
54054e8bc5dSHans de Goede 	case CPIA_COMMAND_ReadMCPorts:
54154e8bc5dSHans de Goede 		/* test button press */
542c2f644aeSHans de Goede 		a = ((gspca_dev->usb_buf[1] & 0x02) == 0);
543c2f644aeSHans de Goede 		if (a != sd->params.qx3.button) {
544c2f644aeSHans de Goede #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
545c2f644aeSHans de Goede 			input_report_key(gspca_dev->input_dev, KEY_CAMERA, a);
546c2f644aeSHans de Goede 			input_sync(gspca_dev->input_dev);
547c2f644aeSHans de Goede #endif
548c2f644aeSHans de Goede 	        	sd->params.qx3.button = a;
549c2f644aeSHans de Goede 		}
55054e8bc5dSHans de Goede 		if (sd->params.qx3.button) {
55154e8bc5dSHans de Goede 			/* button pressed - unlock the latch */
55254e8bc5dSHans de Goede 			do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
5531d00d6c1SJean-François Moine 				   3, 0xdf, 0xdf, 0);
55454e8bc5dSHans de Goede 			do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
5551d00d6c1SJean-François Moine 				   3, 0xff, 0xff, 0);
55654e8bc5dSHans de Goede 		}
55754e8bc5dSHans de Goede 
55854e8bc5dSHans de Goede 		/* test whether microscope is cradled */
55954e8bc5dSHans de Goede 		sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0);
56054e8bc5dSHans de Goede 		break;
56154e8bc5dSHans de Goede 	}
56254e8bc5dSHans de Goede 
56354e8bc5dSHans de Goede 	return 0;
56454e8bc5dSHans de Goede }
56554e8bc5dSHans de Goede 
56654e8bc5dSHans de Goede /* send a command to the camera with an additional data transaction */
56754e8bc5dSHans de Goede static int do_command_extended(struct gspca_dev *gspca_dev, u16 command,
56854e8bc5dSHans de Goede 			       u8 a, u8 b, u8 c, u8 d,
56954e8bc5dSHans de Goede 			       u8 e, u8 f, u8 g, u8 h,
57054e8bc5dSHans de Goede 			       u8 i, u8 j, u8 k, u8 l)
57154e8bc5dSHans de Goede {
57254e8bc5dSHans de Goede 	u8 cmd[8];
57354e8bc5dSHans de Goede 
57454e8bc5dSHans de Goede 	cmd[0] = command >> 8;
57554e8bc5dSHans de Goede 	cmd[1] = command & 0xff;
57654e8bc5dSHans de Goede 	cmd[2] = a;
57754e8bc5dSHans de Goede 	cmd[3] = b;
57854e8bc5dSHans de Goede 	cmd[4] = c;
57954e8bc5dSHans de Goede 	cmd[5] = d;
58054e8bc5dSHans de Goede 	cmd[6] = 8;
58154e8bc5dSHans de Goede 	cmd[7] = 0;
58254e8bc5dSHans de Goede 	gspca_dev->usb_buf[0] = e;
58354e8bc5dSHans de Goede 	gspca_dev->usb_buf[1] = f;
58454e8bc5dSHans de Goede 	gspca_dev->usb_buf[2] = g;
58554e8bc5dSHans de Goede 	gspca_dev->usb_buf[3] = h;
58654e8bc5dSHans de Goede 	gspca_dev->usb_buf[4] = i;
58754e8bc5dSHans de Goede 	gspca_dev->usb_buf[5] = j;
58854e8bc5dSHans de Goede 	gspca_dev->usb_buf[6] = k;
58954e8bc5dSHans de Goede 	gspca_dev->usb_buf[7] = l;
59054e8bc5dSHans de Goede 
59154e8bc5dSHans de Goede 	return cpia_usb_transferCmd(gspca_dev, cmd);
59254e8bc5dSHans de Goede }
59354e8bc5dSHans de Goede 
59454e8bc5dSHans de Goede /*  find_over_exposure
59554e8bc5dSHans de Goede  *  Finds a suitable value of OverExposure for use with SetFlickerCtrl
59654e8bc5dSHans de Goede  *  Some calculation is required because this value changes with the brightness
59754e8bc5dSHans de Goede  *  set with SetColourParameters
59854e8bc5dSHans de Goede  *
59954e8bc5dSHans de Goede  *  Parameters: Brightness - last brightness value set with SetColourParameters
60054e8bc5dSHans de Goede  *
60154e8bc5dSHans de Goede  *  Returns: OverExposure value to use with SetFlickerCtrl
60254e8bc5dSHans de Goede  */
60354e8bc5dSHans de Goede #define FLICKER_MAX_EXPOSURE                    250
60454e8bc5dSHans de Goede #define FLICKER_ALLOWABLE_OVER_EXPOSURE         146
60554e8bc5dSHans de Goede #define FLICKER_BRIGHTNESS_CONSTANT             59
60654e8bc5dSHans de Goede static int find_over_exposure(int brightness)
60754e8bc5dSHans de Goede {
60854e8bc5dSHans de Goede 	int MaxAllowableOverExposure, OverExposure;
60954e8bc5dSHans de Goede 
61054e8bc5dSHans de Goede 	MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
61154e8bc5dSHans de Goede 				   FLICKER_BRIGHTNESS_CONSTANT;
61254e8bc5dSHans de Goede 
61354e8bc5dSHans de Goede 	if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE)
61454e8bc5dSHans de Goede 		OverExposure = MaxAllowableOverExposure;
61554e8bc5dSHans de Goede 	else
61654e8bc5dSHans de Goede 		OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
61754e8bc5dSHans de Goede 
61854e8bc5dSHans de Goede 	return OverExposure;
61954e8bc5dSHans de Goede }
62054e8bc5dSHans de Goede #undef FLICKER_MAX_EXPOSURE
62154e8bc5dSHans de Goede #undef FLICKER_ALLOWABLE_OVER_EXPOSURE
62254e8bc5dSHans de Goede #undef FLICKER_BRIGHTNESS_CONSTANT
62354e8bc5dSHans de Goede 
62454e8bc5dSHans de Goede /* initialise cam_data structure  */
62554e8bc5dSHans de Goede static void reset_camera_params(struct gspca_dev *gspca_dev)
62654e8bc5dSHans de Goede {
62754e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
62854e8bc5dSHans de Goede 	struct cam_params *params = &sd->params;
62954e8bc5dSHans de Goede 
63054e8bc5dSHans de Goede 	/* The following parameter values are the defaults from
63154e8bc5dSHans de Goede 	 * "Software Developer's Guide for CPiA Cameras".  Any changes
63254e8bc5dSHans de Goede 	 * to the defaults are noted in comments. */
63354e8bc5dSHans de Goede 	params->colourParams.brightness = BRIGHTNESS_DEF;
63454e8bc5dSHans de Goede 	params->colourParams.contrast = CONTRAST_DEF;
63554e8bc5dSHans de Goede 	params->colourParams.saturation = SATURATION_DEF;
63654e8bc5dSHans de Goede 	params->exposure.gainMode = 4;
63754e8bc5dSHans de Goede 	params->exposure.expMode = 2;		/* AEC */
63854e8bc5dSHans de Goede 	params->exposure.compMode = 1;
63954e8bc5dSHans de Goede 	params->exposure.centreWeight = 1;
64054e8bc5dSHans de Goede 	params->exposure.gain = 0;
64154e8bc5dSHans de Goede 	params->exposure.fineExp = 0;
64254e8bc5dSHans de Goede 	params->exposure.coarseExpLo = 185;
64354e8bc5dSHans de Goede 	params->exposure.coarseExpHi = 0;
64454e8bc5dSHans de Goede 	params->exposure.redComp = COMP_RED;
64554e8bc5dSHans de Goede 	params->exposure.green1Comp = COMP_GREEN1;
64654e8bc5dSHans de Goede 	params->exposure.green2Comp = COMP_GREEN2;
64754e8bc5dSHans de Goede 	params->exposure.blueComp = COMP_BLUE;
64854e8bc5dSHans de Goede 	params->colourBalance.balanceMode = 2;	/* ACB */
64954e8bc5dSHans de Goede 	params->colourBalance.redGain = 32;
65054e8bc5dSHans de Goede 	params->colourBalance.greenGain = 6;
65154e8bc5dSHans de Goede 	params->colourBalance.blueGain = 92;
65254e8bc5dSHans de Goede 	params->apcor.gain1 = 0x18;
65354e8bc5dSHans de Goede 	params->apcor.gain2 = 0x16;
65454e8bc5dSHans de Goede 	params->apcor.gain4 = 0x24;
65554e8bc5dSHans de Goede 	params->apcor.gain8 = 0x34;
65654e8bc5dSHans de Goede 	params->vlOffset.gain1 = 20;
65754e8bc5dSHans de Goede 	params->vlOffset.gain2 = 24;
65854e8bc5dSHans de Goede 	params->vlOffset.gain4 = 26;
65954e8bc5dSHans de Goede 	params->vlOffset.gain8 = 26;
66054e8bc5dSHans de Goede 	params->compressionParams.hysteresis = 3;
66154e8bc5dSHans de Goede 	params->compressionParams.threshMax = 11;
66254e8bc5dSHans de Goede 	params->compressionParams.smallStep = 1;
66354e8bc5dSHans de Goede 	params->compressionParams.largeStep = 3;
66454e8bc5dSHans de Goede 	params->compressionParams.decimationHysteresis = 2;
66554e8bc5dSHans de Goede 	params->compressionParams.frDiffStepThresh = 5;
66654e8bc5dSHans de Goede 	params->compressionParams.qDiffStepThresh = 3;
66754e8bc5dSHans de Goede 	params->compressionParams.decimationThreshMod = 2;
66854e8bc5dSHans de Goede 	/* End of default values from Software Developer's Guide */
66954e8bc5dSHans de Goede 
67054e8bc5dSHans de Goede 	/* Set Sensor FPS to 15fps. This seems better than 30fps
67154e8bc5dSHans de Goede 	 * for indoor lighting. */
67254e8bc5dSHans de Goede 	params->sensorFps.divisor = 1;
67354e8bc5dSHans de Goede 	params->sensorFps.baserate = 1;
67454e8bc5dSHans de Goede 
6751bfea3e4SHans Verkuil 	params->flickerControl.flickerMode = 0;
6761bfea3e4SHans Verkuil 	params->flickerControl.disabled = 1;
6771bfea3e4SHans Verkuil 	params->flickerControl.coarseJump =
6781bfea3e4SHans Verkuil 		flicker_jumps[sd->mainsFreq]
6791bfea3e4SHans Verkuil 			     [params->sensorFps.baserate]
6801bfea3e4SHans Verkuil 			     [params->sensorFps.divisor];
6811bfea3e4SHans Verkuil 	params->flickerControl.allowableOverExposure =
6821bfea3e4SHans Verkuil 		find_over_exposure(params->colourParams.brightness);
6831bfea3e4SHans Verkuil 
68454e8bc5dSHans de Goede 	params->yuvThreshold.yThreshold = 6; /* From windows driver */
68554e8bc5dSHans de Goede 	params->yuvThreshold.uvThreshold = 6; /* From windows driver */
68654e8bc5dSHans de Goede 
68754e8bc5dSHans de Goede 	params->format.subSample = SUBSAMPLE_420;
68854e8bc5dSHans de Goede 	params->format.yuvOrder = YUVORDER_YUYV;
68954e8bc5dSHans de Goede 
69054e8bc5dSHans de Goede 	params->compression.mode = CPIA_COMPRESSION_AUTO;
69154e8bc5dSHans de Goede 	params->compression.decimation = NO_DECIMATION;
69254e8bc5dSHans de Goede 
69354e8bc5dSHans de Goede 	params->compressionTarget.frTargeting = COMP_TARGET_DEF;
69454e8bc5dSHans de Goede 	params->compressionTarget.targetFR = 15; /* From windows driver */
69554e8bc5dSHans de Goede 	params->compressionTarget.targetQ = 5; /* From windows driver */
69654e8bc5dSHans de Goede 
69754e8bc5dSHans de Goede 	params->qx3.qx3_detected = 0;
69854e8bc5dSHans de Goede 	params->qx3.toplight = 0;
69954e8bc5dSHans de Goede 	params->qx3.bottomlight = 0;
70054e8bc5dSHans de Goede 	params->qx3.button = 0;
70154e8bc5dSHans de Goede 	params->qx3.cradled = 0;
70254e8bc5dSHans de Goede }
70354e8bc5dSHans de Goede 
70454e8bc5dSHans de Goede static void printstatus(struct cam_params *params)
70554e8bc5dSHans de Goede {
70654e8bc5dSHans de Goede 	PDEBUG(D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x",
70754e8bc5dSHans de Goede 	       params->status.systemState, params->status.grabState,
70854e8bc5dSHans de Goede 	       params->status.streamState, params->status.fatalError,
70954e8bc5dSHans de Goede 	       params->status.cmdError, params->status.debugFlags,
71054e8bc5dSHans de Goede 	       params->status.vpStatus, params->status.errorCode);
71154e8bc5dSHans de Goede }
71254e8bc5dSHans de Goede 
71354e8bc5dSHans de Goede static int goto_low_power(struct gspca_dev *gspca_dev)
71454e8bc5dSHans de Goede {
71554e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
71654e8bc5dSHans de Goede 	int ret;
71754e8bc5dSHans de Goede 
71854e8bc5dSHans de Goede 	ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0);
71954e8bc5dSHans de Goede 	if (ret)
72054e8bc5dSHans de Goede 		return ret;
72154e8bc5dSHans de Goede 
7229be1d6cdSNicolas Kaiser 	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
72354e8bc5dSHans de Goede 	if (ret)
72454e8bc5dSHans de Goede 		return ret;
72554e8bc5dSHans de Goede 
72654e8bc5dSHans de Goede 	if (sd->params.status.systemState != LO_POWER_STATE) {
72754e8bc5dSHans de Goede 		if (sd->params.status.systemState != WARM_BOOT_STATE) {
72854e8bc5dSHans de Goede 			PDEBUG(D_ERR,
72954e8bc5dSHans de Goede 			       "unexpected state after lo power cmd: %02x",
73054e8bc5dSHans de Goede 			       sd->params.status.systemState);
73154e8bc5dSHans de Goede 			printstatus(&sd->params);
73254e8bc5dSHans de Goede 		}
73354e8bc5dSHans de Goede 		return -EIO;
73454e8bc5dSHans de Goede 	}
73554e8bc5dSHans de Goede 
73654e8bc5dSHans de Goede 	PDEBUG(D_CONF, "camera now in LOW power state");
73754e8bc5dSHans de Goede 	return 0;
73854e8bc5dSHans de Goede }
73954e8bc5dSHans de Goede 
74054e8bc5dSHans de Goede static int goto_high_power(struct gspca_dev *gspca_dev)
74154e8bc5dSHans de Goede {
74254e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
74354e8bc5dSHans de Goede 	int ret;
74454e8bc5dSHans de Goede 
74554e8bc5dSHans de Goede 	ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0);
74654e8bc5dSHans de Goede 	if (ret)
74754e8bc5dSHans de Goede 		return ret;
74854e8bc5dSHans de Goede 
74954e8bc5dSHans de Goede 	msleep_interruptible(40);	/* windows driver does it too */
75054e8bc5dSHans de Goede 
75154e8bc5dSHans de Goede 	if (signal_pending(current))
75254e8bc5dSHans de Goede 		return -EINTR;
75354e8bc5dSHans de Goede 
754*8c96f0a2SPeter Senna Tschudin 	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
75554e8bc5dSHans de Goede 	if (ret)
75654e8bc5dSHans de Goede 		return ret;
75754e8bc5dSHans de Goede 
75854e8bc5dSHans de Goede 	if (sd->params.status.systemState != HI_POWER_STATE) {
75954e8bc5dSHans de Goede 		PDEBUG(D_ERR, "unexpected state after hi power cmd: %02x",
76054e8bc5dSHans de Goede 			       sd->params.status.systemState);
76154e8bc5dSHans de Goede 		printstatus(&sd->params);
76254e8bc5dSHans de Goede 		return -EIO;
76354e8bc5dSHans de Goede 	}
76454e8bc5dSHans de Goede 
76554e8bc5dSHans de Goede 	PDEBUG(D_CONF, "camera now in HIGH power state");
76654e8bc5dSHans de Goede 	return 0;
76754e8bc5dSHans de Goede }
76854e8bc5dSHans de Goede 
76954e8bc5dSHans de Goede static int get_version_information(struct gspca_dev *gspca_dev)
77054e8bc5dSHans de Goede {
77154e8bc5dSHans de Goede 	int ret;
77254e8bc5dSHans de Goede 
77354e8bc5dSHans de Goede 	/* GetCPIAVersion */
77454e8bc5dSHans de Goede 	ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
77554e8bc5dSHans de Goede 	if (ret)
77654e8bc5dSHans de Goede 		return ret;
77754e8bc5dSHans de Goede 
77854e8bc5dSHans de Goede 	/* GetPnPID */
77954e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
78054e8bc5dSHans de Goede }
78154e8bc5dSHans de Goede 
78254e8bc5dSHans de Goede static int save_camera_state(struct gspca_dev *gspca_dev)
78354e8bc5dSHans de Goede {
78454e8bc5dSHans de Goede 	int ret;
78554e8bc5dSHans de Goede 
78654e8bc5dSHans de Goede 	ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
78754e8bc5dSHans de Goede 	if (ret)
78854e8bc5dSHans de Goede 		return ret;
78954e8bc5dSHans de Goede 
79054e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
79154e8bc5dSHans de Goede }
79254e8bc5dSHans de Goede 
793e7c3ee63SMárton Németh static int command_setformat(struct gspca_dev *gspca_dev)
79454e8bc5dSHans de Goede {
79554e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
79654e8bc5dSHans de Goede 	int ret;
79754e8bc5dSHans de Goede 
79854e8bc5dSHans de Goede 	ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat,
79954e8bc5dSHans de Goede 			 sd->params.format.videoSize,
80054e8bc5dSHans de Goede 			 sd->params.format.subSample,
80154e8bc5dSHans de Goede 			 sd->params.format.yuvOrder, 0);
80254e8bc5dSHans de Goede 	if (ret)
80354e8bc5dSHans de Goede 		return ret;
80454e8bc5dSHans de Goede 
80554e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_SetROI,
80654e8bc5dSHans de Goede 			  sd->params.roi.colStart, sd->params.roi.colEnd,
80754e8bc5dSHans de Goede 			  sd->params.roi.rowStart, sd->params.roi.rowEnd);
80854e8bc5dSHans de Goede }
80954e8bc5dSHans de Goede 
810e7c3ee63SMárton Németh static int command_setcolourparams(struct gspca_dev *gspca_dev)
81154e8bc5dSHans de Goede {
81254e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
81354e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_SetColourParams,
81454e8bc5dSHans de Goede 			  sd->params.colourParams.brightness,
81554e8bc5dSHans de Goede 			  sd->params.colourParams.contrast,
81654e8bc5dSHans de Goede 			  sd->params.colourParams.saturation, 0);
81754e8bc5dSHans de Goede }
81854e8bc5dSHans de Goede 
819e7c3ee63SMárton Németh static int command_setapcor(struct gspca_dev *gspca_dev)
82054e8bc5dSHans de Goede {
82154e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
82254e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_SetApcor,
82354e8bc5dSHans de Goede 			  sd->params.apcor.gain1,
82454e8bc5dSHans de Goede 			  sd->params.apcor.gain2,
82554e8bc5dSHans de Goede 			  sd->params.apcor.gain4,
82654e8bc5dSHans de Goede 			  sd->params.apcor.gain8);
82754e8bc5dSHans de Goede }
82854e8bc5dSHans de Goede 
829e7c3ee63SMárton Németh static int command_setvloffset(struct gspca_dev *gspca_dev)
83054e8bc5dSHans de Goede {
83154e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
83254e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset,
83354e8bc5dSHans de Goede 			  sd->params.vlOffset.gain1,
83454e8bc5dSHans de Goede 			  sd->params.vlOffset.gain2,
83554e8bc5dSHans de Goede 			  sd->params.vlOffset.gain4,
83654e8bc5dSHans de Goede 			  sd->params.vlOffset.gain8);
83754e8bc5dSHans de Goede }
83854e8bc5dSHans de Goede 
839e7c3ee63SMárton Németh static int command_setexposure(struct gspca_dev *gspca_dev)
84054e8bc5dSHans de Goede {
84154e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
84254e8bc5dSHans de Goede 	int ret;
84354e8bc5dSHans de Goede 
84454e8bc5dSHans de Goede 	ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
84554e8bc5dSHans de Goede 				  sd->params.exposure.gainMode,
84654e8bc5dSHans de Goede 				  1,
84754e8bc5dSHans de Goede 				  sd->params.exposure.compMode,
84854e8bc5dSHans de Goede 				  sd->params.exposure.centreWeight,
84954e8bc5dSHans de Goede 				  sd->params.exposure.gain,
85054e8bc5dSHans de Goede 				  sd->params.exposure.fineExp,
85154e8bc5dSHans de Goede 				  sd->params.exposure.coarseExpLo,
85254e8bc5dSHans de Goede 				  sd->params.exposure.coarseExpHi,
85354e8bc5dSHans de Goede 				  sd->params.exposure.redComp,
85454e8bc5dSHans de Goede 				  sd->params.exposure.green1Comp,
85554e8bc5dSHans de Goede 				  sd->params.exposure.green2Comp,
85654e8bc5dSHans de Goede 				  sd->params.exposure.blueComp);
85754e8bc5dSHans de Goede 	if (ret)
85854e8bc5dSHans de Goede 		return ret;
85954e8bc5dSHans de Goede 
86054e8bc5dSHans de Goede 	if (sd->params.exposure.expMode != 1) {
86154e8bc5dSHans de Goede 		ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
86254e8bc5dSHans de Goede 					  0,
86354e8bc5dSHans de Goede 					  sd->params.exposure.expMode,
86454e8bc5dSHans de Goede 					  0, 0,
86554e8bc5dSHans de Goede 					  sd->params.exposure.gain,
86654e8bc5dSHans de Goede 					  sd->params.exposure.fineExp,
86754e8bc5dSHans de Goede 					  sd->params.exposure.coarseExpLo,
86854e8bc5dSHans de Goede 					  sd->params.exposure.coarseExpHi,
86954e8bc5dSHans de Goede 					  0, 0, 0, 0);
87054e8bc5dSHans de Goede 	}
87154e8bc5dSHans de Goede 
87254e8bc5dSHans de Goede 	return ret;
87354e8bc5dSHans de Goede }
87454e8bc5dSHans de Goede 
875e7c3ee63SMárton Németh static int command_setcolourbalance(struct gspca_dev *gspca_dev)
87654e8bc5dSHans de Goede {
87754e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
87854e8bc5dSHans de Goede 
87954e8bc5dSHans de Goede 	if (sd->params.colourBalance.balanceMode == 1) {
88054e8bc5dSHans de Goede 		int ret;
88154e8bc5dSHans de Goede 
88254e8bc5dSHans de Goede 		ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
88354e8bc5dSHans de Goede 				 1,
88454e8bc5dSHans de Goede 				 sd->params.colourBalance.redGain,
88554e8bc5dSHans de Goede 				 sd->params.colourBalance.greenGain,
88654e8bc5dSHans de Goede 				 sd->params.colourBalance.blueGain);
88754e8bc5dSHans de Goede 		if (ret)
88854e8bc5dSHans de Goede 			return ret;
88954e8bc5dSHans de Goede 
89054e8bc5dSHans de Goede 		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
89154e8bc5dSHans de Goede 				  3, 0, 0, 0);
89254e8bc5dSHans de Goede 	}
89354e8bc5dSHans de Goede 	if (sd->params.colourBalance.balanceMode == 2) {
89454e8bc5dSHans de Goede 		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
89554e8bc5dSHans de Goede 				  2, 0, 0, 0);
89654e8bc5dSHans de Goede 	}
89754e8bc5dSHans de Goede 	if (sd->params.colourBalance.balanceMode == 3) {
89854e8bc5dSHans de Goede 		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
89954e8bc5dSHans de Goede 				  3, 0, 0, 0);
90054e8bc5dSHans de Goede 	}
90154e8bc5dSHans de Goede 
90254e8bc5dSHans de Goede 	return -EINVAL;
90354e8bc5dSHans de Goede }
90454e8bc5dSHans de Goede 
905e7c3ee63SMárton Németh static int command_setcompressiontarget(struct gspca_dev *gspca_dev)
90654e8bc5dSHans de Goede {
90754e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
90854e8bc5dSHans de Goede 
90954e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget,
91054e8bc5dSHans de Goede 			  sd->params.compressionTarget.frTargeting,
91154e8bc5dSHans de Goede 			  sd->params.compressionTarget.targetFR,
91254e8bc5dSHans de Goede 			  sd->params.compressionTarget.targetQ, 0);
91354e8bc5dSHans de Goede }
91454e8bc5dSHans de Goede 
915e7c3ee63SMárton Németh static int command_setyuvtresh(struct gspca_dev *gspca_dev)
91654e8bc5dSHans de Goede {
91754e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
91854e8bc5dSHans de Goede 
91954e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh,
92054e8bc5dSHans de Goede 			  sd->params.yuvThreshold.yThreshold,
92154e8bc5dSHans de Goede 			  sd->params.yuvThreshold.uvThreshold, 0, 0);
92254e8bc5dSHans de Goede }
92354e8bc5dSHans de Goede 
924e7c3ee63SMárton Németh static int command_setcompressionparams(struct gspca_dev *gspca_dev)
92554e8bc5dSHans de Goede {
92654e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
92754e8bc5dSHans de Goede 
92854e8bc5dSHans de Goede 	return do_command_extended(gspca_dev,
92954e8bc5dSHans de Goede 			    CPIA_COMMAND_SetCompressionParams,
93054e8bc5dSHans de Goede 			    0, 0, 0, 0,
93154e8bc5dSHans de Goede 			    sd->params.compressionParams.hysteresis,
93254e8bc5dSHans de Goede 			    sd->params.compressionParams.threshMax,
93354e8bc5dSHans de Goede 			    sd->params.compressionParams.smallStep,
93454e8bc5dSHans de Goede 			    sd->params.compressionParams.largeStep,
93554e8bc5dSHans de Goede 			    sd->params.compressionParams.decimationHysteresis,
93654e8bc5dSHans de Goede 			    sd->params.compressionParams.frDiffStepThresh,
93754e8bc5dSHans de Goede 			    sd->params.compressionParams.qDiffStepThresh,
93854e8bc5dSHans de Goede 			    sd->params.compressionParams.decimationThreshMod);
93954e8bc5dSHans de Goede }
94054e8bc5dSHans de Goede 
941e7c3ee63SMárton Németh static int command_setcompression(struct gspca_dev *gspca_dev)
94254e8bc5dSHans de Goede {
94354e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
94454e8bc5dSHans de Goede 
94554e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_SetCompression,
94654e8bc5dSHans de Goede 			  sd->params.compression.mode,
94754e8bc5dSHans de Goede 			  sd->params.compression.decimation, 0, 0);
94854e8bc5dSHans de Goede }
94954e8bc5dSHans de Goede 
950e7c3ee63SMárton Németh static int command_setsensorfps(struct gspca_dev *gspca_dev)
95154e8bc5dSHans de Goede {
95254e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
95354e8bc5dSHans de Goede 
95454e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS,
95554e8bc5dSHans de Goede 			  sd->params.sensorFps.divisor,
95654e8bc5dSHans de Goede 			  sd->params.sensorFps.baserate, 0, 0);
95754e8bc5dSHans de Goede }
95854e8bc5dSHans de Goede 
959e7c3ee63SMárton Németh static int command_setflickerctrl(struct gspca_dev *gspca_dev)
96054e8bc5dSHans de Goede {
96154e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
96254e8bc5dSHans de Goede 
96354e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl,
96454e8bc5dSHans de Goede 			  sd->params.flickerControl.flickerMode,
96554e8bc5dSHans de Goede 			  sd->params.flickerControl.coarseJump,
96654e8bc5dSHans de Goede 			  sd->params.flickerControl.allowableOverExposure,
96754e8bc5dSHans de Goede 			  0);
96854e8bc5dSHans de Goede }
96954e8bc5dSHans de Goede 
970e7c3ee63SMárton Németh static int command_setecptiming(struct gspca_dev *gspca_dev)
97154e8bc5dSHans de Goede {
97254e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
97354e8bc5dSHans de Goede 
97454e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming,
97554e8bc5dSHans de Goede 			  sd->params.ecpTiming, 0, 0, 0);
97654e8bc5dSHans de Goede }
97754e8bc5dSHans de Goede 
978e7c3ee63SMárton Németh static int command_pause(struct gspca_dev *gspca_dev)
97954e8bc5dSHans de Goede {
98054e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
98154e8bc5dSHans de Goede }
98254e8bc5dSHans de Goede 
983e7c3ee63SMárton Németh static int command_resume(struct gspca_dev *gspca_dev)
98454e8bc5dSHans de Goede {
98554e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
98654e8bc5dSHans de Goede 
98754e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap,
98854e8bc5dSHans de Goede 			  0, sd->params.streamStartLine, 0, 0);
98954e8bc5dSHans de Goede }
99054e8bc5dSHans de Goede 
991e7c3ee63SMárton Németh static int command_setlights(struct gspca_dev *gspca_dev)
99254e8bc5dSHans de Goede {
99354e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
99454e8bc5dSHans de Goede 	int ret, p1, p2;
99554e8bc5dSHans de Goede 
99654e8bc5dSHans de Goede 	p1 = (sd->params.qx3.bottomlight == 0) << 1;
99754e8bc5dSHans de Goede 	p2 = (sd->params.qx3.toplight == 0) << 3;
99854e8bc5dSHans de Goede 
99954e8bc5dSHans de Goede 	ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg,
10001d00d6c1SJean-François Moine 			 0x90, 0x8f, 0x50, 0);
100154e8bc5dSHans de Goede 	if (ret)
100254e8bc5dSHans de Goede 		return ret;
100354e8bc5dSHans de Goede 
100454e8bc5dSHans de Goede 	return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0,
10051d00d6c1SJean-François Moine 			  p1 | p2 | 0xe0, 0);
100654e8bc5dSHans de Goede }
100754e8bc5dSHans de Goede 
100854e8bc5dSHans de Goede static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply)
100954e8bc5dSHans de Goede {
101054e8bc5dSHans de Goede 	/* Everything in here is from the Windows driver */
101154e8bc5dSHans de Goede /* define for compgain calculation */
101254e8bc5dSHans de Goede #if 0
101354e8bc5dSHans de Goede #define COMPGAIN(base, curexp, newexp) \
101454e8bc5dSHans de Goede     (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
101554e8bc5dSHans de Goede #define EXP_FROM_COMP(basecomp, curcomp, curexp) \
101654e8bc5dSHans de Goede     (u16)((float)curexp * (float)(u8)(curcomp + 128) / \
101754e8bc5dSHans de Goede     (float)(u8)(basecomp - 128))
101854e8bc5dSHans de Goede #else
101954e8bc5dSHans de Goede   /* equivalent functions without floating point math */
102054e8bc5dSHans de Goede #define COMPGAIN(base, curexp, newexp) \
102154e8bc5dSHans de Goede     (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp)))
102254e8bc5dSHans de Goede #define EXP_FROM_COMP(basecomp, curcomp, curexp) \
102354e8bc5dSHans de Goede     (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
102454e8bc5dSHans de Goede #endif
102554e8bc5dSHans de Goede 
102654e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
102754e8bc5dSHans de Goede 	int currentexp = sd->params.exposure.coarseExpLo +
102854e8bc5dSHans de Goede 			 sd->params.exposure.coarseExpHi * 256;
102954e8bc5dSHans de Goede 	int ret, startexp;
103054e8bc5dSHans de Goede 
103154e8bc5dSHans de Goede 	if (on) {
103254e8bc5dSHans de Goede 		int cj = sd->params.flickerControl.coarseJump;
103354e8bc5dSHans de Goede 		sd->params.flickerControl.flickerMode = 1;
103454e8bc5dSHans de Goede 		sd->params.flickerControl.disabled = 0;
103554e8bc5dSHans de Goede 		if (sd->params.exposure.expMode != 2) {
103654e8bc5dSHans de Goede 			sd->params.exposure.expMode = 2;
103754e8bc5dSHans de Goede 			sd->exposure_status = EXPOSURE_NORMAL;
103854e8bc5dSHans de Goede 		}
103954e8bc5dSHans de Goede 		currentexp = currentexp << sd->params.exposure.gain;
104054e8bc5dSHans de Goede 		sd->params.exposure.gain = 0;
104154e8bc5dSHans de Goede 		/* round down current exposure to nearest value */
104254e8bc5dSHans de Goede 		startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
104354e8bc5dSHans de Goede 		if (startexp < 1)
104454e8bc5dSHans de Goede 			startexp = 1;
104554e8bc5dSHans de Goede 		startexp = (startexp * cj) - 1;
104654e8bc5dSHans de Goede 		if (FIRMWARE_VERSION(1, 2))
104754e8bc5dSHans de Goede 			while (startexp > MAX_EXP_102)
104854e8bc5dSHans de Goede 				startexp -= cj;
104954e8bc5dSHans de Goede 		else
105054e8bc5dSHans de Goede 			while (startexp > MAX_EXP)
105154e8bc5dSHans de Goede 				startexp -= cj;
105254e8bc5dSHans de Goede 		sd->params.exposure.coarseExpLo = startexp & 0xff;
105354e8bc5dSHans de Goede 		sd->params.exposure.coarseExpHi = startexp >> 8;
105454e8bc5dSHans de Goede 		if (currentexp > startexp) {
105554e8bc5dSHans de Goede 			if (currentexp > (2 * startexp))
105654e8bc5dSHans de Goede 				currentexp = 2 * startexp;
105754e8bc5dSHans de Goede 			sd->params.exposure.redComp =
105854e8bc5dSHans de Goede 				COMPGAIN(COMP_RED, currentexp, startexp);
105954e8bc5dSHans de Goede 			sd->params.exposure.green1Comp =
106054e8bc5dSHans de Goede 				COMPGAIN(COMP_GREEN1, currentexp, startexp);
106154e8bc5dSHans de Goede 			sd->params.exposure.green2Comp =
106254e8bc5dSHans de Goede 				COMPGAIN(COMP_GREEN2, currentexp, startexp);
106354e8bc5dSHans de Goede 			sd->params.exposure.blueComp =
106454e8bc5dSHans de Goede 				COMPGAIN(COMP_BLUE, currentexp, startexp);
106554e8bc5dSHans de Goede 		} else {
106654e8bc5dSHans de Goede 			sd->params.exposure.redComp = COMP_RED;
106754e8bc5dSHans de Goede 			sd->params.exposure.green1Comp = COMP_GREEN1;
106854e8bc5dSHans de Goede 			sd->params.exposure.green2Comp = COMP_GREEN2;
106954e8bc5dSHans de Goede 			sd->params.exposure.blueComp = COMP_BLUE;
107054e8bc5dSHans de Goede 		}
107154e8bc5dSHans de Goede 		if (FIRMWARE_VERSION(1, 2))
107254e8bc5dSHans de Goede 			sd->params.exposure.compMode = 0;
107354e8bc5dSHans de Goede 		else
107454e8bc5dSHans de Goede 			sd->params.exposure.compMode = 1;
107554e8bc5dSHans de Goede 
107654e8bc5dSHans de Goede 		sd->params.apcor.gain1 = 0x18;
107754e8bc5dSHans de Goede 		sd->params.apcor.gain2 = 0x18;
107854e8bc5dSHans de Goede 		sd->params.apcor.gain4 = 0x16;
107954e8bc5dSHans de Goede 		sd->params.apcor.gain8 = 0x14;
108054e8bc5dSHans de Goede 	} else {
108154e8bc5dSHans de Goede 		sd->params.flickerControl.flickerMode = 0;
108254e8bc5dSHans de Goede 		sd->params.flickerControl.disabled = 1;
108354e8bc5dSHans de Goede 		/* Average equivalent coarse for each comp channel */
108454e8bc5dSHans de Goede 		startexp = EXP_FROM_COMP(COMP_RED,
108554e8bc5dSHans de Goede 				sd->params.exposure.redComp, currentexp);
108654e8bc5dSHans de Goede 		startexp += EXP_FROM_COMP(COMP_GREEN1,
108754e8bc5dSHans de Goede 				sd->params.exposure.green1Comp, currentexp);
108854e8bc5dSHans de Goede 		startexp += EXP_FROM_COMP(COMP_GREEN2,
108954e8bc5dSHans de Goede 				sd->params.exposure.green2Comp, currentexp);
109054e8bc5dSHans de Goede 		startexp += EXP_FROM_COMP(COMP_BLUE,
109154e8bc5dSHans de Goede 				sd->params.exposure.blueComp, currentexp);
109254e8bc5dSHans de Goede 		startexp = startexp >> 2;
109354e8bc5dSHans de Goede 		while (startexp > MAX_EXP && sd->params.exposure.gain <
109454e8bc5dSHans de Goede 		       sd->params.exposure.gainMode - 1) {
109554e8bc5dSHans de Goede 			startexp = startexp >> 1;
109654e8bc5dSHans de Goede 			++sd->params.exposure.gain;
109754e8bc5dSHans de Goede 		}
109854e8bc5dSHans de Goede 		if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102)
109954e8bc5dSHans de Goede 			startexp = MAX_EXP_102;
110054e8bc5dSHans de Goede 		if (startexp > MAX_EXP)
110154e8bc5dSHans de Goede 			startexp = MAX_EXP;
110254e8bc5dSHans de Goede 		sd->params.exposure.coarseExpLo = startexp & 0xff;
110354e8bc5dSHans de Goede 		sd->params.exposure.coarseExpHi = startexp >> 8;
110454e8bc5dSHans de Goede 		sd->params.exposure.redComp = COMP_RED;
110554e8bc5dSHans de Goede 		sd->params.exposure.green1Comp = COMP_GREEN1;
110654e8bc5dSHans de Goede 		sd->params.exposure.green2Comp = COMP_GREEN2;
110754e8bc5dSHans de Goede 		sd->params.exposure.blueComp = COMP_BLUE;
110854e8bc5dSHans de Goede 		sd->params.exposure.compMode = 1;
110954e8bc5dSHans de Goede 		sd->params.apcor.gain1 = 0x18;
111054e8bc5dSHans de Goede 		sd->params.apcor.gain2 = 0x16;
111154e8bc5dSHans de Goede 		sd->params.apcor.gain4 = 0x24;
111254e8bc5dSHans de Goede 		sd->params.apcor.gain8 = 0x34;
111354e8bc5dSHans de Goede 	}
111454e8bc5dSHans de Goede 	sd->params.vlOffset.gain1 = 20;
111554e8bc5dSHans de Goede 	sd->params.vlOffset.gain2 = 24;
111654e8bc5dSHans de Goede 	sd->params.vlOffset.gain4 = 26;
111754e8bc5dSHans de Goede 	sd->params.vlOffset.gain8 = 26;
111854e8bc5dSHans de Goede 
111954e8bc5dSHans de Goede 	if (apply) {
112054e8bc5dSHans de Goede 		ret = command_setexposure(gspca_dev);
112154e8bc5dSHans de Goede 		if (ret)
112254e8bc5dSHans de Goede 			return ret;
112354e8bc5dSHans de Goede 
112454e8bc5dSHans de Goede 		ret = command_setapcor(gspca_dev);
112554e8bc5dSHans de Goede 		if (ret)
112654e8bc5dSHans de Goede 			return ret;
112754e8bc5dSHans de Goede 
112854e8bc5dSHans de Goede 		ret = command_setvloffset(gspca_dev);
112954e8bc5dSHans de Goede 		if (ret)
113054e8bc5dSHans de Goede 			return ret;
113154e8bc5dSHans de Goede 
113254e8bc5dSHans de Goede 		ret = command_setflickerctrl(gspca_dev);
113354e8bc5dSHans de Goede 		if (ret)
113454e8bc5dSHans de Goede 			return ret;
113554e8bc5dSHans de Goede 	}
113654e8bc5dSHans de Goede 
113754e8bc5dSHans de Goede 	return 0;
113854e8bc5dSHans de Goede #undef EXP_FROM_COMP
113954e8bc5dSHans de Goede #undef COMPGAIN
114054e8bc5dSHans de Goede }
114154e8bc5dSHans de Goede 
114254e8bc5dSHans de Goede /* monitor the exposure and adjust the sensor frame rate if needed */
114354e8bc5dSHans de Goede static void monitor_exposure(struct gspca_dev *gspca_dev)
114454e8bc5dSHans de Goede {
114554e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
1146cc90b15eSJean-François Moine 	u8 exp_acc, bcomp, cmd[8];
114754e8bc5dSHans de Goede 	int ret, light_exp, dark_exp, very_dark_exp;
114854e8bc5dSHans de Goede 	int old_exposure, new_exposure, framerate;
114954e8bc5dSHans de Goede 	int setfps = 0, setexp = 0, setflicker = 0;
115054e8bc5dSHans de Goede 
115154e8bc5dSHans de Goede 	/* get necessary stats and register settings from camera */
115254e8bc5dSHans de Goede 	/* do_command can't handle this, so do it ourselves */
115354e8bc5dSHans de Goede 	cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8;
115454e8bc5dSHans de Goede 	cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff;
115554e8bc5dSHans de Goede 	cmd[2] = 30;
115654e8bc5dSHans de Goede 	cmd[3] = 4;
115754e8bc5dSHans de Goede 	cmd[4] = 9;
115854e8bc5dSHans de Goede 	cmd[5] = 8;
115954e8bc5dSHans de Goede 	cmd[6] = 8;
116054e8bc5dSHans de Goede 	cmd[7] = 0;
116154e8bc5dSHans de Goede 	ret = cpia_usb_transferCmd(gspca_dev, cmd);
116254e8bc5dSHans de Goede 	if (ret) {
1163133a9fe9SJoe Perches 		pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret);
116454e8bc5dSHans de Goede 		return;
116554e8bc5dSHans de Goede 	}
116654e8bc5dSHans de Goede 	exp_acc = gspca_dev->usb_buf[0];
116754e8bc5dSHans de Goede 	bcomp = gspca_dev->usb_buf[1];
116854e8bc5dSHans de Goede 
116954e8bc5dSHans de Goede 	light_exp = sd->params.colourParams.brightness +
117054e8bc5dSHans de Goede 		    TC - 50 + EXP_ACC_LIGHT;
117154e8bc5dSHans de Goede 	if (light_exp > 255)
117254e8bc5dSHans de Goede 		light_exp = 255;
117354e8bc5dSHans de Goede 	dark_exp = sd->params.colourParams.brightness +
117454e8bc5dSHans de Goede 		   TC - 50 - EXP_ACC_DARK;
117554e8bc5dSHans de Goede 	if (dark_exp < 0)
117654e8bc5dSHans de Goede 		dark_exp = 0;
117754e8bc5dSHans de Goede 	very_dark_exp = dark_exp / 2;
117854e8bc5dSHans de Goede 
117954e8bc5dSHans de Goede 	old_exposure = sd->params.exposure.coarseExpHi * 256 +
118054e8bc5dSHans de Goede 		       sd->params.exposure.coarseExpLo;
118154e8bc5dSHans de Goede 
118254e8bc5dSHans de Goede 	if (!sd->params.flickerControl.disabled) {
118354e8bc5dSHans de Goede 		/* Flicker control on */
118454e8bc5dSHans de Goede 		int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP :
118554e8bc5dSHans de Goede 							HIGH_COMP_102;
118654e8bc5dSHans de Goede 		bcomp += 128;	/* decode */
118754e8bc5dSHans de Goede 		if (bcomp >= max_comp && exp_acc < dark_exp) {
118854e8bc5dSHans de Goede 			/* dark */
118954e8bc5dSHans de Goede 			if (exp_acc < very_dark_exp) {
119054e8bc5dSHans de Goede 				/* very dark */
119154e8bc5dSHans de Goede 				if (sd->exposure_status == EXPOSURE_VERY_DARK)
119254e8bc5dSHans de Goede 					++sd->exposure_count;
119354e8bc5dSHans de Goede 				else {
119454e8bc5dSHans de Goede 					sd->exposure_status =
119554e8bc5dSHans de Goede 						EXPOSURE_VERY_DARK;
119654e8bc5dSHans de Goede 					sd->exposure_count = 1;
119754e8bc5dSHans de Goede 				}
119854e8bc5dSHans de Goede 			} else {
119954e8bc5dSHans de Goede 				/* just dark */
120054e8bc5dSHans de Goede 				if (sd->exposure_status == EXPOSURE_DARK)
120154e8bc5dSHans de Goede 					++sd->exposure_count;
120254e8bc5dSHans de Goede 				else {
120354e8bc5dSHans de Goede 					sd->exposure_status = EXPOSURE_DARK;
120454e8bc5dSHans de Goede 					sd->exposure_count = 1;
120554e8bc5dSHans de Goede 				}
120654e8bc5dSHans de Goede 			}
120754e8bc5dSHans de Goede 		} else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
120854e8bc5dSHans de Goede 			/* light */
120954e8bc5dSHans de Goede 			if (old_exposure <= VERY_LOW_EXP) {
121054e8bc5dSHans de Goede 				/* very light */
121154e8bc5dSHans de Goede 				if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
121254e8bc5dSHans de Goede 					++sd->exposure_count;
121354e8bc5dSHans de Goede 				else {
121454e8bc5dSHans de Goede 					sd->exposure_status =
121554e8bc5dSHans de Goede 						EXPOSURE_VERY_LIGHT;
121654e8bc5dSHans de Goede 					sd->exposure_count = 1;
121754e8bc5dSHans de Goede 				}
121854e8bc5dSHans de Goede 			} else {
121954e8bc5dSHans de Goede 				/* just light */
122054e8bc5dSHans de Goede 				if (sd->exposure_status == EXPOSURE_LIGHT)
122154e8bc5dSHans de Goede 					++sd->exposure_count;
122254e8bc5dSHans de Goede 				else {
122354e8bc5dSHans de Goede 					sd->exposure_status = EXPOSURE_LIGHT;
122454e8bc5dSHans de Goede 					sd->exposure_count = 1;
122554e8bc5dSHans de Goede 				}
122654e8bc5dSHans de Goede 			}
122754e8bc5dSHans de Goede 		} else {
122854e8bc5dSHans de Goede 			/* not dark or light */
122954e8bc5dSHans de Goede 			sd->exposure_status = EXPOSURE_NORMAL;
123054e8bc5dSHans de Goede 		}
123154e8bc5dSHans de Goede 	} else {
123254e8bc5dSHans de Goede 		/* Flicker control off */
123354e8bc5dSHans de Goede 		if (old_exposure >= MAX_EXP && exp_acc < dark_exp) {
123454e8bc5dSHans de Goede 			/* dark */
123554e8bc5dSHans de Goede 			if (exp_acc < very_dark_exp) {
123654e8bc5dSHans de Goede 				/* very dark */
123754e8bc5dSHans de Goede 				if (sd->exposure_status == EXPOSURE_VERY_DARK)
123854e8bc5dSHans de Goede 					++sd->exposure_count;
123954e8bc5dSHans de Goede 				else {
124054e8bc5dSHans de Goede 					sd->exposure_status =
124154e8bc5dSHans de Goede 						EXPOSURE_VERY_DARK;
124254e8bc5dSHans de Goede 					sd->exposure_count = 1;
124354e8bc5dSHans de Goede 				}
124454e8bc5dSHans de Goede 			} else {
124554e8bc5dSHans de Goede 				/* just dark */
124654e8bc5dSHans de Goede 				if (sd->exposure_status == EXPOSURE_DARK)
124754e8bc5dSHans de Goede 					++sd->exposure_count;
124854e8bc5dSHans de Goede 				else {
124954e8bc5dSHans de Goede 					sd->exposure_status = EXPOSURE_DARK;
125054e8bc5dSHans de Goede 					sd->exposure_count = 1;
125154e8bc5dSHans de Goede 				}
125254e8bc5dSHans de Goede 			}
125354e8bc5dSHans de Goede 		} else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
125454e8bc5dSHans de Goede 			/* light */
125554e8bc5dSHans de Goede 			if (old_exposure <= VERY_LOW_EXP) {
125654e8bc5dSHans de Goede 				/* very light */
125754e8bc5dSHans de Goede 				if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
125854e8bc5dSHans de Goede 					++sd->exposure_count;
125954e8bc5dSHans de Goede 				else {
126054e8bc5dSHans de Goede 					sd->exposure_status =
126154e8bc5dSHans de Goede 						EXPOSURE_VERY_LIGHT;
126254e8bc5dSHans de Goede 					sd->exposure_count = 1;
126354e8bc5dSHans de Goede 				}
126454e8bc5dSHans de Goede 			} else {
126554e8bc5dSHans de Goede 				/* just light */
126654e8bc5dSHans de Goede 				if (sd->exposure_status == EXPOSURE_LIGHT)
126754e8bc5dSHans de Goede 					++sd->exposure_count;
126854e8bc5dSHans de Goede 				else {
126954e8bc5dSHans de Goede 					sd->exposure_status = EXPOSURE_LIGHT;
127054e8bc5dSHans de Goede 					sd->exposure_count = 1;
127154e8bc5dSHans de Goede 				}
127254e8bc5dSHans de Goede 			}
127354e8bc5dSHans de Goede 		} else {
127454e8bc5dSHans de Goede 			/* not dark or light */
127554e8bc5dSHans de Goede 			sd->exposure_status = EXPOSURE_NORMAL;
127654e8bc5dSHans de Goede 		}
127754e8bc5dSHans de Goede 	}
127854e8bc5dSHans de Goede 
127954e8bc5dSHans de Goede 	framerate = atomic_read(&sd->fps);
128054e8bc5dSHans de Goede 	if (framerate > 30 || framerate < 1)
128154e8bc5dSHans de Goede 		framerate = 1;
128254e8bc5dSHans de Goede 
128354e8bc5dSHans de Goede 	if (!sd->params.flickerControl.disabled) {
128454e8bc5dSHans de Goede 		/* Flicker control on */
128554e8bc5dSHans de Goede 		if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
128654e8bc5dSHans de Goede 		     sd->exposure_status == EXPOSURE_DARK) &&
128754e8bc5dSHans de Goede 		    sd->exposure_count >= DARK_TIME * framerate &&
128876fafe78SHans de Goede 		    sd->params.sensorFps.divisor < 2) {
128954e8bc5dSHans de Goede 
129054e8bc5dSHans de Goede 			/* dark for too long */
129154e8bc5dSHans de Goede 			++sd->params.sensorFps.divisor;
129254e8bc5dSHans de Goede 			setfps = 1;
129354e8bc5dSHans de Goede 
129454e8bc5dSHans de Goede 			sd->params.flickerControl.coarseJump =
129554e8bc5dSHans de Goede 				flicker_jumps[sd->mainsFreq]
129654e8bc5dSHans de Goede 					     [sd->params.sensorFps.baserate]
129754e8bc5dSHans de Goede 					     [sd->params.sensorFps.divisor];
129854e8bc5dSHans de Goede 			setflicker = 1;
129954e8bc5dSHans de Goede 
130054e8bc5dSHans de Goede 			new_exposure = sd->params.flickerControl.coarseJump-1;
130154e8bc5dSHans de Goede 			while (new_exposure < old_exposure / 2)
130254e8bc5dSHans de Goede 				new_exposure +=
130354e8bc5dSHans de Goede 					sd->params.flickerControl.coarseJump;
130454e8bc5dSHans de Goede 			sd->params.exposure.coarseExpLo = new_exposure & 0xff;
130554e8bc5dSHans de Goede 			sd->params.exposure.coarseExpHi = new_exposure >> 8;
130654e8bc5dSHans de Goede 			setexp = 1;
130754e8bc5dSHans de Goede 			sd->exposure_status = EXPOSURE_NORMAL;
130854e8bc5dSHans de Goede 			PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
130954e8bc5dSHans de Goede 
131054e8bc5dSHans de Goede 		} else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
131154e8bc5dSHans de Goede 			    sd->exposure_status == EXPOSURE_LIGHT) &&
131254e8bc5dSHans de Goede 			   sd->exposure_count >= LIGHT_TIME * framerate &&
131354e8bc5dSHans de Goede 			   sd->params.sensorFps.divisor > 0) {
131454e8bc5dSHans de Goede 
131554e8bc5dSHans de Goede 			/* light for too long */
131654e8bc5dSHans de Goede 			int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 :
131754e8bc5dSHans de Goede 							       MAX_EXP;
131854e8bc5dSHans de Goede 			--sd->params.sensorFps.divisor;
131954e8bc5dSHans de Goede 			setfps = 1;
132054e8bc5dSHans de Goede 
132154e8bc5dSHans de Goede 			sd->params.flickerControl.coarseJump =
132254e8bc5dSHans de Goede 				flicker_jumps[sd->mainsFreq]
132354e8bc5dSHans de Goede 					     [sd->params.sensorFps.baserate]
132454e8bc5dSHans de Goede 					     [sd->params.sensorFps.divisor];
132554e8bc5dSHans de Goede 			setflicker = 1;
132654e8bc5dSHans de Goede 
132754e8bc5dSHans de Goede 			new_exposure = sd->params.flickerControl.coarseJump-1;
132854e8bc5dSHans de Goede 			while (new_exposure < 2 * old_exposure &&
132954e8bc5dSHans de Goede 			       new_exposure +
133054e8bc5dSHans de Goede 			       sd->params.flickerControl.coarseJump < max_exp)
133154e8bc5dSHans de Goede 				new_exposure +=
133254e8bc5dSHans de Goede 					sd->params.flickerControl.coarseJump;
133354e8bc5dSHans de Goede 			sd->params.exposure.coarseExpLo = new_exposure & 0xff;
133454e8bc5dSHans de Goede 			sd->params.exposure.coarseExpHi = new_exposure >> 8;
133554e8bc5dSHans de Goede 			setexp = 1;
133654e8bc5dSHans de Goede 			sd->exposure_status = EXPOSURE_NORMAL;
133754e8bc5dSHans de Goede 			PDEBUG(D_CONF, "Automatically increasing sensor_fps");
133854e8bc5dSHans de Goede 		}
133954e8bc5dSHans de Goede 	} else {
134054e8bc5dSHans de Goede 		/* Flicker control off */
134154e8bc5dSHans de Goede 		if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
134254e8bc5dSHans de Goede 		     sd->exposure_status == EXPOSURE_DARK) &&
134354e8bc5dSHans de Goede 		    sd->exposure_count >= DARK_TIME * framerate &&
134476fafe78SHans de Goede 		    sd->params.sensorFps.divisor < 2) {
134554e8bc5dSHans de Goede 
134654e8bc5dSHans de Goede 			/* dark for too long */
134754e8bc5dSHans de Goede 			++sd->params.sensorFps.divisor;
134854e8bc5dSHans de Goede 			setfps = 1;
134954e8bc5dSHans de Goede 
135054e8bc5dSHans de Goede 			if (sd->params.exposure.gain > 0) {
135154e8bc5dSHans de Goede 				--sd->params.exposure.gain;
135254e8bc5dSHans de Goede 				setexp = 1;
135354e8bc5dSHans de Goede 			}
135454e8bc5dSHans de Goede 			sd->exposure_status = EXPOSURE_NORMAL;
135554e8bc5dSHans de Goede 			PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
135654e8bc5dSHans de Goede 
135754e8bc5dSHans de Goede 		} else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
135854e8bc5dSHans de Goede 			    sd->exposure_status == EXPOSURE_LIGHT) &&
135954e8bc5dSHans de Goede 			   sd->exposure_count >= LIGHT_TIME * framerate &&
136054e8bc5dSHans de Goede 			   sd->params.sensorFps.divisor > 0) {
136154e8bc5dSHans de Goede 
136254e8bc5dSHans de Goede 			/* light for too long */
136354e8bc5dSHans de Goede 			--sd->params.sensorFps.divisor;
136454e8bc5dSHans de Goede 			setfps = 1;
136554e8bc5dSHans de Goede 
136654e8bc5dSHans de Goede 			if (sd->params.exposure.gain <
136754e8bc5dSHans de Goede 			    sd->params.exposure.gainMode - 1) {
136854e8bc5dSHans de Goede 				++sd->params.exposure.gain;
136954e8bc5dSHans de Goede 				setexp = 1;
137054e8bc5dSHans de Goede 			}
137154e8bc5dSHans de Goede 			sd->exposure_status = EXPOSURE_NORMAL;
137254e8bc5dSHans de Goede 			PDEBUG(D_CONF, "Automatically increasing sensor_fps");
137354e8bc5dSHans de Goede 		}
137454e8bc5dSHans de Goede 	}
137554e8bc5dSHans de Goede 
137654e8bc5dSHans de Goede 	if (setexp)
137754e8bc5dSHans de Goede 		command_setexposure(gspca_dev);
137854e8bc5dSHans de Goede 
137954e8bc5dSHans de Goede 	if (setfps)
138054e8bc5dSHans de Goede 		command_setsensorfps(gspca_dev);
138154e8bc5dSHans de Goede 
138254e8bc5dSHans de Goede 	if (setflicker)
138354e8bc5dSHans de Goede 		command_setflickerctrl(gspca_dev);
138454e8bc5dSHans de Goede }
138554e8bc5dSHans de Goede 
138654e8bc5dSHans de Goede /*-----------------------------------------------------------------*/
138754e8bc5dSHans de Goede /* if flicker is switched off, this function switches it back on.It checks,
138854e8bc5dSHans de Goede    however, that conditions are suitable before restarting it.
138954e8bc5dSHans de Goede    This should only be called for firmware version 1.2.
139054e8bc5dSHans de Goede 
139154e8bc5dSHans de Goede    It also adjust the colour balance when an exposure step is detected - as
139254e8bc5dSHans de Goede    long as flicker is running
139354e8bc5dSHans de Goede */
139454e8bc5dSHans de Goede static void restart_flicker(struct gspca_dev *gspca_dev)
139554e8bc5dSHans de Goede {
139654e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
139754e8bc5dSHans de Goede 	int cam_exposure, old_exp;
139854e8bc5dSHans de Goede 
139954e8bc5dSHans de Goede 	if (!FIRMWARE_VERSION(1, 2))
140054e8bc5dSHans de Goede 		return;
140154e8bc5dSHans de Goede 
140254e8bc5dSHans de Goede 	cam_exposure = atomic_read(&sd->cam_exposure);
140354e8bc5dSHans de Goede 
140454e8bc5dSHans de Goede 	if (sd->params.flickerControl.flickerMode == 0 ||
140554e8bc5dSHans de Goede 	    cam_exposure == 0)
140654e8bc5dSHans de Goede 		return;
140754e8bc5dSHans de Goede 
140854e8bc5dSHans de Goede 	old_exp = sd->params.exposure.coarseExpLo +
140954e8bc5dSHans de Goede 		  sd->params.exposure.coarseExpHi*256;
141054e8bc5dSHans de Goede 	/*
141154e8bc5dSHans de Goede 	  see how far away camera exposure is from a valid
141254e8bc5dSHans de Goede 	  flicker exposure value
141354e8bc5dSHans de Goede 	*/
141454e8bc5dSHans de Goede 	cam_exposure %= sd->params.flickerControl.coarseJump;
141554e8bc5dSHans de Goede 	if (!sd->params.flickerControl.disabled &&
141654e8bc5dSHans de Goede 	    cam_exposure <= sd->params.flickerControl.coarseJump - 3) {
141754e8bc5dSHans de Goede 		/* Flicker control auto-disabled */
141854e8bc5dSHans de Goede 		sd->params.flickerControl.disabled = 1;
141954e8bc5dSHans de Goede 	}
142054e8bc5dSHans de Goede 
142154e8bc5dSHans de Goede 	if (sd->params.flickerControl.disabled &&
142254e8bc5dSHans de Goede 	    old_exp > sd->params.flickerControl.coarseJump +
142354e8bc5dSHans de Goede 		      ROUND_UP_EXP_FOR_FLICKER) {
142454e8bc5dSHans de Goede 		/* exposure is now high enough to switch
142554e8bc5dSHans de Goede 		   flicker control back on */
142654e8bc5dSHans de Goede 		set_flicker(gspca_dev, 1, 1);
142754e8bc5dSHans de Goede 	}
142854e8bc5dSHans de Goede }
142954e8bc5dSHans de Goede 
143054e8bc5dSHans de Goede /* this function is called at probe time */
143154e8bc5dSHans de Goede static int sd_config(struct gspca_dev *gspca_dev,
143254e8bc5dSHans de Goede 			const struct usb_device_id *id)
143354e8bc5dSHans de Goede {
14341bfea3e4SHans Verkuil 	struct sd *sd = (struct sd *) gspca_dev;
143554e8bc5dSHans de Goede 	struct cam *cam;
143654e8bc5dSHans de Goede 
14371bfea3e4SHans Verkuil 	sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
143854e8bc5dSHans de Goede 	reset_camera_params(gspca_dev);
143954e8bc5dSHans de Goede 
144054e8bc5dSHans de Goede 	PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)",
144154e8bc5dSHans de Goede 	       id->idVendor, id->idProduct);
144254e8bc5dSHans de Goede 
144354e8bc5dSHans de Goede 	cam = &gspca_dev->cam;
144454e8bc5dSHans de Goede 	cam->cam_mode = mode;
144554e8bc5dSHans de Goede 	cam->nmodes = ARRAY_SIZE(mode);
144654e8bc5dSHans de Goede 
14471bfea3e4SHans Verkuil 	goto_low_power(gspca_dev);
14481bfea3e4SHans Verkuil 	/* Check the firmware version. */
14491bfea3e4SHans Verkuil 	sd->params.version.firmwareVersion = 0;
14501bfea3e4SHans Verkuil 	get_version_information(gspca_dev);
14511bfea3e4SHans Verkuil 	if (sd->params.version.firmwareVersion != 1) {
14521bfea3e4SHans Verkuil 		PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
14531bfea3e4SHans Verkuil 		       sd->params.version.firmwareVersion);
14541bfea3e4SHans Verkuil 		return -ENODEV;
14551bfea3e4SHans Verkuil 	}
145654e8bc5dSHans de Goede 
14571bfea3e4SHans Verkuil 	/* A bug in firmware 1-02 limits gainMode to 2 */
14581bfea3e4SHans Verkuil 	if (sd->params.version.firmwareRevision <= 2 &&
14591bfea3e4SHans Verkuil 	    sd->params.exposure.gainMode > 2) {
14601bfea3e4SHans Verkuil 		sd->params.exposure.gainMode = 2;
14611bfea3e4SHans Verkuil 	}
14621bfea3e4SHans Verkuil 
14631bfea3e4SHans Verkuil 	/* set QX3 detected flag */
14641bfea3e4SHans Verkuil 	sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
14651bfea3e4SHans Verkuil 				       sd->params.pnpID.product == 0x0001);
146654e8bc5dSHans de Goede 	return 0;
146754e8bc5dSHans de Goede }
146854e8bc5dSHans de Goede 
146954e8bc5dSHans de Goede /* -- start the camera -- */
147054e8bc5dSHans de Goede static int sd_start(struct gspca_dev *gspca_dev)
147154e8bc5dSHans de Goede {
147254e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
147354e8bc5dSHans de Goede 	int priv, ret;
147454e8bc5dSHans de Goede 
147554e8bc5dSHans de Goede 	/* Start the camera in low power mode */
147654e8bc5dSHans de Goede 	if (goto_low_power(gspca_dev)) {
147754e8bc5dSHans de Goede 		if (sd->params.status.systemState != WARM_BOOT_STATE) {
147854e8bc5dSHans de Goede 			PDEBUG(D_ERR, "unexpected systemstate: %02x",
147954e8bc5dSHans de Goede 			       sd->params.status.systemState);
148054e8bc5dSHans de Goede 			printstatus(&sd->params);
148154e8bc5dSHans de Goede 			return -ENODEV;
148254e8bc5dSHans de Goede 		}
148354e8bc5dSHans de Goede 
148454e8bc5dSHans de Goede 		/* FIXME: this is just dirty trial and error */
148554e8bc5dSHans de Goede 		ret = goto_high_power(gspca_dev);
148654e8bc5dSHans de Goede 		if (ret)
148754e8bc5dSHans de Goede 			return ret;
148854e8bc5dSHans de Goede 
148954e8bc5dSHans de Goede 		ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame,
149054e8bc5dSHans de Goede 				 0, 0, 0, 0);
149154e8bc5dSHans de Goede 		if (ret)
149254e8bc5dSHans de Goede 			return ret;
149354e8bc5dSHans de Goede 
149454e8bc5dSHans de Goede 		ret = goto_low_power(gspca_dev);
149554e8bc5dSHans de Goede 		if (ret)
149654e8bc5dSHans de Goede 			return ret;
149754e8bc5dSHans de Goede 	}
149854e8bc5dSHans de Goede 
149954e8bc5dSHans de Goede 	/* procedure described in developer's guide p3-28 */
150054e8bc5dSHans de Goede 
150154e8bc5dSHans de Goede 	/* Check the firmware version. */
150254e8bc5dSHans de Goede 	sd->params.version.firmwareVersion = 0;
150354e8bc5dSHans de Goede 	get_version_information(gspca_dev);
150454e8bc5dSHans de Goede 
150554e8bc5dSHans de Goede 	/* The fatal error checking should be done after
150654e8bc5dSHans de Goede 	 * the camera powers up (developer's guide p 3-38) */
150754e8bc5dSHans de Goede 
150854e8bc5dSHans de Goede 	/* Set streamState before transition to high power to avoid bug
150954e8bc5dSHans de Goede 	 * in firmware 1-02 */
151054e8bc5dSHans de Goede 	ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus,
151154e8bc5dSHans de Goede 			 STREAMSTATE, 0, STREAM_NOT_READY, 0);
151254e8bc5dSHans de Goede 	if (ret)
151354e8bc5dSHans de Goede 		return ret;
151454e8bc5dSHans de Goede 
151554e8bc5dSHans de Goede 	/* GotoHiPower */
151654e8bc5dSHans de Goede 	ret = goto_high_power(gspca_dev);
151754e8bc5dSHans de Goede 	if (ret)
151854e8bc5dSHans de Goede 		return ret;
151954e8bc5dSHans de Goede 
152054e8bc5dSHans de Goede 	/* Check the camera status */
152154e8bc5dSHans de Goede 	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
152254e8bc5dSHans de Goede 	if (ret)
152354e8bc5dSHans de Goede 		return ret;
152454e8bc5dSHans de Goede 
152554e8bc5dSHans de Goede 	if (sd->params.status.fatalError) {
152654e8bc5dSHans de Goede 		PDEBUG(D_ERR, "fatal_error: %04x, vp_status: %04x",
152754e8bc5dSHans de Goede 		       sd->params.status.fatalError,
152854e8bc5dSHans de Goede 		       sd->params.status.vpStatus);
152954e8bc5dSHans de Goede 		return -EIO;
153054e8bc5dSHans de Goede 	}
153154e8bc5dSHans de Goede 
153254e8bc5dSHans de Goede 	/* VPVersion can't be retrieved before the camera is in HiPower,
153354e8bc5dSHans de Goede 	 * so get it here instead of in get_version_information. */
153454e8bc5dSHans de Goede 	ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
153554e8bc5dSHans de Goede 	if (ret)
153654e8bc5dSHans de Goede 		return ret;
153754e8bc5dSHans de Goede 
153854e8bc5dSHans de Goede 	/* Determine video mode settings */
153954e8bc5dSHans de Goede 	sd->params.streamStartLine = 120;
154054e8bc5dSHans de Goede 
154154e8bc5dSHans de Goede 	priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
154254e8bc5dSHans de Goede 	if (priv & 0x01) { /* crop */
154354e8bc5dSHans de Goede 		sd->params.roi.colStart = 2;
154454e8bc5dSHans de Goede 		sd->params.roi.rowStart = 6;
154554e8bc5dSHans de Goede 	} else {
154654e8bc5dSHans de Goede 		sd->params.roi.colStart = 0;
154754e8bc5dSHans de Goede 		sd->params.roi.rowStart = 0;
154854e8bc5dSHans de Goede 	}
154954e8bc5dSHans de Goede 
155054e8bc5dSHans de Goede 	if (priv & 0x02) { /* quarter */
155154e8bc5dSHans de Goede 		sd->params.format.videoSize = VIDEOSIZE_QCIF;
155254e8bc5dSHans de Goede 		sd->params.roi.colStart /= 2;
155354e8bc5dSHans de Goede 		sd->params.roi.rowStart /= 2;
155454e8bc5dSHans de Goede 		sd->params.streamStartLine /= 2;
155554e8bc5dSHans de Goede 	} else
155654e8bc5dSHans de Goede 		sd->params.format.videoSize = VIDEOSIZE_CIF;
155754e8bc5dSHans de Goede 
155854e8bc5dSHans de Goede 	sd->params.roi.colEnd = sd->params.roi.colStart +
155954e8bc5dSHans de Goede 				(gspca_dev->width >> 3);
156054e8bc5dSHans de Goede 	sd->params.roi.rowEnd = sd->params.roi.rowStart +
156154e8bc5dSHans de Goede 				(gspca_dev->height >> 2);
156254e8bc5dSHans de Goede 
156354e8bc5dSHans de Goede 	/* And now set the camera to a known state */
156454e8bc5dSHans de Goede 	ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode,
156554e8bc5dSHans de Goede 			 CPIA_GRAB_CONTINEOUS, 0, 0, 0);
156654e8bc5dSHans de Goede 	if (ret)
156754e8bc5dSHans de Goede 		return ret;
156854e8bc5dSHans de Goede 	/* We start with compression disabled, as we need one uncompressed
156954e8bc5dSHans de Goede 	   frame to handle later compressed frames */
157054e8bc5dSHans de Goede 	ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression,
157154e8bc5dSHans de Goede 			 CPIA_COMPRESSION_NONE,
157254e8bc5dSHans de Goede 			 NO_DECIMATION, 0, 0);
157354e8bc5dSHans de Goede 	if (ret)
157454e8bc5dSHans de Goede 		return ret;
157554e8bc5dSHans de Goede 	ret = command_setcompressiontarget(gspca_dev);
157654e8bc5dSHans de Goede 	if (ret)
157754e8bc5dSHans de Goede 		return ret;
157854e8bc5dSHans de Goede 	ret = command_setcolourparams(gspca_dev);
157954e8bc5dSHans de Goede 	if (ret)
158054e8bc5dSHans de Goede 		return ret;
158154e8bc5dSHans de Goede 	ret = command_setformat(gspca_dev);
158254e8bc5dSHans de Goede 	if (ret)
158354e8bc5dSHans de Goede 		return ret;
158454e8bc5dSHans de Goede 	ret = command_setyuvtresh(gspca_dev);
158554e8bc5dSHans de Goede 	if (ret)
158654e8bc5dSHans de Goede 		return ret;
158754e8bc5dSHans de Goede 	ret = command_setecptiming(gspca_dev);
158854e8bc5dSHans de Goede 	if (ret)
158954e8bc5dSHans de Goede 		return ret;
159054e8bc5dSHans de Goede 	ret = command_setcompressionparams(gspca_dev);
159154e8bc5dSHans de Goede 	if (ret)
159254e8bc5dSHans de Goede 		return ret;
159354e8bc5dSHans de Goede 	ret = command_setexposure(gspca_dev);
159454e8bc5dSHans de Goede 	if (ret)
159554e8bc5dSHans de Goede 		return ret;
159654e8bc5dSHans de Goede 	ret = command_setcolourbalance(gspca_dev);
159754e8bc5dSHans de Goede 	if (ret)
159854e8bc5dSHans de Goede 		return ret;
159954e8bc5dSHans de Goede 	ret = command_setsensorfps(gspca_dev);
160054e8bc5dSHans de Goede 	if (ret)
160154e8bc5dSHans de Goede 		return ret;
160254e8bc5dSHans de Goede 	ret = command_setapcor(gspca_dev);
160354e8bc5dSHans de Goede 	if (ret)
160454e8bc5dSHans de Goede 		return ret;
160554e8bc5dSHans de Goede 	ret = command_setflickerctrl(gspca_dev);
160654e8bc5dSHans de Goede 	if (ret)
160754e8bc5dSHans de Goede 		return ret;
160854e8bc5dSHans de Goede 	ret = command_setvloffset(gspca_dev);
160954e8bc5dSHans de Goede 	if (ret)
161054e8bc5dSHans de Goede 		return ret;
161154e8bc5dSHans de Goede 
161254e8bc5dSHans de Goede 	/* Start stream */
161354e8bc5dSHans de Goede 	ret = command_resume(gspca_dev);
161454e8bc5dSHans de Goede 	if (ret)
161554e8bc5dSHans de Goede 		return ret;
161654e8bc5dSHans de Goede 
161754e8bc5dSHans de Goede 	/* Wait 6 frames before turning compression on for the sensor to get
161854e8bc5dSHans de Goede 	   all settings and AEC/ACB to settle */
161954e8bc5dSHans de Goede 	sd->first_frame = 6;
162054e8bc5dSHans de Goede 	sd->exposure_status = EXPOSURE_NORMAL;
162154e8bc5dSHans de Goede 	sd->exposure_count = 0;
162254e8bc5dSHans de Goede 	atomic_set(&sd->cam_exposure, 0);
162354e8bc5dSHans de Goede 	atomic_set(&sd->fps, 0);
162454e8bc5dSHans de Goede 
162554e8bc5dSHans de Goede 	return 0;
162654e8bc5dSHans de Goede }
162754e8bc5dSHans de Goede 
162854e8bc5dSHans de Goede static void sd_stopN(struct gspca_dev *gspca_dev)
162954e8bc5dSHans de Goede {
1630c2f644aeSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
1631c2f644aeSHans de Goede 
163254e8bc5dSHans de Goede 	command_pause(gspca_dev);
163354e8bc5dSHans de Goede 
163454e8bc5dSHans de Goede 	/* save camera state for later open (developers guide ch 3.5.3) */
163554e8bc5dSHans de Goede 	save_camera_state(gspca_dev);
163654e8bc5dSHans de Goede 
163754e8bc5dSHans de Goede 	/* GotoLoPower */
163854e8bc5dSHans de Goede 	goto_low_power(gspca_dev);
163954e8bc5dSHans de Goede 
164054e8bc5dSHans de Goede 	/* Update the camera status */
164154e8bc5dSHans de Goede 	do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
1642c2f644aeSHans de Goede 
1643c2f644aeSHans de Goede #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
1644c2f644aeSHans de Goede 	/* If the last button state is pressed, release it now! */
1645c2f644aeSHans de Goede 	if (sd->params.qx3.button) {
1646c2f644aeSHans de Goede 		/* The camera latch will hold the pressed state until we reset
1647c2f644aeSHans de Goede 		   the latch, so we do not reset sd->params.qx3.button now, to
1648c2f644aeSHans de Goede 		   avoid a false keypress being reported the next sd_start */
1649c2f644aeSHans de Goede 		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
1650c2f644aeSHans de Goede 		input_sync(gspca_dev->input_dev);
1651c2f644aeSHans de Goede 	}
1652c2f644aeSHans de Goede #endif
165354e8bc5dSHans de Goede }
165454e8bc5dSHans de Goede 
165554e8bc5dSHans de Goede /* this function is called at probe and resume time */
165654e8bc5dSHans de Goede static int sd_init(struct gspca_dev *gspca_dev)
165754e8bc5dSHans de Goede {
165854e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
165954e8bc5dSHans de Goede 	int ret;
166054e8bc5dSHans de Goede 
166154e8bc5dSHans de Goede 	/* Start / Stop the camera to make sure we are talking to
166254e8bc5dSHans de Goede 	   a supported camera, and to get some information from it
166354e8bc5dSHans de Goede 	   to print. */
166454e8bc5dSHans de Goede 	ret = sd_start(gspca_dev);
166554e8bc5dSHans de Goede 	if (ret)
166654e8bc5dSHans de Goede 		return ret;
166754e8bc5dSHans de Goede 
166847399d98SAndy Walls 	/* Ensure the QX3 illuminators' states are restored upon resume,
166947399d98SAndy Walls 	   or disable the illuminator controls, if this isn't a QX3 */
1670c67be3ccSAndy Walls 	if (sd->params.qx3.qx3_detected)
1671c67be3ccSAndy Walls 		command_setlights(gspca_dev);
1672c67be3ccSAndy Walls 
167354e8bc5dSHans de Goede 	sd_stopN(gspca_dev);
167454e8bc5dSHans de Goede 
167554e8bc5dSHans de Goede 	PDEBUG(D_PROBE, "CPIA Version:             %d.%02d (%d.%d)",
167654e8bc5dSHans de Goede 			sd->params.version.firmwareVersion,
167754e8bc5dSHans de Goede 			sd->params.version.firmwareRevision,
167854e8bc5dSHans de Goede 			sd->params.version.vcVersion,
167954e8bc5dSHans de Goede 			sd->params.version.vcRevision);
168054e8bc5dSHans de Goede 	PDEBUG(D_PROBE, "CPIA PnP-ID:              %04x:%04x:%04x",
168154e8bc5dSHans de Goede 			sd->params.pnpID.vendor, sd->params.pnpID.product,
168254e8bc5dSHans de Goede 			sd->params.pnpID.deviceRevision);
168354e8bc5dSHans de Goede 	PDEBUG(D_PROBE, "VP-Version:               %d.%d %04x",
168454e8bc5dSHans de Goede 			sd->params.vpVersion.vpVersion,
168554e8bc5dSHans de Goede 			sd->params.vpVersion.vpRevision,
168654e8bc5dSHans de Goede 			sd->params.vpVersion.cameraHeadID);
168754e8bc5dSHans de Goede 
168854e8bc5dSHans de Goede 	return 0;
168954e8bc5dSHans de Goede }
169054e8bc5dSHans de Goede 
169154e8bc5dSHans de Goede static void sd_pkt_scan(struct gspca_dev *gspca_dev,
169254e8bc5dSHans de Goede 			u8 *data,
169354e8bc5dSHans de Goede 			int len)
169454e8bc5dSHans de Goede {
169554e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
169654e8bc5dSHans de Goede 
169754e8bc5dSHans de Goede 	/* Check for SOF */
169854e8bc5dSHans de Goede 	if (len >= 64 &&
169954e8bc5dSHans de Goede 	    data[0] == MAGIC_0 && data[1] == MAGIC_1 &&
170054e8bc5dSHans de Goede 	    data[16] == sd->params.format.videoSize &&
170154e8bc5dSHans de Goede 	    data[17] == sd->params.format.subSample &&
170254e8bc5dSHans de Goede 	    data[18] == sd->params.format.yuvOrder &&
170354e8bc5dSHans de Goede 	    data[24] == sd->params.roi.colStart &&
170454e8bc5dSHans de Goede 	    data[25] == sd->params.roi.colEnd &&
170554e8bc5dSHans de Goede 	    data[26] == sd->params.roi.rowStart &&
170654e8bc5dSHans de Goede 	    data[27] == sd->params.roi.rowEnd) {
1707b192ca98SJean-François Moine 		u8 *image;
170854e8bc5dSHans de Goede 
170954e8bc5dSHans de Goede 		atomic_set(&sd->cam_exposure, data[39] * 2);
171054e8bc5dSHans de Goede 		atomic_set(&sd->fps, data[41]);
171154e8bc5dSHans de Goede 
171254e8bc5dSHans de Goede 		/* Check for proper EOF for last frame */
1713f7059eaaSJean-François Moine 		image = gspca_dev->image;
1714f7059eaaSJean-François Moine 		if (image != NULL &&
1715f7059eaaSJean-François Moine 		    gspca_dev->image_len > 4 &&
1716b192ca98SJean-François Moine 		    image[gspca_dev->image_len - 4] == 0xff &&
1717b192ca98SJean-François Moine 		    image[gspca_dev->image_len - 3] == 0xff &&
1718b192ca98SJean-François Moine 		    image[gspca_dev->image_len - 2] == 0xff &&
1719b192ca98SJean-François Moine 		    image[gspca_dev->image_len - 1] == 0xff)
172054e8bc5dSHans de Goede 			gspca_frame_add(gspca_dev, LAST_PACKET,
172154e8bc5dSHans de Goede 						NULL, 0);
172254e8bc5dSHans de Goede 
172354e8bc5dSHans de Goede 		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
172454e8bc5dSHans de Goede 		return;
172554e8bc5dSHans de Goede 	}
172654e8bc5dSHans de Goede 
172754e8bc5dSHans de Goede 	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
172854e8bc5dSHans de Goede }
172954e8bc5dSHans de Goede 
173054e8bc5dSHans de Goede static void sd_dq_callback(struct gspca_dev *gspca_dev)
173154e8bc5dSHans de Goede {
173254e8bc5dSHans de Goede 	struct sd *sd = (struct sd *) gspca_dev;
173354e8bc5dSHans de Goede 
173454e8bc5dSHans de Goede 	/* Set the normal compression settings once we have captured a
173554e8bc5dSHans de Goede 	   few uncompressed frames (and AEC has hopefully settled) */
173654e8bc5dSHans de Goede 	if (sd->first_frame) {
173754e8bc5dSHans de Goede 		sd->first_frame--;
173854e8bc5dSHans de Goede 		if (sd->first_frame == 0)
173954e8bc5dSHans de Goede 			command_setcompression(gspca_dev);
174054e8bc5dSHans de Goede 	}
174154e8bc5dSHans de Goede 
174254e8bc5dSHans de Goede 	/* Switch flicker control back on if it got turned off */
174354e8bc5dSHans de Goede 	restart_flicker(gspca_dev);
174454e8bc5dSHans de Goede 
174554e8bc5dSHans de Goede 	/* If AEC is enabled, monitor the exposure and
174654e8bc5dSHans de Goede 	   adjust the sensor frame rate if needed */
174754e8bc5dSHans de Goede 	if (sd->params.exposure.expMode == 2)
174854e8bc5dSHans de Goede 		monitor_exposure(gspca_dev);
174954e8bc5dSHans de Goede 
175054e8bc5dSHans de Goede 	/* Update our knowledge of the camera state */
175154e8bc5dSHans de Goede 	do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
175254e8bc5dSHans de Goede 	do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
175354e8bc5dSHans de Goede }
175454e8bc5dSHans de Goede 
17551bfea3e4SHans Verkuil static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
175654e8bc5dSHans de Goede {
17571bfea3e4SHans Verkuil 	struct gspca_dev *gspca_dev =
17581bfea3e4SHans Verkuil 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
175954e8bc5dSHans de Goede 	struct sd *sd = (struct sd *)gspca_dev;
176054e8bc5dSHans de Goede 
17611bfea3e4SHans Verkuil 	gspca_dev->usb_err = 0;
17621bfea3e4SHans Verkuil 
17631bfea3e4SHans Verkuil 	if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
17641bfea3e4SHans Verkuil 		return 0;
17651bfea3e4SHans Verkuil 
17661bfea3e4SHans Verkuil 	switch (ctrl->id) {
17671bfea3e4SHans Verkuil 	case V4L2_CID_BRIGHTNESS:
17681bfea3e4SHans Verkuil 		sd->params.colourParams.brightness = ctrl->val;
176954e8bc5dSHans de Goede 		sd->params.flickerControl.allowableOverExposure =
177054e8bc5dSHans de Goede 			find_over_exposure(sd->params.colourParams.brightness);
17711bfea3e4SHans Verkuil 		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
17721bfea3e4SHans Verkuil 		if (!gspca_dev->usb_err)
17731bfea3e4SHans Verkuil 			gspca_dev->usb_err = command_setflickerctrl(gspca_dev);
177454e8bc5dSHans de Goede 		break;
17751bfea3e4SHans Verkuil 	case V4L2_CID_CONTRAST:
17761bfea3e4SHans Verkuil 		sd->params.colourParams.contrast = ctrl->val;
17771bfea3e4SHans Verkuil 		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
177854e8bc5dSHans de Goede 		break;
17791bfea3e4SHans Verkuil 	case V4L2_CID_SATURATION:
17801bfea3e4SHans Verkuil 		sd->params.colourParams.saturation = ctrl->val;
17811bfea3e4SHans Verkuil 		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
178254e8bc5dSHans de Goede 		break;
17831bfea3e4SHans Verkuil 	case V4L2_CID_POWER_LINE_FREQUENCY:
17841bfea3e4SHans Verkuil 		sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
178554e8bc5dSHans de Goede 		sd->params.flickerControl.coarseJump =
178654e8bc5dSHans de Goede 			flicker_jumps[sd->mainsFreq]
178754e8bc5dSHans de Goede 			[sd->params.sensorFps.baserate]
178854e8bc5dSHans de Goede 			[sd->params.sensorFps.divisor];
178954e8bc5dSHans de Goede 
17901bfea3e4SHans Verkuil 		gspca_dev->usb_err = set_flicker(gspca_dev,
17911bfea3e4SHans Verkuil 			ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
17921bfea3e4SHans Verkuil 			gspca_dev->streaming);
17931bfea3e4SHans Verkuil 		break;
17941bfea3e4SHans Verkuil 	case V4L2_CID_ILLUMINATORS_1:
17951bfea3e4SHans Verkuil 		sd->params.qx3.bottomlight = ctrl->val;
17961bfea3e4SHans Verkuil 		gspca_dev->usb_err = command_setlights(gspca_dev);
17971bfea3e4SHans Verkuil 		break;
17981bfea3e4SHans Verkuil 	case V4L2_CID_ILLUMINATORS_2:
17991bfea3e4SHans Verkuil 		sd->params.qx3.toplight = ctrl->val;
18001bfea3e4SHans Verkuil 		gspca_dev->usb_err = command_setlights(gspca_dev);
18011bfea3e4SHans Verkuil 		break;
18021bfea3e4SHans Verkuil 	case CPIA1_CID_COMP_TARGET:
18031bfea3e4SHans Verkuil 		sd->params.compressionTarget.frTargeting = ctrl->val;
18041bfea3e4SHans Verkuil 		gspca_dev->usb_err = command_setcompressiontarget(gspca_dev);
18051bfea3e4SHans Verkuil 		break;
18061bfea3e4SHans Verkuil 	}
18071bfea3e4SHans Verkuil 	return gspca_dev->usb_err;
180854e8bc5dSHans de Goede }
180954e8bc5dSHans de Goede 
18101bfea3e4SHans Verkuil static const struct v4l2_ctrl_ops sd_ctrl_ops = {
18111bfea3e4SHans Verkuil 	.s_ctrl = sd_s_ctrl,
18121bfea3e4SHans Verkuil };
18131bfea3e4SHans Verkuil 
18141bfea3e4SHans Verkuil static int sd_init_controls(struct gspca_dev *gspca_dev)
181554e8bc5dSHans de Goede {
181654e8bc5dSHans de Goede 	struct sd *sd = (struct sd *)gspca_dev;
18171bfea3e4SHans Verkuil 	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
18181bfea3e4SHans Verkuil 	static const char * const comp_target_menu[] = {
18191bfea3e4SHans Verkuil 		"Quality",
18201bfea3e4SHans Verkuil 		"Framerate",
18211bfea3e4SHans Verkuil 		NULL
18221bfea3e4SHans Verkuil 	};
18231bfea3e4SHans Verkuil 	static const struct v4l2_ctrl_config comp_target = {
18241bfea3e4SHans Verkuil 		.ops = &sd_ctrl_ops,
18251bfea3e4SHans Verkuil 		.id = CPIA1_CID_COMP_TARGET,
18261bfea3e4SHans Verkuil 		.type = V4L2_CTRL_TYPE_MENU,
18271bfea3e4SHans Verkuil 		.name = "Compression Target",
18281bfea3e4SHans Verkuil 		.qmenu = comp_target_menu,
18291bfea3e4SHans Verkuil 		.max = 1,
18301bfea3e4SHans Verkuil 		.def = COMP_TARGET_DEF,
18311bfea3e4SHans Verkuil 	};
183254e8bc5dSHans de Goede 
18331bfea3e4SHans Verkuil 	gspca_dev->vdev.ctrl_handler = hdl;
18341bfea3e4SHans Verkuil 	v4l2_ctrl_handler_init(hdl, 7);
18351bfea3e4SHans Verkuil 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
18361bfea3e4SHans Verkuil 			V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF);
18371bfea3e4SHans Verkuil 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
18381bfea3e4SHans Verkuil 			V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF);
18391bfea3e4SHans Verkuil 	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
18401bfea3e4SHans Verkuil 			V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF);
18411bfea3e4SHans Verkuil 	sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
18421bfea3e4SHans Verkuil 			V4L2_CID_POWER_LINE_FREQUENCY,
18431bfea3e4SHans Verkuil 			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
18441bfea3e4SHans Verkuil 			FREQ_DEF);
18451bfea3e4SHans Verkuil 	if (sd->params.qx3.qx3_detected) {
18461bfea3e4SHans Verkuil 		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
18471bfea3e4SHans Verkuil 				V4L2_CID_ILLUMINATORS_1, 0, 1, 1,
18481bfea3e4SHans Verkuil 				ILLUMINATORS_1_DEF);
18491bfea3e4SHans Verkuil 		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
18501bfea3e4SHans Verkuil 				V4L2_CID_ILLUMINATORS_2, 0, 1, 1,
18511bfea3e4SHans Verkuil 				ILLUMINATORS_2_DEF);
185254e8bc5dSHans de Goede 	}
18531bfea3e4SHans Verkuil 	v4l2_ctrl_new_custom(hdl, &comp_target, NULL);
185454e8bc5dSHans de Goede 
18551bfea3e4SHans Verkuil 	if (hdl->error) {
18561bfea3e4SHans Verkuil 		pr_err("Could not initialize controls\n");
18571bfea3e4SHans Verkuil 		return hdl->error;
185851513353SAndy Walls 	}
185951513353SAndy Walls 	return 0;
186051513353SAndy Walls }
186151513353SAndy Walls 
186254e8bc5dSHans de Goede /* sub-driver description */
186354e8bc5dSHans de Goede static const struct sd_desc sd_desc = {
186454e8bc5dSHans de Goede 	.name = MODULE_NAME,
186554e8bc5dSHans de Goede 	.config = sd_config,
186654e8bc5dSHans de Goede 	.init = sd_init,
18671bfea3e4SHans Verkuil 	.init_controls = sd_init_controls,
186854e8bc5dSHans de Goede 	.start = sd_start,
186954e8bc5dSHans de Goede 	.stopN = sd_stopN,
187054e8bc5dSHans de Goede 	.dq_callback = sd_dq_callback,
187154e8bc5dSHans de Goede 	.pkt_scan = sd_pkt_scan,
1872c2f644aeSHans de Goede #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
1873c2f644aeSHans de Goede 	.other_input = 1,
1874c2f644aeSHans de Goede #endif
187554e8bc5dSHans de Goede };
187654e8bc5dSHans de Goede 
187754e8bc5dSHans de Goede /* -- module initialisation -- */
187895c967c1SJean-François Moine static const struct usb_device_id device_table[] = {
187954e8bc5dSHans de Goede 	{USB_DEVICE(0x0553, 0x0002)},
188054e8bc5dSHans de Goede 	{USB_DEVICE(0x0813, 0x0001)},
188154e8bc5dSHans de Goede 	{}
188254e8bc5dSHans de Goede };
188354e8bc5dSHans de Goede MODULE_DEVICE_TABLE(usb, device_table);
188454e8bc5dSHans de Goede 
188554e8bc5dSHans de Goede /* -- device connect -- */
188654e8bc5dSHans de Goede static int sd_probe(struct usb_interface *intf,
188754e8bc5dSHans de Goede 			const struct usb_device_id *id)
188854e8bc5dSHans de Goede {
188954e8bc5dSHans de Goede 	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
189054e8bc5dSHans de Goede 				THIS_MODULE);
189154e8bc5dSHans de Goede }
189254e8bc5dSHans de Goede 
189354e8bc5dSHans de Goede static struct usb_driver sd_driver = {
189454e8bc5dSHans de Goede 	.name = MODULE_NAME,
189554e8bc5dSHans de Goede 	.id_table = device_table,
189654e8bc5dSHans de Goede 	.probe = sd_probe,
189754e8bc5dSHans de Goede 	.disconnect = gspca_disconnect,
189854e8bc5dSHans de Goede #ifdef CONFIG_PM
189954e8bc5dSHans de Goede 	.suspend = gspca_suspend,
190054e8bc5dSHans de Goede 	.resume = gspca_resume,
19018bb58964SHans de Goede 	.reset_resume = gspca_resume,
190254e8bc5dSHans de Goede #endif
190354e8bc5dSHans de Goede };
190454e8bc5dSHans de Goede 
1905ecb3b2b3SGreg Kroah-Hartman module_usb_driver(sd_driver);
1906