18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * cpia CPiA (1) gspca driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This module is adapted from the in kernel v4l1 cpia driver which is :
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * (C) Copyright 1999-2000 Peter Pregler
108c2ecf20Sopenharmony_ci * (C) Copyright 1999-2000 Scott J. Bertin
118c2ecf20Sopenharmony_ci * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
128c2ecf20Sopenharmony_ci * (C) Copyright 2000 STMicroelectronics
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define MODULE_NAME "cpia1"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/input.h>
208c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
218c2ecf20Sopenharmony_ci#include <linux/bitops.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "gspca.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Vision CPiA");
278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* constant value's */
308c2ecf20Sopenharmony_ci#define MAGIC_0		0x19
318c2ecf20Sopenharmony_ci#define MAGIC_1		0x68
328c2ecf20Sopenharmony_ci#define DATA_IN		0xc0
338c2ecf20Sopenharmony_ci#define DATA_OUT	0x40
348c2ecf20Sopenharmony_ci#define VIDEOSIZE_QCIF	0	/* 176x144 */
358c2ecf20Sopenharmony_ci#define VIDEOSIZE_CIF	1	/* 352x288 */
368c2ecf20Sopenharmony_ci#define SUBSAMPLE_420	0
378c2ecf20Sopenharmony_ci#define SUBSAMPLE_422	1
388c2ecf20Sopenharmony_ci#define YUVORDER_YUYV	0
398c2ecf20Sopenharmony_ci#define YUVORDER_UYVY	1
408c2ecf20Sopenharmony_ci#define NOT_COMPRESSED	0
418c2ecf20Sopenharmony_ci#define COMPRESSED	1
428c2ecf20Sopenharmony_ci#define NO_DECIMATION	0
438c2ecf20Sopenharmony_ci#define DECIMATION_ENAB	1
448c2ecf20Sopenharmony_ci#define EOI		0xff	/* End Of Image */
458c2ecf20Sopenharmony_ci#define EOL		0xfd	/* End Of Line */
468c2ecf20Sopenharmony_ci#define FRAME_HEADER_SIZE	64
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* Image grab modes */
498c2ecf20Sopenharmony_ci#define CPIA_GRAB_SINGLE	0
508c2ecf20Sopenharmony_ci#define CPIA_GRAB_CONTINEOUS	1
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* Compression parameters */
538c2ecf20Sopenharmony_ci#define CPIA_COMPRESSION_NONE	0
548c2ecf20Sopenharmony_ci#define CPIA_COMPRESSION_AUTO	1
558c2ecf20Sopenharmony_ci#define CPIA_COMPRESSION_MANUAL	2
568c2ecf20Sopenharmony_ci#define CPIA_COMPRESSION_TARGET_QUALITY         0
578c2ecf20Sopenharmony_ci#define CPIA_COMPRESSION_TARGET_FRAMERATE       1
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* Return offsets for GetCameraState */
608c2ecf20Sopenharmony_ci#define SYSTEMSTATE	0
618c2ecf20Sopenharmony_ci#define GRABSTATE	1
628c2ecf20Sopenharmony_ci#define STREAMSTATE	2
638c2ecf20Sopenharmony_ci#define FATALERROR	3
648c2ecf20Sopenharmony_ci#define CMDERROR	4
658c2ecf20Sopenharmony_ci#define DEBUGFLAGS	5
668c2ecf20Sopenharmony_ci#define VPSTATUS	6
678c2ecf20Sopenharmony_ci#define ERRORCODE	7
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* SystemState */
708c2ecf20Sopenharmony_ci#define UNINITIALISED_STATE	0
718c2ecf20Sopenharmony_ci#define PASS_THROUGH_STATE	1
728c2ecf20Sopenharmony_ci#define LO_POWER_STATE		2
738c2ecf20Sopenharmony_ci#define HI_POWER_STATE		3
748c2ecf20Sopenharmony_ci#define WARM_BOOT_STATE		4
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* GrabState */
778c2ecf20Sopenharmony_ci#define GRAB_IDLE		0
788c2ecf20Sopenharmony_ci#define GRAB_ACTIVE		1
798c2ecf20Sopenharmony_ci#define GRAB_DONE		2
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* StreamState */
828c2ecf20Sopenharmony_ci#define STREAM_NOT_READY	0
838c2ecf20Sopenharmony_ci#define STREAM_READY		1
848c2ecf20Sopenharmony_ci#define STREAM_OPEN		2
858c2ecf20Sopenharmony_ci#define STREAM_PAUSED		3
868c2ecf20Sopenharmony_ci#define STREAM_FINISHED		4
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* Fatal Error, CmdError, and DebugFlags */
898c2ecf20Sopenharmony_ci#define CPIA_FLAG	  1
908c2ecf20Sopenharmony_ci#define SYSTEM_FLAG	  2
918c2ecf20Sopenharmony_ci#define INT_CTRL_FLAG	  4
928c2ecf20Sopenharmony_ci#define PROCESS_FLAG	  8
938c2ecf20Sopenharmony_ci#define COM_FLAG	 16
948c2ecf20Sopenharmony_ci#define VP_CTRL_FLAG	 32
958c2ecf20Sopenharmony_ci#define CAPTURE_FLAG	 64
968c2ecf20Sopenharmony_ci#define DEBUG_FLAG	128
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* VPStatus */
998c2ecf20Sopenharmony_ci#define VP_STATE_OK			0x00
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci#define VP_STATE_FAILED_VIDEOINIT	0x01
1028c2ecf20Sopenharmony_ci#define VP_STATE_FAILED_AECACBINIT	0x02
1038c2ecf20Sopenharmony_ci#define VP_STATE_AEC_MAX		0x04
1048c2ecf20Sopenharmony_ci#define VP_STATE_ACB_BMAX		0x08
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci#define VP_STATE_ACB_RMIN		0x10
1078c2ecf20Sopenharmony_ci#define VP_STATE_ACB_GMIN		0x20
1088c2ecf20Sopenharmony_ci#define VP_STATE_ACB_RMAX		0x40
1098c2ecf20Sopenharmony_ci#define VP_STATE_ACB_GMAX		0x80
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* default (minimum) compensation values */
1128c2ecf20Sopenharmony_ci#define COMP_RED        220
1138c2ecf20Sopenharmony_ci#define COMP_GREEN1     214
1148c2ecf20Sopenharmony_ci#define COMP_GREEN2     COMP_GREEN1
1158c2ecf20Sopenharmony_ci#define COMP_BLUE       230
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/* exposure status */
1188c2ecf20Sopenharmony_ci#define EXPOSURE_VERY_LIGHT 0
1198c2ecf20Sopenharmony_ci#define EXPOSURE_LIGHT      1
1208c2ecf20Sopenharmony_ci#define EXPOSURE_NORMAL     2
1218c2ecf20Sopenharmony_ci#define EXPOSURE_DARK       3
1228c2ecf20Sopenharmony_ci#define EXPOSURE_VERY_DARK  4
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#define CPIA_MODULE_CPIA			(0 << 5)
1258c2ecf20Sopenharmony_ci#define CPIA_MODULE_SYSTEM			(1 << 5)
1268c2ecf20Sopenharmony_ci#define CPIA_MODULE_VP_CTRL			(5 << 5)
1278c2ecf20Sopenharmony_ci#define CPIA_MODULE_CAPTURE			(6 << 5)
1288c2ecf20Sopenharmony_ci#define CPIA_MODULE_DEBUG			(7 << 5)
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci#define INPUT (DATA_IN << 8)
1318c2ecf20Sopenharmony_ci#define OUTPUT (DATA_OUT << 8)
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GetCPIAVersion	(INPUT | CPIA_MODULE_CPIA | 1)
1348c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GetPnPID		(INPUT | CPIA_MODULE_CPIA | 2)
1358c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GetCameraStatus	(INPUT | CPIA_MODULE_CPIA | 3)
1368c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GotoHiPower	(OUTPUT | CPIA_MODULE_CPIA | 4)
1378c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GotoLoPower	(OUTPUT | CPIA_MODULE_CPIA | 5)
1388c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GotoSuspend	(OUTPUT | CPIA_MODULE_CPIA | 7)
1398c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GotoPassThrough	(OUTPUT | CPIA_MODULE_CPIA | 8)
1408c2ecf20Sopenharmony_ci#define CPIA_COMMAND_ModifyCameraStatus	(OUTPUT | CPIA_MODULE_CPIA | 10)
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci#define CPIA_COMMAND_ReadVCRegs		(INPUT | CPIA_MODULE_SYSTEM | 1)
1438c2ecf20Sopenharmony_ci#define CPIA_COMMAND_WriteVCReg		(OUTPUT | CPIA_MODULE_SYSTEM | 2)
1448c2ecf20Sopenharmony_ci#define CPIA_COMMAND_ReadMCPorts	(INPUT | CPIA_MODULE_SYSTEM | 3)
1458c2ecf20Sopenharmony_ci#define CPIA_COMMAND_WriteMCPort	(OUTPUT | CPIA_MODULE_SYSTEM | 4)
1468c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetBaudRate	(OUTPUT | CPIA_MODULE_SYSTEM | 5)
1478c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetECPTiming	(OUTPUT | CPIA_MODULE_SYSTEM | 6)
1488c2ecf20Sopenharmony_ci#define CPIA_COMMAND_ReadIDATA		(INPUT | CPIA_MODULE_SYSTEM | 7)
1498c2ecf20Sopenharmony_ci#define CPIA_COMMAND_WriteIDATA		(OUTPUT | CPIA_MODULE_SYSTEM | 8)
1508c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GenericCall	(OUTPUT | CPIA_MODULE_SYSTEM | 9)
1518c2ecf20Sopenharmony_ci#define CPIA_COMMAND_I2CStart		(OUTPUT | CPIA_MODULE_SYSTEM | 10)
1528c2ecf20Sopenharmony_ci#define CPIA_COMMAND_I2CStop		(OUTPUT | CPIA_MODULE_SYSTEM | 11)
1538c2ecf20Sopenharmony_ci#define CPIA_COMMAND_I2CWrite		(OUTPUT | CPIA_MODULE_SYSTEM | 12)
1548c2ecf20Sopenharmony_ci#define CPIA_COMMAND_I2CRead		(INPUT | CPIA_MODULE_SYSTEM | 13)
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GetVPVersion	(INPUT | CPIA_MODULE_VP_CTRL | 1)
1578c2ecf20Sopenharmony_ci#define CPIA_COMMAND_ResetFrameCounter	(INPUT | CPIA_MODULE_VP_CTRL | 2)
1588c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetColourParams	(OUTPUT | CPIA_MODULE_VP_CTRL | 3)
1598c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetExposure	(OUTPUT | CPIA_MODULE_VP_CTRL | 4)
1608c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetColourBalance	(OUTPUT | CPIA_MODULE_VP_CTRL | 6)
1618c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetSensorFPS	(OUTPUT | CPIA_MODULE_VP_CTRL | 7)
1628c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetVPDefaults	(OUTPUT | CPIA_MODULE_VP_CTRL | 8)
1638c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetApcor		(OUTPUT | CPIA_MODULE_VP_CTRL | 9)
1648c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetFlickerCtrl	(OUTPUT | CPIA_MODULE_VP_CTRL | 10)
1658c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetVLOffset	(OUTPUT | CPIA_MODULE_VP_CTRL | 11)
1668c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GetColourParams	(INPUT | CPIA_MODULE_VP_CTRL | 16)
1678c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GetColourBalance	(INPUT | CPIA_MODULE_VP_CTRL | 17)
1688c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GetExposure	(INPUT | CPIA_MODULE_VP_CTRL | 18)
1698c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetSensorMatrix	(OUTPUT | CPIA_MODULE_VP_CTRL | 19)
1708c2ecf20Sopenharmony_ci#define CPIA_COMMAND_ColourBars		(OUTPUT | CPIA_MODULE_VP_CTRL | 25)
1718c2ecf20Sopenharmony_ci#define CPIA_COMMAND_ReadVPRegs		(INPUT | CPIA_MODULE_VP_CTRL | 30)
1728c2ecf20Sopenharmony_ci#define CPIA_COMMAND_WriteVPReg		(OUTPUT | CPIA_MODULE_VP_CTRL | 31)
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GrabFrame		(OUTPUT | CPIA_MODULE_CAPTURE | 1)
1758c2ecf20Sopenharmony_ci#define CPIA_COMMAND_UploadFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 2)
1768c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetGrabMode	(OUTPUT | CPIA_MODULE_CAPTURE | 3)
1778c2ecf20Sopenharmony_ci#define CPIA_COMMAND_InitStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 4)
1788c2ecf20Sopenharmony_ci#define CPIA_COMMAND_FiniStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 5)
1798c2ecf20Sopenharmony_ci#define CPIA_COMMAND_StartStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 6)
1808c2ecf20Sopenharmony_ci#define CPIA_COMMAND_EndStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 7)
1818c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetFormat		(OUTPUT | CPIA_MODULE_CAPTURE | 8)
1828c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetROI		(OUTPUT | CPIA_MODULE_CAPTURE | 9)
1838c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetCompression	(OUTPUT | CPIA_MODULE_CAPTURE | 10)
1848c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
1858c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetYUVThresh	(OUTPUT | CPIA_MODULE_CAPTURE | 12)
1868c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
1878c2ecf20Sopenharmony_ci#define CPIA_COMMAND_DiscardFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 14)
1888c2ecf20Sopenharmony_ci#define CPIA_COMMAND_GrabReset		(OUTPUT | CPIA_MODULE_CAPTURE | 15)
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci#define CPIA_COMMAND_OutputRS232	(OUTPUT | CPIA_MODULE_DEBUG | 1)
1918c2ecf20Sopenharmony_ci#define CPIA_COMMAND_AbortProcess	(OUTPUT | CPIA_MODULE_DEBUG | 4)
1928c2ecf20Sopenharmony_ci#define CPIA_COMMAND_SetDramPage	(OUTPUT | CPIA_MODULE_DEBUG | 5)
1938c2ecf20Sopenharmony_ci#define CPIA_COMMAND_StartDramUpload	(OUTPUT | CPIA_MODULE_DEBUG | 6)
1948c2ecf20Sopenharmony_ci#define CPIA_COMMAND_StartDummyDtream	(OUTPUT | CPIA_MODULE_DEBUG | 8)
1958c2ecf20Sopenharmony_ci#define CPIA_COMMAND_AbortStream	(OUTPUT | CPIA_MODULE_DEBUG | 9)
1968c2ecf20Sopenharmony_ci#define CPIA_COMMAND_DownloadDRAM	(OUTPUT | CPIA_MODULE_DEBUG | 10)
1978c2ecf20Sopenharmony_ci#define CPIA_COMMAND_Null		(OUTPUT | CPIA_MODULE_DEBUG | 11)
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci#define ROUND_UP_EXP_FOR_FLICKER 15
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/* Constants for automatic frame rate adjustment */
2028c2ecf20Sopenharmony_ci#define MAX_EXP       302
2038c2ecf20Sopenharmony_ci#define MAX_EXP_102   255
2048c2ecf20Sopenharmony_ci#define LOW_EXP       140
2058c2ecf20Sopenharmony_ci#define VERY_LOW_EXP   70
2068c2ecf20Sopenharmony_ci#define TC             94
2078c2ecf20Sopenharmony_ci#define	EXP_ACC_DARK   50
2088c2ecf20Sopenharmony_ci#define	EXP_ACC_LIGHT  90
2098c2ecf20Sopenharmony_ci#define HIGH_COMP_102 160
2108c2ecf20Sopenharmony_ci#define MAX_COMP      239
2118c2ecf20Sopenharmony_ci#define DARK_TIME       3
2128c2ecf20Sopenharmony_ci#define LIGHT_TIME      3
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
2158c2ecf20Sopenharmony_ci				sd->params.version.firmwareRevision == (y))
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci#define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000)
2188c2ecf20Sopenharmony_ci#define BRIGHTNESS_DEF 50
2198c2ecf20Sopenharmony_ci#define CONTRAST_DEF 48
2208c2ecf20Sopenharmony_ci#define SATURATION_DEF 50
2218c2ecf20Sopenharmony_ci#define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ
2228c2ecf20Sopenharmony_ci#define ILLUMINATORS_1_DEF 0
2238c2ecf20Sopenharmony_ci#define ILLUMINATORS_2_DEF 0
2248c2ecf20Sopenharmony_ci#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci/* Developer's Guide Table 5 p 3-34
2278c2ecf20Sopenharmony_ci * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
2288c2ecf20Sopenharmony_cistatic u8 flicker_jumps[2][2][4] =
2298c2ecf20Sopenharmony_ci{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
2308c2ecf20Sopenharmony_ci  { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
2318c2ecf20Sopenharmony_ci};
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistruct cam_params {
2348c2ecf20Sopenharmony_ci	struct {
2358c2ecf20Sopenharmony_ci		u8 firmwareVersion;
2368c2ecf20Sopenharmony_ci		u8 firmwareRevision;
2378c2ecf20Sopenharmony_ci		u8 vcVersion;
2388c2ecf20Sopenharmony_ci		u8 vcRevision;
2398c2ecf20Sopenharmony_ci	} version;
2408c2ecf20Sopenharmony_ci	struct {
2418c2ecf20Sopenharmony_ci		u16 vendor;
2428c2ecf20Sopenharmony_ci		u16 product;
2438c2ecf20Sopenharmony_ci		u16 deviceRevision;
2448c2ecf20Sopenharmony_ci	} pnpID;
2458c2ecf20Sopenharmony_ci	struct {
2468c2ecf20Sopenharmony_ci		u8 vpVersion;
2478c2ecf20Sopenharmony_ci		u8 vpRevision;
2488c2ecf20Sopenharmony_ci		u16 cameraHeadID;
2498c2ecf20Sopenharmony_ci	} vpVersion;
2508c2ecf20Sopenharmony_ci	struct {
2518c2ecf20Sopenharmony_ci		u8 systemState;
2528c2ecf20Sopenharmony_ci		u8 grabState;
2538c2ecf20Sopenharmony_ci		u8 streamState;
2548c2ecf20Sopenharmony_ci		u8 fatalError;
2558c2ecf20Sopenharmony_ci		u8 cmdError;
2568c2ecf20Sopenharmony_ci		u8 debugFlags;
2578c2ecf20Sopenharmony_ci		u8 vpStatus;
2588c2ecf20Sopenharmony_ci		u8 errorCode;
2598c2ecf20Sopenharmony_ci	} status;
2608c2ecf20Sopenharmony_ci	struct {
2618c2ecf20Sopenharmony_ci		u8 brightness;
2628c2ecf20Sopenharmony_ci		u8 contrast;
2638c2ecf20Sopenharmony_ci		u8 saturation;
2648c2ecf20Sopenharmony_ci	} colourParams;
2658c2ecf20Sopenharmony_ci	struct {
2668c2ecf20Sopenharmony_ci		u8 gainMode;
2678c2ecf20Sopenharmony_ci		u8 expMode;
2688c2ecf20Sopenharmony_ci		u8 compMode;
2698c2ecf20Sopenharmony_ci		u8 centreWeight;
2708c2ecf20Sopenharmony_ci		u8 gain;
2718c2ecf20Sopenharmony_ci		u8 fineExp;
2728c2ecf20Sopenharmony_ci		u8 coarseExpLo;
2738c2ecf20Sopenharmony_ci		u8 coarseExpHi;
2748c2ecf20Sopenharmony_ci		u8 redComp;
2758c2ecf20Sopenharmony_ci		u8 green1Comp;
2768c2ecf20Sopenharmony_ci		u8 green2Comp;
2778c2ecf20Sopenharmony_ci		u8 blueComp;
2788c2ecf20Sopenharmony_ci	} exposure;
2798c2ecf20Sopenharmony_ci	struct {
2808c2ecf20Sopenharmony_ci		u8 balanceMode;
2818c2ecf20Sopenharmony_ci		u8 redGain;
2828c2ecf20Sopenharmony_ci		u8 greenGain;
2838c2ecf20Sopenharmony_ci		u8 blueGain;
2848c2ecf20Sopenharmony_ci	} colourBalance;
2858c2ecf20Sopenharmony_ci	struct {
2868c2ecf20Sopenharmony_ci		u8 divisor;
2878c2ecf20Sopenharmony_ci		u8 baserate;
2888c2ecf20Sopenharmony_ci	} sensorFps;
2898c2ecf20Sopenharmony_ci	struct {
2908c2ecf20Sopenharmony_ci		u8 gain1;
2918c2ecf20Sopenharmony_ci		u8 gain2;
2928c2ecf20Sopenharmony_ci		u8 gain4;
2938c2ecf20Sopenharmony_ci		u8 gain8;
2948c2ecf20Sopenharmony_ci	} apcor;
2958c2ecf20Sopenharmony_ci	struct {
2968c2ecf20Sopenharmony_ci		u8 disabled;
2978c2ecf20Sopenharmony_ci		u8 flickerMode;
2988c2ecf20Sopenharmony_ci		u8 coarseJump;
2998c2ecf20Sopenharmony_ci		u8 allowableOverExposure;
3008c2ecf20Sopenharmony_ci	} flickerControl;
3018c2ecf20Sopenharmony_ci	struct {
3028c2ecf20Sopenharmony_ci		u8 gain1;
3038c2ecf20Sopenharmony_ci		u8 gain2;
3048c2ecf20Sopenharmony_ci		u8 gain4;
3058c2ecf20Sopenharmony_ci		u8 gain8;
3068c2ecf20Sopenharmony_ci	} vlOffset;
3078c2ecf20Sopenharmony_ci	struct {
3088c2ecf20Sopenharmony_ci		u8 mode;
3098c2ecf20Sopenharmony_ci		u8 decimation;
3108c2ecf20Sopenharmony_ci	} compression;
3118c2ecf20Sopenharmony_ci	struct {
3128c2ecf20Sopenharmony_ci		u8 frTargeting;
3138c2ecf20Sopenharmony_ci		u8 targetFR;
3148c2ecf20Sopenharmony_ci		u8 targetQ;
3158c2ecf20Sopenharmony_ci	} compressionTarget;
3168c2ecf20Sopenharmony_ci	struct {
3178c2ecf20Sopenharmony_ci		u8 yThreshold;
3188c2ecf20Sopenharmony_ci		u8 uvThreshold;
3198c2ecf20Sopenharmony_ci	} yuvThreshold;
3208c2ecf20Sopenharmony_ci	struct {
3218c2ecf20Sopenharmony_ci		u8 hysteresis;
3228c2ecf20Sopenharmony_ci		u8 threshMax;
3238c2ecf20Sopenharmony_ci		u8 smallStep;
3248c2ecf20Sopenharmony_ci		u8 largeStep;
3258c2ecf20Sopenharmony_ci		u8 decimationHysteresis;
3268c2ecf20Sopenharmony_ci		u8 frDiffStepThresh;
3278c2ecf20Sopenharmony_ci		u8 qDiffStepThresh;
3288c2ecf20Sopenharmony_ci		u8 decimationThreshMod;
3298c2ecf20Sopenharmony_ci	} compressionParams;
3308c2ecf20Sopenharmony_ci	struct {
3318c2ecf20Sopenharmony_ci		u8 videoSize;		/* CIF/QCIF */
3328c2ecf20Sopenharmony_ci		u8 subSample;
3338c2ecf20Sopenharmony_ci		u8 yuvOrder;
3348c2ecf20Sopenharmony_ci	} format;
3358c2ecf20Sopenharmony_ci	struct {                        /* Intel QX3 specific data */
3368c2ecf20Sopenharmony_ci		u8 qx3_detected;        /* a QX3 is present */
3378c2ecf20Sopenharmony_ci		u8 toplight;            /* top light lit , R/W */
3388c2ecf20Sopenharmony_ci		u8 bottomlight;         /* bottom light lit, R/W */
3398c2ecf20Sopenharmony_ci		u8 button;              /* snapshot button pressed (R/O) */
3408c2ecf20Sopenharmony_ci		u8 cradled;             /* microscope is in cradle (R/O) */
3418c2ecf20Sopenharmony_ci	} qx3;
3428c2ecf20Sopenharmony_ci	struct {
3438c2ecf20Sopenharmony_ci		u8 colStart;		/* skip first 8*colStart pixels */
3448c2ecf20Sopenharmony_ci		u8 colEnd;		/* finish at 8*colEnd pixels */
3458c2ecf20Sopenharmony_ci		u8 rowStart;		/* skip first 4*rowStart lines */
3468c2ecf20Sopenharmony_ci		u8 rowEnd;		/* finish at 4*rowEnd lines */
3478c2ecf20Sopenharmony_ci	} roi;
3488c2ecf20Sopenharmony_ci	u8 ecpTiming;
3498c2ecf20Sopenharmony_ci	u8 streamStartLine;
3508c2ecf20Sopenharmony_ci};
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci/* specific webcam descriptor */
3538c2ecf20Sopenharmony_cistruct sd {
3548c2ecf20Sopenharmony_ci	struct gspca_dev gspca_dev;		/* !! must be the first item */
3558c2ecf20Sopenharmony_ci	struct cam_params params;		/* camera settings */
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	atomic_t cam_exposure;
3588c2ecf20Sopenharmony_ci	atomic_t fps;
3598c2ecf20Sopenharmony_ci	int exposure_count;
3608c2ecf20Sopenharmony_ci	u8 exposure_status;
3618c2ecf20Sopenharmony_ci	struct v4l2_ctrl *freq;
3628c2ecf20Sopenharmony_ci	u8 mainsFreq;				/* 0 = 50hz, 1 = 60hz */
3638c2ecf20Sopenharmony_ci	u8 first_frame;
3648c2ecf20Sopenharmony_ci};
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format mode[] = {
3678c2ecf20Sopenharmony_ci	{160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
3688c2ecf20Sopenharmony_ci		/* The sizeimage is trial and error, as with low framerates
3698c2ecf20Sopenharmony_ci		   the camera will pad out usb frames, making the image
3708c2ecf20Sopenharmony_ci		   data larger then strictly necessary */
3718c2ecf20Sopenharmony_ci		.bytesperline = 160,
3728c2ecf20Sopenharmony_ci		.sizeimage = 65536,
3738c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
3748c2ecf20Sopenharmony_ci		.priv = 3},
3758c2ecf20Sopenharmony_ci	{176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
3768c2ecf20Sopenharmony_ci		.bytesperline = 172,
3778c2ecf20Sopenharmony_ci		.sizeimage = 65536,
3788c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
3798c2ecf20Sopenharmony_ci		.priv = 2},
3808c2ecf20Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
3818c2ecf20Sopenharmony_ci		.bytesperline = 320,
3828c2ecf20Sopenharmony_ci		.sizeimage = 262144,
3838c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
3848c2ecf20Sopenharmony_ci		.priv = 1},
3858c2ecf20Sopenharmony_ci	{352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
3868c2ecf20Sopenharmony_ci		.bytesperline = 352,
3878c2ecf20Sopenharmony_ci		.sizeimage = 262144,
3888c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
3898c2ecf20Sopenharmony_ci		.priv = 0},
3908c2ecf20Sopenharmony_ci};
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci/**********************************************************************
3938c2ecf20Sopenharmony_ci *
3948c2ecf20Sopenharmony_ci * General functions
3958c2ecf20Sopenharmony_ci *
3968c2ecf20Sopenharmony_ci **********************************************************************/
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	u8 requesttype;
4018c2ecf20Sopenharmony_ci	unsigned int pipe;
4028c2ecf20Sopenharmony_ci	int ret, databytes = command[6] | (command[7] << 8);
4038c2ecf20Sopenharmony_ci	/* Sometimes we see spurious EPIPE errors */
4048c2ecf20Sopenharmony_ci	int retries = 3;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	if (command[0] == DATA_IN) {
4078c2ecf20Sopenharmony_ci		pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
4088c2ecf20Sopenharmony_ci		requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
4098c2ecf20Sopenharmony_ci	} else if (command[0] == DATA_OUT) {
4108c2ecf20Sopenharmony_ci		pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
4118c2ecf20Sopenharmony_ci		requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
4128c2ecf20Sopenharmony_ci	} else {
4138c2ecf20Sopenharmony_ci		gspca_err(gspca_dev, "Unexpected first byte of command: %x\n",
4148c2ecf20Sopenharmony_ci			  command[0]);
4158c2ecf20Sopenharmony_ci		return -EINVAL;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ciretry:
4198c2ecf20Sopenharmony_ci	ret = usb_control_msg(gspca_dev->dev, pipe,
4208c2ecf20Sopenharmony_ci			      command[1],
4218c2ecf20Sopenharmony_ci			      requesttype,
4228c2ecf20Sopenharmony_ci			      command[2] | (command[3] << 8),
4238c2ecf20Sopenharmony_ci			      command[4] | (command[5] << 8),
4248c2ecf20Sopenharmony_ci			      gspca_dev->usb_buf, databytes, 1000);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (ret < 0)
4278c2ecf20Sopenharmony_ci		pr_err("usb_control_msg %02x, error %d\n", command[1], ret);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (ret == -EPIPE && retries > 0) {
4308c2ecf20Sopenharmony_ci		retries--;
4318c2ecf20Sopenharmony_ci		goto retry;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	return (ret < 0) ? ret : 0;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci/* send an arbitrary command to the camera */
4388c2ecf20Sopenharmony_cistatic int do_command(struct gspca_dev *gspca_dev, u16 command,
4398c2ecf20Sopenharmony_ci		      u8 a, u8 b, u8 c, u8 d)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
4428c2ecf20Sopenharmony_ci	int ret, datasize;
4438c2ecf20Sopenharmony_ci	u8 cmd[8];
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	switch (command) {
4468c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetCPIAVersion:
4478c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetPnPID:
4488c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetCameraStatus:
4498c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetVPVersion:
4508c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetColourParams:
4518c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetColourBalance:
4528c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetExposure:
4538c2ecf20Sopenharmony_ci		datasize = 8;
4548c2ecf20Sopenharmony_ci		break;
4558c2ecf20Sopenharmony_ci	case CPIA_COMMAND_ReadMCPorts:
4568c2ecf20Sopenharmony_ci	case CPIA_COMMAND_ReadVCRegs:
4578c2ecf20Sopenharmony_ci		datasize = 4;
4588c2ecf20Sopenharmony_ci		break;
4598c2ecf20Sopenharmony_ci	default:
4608c2ecf20Sopenharmony_ci		datasize = 0;
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	cmd[0] = command >> 8;
4658c2ecf20Sopenharmony_ci	cmd[1] = command & 0xff;
4668c2ecf20Sopenharmony_ci	cmd[2] = a;
4678c2ecf20Sopenharmony_ci	cmd[3] = b;
4688c2ecf20Sopenharmony_ci	cmd[4] = c;
4698c2ecf20Sopenharmony_ci	cmd[5] = d;
4708c2ecf20Sopenharmony_ci	cmd[6] = datasize;
4718c2ecf20Sopenharmony_ci	cmd[7] = 0;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	ret = cpia_usb_transferCmd(gspca_dev, cmd);
4748c2ecf20Sopenharmony_ci	if (ret)
4758c2ecf20Sopenharmony_ci		return ret;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	switch (command) {
4788c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetCPIAVersion:
4798c2ecf20Sopenharmony_ci		sd->params.version.firmwareVersion = gspca_dev->usb_buf[0];
4808c2ecf20Sopenharmony_ci		sd->params.version.firmwareRevision = gspca_dev->usb_buf[1];
4818c2ecf20Sopenharmony_ci		sd->params.version.vcVersion = gspca_dev->usb_buf[2];
4828c2ecf20Sopenharmony_ci		sd->params.version.vcRevision = gspca_dev->usb_buf[3];
4838c2ecf20Sopenharmony_ci		break;
4848c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetPnPID:
4858c2ecf20Sopenharmony_ci		sd->params.pnpID.vendor =
4868c2ecf20Sopenharmony_ci			gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
4878c2ecf20Sopenharmony_ci		sd->params.pnpID.product =
4888c2ecf20Sopenharmony_ci			gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
4898c2ecf20Sopenharmony_ci		sd->params.pnpID.deviceRevision =
4908c2ecf20Sopenharmony_ci			gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8);
4918c2ecf20Sopenharmony_ci		break;
4928c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetCameraStatus:
4938c2ecf20Sopenharmony_ci		sd->params.status.systemState = gspca_dev->usb_buf[0];
4948c2ecf20Sopenharmony_ci		sd->params.status.grabState = gspca_dev->usb_buf[1];
4958c2ecf20Sopenharmony_ci		sd->params.status.streamState = gspca_dev->usb_buf[2];
4968c2ecf20Sopenharmony_ci		sd->params.status.fatalError = gspca_dev->usb_buf[3];
4978c2ecf20Sopenharmony_ci		sd->params.status.cmdError = gspca_dev->usb_buf[4];
4988c2ecf20Sopenharmony_ci		sd->params.status.debugFlags = gspca_dev->usb_buf[5];
4998c2ecf20Sopenharmony_ci		sd->params.status.vpStatus = gspca_dev->usb_buf[6];
5008c2ecf20Sopenharmony_ci		sd->params.status.errorCode = gspca_dev->usb_buf[7];
5018c2ecf20Sopenharmony_ci		break;
5028c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetVPVersion:
5038c2ecf20Sopenharmony_ci		sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0];
5048c2ecf20Sopenharmony_ci		sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1];
5058c2ecf20Sopenharmony_ci		sd->params.vpVersion.cameraHeadID =
5068c2ecf20Sopenharmony_ci			gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
5078c2ecf20Sopenharmony_ci		break;
5088c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetColourParams:
5098c2ecf20Sopenharmony_ci		sd->params.colourParams.brightness = gspca_dev->usb_buf[0];
5108c2ecf20Sopenharmony_ci		sd->params.colourParams.contrast = gspca_dev->usb_buf[1];
5118c2ecf20Sopenharmony_ci		sd->params.colourParams.saturation = gspca_dev->usb_buf[2];
5128c2ecf20Sopenharmony_ci		break;
5138c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetColourBalance:
5148c2ecf20Sopenharmony_ci		sd->params.colourBalance.redGain = gspca_dev->usb_buf[0];
5158c2ecf20Sopenharmony_ci		sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1];
5168c2ecf20Sopenharmony_ci		sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2];
5178c2ecf20Sopenharmony_ci		break;
5188c2ecf20Sopenharmony_ci	case CPIA_COMMAND_GetExposure:
5198c2ecf20Sopenharmony_ci		sd->params.exposure.gain = gspca_dev->usb_buf[0];
5208c2ecf20Sopenharmony_ci		sd->params.exposure.fineExp = gspca_dev->usb_buf[1];
5218c2ecf20Sopenharmony_ci		sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2];
5228c2ecf20Sopenharmony_ci		sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3];
5238c2ecf20Sopenharmony_ci		sd->params.exposure.redComp = gspca_dev->usb_buf[4];
5248c2ecf20Sopenharmony_ci		sd->params.exposure.green1Comp = gspca_dev->usb_buf[5];
5258c2ecf20Sopenharmony_ci		sd->params.exposure.green2Comp = gspca_dev->usb_buf[6];
5268c2ecf20Sopenharmony_ci		sd->params.exposure.blueComp = gspca_dev->usb_buf[7];
5278c2ecf20Sopenharmony_ci		break;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	case CPIA_COMMAND_ReadMCPorts:
5308c2ecf20Sopenharmony_ci		/* test button press */
5318c2ecf20Sopenharmony_ci		a = ((gspca_dev->usb_buf[1] & 0x02) == 0);
5328c2ecf20Sopenharmony_ci		if (a != sd->params.qx3.button) {
5338c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
5348c2ecf20Sopenharmony_ci			input_report_key(gspca_dev->input_dev, KEY_CAMERA, a);
5358c2ecf20Sopenharmony_ci			input_sync(gspca_dev->input_dev);
5368c2ecf20Sopenharmony_ci#endif
5378c2ecf20Sopenharmony_ci			sd->params.qx3.button = a;
5388c2ecf20Sopenharmony_ci		}
5398c2ecf20Sopenharmony_ci		if (sd->params.qx3.button) {
5408c2ecf20Sopenharmony_ci			/* button pressed - unlock the latch */
5418c2ecf20Sopenharmony_ci			ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
5428c2ecf20Sopenharmony_ci				   3, 0xdf, 0xdf, 0);
5438c2ecf20Sopenharmony_ci			if (ret)
5448c2ecf20Sopenharmony_ci				return ret;
5458c2ecf20Sopenharmony_ci			ret = do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
5468c2ecf20Sopenharmony_ci				   3, 0xff, 0xff, 0);
5478c2ecf20Sopenharmony_ci			if (ret)
5488c2ecf20Sopenharmony_ci				return ret;
5498c2ecf20Sopenharmony_ci		}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci		/* test whether microscope is cradled */
5528c2ecf20Sopenharmony_ci		sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0);
5538c2ecf20Sopenharmony_ci		break;
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	return 0;
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci/* send a command to the camera with an additional data transaction */
5608c2ecf20Sopenharmony_cistatic int do_command_extended(struct gspca_dev *gspca_dev, u16 command,
5618c2ecf20Sopenharmony_ci			       u8 a, u8 b, u8 c, u8 d,
5628c2ecf20Sopenharmony_ci			       u8 e, u8 f, u8 g, u8 h,
5638c2ecf20Sopenharmony_ci			       u8 i, u8 j, u8 k, u8 l)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	u8 cmd[8];
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	cmd[0] = command >> 8;
5688c2ecf20Sopenharmony_ci	cmd[1] = command & 0xff;
5698c2ecf20Sopenharmony_ci	cmd[2] = a;
5708c2ecf20Sopenharmony_ci	cmd[3] = b;
5718c2ecf20Sopenharmony_ci	cmd[4] = c;
5728c2ecf20Sopenharmony_ci	cmd[5] = d;
5738c2ecf20Sopenharmony_ci	cmd[6] = 8;
5748c2ecf20Sopenharmony_ci	cmd[7] = 0;
5758c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[0] = e;
5768c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[1] = f;
5778c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[2] = g;
5788c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[3] = h;
5798c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[4] = i;
5808c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[5] = j;
5818c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[6] = k;
5828c2ecf20Sopenharmony_ci	gspca_dev->usb_buf[7] = l;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	return cpia_usb_transferCmd(gspca_dev, cmd);
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci/*  find_over_exposure
5888c2ecf20Sopenharmony_ci *  Finds a suitable value of OverExposure for use with SetFlickerCtrl
5898c2ecf20Sopenharmony_ci *  Some calculation is required because this value changes with the brightness
5908c2ecf20Sopenharmony_ci *  set with SetColourParameters
5918c2ecf20Sopenharmony_ci *
5928c2ecf20Sopenharmony_ci *  Parameters: Brightness - last brightness value set with SetColourParameters
5938c2ecf20Sopenharmony_ci *
5948c2ecf20Sopenharmony_ci *  Returns: OverExposure value to use with SetFlickerCtrl
5958c2ecf20Sopenharmony_ci */
5968c2ecf20Sopenharmony_ci#define FLICKER_MAX_EXPOSURE                    250
5978c2ecf20Sopenharmony_ci#define FLICKER_ALLOWABLE_OVER_EXPOSURE         146
5988c2ecf20Sopenharmony_ci#define FLICKER_BRIGHTNESS_CONSTANT             59
5998c2ecf20Sopenharmony_cistatic int find_over_exposure(int brightness)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	int MaxAllowableOverExposure, OverExposure;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
6048c2ecf20Sopenharmony_ci				   FLICKER_BRIGHTNESS_CONSTANT;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE)
6078c2ecf20Sopenharmony_ci		OverExposure = MaxAllowableOverExposure;
6088c2ecf20Sopenharmony_ci	else
6098c2ecf20Sopenharmony_ci		OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	return OverExposure;
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci#undef FLICKER_MAX_EXPOSURE
6148c2ecf20Sopenharmony_ci#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
6158c2ecf20Sopenharmony_ci#undef FLICKER_BRIGHTNESS_CONSTANT
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci/* initialise cam_data structure  */
6188c2ecf20Sopenharmony_cistatic void reset_camera_params(struct gspca_dev *gspca_dev)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
6218c2ecf20Sopenharmony_ci	struct cam_params *params = &sd->params;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	/* The following parameter values are the defaults from
6248c2ecf20Sopenharmony_ci	 * "Software Developer's Guide for CPiA Cameras".  Any changes
6258c2ecf20Sopenharmony_ci	 * to the defaults are noted in comments. */
6268c2ecf20Sopenharmony_ci	params->colourParams.brightness = BRIGHTNESS_DEF;
6278c2ecf20Sopenharmony_ci	params->colourParams.contrast = CONTRAST_DEF;
6288c2ecf20Sopenharmony_ci	params->colourParams.saturation = SATURATION_DEF;
6298c2ecf20Sopenharmony_ci	params->exposure.gainMode = 4;
6308c2ecf20Sopenharmony_ci	params->exposure.expMode = 2;		/* AEC */
6318c2ecf20Sopenharmony_ci	params->exposure.compMode = 1;
6328c2ecf20Sopenharmony_ci	params->exposure.centreWeight = 1;
6338c2ecf20Sopenharmony_ci	params->exposure.gain = 0;
6348c2ecf20Sopenharmony_ci	params->exposure.fineExp = 0;
6358c2ecf20Sopenharmony_ci	params->exposure.coarseExpLo = 185;
6368c2ecf20Sopenharmony_ci	params->exposure.coarseExpHi = 0;
6378c2ecf20Sopenharmony_ci	params->exposure.redComp = COMP_RED;
6388c2ecf20Sopenharmony_ci	params->exposure.green1Comp = COMP_GREEN1;
6398c2ecf20Sopenharmony_ci	params->exposure.green2Comp = COMP_GREEN2;
6408c2ecf20Sopenharmony_ci	params->exposure.blueComp = COMP_BLUE;
6418c2ecf20Sopenharmony_ci	params->colourBalance.balanceMode = 2;	/* ACB */
6428c2ecf20Sopenharmony_ci	params->colourBalance.redGain = 32;
6438c2ecf20Sopenharmony_ci	params->colourBalance.greenGain = 6;
6448c2ecf20Sopenharmony_ci	params->colourBalance.blueGain = 92;
6458c2ecf20Sopenharmony_ci	params->apcor.gain1 = 0x18;
6468c2ecf20Sopenharmony_ci	params->apcor.gain2 = 0x16;
6478c2ecf20Sopenharmony_ci	params->apcor.gain4 = 0x24;
6488c2ecf20Sopenharmony_ci	params->apcor.gain8 = 0x34;
6498c2ecf20Sopenharmony_ci	params->vlOffset.gain1 = 20;
6508c2ecf20Sopenharmony_ci	params->vlOffset.gain2 = 24;
6518c2ecf20Sopenharmony_ci	params->vlOffset.gain4 = 26;
6528c2ecf20Sopenharmony_ci	params->vlOffset.gain8 = 26;
6538c2ecf20Sopenharmony_ci	params->compressionParams.hysteresis = 3;
6548c2ecf20Sopenharmony_ci	params->compressionParams.threshMax = 11;
6558c2ecf20Sopenharmony_ci	params->compressionParams.smallStep = 1;
6568c2ecf20Sopenharmony_ci	params->compressionParams.largeStep = 3;
6578c2ecf20Sopenharmony_ci	params->compressionParams.decimationHysteresis = 2;
6588c2ecf20Sopenharmony_ci	params->compressionParams.frDiffStepThresh = 5;
6598c2ecf20Sopenharmony_ci	params->compressionParams.qDiffStepThresh = 3;
6608c2ecf20Sopenharmony_ci	params->compressionParams.decimationThreshMod = 2;
6618c2ecf20Sopenharmony_ci	/* End of default values from Software Developer's Guide */
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	/* Set Sensor FPS to 15fps. This seems better than 30fps
6648c2ecf20Sopenharmony_ci	 * for indoor lighting. */
6658c2ecf20Sopenharmony_ci	params->sensorFps.divisor = 1;
6668c2ecf20Sopenharmony_ci	params->sensorFps.baserate = 1;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	params->flickerControl.flickerMode = 0;
6698c2ecf20Sopenharmony_ci	params->flickerControl.disabled = 1;
6708c2ecf20Sopenharmony_ci	params->flickerControl.coarseJump =
6718c2ecf20Sopenharmony_ci		flicker_jumps[sd->mainsFreq]
6728c2ecf20Sopenharmony_ci			     [params->sensorFps.baserate]
6738c2ecf20Sopenharmony_ci			     [params->sensorFps.divisor];
6748c2ecf20Sopenharmony_ci	params->flickerControl.allowableOverExposure =
6758c2ecf20Sopenharmony_ci		find_over_exposure(params->colourParams.brightness);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	params->yuvThreshold.yThreshold = 6; /* From windows driver */
6788c2ecf20Sopenharmony_ci	params->yuvThreshold.uvThreshold = 6; /* From windows driver */
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	params->format.subSample = SUBSAMPLE_420;
6818c2ecf20Sopenharmony_ci	params->format.yuvOrder = YUVORDER_YUYV;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	params->compression.mode = CPIA_COMPRESSION_AUTO;
6848c2ecf20Sopenharmony_ci	params->compression.decimation = NO_DECIMATION;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	params->compressionTarget.frTargeting = COMP_TARGET_DEF;
6878c2ecf20Sopenharmony_ci	params->compressionTarget.targetFR = 15; /* From windows driver */
6888c2ecf20Sopenharmony_ci	params->compressionTarget.targetQ = 5; /* From windows driver */
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	params->qx3.qx3_detected = 0;
6918c2ecf20Sopenharmony_ci	params->qx3.toplight = 0;
6928c2ecf20Sopenharmony_ci	params->qx3.bottomlight = 0;
6938c2ecf20Sopenharmony_ci	params->qx3.button = 0;
6948c2ecf20Sopenharmony_ci	params->qx3.cradled = 0;
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic void printstatus(struct gspca_dev *gspca_dev, struct cam_params *params)
6988c2ecf20Sopenharmony_ci{
6998c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x\n",
7008c2ecf20Sopenharmony_ci		  params->status.systemState, params->status.grabState,
7018c2ecf20Sopenharmony_ci		  params->status.streamState, params->status.fatalError,
7028c2ecf20Sopenharmony_ci		  params->status.cmdError, params->status.debugFlags,
7038c2ecf20Sopenharmony_ci		  params->status.vpStatus, params->status.errorCode);
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic int goto_low_power(struct gspca_dev *gspca_dev)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
7098c2ecf20Sopenharmony_ci	int ret;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0);
7128c2ecf20Sopenharmony_ci	if (ret)
7138c2ecf20Sopenharmony_ci		return ret;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
7168c2ecf20Sopenharmony_ci	if (ret)
7178c2ecf20Sopenharmony_ci		return ret;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	if (sd->params.status.systemState != LO_POWER_STATE) {
7208c2ecf20Sopenharmony_ci		if (sd->params.status.systemState != WARM_BOOT_STATE) {
7218c2ecf20Sopenharmony_ci			gspca_err(gspca_dev, "unexpected state after lo power cmd: %02x\n",
7228c2ecf20Sopenharmony_ci				  sd->params.status.systemState);
7238c2ecf20Sopenharmony_ci			printstatus(gspca_dev, &sd->params);
7248c2ecf20Sopenharmony_ci		}
7258c2ecf20Sopenharmony_ci		return -EIO;
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "camera now in LOW power state\n");
7298c2ecf20Sopenharmony_ci	return 0;
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_cistatic int goto_high_power(struct gspca_dev *gspca_dev)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
7358c2ecf20Sopenharmony_ci	int ret;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0);
7388c2ecf20Sopenharmony_ci	if (ret)
7398c2ecf20Sopenharmony_ci		return ret;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	msleep_interruptible(40);	/* windows driver does it too */
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	if (signal_pending(current))
7448c2ecf20Sopenharmony_ci		return -EINTR;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
7478c2ecf20Sopenharmony_ci	if (ret)
7488c2ecf20Sopenharmony_ci		return ret;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (sd->params.status.systemState != HI_POWER_STATE) {
7518c2ecf20Sopenharmony_ci		gspca_err(gspca_dev, "unexpected state after hi power cmd: %02x\n",
7528c2ecf20Sopenharmony_ci			  sd->params.status.systemState);
7538c2ecf20Sopenharmony_ci		printstatus(gspca_dev, &sd->params);
7548c2ecf20Sopenharmony_ci		return -EIO;
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_CONF, "camera now in HIGH power state\n");
7588c2ecf20Sopenharmony_ci	return 0;
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic int get_version_information(struct gspca_dev *gspca_dev)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	int ret;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	/* GetCPIAVersion */
7668c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
7678c2ecf20Sopenharmony_ci	if (ret)
7688c2ecf20Sopenharmony_ci		return ret;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	/* GetPnPID */
7718c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
7728c2ecf20Sopenharmony_ci}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic int save_camera_state(struct gspca_dev *gspca_dev)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	int ret;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
7798c2ecf20Sopenharmony_ci	if (ret)
7808c2ecf20Sopenharmony_ci		return ret;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic int command_setformat(struct gspca_dev *gspca_dev)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
7888c2ecf20Sopenharmony_ci	int ret;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat,
7918c2ecf20Sopenharmony_ci			 sd->params.format.videoSize,
7928c2ecf20Sopenharmony_ci			 sd->params.format.subSample,
7938c2ecf20Sopenharmony_ci			 sd->params.format.yuvOrder, 0);
7948c2ecf20Sopenharmony_ci	if (ret)
7958c2ecf20Sopenharmony_ci		return ret;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetROI,
7988c2ecf20Sopenharmony_ci			  sd->params.roi.colStart, sd->params.roi.colEnd,
7998c2ecf20Sopenharmony_ci			  sd->params.roi.rowStart, sd->params.roi.rowEnd);
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic int command_setcolourparams(struct gspca_dev *gspca_dev)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
8058c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetColourParams,
8068c2ecf20Sopenharmony_ci			  sd->params.colourParams.brightness,
8078c2ecf20Sopenharmony_ci			  sd->params.colourParams.contrast,
8088c2ecf20Sopenharmony_ci			  sd->params.colourParams.saturation, 0);
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_cistatic int command_setapcor(struct gspca_dev *gspca_dev)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
8148c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetApcor,
8158c2ecf20Sopenharmony_ci			  sd->params.apcor.gain1,
8168c2ecf20Sopenharmony_ci			  sd->params.apcor.gain2,
8178c2ecf20Sopenharmony_ci			  sd->params.apcor.gain4,
8188c2ecf20Sopenharmony_ci			  sd->params.apcor.gain8);
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_cistatic int command_setvloffset(struct gspca_dev *gspca_dev)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
8248c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset,
8258c2ecf20Sopenharmony_ci			  sd->params.vlOffset.gain1,
8268c2ecf20Sopenharmony_ci			  sd->params.vlOffset.gain2,
8278c2ecf20Sopenharmony_ci			  sd->params.vlOffset.gain4,
8288c2ecf20Sopenharmony_ci			  sd->params.vlOffset.gain8);
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_cistatic int command_setexposure(struct gspca_dev *gspca_dev)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
8348c2ecf20Sopenharmony_ci	int ret;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
8378c2ecf20Sopenharmony_ci				  sd->params.exposure.gainMode,
8388c2ecf20Sopenharmony_ci				  1,
8398c2ecf20Sopenharmony_ci				  sd->params.exposure.compMode,
8408c2ecf20Sopenharmony_ci				  sd->params.exposure.centreWeight,
8418c2ecf20Sopenharmony_ci				  sd->params.exposure.gain,
8428c2ecf20Sopenharmony_ci				  sd->params.exposure.fineExp,
8438c2ecf20Sopenharmony_ci				  sd->params.exposure.coarseExpLo,
8448c2ecf20Sopenharmony_ci				  sd->params.exposure.coarseExpHi,
8458c2ecf20Sopenharmony_ci				  sd->params.exposure.redComp,
8468c2ecf20Sopenharmony_ci				  sd->params.exposure.green1Comp,
8478c2ecf20Sopenharmony_ci				  sd->params.exposure.green2Comp,
8488c2ecf20Sopenharmony_ci				  sd->params.exposure.blueComp);
8498c2ecf20Sopenharmony_ci	if (ret)
8508c2ecf20Sopenharmony_ci		return ret;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	if (sd->params.exposure.expMode != 1) {
8538c2ecf20Sopenharmony_ci		ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
8548c2ecf20Sopenharmony_ci					  0,
8558c2ecf20Sopenharmony_ci					  sd->params.exposure.expMode,
8568c2ecf20Sopenharmony_ci					  0, 0,
8578c2ecf20Sopenharmony_ci					  sd->params.exposure.gain,
8588c2ecf20Sopenharmony_ci					  sd->params.exposure.fineExp,
8598c2ecf20Sopenharmony_ci					  sd->params.exposure.coarseExpLo,
8608c2ecf20Sopenharmony_ci					  sd->params.exposure.coarseExpHi,
8618c2ecf20Sopenharmony_ci					  0, 0, 0, 0);
8628c2ecf20Sopenharmony_ci	}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	return ret;
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_cistatic int command_setcolourbalance(struct gspca_dev *gspca_dev)
8688c2ecf20Sopenharmony_ci{
8698c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	if (sd->params.colourBalance.balanceMode == 1) {
8728c2ecf20Sopenharmony_ci		int ret;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci		ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
8758c2ecf20Sopenharmony_ci				 1,
8768c2ecf20Sopenharmony_ci				 sd->params.colourBalance.redGain,
8778c2ecf20Sopenharmony_ci				 sd->params.colourBalance.greenGain,
8788c2ecf20Sopenharmony_ci				 sd->params.colourBalance.blueGain);
8798c2ecf20Sopenharmony_ci		if (ret)
8808c2ecf20Sopenharmony_ci			return ret;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
8838c2ecf20Sopenharmony_ci				  3, 0, 0, 0);
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci	if (sd->params.colourBalance.balanceMode == 2) {
8868c2ecf20Sopenharmony_ci		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
8878c2ecf20Sopenharmony_ci				  2, 0, 0, 0);
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci	if (sd->params.colourBalance.balanceMode == 3) {
8908c2ecf20Sopenharmony_ci		return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
8918c2ecf20Sopenharmony_ci				  3, 0, 0, 0);
8928c2ecf20Sopenharmony_ci	}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	return -EINVAL;
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_cistatic int command_setcompressiontarget(struct gspca_dev *gspca_dev)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget,
9028c2ecf20Sopenharmony_ci			  sd->params.compressionTarget.frTargeting,
9038c2ecf20Sopenharmony_ci			  sd->params.compressionTarget.targetFR,
9048c2ecf20Sopenharmony_ci			  sd->params.compressionTarget.targetQ, 0);
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_cistatic int command_setyuvtresh(struct gspca_dev *gspca_dev)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh,
9128c2ecf20Sopenharmony_ci			  sd->params.yuvThreshold.yThreshold,
9138c2ecf20Sopenharmony_ci			  sd->params.yuvThreshold.uvThreshold, 0, 0);
9148c2ecf20Sopenharmony_ci}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_cistatic int command_setcompressionparams(struct gspca_dev *gspca_dev)
9178c2ecf20Sopenharmony_ci{
9188c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	return do_command_extended(gspca_dev,
9218c2ecf20Sopenharmony_ci			    CPIA_COMMAND_SetCompressionParams,
9228c2ecf20Sopenharmony_ci			    0, 0, 0, 0,
9238c2ecf20Sopenharmony_ci			    sd->params.compressionParams.hysteresis,
9248c2ecf20Sopenharmony_ci			    sd->params.compressionParams.threshMax,
9258c2ecf20Sopenharmony_ci			    sd->params.compressionParams.smallStep,
9268c2ecf20Sopenharmony_ci			    sd->params.compressionParams.largeStep,
9278c2ecf20Sopenharmony_ci			    sd->params.compressionParams.decimationHysteresis,
9288c2ecf20Sopenharmony_ci			    sd->params.compressionParams.frDiffStepThresh,
9298c2ecf20Sopenharmony_ci			    sd->params.compressionParams.qDiffStepThresh,
9308c2ecf20Sopenharmony_ci			    sd->params.compressionParams.decimationThreshMod);
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_cistatic int command_setcompression(struct gspca_dev *gspca_dev)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetCompression,
9388c2ecf20Sopenharmony_ci			  sd->params.compression.mode,
9398c2ecf20Sopenharmony_ci			  sd->params.compression.decimation, 0, 0);
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cistatic int command_setsensorfps(struct gspca_dev *gspca_dev)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS,
9478c2ecf20Sopenharmony_ci			  sd->params.sensorFps.divisor,
9488c2ecf20Sopenharmony_ci			  sd->params.sensorFps.baserate, 0, 0);
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic int command_setflickerctrl(struct gspca_dev *gspca_dev)
9528c2ecf20Sopenharmony_ci{
9538c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl,
9568c2ecf20Sopenharmony_ci			  sd->params.flickerControl.flickerMode,
9578c2ecf20Sopenharmony_ci			  sd->params.flickerControl.coarseJump,
9588c2ecf20Sopenharmony_ci			  sd->params.flickerControl.allowableOverExposure,
9598c2ecf20Sopenharmony_ci			  0);
9608c2ecf20Sopenharmony_ci}
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_cistatic int command_setecptiming(struct gspca_dev *gspca_dev)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming,
9678c2ecf20Sopenharmony_ci			  sd->params.ecpTiming, 0, 0, 0);
9688c2ecf20Sopenharmony_ci}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_cistatic int command_pause(struct gspca_dev *gspca_dev)
9718c2ecf20Sopenharmony_ci{
9728c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_cistatic int command_resume(struct gspca_dev *gspca_dev)
9768c2ecf20Sopenharmony_ci{
9778c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap,
9808c2ecf20Sopenharmony_ci			  0, sd->params.streamStartLine, 0, 0);
9818c2ecf20Sopenharmony_ci}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_cistatic int command_setlights(struct gspca_dev *gspca_dev)
9848c2ecf20Sopenharmony_ci{
9858c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
9868c2ecf20Sopenharmony_ci	int ret, p1, p2;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	p1 = (sd->params.qx3.bottomlight == 0) << 1;
9898c2ecf20Sopenharmony_ci	p2 = (sd->params.qx3.toplight == 0) << 3;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg,
9928c2ecf20Sopenharmony_ci			 0x90, 0x8f, 0x50, 0);
9938c2ecf20Sopenharmony_ci	if (ret)
9948c2ecf20Sopenharmony_ci		return ret;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0,
9978c2ecf20Sopenharmony_ci			  p1 | p2 | 0xe0, 0);
9988c2ecf20Sopenharmony_ci}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_cistatic int set_flicker(struct gspca_dev *gspca_dev, int on, int apply)
10018c2ecf20Sopenharmony_ci{
10028c2ecf20Sopenharmony_ci	/* Everything in here is from the Windows driver */
10038c2ecf20Sopenharmony_ci/* define for compgain calculation */
10048c2ecf20Sopenharmony_ci#if 0
10058c2ecf20Sopenharmony_ci#define COMPGAIN(base, curexp, newexp) \
10068c2ecf20Sopenharmony_ci    (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
10078c2ecf20Sopenharmony_ci#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
10088c2ecf20Sopenharmony_ci    (u16)((float)curexp * (float)(u8)(curcomp + 128) / \
10098c2ecf20Sopenharmony_ci    (float)(u8)(basecomp - 128))
10108c2ecf20Sopenharmony_ci#else
10118c2ecf20Sopenharmony_ci  /* equivalent functions without floating point math */
10128c2ecf20Sopenharmony_ci#define COMPGAIN(base, curexp, newexp) \
10138c2ecf20Sopenharmony_ci    (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp)))
10148c2ecf20Sopenharmony_ci#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
10158c2ecf20Sopenharmony_ci    (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
10168c2ecf20Sopenharmony_ci#endif
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
10198c2ecf20Sopenharmony_ci	int currentexp = sd->params.exposure.coarseExpLo +
10208c2ecf20Sopenharmony_ci			 sd->params.exposure.coarseExpHi * 256;
10218c2ecf20Sopenharmony_ci	int ret, startexp;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	if (on) {
10248c2ecf20Sopenharmony_ci		int cj = sd->params.flickerControl.coarseJump;
10258c2ecf20Sopenharmony_ci		sd->params.flickerControl.flickerMode = 1;
10268c2ecf20Sopenharmony_ci		sd->params.flickerControl.disabled = 0;
10278c2ecf20Sopenharmony_ci		if (sd->params.exposure.expMode != 2) {
10288c2ecf20Sopenharmony_ci			sd->params.exposure.expMode = 2;
10298c2ecf20Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
10308c2ecf20Sopenharmony_ci		}
10318c2ecf20Sopenharmony_ci		if (sd->params.exposure.gain >= BITS_PER_TYPE(currentexp))
10328c2ecf20Sopenharmony_ci			return -EINVAL;
10338c2ecf20Sopenharmony_ci		currentexp = currentexp << sd->params.exposure.gain;
10348c2ecf20Sopenharmony_ci		sd->params.exposure.gain = 0;
10358c2ecf20Sopenharmony_ci		/* round down current exposure to nearest value */
10368c2ecf20Sopenharmony_ci		startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
10378c2ecf20Sopenharmony_ci		if (startexp < 1)
10388c2ecf20Sopenharmony_ci			startexp = 1;
10398c2ecf20Sopenharmony_ci		startexp = (startexp * cj) - 1;
10408c2ecf20Sopenharmony_ci		if (FIRMWARE_VERSION(1, 2))
10418c2ecf20Sopenharmony_ci			while (startexp > MAX_EXP_102)
10428c2ecf20Sopenharmony_ci				startexp -= cj;
10438c2ecf20Sopenharmony_ci		else
10448c2ecf20Sopenharmony_ci			while (startexp > MAX_EXP)
10458c2ecf20Sopenharmony_ci				startexp -= cj;
10468c2ecf20Sopenharmony_ci		sd->params.exposure.coarseExpLo = startexp & 0xff;
10478c2ecf20Sopenharmony_ci		sd->params.exposure.coarseExpHi = startexp >> 8;
10488c2ecf20Sopenharmony_ci		if (currentexp > startexp) {
10498c2ecf20Sopenharmony_ci			if (currentexp > (2 * startexp))
10508c2ecf20Sopenharmony_ci				currentexp = 2 * startexp;
10518c2ecf20Sopenharmony_ci			sd->params.exposure.redComp =
10528c2ecf20Sopenharmony_ci				COMPGAIN(COMP_RED, currentexp, startexp);
10538c2ecf20Sopenharmony_ci			sd->params.exposure.green1Comp =
10548c2ecf20Sopenharmony_ci				COMPGAIN(COMP_GREEN1, currentexp, startexp);
10558c2ecf20Sopenharmony_ci			sd->params.exposure.green2Comp =
10568c2ecf20Sopenharmony_ci				COMPGAIN(COMP_GREEN2, currentexp, startexp);
10578c2ecf20Sopenharmony_ci			sd->params.exposure.blueComp =
10588c2ecf20Sopenharmony_ci				COMPGAIN(COMP_BLUE, currentexp, startexp);
10598c2ecf20Sopenharmony_ci		} else {
10608c2ecf20Sopenharmony_ci			sd->params.exposure.redComp = COMP_RED;
10618c2ecf20Sopenharmony_ci			sd->params.exposure.green1Comp = COMP_GREEN1;
10628c2ecf20Sopenharmony_ci			sd->params.exposure.green2Comp = COMP_GREEN2;
10638c2ecf20Sopenharmony_ci			sd->params.exposure.blueComp = COMP_BLUE;
10648c2ecf20Sopenharmony_ci		}
10658c2ecf20Sopenharmony_ci		if (FIRMWARE_VERSION(1, 2))
10668c2ecf20Sopenharmony_ci			sd->params.exposure.compMode = 0;
10678c2ecf20Sopenharmony_ci		else
10688c2ecf20Sopenharmony_ci			sd->params.exposure.compMode = 1;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci		sd->params.apcor.gain1 = 0x18;
10718c2ecf20Sopenharmony_ci		sd->params.apcor.gain2 = 0x18;
10728c2ecf20Sopenharmony_ci		sd->params.apcor.gain4 = 0x16;
10738c2ecf20Sopenharmony_ci		sd->params.apcor.gain8 = 0x14;
10748c2ecf20Sopenharmony_ci	} else {
10758c2ecf20Sopenharmony_ci		sd->params.flickerControl.flickerMode = 0;
10768c2ecf20Sopenharmony_ci		sd->params.flickerControl.disabled = 1;
10778c2ecf20Sopenharmony_ci		/* Average equivalent coarse for each comp channel */
10788c2ecf20Sopenharmony_ci		startexp = EXP_FROM_COMP(COMP_RED,
10798c2ecf20Sopenharmony_ci				sd->params.exposure.redComp, currentexp);
10808c2ecf20Sopenharmony_ci		startexp += EXP_FROM_COMP(COMP_GREEN1,
10818c2ecf20Sopenharmony_ci				sd->params.exposure.green1Comp, currentexp);
10828c2ecf20Sopenharmony_ci		startexp += EXP_FROM_COMP(COMP_GREEN2,
10838c2ecf20Sopenharmony_ci				sd->params.exposure.green2Comp, currentexp);
10848c2ecf20Sopenharmony_ci		startexp += EXP_FROM_COMP(COMP_BLUE,
10858c2ecf20Sopenharmony_ci				sd->params.exposure.blueComp, currentexp);
10868c2ecf20Sopenharmony_ci		startexp = startexp >> 2;
10878c2ecf20Sopenharmony_ci		while (startexp > MAX_EXP && sd->params.exposure.gain <
10888c2ecf20Sopenharmony_ci		       sd->params.exposure.gainMode - 1) {
10898c2ecf20Sopenharmony_ci			startexp = startexp >> 1;
10908c2ecf20Sopenharmony_ci			++sd->params.exposure.gain;
10918c2ecf20Sopenharmony_ci		}
10928c2ecf20Sopenharmony_ci		if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102)
10938c2ecf20Sopenharmony_ci			startexp = MAX_EXP_102;
10948c2ecf20Sopenharmony_ci		if (startexp > MAX_EXP)
10958c2ecf20Sopenharmony_ci			startexp = MAX_EXP;
10968c2ecf20Sopenharmony_ci		sd->params.exposure.coarseExpLo = startexp & 0xff;
10978c2ecf20Sopenharmony_ci		sd->params.exposure.coarseExpHi = startexp >> 8;
10988c2ecf20Sopenharmony_ci		sd->params.exposure.redComp = COMP_RED;
10998c2ecf20Sopenharmony_ci		sd->params.exposure.green1Comp = COMP_GREEN1;
11008c2ecf20Sopenharmony_ci		sd->params.exposure.green2Comp = COMP_GREEN2;
11018c2ecf20Sopenharmony_ci		sd->params.exposure.blueComp = COMP_BLUE;
11028c2ecf20Sopenharmony_ci		sd->params.exposure.compMode = 1;
11038c2ecf20Sopenharmony_ci		sd->params.apcor.gain1 = 0x18;
11048c2ecf20Sopenharmony_ci		sd->params.apcor.gain2 = 0x16;
11058c2ecf20Sopenharmony_ci		sd->params.apcor.gain4 = 0x24;
11068c2ecf20Sopenharmony_ci		sd->params.apcor.gain8 = 0x34;
11078c2ecf20Sopenharmony_ci	}
11088c2ecf20Sopenharmony_ci	sd->params.vlOffset.gain1 = 20;
11098c2ecf20Sopenharmony_ci	sd->params.vlOffset.gain2 = 24;
11108c2ecf20Sopenharmony_ci	sd->params.vlOffset.gain4 = 26;
11118c2ecf20Sopenharmony_ci	sd->params.vlOffset.gain8 = 26;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	if (apply) {
11148c2ecf20Sopenharmony_ci		ret = command_setexposure(gspca_dev);
11158c2ecf20Sopenharmony_ci		if (ret)
11168c2ecf20Sopenharmony_ci			return ret;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci		ret = command_setapcor(gspca_dev);
11198c2ecf20Sopenharmony_ci		if (ret)
11208c2ecf20Sopenharmony_ci			return ret;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci		ret = command_setvloffset(gspca_dev);
11238c2ecf20Sopenharmony_ci		if (ret)
11248c2ecf20Sopenharmony_ci			return ret;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci		ret = command_setflickerctrl(gspca_dev);
11278c2ecf20Sopenharmony_ci		if (ret)
11288c2ecf20Sopenharmony_ci			return ret;
11298c2ecf20Sopenharmony_ci	}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	return 0;
11328c2ecf20Sopenharmony_ci#undef EXP_FROM_COMP
11338c2ecf20Sopenharmony_ci#undef COMPGAIN
11348c2ecf20Sopenharmony_ci}
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci/* monitor the exposure and adjust the sensor frame rate if needed */
11378c2ecf20Sopenharmony_cistatic void monitor_exposure(struct gspca_dev *gspca_dev)
11388c2ecf20Sopenharmony_ci{
11398c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
11408c2ecf20Sopenharmony_ci	u8 exp_acc, bcomp, cmd[8];
11418c2ecf20Sopenharmony_ci	int ret, light_exp, dark_exp, very_dark_exp;
11428c2ecf20Sopenharmony_ci	int old_exposure, new_exposure, framerate;
11438c2ecf20Sopenharmony_ci	int setfps = 0, setexp = 0, setflicker = 0;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	/* get necessary stats and register settings from camera */
11468c2ecf20Sopenharmony_ci	/* do_command can't handle this, so do it ourselves */
11478c2ecf20Sopenharmony_ci	cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8;
11488c2ecf20Sopenharmony_ci	cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff;
11498c2ecf20Sopenharmony_ci	cmd[2] = 30;
11508c2ecf20Sopenharmony_ci	cmd[3] = 4;
11518c2ecf20Sopenharmony_ci	cmd[4] = 9;
11528c2ecf20Sopenharmony_ci	cmd[5] = 8;
11538c2ecf20Sopenharmony_ci	cmd[6] = 8;
11548c2ecf20Sopenharmony_ci	cmd[7] = 0;
11558c2ecf20Sopenharmony_ci	ret = cpia_usb_transferCmd(gspca_dev, cmd);
11568c2ecf20Sopenharmony_ci	if (ret) {
11578c2ecf20Sopenharmony_ci		pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret);
11588c2ecf20Sopenharmony_ci		return;
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci	exp_acc = gspca_dev->usb_buf[0];
11618c2ecf20Sopenharmony_ci	bcomp = gspca_dev->usb_buf[1];
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	light_exp = sd->params.colourParams.brightness +
11648c2ecf20Sopenharmony_ci		    TC - 50 + EXP_ACC_LIGHT;
11658c2ecf20Sopenharmony_ci	if (light_exp > 255)
11668c2ecf20Sopenharmony_ci		light_exp = 255;
11678c2ecf20Sopenharmony_ci	dark_exp = sd->params.colourParams.brightness +
11688c2ecf20Sopenharmony_ci		   TC - 50 - EXP_ACC_DARK;
11698c2ecf20Sopenharmony_ci	if (dark_exp < 0)
11708c2ecf20Sopenharmony_ci		dark_exp = 0;
11718c2ecf20Sopenharmony_ci	very_dark_exp = dark_exp / 2;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	old_exposure = sd->params.exposure.coarseExpHi * 256 +
11748c2ecf20Sopenharmony_ci		       sd->params.exposure.coarseExpLo;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	if (!sd->params.flickerControl.disabled) {
11778c2ecf20Sopenharmony_ci		/* Flicker control on */
11788c2ecf20Sopenharmony_ci		int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP :
11798c2ecf20Sopenharmony_ci							HIGH_COMP_102;
11808c2ecf20Sopenharmony_ci		bcomp += 128;	/* decode */
11818c2ecf20Sopenharmony_ci		if (bcomp >= max_comp && exp_acc < dark_exp) {
11828c2ecf20Sopenharmony_ci			/* dark */
11838c2ecf20Sopenharmony_ci			if (exp_acc < very_dark_exp) {
11848c2ecf20Sopenharmony_ci				/* very dark */
11858c2ecf20Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_VERY_DARK)
11868c2ecf20Sopenharmony_ci					++sd->exposure_count;
11878c2ecf20Sopenharmony_ci				else {
11888c2ecf20Sopenharmony_ci					sd->exposure_status =
11898c2ecf20Sopenharmony_ci						EXPOSURE_VERY_DARK;
11908c2ecf20Sopenharmony_ci					sd->exposure_count = 1;
11918c2ecf20Sopenharmony_ci				}
11928c2ecf20Sopenharmony_ci			} else {
11938c2ecf20Sopenharmony_ci				/* just dark */
11948c2ecf20Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_DARK)
11958c2ecf20Sopenharmony_ci					++sd->exposure_count;
11968c2ecf20Sopenharmony_ci				else {
11978c2ecf20Sopenharmony_ci					sd->exposure_status = EXPOSURE_DARK;
11988c2ecf20Sopenharmony_ci					sd->exposure_count = 1;
11998c2ecf20Sopenharmony_ci				}
12008c2ecf20Sopenharmony_ci			}
12018c2ecf20Sopenharmony_ci		} else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
12028c2ecf20Sopenharmony_ci			/* light */
12038c2ecf20Sopenharmony_ci			if (old_exposure <= VERY_LOW_EXP) {
12048c2ecf20Sopenharmony_ci				/* very light */
12058c2ecf20Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
12068c2ecf20Sopenharmony_ci					++sd->exposure_count;
12078c2ecf20Sopenharmony_ci				else {
12088c2ecf20Sopenharmony_ci					sd->exposure_status =
12098c2ecf20Sopenharmony_ci						EXPOSURE_VERY_LIGHT;
12108c2ecf20Sopenharmony_ci					sd->exposure_count = 1;
12118c2ecf20Sopenharmony_ci				}
12128c2ecf20Sopenharmony_ci			} else {
12138c2ecf20Sopenharmony_ci				/* just light */
12148c2ecf20Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_LIGHT)
12158c2ecf20Sopenharmony_ci					++sd->exposure_count;
12168c2ecf20Sopenharmony_ci				else {
12178c2ecf20Sopenharmony_ci					sd->exposure_status = EXPOSURE_LIGHT;
12188c2ecf20Sopenharmony_ci					sd->exposure_count = 1;
12198c2ecf20Sopenharmony_ci				}
12208c2ecf20Sopenharmony_ci			}
12218c2ecf20Sopenharmony_ci		} else {
12228c2ecf20Sopenharmony_ci			/* not dark or light */
12238c2ecf20Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
12248c2ecf20Sopenharmony_ci		}
12258c2ecf20Sopenharmony_ci	} else {
12268c2ecf20Sopenharmony_ci		/* Flicker control off */
12278c2ecf20Sopenharmony_ci		if (old_exposure >= MAX_EXP && exp_acc < dark_exp) {
12288c2ecf20Sopenharmony_ci			/* dark */
12298c2ecf20Sopenharmony_ci			if (exp_acc < very_dark_exp) {
12308c2ecf20Sopenharmony_ci				/* very dark */
12318c2ecf20Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_VERY_DARK)
12328c2ecf20Sopenharmony_ci					++sd->exposure_count;
12338c2ecf20Sopenharmony_ci				else {
12348c2ecf20Sopenharmony_ci					sd->exposure_status =
12358c2ecf20Sopenharmony_ci						EXPOSURE_VERY_DARK;
12368c2ecf20Sopenharmony_ci					sd->exposure_count = 1;
12378c2ecf20Sopenharmony_ci				}
12388c2ecf20Sopenharmony_ci			} else {
12398c2ecf20Sopenharmony_ci				/* just dark */
12408c2ecf20Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_DARK)
12418c2ecf20Sopenharmony_ci					++sd->exposure_count;
12428c2ecf20Sopenharmony_ci				else {
12438c2ecf20Sopenharmony_ci					sd->exposure_status = EXPOSURE_DARK;
12448c2ecf20Sopenharmony_ci					sd->exposure_count = 1;
12458c2ecf20Sopenharmony_ci				}
12468c2ecf20Sopenharmony_ci			}
12478c2ecf20Sopenharmony_ci		} else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
12488c2ecf20Sopenharmony_ci			/* light */
12498c2ecf20Sopenharmony_ci			if (old_exposure <= VERY_LOW_EXP) {
12508c2ecf20Sopenharmony_ci				/* very light */
12518c2ecf20Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
12528c2ecf20Sopenharmony_ci					++sd->exposure_count;
12538c2ecf20Sopenharmony_ci				else {
12548c2ecf20Sopenharmony_ci					sd->exposure_status =
12558c2ecf20Sopenharmony_ci						EXPOSURE_VERY_LIGHT;
12568c2ecf20Sopenharmony_ci					sd->exposure_count = 1;
12578c2ecf20Sopenharmony_ci				}
12588c2ecf20Sopenharmony_ci			} else {
12598c2ecf20Sopenharmony_ci				/* just light */
12608c2ecf20Sopenharmony_ci				if (sd->exposure_status == EXPOSURE_LIGHT)
12618c2ecf20Sopenharmony_ci					++sd->exposure_count;
12628c2ecf20Sopenharmony_ci				else {
12638c2ecf20Sopenharmony_ci					sd->exposure_status = EXPOSURE_LIGHT;
12648c2ecf20Sopenharmony_ci					sd->exposure_count = 1;
12658c2ecf20Sopenharmony_ci				}
12668c2ecf20Sopenharmony_ci			}
12678c2ecf20Sopenharmony_ci		} else {
12688c2ecf20Sopenharmony_ci			/* not dark or light */
12698c2ecf20Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
12708c2ecf20Sopenharmony_ci		}
12718c2ecf20Sopenharmony_ci	}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	framerate = atomic_read(&sd->fps);
12748c2ecf20Sopenharmony_ci	if (framerate > 30 || framerate < 1)
12758c2ecf20Sopenharmony_ci		framerate = 1;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	if (!sd->params.flickerControl.disabled) {
12788c2ecf20Sopenharmony_ci		/* Flicker control on */
12798c2ecf20Sopenharmony_ci		if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
12808c2ecf20Sopenharmony_ci		     sd->exposure_status == EXPOSURE_DARK) &&
12818c2ecf20Sopenharmony_ci		    sd->exposure_count >= DARK_TIME * framerate &&
12828c2ecf20Sopenharmony_ci		    sd->params.sensorFps.divisor < 2) {
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci			/* dark for too long */
12858c2ecf20Sopenharmony_ci			++sd->params.sensorFps.divisor;
12868c2ecf20Sopenharmony_ci			setfps = 1;
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci			sd->params.flickerControl.coarseJump =
12898c2ecf20Sopenharmony_ci				flicker_jumps[sd->mainsFreq]
12908c2ecf20Sopenharmony_ci					     [sd->params.sensorFps.baserate]
12918c2ecf20Sopenharmony_ci					     [sd->params.sensorFps.divisor];
12928c2ecf20Sopenharmony_ci			setflicker = 1;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci			new_exposure = sd->params.flickerControl.coarseJump-1;
12958c2ecf20Sopenharmony_ci			while (new_exposure < old_exposure / 2)
12968c2ecf20Sopenharmony_ci				new_exposure +=
12978c2ecf20Sopenharmony_ci					sd->params.flickerControl.coarseJump;
12988c2ecf20Sopenharmony_ci			sd->params.exposure.coarseExpLo = new_exposure & 0xff;
12998c2ecf20Sopenharmony_ci			sd->params.exposure.coarseExpHi = new_exposure >> 8;
13008c2ecf20Sopenharmony_ci			setexp = 1;
13018c2ecf20Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
13028c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_CONF, "Automatically decreasing sensor_fps\n");
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci		} else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
13058c2ecf20Sopenharmony_ci			    sd->exposure_status == EXPOSURE_LIGHT) &&
13068c2ecf20Sopenharmony_ci			   sd->exposure_count >= LIGHT_TIME * framerate &&
13078c2ecf20Sopenharmony_ci			   sd->params.sensorFps.divisor > 0) {
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci			/* light for too long */
13108c2ecf20Sopenharmony_ci			int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 :
13118c2ecf20Sopenharmony_ci							       MAX_EXP;
13128c2ecf20Sopenharmony_ci			--sd->params.sensorFps.divisor;
13138c2ecf20Sopenharmony_ci			setfps = 1;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci			sd->params.flickerControl.coarseJump =
13168c2ecf20Sopenharmony_ci				flicker_jumps[sd->mainsFreq]
13178c2ecf20Sopenharmony_ci					     [sd->params.sensorFps.baserate]
13188c2ecf20Sopenharmony_ci					     [sd->params.sensorFps.divisor];
13198c2ecf20Sopenharmony_ci			setflicker = 1;
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci			new_exposure = sd->params.flickerControl.coarseJump-1;
13228c2ecf20Sopenharmony_ci			while (new_exposure < 2 * old_exposure &&
13238c2ecf20Sopenharmony_ci			       new_exposure +
13248c2ecf20Sopenharmony_ci			       sd->params.flickerControl.coarseJump < max_exp)
13258c2ecf20Sopenharmony_ci				new_exposure +=
13268c2ecf20Sopenharmony_ci					sd->params.flickerControl.coarseJump;
13278c2ecf20Sopenharmony_ci			sd->params.exposure.coarseExpLo = new_exposure & 0xff;
13288c2ecf20Sopenharmony_ci			sd->params.exposure.coarseExpHi = new_exposure >> 8;
13298c2ecf20Sopenharmony_ci			setexp = 1;
13308c2ecf20Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
13318c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_CONF, "Automatically increasing sensor_fps\n");
13328c2ecf20Sopenharmony_ci		}
13338c2ecf20Sopenharmony_ci	} else {
13348c2ecf20Sopenharmony_ci		/* Flicker control off */
13358c2ecf20Sopenharmony_ci		if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
13368c2ecf20Sopenharmony_ci		     sd->exposure_status == EXPOSURE_DARK) &&
13378c2ecf20Sopenharmony_ci		    sd->exposure_count >= DARK_TIME * framerate &&
13388c2ecf20Sopenharmony_ci		    sd->params.sensorFps.divisor < 2) {
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci			/* dark for too long */
13418c2ecf20Sopenharmony_ci			++sd->params.sensorFps.divisor;
13428c2ecf20Sopenharmony_ci			setfps = 1;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci			if (sd->params.exposure.gain > 0) {
13458c2ecf20Sopenharmony_ci				--sd->params.exposure.gain;
13468c2ecf20Sopenharmony_ci				setexp = 1;
13478c2ecf20Sopenharmony_ci			}
13488c2ecf20Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
13498c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_CONF, "Automatically decreasing sensor_fps\n");
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci		} else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
13528c2ecf20Sopenharmony_ci			    sd->exposure_status == EXPOSURE_LIGHT) &&
13538c2ecf20Sopenharmony_ci			   sd->exposure_count >= LIGHT_TIME * framerate &&
13548c2ecf20Sopenharmony_ci			   sd->params.sensorFps.divisor > 0) {
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci			/* light for too long */
13578c2ecf20Sopenharmony_ci			--sd->params.sensorFps.divisor;
13588c2ecf20Sopenharmony_ci			setfps = 1;
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci			if (sd->params.exposure.gain <
13618c2ecf20Sopenharmony_ci			    sd->params.exposure.gainMode - 1) {
13628c2ecf20Sopenharmony_ci				++sd->params.exposure.gain;
13638c2ecf20Sopenharmony_ci				setexp = 1;
13648c2ecf20Sopenharmony_ci			}
13658c2ecf20Sopenharmony_ci			sd->exposure_status = EXPOSURE_NORMAL;
13668c2ecf20Sopenharmony_ci			gspca_dbg(gspca_dev, D_CONF, "Automatically increasing sensor_fps\n");
13678c2ecf20Sopenharmony_ci		}
13688c2ecf20Sopenharmony_ci	}
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	if (setexp)
13718c2ecf20Sopenharmony_ci		command_setexposure(gspca_dev);
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	if (setfps)
13748c2ecf20Sopenharmony_ci		command_setsensorfps(gspca_dev);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	if (setflicker)
13778c2ecf20Sopenharmony_ci		command_setflickerctrl(gspca_dev);
13788c2ecf20Sopenharmony_ci}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci/*-----------------------------------------------------------------*/
13818c2ecf20Sopenharmony_ci/* if flicker is switched off, this function switches it back on.It checks,
13828c2ecf20Sopenharmony_ci   however, that conditions are suitable before restarting it.
13838c2ecf20Sopenharmony_ci   This should only be called for firmware version 1.2.
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci   It also adjust the colour balance when an exposure step is detected - as
13868c2ecf20Sopenharmony_ci   long as flicker is running
13878c2ecf20Sopenharmony_ci*/
13888c2ecf20Sopenharmony_cistatic void restart_flicker(struct gspca_dev *gspca_dev)
13898c2ecf20Sopenharmony_ci{
13908c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
13918c2ecf20Sopenharmony_ci	int cam_exposure, old_exp;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	if (!FIRMWARE_VERSION(1, 2))
13948c2ecf20Sopenharmony_ci		return;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	cam_exposure = atomic_read(&sd->cam_exposure);
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	if (sd->params.flickerControl.flickerMode == 0 ||
13998c2ecf20Sopenharmony_ci	    cam_exposure == 0)
14008c2ecf20Sopenharmony_ci		return;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	old_exp = sd->params.exposure.coarseExpLo +
14038c2ecf20Sopenharmony_ci		  sd->params.exposure.coarseExpHi*256;
14048c2ecf20Sopenharmony_ci	/*
14058c2ecf20Sopenharmony_ci	  see how far away camera exposure is from a valid
14068c2ecf20Sopenharmony_ci	  flicker exposure value
14078c2ecf20Sopenharmony_ci	*/
14088c2ecf20Sopenharmony_ci	cam_exposure %= sd->params.flickerControl.coarseJump;
14098c2ecf20Sopenharmony_ci	if (!sd->params.flickerControl.disabled &&
14108c2ecf20Sopenharmony_ci	    cam_exposure <= sd->params.flickerControl.coarseJump - 3) {
14118c2ecf20Sopenharmony_ci		/* Flicker control auto-disabled */
14128c2ecf20Sopenharmony_ci		sd->params.flickerControl.disabled = 1;
14138c2ecf20Sopenharmony_ci	}
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	if (sd->params.flickerControl.disabled &&
14168c2ecf20Sopenharmony_ci	    old_exp > sd->params.flickerControl.coarseJump +
14178c2ecf20Sopenharmony_ci		      ROUND_UP_EXP_FOR_FLICKER) {
14188c2ecf20Sopenharmony_ci		/* exposure is now high enough to switch
14198c2ecf20Sopenharmony_ci		   flicker control back on */
14208c2ecf20Sopenharmony_ci		set_flicker(gspca_dev, 1, 1);
14218c2ecf20Sopenharmony_ci	}
14228c2ecf20Sopenharmony_ci}
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci/* this function is called at probe time */
14258c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev,
14268c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
14278c2ecf20Sopenharmony_ci{
14288c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
14298c2ecf20Sopenharmony_ci	struct cam *cam;
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
14328c2ecf20Sopenharmony_ci	reset_camera_params(gspca_dev);
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)\n",
14358c2ecf20Sopenharmony_ci		  id->idVendor, id->idProduct);
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	cam = &gspca_dev->cam;
14388c2ecf20Sopenharmony_ci	cam->cam_mode = mode;
14398c2ecf20Sopenharmony_ci	cam->nmodes = ARRAY_SIZE(mode);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	goto_low_power(gspca_dev);
14428c2ecf20Sopenharmony_ci	/* Check the firmware version. */
14438c2ecf20Sopenharmony_ci	sd->params.version.firmwareVersion = 0;
14448c2ecf20Sopenharmony_ci	get_version_information(gspca_dev);
14458c2ecf20Sopenharmony_ci	if (sd->params.version.firmwareVersion != 1) {
14468c2ecf20Sopenharmony_ci		gspca_err(gspca_dev, "only firmware version 1 is supported (got: %d)\n",
14478c2ecf20Sopenharmony_ci			  sd->params.version.firmwareVersion);
14488c2ecf20Sopenharmony_ci		return -ENODEV;
14498c2ecf20Sopenharmony_ci	}
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	/* A bug in firmware 1-02 limits gainMode to 2 */
14528c2ecf20Sopenharmony_ci	if (sd->params.version.firmwareRevision <= 2 &&
14538c2ecf20Sopenharmony_ci	    sd->params.exposure.gainMode > 2) {
14548c2ecf20Sopenharmony_ci		sd->params.exposure.gainMode = 2;
14558c2ecf20Sopenharmony_ci	}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	/* set QX3 detected flag */
14588c2ecf20Sopenharmony_ci	sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
14598c2ecf20Sopenharmony_ci				       sd->params.pnpID.product == 0x0001);
14608c2ecf20Sopenharmony_ci	return 0;
14618c2ecf20Sopenharmony_ci}
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci/* -- start the camera -- */
14648c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev)
14658c2ecf20Sopenharmony_ci{
14668c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
14678c2ecf20Sopenharmony_ci	int priv, ret;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	/* Start the camera in low power mode */
14708c2ecf20Sopenharmony_ci	if (goto_low_power(gspca_dev)) {
14718c2ecf20Sopenharmony_ci		if (sd->params.status.systemState != WARM_BOOT_STATE) {
14728c2ecf20Sopenharmony_ci			gspca_err(gspca_dev, "unexpected systemstate: %02x\n",
14738c2ecf20Sopenharmony_ci				  sd->params.status.systemState);
14748c2ecf20Sopenharmony_ci			printstatus(gspca_dev, &sd->params);
14758c2ecf20Sopenharmony_ci			return -ENODEV;
14768c2ecf20Sopenharmony_ci		}
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci		/* FIXME: this is just dirty trial and error */
14798c2ecf20Sopenharmony_ci		ret = goto_high_power(gspca_dev);
14808c2ecf20Sopenharmony_ci		if (ret)
14818c2ecf20Sopenharmony_ci			return ret;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci		ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame,
14848c2ecf20Sopenharmony_ci				 0, 0, 0, 0);
14858c2ecf20Sopenharmony_ci		if (ret)
14868c2ecf20Sopenharmony_ci			return ret;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci		ret = goto_low_power(gspca_dev);
14898c2ecf20Sopenharmony_ci		if (ret)
14908c2ecf20Sopenharmony_ci			return ret;
14918c2ecf20Sopenharmony_ci	}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	/* procedure described in developer's guide p3-28 */
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	/* Check the firmware version. */
14968c2ecf20Sopenharmony_ci	sd->params.version.firmwareVersion = 0;
14978c2ecf20Sopenharmony_ci	get_version_information(gspca_dev);
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	/* The fatal error checking should be done after
15008c2ecf20Sopenharmony_ci	 * the camera powers up (developer's guide p 3-38) */
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	/* Set streamState before transition to high power to avoid bug
15038c2ecf20Sopenharmony_ci	 * in firmware 1-02 */
15048c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus,
15058c2ecf20Sopenharmony_ci			 STREAMSTATE, 0, STREAM_NOT_READY, 0);
15068c2ecf20Sopenharmony_ci	if (ret)
15078c2ecf20Sopenharmony_ci		return ret;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	/* GotoHiPower */
15108c2ecf20Sopenharmony_ci	ret = goto_high_power(gspca_dev);
15118c2ecf20Sopenharmony_ci	if (ret)
15128c2ecf20Sopenharmony_ci		return ret;
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	/* Check the camera status */
15158c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
15168c2ecf20Sopenharmony_ci	if (ret)
15178c2ecf20Sopenharmony_ci		return ret;
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	if (sd->params.status.fatalError) {
15208c2ecf20Sopenharmony_ci		gspca_err(gspca_dev, "fatal_error: %04x, vp_status: %04x\n",
15218c2ecf20Sopenharmony_ci			  sd->params.status.fatalError,
15228c2ecf20Sopenharmony_ci			  sd->params.status.vpStatus);
15238c2ecf20Sopenharmony_ci		return -EIO;
15248c2ecf20Sopenharmony_ci	}
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	/* VPVersion can't be retrieved before the camera is in HiPower,
15278c2ecf20Sopenharmony_ci	 * so get it here instead of in get_version_information. */
15288c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
15298c2ecf20Sopenharmony_ci	if (ret)
15308c2ecf20Sopenharmony_ci		return ret;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	/* Determine video mode settings */
15338c2ecf20Sopenharmony_ci	sd->params.streamStartLine = 120;
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
15368c2ecf20Sopenharmony_ci	if (priv & 0x01) { /* crop */
15378c2ecf20Sopenharmony_ci		sd->params.roi.colStart = 2;
15388c2ecf20Sopenharmony_ci		sd->params.roi.rowStart = 6;
15398c2ecf20Sopenharmony_ci	} else {
15408c2ecf20Sopenharmony_ci		sd->params.roi.colStart = 0;
15418c2ecf20Sopenharmony_ci		sd->params.roi.rowStart = 0;
15428c2ecf20Sopenharmony_ci	}
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	if (priv & 0x02) { /* quarter */
15458c2ecf20Sopenharmony_ci		sd->params.format.videoSize = VIDEOSIZE_QCIF;
15468c2ecf20Sopenharmony_ci		sd->params.roi.colStart /= 2;
15478c2ecf20Sopenharmony_ci		sd->params.roi.rowStart /= 2;
15488c2ecf20Sopenharmony_ci		sd->params.streamStartLine /= 2;
15498c2ecf20Sopenharmony_ci	} else
15508c2ecf20Sopenharmony_ci		sd->params.format.videoSize = VIDEOSIZE_CIF;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	sd->params.roi.colEnd = sd->params.roi.colStart +
15538c2ecf20Sopenharmony_ci				(gspca_dev->pixfmt.width >> 3);
15548c2ecf20Sopenharmony_ci	sd->params.roi.rowEnd = sd->params.roi.rowStart +
15558c2ecf20Sopenharmony_ci				(gspca_dev->pixfmt.height >> 2);
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	/* And now set the camera to a known state */
15588c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode,
15598c2ecf20Sopenharmony_ci			 CPIA_GRAB_CONTINEOUS, 0, 0, 0);
15608c2ecf20Sopenharmony_ci	if (ret)
15618c2ecf20Sopenharmony_ci		return ret;
15628c2ecf20Sopenharmony_ci	/* We start with compression disabled, as we need one uncompressed
15638c2ecf20Sopenharmony_ci	   frame to handle later compressed frames */
15648c2ecf20Sopenharmony_ci	ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression,
15658c2ecf20Sopenharmony_ci			 CPIA_COMPRESSION_NONE,
15668c2ecf20Sopenharmony_ci			 NO_DECIMATION, 0, 0);
15678c2ecf20Sopenharmony_ci	if (ret)
15688c2ecf20Sopenharmony_ci		return ret;
15698c2ecf20Sopenharmony_ci	ret = command_setcompressiontarget(gspca_dev);
15708c2ecf20Sopenharmony_ci	if (ret)
15718c2ecf20Sopenharmony_ci		return ret;
15728c2ecf20Sopenharmony_ci	ret = command_setcolourparams(gspca_dev);
15738c2ecf20Sopenharmony_ci	if (ret)
15748c2ecf20Sopenharmony_ci		return ret;
15758c2ecf20Sopenharmony_ci	ret = command_setformat(gspca_dev);
15768c2ecf20Sopenharmony_ci	if (ret)
15778c2ecf20Sopenharmony_ci		return ret;
15788c2ecf20Sopenharmony_ci	ret = command_setyuvtresh(gspca_dev);
15798c2ecf20Sopenharmony_ci	if (ret)
15808c2ecf20Sopenharmony_ci		return ret;
15818c2ecf20Sopenharmony_ci	ret = command_setecptiming(gspca_dev);
15828c2ecf20Sopenharmony_ci	if (ret)
15838c2ecf20Sopenharmony_ci		return ret;
15848c2ecf20Sopenharmony_ci	ret = command_setcompressionparams(gspca_dev);
15858c2ecf20Sopenharmony_ci	if (ret)
15868c2ecf20Sopenharmony_ci		return ret;
15878c2ecf20Sopenharmony_ci	ret = command_setexposure(gspca_dev);
15888c2ecf20Sopenharmony_ci	if (ret)
15898c2ecf20Sopenharmony_ci		return ret;
15908c2ecf20Sopenharmony_ci	ret = command_setcolourbalance(gspca_dev);
15918c2ecf20Sopenharmony_ci	if (ret)
15928c2ecf20Sopenharmony_ci		return ret;
15938c2ecf20Sopenharmony_ci	ret = command_setsensorfps(gspca_dev);
15948c2ecf20Sopenharmony_ci	if (ret)
15958c2ecf20Sopenharmony_ci		return ret;
15968c2ecf20Sopenharmony_ci	ret = command_setapcor(gspca_dev);
15978c2ecf20Sopenharmony_ci	if (ret)
15988c2ecf20Sopenharmony_ci		return ret;
15998c2ecf20Sopenharmony_ci	ret = command_setflickerctrl(gspca_dev);
16008c2ecf20Sopenharmony_ci	if (ret)
16018c2ecf20Sopenharmony_ci		return ret;
16028c2ecf20Sopenharmony_ci	ret = command_setvloffset(gspca_dev);
16038c2ecf20Sopenharmony_ci	if (ret)
16048c2ecf20Sopenharmony_ci		return ret;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	/* Start stream */
16078c2ecf20Sopenharmony_ci	ret = command_resume(gspca_dev);
16088c2ecf20Sopenharmony_ci	if (ret)
16098c2ecf20Sopenharmony_ci		return ret;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	/* Wait 6 frames before turning compression on for the sensor to get
16128c2ecf20Sopenharmony_ci	   all settings and AEC/ACB to settle */
16138c2ecf20Sopenharmony_ci	sd->first_frame = 6;
16148c2ecf20Sopenharmony_ci	sd->exposure_status = EXPOSURE_NORMAL;
16158c2ecf20Sopenharmony_ci	sd->exposure_count = 0;
16168c2ecf20Sopenharmony_ci	atomic_set(&sd->cam_exposure, 0);
16178c2ecf20Sopenharmony_ci	atomic_set(&sd->fps, 0);
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	return 0;
16208c2ecf20Sopenharmony_ci}
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev)
16238c2ecf20Sopenharmony_ci{
16248c2ecf20Sopenharmony_ci	struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	command_pause(gspca_dev);
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	/* save camera state for later open (developers guide ch 3.5.3) */
16298c2ecf20Sopenharmony_ci	save_camera_state(gspca_dev);
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	/* GotoLoPower */
16328c2ecf20Sopenharmony_ci	goto_low_power(gspca_dev);
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	/* Update the camera status */
16358c2ecf20Sopenharmony_ci	do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
16388c2ecf20Sopenharmony_ci	/* If the last button state is pressed, release it now! */
16398c2ecf20Sopenharmony_ci	if (sd->params.qx3.button) {
16408c2ecf20Sopenharmony_ci		/* The camera latch will hold the pressed state until we reset
16418c2ecf20Sopenharmony_ci		   the latch, so we do not reset sd->params.qx3.button now, to
16428c2ecf20Sopenharmony_ci		   avoid a false keypress being reported the next sd_start */
16438c2ecf20Sopenharmony_ci		input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
16448c2ecf20Sopenharmony_ci		input_sync(gspca_dev->input_dev);
16458c2ecf20Sopenharmony_ci	}
16468c2ecf20Sopenharmony_ci#endif
16478c2ecf20Sopenharmony_ci}
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */
16508c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
16518c2ecf20Sopenharmony_ci{
16528c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
16538c2ecf20Sopenharmony_ci	int ret;
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	/* Start / Stop the camera to make sure we are talking to
16568c2ecf20Sopenharmony_ci	   a supported camera, and to get some information from it
16578c2ecf20Sopenharmony_ci	   to print. */
16588c2ecf20Sopenharmony_ci	ret = sd_start(gspca_dev);
16598c2ecf20Sopenharmony_ci	if (ret)
16608c2ecf20Sopenharmony_ci		return ret;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	/* Ensure the QX3 illuminators' states are restored upon resume,
16638c2ecf20Sopenharmony_ci	   or disable the illuminator controls, if this isn't a QX3 */
16648c2ecf20Sopenharmony_ci	if (sd->params.qx3.qx3_detected)
16658c2ecf20Sopenharmony_ci		command_setlights(gspca_dev);
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	sd_stopN(gspca_dev);
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "CPIA Version:             %d.%02d (%d.%d)\n",
16708c2ecf20Sopenharmony_ci		  sd->params.version.firmwareVersion,
16718c2ecf20Sopenharmony_ci		  sd->params.version.firmwareRevision,
16728c2ecf20Sopenharmony_ci		  sd->params.version.vcVersion,
16738c2ecf20Sopenharmony_ci		  sd->params.version.vcRevision);
16748c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "CPIA PnP-ID:              %04x:%04x:%04x",
16758c2ecf20Sopenharmony_ci		  sd->params.pnpID.vendor, sd->params.pnpID.product,
16768c2ecf20Sopenharmony_ci		  sd->params.pnpID.deviceRevision);
16778c2ecf20Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "VP-Version:               %d.%d %04x",
16788c2ecf20Sopenharmony_ci		  sd->params.vpVersion.vpVersion,
16798c2ecf20Sopenharmony_ci		  sd->params.vpVersion.vpRevision,
16808c2ecf20Sopenharmony_ci		  sd->params.vpVersion.cameraHeadID);
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	return 0;
16838c2ecf20Sopenharmony_ci}
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev,
16868c2ecf20Sopenharmony_ci			u8 *data,
16878c2ecf20Sopenharmony_ci			int len)
16888c2ecf20Sopenharmony_ci{
16898c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci	/* Check for SOF */
16928c2ecf20Sopenharmony_ci	if (len >= 64 &&
16938c2ecf20Sopenharmony_ci	    data[0] == MAGIC_0 && data[1] == MAGIC_1 &&
16948c2ecf20Sopenharmony_ci	    data[16] == sd->params.format.videoSize &&
16958c2ecf20Sopenharmony_ci	    data[17] == sd->params.format.subSample &&
16968c2ecf20Sopenharmony_ci	    data[18] == sd->params.format.yuvOrder &&
16978c2ecf20Sopenharmony_ci	    data[24] == sd->params.roi.colStart &&
16988c2ecf20Sopenharmony_ci	    data[25] == sd->params.roi.colEnd &&
16998c2ecf20Sopenharmony_ci	    data[26] == sd->params.roi.rowStart &&
17008c2ecf20Sopenharmony_ci	    data[27] == sd->params.roi.rowEnd) {
17018c2ecf20Sopenharmony_ci		u8 *image;
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci		atomic_set(&sd->cam_exposure, data[39] * 2);
17048c2ecf20Sopenharmony_ci		atomic_set(&sd->fps, data[41]);
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci		/* Check for proper EOF for last frame */
17078c2ecf20Sopenharmony_ci		image = gspca_dev->image;
17088c2ecf20Sopenharmony_ci		if (image != NULL &&
17098c2ecf20Sopenharmony_ci		    gspca_dev->image_len > 4 &&
17108c2ecf20Sopenharmony_ci		    image[gspca_dev->image_len - 4] == 0xff &&
17118c2ecf20Sopenharmony_ci		    image[gspca_dev->image_len - 3] == 0xff &&
17128c2ecf20Sopenharmony_ci		    image[gspca_dev->image_len - 2] == 0xff &&
17138c2ecf20Sopenharmony_ci		    image[gspca_dev->image_len - 1] == 0xff)
17148c2ecf20Sopenharmony_ci			gspca_frame_add(gspca_dev, LAST_PACKET,
17158c2ecf20Sopenharmony_ci						NULL, 0);
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci		gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
17188c2ecf20Sopenharmony_ci		return;
17198c2ecf20Sopenharmony_ci	}
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
17228c2ecf20Sopenharmony_ci}
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_cistatic void sd_dq_callback(struct gspca_dev *gspca_dev)
17258c2ecf20Sopenharmony_ci{
17268c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	/* Set the normal compression settings once we have captured a
17298c2ecf20Sopenharmony_ci	   few uncompressed frames (and AEC has hopefully settled) */
17308c2ecf20Sopenharmony_ci	if (sd->first_frame) {
17318c2ecf20Sopenharmony_ci		sd->first_frame--;
17328c2ecf20Sopenharmony_ci		if (sd->first_frame == 0)
17338c2ecf20Sopenharmony_ci			command_setcompression(gspca_dev);
17348c2ecf20Sopenharmony_ci	}
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	/* Switch flicker control back on if it got turned off */
17378c2ecf20Sopenharmony_ci	restart_flicker(gspca_dev);
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci	/* If AEC is enabled, monitor the exposure and
17408c2ecf20Sopenharmony_ci	   adjust the sensor frame rate if needed */
17418c2ecf20Sopenharmony_ci	if (sd->params.exposure.expMode == 2)
17428c2ecf20Sopenharmony_ci		monitor_exposure(gspca_dev);
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	/* Update our knowledge of the camera state */
17458c2ecf20Sopenharmony_ci	do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
17468c2ecf20Sopenharmony_ci	do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
17478c2ecf20Sopenharmony_ci}
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl)
17508c2ecf20Sopenharmony_ci{
17518c2ecf20Sopenharmony_ci	struct gspca_dev *gspca_dev =
17528c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
17538c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	gspca_dev->usb_err = 0;
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci	if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
17588c2ecf20Sopenharmony_ci		return 0;
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci	switch (ctrl->id) {
17618c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
17628c2ecf20Sopenharmony_ci		sd->params.colourParams.brightness = ctrl->val;
17638c2ecf20Sopenharmony_ci		sd->params.flickerControl.allowableOverExposure =
17648c2ecf20Sopenharmony_ci			find_over_exposure(sd->params.colourParams.brightness);
17658c2ecf20Sopenharmony_ci		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
17668c2ecf20Sopenharmony_ci		if (!gspca_dev->usb_err)
17678c2ecf20Sopenharmony_ci			gspca_dev->usb_err = command_setflickerctrl(gspca_dev);
17688c2ecf20Sopenharmony_ci		break;
17698c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
17708c2ecf20Sopenharmony_ci		sd->params.colourParams.contrast = ctrl->val;
17718c2ecf20Sopenharmony_ci		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
17728c2ecf20Sopenharmony_ci		break;
17738c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
17748c2ecf20Sopenharmony_ci		sd->params.colourParams.saturation = ctrl->val;
17758c2ecf20Sopenharmony_ci		gspca_dev->usb_err = command_setcolourparams(gspca_dev);
17768c2ecf20Sopenharmony_ci		break;
17778c2ecf20Sopenharmony_ci	case V4L2_CID_POWER_LINE_FREQUENCY:
17788c2ecf20Sopenharmony_ci		sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
17798c2ecf20Sopenharmony_ci		sd->params.flickerControl.coarseJump =
17808c2ecf20Sopenharmony_ci			flicker_jumps[sd->mainsFreq]
17818c2ecf20Sopenharmony_ci			[sd->params.sensorFps.baserate]
17828c2ecf20Sopenharmony_ci			[sd->params.sensorFps.divisor];
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci		gspca_dev->usb_err = set_flicker(gspca_dev,
17858c2ecf20Sopenharmony_ci			ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
17868c2ecf20Sopenharmony_ci			gspca_dev->streaming);
17878c2ecf20Sopenharmony_ci		break;
17888c2ecf20Sopenharmony_ci	case V4L2_CID_ILLUMINATORS_1:
17898c2ecf20Sopenharmony_ci		sd->params.qx3.bottomlight = ctrl->val;
17908c2ecf20Sopenharmony_ci		gspca_dev->usb_err = command_setlights(gspca_dev);
17918c2ecf20Sopenharmony_ci		break;
17928c2ecf20Sopenharmony_ci	case V4L2_CID_ILLUMINATORS_2:
17938c2ecf20Sopenharmony_ci		sd->params.qx3.toplight = ctrl->val;
17948c2ecf20Sopenharmony_ci		gspca_dev->usb_err = command_setlights(gspca_dev);
17958c2ecf20Sopenharmony_ci		break;
17968c2ecf20Sopenharmony_ci	case CPIA1_CID_COMP_TARGET:
17978c2ecf20Sopenharmony_ci		sd->params.compressionTarget.frTargeting = ctrl->val;
17988c2ecf20Sopenharmony_ci		gspca_dev->usb_err = command_setcompressiontarget(gspca_dev);
17998c2ecf20Sopenharmony_ci		break;
18008c2ecf20Sopenharmony_ci	}
18018c2ecf20Sopenharmony_ci	return gspca_dev->usb_err;
18028c2ecf20Sopenharmony_ci}
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = {
18058c2ecf20Sopenharmony_ci	.s_ctrl = sd_s_ctrl,
18068c2ecf20Sopenharmony_ci};
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev)
18098c2ecf20Sopenharmony_ci{
18108c2ecf20Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
18118c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
18128c2ecf20Sopenharmony_ci	static const char * const comp_target_menu[] = {
18138c2ecf20Sopenharmony_ci		"Quality",
18148c2ecf20Sopenharmony_ci		"Framerate",
18158c2ecf20Sopenharmony_ci		NULL
18168c2ecf20Sopenharmony_ci	};
18178c2ecf20Sopenharmony_ci	static const struct v4l2_ctrl_config comp_target = {
18188c2ecf20Sopenharmony_ci		.ops = &sd_ctrl_ops,
18198c2ecf20Sopenharmony_ci		.id = CPIA1_CID_COMP_TARGET,
18208c2ecf20Sopenharmony_ci		.type = V4L2_CTRL_TYPE_MENU,
18218c2ecf20Sopenharmony_ci		.name = "Compression Target",
18228c2ecf20Sopenharmony_ci		.qmenu = comp_target_menu,
18238c2ecf20Sopenharmony_ci		.max = 1,
18248c2ecf20Sopenharmony_ci		.def = COMP_TARGET_DEF,
18258c2ecf20Sopenharmony_ci	};
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci	gspca_dev->vdev.ctrl_handler = hdl;
18288c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 7);
18298c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
18308c2ecf20Sopenharmony_ci			V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF);
18318c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
18328c2ecf20Sopenharmony_ci			V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF);
18338c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
18348c2ecf20Sopenharmony_ci			V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF);
18358c2ecf20Sopenharmony_ci	sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
18368c2ecf20Sopenharmony_ci			V4L2_CID_POWER_LINE_FREQUENCY,
18378c2ecf20Sopenharmony_ci			V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
18388c2ecf20Sopenharmony_ci			FREQ_DEF);
18398c2ecf20Sopenharmony_ci	if (sd->params.qx3.qx3_detected) {
18408c2ecf20Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
18418c2ecf20Sopenharmony_ci				V4L2_CID_ILLUMINATORS_1, 0, 1, 1,
18428c2ecf20Sopenharmony_ci				ILLUMINATORS_1_DEF);
18438c2ecf20Sopenharmony_ci		v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
18448c2ecf20Sopenharmony_ci				V4L2_CID_ILLUMINATORS_2, 0, 1, 1,
18458c2ecf20Sopenharmony_ci				ILLUMINATORS_2_DEF);
18468c2ecf20Sopenharmony_ci	}
18478c2ecf20Sopenharmony_ci	v4l2_ctrl_new_custom(hdl, &comp_target, NULL);
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	if (hdl->error) {
18508c2ecf20Sopenharmony_ci		pr_err("Could not initialize controls\n");
18518c2ecf20Sopenharmony_ci		return hdl->error;
18528c2ecf20Sopenharmony_ci	}
18538c2ecf20Sopenharmony_ci	return 0;
18548c2ecf20Sopenharmony_ci}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci/* sub-driver description */
18578c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = {
18588c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
18598c2ecf20Sopenharmony_ci	.config = sd_config,
18608c2ecf20Sopenharmony_ci	.init = sd_init,
18618c2ecf20Sopenharmony_ci	.init_controls = sd_init_controls,
18628c2ecf20Sopenharmony_ci	.start = sd_start,
18638c2ecf20Sopenharmony_ci	.stopN = sd_stopN,
18648c2ecf20Sopenharmony_ci	.dq_callback = sd_dq_callback,
18658c2ecf20Sopenharmony_ci	.pkt_scan = sd_pkt_scan,
18668c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT)
18678c2ecf20Sopenharmony_ci	.other_input = 1,
18688c2ecf20Sopenharmony_ci#endif
18698c2ecf20Sopenharmony_ci};
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci/* -- module initialisation -- */
18728c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = {
18738c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0553, 0x0002)},
18748c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0813, 0x0001)},
18758c2ecf20Sopenharmony_ci	{}
18768c2ecf20Sopenharmony_ci};
18778c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci/* -- device connect -- */
18808c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
18818c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
18828c2ecf20Sopenharmony_ci{
18838c2ecf20Sopenharmony_ci	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
18848c2ecf20Sopenharmony_ci				THIS_MODULE);
18858c2ecf20Sopenharmony_ci}
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = {
18888c2ecf20Sopenharmony_ci	.name = MODULE_NAME,
18898c2ecf20Sopenharmony_ci	.id_table = device_table,
18908c2ecf20Sopenharmony_ci	.probe = sd_probe,
18918c2ecf20Sopenharmony_ci	.disconnect = gspca_disconnect,
18928c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
18938c2ecf20Sopenharmony_ci	.suspend = gspca_suspend,
18948c2ecf20Sopenharmony_ci	.resume = gspca_resume,
18958c2ecf20Sopenharmony_ci	.reset_resume = gspca_resume,
18968c2ecf20Sopenharmony_ci#endif
18978c2ecf20Sopenharmony_ci};
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver);
1900