From ee34eedf620ff0b697d66eb70e06b91062ab7e15 Mon Sep 17 00:00:00 2001 From: Spencer Bull Date: Tue, 29 Sep 2026 00:24:27 -0500 Subject: [PATCH] Scale AIQ analog gain codes to the ov08x40 driver's units The OV08X40 tuning (OV08X40_KAFE799_PTL.aiqb) makes AIQ produce the raw 0x3508 register value, where 1x = 256. The in-tree ov08x40 driver takes V4L2_CID_ANALOGUE_GAIN in units of half that (1x = 128, range 128..1984, written to the register as val << 1). The HAL passed the code through, so every analog gain below 7.75x ran at twice what AE and the ISP noise model believed, and every request above collapsed to the 15.5x clamp. Measured on a Dell XPS 16 DA16260: a 7.75x request (code 1984) reads back as analogue_gain = 1984, i.e. 15.5x on the sensor. Add an "analogGainCodeShift" sensor option that right-shifts the code before V4L2_CID_ANALOGUE_GAIN, and set it to 1 for the ov08x40 on both IPU7.5 and IPU7: the Lunar Lake tuning (OV08X40_BBG802N3_LNL.aiqb) carries the same gain table. Sensors without the option are unchanged. --- config/linux/ipu75xa/sensors/ov08x40-uf.json | 3 +++ config/linux/ipu7x/sensors/ov08x40-uf.json | 3 +++ src/core/SensorHwCtrl.cpp | 9 ++++++--- src/platformdata/CameraSensorsParser.cpp | 3 +++ src/platformdata/PlatformData.cpp | 4 ++++ src/platformdata/PlatformData.h | 11 +++++++++++ 6 files changed, 30 insertions(+), 3 deletions(-) diff --git a/config/linux/ipu75xa/sensors/ov08x40-uf.json b/config/linux/ipu75xa/sensors/ov08x40-uf.json index 483bf1d..dbe2363 100644 --- a/config/linux/ipu75xa/sensors/ov08x40-uf.json +++ b/config/linux/ipu75xa/sensors/ov08x40-uf.json @@ -194,6 +194,9 @@ "initialSkipFrame": 0, "exposureLag": 2, "gainLag": 2, + // AIQ codes are the 0x3508 register value (1x = 256); the in-tree + // ov08x40 driver takes V4L2_CID_ANALOGUE_GAIN as register / 2 (1x = 128). + "analogGainCodeShift": 1, "digitalGainLag": 0, "ltmGainLag": 1, // There are 2 yuv color range mode, like full, reduced. diff --git a/config/linux/ipu7x/sensors/ov08x40-uf.json b/config/linux/ipu7x/sensors/ov08x40-uf.json index 472b0da..00f78d7 100644 --- a/config/linux/ipu7x/sensors/ov08x40-uf.json +++ b/config/linux/ipu7x/sensors/ov08x40-uf.json @@ -194,6 +194,9 @@ "initialSkipFrame": 0, "exposureLag": 2, "gainLag": 2, + // AIQ codes are the 0x3508 register value (1x = 256); the in-tree + // ov08x40 driver takes V4L2_CID_ANALOGUE_GAIN as register / 2 (1x = 128). + "analogGainCodeShift": 1, "digitalGainLag": 0, "ltmGainLag": 1, // There are 2 yuv color range mode, like full, reduced. diff --git a/src/core/SensorHwCtrl.cpp b/src/core/SensorHwCtrl.cpp index 799e02e..a7832a2 100644 --- a/src/core/SensorHwCtrl.cpp +++ b/src/core/SensorHwCtrl.cpp @@ -136,9 +136,12 @@ int SensorHwCtrl::setAnalogGains(const vector& analogGains) { CheckAndLogError(mPixelArraySubdev == nullptr, NO_INIT, "pixel array sub device is not set"); CheckAndLogError(analogGains.empty(), BAD_VALUE, "No analog gain data!"); - LOG2("%s analogGain=%d", __func__, analogGains[0]); - int status = mPixelArraySubdev->SetControl(V4L2_CID_ANALOGUE_GAIN, analogGains[0]); - CheckAndLogError((status != 0), status, "failed to set analog gain %d.", analogGains[0]); + // AIQ produces the sensor's register code; some drivers expose the gain in + // coarser units (e.g. ov08x40 takes code / 2), so scale it down to match. + const int analogGain = analogGains[0] >> PlatformData::getAnalogGainCodeShift(mCameraId); + LOG2("%s analogGain=%d", __func__, analogGain); + int status = mPixelArraySubdev->SetControl(V4L2_CID_ANALOGUE_GAIN, analogGain); + CheckAndLogError((status != 0), status, "failed to set analog gain %d.", analogGain); return OK; } diff --git a/src/platformdata/CameraSensorsParser.cpp b/src/platformdata/CameraSensorsParser.cpp index c5a4633..04ac4ef 100644 --- a/src/platformdata/CameraSensorsParser.cpp +++ b/src/platformdata/CameraSensorsParser.cpp @@ -804,6 +804,9 @@ void CameraSensorsParser::parseSensorSection(const Json::Value& node) { if (node.isMember("gainLag")) { mCurCam->mAnalogGainLag = node["gainLag"].asInt(); } + if (node.isMember("analogGainCodeShift")) { + mCurCam->mAnalogGainCodeShift = node["analogGainCodeShift"].asInt(); + } if (node.isMember("digitalGainLag")) { mCurCam->mDigitalGainLag = node["digitalGainLag"].asInt(); } diff --git a/src/platformdata/PlatformData.cpp b/src/platformdata/PlatformData.cpp index 7e25e61..d4b7cd3 100644 --- a/src/platformdata/PlatformData.cpp +++ b/src/platformdata/PlatformData.cpp @@ -379,6 +379,10 @@ int PlatformData::getDigitalGainLag(int cameraId) { return getInstance()->mStaticCfg.mCameras[cameraId].mDigitalGainLag; } +int PlatformData::getAnalogGainCodeShift(int cameraId) { + return getInstance()->mStaticCfg.mCameras[cameraId].mAnalogGainCodeShift; +} + int PlatformData::getExposureLag(int cameraId) { return getInstance()->mStaticCfg.mCameras[cameraId].mExposureLag; } diff --git a/src/platformdata/PlatformData.h b/src/platformdata/PlatformData.h index 8f1f84d..bfc8e6e 100644 --- a/src/platformdata/PlatformData.h +++ b/src/platformdata/PlatformData.h @@ -211,6 +211,7 @@ class PlatformData { mDigitalGainLag(-1), mExposureLag(MAX_BUFFER_COUNT), mAnalogGainLag(0), + mAnalogGainCodeShift(0), mMaxSensorDigitalGain(0), mSensorDgType(SensorDgType::SENSOR_DG_TYPE_NONE), mISysFourcc(V4L2_PIX_FMT_SGRBG8), @@ -296,6 +297,7 @@ class PlatformData { int mDigitalGainLag; int mExposureLag; int mAnalogGainLag; + int mAnalogGainCodeShift; int mMaxSensorDigitalGain; SensorDgType mSensorDgType; std::string mCustomAicLibraryName; @@ -843,6 +845,15 @@ class PlatformData { * \return the value of digital gain lag */ static int getDigitalGainLag(int cameraId); + + /** + * Get the right shift from AIQ analog gain codes to the sensor driver's + * V4L2_CID_ANALOGUE_GAIN units + * + * \param cameraId: [0, MAX_CAMERA_NUMBER - 1] + * \return the shift, 0 when the driver takes AIQ codes as they are + */ + static int getAnalogGainCodeShift(int cameraId); /** * Get sensor's exposure lag *