camera: usb: Add a quirk to favor MJPEG over YUYV

Add a quirk to workaround the problem that Logitech Webcam Pro 9000
might stuck in DQBUF after switching resolutions.

BUG=b:138159048
TEST=Open Hangout with Logitech Webcam Pro 9000

Cq-Depend: chrome-internal:1513379
Change-Id: I2a333eb59bc61e7f230dfb45b85b676b20388083
Reviewed-on: https://chromium-review.googlesource.com/1717852
Tested-by: Shik Chen <shik@chromium.org>
Commit-Ready: Shik Chen <shik@chromium.org>
Legacy-Commit-Queue: Commit Bot <commit-bot@chromium.org>
Reviewed-by: Ricky Liang <jcliang@chromium.org>
(cherry picked from commit 1dee5efe3198f413f0bc32c7d7f4d4410bfd3203)
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/1736187
Reviewed-by: Shik Chen <shik@chromium.org>
Commit-Queue: Shik Chen <shik@chromium.org>
diff --git a/camera/hal/usb/camera_characteristics.cc b/camera/hal/usb/camera_characteristics.cc
index e5b26cc..e50f90b 100644
--- a/camera/hal/usb/camera_characteristics.cc
+++ b/camera/hal/usb/camera_characteristics.cc
@@ -20,6 +20,7 @@
 
 #include "cros-camera/common.h"
 #include "cros-camera/timezone.h"
+#include "hal/usb/quirks.h"
 
 namespace cros {
 
@@ -55,6 +56,22 @@
   return res;
 }
 
