QCamera3: Initial support for PB HDR+

Initialize HDR+ client when HDR+ is enabled and create a HDR+
RAW channel to request an additional RAW for every request
from camera service.

Get sensor resolution and output pixel clock from mm-camera2.

Configure streams with HDR+ client using sensor mode
information and configured streams with camera service.

HDR+ mode is OFF by default and can be enabled by setting
persist.camera.hdrplus to 1.

Bug: 28637032
Change-Id: Id79077baefe426014cd489ae7380ad6ced1738b0
diff --git a/msm8998/QCamera2/HAL3/QCamera3HWI.cpp b/msm8998/QCamera2/HAL3/QCamera3HWI.cpp
index 1abd515..443c5f1 100644
--- a/msm8998/QCamera2/HAL3/QCamera3HWI.cpp
+++ b/msm8998/QCamera2/HAL3/QCamera3HWI.cpp
@@ -373,6 +373,7 @@
       mSupportChannel(NULL),
       mAnalysisChannel(NULL),
       mRawDumpChannel(NULL),
+      mHdrPlusRawSrcChannel(NULL),
       mDummyBatchChannel(NULL),
       mPerfLockMgr(),
       mCommon(),
@@ -549,6 +550,10 @@
         mRawDumpChannel->stop();
     }
 
+    if (mHdrPlusRawSrcChannel) {
+        mHdrPlusRawSrcChannel->stop();
+    }
+
     // NOTE: 'camera3_stream_t *' objects are already freed at
     //        this stage by the framework
     for (List<stream_info_t *>::iterator it = mStreamInfo.begin();
@@ -593,6 +598,10 @@
         delete mRawDumpChannel;
         mRawDumpChannel = NULL;
     }
+    if (mHdrPlusRawSrcChannel) {
+        delete mHdrPlusRawSrcChannel;
+        mHdrPlusRawSrcChannel = NULL;
+    }
     if (mDummyBatchChannel) {
         delete mDummyBatchChannel;
         mDummyBatchChannel = NULL;
@@ -914,6 +923,12 @@
     rc = mCameraHandle->ops->close_camera(mCameraHandle->camera_handle);
     mCameraHandle = NULL;
 
+    // Disconnect from HDR+ client.
+    if (mHdrPlusClient != nullptr) {
+        mHdrPlusClient->disconnect();
+        mHdrPlusClient = nullptr;
+    }
+
     //reset session id to some invalid id
     pthread_mutex_lock(&gCamLock);
     sessionId[mCameraId] = 0xDEADBEEF;
@@ -1172,19 +1187,19 @@
 }
 
 /*==============================================================================
- * FUNCTION   : getSensorOutputSize
+ * FUNCTION   : sensor_mode_info
  *
- * DESCRIPTION: Get sensor output size based on current stream configuratoin
+ * DESCRIPTION: Get sensor mode information based on current stream configuratoin
  *
  * PARAMETERS :
- *   @sensor_dim : sensor output dimension (output)
+ *   @sensor_mode_info : sensor mode information (output)
  *
  * RETURN     : int32_t type of status
  *              NO_ERROR  -- success
  *              none-zero failure code
  *
  *==========================================================================*/
-int32_t QCamera3HardwareInterface::getSensorOutputSize(cam_dimension_t &sensor_dim)
+int32_t QCamera3HardwareInterface::getSensorModeInfo(cam_sensor_mode_info_t &sensorModeInfo)
 {
     int32_t rc = NO_ERROR;
 
@@ -1212,17 +1227,20 @@
     }
 
     clear_metadata_buffer(mParameters);
-    ADD_GET_PARAM_ENTRY_TO_BATCH(mParameters, CAM_INTF_PARM_RAW_DIMENSION);
+    ADD_GET_PARAM_ENTRY_TO_BATCH(mParameters, CAM_INTF_PARM_SENSOR_MODE_INFO);
 
     rc = mCameraHandle->ops->get_parms(mCameraHandle->camera_handle,
             mParameters);
     if (rc != NO_ERROR) {
-        LOGE("Failed to get CAM_INTF_PARM_RAW_DIMENSION");
+        LOGE("Failed to get CAM_INTF_PARM_SENSOR_MODE_INFO");
         return rc;
     }
 
