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