+uint32_t ParseQuirks(const std::string& value) {
+  static const std::map<std::string, uint32_t> kNameMap = {
+      {"monocle", kQuirkMonocle},
+      {"prefer_mjpeg", kQuirkPreferMjpeg},
+  };
+  std::vector<std::string> names = base::SplitString(
+      value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  uint32_t quirks = 0;
+  for (const auto& name : names) {
+    auto it = kNameMap.find(name);
+    CHECK(it != kNameMap.end()) << "Invalid quirk name " << name;
+    quirks |= it->second;
+  }
+  return quirks;
+}
+
 void SetEntry(const std::string& key,
               const std::string& value,
               DeviceInfo* info) {
@@ -67,8 +84,8 @@
   } else if (key == "constant_framerate_unsupported") {
     std::istringstream(value) >> std::boolalpha >>
         info->constant_framerate_unsupported;
-  } else if (key == "monocle_quirks") {
-    std::istringstream(value) >> std::boolalpha >> info->monocle_quirks;
+  } else if (key == "quirks") {
+    info->quirks = ParseQuirks(value);
   } else if (key == "lens_facing") {
     info->lens_facing = stoi(value);
   } else if (key == "sensor_orientation") {
diff --git a/camera/hal/usb/camera_client.cc b/camera/hal/usb/camera_client.cc
index c0a4e58..47e7190 100644
--- a/camera/hal/usb/camera_client.cc
+++ b/camera/hal/usb/camera_client.cc
@@ -54,7 +54,8 @@
 
   SupportedFormats supported_formats =
       device_->GetDeviceSupportedFormats(device_info_.device_path);
-  qualified_formats_ = GetQualifiedFormats(supported_formats);
+  qualified_formats_ =
+      GetQualifiedFormats(supported_formats, device_info_.quirks);
 
   metadata_handler_ = std::make_unique<MetadataHandler>(
       static_info, device_info, qualified_formats_);
@@ -526,7 +527,8 @@
       is_video_recording_(false) {
   SupportedFormats supported_formats =
       device_->GetDeviceSupportedFormats(device_info_.device_path);
-  qualified_formats_ = GetQualifiedFormats(supported_formats);
+  qualified_formats_ =
+      GetQualifiedFormats(supported_formats, device_info_.quirks);
 }
 
 CameraClient::RequestHandler::~RequestHandler() {}
diff --git a/camera/hal/usb/camera_hal.cc b/camera/hal/usb/camera_hal.cc
index 4c56d55..7805972 100644
--- a/camera/hal/usb/camera_hal.cc
+++ b/camera/hal/usb/camera_hal.cc
@@ -17,6 +17,7 @@
 #include "hal/usb/camera_characteristics.h"
 #include "hal/usb/common_types.h"
 #include "hal/usb/metadata_handler.h"
+#include "hal/usb/quirks.h"
 #include "hal/usb/stream_format.h"
 #include "hal/usb/v4l2_camera_device.h"
 #include "hal/usb/vendor_tag.h"
@@ -41,7 +42,8 @@
   SupportedFormats supported_formats =
       V4L2CameraDevice::GetDeviceSupportedFormats(device_info.device_path);
   bool is_external = device_info.lens_facing == ANDROID_LENS_FACING_EXTERNAL;
-  SupportedFormats qualified_formats = GetQualifiedFormats(supported_formats);
+  SupportedFormats qualified_formats =
+      GetQualifiedFormats(supported_formats, device_info.quirks);
   if (MetadataHandler::FillMetadataFromSupportedFormats(
           qualified_formats, &metadata, is_external) != 0) {
     return nullptr;
@@ -364,6 +366,9 @@
   info.usb_pid = pid;
   info.is_vivid = is_vivid;
   info.power_line_frequency = V4L2CameraDevice::GetPowerLineFrequency(path);
+  if (!is_vivid) {
+    info.quirks |= GetQuirks(vid, pid);
+  }
 
   if (info_ptr == nullptr) {
     info.lens_facing = ANDROID_LENS_FACING_EXTERNAL;
diff --git a/camera/hal/usb/common_types.h b/camera/hal/usb/common_types.h
index 1aa684a..665a10a 100644
--- a/camera/hal/usb/common_types.h
+++ b/camera/hal/usb/common_types.h
@@ -49,6 +49,9 @@
   uint32_t lens_facing;
   int32_t sensor_orientation = 0;
 
+  // Special settings for device specific quirks.
+  uint32_t quirks = 0;
+
   // These fields are not available for external cameras.
   std::vector<float> lens_info_available_apertures;
   std::vector<float> lens_info_available_focal_lengths;
@@ -58,9 +61,6 @@
   int32_t sensor_info_pixel_array_size_height;
   float sensor_info_physical_size_width;
   float sensor_info_physical_size_height;
-
-  // Special setting for specified camera modules.
-  bool monocle_quirks = false;
 };
 
 typedef std::vector<DeviceInfo> DeviceInfos;
diff --git a/camera/hal/usb/libcamera_hal.gyp b/camera/hal/usb/libcamera_hal.gyp
index e2286f6..5ecbb5f 100644
--- a/camera/hal/usb/libcamera_hal.gyp
+++ b/camera/hal/usb/libcamera_hal.gyp
@@ -56,6 +56,7 @@
         'frame_buffer.cc',
         'image_processor.cc',
         'metadata_handler.cc',
+        'quirks.cc',
         'sensor_handler.cc',
         'stream_format.cc',
         'test_pattern.cc',
diff --git a/camera/hal/usb/metadata_handler.cc b/camera/hal/usb/metadata_handler.cc
index 0d020ec..984d602 100644
--- a/camera/hal/usb/metadata_handler.cc
+++ b/camera/hal/usb/metadata_handler.cc
@@ -12,6 +12,7 @@
 
 #include "cros-camera/common.h"
 #include "cros-camera/utils/camera_config.h"
+#include "hal/usb/quirks.h"
 #include "hal/usb/stream_format.h"
 #include "hal/usb/v4l2_camera_device.h"
 #include "hal/usb/vendor_tag.h"
@@ -634,7 +635,7 @@
   UPDATE(ANDROID_CONTROL_AE_ANTIBANDING_MODE, &ae_antibanding_mode, 1);
 
   // Set vendor tags for specified boards.
-  if (device_info.monocle_quirks) {
+  if (device_info.quirks & kQuirkMonocle) {
     int32_t timestamp_sync =
         static_cast<int32_t>(mojom::CameraSensorSyncTimestamp::NEAREST);
     UPDATE(kVendorTagTimestampSync, &timestamp_sync, 1);
diff --git a/camera/hal/usb/quirks.cc b/camera/hal/usb/quirks.cc
new file mode 100644
index 0000000..1ce80e8
--- /dev/null
+++ b/camera/hal/usb/quirks.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "hal/usb/quirks.h"
+
+#include <map>
+#include <utility>
+
+namespace cros {
+
+namespace {
+
+using VidPidPair = std::pair<std::string, std::string>;
+using QuirksMap = std::map<VidPidPair, uint32_t>;
+
+const QuirksMap& GetQuirksMap() {
+  static const QuirksMap kQuirksMap = {
+      // Logitech Webcam Pro 9000 (b/138159048)
+      {{"046d", "0809"}, kQuirkPreferMjpeg},
+  };
+  return kQuirksMap;
+}
+
+}  // namespace
+
+uint32_t GetQuirks(const std::string& vid, const std::string& pid) {
+  const QuirksMap& quirks_map = GetQuirksMap();
+  auto it = quirks_map.find({vid, pid});
+  if (it != quirks_map.end()) {
+    return it->second;
+  }
+  return 0;
+}
+
+}  // namespace cros
diff --git a/camera/hal/usb/quirks.h b/camera/hal/usb/quirks.h
new file mode 100644
index 0000000..6414c3a
--- /dev/null
+++ b/camera/hal/usb/quirks.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CAMERA_HAL_USB_QUIRKS_H_
+#define CAMERA_HAL_USB_QUIRKS_H_
+
+#include <cstdint>
+#include <string>
+
+#include "hal/usb/common_types.h"
+
+namespace cros {
+
+// The bitmask for each quirk.
+enum : uint32_t {
+  kQuirkMonocle = 1 << 0,
+  kQuirkPreferMjpeg = 1 << 1,
+};
+
+uint32_t GetQuirks(const std::string& vid, const std::string& pid);
+
+}  // namespace cros
+
+#endif  // CAMERA_HAL_USB_QUIRKS_H_
diff --git a/camera/hal/usb/sensor_handler_monocle.cc b/camera/hal/usb/sensor_handler_monocle.cc
index 848d46d..f421c0f 100644
--- a/camera/hal/usb/sensor_handler_monocle.cc
+++ b/camera/hal/usb/sensor_handler_monocle.cc
@@ -11,13 +11,14 @@
 #include <rts_read_sensor.h>
 
 #include "cros-camera/common.h"
+#include "hal/usb/quirks.h"
 
 namespace cros {
 
 SensorHandlerMonocle::SensorHandlerMonocle(
     const DeviceInfo& device_info, const SupportedFormats& supported_formats)
     : handle_(nullptr) {
-  if (!device_info.monocle_quirks) {
+  if (!(device_info.quirks & kQuirkMonocle)) {
     return;
   }
 
diff --git a/camera/hal/usb/stream_format.cc b/camera/hal/usb/stream_format.cc
index e7e6a92..fec0aec 100644
--- a/camera/hal/usb/stream_format.cc
+++ b/camera/hal/usb/stream_format.cc
@@ -13,22 +13,32 @@
 #include <system/graphics.h>
 
 #include "cros-camera/common.h"
+#include "hal/usb/quirks.h"
 
 namespace cros {
 
-static std::vector<int> kSupportedHalFormats{
+namespace {
+
+constexpr int kSupportedHalFormats[] = {
     HAL_PIXEL_FORMAT_BLOB, HAL_PIXEL_FORMAT_YCbCr_420_888,
     HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED};
 
-static const std::vector<uint32_t> GetSupportedFourCCs() {
+std::vector<uint32_t> GetSupportedFourCCs(bool prefer_mjpeg) {
   // The preference of supported fourccs in the list is from high to low.
-  static const std::vector<uint32_t> kSupportedFourCCs = {
+  if (prefer_mjpeg) {
+    return {
+        V4L2_PIX_FMT_MJPEG,
+        V4L2_PIX_FMT_YUYV,
+    };
+  }
+  return {
       V4L2_PIX_FMT_YUYV,
       V4L2_PIX_FMT_MJPEG,
   };
-  return kSupportedFourCCs;
 }
 
+}  // namespace
+
 // Return corresponding format by matching resolution |width|x|height| in
 // |formats|.
 const SupportedFormat* FindFormatByResolution(const SupportedFormats& formats,
@@ -117,10 +127,12 @@
   return ret;
 }
 
-SupportedFormats GetQualifiedFormats(
-    const SupportedFormats& supported_formats) {
+SupportedFormats GetQualifiedFormats(const SupportedFormats& supported_formats,
+                                     uint32_t quirks) {
   // The preference of supported fourccs in the list is from high to low.
-  const std::vector<uint32_t> supported_fourccs = GetSupportedFourCCs();
+  bool prefer_mjpeg = quirks & kQuirkPreferMjpeg;
+  const std::vector<uint32_t> supported_fourccs =
+      GetSupportedFourCCs(prefer_mjpeg);
   SupportedFormats qualified_formats;
   for (const auto& supported_fourcc : supported_fourccs) {
     for (const auto& supported_format : supported_formats) {
@@ -161,8 +173,9 @@
 
 bool IsFormatSupported(const SupportedFormats& supported_formats,
                        const camera3_stream_t& stream) {
-  if (std::find(kSupportedHalFormats.begin(), kSupportedHalFormats.end(),
-                stream.format) == kSupportedHalFormats.end()) {
+  if (std::find(std::begin(kSupportedHalFormats),
+                std::end(kSupportedHalFormats),
+                stream.format) == std::end(kSupportedHalFormats)) {
     return false;
   }
   for (const auto& supported_format : supported_formats) {
diff --git a/camera/hal/usb/stream_format.h b/camera/hal/usb/stream_format.h
index 6c15a7c..97d4314 100644
--- a/camera/hal/usb/stream_format.h
+++ b/camera/hal/usb/stream_format.h
@@ -31,7 +31,8 @@
 
 // Find all formats in preference order.
 // The resolutions in returned SupportedFormats vector are unique.
-SupportedFormats GetQualifiedFormats(const SupportedFormats& supported_formats);
+SupportedFormats GetQualifiedFormats(const SupportedFormats& supported_formats,
+                                     uint32_t quirks);
 
 // Check |stream| is supported in |supported_formats|.
 bool IsFormatSupported(const SupportedFormats& supported_formats,