-    READ_PARAM_ENTRY(mParameters, CAM_INTF_PARM_RAW_DIMENSION, sensor_dim);
-    LOGH("sensor output dimension = %d x %d", sensor_dim.width, sensor_dim.height);
+    READ_PARAM_ENTRY(mParameters, CAM_INTF_PARM_SENSOR_MODE_INFO, sensorModeInfo);
+    LOGH("%s: active array size %dx%d, pixel array size %dx%d, output pixel clock %u", __FUNCTION__,
+        sensorModeInfo.active_array_size.width, sensorModeInfo.active_array_size.height,
+        sensorModeInfo.pixel_array_size.width, sensorModeInfo.pixel_array_size.height,
+        sensorModeInfo.op_pixel_clk);
 
     return rc;
 }
@@ -1451,6 +1469,12 @@
         mRawDumpChannel = NULL;
     }
 
+    if (mHdrPlusRawSrcChannel) {
+        mHdrPlusRawSrcChannel->stop();
+        delete mHdrPlusRawSrcChannel;
+        mHdrPlusRawSrcChannel = NULL;
+    }
+
     if (mSupportChannel)
         mSupportChannel->stop();
 
@@ -1469,6 +1493,41 @@
 
     pthread_mutex_lock(&mMutex);
 
+    // Check if HDR+ is enabled.
+    char prop[PROPERTY_VALUE_MAX];
+    property_get("persist.camera.hdrplus", prop, "0");
+    bool enableHdrPlus = atoi(prop);
+    if (enableHdrPlus) {
+        ALOGD("%s: HDR+ in Camera HAL enabled.", __FUNCTION__);
+        // Connect to HDR+ client if not yet.
+        if (mHdrPlusClient == nullptr) {
+            mHdrPlusClient = std::make_shared<HdrPlusClient>();
+            rc = mHdrPlusClient->connect(this);
+            if (rc < 0) {
+                LOGE("%s: Failed to connect to HDR+ client: %s (%d).", __FUNCTION__,
+                    strerror(-rc), rc);
+                pthread_mutex_unlock(&mMutex);
+                return -ENODEV;
+            }
+
+            // Set static metadata.
+            rc = mHdrPlusClient->setStaticMetadata(*gStaticMetadata[mCameraId]);
+            if (rc < 0) {
+                LOGE("%s: Failed set static metadata in HDR+ client: %s (%d).", __FUNCTION__,
+                    strerror(-rc), rc);
+                pthread_mutex_unlock(&mMutex);
+                return -ENODEV;
+            }
+        }
+    } else {
+        ALOGD("%s: HDR+ in Camera HAL disabled.", __FUNCTION__);
+        // Disconnect from HDR+ client if HDR+ is not enabled.
+        if (mHdrPlusClient != nullptr) {
+            mHdrPlusClient->disconnect();
+            mHdrPlusClient = nullptr;
+        }
+    }
+
     // Check state
     switch (mState) {
         case INITIALIZED:
@@ -2280,6 +2339,33 @@
         }
     }
 
+    // Initialize HDR+ Raw Source channel.
+    if (mHdrPlusClient != nullptr) {
+        if (isRawStreamRequested || mRawDumpChannel) {
+            ALOGE("%s: Enabling HDR+ while RAW output stream is configured is not supported.",
+                __FUNCTION__);
+            mHdrPlusClient->disconnect();
+            mHdrPlusClient = nullptr;
+        } else {
+            cam_dimension_t rawSize = getMaxRawSize(mCameraId);
+            cam_feature_mask_t hdrPlusRawFeatureMask = CAM_QCOM_FEATURE_NONE;
+            setPAAFSupport(hdrPlusRawFeatureMask,
+                    CAM_STREAM_TYPE_RAW,
+                    gCamCapability[mCameraId]->color_arrangement);
+            mHdrPlusRawSrcChannel = new QCamera3HdrPlusRawSrcChannel(mCameraHandle->camera_handle,
+                                      mChannelHandle,
+                                      mCameraHandle->ops,
+                                      rawSize,
+                                      &padding_info,
+                                      this, hdrPlusRawFeatureMask);
+            if (!mHdrPlusRawSrcChannel) {
+                LOGE("HDR+ Raw Source channel cannot be created");
+                pthread_mutex_unlock(&mMutex);
+                return -ENOMEM;
+            }
+        }
+    }
+
 
     if (mAnalysisChannel) {
         cam_analysis_info_t analysisInfo;
@@ -2369,6 +2455,19 @@
                 gCamCapability[mCameraId]->color_arrangement);
         mStreamConfigInfo.num_streams++;
     }
