162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * cpia CPiA (1) gspca driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This module is adapted from the in kernel v4l1 cpia driver which is :
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * (C) Copyright 1999-2000 Peter Pregler
1062306a36Sopenharmony_ci * (C) Copyright 1999-2000 Scott J. Bertin
1162306a36Sopenharmony_ci * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
1262306a36Sopenharmony_ci * (C) Copyright 2000 STMicroelectronics
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define MODULE_NAME "cpia1"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/input.h>
2062306a36Sopenharmony_ci#include <linux/sched/signal.h>
2162306a36Sopenharmony_ci#include <linux/bitops.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "gspca.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
2662306a36Sopenharmony_ciMODULE_DESCRIPTION("Vision CPiA");
2762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* constant value's */
3062306a36Sopenharmony_ci#define MAGIC_0		0x19
3162306a36Sopenharmony_ci#define MAGIC_1		0x68
3262306a36Sopenharmony_ci#define DATA_IN		0xc0
3362306a36Sopenharmony_ci#define DATA_OUT	0x40
3462306a36Sopenharmony_ci#define VIDEOSIZE_QCIF	0	/* 176x144 */
3562306a36Sopenharmony_ci#define VIDEOSIZE_CIF	1	/* 352x288 */
3662306a36Sopenharmony_ci#define SUBSAMPLE_420	0
3762306a36Sopenharmony_ci#define SUBSAMPLE_422	1
3862306a36Sopenharmony_ci#define YUVORDER_YUYV	0
3962306a36Sopenharmony_ci#define YUVORDER_UYVY	1
4062306a36Sopenharmony_ci#define NOT_COMPRESSED	0
4162306a36Sopenharmony_ci#define COMPRESSED	1
4262306a36Sopenharmony_ci#define NO_DECIMATION	0
4362306a36Sopenharmony_ci#define DECIMATION_ENAB	1
4462306a36Sopenharmony_ci#define EOI		0xff	/* End Of Image */
4562306a36Sopenharmony_ci#define EOL		0xfd	/* End Of Line */
4662306a36Sopenharmony_ci#define FRAME_HEADER_SIZE	64
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Image grab modes */
4962306a36Sopenharmony_ci#define CPIA_GRAB_SINGLE	0
5062306a36Sopenharmony_ci#define CPIA_GRAB_CONTINEOUS	1
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* Compression parameters */
5362306a36Sopenharmony_ci#define CPIA_COMPRESSION_NONE	0
5462306a36Sopenharmony_ci#define CPIA_COMPRESSION_AUTO	1
5562306a36Sopenharmony_ci#define CPIA_COMPRESSION_MANUAL	2
5662306a36Sopenharmony_ci#define CPIA_COMPRESSION_TARGET_QUALITY         0
5762306a36Sopenharmony_ci#define CPIA_COMPRESSION_TARGET_FRAMERATE       1
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Return offsets for GetCameraState */
6062306a36Sopenharmony_ci#define SYSTEMSTATE	0
6162306a36Sopenharmony_ci#define GRABSTATE	1
6262306a36Sopenharmony_ci#define STREAMSTATE	2
6362306a36Sopenharmony_ci#define FATALERROR	3
6462306a36Sopenharmony_ci#define CMDERROR	4
6562306a36Sopenharmony_ci#define DEBUGFLAGS	5
6662306a36Sopenharmony_ci#define VPSTATUS	6
6762306a36Sopenharmony_ci#define ERRORCODE	7
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* SystemState */
7062306a36Sopenharmony_ci#define UNINITIALISED_STATE	0
7162306a36Sopenharmony_ci#define PASS_THROUGH_STATE	1
7262306a36Sopenharmony_ci#define LO_POWER_STATE		2
7362306a36Sopenharmony_ci#define HI_POWER_STATE		3
7462306a36Sopenharmony_ci#define WARM_BOOT_STATE		4
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* GrabState */
7762306a36Sopenharmony_ci#define GRAB_IDLE		0
7862306a36Sopenharmony_ci#define GRAB_ACTIVE		1
7962306a36Sopenharmony_ci#define GRAB_DONE		2
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/* StreamState */
8262306a36Sopenharmony_ci#define STREAM_NOT_READY	0
8362306a36Sopenharmony_ci#define STREAM_READY		1
8462306a36Sopenharmony_ci#define STREAM_OPEN		2
8562306a36Sopenharmony_ci#define STREAM_PAUSED		3
8662306a36Sopenharmony_ci#define STREAM_FINISHED		4
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* Fatal Error, CmdError, and DebugFlags */
8962306a36Sopenharmony_ci#define CPIA_FLAG	  1
9062306a36Sopenharmony_ci#define SYSTEM_FLAG	  2
9162306a36Sopenharmony_ci#define INT_CTRL_FLAG	  4
9262306a36Sopenharmony_ci#define PROCESS_FLAG	  8
9362306a36Sopenharmony_ci#define COM_FLAG	 16
9462306a36Sopenharmony_ci#define VP_CTRL_FLAG	 32
9562306a36Sopenharmony_ci#define CAPTURE_FLAG	 64
9662306a36Sopenharmony_ci#define DEBUG_FLAG	128
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* VPStatus */
9962306a36Sopenharmony_ci#define VP_STATE_OK			0x00
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define VP_STATE_FAILED_VIDEOINIT	0x01
10262306a36Sopenharmony_ci#define VP_STATE_FAILED_AECACBINIT	0x02
10362306a36Sopenharmony_ci#define VP_STATE_AEC_MAX		0x04
10462306a36Sopenharmony_ci#define VP_STATE_ACB_BMAX		0x08
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#define VP_STATE_ACB_RMIN		0x10
10762306a36Sopenharmony_ci#define VP_STATE_ACB_GMIN		0x20
10862306a36Sopenharmony_ci#define VP_STATE_ACB_RMAX		0x40
10962306a36Sopenharmony_ci#define VP_STATE_ACB_GMAX		0x80
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* default (minimum) compensation values */
11262306a36Sopenharmony_ci#define COMP_RED        220
11362306a36Sopenharmony_ci#define COMP_GREEN1     214
11462306a36Sopenharmony_ci#define COMP_GREEN2     COMP_GREEN1
11562306a36Sopenharmony_ci#define COMP_BLUE       230
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/* exposure status */
11862306a36Sopenharmony_ci#define EXPOSURE_VERY_LIGHT 0
11962306a36Sopenharmony_ci#define EXPOSURE_LIGHT      1
12062306a36Sopenharmony_ci#define EXPOSURE_NORMAL     2
12162306a36Sopenharmony_ci#define EXPOSURE_DARK       3
12262306a36Sopenharmony_ci#define EXPOSURE_VERY_DARK  4
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define CPIA_MODULE_CPIA			(0 << 5)
12562306a36Sopenharmony_ci#define CPIA_MODULE_SYSTEM			(1 << 5)
12662306a36Sopenharmony_ci#define CPIA_MODULE_VP_CTRL			(5 << 5)
12762306a36Sopenharmony_ci#define CPIA_MODULE_CAPTURE			(6 << 5)
12862306a36Sopenharmony_ci#define CPIA_MODULE_DEBUG			(7 << 5)
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define INPUT (DATA_IN << 8)
13162306a36Sopenharmony_ci#define OUTPUT (DATA_OUT << 8)
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#define CPIA_COMMAND_GetCPIAVersion	(INPUT | CPIA_MODULE_CPIA | 1)
13462306a36Sopenharmony_ci#define CPIA_COMMAND_GetPnPID		(INPUT | CPIA_MODULE_CPIA | 2)
13562306a36Sopenharmony_ci#define CPIA_COMMAND_GetCameraStatus	(INPUT | CPIA_MODULE_CPIA | 3)
13662306a36Sopenharmony_ci#define CPIA_COMMAND_GotoHiPower	(OUTPUT | CPIA_MODULE_CPIA | 4)
13762306a36Sopenharmony_ci#define CPIA_COMMAND_GotoLoPower	(OUTPUT | CPIA_MODULE_CPIA | 5)
13862306a36Sopenharmony_ci#define CPIA_COMMAND_GotoSuspend	(OUTPUT | CPIA_MODULE_CPIA | 7)
13962306a36Sopenharmony_ci#define CPIA_COMMAND_GotoPassThrough	(OUTPUT | CPIA_MODULE_CPIA | 8)
14062306a36Sopenharmony_ci#define CPIA_COMMAND_ModifyCameraStatus	(OUTPUT | CPIA_MODULE_CPIA | 10)
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci#define CPIA_COMMAND_ReadVCRegs		(INPUT | CPIA_MODULE_SYSTEM | 1)
14362306a36Sopenharmony_ci#define CPIA_COMMAND_WriteVCReg		(OUTPUT | CPIA_MODULE_SYSTEM | 2)
14462306a36Sopenharmony_ci#define CPIA_COMMAND_ReadMCPorts	(INPUT | CPIA_MODULE_SYSTEM | 3)
14562306a36Sopenharmony_ci#define CPIA_COMMAND_WriteMCPort	(OUTPUT | CPIA_MODULE_SYSTEM | 4)
14662306a36Sopenharmony_ci#define CPIA_COMMAND_SetBaudRate	(OUTPUT | CPIA_MODULE_SYSTEM | 5)
14762306a36Sopenharmony_ci#define CPIA_COMMAND_SetECPTiming	(OUTPUT | CPIA_MODULE_SYSTEM | 6)
14862306a36Sopenharmony_ci#define CPIA_COMMAND_ReadIDATA		(INPUT | CPIA_MODULE_SYSTEM | 7)
14962306a36Sopenharmony_ci#define CPIA_COMMAND_WriteIDATA		(OUTPUT | CPIA_MODULE_SYSTEM | 8)
15062306a36Sopenharmony_ci#define CPIA_COMMAND_GenericCall	(OUTPUT | CPIA_MODULE_SYSTEM | 9)
15162306a36Sopenharmony_ci#define CPIA_COMMAND_I2CStart		(OUTPUT | CPIA_MODULE_SYSTEM | 10)
15262306a36Sopenharmony_ci#define CPIA_COMMAND_I2CStop		(OUTPUT | CPIA_MODULE_SYSTEM | 11)
15362306a36Sopenharmony_ci#define CPIA_COMMAND_I2CWrite		(OUTPUT | CPIA_MODULE_SYSTEM | 12)
15462306a36Sopenharmony_ci#define CPIA_COMMAND_I2CRead		(INPUT | CPIA_MODULE_SYSTEM | 13)
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci#define CPIA_COMMAND_GetVPVersion	(INPUT | CPIA_MODULE_VP_CTRL | 1)
15762306a36Sopenharmony_ci#define CPIA_COMMAND_ResetFrameCounter	(INPUT | CPIA_MODULE_VP_CTRL | 2)
15862306a36Sopenharmony_ci#define CPIA_COMMAND_SetColourParams	(OUTPUT | CPIA_MODULE_VP_CTRL | 3)
15962306a36Sopenharmony_ci#define CPIA_COMMAND_SetExposure	(OUTPUT | CPIA_MODULE_VP_CTRL | 4)
16062306a36Sopenharmony_ci#define CPIA_COMMAND_SetColourBalance	(OUTPUT | CPIA_MODULE_VP_CTRL | 6)
16162306a36Sopenharmony_ci#define CPIA_COMMAND_SetSensorFPS	(OUTPUT | CPIA_MODULE_VP_CTRL | 7)
16262306a36Sopenharmony_ci#define CPIA_COMMAND_SetVPDefaults	(OUTPUT | CPIA_MODULE_VP_CTRL | 8)
16362306a36Sopenharmony_ci#define CPIA_COMMAND_SetApcor		(OUTPUT | CPIA_MODULE_VP_CTRL | 9)
16462306a36Sopenharmony_ci#define CPIA_COMMAND_SetFlickerCtrl	(OUTPUT | CPIA_MODULE_VP_CTRL | 10)
16562306a36Sopenharmony_ci#define CPIA_COMMAND_SetVLOffset	(OUTPUT | CPIA_MODULE_VP_CTRL | 11)
16662306a36Sopenharmony_ci#define CPIA_COMMAND_GetColourParams	(INPUT | CPIA_MODULE_VP_CTRL | 16)
16762306a36Sopenharmony_ci#define CPIA_COMMAND_GetColourBalance	(INPUT | CPIA_MODULE_VP_CTRL | 17)
16862306a36Sopenharmony_ci#define CPIA_COMMAND_GetExposure	(INPUT | CPIA_MODULE_VP_CTRL | 18)
16962306a36Sopenharmony_ci#define CPIA_COMMAND_SetSensorMatrix	(OUTPUT | CPIA_MODULE_VP_CTRL | 19)
17062306a36Sopenharmony_ci#define CPIA_COMMAND_ColourBars		(OUTPUT | CPIA_MODULE_VP_CTRL | 25)
17162306a36Sopenharmony_ci#define CPIA_COMMAND_ReadVPRegs		(INPUT | CPIA_MODULE_VP_CTRL | 30)
17262306a36Sopenharmony_ci#define CPIA_COMMAND_WriteVPReg		(OUTPUT | CPIA_MODULE_VP_CTRL | 31)
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci#define CPIA_COMMAND_GrabFrame		(OUTPUT | CPIA_MODULE_CAPTURE | 1)
17562306a36Sopenharmony_ci#define CPIA_COMMAND_UploadFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 2)
17662306a36Sopenharmony_ci#define CPIA_COMMAND_SetGrabMode	(OUTPUT | CPIA_MODULE_CAPTURE | 3)
17762306a36Sopenharmony_ci#define CPIA_COMMAND_InitStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 4)
17862306a36Sopenharmony_ci#define CPIA_COMMAND_FiniStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 5)
17962306a36Sopenharmony_ci#define CPIA_COMMAND_StartStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 6)
18062306a36Sopenharmony_ci#define CPIA_COMMAND_EndStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 7)
18162306a36Sopenharmony_ci#define CPIA_COMMAND_SetFormat		(OUTPUT | CPIA_MODULE_CAPTURE | 8)
18262306a36Sopenharmony_ci#define CPIA_COMMAND_SetROI		(OUTPUT | CPIA_MODULE_CAPTURE | 9)
18362306a36Sopenharmony_ci#define CPIA_COMMAND_SetCompression	(OUTPUT | CPIA_MODULE_CAPTURE | 10)
18462306a36Sopenharmony_ci#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
18562306a36Sopenharmony_ci#define CPIA_COMMAND_SetYUVThresh	(OUTPUT | CPIA_MODULE_CAPTURE | 12)
18662306a36Sopenharmony_ci#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
18762306a36Sopenharmony_ci#define CPIA_COMMAND_DiscardFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 14)
18862306a36Sopenharmony_ci#define CPIA_COMMAND_GrabReset		(OUTPUT | CPIA_MODULE_CAPTURE | 15)
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci#define CPIA_COMMAND_OutputRS232	(OUTPUT | CPIA_MODULE_DEBUG | 1)
19162306a36Sopenharmony_ci#define CPIA_COMMAND_AbortProcess	(OUTPUT | CPIA_MODULE_DEBUG | 4)
19262306a36Sopenharmony_ci#define CPIA_COMMAND_SetDramPage	(OUTPUT | CPIA_MODULE_DEBUG | 5)
19362306a36Sopenharmony_ci#define CPIA_COMMAND_StartDramUpload	(OUTPUT | CPIA_MODULE_DEBUG | 6)
19462306a36Sopenharmony_ci#define CPIA_COMMAND_StartDummyDtream	(OUTPUT | CPIA_MODULE_DEBUG | 8)
19562306a36Sopenharmony_ci#define CPIA_COMMAND_AbortStream	(OUTPUT | CPIA_MODULE_DEBUG | 9)
19662306a36Sopenharmony_ci#define CPIA_COMMAND_DownloadDRAM	(OUTPUT | CPIA_MODULE_DEBUG | 10)
19762306a36Sopenharmony_ci#define CPIA_COMMAND_Null		(OUTPUT | CPIA_MODULE_DEBUG | 11)
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci#define ROUND_UP_EXP_FOR_FLICKER 15
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/* Constants for automatic frame rate adjustment */
20262306a36Sopenharmony_ci#define MAX_EXP       302
20362306a36Sopenharmony_ci#define MAX_EXP_102   255
20462306a36Sopenharmony_ci#define LOW_EXP       140
20562306a36Sopenharmony_ci#define VERY_LOW_EXP   70
20662306a36Sopenharmony_ci#define TC             94
20762306a36Sopenharmony_ci#define	EXP_ACC_DARK   50
20862306a36Sopenharmony_ci#define	EXP_ACC_LIGHT  90
20962306a36Sopenharmony_ci#define HIGH_COMP_102 160
21062306a36Sopenharmony_ci#define MAX_COMP      239
21162306a36Sopenharmony_ci#define DARK_TIME       3
21262306a36Sopenharmony_ci#define LIGHT_TIME      3
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
21562306a36Sopenharmony_ci				sd->params.version.firmwareRevision == (y))
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci#define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000)
21862306a36Sopenharmony_ci#define BRIGHTNESS_DEF 50
21962306a36Sopenharmony_ci#define CONTRAST_DEF 48
22062306a36Sopenharmony_ci#define SATURATION_DEF 50
22162306a36Sopenharmony_ci#define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ
22262306a36Sopenharmony_ci#define ILLUMINATORS_1_DEF 0
22362306a36Sopenharmony_ci#define ILLUMINATORS_2_DEF 0
22462306a36Sopenharmony_ci#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/* Developer's Guide Table 5 p 3-34
22762306a36Sopenharmony_ci * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
22862306a36Sopenharmony_cistatic u8 flicker_jumps[2][2][4] =
22962306a36Sopenharmony_ci{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
23062306a36Sopenharmony_ci  { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistruct cam_params {
23462306a36Sopenharmony_ci	struct {
23562306a36Sopenharmony_ci		u8 firmwareVersion;
23662306a36Sopenharmony_ci		u8 firmwareRevision;
23762306a36Sopenharmony_ci		u8 vcVersion;
23862306a36Sopenharmony_ci		u8 vcRevision;
23962306a36Sopenharmony_ci	} version;
24062306a36Sopenharmony_ci	struct {
24162306a36Sopenharmony_ci		u16 vendor;
24262306a36Sopenharmony_ci		u16 product;
24362306a36Sopenharmony_ci		u16 deviceRevision;
24462306a36Sopenharmony_ci	} pnpID;
24562306a36Sopenharmony_ci	struct {
24662306a36Sopenharmony_ci		u8 vpVersion;
24762306a36Sopenharmony_ci		u8 vpRevision;
24862306a36Sopenharmony_ci		u16 cameraHeadID;
24962306a36Sopenharmony_ci	} vpVersion;
25062306a36Sopenharmony_ci	struct {
25162306a36Sopenharmony_ci		u8 systemState;
25262306a36Sopenharmony_ci		u8 grabState;
25362306a36Sopenharmony_ci		u8 streamState;
25462306a36Sopenharmony_ci		u8 fatalError;
25562306a36Sopenharmony_ci		u8 cmdError;
25662306a36Sopenharmony_ci		u8 debugFlags;
25762306a36Sopenharmony_ci		u8 vpStatus;
25862306a36Sopenharmony_ci		u8 errorCode;
25962306a36Sopenharmony_ci	} status;
26062306a36Sopenharmony_ci	struct {
26162306a36Sopenharmony_ci		u8 brightness;
26262306a36Sopenharmony_ci		u8 contrast;
26362306a36Sopenharmony_ci		u8 saturation;
26462306a36Sopenharmony_ci	} colourParams;
26562306a36Sopenharmony_ci	struct {
26662306a36Sopenharmony_ci		u8 gainMode;
26762306a36Sopenharmony_ci		u8 expMode;
26862306a36Sopenharmony_ci		u8 compMode;
26962306a36Sopenharmony_ci		u8 centreWeight;
27062306a36Sopenharmony_ci		u8 gain;
27162306a36Sopenharmony_ci		u8 fineExp;
27262306a36Sopenharmony_ci		u8 coarseExpLo;
27362306a36Sopenharmony_ci		u8 coarseExpHi;
27462306a36Sopenharmony_ci		u8 redComp;
27562306a36Sopenharmony_ci		u8 green1Comp;
27662306a36Sopenharmony_ci		u8 green2Comp;
27762306a36Sopenharmony_ci		u8 blueComp;
27862306a36Sopenharmony_ci	} exposure;
27962306a36Sopenharmony_ci	struct {
28062306a36Sopenharmony_ci		u8 balanceMode;
28162306a36Sopenharmony_ci		u8 redGain;
28262306a36Sopenharmony_ci		u8 greenGain;
28362306a36Sopenharmony_ci		u8 blueGain;
28462306a36Sopenharmony_ci	} colourBalance;
28562306a36Sopenharmony_ci	struct {
28662306a36Sopenharmony_ci		u8 divisor;
28762306a36Sopenharmony_ci		u8 baserate;
28862306a36Sopenharmony_ci	} sensorFps;
28962306a36Sopenharmony_ci	struct {
29062306a36Sopenharmony_ci		u8 gain1;
29162306a36Sopenharmony_ci		u8 gain2;
29262306a36Sopenharmony_ci		u8 gain4;
29362306a36Sopenharmony_ci		u8 gain8;
29462306a36Sopenharmony_ci	} apcor;
29562306a36Sopenharmony_ci	struct {
29662306a36Sopenharmony_ci		u8 disabled;
29762306a36Sopenharmony_ci		u8 flickerMode;
29862306a36Sopenharmony_ci		u8 coarseJump;
29962306a36Sopenharmony_ci		u8 allowableOverExposure;
30062306a36Sopenharmony_ci	} flickerControl;
30162306a36Sopenharmony_ci	struct {
30262306a36Sopenharmony_ci		u8 gain1;
30362306a36Sopenharmony_ci		u8 gain2;
30462306a36Sopenharmony_ci		u8 gain4;
30562306a36Sopenharmony_ci		u8 gain8;
30662306a36Sopenharmony_ci	} vlOffset;
30762306a36Sopenharmony_ci	struct {
30862306a36Sopenharmony_ci		u8 mode;
30962306a36Sopenharmony_ci		u8 decimation;
31062306a36Sopenharmony_ci	} compression;
31162306a36Sopenharmony_ci	struct {
31262306a36Sopenharmony_ci		u8 frTargeting;
31362306a36Sopenharmony_ci		u8 targetFR;
31462306a36Sopenharmony_ci		u8 targetQ;
31562306a36Sopenharmony_ci	} compressionTarget;
31662306a36Sopenharmony_ci	struct {
31762306a36Sopenharmony_ci		u8 yThreshold;
31862306a36Sopenharmony_ci		u8 uvThreshold;
31962306a36Sopenharmony_ci	} yuvThreshold;
32062306a36Sopenharmony_ci	struct {
32162306a36Sopenharmony_ci		u8 hysteresis;
32262306a36Sopenharmony_ci		u8 threshMax;
32362306a36Sopenharmony_ci		u8 smallStep;
32462306a36Sopenharmony_ci		u8 largeStep;
32562306a36Sopenharmony_ci		u8 decimationHysteresis;
32662306a36Sopenharmony_ci		u8 frDiffStepThresh;
32762306a36Sopenharmony_ci		u8 qDiffStepThresh;
32862306a36Sopenharmony_ci		u8 decimationThreshMod;
32962306a36Sopenharmony_ci	} compressionParams;
33062306a36Sopenharmony_ci	struct {
33162306a36Sopenharmony_ci		u8 videoSize;		/* CIF/QCIF */
33262306a36Sopenharmony_ci		u8 subSample;
33362306a36Sopenharmony_ci		u8 yuvOrder;
33462306a36Sopenharmony_ci	} format;
33562306a36Sopenharmony_ci	struct {                        /* Intel QX3 specific data */
33662306a36Sopenharmony_ci		u8 qx3_detected;        /* a QX3 is present */
33762306a36Sopenharmony_ci		u8 toplight;            /* top light lit , R/W */
33862306a36Sopenharmony_ci		u8 bottomlight;         /* bottom light lit, R/W */
33962306a36Sopenharmony_ci		u8 button;              /* snapshot button pressed (R/O) */
34062306a36Sopenharmony_ci		u8 cradled;             /* microscope is in cradle (R/O) */
34162306a36Sopenharmony_ci	} qx3;
34262306a36Sopenharmony_ci	struct {
34362306a36Sopenharmony_ci		u8 colStart;		/* skip first 8*colStart pixels */
34462306a36Sopenharmony_ci		u8 colEnd;		/* finish at 8*colEnd pixels */
34562306a36Sopenharmony_ci		u8 rowStart;		/* skip first 4*rowStart lines */
34662306a36Sopenharmony_ci		u8 rowEnd;		/* finish at 4*rowEnd lines */
34762306a36Sopenharmony_ci	} roi;
34862306a36Sopenharmony_ci	u8 ecpTiming;
34962306a36Sopenharmony_ci	u8 streamStartLine;
35062306a36Sopenharmony_ci};
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/* specific webcam descriptor */
35362306a36Sopenharmony_cistruct sd {
35462306a36Sopenharmony_ci	struct gspca_dev gspca_dev;		/* !! must be the first item */
35562306a36Sopenharmony_ci	struct cam_params params;		/* camera settings */
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	atomic_t cam_exposure;
35862306a36Sopenharmony_ci	atomic_t fps;
35962306a36Sopenharmony_ci	int exposure_count;
36062306a36Sopenharmony_ci	u8 exposure_status;
36162306a36Sopenharmony_ci	struct v4l2_ctrl *freq;
36262306a36Sopenharmony_ci	u8 mainsFreq;				/* 0 = 50hz, 1 = 60hz */
36362306a36Sopenharmony_ci	u8 first_frame;
36462306a36Sopenharmony_ci};
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic const struct v4l2_pix_format mode[] = {
36762306a36Sopenharmony_ci	{160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
36862306a36Sopenharmony_ci		/* The sizeimage is trial and error, as with low framerates
36962306a36Sopenharmony_ci		 *  the camera will pad out usb frames, making the image
37062306a36Sopenharmony_ci		 *  data larger than strictly necessary
37162306a36Sopenharmony_ci		 */
37262306a36Sopenharmony_ci		.bytesperline = 160,
37362306a36Sopenharmony_ci		.sizeimage = 65536,
37462306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
37562306a36Sopenharmony_ci		.priv = 3},
37662306a36Sopenharmony_ci	{176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
37762306a36Sopenharmony_ci		.bytesperline = 172,
37862306a36Sopenharmony_ci		.sizeimage = 65536,
37962306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
38062306a36Sopenharmony_ci		.priv = 2},
38162306a36Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
38262306a36Sopenharmony_ci		.bytesperline = 320,
38362306a36Sopenharmony_ci		.sizeimage = 262144,
38462306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
38562306a36Sopenharmony_ci		.priv = 1},
38662306a36Sopenharmony_ci	{352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
38762306a36Sopenharmony_ci		.bytesperline = 352,
38862306a36Sopenharmony_ci		.sizeimage = 262144,
38962306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
39062306a36Sopenharmony_ci		.priv = 0},
39162306a36Sopenharmony_ci};
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci/**********************************************************************
39462306a36Sopenharmony_ci *
39562306a36Sopenharmony_ci * General functions
39662306a36Sopenharmony_ci *
39762306a36Sopenharmony_ci **********************************************************************/
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	u8 requesttype;
40262306a36Sopenharmony_ci	unsigned int pipe;
40362306a36Sopenharmony_ci	int ret, databytes = command[6] | (command[7] << 8);
40462306a36Sopenharmony_ci	/* Sometimes we see spurious EPIPE errors */
40562306a36Sopenharmony_ci	int retries = 3;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (command[0] == DATA_IN) {
40862306a36Sopenharmony_ci		pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
40962306a36Sopenharmony_ci		requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
41062306a36Sopenharmony_ci	} else if (command[0] == DATA_OUT) {
41162306a36Sopenharmony_ci		pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
41262306a36Sopenharmony_ci		requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
41362306a36Sopenharmony_ci	} else {
41462306a36Sopenharmony_ci		gspca_err(gspca_dev, "Unexpected first byte of command: %x\n",
41562306a36Sopenharmony_ci			  command[0]);
41662306a36Sopenharmony_ci		return -EINVAL;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ciretry:
42062306a36Sopenharmony_ci	ret = usb_control_msg(gspca_dev->dev, pipe,
42162306a36Sopenharmony_ci			      command[1],
42262306a36Sopenharmony_ci			      requesttype,
42362306a36Sopenharmony_ci			      command[2] | (command[3] << 8),
42462306a36Sopenharmony_ci			      command[4] | (command[5] << 8),
42562306a36Sopenharmony_ci			      gspca_dev->usb_buf, databytes, 1000);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (ret < 0)
42862306a36Sopenharmony_ci		pr_err("usb_control_msg %02x, error %d\n", command[1], ret);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (ret == -EPIPE && retries > 0) {
43162306a36Sopenharmony_ci		retries--;
43262306a36Sopenharmony_ci		goto retry;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	return (ret < 0) ? ret : 0;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci/* send an arbitrary command to the camera */
43962306a36Sopenharmony_cistatic int do_command(struct gspca_dev *gspca_dev, u16 command,
44062306a36Sopenharmony_ci		      u8 a, u8 b, u8 c, u8 d)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
44362306a36Sopenharmony_ci	int ret, datasize;
44462306a36Sopenharmony_ci	u8 cmd[8];
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	switch (command) {
44762306a36Sopenharmony_ci	case CPIA_COMMAND_GetCPIAVersion:
44862306a36Sopenharmony_ci	case CPIA_COMMAND_GetPnPID:
44962306a36Sopenharmony_ci	case CPIA_COMMAND_GetCameraStatus:
45062306a36Sopenharmony_ci	case CPIA_COMMAND_GetVPVersion:
45162306a36Sopenharmony_ci	case CPIA_COMMAND_GetColourParams:
45262306a36Sopenharmony_ci	case CPIA_COMMAND_GetColourBalance:
45362306a36Sopenharmony_ci	case CPIA_COMMAND_GetExposure:
45462306a36Sopenharmony_ci		datasize = 8;
45562306a36Sopenharmony_ci		break;
45662306a36Sopenharmony_ci	case CPIA_COMMAND_ReadMCPorts:
45762306a36Sopenharmony_ci	case CPIA_COMMAND_ReadVCRegs:
45862306a36Sopenharmony_ci		datasize = 4;
45962306a36Sopenharmony_ci		break;
46062306a36Sopenharmony_ci	default:
46162306a36Sopenharmony_ci		datasize = 0;
46262306a36Sopenharmony_ci		break;
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	cmd[0] = command >> 8;
46662306a36Sopenharmony_ci	cmd[1] = command & 0xff;
46762306a36Sopenharmony_ci	cmd[2] = a;
46862306a36Sopenharmony_ci	cmd[3] = b;
46962306a36Sopenharmony_ci	cmd[4] = c;
47062306a36Sopenharmony_ci	cmd[5] = d;
47162306a36Sopenharmony_ci	cmd[6] = datasize;
47262306a36Sopenharmony_ci	cmd[7] = 0;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	ret = cpia_usb_transferCmd(gspca_dev, cmd);
47562306a36Sopenharmony_ci	if (ret)
47662306a36Sopenharmony_ci		return ret;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	switch (command) {
47962306a36Sopenharmony_ci	case CPIA_COMMAND_GetCPIAVersion:
48062306a36Sopenharmony_ci		sd->params.version.firmwareVersion = gspca_dev->usb_buf[0];
48162306a36Sopenharmony_ci		sd->params.version.firmwareRevision = gspca_dev->usb_buf[1];
48262306a36Sopenharmony_ci		sd->params.version.vcVersion = gspca_dev->usb_buf[2];
48362306a36Sopenharmony_ci		sd->params.version.vcRevision = gspca_dev->usb_buf[3];
48462306a36Sopenharmony_ci		break;
48562306a36Sopenharmony_ci	case CPIA_COMMAND_GetPnPID:
48662306a36Sopenharmony_ci		sd->params.pnpID.vendor =
48762306a36Sopenharmony_ci			gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
48862306a36Sopenharmony_ci		sd->params.pnpID.product =
48962306a36Sopenharmony_ci			gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
49062306a36Sopenharmony_ci		sd->params.pnpID.deviceRevision =
49162306a36Sopenharmony_ci			gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8);
49262306a36Sopenharmony_ci		break;
49362306a36Sopenharmony_ci	case CPIA_COMMAND_GetCameraStatus:
49462306a36Sopenharmony_ci		sd->params.status.systemState = gspca_dev->usb_buf[0];
49562306a36Sopenharmony_ci		sd->params.status.grabState = gspca_dev->usb_buf[1];
49662306a36Sopenharmony_ci		sd->params.status.streamState = gspca_dev->usb_buf[2];
49762306a36Sopenharmony_ci		sd->params.status.fatalError = gspca_dev->usb_buf[3];
49862306a36Sopenharmony_ci		sd->params.status.cmdError = gspca_dev->usb_buf[4];
49962306a36Sopenharmony_ci		sd->params.status.debugFlags = gspca_dev->usb_buf[5];
50062306a36Sopenharmony_ci		sd->params.status.vpStatus = gspca_dev->usb_buf[6];
50162306a36Sopenharmony_ci		sd->params.status.errorCode = gspca_dev->usb_buf[7];
50262306a36Sopenharmony_ci		break;
50362306a36Sopenharmony_ci	case CPIA_COMMAND_GetVPVersion:
50462306a36Sopenharmony_ci		sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0];
50562306a36Sopenharmony_ci		sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1];
50662306a36Sopenharmony_ci		sd->params.vpVersion.cameraHeadID =
50762306a36Sopenharmony_ci			gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
50862306a36Sopenharmony_ci		break;
50962306a36Sopenharmony_ci	case CPIA_COMMAND_GetColourParams:
51062306a36Sopenharmony_ci		sd->params.colourParams.brightness = gspca_dev->usb_buf[0];
51162306a36Sopenharmony_ci		sd->params.colourParams.contrast = gspca_dev->usb_buf[1];
51262306a36Sopenharmony_ci		sd->params.colourParams.saturation = gspca_dev->usb_buf[2];
51362306a36Sopenharmony_ci		break;
51462306a36Sopenharmony_ci	case CPIA_COMMAND_GetColourBalance:
51562306a36Sopenharmony_ci		sd->params.colourBalance.redGain = gspca_dev->usb_buf[0];
51662306a36Sopenharmony_ci		sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1];
51762306a36Sopenharmony_ci		sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2];
51862306a36Sopenharmony_ci		break;
51962306a36Sopenharmony_ci	case CPIA_COMMAND_GetExposure:
52062306a36Sopenharmony_ci		sd->params.exposure.gain = gspca_dev->usb_buf[0];
52162306a36Sopenharmony_ci		sd->params.exposure.fineExp = gspca_dev->usb_buf[1];
52262306a36Sopenharmony_ci		sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2];
52362306a36Sopenharmony_ci		sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3];
52462306a36Sopenharmony_ci		sd->params.exposure.redComp = gspca_dev->usb_buf[4];
52562306a36Sopenharmony_ci		sd->params.exposure.green1Comp = gspca_dev->usb_buf[5];
52662306a36Sopenharmony_ci		sd->params.exposure.green2Comp = gspca_dev->usb_buf[6];
52762306a36Sopenharmony_ci		sd->params.exposure.blueComp = gspca_dev->usb_buf[7];
52862306a36Sopenharmony_ci		break;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	case CPIA_COMMAND_ReadMCPorts:
53162306a36Sopenharmony_ci		/* test button press */
53262306a36Sopenharmony_ci		a = ((gspca_dev->usb_buf[1] & 0x02) == 0);
53362306a36Sopenharmony_ci		if (a != sd->params.qx3.button) {
53462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
53562306a36Sopenharmony_ci			input_report_key(gspca_dev->input_dev, KEY_CAMERA, a);
53662306a36Sopenharmony_ci			input_sync(gspca_dev->input_dev);
53762306a36Sopenharmony_ci#endif
53862306a36Sopenharmony_ci			sd->params.qx3.button = a;
53962306a36Sopenharmony_ci		}
54062306a36Sopenharmony_ci		if (sd->params.qx3.button) {
54162306a36Sopenharmony_ci			/* button pressed - unlock the latch */
54262306a36Sopenharmony_ci			ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
54362306a36Sopenharmony_ci				   3, 0xdf, 0xdf, 0);
54462306a36Sopenharmony_ci			if (ret)
54562306a36Sopenharmony_ci				return ret;
54662306a36Sopenharmony_ci			ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
54762306a36Sopenharmony_ci				   3, 0xff, 0xff, 0);
54862306a36Sopenharmony_ci			if (ret)
54962306a36Sopenharmony_ci				return ret;
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		/* test whether microscope is cradled */
55362306a36Sopenharmony_ci		sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0);
55462306a36Sopenharmony_ci		break;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	return 0;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci/* send a command to the camera with an additional data transaction */
56162306a36Sopenharmony_cistatic int do_command_extended(struct gspca_dev *gspca_dev, u16 command,
56262306a36Sopenharmony_ci			       u8 a, u8 b, u8 c, u8 d,
56362306a36Sopenharmony_ci			       u8 e, u8 f, u8 g, u8 h,
56462306a36Sopenharmony_ci			       u8 i, u8 j, u8 k, u8 l)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	u8 cmd[8];
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	cmd[0] = command >> 8;
56962306a36Sopenharmony_ci	cmd[1] = command & 0xff;
57062306a36Sopenharmony_ci	cmd[2] = a;
57162306a36Sopenharmony_ci	cmd[3] = b;
57262306a36Sopenharmony_ci	cmd[4] = c;
57362306a36Sopenharmony_ci	cmd[5] = d;
57462306a36Sopenharmony_ci	cmd[6] = 8;
57562306a36Sopenharmony_ci	cmd[7] = 0;
57662306a36Sopenharmony_ci	gspca_dev->usb_buf[0] = e;
57762306a36Sopenharmony_ci	gspca_dev->usb_buf[1] = f;
57862306a36Sopenharmony_ci	gspca_dev->usb_buf[2] = g;
57962306a36Sopenharmony_ci	gspca_dev->usb_buf[3] = h;
58062306a36Sopenharmony_ci	gspca_dev->usb_buf[4] = i;
58162306a36Sopenharmony_ci	gspca_dev->usb_buf[5] = j;
58262306a36Sopenharmony_ci	gspca_dev->usb_buf[6] = k;
58362306a36Sopenharmony_ci	gspca_dev->usb_buf[7] = l;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	return cpia_usb_transferCmd(gspca_dev, cmd);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci/*  find_over_exposure
58962306a36Sopenharmony_ci *  Finds a suitable value of OverExposure for use with SetFlickerCtrl
59062306a36Sopenharmony_ci *  Some calculation is required because this value changes with the brightness
59162306a36Sopenharmony_ci *  set with SetColourParameters
59262306a36Sopenharmony_ci *
59362306a36Sopenharmony_ci *  Parameters: Brightness - last brightness value set with SetColourParameters
59462306a36Sopenharmony_ci *
59562306a36Sopenharmony_ci *  Returns: OverExposure value to use with SetFlickerCtrl
59662306a36Sopenharmony_ci */
59762306a36Sopenharmony_ci#define FLICKER_MAX_EXPOSURE                    250
59862306a36Sopenharmony_ci#define FLICKER_ALLOWABLE_OVER_EXPOSURE         146
59962306a36Sopenharmony_ci#define FLICKER_BRIGHTNESS_CONSTANT             59
60062306a36Sopenharmony_cistatic int find_over_exposure(int brightness)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	int MaxAllowableOverExposure, OverExposure;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
60562306a36Sopenharmony_ci				   FLICKER_BRIGHTNESS_CONSTANT;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE)
60862306a36Sopenharmony_ci		OverExposure = MaxAllowableOverExposure;
60962306a36Sopenharmony_ci	else
61062306a36Sopenharmony_ci		OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	return OverExposure;
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci#undef FLICKER_MAX_EXPOSURE
61562306a36Sopenharmony_ci#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
61662306a36Sopenharmony_ci#undef FLICKER_BRIGHTNESS_CONSTANT
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci/* initialise cam_data structure  */
61962306a36Sopenharmony_cistatic void reset_camera_params(struct gspca_dev *gspca_dev)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
62262306a36Sopenharmony_ci	struct cam_params *params = &sd->params;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* The following parameter values are the defaults from
62562306a36Sopenharmony_ci	 * "Software Developer's Guide for CPiA Cameras".  Any changes
62662306a36Sopenharmony_ci	 * to the defaults are noted in comments. */
62762306a36Sopenharmony_ci	params->colourParams.brightness = BRIGHTNESS_DEF;
62862306a36Sopenharmony_ci	params->colourParams.contrast = CONTRAST_DEF;
62962306a36Sopenharmony_ci	params->colourParams.saturation = SATURATION_DEF;
63062306a36Sopenharmony_ci	params->exposure.gainMode = 4;
63162306a36Sopenharmony_ci	params->exposure.expMode = 2;		/* AEC */
63262306a36Sopenharmony_ci	params->exposure.compMode = 1;
63362306a36Sopenharmony_ci	params->exposure.centreWeight = 1;
63462306a36Sopenharmony_ci	params->exposure.gain = 0;
63562306a36Sopenharmony_ci	params->exposure.fineExp = 0;
63662306a36Sopenharmony_ci	params->exposure.coarseExpLo = 185;
63762306a36Sopenharmony_ci	params->exposure.coarseExpHi = 0;
63862306a36Sopenharmony_ci	params->exposure.redComp = COMP_RED;
63962306a36Sopenharmony_ci	params->exposure.green1Comp = COMP_GREEN1;
64062306a36Sopenharmony_ci	params->exposure.green2Comp = COMP_GREEN2;
64162306a36Sopenharmony_ci	params->exposure.blueComp = COMP_BLUE;
64262306a36Sopenharmony_ci	params->colourBalance.balanceMode = 2;	/* ACB */
64362306a36Sopenharmony_ci	params->colourBalance.redGain = 32;
64462306a36Sopenharmony_ci	params->colourBalance.greenGain = 6;
64562306a36Sopenharmony_ci	params->colourBalance.blueGain = 92;
64662306a36Sopenharmony_ci	params->apcor.gain1 = 0x18;
64762306a36Sopenharmony_ci	params->apcor.gain2 = 0x16;
64862306a36Sopenharmony_ci	params->apcor.gain4 = 0x24;
64962306a36Sopenharmony_ci	params->apcor.gain8 = 0x34;
65062306a36Sopenharmony_ci	params->vlOffset.gain1 = 20;
65162306a36Sopenharmony_ci	params->vlOffset.gain2 = 24;
65262306a36Sopenharmony_ci	params->vlOffset.gain4 = 26;
65362306a36Sopenharmony_ci	params->vlOffset.gain8 = 26;
65462306a36Sopenharmony_ci	params->compressionParams.hysteresis = 3;
65562306a36Sopenharmony_ci	params->compressionParams.threshMax = 11;
65662306a36Sopenharmony_ci	params->compressionParams.smallStep = 1;
65762306a36Sopenharmony_ci	params->compressionParams.largeStep = 3;
65862306a36Sopenharmony_ci	params->compressionParams.decimationHysteresis = 2;
65962306a36Sopenharmony_ci	params->compressionParams.frDiffStepThresh = 5;
66062306a36Sopenharmony_ci	params->compressionParams.qDiffStepThresh = 3;
66162306a36Sopenharmony_ci	params->compressionParams.decimationThreshMod = 2;
66262306a36Sopenharmony_ci	/* End of default values from Software Developer's Guide */
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	/* Set Sensor FPS to 15fps. This seems better than 30fps
66562306a36Sopenharmony_ci	 * for indoor lighting. */
66662306a36Sopenharmony_ci	params->sensorFps.divisor = 1;
66762306a36Sopenharmony_ci	params->sensorFps.baserate = 1;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	params->flickerControl.flickerMode = 0;
67062306a36Sopenharmony_ci	params->flickerControl.disabled = 1;
67162306a36Sopenharmony_ci	params->flickerControl.coarseJump =
67262306a36Sopenharmony_ci		flicker_jumps[sd->mainsFreq]
67362306a36Sopenharmony_ci			     [params->sensorFps.baserate]
67462306a36Sopenharmony_ci			     [params->sensorFps.divisor];
67562306a36Sopenharmony_ci	params->flickerControl.allowableOverExposure =
67662306a36Sopenharmony_ci		find_over_exposure(params->colourParams.brightness);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	params->yuvThreshold.yThreshold = 6; /* From windows driver */
67962306a36Sopenharmony_ci	params->yuvThreshold.uvThreshold = 6; /* From windows driver */
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	params->format.subSample = SUBSAMPLE_420;
68262306a36Sopenharmony_ci	params->format.yuvOrder = YUVORDER_YUYV;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	params->compression.mode = CPIA_COMPRESSION_AUTO;
68562306a36Sopenharmony_ci	params->compression.decimation = NO_DECIMATION;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	params->compressionTarget.frTargeting = COMP_TARGET_DEF;
68862306a36Sopenharmony_ci	params->compressionTarget.targetFR = 15; /* From windows driver */
68962306a36Sopenharmony_ci	params->compressionTarget.targetQ = 5; /* From windows driver */
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	params->qx3.qx3_detected = 0;
69262306a36Sopenharmony_ci	params->qx3.toplight = 0;
69362306a36Sopenharmony_ci	params->qx3.bottomlight = 0;
69462306a36Sopenharmony_ci	params->qx3.button = 0;
69562306a36Sopenharmony_ci	params->qx3.cradled = 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic void printstatus(struct gspca_dev *gspca_dev, struct cam_params *params)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x\n",
70162306a36Sopenharmony_ci		  params->status.systemState, params->status.grabState,
70262306a36Sopenharmony_ci		  params->status.streamState, params->status.fatalError,
70362306a36Sopenharmony_ci		  params->status.cmdError, params->status.debugFlags,
70462306a36Sopenharmony_ci		  params->status.vpStatus, params->status.errorCode);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic int goto_low_power(struct gspca_dev *gspca_dev)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
71062306a36Sopenharmony_ci	int ret;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0);
71362306a36Sopenharmony_ci	if (ret)
71462306a36Sopenharmony_ci		return ret;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
71762306a36Sopenharmony_ci	if (ret)
71862306a36Sopenharmony_ci		return ret;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (sd->params.status.systemState != LO_POWER_STATE) {
72162306a36Sopenharmony_ci		if (sd->params.status.systemState != WARM_BOOT_STATE) {
72262306a36Sopenharmony_ci			gspca_err(gspca_dev, "unexpected state after lo power cmd: %02x\n",
72362306a36Sopenharmony_ci				  sd->params.status.systemState);
72462306a36Sopenharmony_ci			printstatus(gspca_dev, &sd->params);
72562306a36Sopenharmony_ci		}
72662306a36Sopenharmony_ci		return -EIO;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "camera now in LOW power state\n");
73062306a36Sopenharmony_ci	return 0;
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cistatic int goto_high_power(struct gspca_dev *gspca_dev)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
73662306a36Sopenharmony_ci	int ret;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0);
73962306a36Sopenharmony_ci	if (ret)
74062306a36Sopenharmony_ci		return ret;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	msleep_interruptible(40);	/* windows driver does it too */
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (signal_pending(current))
74562306a36Sopenharmony_ci		return -EINTR;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
74862306a36Sopenharmony_ci	if (ret)
74962306a36Sopenharmony_ci		return ret;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if (sd->params.status.systemState != HI_POWER_STATE) {
75262306a36Sopenharmony_ci		gspca_err(gspca_dev, "unexpected state after hi power cmd: %02x\n",
75362306a36Sopenharmony_ci			  sd->params.status.systemState);
75462306a36Sopenharmony_ci		printstatus(gspca_dev, &sd->params);
75562306a36Sopenharmony_ci		return -EIO;
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "camera now in HIGH power state\n");
75962306a36Sopenharmony_ci	return 0;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic int get_version_information(struct gspca_dev *gspca_dev)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	int ret;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/* GetCPIAVersion */
76762306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
76862306a36Sopenharmony_ci	if (ret)
76962306a36Sopenharmony_ci		return ret;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	/* GetPnPID */
77262306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic int save_camera_state(struct gspca_dev *gspca_dev)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	int ret;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
78062306a36Sopenharmony_ci	if (ret)
78162306a36Sopenharmony_ci		return ret;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic int command_setformat(struct gspca_dev *gspca_dev)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
78962306a36Sopenharmony_ci	int ret;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat,
79262306a36Sopenharmony_ci			 sd->params.format.videoSize,
79362306a36Sopenharmony_ci			 sd->params.format.subSample,
79462306a36Sopenharmony_ci			 sd->params.format.yuvOrder, 0);
79562306a36Sopenharmony_ci	if (ret)
79662306a36Sopenharmony_ci		return ret;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetROI,
79962306a36Sopenharmony_ci			  sd->params.roi.colStart, sd->params.roi.colEnd,
80062306a36Sopenharmony_ci			  sd->params.roi.rowStart, sd->params.roi.rowEnd);
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic int command_setcolourparams(struct gspca_dev *gspca_dev)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
80662306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetColourParams,
80762306a36Sopenharmony_ci			  sd->params.colourParams.brightness,
80862306a36Sopenharmony_ci			  sd->params.colourParams.contrast,
80962306a36Sopenharmony_ci			  sd->params.colourParams.saturation, 0);
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic int command_setapcor(struct gspca_dev *gspca_dev)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
81562306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetApcor,
81662306a36Sopenharmony_ci			  sd->params.apcor.gain1,
81762306a36Sopenharmony_ci			  sd->params.apcor.gain2,
81862306a36Sopenharmony_ci			  sd->params.apcor.gain4,
81962306a36Sopenharmony_ci			  sd->params.apcor.gain8);
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_cistatic int command_setvloffset(struct gspca_dev *gspca_dev)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
82562306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset,
82662306a36Sopenharmony_ci			  sd->params.vlOffset.gain1,
82762306a36Sopenharmony_ci			  sd->params.vlOffset.gain2,
82862306a36Sopenharmony_ci			  sd->params.vlOffset.gain4,
82962306a36Sopenharmony_ci			  sd->params.vlOffset.gain8);
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic int command_setexposure(struct gspca_dev *gspca_dev)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
83562306a36Sopenharmony_ci	int ret;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
83862306a36Sopenharmony_ci				  sd->params.exposure.gainMode,
83962306a36Sopenharmony_ci				  1,
84062306a36Sopenharmony_ci				  sd->params.exposure.compMode,
84162306a36Sopenharmony_ci				  sd->params.exposure.centreWeight,
84262306a36Sopenharmony_ci				  sd->params.exposure.gain,
84362306a36Sopenharmony_ci				  sd->params.exposure.fineExp,
84462306a36Sopenharmony_ci				  sd->params.exposure.coarseExpLo,
84562306a36Sopenharmony_ci				  sd->params.exposure.coarseExpHi,
84662306a36Sopenharmony_ci				  sd->params.exposure.redComp,
84762306a36Sopenharmony_ci				  sd->params.exposure.green1Comp,
84862306a36Sopenharmony_ci				  sd->params.exposure.green2Comp,
84962306a36Sopenharmony_ci				  sd->params.exposure.blueComp);
85062306a36Sopenharmony_ci	if (ret)
85162306a36Sopenharmony_ci		return ret;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (sd->params.exposure.expMode != 1) {
85462306a36Sopenharmony_ci		ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
85562306a36Sopenharmony_ci					  0,
85662306a36Sopenharmony_ci					  sd->params.exposure.expMode,
85762306a36Sopenharmony_ci					  0, 0,
85862306a36Sopenharmony_ci					  sd->params.exposure.gain,
85962306a36Sopenharmony_ci					  sd->params.exposure.fineExp,
86062306a36Sopenharmony_ci					  sd->params.exposure.coarseExpLo,
86162306a36Sopenharmony_ci					  sd->params.exposure.coarseExpHi,
86262306a36Sopenharmony_ci					  0, 0, 0, 0);
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	return ret;
86662306a36Sopenharmony_ci}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_cistatic int command_setcolourbalance(struct gspca_dev *gspca_dev)
86962306a36Sopenharmony_ci{
87062306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	if (sd->params.colourBalance.balanceMode == 1) {
87362306a36Sopenharmony_ci		int ret;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
87662306a36Sopenharmony_ci				 1,
87762306a36Sopenharmony_ci				 sd->params.colourBalance.redGain,
87862306a36Sopenharmony_ci				 sd->params.colourBalance.greenGain,
87962306a36Sopenharmony_ci				 sd->params.colourBalance.blueGain);
88062306a36Sopenharmony_ci		if (ret)
88162306a36Sopenharmony_ci			return ret;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
88462306a36Sopenharmony_ci				  3, 0, 0, 0);
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci	if (sd->params.colourBalance.balanceMode == 2) {
88762306a36Sopenharmony_ci		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
88862306a36Sopenharmony_ci				  2, 0, 0, 0);
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci	if (sd->params.colourBalance.balanceMode == 3) {
89162306a36Sopenharmony_ci		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
89262306a36Sopenharmony_ci				  3, 0, 0, 0);
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	return -EINVAL;
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic int command_setcompressiontarget(struct gspca_dev *gspca_dev)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget,
90362306a36Sopenharmony_ci			  sd->params.compressionTarget.frTargeting,
90462306a36Sopenharmony_ci			  sd->params.compressionTarget.targetFR,
90562306a36Sopenharmony_ci			  sd->params.compressionTarget.targetQ, 0);
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic int command_setyuvtresh(struct gspca_dev *gspca_dev)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh,
91362306a36Sopenharmony_ci			  sd->params.yuvThreshold.yThreshold,
91462306a36Sopenharmony_ci			  sd->params.yuvThreshold.uvThreshold, 0, 0);
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_cistatic int command_setcompressionparams(struct gspca_dev *gspca_dev)
91862306a36Sopenharmony_ci{
91962306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	return do_command_extended(gspca_dev,
92262306a36Sopenharmony_ci			    CPIA_COMMAND_SetCompressionParams,
92362306a36Sopenharmony_ci			    0, 0, 0, 0,
92462306a36Sopenharmony_ci			    sd->params.compressionParams.hysteresis,
92562306a36Sopenharmony_ci			    sd->params.compressionParams.threshMax,
92662306a36Sopenharmony_ci			    sd->params.compressionParams.smallStep,
92762306a36Sopenharmony_ci			    sd->params.compressionParams.largeStep,
92862306a36Sopenharmony_ci			    sd->params.compressionParams.decimationHysteresis,
92962306a36Sopenharmony_ci			    sd->params.compressionParams.frDiffStepThresh,
93062306a36Sopenharmony_ci			    sd->params.compressionParams.qDiffStepThresh,
93162306a36Sopenharmony_ci			    sd->params.compressionParams.decimationThreshMod);
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic int command_setcompression(struct gspca_dev *gspca_dev)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetCompression,
93962306a36Sopenharmony_ci			  sd->params.compression.mode,
94062306a36Sopenharmony_ci			  sd->params.compression.decimation, 0, 0);
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic int command_setsensorfps(struct gspca_dev *gspca_dev)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS,
94862306a36Sopenharmony_ci			  sd->params.sensorFps.divisor,
94962306a36Sopenharmony_ci			  sd->params.sensorFps.baserate, 0, 0);
95062306a36Sopenharmony_ci}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_cistatic int command_setflickerctrl(struct gspca_dev *gspca_dev)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl,
95762306a36Sopenharmony_ci			  sd->params.flickerControl.flickerMode,
95862306a36Sopenharmony_ci			  sd->params.flickerControl.coarseJump,
95962306a36Sopenharmony_ci			  sd->params.flickerControl.allowableOverExposure,
96062306a36Sopenharmony_ci			  0);
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_cistatic int command_setecptiming(struct gspca_dev *gspca_dev)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming,
96862306a36Sopenharmony_ci			  sd->params.ecpTiming, 0, 0, 0);
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cistatic int command_pause(struct gspca_dev *gspca_dev)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
97462306a36Sopenharmony_ci}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cistatic int command_resume(struct gspca_dev *gspca_dev)
97762306a36Sopenharmony_ci{
97862306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap,
98162306a36Sopenharmony_ci			  0, sd->params.streamStartLine, 0, 0);
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic int command_setlights(struct gspca_dev *gspca_dev)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
98762306a36Sopenharmony_ci	int ret, p1, p2;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	p1 = (sd->params.qx3.bottomlight == 0) << 1;
99062306a36Sopenharmony_ci	p2 = (sd->params.qx3.toplight == 0) << 3;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg,
99362306a36Sopenharmony_ci			 0x90, 0x8f, 0x50, 0);
99462306a36Sopenharmony_ci	if (ret)
99562306a36Sopenharmony_ci		return ret;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0,
99862306a36Sopenharmony_ci			  p1 | p2 | 0xe0, 0);
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cistatic int set_flicker(struct gspca_dev *gspca_dev, int on, int apply)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	/* Everything in here is from the Windows driver */
100462306a36Sopenharmony_ci/* define for compgain calculation */
100562306a36Sopenharmony_ci#if 0
100662306a36Sopenharmony_ci#define COMPGAIN(base, curexp, newexp) \
100762306a36Sopenharmony_ci    (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
100862306a36Sopenharmony_ci#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
100962306a36Sopenharmony_ci    (u16)((float)curexp * (float)(u8)(curcomp + 128) / \
101062306a36Sopenharmony_ci    (float)(u8)(basecomp - 128))
101162306a36Sopenharmony_ci#else
101262306a36Sopenharmony_ci  /* equivalent functions without floating point math */
101362306a36Sopenharmony_ci#define COMPGAIN(base, curexp, newexp) \
101462306a36Sopenharmony_ci    (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp)))
101562306a36Sopenharmony_ci#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
101662306a36Sopenharmony_ci    (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
101762306a36Sopenharmony_ci#endif
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
102062306a36Sopenharmony_ci	int currentexp = sd->params.exposure.coarseExpLo +
102162306a36Sopenharmony_ci			 sd->params.exposure.coarseExpHi * 256;
102262306a36Sopenharmony_ci	int ret, startexp;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	if (on) {
102562306a36Sopenharmony_ci		int cj = sd->params.flickerControl.coarseJump;
102662306a36Sopenharmony_ci		sd->params.flickerControl.flickerMode = 1;
102762306a36Sopenharmony_ci		sd->params.flickerControl.disabled = 0;
102862306a36Sopenharmony_ci		if (sd->params.exposure.expMode != 2) {
102962306a36Sopenharmony_ci			sd->params.exposure.expMode = 2;
103062306a36Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
103162306a36Sopenharmony_ci		}
103262306a36Sopenharmony_ci		if (sd->params.exposure.gain >= BITS_PER_TYPE(currentexp))
103362306a36Sopenharmony_ci			return -EINVAL;
103462306a36Sopenharmony_ci		currentexp = currentexp << sd->params.exposure.gain;
103562306a36Sopenharmony_ci		sd->params.exposure.gain = 0;
103662306a36Sopenharmony_ci		/* round down current exposure to nearest value */
103762306a36Sopenharmony_ci		startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
103862306a36Sopenharmony_ci		if (startexp < 1)
103962306a36Sopenharmony_ci			startexp = 1;
104062306a36Sopenharmony_ci		startexp = (startexp * cj) - 1;
104162306a36Sopenharmony_ci		if (FIRMWARE_VERSION(1, 2))
104262306a36Sopenharmony_ci			while (startexp > MAX_EXP_102)
104362306a36Sopenharmony_ci				startexp -= cj;
104462306a36Sopenharmony_ci		else
104562306a36Sopenharmony_ci			while (startexp > MAX_EXP)
104662306a36Sopenharmony_ci				startexp -= cj;
104762306a36Sopenharmony_ci		sd->params.exposure.coarseExpLo = startexp & 0xff;
104862306a36Sopenharmony_ci		sd->params.exposure.coarseExpHi = startexp >> 8;
104962306a36Sopenharmony_ci		if (currentexp > startexp) {
105062306a36Sopenharmony_ci			if (currentexp > (2 * startexp))
105162306a36Sopenharmony_ci				currentexp = 2 * startexp;
105262306a36Sopenharmony_ci			sd->params.exposure.redComp =
105362306a36Sopenharmony_ci				COMPGAIN(COMP_RED, currentexp, startexp);
105462306a36Sopenharmony_ci			sd->params.exposure.green1Comp =
105562306a36Sopenharmony_ci				COMPGAIN(COMP_GREEN1, currentexp, startexp);
105662306a36Sopenharmony_ci			sd->params.exposure.green2Comp =
105762306a36Sopenharmony_ci				COMPGAIN(COMP_GREEN2, currentexp, startexp);
105862306a36Sopenharmony_ci			sd->params.exposure.blueComp =
105962306a36Sopenharmony_ci				COMPGAIN(COMP_BLUE, currentexp, startexp);
106062306a36Sopenharmony_ci		} else {
106162306a36Sopenharmony_ci			sd->params.exposure.redComp = COMP_RED;
106262306a36Sopenharmony_ci			sd->params.exposure.green1Comp = COMP_GREEN1;
106362306a36Sopenharmony_ci			sd->params.exposure.green2Comp = COMP_GREEN2;
106462306a36Sopenharmony_ci			sd->params.exposure.blueComp = COMP_BLUE;
106562306a36Sopenharmony_ci		}
106662306a36Sopenharmony_ci		if (FIRMWARE_VERSION(1, 2))
106762306a36Sopenharmony_ci			sd->params.exposure.compMode = 0;
106862306a36Sopenharmony_ci		else
106962306a36Sopenharmony_ci			sd->params.exposure.compMode = 1;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		sd->params.apcor.gain1 = 0x18;
107262306a36Sopenharmony_ci		sd->params.apcor.gain2 = 0x18;
107362306a36Sopenharmony_ci		sd->params.apcor.gain4 = 0x16;
107462306a36Sopenharmony_ci		sd->params.apcor.gain8 = 0x14;
107562306a36Sopenharmony_ci	} else {
107662306a36Sopenharmony_ci		sd->params.flickerControl.flickerMode = 0;
107762306a36Sopenharmony_ci		sd->params.flickerControl.disabled = 1;
107862306a36Sopenharmony_ci		/* Average equivalent coarse for each comp channel */
107962306a36Sopenharmony_ci		startexp = EXP_FROM_COMP(COMP_RED,
108062306a36Sopenharmony_ci				sd->params.exposure.redComp, currentexp);
108162306a36Sopenharmony_ci		startexp += EXP_FROM_COMP(COMP_GREEN1,
108262306a36Sopenharmony_ci				sd->params.exposure.green1Comp, currentexp);
108362306a36Sopenharmony_ci		startexp += EXP_FROM_COMP(COMP_GREEN2,
108462306a36Sopenharmony_ci				sd->params.exposure.green2Comp, currentexp);
108562306a36Sopenharmony_ci		startexp += EXP_FROM_COMP(COMP_BLUE,
108662306a36Sopenharmony_ci				sd->params.exposure.blueComp, currentexp);
108762306a36Sopenharmony_ci		startexp = startexp >> 2;
108862306a36Sopenharmony_ci		while (startexp > MAX_EXP && sd->params.exposure.gain <
108962306a36Sopenharmony_ci		       sd->params.exposure.gainMode - 1) {
109062306a36Sopenharmony_ci			startexp = startexp >> 1;
109162306a36Sopenharmony_ci			++sd->params.exposure.gain;
109262306a36Sopenharmony_ci		}
109362306a36Sopenharmony_ci		if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102)
109462306a36Sopenharmony_ci			startexp = MAX_EXP_102;
109562306a36Sopenharmony_ci		if (startexp > MAX_EXP)
109662306a36Sopenharmony_ci			startexp = MAX_EXP;
109762306a36Sopenharmony_ci		sd->params.exposure.coarseExpLo = startexp & 0xff;
109862306a36Sopenharmony_ci		sd->params.exposure.coarseExpHi = startexp >> 8;
109962306a36Sopenharmony_ci		sd->params.exposure.redComp = COMP_RED;
110062306a36Sopenharmony_ci		sd->params.exposure.green1Comp = COMP_GREEN1;
110162306a36Sopenharmony_ci		sd->params.exposure.green2Comp = COMP_GREEN2;
110262306a36Sopenharmony_ci		sd->params.exposure.blueComp = COMP_BLUE;
110362306a36Sopenharmony_ci		sd->params.exposure.compMode = 1;
110462306a36Sopenharmony_ci		sd->params.apcor.gain1 = 0x18;
110562306a36Sopenharmony_ci		sd->params.apcor.gain2 = 0x16;
110662306a36Sopenharmony_ci		sd->params.apcor.gain4 = 0x24;
110762306a36Sopenharmony_ci		sd->params.apcor.gain8 = 0x34;
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci	sd->params.vlOffset.gain1 = 20;
111062306a36Sopenharmony_ci	sd->params.vlOffset.gain2 = 24;
111162306a36Sopenharmony_ci	sd->params.vlOffset.gain4 = 26;
111262306a36Sopenharmony_ci	sd->params.vlOffset.gain8 = 26;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	if (apply) {
111562306a36Sopenharmony_ci		ret = command_setexposure(gspca_dev);
111662306a36Sopenharmony_ci		if (ret)
111762306a36Sopenharmony_ci			return ret;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci		ret = command_setapcor(gspca_dev);
112062306a36Sopenharmony_ci		if (ret)
112162306a36Sopenharmony_ci			return ret;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci		ret = command_setvloffset(gspca_dev);
112462306a36Sopenharmony_ci		if (ret)
112562306a36Sopenharmony_ci			return ret;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci		ret = command_setflickerctrl(gspca_dev);
112862306a36Sopenharmony_ci		if (ret)
112962306a36Sopenharmony_ci			return ret;
113062306a36Sopenharmony_ci	}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	return 0;
113362306a36Sopenharmony_ci#undef EXP_FROM_COMP
113462306a36Sopenharmony_ci#undef COMPGAIN
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci/* monitor the exposure and adjust the sensor frame rate if needed */
113862306a36Sopenharmony_cistatic void monitor_exposure(struct gspca_dev *gspca_dev)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
114162306a36Sopenharmony_ci	u8 exp_acc, bcomp, cmd[8];
114262306a36Sopenharmony_ci	int ret, light_exp, dark_exp, very_dark_exp;
114362306a36Sopenharmony_ci	int old_exposure, new_exposure, framerate;
114462306a36Sopenharmony_ci	int setfps = 0, setexp = 0, setflicker = 0;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	/* get necessary stats and register settings from camera */
114762306a36Sopenharmony_ci	/* do_command can't handle this, so do it ourselves */
114862306a36Sopenharmony_ci	cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8;
114962306a36Sopenharmony_ci	cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff;
115062306a36Sopenharmony_ci	cmd[2] = 30;
115162306a36Sopenharmony_ci	cmd[3] = 4;
115262306a36Sopenharmony_ci	cmd[4] = 9;
115362306a36Sopenharmony_ci	cmd[5] = 8;
115462306a36Sopenharmony_ci	cmd[6] = 8;
115562306a36Sopenharmony_ci	cmd[7] = 0;
115662306a36Sopenharmony_ci	ret = cpia_usb_transferCmd(gspca_dev, cmd);
115762306a36Sopenharmony_ci	if (ret) {
115862306a36Sopenharmony_ci		pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret);
115962306a36Sopenharmony_ci		return;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci	exp_acc = gspca_dev->usb_buf[0];
116262306a36Sopenharmony_ci	bcomp = gspca_dev->usb_buf[1];
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	light_exp = sd->params.colourParams.brightness +
116562306a36Sopenharmony_ci		    TC - 50 + EXP_ACC_LIGHT;
116662306a36Sopenharmony_ci	if (light_exp > 255)
116762306a36Sopenharmony_ci		light_exp = 255;
116862306a36Sopenharmony_ci	dark_exp = sd->params.colourParams.brightness +
116962306a36Sopenharmony_ci		   TC - 50 - EXP_ACC_DARK;
117062306a36Sopenharmony_ci	if (dark_exp < 0)
117162306a36Sopenharmony_ci		dark_exp = 0;
117262306a36Sopenharmony_ci	very_dark_exp = dark_exp / 2;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	old_exposure = sd->params.exposure.coarseExpHi * 256 +
117562306a36Sopenharmony_ci		       sd->params.exposure.coarseExpLo;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (!sd->params.flickerControl.disabled) {
117862306a36Sopenharmony_ci		/* Flicker control on */
117962306a36Sopenharmony_ci		int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP :
118062306a36Sopenharmony_ci							HIGH_COMP_102;
118162306a36Sopenharmony_ci		bcomp += 128;	/* decode */
118262306a36Sopenharmony_ci		if (bcomp >= max_comp && exp_acc < dark_exp) {
118362306a36Sopenharmony_ci			/* dark */
118462306a36Sopenharmony_ci			if (exp_acc < very_dark_exp) {
118562306a36Sopenharmony_ci				/* very dark */
118662306a36Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_VERY_DARK)
118762306a36Sopenharmony_ci					++sd->exposure_count;
118862306a36Sopenharmony_ci				else {
118962306a36Sopenharmony_ci					sd->exposure_status =
119062306a36Sopenharmony_ci						EXPOSURE_VERY_DARK;
119162306a36Sopenharmony_ci					sd->exposure_count = 1;
119262306a36Sopenharmony_ci				}
119362306a36Sopenharmony_ci			} else {
119462306a36Sopenharmony_ci				/* just dark */
119562306a36Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_DARK)
119662306a36Sopenharmony_ci					++sd->exposure_count;
119762306a36Sopenharmony_ci				else {
119862306a36Sopenharmony_ci					sd->exposure_status = EXPOSURE_DARK;
119962306a36Sopenharmony_ci					sd->exposure_count = 1;
120062306a36Sopenharmony_ci				}
120162306a36Sopenharmony_ci			}
120262306a36Sopenharmony_ci		} else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
120362306a36Sopenharmony_ci			/* light */
120462306a36Sopenharmony_ci			if (old_exposure <= VERY_LOW_EXP) {
120562306a36Sopenharmony_ci				/* very light */
120662306a36Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
120762306a36Sopenharmony_ci					++sd->exposure_count;
120862306a36Sopenharmony_ci				else {
120962306a36Sopenharmony_ci					sd->exposure_status =
121062306a36Sopenharmony_ci						EXPOSURE_VERY_LIGHT;
121162306a36Sopenharmony_ci					sd->exposure_count = 1;
121262306a36Sopenharmony_ci				}
121362306a36Sopenharmony_ci			} else {
121462306a36Sopenharmony_ci				/* just light */
121562306a36Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_LIGHT)
121662306a36Sopenharmony_ci					++sd->exposure_count;
121762306a36Sopenharmony_ci				else {
121862306a36Sopenharmony_ci					sd->exposure_status = EXPOSURE_LIGHT;
121962306a36Sopenharmony_ci					sd->exposure_count = 1;
122062306a36Sopenharmony_ci				}
122162306a36Sopenharmony_ci			}
122262306a36Sopenharmony_ci		} else {
122362306a36Sopenharmony_ci			/* not dark or light */
122462306a36Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
122562306a36Sopenharmony_ci		}
122662306a36Sopenharmony_ci	} else {
122762306a36Sopenharmony_ci		/* Flicker control off */
122862306a36Sopenharmony_ci		if (old_exposure >= MAX_EXP && exp_acc < dark_exp) {
122962306a36Sopenharmony_ci			/* dark */
123062306a36Sopenharmony_ci			if (exp_acc < very_dark_exp) {
123162306a36Sopenharmony_ci				/* very dark */
123262306a36Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_VERY_DARK)
123362306a36Sopenharmony_ci					++sd->exposure_count;
123462306a36Sopenharmony_ci				else {
123562306a36Sopenharmony_ci					sd->exposure_status =
123662306a36Sopenharmony_ci						EXPOSURE_VERY_DARK;
123762306a36Sopenharmony_ci					sd->exposure_count = 1;
123862306a36Sopenharmony_ci				}
123962306a36Sopenharmony_ci			} else {
124062306a36Sopenharmony_ci				/* just dark */
124162306a36Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_DARK)
124262306a36Sopenharmony_ci					++sd->exposure_count;
124362306a36Sopenharmony_ci				else {
124462306a36Sopenharmony_ci					sd->exposure_status = EXPOSURE_DARK;
124562306a36Sopenharmony_ci					sd->exposure_count = 1;
124662306a36Sopenharmony_ci				}
124762306a36Sopenharmony_ci			}
124862306a36Sopenharmony_ci		} else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
124962306a36Sopenharmony_ci			/* light */
125062306a36Sopenharmony_ci			if (old_exposure <= VERY_LOW_EXP) {
125162306a36Sopenharmony_ci				/* very light */
125262306a36Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
125362306a36Sopenharmony_ci					++sd->exposure_count;
125462306a36Sopenharmony_ci				else {
125562306a36Sopenharmony_ci					sd->exposure_status =
125662306a36Sopenharmony_ci						EXPOSURE_VERY_LIGHT;
125762306a36Sopenharmony_ci					sd->exposure_count = 1;
125862306a36Sopenharmony_ci				}
125962306a36Sopenharmony_ci			} else {
126062306a36Sopenharmony_ci				/* just light */
126162306a36Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_LIGHT)
126262306a36Sopenharmony_ci					++sd->exposure_count;
126362306a36Sopenharmony_ci				else {
126462306a36Sopenharmony_ci					sd->exposure_status = EXPOSURE_LIGHT;
126562306a36Sopenharmony_ci					sd->exposure_count = 1;
126662306a36Sopenharmony_ci				}
126762306a36Sopenharmony_ci			}
126862306a36Sopenharmony_ci		} else {
126962306a36Sopenharmony_ci			/* not dark or light */
127062306a36Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
127162306a36Sopenharmony_ci		}
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	framerate = atomic_read(&sd->fps);
127562306a36Sopenharmony_ci	if (framerate > 30 || framerate < 1)
127662306a36Sopenharmony_ci		framerate = 1;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (!sd->params.flickerControl.disabled) {
127962306a36Sopenharmony_ci		/* Flicker control on */
128062306a36Sopenharmony_ci		if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
128162306a36Sopenharmony_ci		     sd->exposure_status == EXPOSURE_DARK) &&
128262306a36Sopenharmony_ci		    sd->exposure_count >= DARK_TIME * framerate &&
128362306a36Sopenharmony_ci		    sd->params.sensorFps.divisor < 2) {
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci			/* dark for too long */
128662306a36Sopenharmony_ci			++sd->params.sensorFps.divisor;
128762306a36Sopenharmony_ci			setfps = 1;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci			sd->params.flickerControl.coarseJump =
129062306a36Sopenharmony_ci				flicker_jumps[sd->mainsFreq]
129162306a36Sopenharmony_ci					     [sd->params.sensorFps.baserate]
129262306a36Sopenharmony_ci					     [sd->params.sensorFps.divisor];
129362306a36Sopenharmony_ci			setflicker = 1;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci			new_exposure = sd->params.flickerControl.coarseJump-1;
129662306a36Sopenharmony_ci			while (new_exposure < old_exposure / 2)
129762306a36Sopenharmony_ci				new_exposure +=
129862306a36Sopenharmony_ci					sd->params.flickerControl.coarseJump;
129962306a36Sopenharmony_ci			sd->params.exposure.coarseExpLo = new_exposure & 0xff;
130062306a36Sopenharmony_ci			sd->params.exposure.coarseExpHi = new_exposure >> 8;
130162306a36Sopenharmony_ci			setexp = 1;
130262306a36Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
130362306a36Sopenharmony_ci			gspca_dbg(gspca_dev, D_CONF, "Automatically decreasing sensor_fps\n");
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci		} else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
130662306a36Sopenharmony_ci			    sd->exposure_status == EXPOSURE_LIGHT) &&
130762306a36Sopenharmony_ci			   sd->exposure_count >= LIGHT_TIME * framerate &&
130862306a36Sopenharmony_ci			   sd->params.sensorFps.divisor > 0) {
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci			/* light for too long */
131162306a36Sopenharmony_ci			int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 :
131262306a36Sopenharmony_ci							       MAX_EXP;
131362306a36Sopenharmony_ci			--sd->params.sensorFps.divisor;
131462306a36Sopenharmony_ci			setfps = 1;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci			sd->params.flickerControl.coarseJump =
131762306a36Sopenharmony_ci				flicker_jumps[sd->mainsFreq]
131862306a36Sopenharmony_ci					     [sd->params.sensorFps.baserate]
131962306a36Sopenharmony_ci					     [sd->params.sensorFps.divisor];
132062306a36Sopenharmony_ci			setflicker = 1;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci			new_exposure = sd->params.flickerControl.coarseJump-1;
132362306a36Sopenharmony_ci			while (new_exposure < 2 * old_exposure &&
132462306a36Sopenharmony_ci			       new_exposure +
132562306a36Sopenharmony_ci			       sd->params.flickerControl.coarseJump < max_exp)
132662306a36Sopenharmony_ci				new_exposure +=
132762306a36Sopenharmony_ci					sd->params.flickerControl.coarseJump;
132862306a36Sopenharmony_ci			sd->params.exposure.coarseExpLo = new_exposure & 0xff;
132962306a36Sopenharmony_ci			sd->params.exposure.coarseExpHi = new_exposure >> 8;
133062306a36Sopenharmony_ci			setexp = 1;
133162306a36Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
133262306a36Sopenharmony_ci			gspca_dbg(gspca_dev, D_CONF, "Automatically increasing sensor_fps\n");
133362306a36Sopenharmony_ci		}
133462306a36Sopenharmony_ci	} else {
133562306a36Sopenharmony_ci		/* Flicker control off */
133662306a36Sopenharmony_ci		if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
133762306a36Sopenharmony_ci		     sd->exposure_status == EXPOSURE_DARK) &&
133862306a36Sopenharmony_ci		    sd->exposure_count >= DARK_TIME * framerate &&
133962306a36Sopenharmony_ci		    sd->params.sensorFps.divisor < 2) {
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci			/* dark for too long */
134262306a36Sopenharmony_ci			++sd->params.sensorFps.divisor;
134362306a36Sopenharmony_ci			setfps = 1;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci			if (sd->params.exposure.gain > 0) {
134662306a36Sopenharmony_ci				--sd->params.exposure.gain;
134762306a36Sopenharmony_ci				setexp = 1;
134862306a36Sopenharmony_ci			}
134962306a36Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
135062306a36Sopenharmony_ci			gspca_dbg(gspca_dev, D_CONF, "Automatically decreasing sensor_fps\n");
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci		} else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
135362306a36Sopenharmony_ci			    sd->exposure_status == EXPOSURE_LIGHT) &&
135462306a36Sopenharmony_ci			   sd->exposure_count >= LIGHT_TIME * framerate &&
135562306a36Sopenharmony_ci			   sd->params.sensorFps.divisor > 0) {
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci			/* light for too long */
135862306a36Sopenharmony_ci			--sd->params.sensorFps.divisor;
135962306a36Sopenharmony_ci			setfps = 1;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci			if (sd->params.exposure.gain <
136262306a36Sopenharmony_ci			    sd->params.exposure.gainMode - 1) {
136362306a36Sopenharmony_ci				++sd->params.exposure.gain;
136462306a36Sopenharmony_ci				setexp = 1;
136562306a36Sopenharmony_ci			}
136662306a36Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
136762306a36Sopenharmony_ci			gspca_dbg(gspca_dev, D_CONF, "Automatically increasing sensor_fps\n");
136862306a36Sopenharmony_ci		}
136962306a36Sopenharmony_ci	}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	if (setexp)
137262306a36Sopenharmony_ci		command_setexposure(gspca_dev);
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	if (setfps)
137562306a36Sopenharmony_ci		command_setsensorfps(gspca_dev);
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	if (setflicker)
137862306a36Sopenharmony_ci		command_setflickerctrl(gspca_dev);
137962306a36Sopenharmony_ci}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci/*-----------------------------------------------------------------*/
138262306a36Sopenharmony_ci/* if flicker is switched off, this function switches it back on.It checks,
138362306a36Sopenharmony_ci   however, that conditions are suitable before restarting it.
138462306a36Sopenharmony_ci   This should only be called for firmware version 1.2.
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci   It also adjust the colour balance when an exposure step is detected - as
138762306a36Sopenharmony_ci   long as flicker is running
138862306a36Sopenharmony_ci*/
138962306a36Sopenharmony_cistatic void restart_flicker(struct gspca_dev *gspca_dev)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
139262306a36Sopenharmony_ci	int cam_exposure, old_exp;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	if (!FIRMWARE_VERSION(1, 2))
139562306a36Sopenharmony_ci		return;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	cam_exposure = atomic_read(&sd->cam_exposure);
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	if (sd->params.flickerControl.flickerMode == 0 ||
140062306a36Sopenharmony_ci	    cam_exposure == 0)
140162306a36Sopenharmony_ci		return;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	old_exp = sd->params.exposure.coarseExpLo +
140462306a36Sopenharmony_ci		  sd->params.exposure.coarseExpHi*256;
140562306a36Sopenharmony_ci	/*
140662306a36Sopenharmony_ci	  see how far away camera exposure is from a valid
140762306a36Sopenharmony_ci	  flicker exposure value
140862306a36Sopenharmony_ci	*/
140962306a36Sopenharmony_ci	cam_exposure %= sd->params.flickerControl.coarseJump;
141062306a36Sopenharmony_ci	if (!sd->params.flickerControl.disabled &&
141162306a36Sopenharmony_ci	    cam_exposure <= sd->params.flickerControl.coarseJump - 3) {
141262306a36Sopenharmony_ci		/* Flicker control auto-disabled */
141362306a36Sopenharmony_ci		sd->params.flickerControl.disabled = 1;
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	if (sd->params.flickerControl.disabled &&
141762306a36Sopenharmony_ci	    old_exp > sd->params.flickerControl.coarseJump +
141862306a36Sopenharmony_ci		      ROUND_UP_EXP_FOR_FLICKER) {
141962306a36Sopenharmony_ci		/* exposure is now high enough to switch
142062306a36Sopenharmony_ci		   flicker control back on */
142162306a36Sopenharmony_ci		set_flicker(gspca_dev, 1, 1);
142262306a36Sopenharmony_ci	}
142362306a36Sopenharmony_ci}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci/* this function is called at probe time */
142662306a36Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev,
142762306a36Sopenharmony_ci			const struct usb_device_id *id)
142862306a36Sopenharmony_ci{
142962306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
143062306a36Sopenharmony_ci	struct cam *cam;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
143362306a36Sopenharmony_ci	reset_camera_params(gspca_dev);
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)\n",
143662306a36Sopenharmony_ci		  id->idVendor, id->idProduct);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	cam = &gspca_dev->cam;
143962306a36Sopenharmony_ci	cam->cam_mode = mode;
144062306a36Sopenharmony_ci	cam->nmodes = ARRAY_SIZE(mode);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	goto_low_power(gspca_dev);
144362306a36Sopenharmony_ci	/* Check the firmware version. */
144462306a36Sopenharmony_ci	sd->params.version.firmwareVersion = 0;
144562306a36Sopenharmony_ci	get_version_information(gspca_dev);
144662306a36Sopenharmony_ci	if (sd->params.version.firmwareVersion != 1) {
144762306a36Sopenharmony_ci		gspca_err(gspca_dev, "only firmware version 1 is supported (got: %d)\n",
144862306a36Sopenharmony_ci			  sd->params.version.firmwareVersion);
144962306a36Sopenharmony_ci		return -ENODEV;
145062306a36Sopenharmony_ci	}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	/* A bug in firmware 1-02 limits gainMode to 2 */
145362306a36Sopenharmony_ci	if (sd->params.version.firmwareRevision <= 2 &&
145462306a36Sopenharmony_ci	    sd->params.exposure.gainMode > 2) {
145562306a36Sopenharmony_ci		sd->params.exposure.gainMode = 2;
145662306a36Sopenharmony_ci	}
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	/* set QX3 detected flag */
145962306a36Sopenharmony_ci	sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
146062306a36Sopenharmony_ci				       sd->params.pnpID.product == 0x0001);
146162306a36Sopenharmony_ci	return 0;
146262306a36Sopenharmony_ci}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci/* -- start the camera -- */
146562306a36Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev)
146662306a36Sopenharmony_ci{
146762306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
146862306a36Sopenharmony_ci	int priv, ret;
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	/* Start the camera in low power mode */
147162306a36Sopenharmony_ci	if (goto_low_power(gspca_dev)) {
147262306a36Sopenharmony_ci		if (sd->params.status.systemState != WARM_BOOT_STATE) {
147362306a36Sopenharmony_ci			gspca_err(gspca_dev, "unexpected systemstate: %02x\n",
147462306a36Sopenharmony_ci				  sd->params.status.systemState);
147562306a36Sopenharmony_ci			printstatus(gspca_dev, &sd->params);
147662306a36Sopenharmony_ci			return -ENODEV;
147762306a36Sopenharmony_ci		}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci		/* FIXME: this is just dirty trial and error */
148062306a36Sopenharmony_ci		ret = goto_high_power(gspca_dev);
148162306a36Sopenharmony_ci		if (ret)
148262306a36Sopenharmony_ci			return ret;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci		ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame,
148562306a36Sopenharmony_ci				 0, 0, 0, 0);
148662306a36Sopenharmony_ci		if (ret)
148762306a36Sopenharmony_ci			return ret;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci		ret = goto_low_power(gspca_dev);
149062306a36Sopenharmony_ci		if (ret)
149162306a36Sopenharmony_ci			return ret;
149262306a36Sopenharmony_ci	}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	/* procedure described in developer's guide p3-28 */
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	/* Check the firmware version. */
149762306a36Sopenharmony_ci	sd->params.version.firmwareVersion = 0;
149862306a36Sopenharmony_ci	get_version_information(gspca_dev);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	/* The fatal error checking should be done after
150162306a36Sopenharmony_ci	 * the camera powers up (developer's guide p 3-38) */
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	/* Set streamState before transition to high power to avoid bug
150462306a36Sopenharmony_ci	 * in firmware 1-02 */
150562306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus,
150662306a36Sopenharmony_ci			 STREAMSTATE, 0, STREAM_NOT_READY, 0);
150762306a36Sopenharmony_ci	if (ret)
150862306a36Sopenharmony_ci		return ret;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	/* GotoHiPower */
151162306a36Sopenharmony_ci	ret = goto_high_power(gspca_dev);
151262306a36Sopenharmony_ci	if (ret)
151362306a36Sopenharmony_ci		return ret;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	/* Check the camera status */
151662306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
151762306a36Sopenharmony_ci	if (ret)
151862306a36Sopenharmony_ci		return ret;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	if (sd->params.status.fatalError) {
152162306a36Sopenharmony_ci		gspca_err(gspca_dev, "fatal_error: %04x, vp_status: %04x\n",
152262306a36Sopenharmony_ci			  sd->params.status.fatalError,
152362306a36Sopenharmony_ci			  sd->params.status.vpStatus);
152462306a36Sopenharmony_ci		return -EIO;
152562306a36Sopenharmony_ci	}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	/* VPVersion can't be retrieved before the camera is in HiPower,
152862306a36Sopenharmony_ci	 * so get it here instead of in get_version_information. */
152962306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
153062306a36Sopenharmony_ci	if (ret)
153162306a36Sopenharmony_ci		return ret;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	/* Determine video mode settings */
153462306a36Sopenharmony_ci	sd->params.streamStartLine = 120;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
153762306a36Sopenharmony_ci	if (priv & 0x01) { /* crop */
153862306a36Sopenharmony_ci		sd->params.roi.colStart = 2;
153962306a36Sopenharmony_ci		sd->params.roi.rowStart = 6;
154062306a36Sopenharmony_ci	} else {
154162306a36Sopenharmony_ci		sd->params.roi.colStart = 0;
154262306a36Sopenharmony_ci		sd->params.roi.rowStart = 0;
154362306a36Sopenharmony_ci	}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	if (priv & 0x02) { /* quarter */
154662306a36Sopenharmony_ci		sd->params.format.videoSize = VIDEOSIZE_QCIF;
154762306a36Sopenharmony_ci		sd->params.roi.colStart /= 2;
154862306a36Sopenharmony_ci		sd->params.roi.rowStart /= 2;
154962306a36Sopenharmony_ci		sd->params.streamStartLine /= 2;
155062306a36Sopenharmony_ci	} else
155162306a36Sopenharmony_ci		sd->params.format.videoSize = VIDEOSIZE_CIF;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	sd->params.roi.colEnd = sd->params.roi.colStart +
155462306a36Sopenharmony_ci				(gspca_dev->pixfmt.width >> 3);
155562306a36Sopenharmony_ci	sd->params.roi.rowEnd = sd->params.roi.rowStart +
155662306a36Sopenharmony_ci				(gspca_dev->pixfmt.height >> 2);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	/* And now set the camera to a known state */
155962306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode,
156062306a36Sopenharmony_ci			 CPIA_GRAB_CONTINEOUS, 0, 0, 0);
156162306a36Sopenharmony_ci	if (ret)
156262306a36Sopenharmony_ci		return ret;
156362306a36Sopenharmony_ci	/* We start with compression disabled, as we need one uncompressed
156462306a36Sopenharmony_ci	   frame to handle later compressed frames */
156562306a36Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression,
156662306a36Sopenharmony_ci			 CPIA_COMPRESSION_NONE,
156762306a36Sopenharmony_ci			 NO_DECIMATION, 0, 0);
156862306a36Sopenharmony_ci	if (ret)
156962306a36Sopenharmony_ci		return ret;
157062306a36Sopenharmony_ci	ret = command_setcompressiontarget(gspca_dev);
157162306a36Sopenharmony_ci	if (ret)
157262306a36Sopenharmony_ci		return ret;
157362306a36Sopenharmony_ci	ret = command_setcolourparams(gspca_dev);
157462306a36Sopenharmony_ci	if (ret)
157562306a36Sopenharmony_ci		return ret;
157662306a36Sopenharmony_ci	ret = command_setformat(gspca_dev);
157762306a36Sopenharmony_ci	if (ret)
157862306a36Sopenharmony_ci		return ret;
157962306a36Sopenharmony_ci	ret = command_setyuvtresh(gspca_dev);
158062306a36Sopenharmony_ci	if (ret)
158162306a36Sopenharmony_ci		return ret;
158262306a36Sopenharmony_ci	ret = command_setecptiming(gspca_dev);
158362306a36Sopenharmony_ci	if (ret)
158462306a36Sopenharmony_ci		return ret;
158562306a36Sopenharmony_ci	ret = command_setcompressionparams(gspca_dev);
158662306a36Sopenharmony_ci	if (ret)
158762306a36Sopenharmony_ci		return ret;
158862306a36Sopenharmony_ci	ret = command_setexposure(gspca_dev);
158962306a36Sopenharmony_ci	if (ret)
159062306a36Sopenharmony_ci		return ret;
159162306a36Sopenharmony_ci	ret = command_setcolourbalance(gspca_dev);
159262306a36Sopenharmony_ci	if (ret)
159362306a36Sopenharmony_ci		return ret;
159462306a36Sopenharmony_ci	ret = command_setsensorfps(gspca_dev);
159562306a36Sopenharmony_ci	if (ret)
159662306a36Sopenharmony_ci		return ret;
159762306a36Sopenharmony_ci	ret = command_setapcor(gspca_dev);
159862306a36Sopenharmony_ci	if (ret)
159962306a36Sopenharmony_ci		return ret;
160062306a36Sopenharmony_ci	ret = command_setflickerctrl(gspca_dev);
160162306a36Sopenharmony_ci	if (ret)
160262306a36Sopenharmony_ci		return ret;
160362306a36Sopenharmony_ci	ret = command_setvloffset(gspca_dev);
160462306a36Sopenharmony_ci	if (ret)
160562306a36Sopenharmony_ci		return ret;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	/* Start stream */
160862306a36Sopenharmony_ci	ret = command_resume(gspca_dev);
160962306a36Sopenharmony_ci	if (ret)
161062306a36Sopenharmony_ci		return ret;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	/* Wait 6 frames before turning compression on for the sensor to get
161362306a36Sopenharmony_ci	   all settings and AEC/ACB to settle */
161462306a36Sopenharmony_ci	sd->first_frame = 6;
161562306a36Sopenharmony_ci	sd->exposure_status = EXPOSURE_NORMAL;
161662306a36Sopenharmony_ci	sd->exposure_count = 0;
161762306a36Sopenharmony_ci	atomic_set(&sd->cam_exposure, 0);
161862306a36Sopenharmony_ci	atomic_set(&sd->fps, 0);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	return 0;
162162306a36Sopenharmony_ci}
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev)
162462306a36Sopenharmony_ci{
162562306a36Sopenharmony_ci	struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	command_pause(gspca_dev);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	/* save camera state for later open (developers guide ch 3.5.3) */
163062306a36Sopenharmony_ci	save_camera_state(gspca_dev);
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	/* GotoLoPower */
163362306a36Sopenharmony_ci	goto_low_power(gspca_dev);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	/* Update the camera status */
163662306a36Sopenharmony_ci	do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
163962306a36Sopenharmony_ci	/* If the last button state is pressed, release it now! */
164062306a36Sopenharmony_ci	if (sd->params.qx3.button) {
164162306a36Sopenharmony_ci		/* The camera latch will hold the pressed state until we reset
164262306a36Sopenharmony_ci		   the latch, so we do not reset sd->params.qx3.button now, to
164362306a36Sopenharmony_ci		   avoid a false keypress being reported the next sd_start */
164462306a36Sopenharmony_ci		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
164562306a36Sopenharmony_ci		input_sync(gspca_dev->input_dev);
164662306a36Sopenharmony_ci	}
164762306a36Sopenharmony_ci#endif
164862306a36Sopenharmony_ci}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci/* this function is called at probe and resume time */
165162306a36Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
165262306a36Sopenharmony_ci{
165362306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
165462306a36Sopenharmony_ci	int ret;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	/* Start / Stop the camera to make sure we are talking to
165762306a36Sopenharmony_ci	   a supported camera, and to get some information from it
165862306a36Sopenharmony_ci	   to print. */
165962306a36Sopenharmony_ci	ret = sd_start(gspca_dev);
166062306a36Sopenharmony_ci	if (ret)
166162306a36Sopenharmony_ci		return ret;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	/* Ensure the QX3 illuminators' states are restored upon resume,
166462306a36Sopenharmony_ci	   or disable the illuminator controls, if this isn't a QX3 */
166562306a36Sopenharmony_ci	if (sd->params.qx3.qx3_detected)
166662306a36Sopenharmony_ci		command_setlights(gspca_dev);
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	sd_stopN(gspca_dev);
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "CPIA Version:             %d.%02d (%d.%d)\n",
167162306a36Sopenharmony_ci		  sd->params.version.firmwareVersion,
167262306a36Sopenharmony_ci		  sd->params.version.firmwareRevision,
167362306a36Sopenharmony_ci		  sd->params.version.vcVersion,
167462306a36Sopenharmony_ci		  sd->params.version.vcRevision);
167562306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "CPIA PnP-ID:              %04x:%04x:%04x",
167662306a36Sopenharmony_ci		  sd->params.pnpID.vendor, sd->params.pnpID.product,
167762306a36Sopenharmony_ci		  sd->params.pnpID.deviceRevision);
167862306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "VP-Version:               %d.%d %04x",
167962306a36Sopenharmony_ci		  sd->params.vpVersion.vpVersion,
168062306a36Sopenharmony_ci		  sd->params.vpVersion.vpRevision,
168162306a36Sopenharmony_ci		  sd->params.vpVersion.cameraHeadID);
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	return 0;
168462306a36Sopenharmony_ci}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev,
168762306a36Sopenharmony_ci			u8 *data,
168862306a36Sopenharmony_ci			int len)
168962306a36Sopenharmony_ci{
169062306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	/* Check for SOF */
169362306a36Sopenharmony_ci	if (len >= 64 &&
169462306a36Sopenharmony_ci	    data[0] == MAGIC_0 && data[1] == MAGIC_1 &&
169562306a36Sopenharmony_ci	    data[16] == sd->params.format.videoSize &&
169662306a36Sopenharmony_ci	    data[17] == sd->params.format.subSample &&
169762306a36Sopenharmony_ci	    data[18] == sd->params.format.yuvOrder &&
169862306a36Sopenharmony_ci	    data[24] == sd->params.roi.colStart &&
169962306a36Sopenharmony_ci	    data[25] == sd->params.roi.colEnd &&
170062306a36Sopenharmony_ci	    data[26] == sd->params.roi.rowStart &&
170162306a36Sopenharmony_ci	    data[27] == sd->params.roi.rowEnd) {
170262306a36Sopenharmony_ci		u8 *image;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci		atomic_set(&sd->cam_exposure, data[39] * 2);
170562306a36Sopenharmony_ci		atomic_set(&sd->fps, data[41]);
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci		/* Check for proper EOF for last frame */
170862306a36Sopenharmony_ci		image = gspca_dev->image;
170962306a36Sopenharmony_ci		if (image != NULL &&
171062306a36Sopenharmony_ci		    gspca_dev->image_len > 4 &&
171162306a36Sopenharmony_ci		    image[gspca_dev->image_len - 4] == 0xff &&
171262306a36Sopenharmony_ci		    image[gspca_dev->image_len - 3] == 0xff &&
171362306a36Sopenharmony_ci		    image[gspca_dev->image_len - 2] == 0xff &&
171462306a36Sopenharmony_ci		    image[gspca_dev->image_len - 1] == 0xff)
171562306a36Sopenharmony_ci			gspca_frame_add(gspca_dev, LAST_PACKET,
171662306a36Sopenharmony_ci						NULL, 0);
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
171962306a36Sopenharmony_ci		return;
172062306a36Sopenharmony_ci	}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
172362306a36Sopenharmony_ci}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_cistatic void sd_dq_callback(struct gspca_dev *gspca_dev)
172662306a36Sopenharmony_ci{
172762306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	/* Set the normal compression settings once we have captured a
173062306a36Sopenharmony_ci	   few uncompressed frames (and AEC has hopefully settled) */
173162306a36Sopenharmony_ci	if (sd->first_frame) {
173262306a36Sopenharmony_ci		sd->first_frame--;
173362306a36Sopenharmony_ci		if (sd->first_frame == 0)
173462306a36Sopenharmony_ci			command_setcompression(gspca_dev);
173562306a36Sopenharmony_ci	}
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	/* Switch flicker control back on if it got turned off */
173862306a36Sopenharmony_ci	restart_flicker(gspca_dev);
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	/* If AEC is enabled, monitor the exposure and
174162306a36Sopenharmony_ci	   adjust the sensor frame rate if needed */
174262306a36Sopenharmony_ci	if (sd->params.exposure.expMode == 2)
174362306a36Sopenharmony_ci		monitor_exposure(gspca_dev);
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	/* Update our knowledge of the camera state */
174662306a36Sopenharmony_ci	do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
174762306a36Sopenharmony_ci	do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
174862306a36Sopenharmony_ci}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl)
175162306a36Sopenharmony_ci{
175262306a36Sopenharmony_ci	struct gspca_dev *gspca_dev =
175362306a36Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
175462306a36Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	gspca_dev->usb_err = 0;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
175962306a36Sopenharmony_ci		return 0;
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	switch (ctrl->id) {
176262306a36Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
176362306a36Sopenharmony_ci		sd->params.colourParams.brightness = ctrl->val;
176462306a36Sopenharmony_ci		sd->params.flickerControl.allowableOverExposure =
176562306a36Sopenharmony_ci			find_over_exposure(sd->params.colourParams.brightness);
176662306a36Sopenharmony_ci		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
176762306a36Sopenharmony_ci		if (!gspca_dev->usb_err)
176862306a36Sopenharmony_ci			gspca_dev->usb_err = command_setflickerctrl(gspca_dev);
176962306a36Sopenharmony_ci		break;
177062306a36Sopenharmony_ci	case V4L2_CID_CONTRAST:
177162306a36Sopenharmony_ci		sd->params.colourParams.contrast = ctrl->val;
177262306a36Sopenharmony_ci		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
177362306a36Sopenharmony_ci		break;
177462306a36Sopenharmony_ci	case V4L2_CID_SATURATION:
177562306a36Sopenharmony_ci		sd->params.colourParams.saturation = ctrl->val;
177662306a36Sopenharmony_ci		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
177762306a36Sopenharmony_ci		break;
177862306a36Sopenharmony_ci	case V4L2_CID_POWER_LINE_FREQUENCY:
177962306a36Sopenharmony_ci		sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
178062306a36Sopenharmony_ci		sd->params.flickerControl.coarseJump =
178162306a36Sopenharmony_ci			flicker_jumps[sd->mainsFreq]
178262306a36Sopenharmony_ci			[sd->params.sensorFps.baserate]
178362306a36Sopenharmony_ci			[sd->params.sensorFps.divisor];
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci		gspca_dev->usb_err = set_flicker(gspca_dev,
178662306a36Sopenharmony_ci			ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
178762306a36Sopenharmony_ci			gspca_dev->streaming);
178862306a36Sopenharmony_ci		break;
178962306a36Sopenharmony_ci	case V4L2_CID_ILLUMINATORS_1:
179062306a36Sopenharmony_ci		sd->params.qx3.bottomlight = ctrl->val;
179162306a36Sopenharmony_ci		gspca_dev->usb_err = command_setlights(gspca_dev);
179262306a36Sopenharmony_ci		break;
179362306a36Sopenharmony_ci	case V4L2_CID_ILLUMINATORS_2:
179462306a36Sopenharmony_ci		sd->params.qx3.toplight = ctrl->val;
179562306a36Sopenharmony_ci		gspca_dev->usb_err = command_setlights(gspca_dev);
179662306a36Sopenharmony_ci		break;
179762306a36Sopenharmony_ci	case CPIA1_CID_COMP_TARGET:
179862306a36Sopenharmony_ci		sd->params.compressionTarget.frTargeting = ctrl->val;
179962306a36Sopenharmony_ci		gspca_dev->usb_err = command_setcompressiontarget(gspca_dev);
180062306a36Sopenharmony_ci		break;
180162306a36Sopenharmony_ci	}
180262306a36Sopenharmony_ci	return gspca_dev->usb_err;
180362306a36Sopenharmony_ci}
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = {
180662306a36Sopenharmony_ci	.s_ctrl = sd_s_ctrl,
180762306a36Sopenharmony_ci};
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev)
181062306a36Sopenharmony_ci{
181162306a36Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
181262306a36Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
181362306a36Sopenharmony_ci	static const char * const comp_target_menu[] = {
181462306a36Sopenharmony_ci		"Quality",
181562306a36Sopenharmony_ci		"Framerate",
181662306a36Sopenharmony_ci		NULL
181762306a36Sopenharmony_ci	};
181862306a36Sopenharmony_ci	static const struct v4l2_ctrl_config comp_target = {
181962306a36Sopenharmony_ci		.ops = &sd_ctrl_ops,
182062306a36Sopenharmony_ci		.id = CPIA1_CID_COMP_TARGET,
182162306a36Sopenharmony_ci		.type = V4L2_CTRL_TYPE_MENU,
182262306a36Sopenharmony_ci		.name = "Compression Target",
182362306a36Sopenharmony_ci		.qmenu = comp_target_menu,
182462306a36Sopenharmony_ci		.max = 1,
182562306a36Sopenharmony_ci		.def = COMP_TARGET_DEF,
182662306a36Sopenharmony_ci	};
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = hdl;
182962306a36Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 7);
183062306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
183162306a36Sopenharmony_ci			V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF);
183262306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
183362306a36Sopenharmony_ci			V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF);
183462306a36Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
183562306a36Sopenharmony_ci			V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF);
183662306a36Sopenharmony_ci	sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
183762306a36Sopenharmony_ci			V4L2_CID_POWER_LINE_FREQUENCY,
183862306a36Sopenharmony_ci			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
183962306a36Sopenharmony_ci			FREQ_DEF);
184062306a36Sopenharmony_ci	if (sd->params.qx3.qx3_detected) {
184162306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
184262306a36Sopenharmony_ci				V4L2_CID_ILLUMINATORS_1, 0, 1, 1,
184362306a36Sopenharmony_ci				ILLUMINATORS_1_DEF);
184462306a36Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
184562306a36Sopenharmony_ci				V4L2_CID_ILLUMINATORS_2, 0, 1, 1,
184662306a36Sopenharmony_ci				ILLUMINATORS_2_DEF);
184762306a36Sopenharmony_ci	}
184862306a36Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &comp_target, NULL);
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	if (hdl->error) {
185162306a36Sopenharmony_ci		pr_err("Could not initialize controls\n");
185262306a36Sopenharmony_ci		return hdl->error;
185362306a36Sopenharmony_ci	}
185462306a36Sopenharmony_ci	return 0;
185562306a36Sopenharmony_ci}
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci/* sub-driver description */
185862306a36Sopenharmony_cistatic const struct sd_desc sd_desc = {
185962306a36Sopenharmony_ci	.name = MODULE_NAME,
186062306a36Sopenharmony_ci	.config = sd_config,
186162306a36Sopenharmony_ci	.init = sd_init,
186262306a36Sopenharmony_ci	.init_controls = sd_init_controls,
186362306a36Sopenharmony_ci	.start = sd_start,
186462306a36Sopenharmony_ci	.stopN = sd_stopN,
186562306a36Sopenharmony_ci	.dq_callback = sd_dq_callback,
186662306a36Sopenharmony_ci	.pkt_scan = sd_pkt_scan,
186762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
186862306a36Sopenharmony_ci	.other_input = 1,
186962306a36Sopenharmony_ci#endif
187062306a36Sopenharmony_ci};
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci/* -- module initialisation -- */
187362306a36Sopenharmony_cistatic const struct usb_device_id device_table[] = {
187462306a36Sopenharmony_ci	{USB_DEVICE(0x0553, 0x0002)},
187562306a36Sopenharmony_ci	{USB_DEVICE(0x0813, 0x0001)},
187662306a36Sopenharmony_ci	{}
187762306a36Sopenharmony_ci};
187862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci/* -- device connect -- */
188162306a36Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
188262306a36Sopenharmony_ci			const struct usb_device_id *id)
188362306a36Sopenharmony_ci{
188462306a36Sopenharmony_ci	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
188562306a36Sopenharmony_ci				THIS_MODULE);
188662306a36Sopenharmony_ci}
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_cistatic struct usb_driver sd_driver = {
188962306a36Sopenharmony_ci	.name = MODULE_NAME,
189062306a36Sopenharmony_ci	.id_table = device_table,
189162306a36Sopenharmony_ci	.probe = sd_probe,
189262306a36Sopenharmony_ci	.disconnect = gspca_disconnect,
189362306a36Sopenharmony_ci#ifdef CONFIG_PM
189462306a36Sopenharmony_ci	.suspend = gspca_suspend,
189562306a36Sopenharmony_ci	.resume = gspca_resume,
189662306a36Sopenharmony_ci	.reset_resume = gspca_resume,
189762306a36Sopenharmony_ci#endif
189862306a36Sopenharmony_ci};
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_cimodule_usb_driver(sd_driver);
1901