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