+
+    if (mHdrPlusRawSrcChannel) {
+        cam_dimension_t rawSize;
+        rawSize = getMaxRawSize(mCameraId);
+        mStreamConfigInfo.stream_sizes[mStreamConfigInfo.num_streams] = rawSize;
+        mStreamConfigInfo.type[mStreamConfigInfo.num_streams] = CAM_STREAM_TYPE_RAW;
+        mStreamConfigInfo.postprocess_mask[mStreamConfigInfo.num_streams] = CAM_QCOM_FEATURE_NONE;
+        setPAAFSupport(mStreamConfigInfo.postprocess_mask[mStreamConfigInfo.num_streams],
+                mStreamConfigInfo.type[mStreamConfigInfo.num_streams],
+                gCamCapability[mCameraId]->color_arrangement);
+        mStreamConfigInfo.num_streams++;
+    }
+
     /* In HFR mode, if video stream is not added, create a dummy channel so that
      * ISP can create a batch mode even for preview only case. This channel is
      * never 'start'ed (no stream-on), it is only 'initialized'  */
@@ -3978,6 +4077,48 @@
     return NO_ERROR;
 }
 
+status_t QCamera3HardwareInterface::fillPbStreamConfig(
+        pbcamera::StreamConfiguration *config, uint32_t pbStreamId, int pbStreamFormat,
+        QCamera3Channel *channel, uint32_t streamIndex) {
+    if (config == nullptr) {
+        LOGE("%s: config is null", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if (channel == nullptr) {
+        LOGE("%s: channel is null", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    QCamera3Stream *stream = channel->getStreamByIndex(streamIndex);
+    if (stream == nullptr) {
+        LOGE("%s: Failed to get stream %d in channel.", __FUNCTION__, streamIndex);
+        return NAME_NOT_FOUND;
+    }
+
+    const cam_stream_info_t* streamInfo = stream->getStreamInfo();
+    if (streamInfo == nullptr) {
+        LOGE("%s: Failed to get stream info for stream %d in channel.", __FUNCTION__, streamIndex);
+        return NAME_NOT_FOUND;
+    }
+
+    config->id = pbStreamId;
+    config->image.width = streamInfo->dim.width;
+    config->image.height = streamInfo->dim.height;
+    config->image.padding = 0;
+    config->image.format = pbStreamFormat;
+
+    // Fill plane information.
+    for (uint32_t i = 0; i < streamInfo->buf_planes.plane_info.num_planes; i++) {
+        pbcamera::PlaneConfiguration plane;
+        plane.stride = streamInfo->buf_planes.plane_info.mp[i].stride_in_bytes;
+        plane.scanline = streamInfo->buf_planes.plane_info.mp[i].scanline;
+        config->image.planes.push_back(plane);
+    }
+
+    return OK;
+}
+
 /*===========================================================================
  * FUNCTION   : processCaptureRequest
  *
@@ -4233,9 +4374,9 @@
             LOGE("set_parms failed for hal version, stream info");
         }
 
-        cam_dimension_t sensor_dim;
-        memset(&sensor_dim, 0, sizeof(sensor_dim));
-        rc = getSensorOutputSize(sensor_dim);
+        cam_sensor_mode_info_t sensor_mode_info;
+        memset(&sensor_mode_info, 0, sizeof(sensor_mode_info));
+        rc = getSensorModeInfo(sensor_mode_info);
         if (rc != NO_ERROR) {
             LOGE("Failed to get sensor output size");
             pthread_mutex_unlock(&mMutex);
@@ -4244,7 +4385,8 @@
 
         mCropRegionMapper.update(gCamCapability[mCameraId]->active_array_size.width,
                 gCamCapability[mCameraId]->active_array_size.height,
-                sensor_dim.width, sensor_dim.height);
+                sensor_mode_info.active_array_size.width,
+                sensor_mode_info.active_array_size.height);
 
         /* Set batchmode before initializing channel. Since registerBuffer
          * internally initializes some of the channels, better set batchmode
@@ -4297,6 +4439,14 @@
                 goto error_exit;
             }
         }
+        if (mHdrPlusRawSrcChannel) {
+            rc = mHdrPlusRawSrcChannel->initialize(IS_TYPE_NONE);
+            if (rc != NO_ERROR) {
+                LOGE("Error: HDR+ RAW Source Channel init failed");
+                pthread_mutex_unlock(&mMutex);
+                goto error_exit;
+            }
+        }
         if (mSupportChannel) {
             rc = mSupportChannel->initialize(IS_TYPE_NONE);
             if (rc < 0) {
@@ -4328,6 +4478,77 @@
             }
         }
 
+        if (mHdrPlusClient != nullptr) {
+            pbcamera::InputConfiguration inputConfig;
+            std::vector<pbcamera::StreamConfiguration> outputStreamConfigs;
+
+            // Configure HDR+ client streams.
+            // Get input config.
+            if (mHdrPlusRawSrcChannel) {
+                // HDR+ input buffers will be provided by HAL.
+                rc = fillPbStreamConfig(&inputConfig.streamConfig, kPbRaw10InputStreamId,
+                        HAL_PIXEL_FORMAT_RAW10, mHdrPlusRawSrcChannel, /*stream index*/0);
+                if (rc != OK) {
+                    LOGE("%s: Failed to get fill stream config for HDR+ raw src stream.",
+                        __FUNCTION__);
+                    pthread_mutex_unlock(&mMutex);
+                    goto error_exit;
+                }
+
+                inputConfig.isSensorInput = false;
+            } else {
+                // Sensor MIPI will send data to Easel.
+                inputConfig.isSensorInput = true;
+                inputConfig.sensorMode.pixelArrayWidth =
+                    sensor_mode_info.pixel_array_size.width;
+                inputConfig.sensorMode.pixelArrayHeight =
+                    sensor_mode_info.pixel_array_size.height;
+                inputConfig.sensorMode.activeArrayWidth =
+                    sensor_mode_info.active_array_size.width;
+                inputConfig.sensorMode.activeArrayHeight =
+                    sensor_mode_info.active_array_size.height;
+                inputConfig.sensorMode.outputPixelClkHz =
+                    sensor_mode_info.op_pixel_clk;
+            }
+
+            // Get output configurations.
+            // Easel may need to output RAW16 buffers if mRawChannel was created.
+            if (mRawChannel != nullptr) {
+                pbcamera::StreamConfiguration outputConfig;
+                rc = fillPbStreamConfig(&outputConfig, kPbRaw16OutputStreamId,
+                        HAL_PIXEL_FORMAT_RAW16, mRawChannel, /*stream index*/0);
+                if (rc != OK) {
+                    LOGE("%s: Failed to get fill stream config for raw stream.", __FUNCTION__);
+                    pthread_mutex_unlock(&mMutex);
+                    goto error_exit;
+                }
+                outputStreamConfigs.push_back(outputConfig);
+            }
+
+            // Easel may need to output YUV output buffers if mPictureChannel was created.
+            if (mPictureChannel != nullptr) {
+                pbcamera::StreamConfiguration outputConfig;
+                rc = fillPbStreamConfig(&outputConfig, kPbYuvOutputStreamId,
+                        HAL_PIXEL_FORMAT_YCrCb_420_SP, mPictureChannel, /*stream index*/0);
+                if (rc != OK) {
+                    LOGE("%s: Failed to get fill stream config for YUV stream.", __FUNCTION__);
+                    pthread_mutex_unlock(&mMutex);
+                    goto error_exit;
+                }
+                outputStreamConfigs.push_back(outputConfig);
+            }
+
+            // TODO: consider other channels for YUV output buffers.
+
+            rc = mHdrPlusClient->configureStreams(inputConfig, outputStreamConfigs);
+            if (rc != OK) {
+                LOGE("%d: Failed to configure streams with HDR+ client: %s (%d)", __FUNCTION__,
+                    strerror(-rc), rc);
+                pthread_mutex_unlock(&mMutex);
+                goto error_exit;
+            }
+        }
+
         // Set bundle info
         rc = setBundleInfo();
         if (rc < 0) {
@@ -4473,6 +4694,33 @@
             }
         }
 
