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