+        if (mHdrPlusRawSrcChannel) {
+            LOGD("Starting HDR+ RAW stream");
+            rc = mHdrPlusRawSrcChannel->start();
+            if (rc != NO_ERROR) {
+                LOGE("Error Starting HDR+ RAW Channel");
+                for (List<stream_info_t *>::iterator it = mStreamInfo.begin();
+                      it != mStreamInfo.end(); it++) {
+                    QCamera3Channel *channel =
+                        (QCamera3Channel *)(*it)->stream->priv;
+                    LOGH("Stopping Processing Channel mask=%d",
+                        channel->getStreamTypeMask());
+                    channel->stop();
+                }
+                if (mSupportChannel)
+                    mSupportChannel->stop();
+                if (mAnalysisChannel) {
+                    mAnalysisChannel->stop();
+                }
+                if (mRawDumpChannel) {
+                    mRawDumpChannel->stop();
+                }
+                mMetadataChannel->stop();
+                pthread_mutex_unlock(&mMutex);
+                goto error_exit;
+            }
+        }
+
         if (mChannelHandle) {
 
             rc = mCameraHandle->ops->start_channel(mCameraHandle->camera_handle,
@@ -4577,6 +4825,12 @@
         streamsArray.stream_request[streamsArray.num_streams++].buf_index = CAM_FREERUN_IDX;
     }
 
+    if (mHdrPlusRawSrcChannel) {
+        streamsArray.stream_request[streamsArray.num_streams].streamID =
+            mHdrPlusRawSrcChannel->getStreamID(mHdrPlusRawSrcChannel->getStreamTypeMask());
+        streamsArray.stream_request[streamsArray.num_streams++].buf_index = CAM_FREERUN_IDX;
+    }
+
     if(request->input_buffer == NULL) {
         /* Parse the settings:
          * - For every request in NORMAL MODE
@@ -12098,6 +12352,9 @@
     if (mRawDumpChannel) {
         mRawDumpChannel->stop();
     }
+    if (mHdrPlusRawSrcChannel) {
+        mHdrPlusRawSrcChannel->stop();
+    }
     if (mMetadataChannel) {
         /* If content of mStreamInfo is not 0, there is metadata stream */
         mMetadataChannel->stop();
@@ -12160,6 +12417,13 @@
             return rc;
         }
     }
+    if (mHdrPlusRawSrcChannel) {
+        rc = mHdrPlusRawSrcChannel->start();
+        if (rc < 0) {
+            LOGE("HDR+ RAW channel start failed");
+            return rc;
+        }
+    }
 
     LOGD("All channels started");
     return rc;
@@ -12361,6 +12625,9 @@
         if (mRawDumpChannel) {
             mRawDumpChannel->setBundleInfo(bundleInfo);
         }
+        if (mHdrPlusRawSrcChannel) {
+            mHdrPlusRawSrcChannel->setBundleInfo(bundleInfo);
+        }
     }
 
     return rc;
@@ -12638,4 +12905,15 @@
         break;
     }
 }
+
+void QCamera3HardwareInterface::onCaptureResult(__unused pbcamera::CaptureResult *result,
+        __unused const camera_metadata_t &resultMetadata) {
+    // TODO: Handle HDR+ capture results.
+}
+
+void QCamera3HardwareInterface::onFailedCaptureResult(
+        __unused pbcamera::CaptureResult *failedResult) {
+    // TODO: Handle HDR+ capture failures.
+}
+
 }; //end namespace qcamera