Merge "sde: Add bandwidth checks in resource manager"
diff --git a/displayengine/include/utils/constants.h b/displayengine/include/utils/constants.h
index 44b6896..a0f2bfb 100644
--- a/displayengine/include/utils/constants.h
+++ b/displayengine/include/utils/constants.h
@@ -30,6 +30,7 @@
 
 #define INT(exp) static_cast<int>(exp)
 #define FLOAT(exp) static_cast<float>(exp)
+#define UINT8(exp) static_cast<uint8_t>(exp)
 #define UINT32(exp) static_cast<uint32_t>(exp)
 #define INT32(exp) static_cast<int32_t>(exp)
 
@@ -46,6 +47,7 @@
 #define SET_BIT(value, bit) (value |= (1 << (bit)))
 #define CLEAR_BIT(value, bit) (value &= (~(1 << (bit))))
 #define IS_BIT_SET(value, bit) (value & (1 << (bit)))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
 
 namespace sde {
 
diff --git a/displayengine/libs/core/hw_interface.h b/displayengine/libs/core/hw_interface.h
index 151dc4a..3e620ce 100644
--- a/displayengine/libs/core/hw_interface.h
+++ b/displayengine/libs/core/hw_interface.h
@@ -92,8 +92,9 @@
   uint32_t pipe_id;
   LayerRect src_roi;
   LayerRect dst_roi;
+  uint8_t decimation;
 
-  HWPipeInfo() : pipe_id(0) { }
+  HWPipeInfo() : pipe_id(0), decimation(1) { }
 };
 
 struct HWLayerConfig {
diff --git a/displayengine/libs/core/res_config.cpp b/displayengine/libs/core/res_config.cpp
index e1b5320..73ea4a4 100755
--- a/displayengine/libs/core/res_config.cpp
+++ b/displayengine/libs/core/res_config.cpp
@@ -230,5 +230,23 @@
   dst_rect->bottom = floorf(src_rect.bottom);
 }
 
+void ResManager::SetDecimationFactor(HWPipeInfo *pipe) {
+  float max_down_scale = FLOAT(hw_res_info_.max_scale_down);
+  float src_h = pipe->src_roi.bottom - pipe->src_roi.top;
+  float dst_h = pipe->dst_roi.bottom - pipe->dst_roi.top;
+  float down_scale = src_h / dst_h;
+  pipe->decimation = 1;
+
+  if (!hw_res_info_.has_decimation || (down_scale <= max_down_scale))
+    return;
+
+  // Decimation is the remaining downscale factor after doing max SDE downscale.
+  // In SDE, decimation is supported in powers of 2.
+  // For ex: If a pipe needs downscale of 8 but max_down_scale is 4
+  // So decimation = powf(2.0, ceilf(log2f(8) - log2f(4))) = powf(2.0, 1.0) = 2
+  float decimation_factor = ceilf(log2f(down_scale) - log2f(max_down_scale));
+  pipe->decimation = UINT8(powf(2.0f, decimation_factor));
+}
+
 }  // namespace sde
 
diff --git a/displayengine/libs/core/res_manager.cpp b/displayengine/libs/core/res_manager.cpp
index 164e7f3..9edca48 100755
--- a/displayengine/libs/core/res_manager.cpp
+++ b/displayengine/libs/core/res_manager.cpp
@@ -231,6 +231,7 @@
     }
 
     src_pipes_[left_index].reserved = true;
+    SetDecimationFactor(pipe_info);
 
     pipe_info =  &hw_layers->config[i].right_pipe;
     if (pipe_info->pipe_id == 0) {
@@ -262,6 +263,12 @@
     src_pipes_[left_index].reserved = true;
     src_pipes_[left_index].at_right = false;
     hw_layers->config[i].left_pipe.pipe_id = src_pipes_[left_index].mdss_pipe_id;
+    SetDecimationFactor(pipe_info);
+  }
+
+  if (!CheckBandwidth(display_resource_ctx, hw_layers)) {
+    DLOGV_IF(kTagResources, "Bandwidth check failed!");
+    goto Acquire_failed;
   }
 
   return kErrorNone;
@@ -272,6 +279,216 @@
   return kErrorResources;
 }
 
+bool ResManager::CheckBandwidth(DisplayResourceContext *display_ctx, HWLayers *hw_layers) {
+  float max_pipe_bw = 1.8f;  // From MDP to hw_res_info_ (in GBps)
+  float max_sde_clk = 400.0f;  // From MDP to hw_res_info_ (in MHz)
+  float clk_fudge_factor = 1.0f;  // From MDP to hw_res_info_
+  const struct HWLayersInfo &layer_info = hw_layers->info;
+
+  float left_pipe_bw[layer_info.count];
+  float right_pipe_bw[layer_info.count];
+  float left_max_clk = 0;
+  float right_max_clk = 0;
+
+  for (uint32_t i = 0; i < layer_info.count; i++) {
+    Layer &layer = layer_info.stack->layers[layer_info.index[i]];
+    float bpp = GetBpp(layer.input_buffer->format);
+    uint32_t left_id = hw_layers->config[i].left_pipe.pipe_id;
+    uint32_t right_id = hw_layers->config[i].right_pipe.pipe_id;
+
+    left_pipe_bw[i] = left_id ? GetPipeBw(display_ctx, &hw_layers->config[i].left_pipe, bpp) : 0;
+    right_pipe_bw[i] = right_id ? GetPipeBw(display_ctx, &hw_layers->config[i].right_pipe, bpp) : 0;
+
+    if ((left_pipe_bw[i] > max_pipe_bw) || (right_pipe_bw[i] > max_pipe_bw)) {
+      DLOGV_IF(kTagResources, "Pipe bandwidth exceeds limit for layer index = %d", i);
+      return false;
+    }
+
+    float left_clk = left_id ? GetClockForPipe(display_ctx, &hw_layers->config[i].left_pipe) : 0;
+    float right_clk = right_id ? GetClockForPipe(display_ctx, &hw_layers->config[i].right_pipe) : 0;
+
+    left_max_clk = MAX(left_clk, left_max_clk);
+    right_max_clk = MAX(right_clk, right_max_clk);
+  }
+
+  float left_mixer_bw = GetOverlapBw(hw_layers, left_pipe_bw, true);
+  float right_mixer_bw = GetOverlapBw(hw_layers, right_pipe_bw, false);
+  float display_bw = left_mixer_bw + right_mixer_bw;
+
+  // Check system bandwidth (nth External + max(nth, n-1th) Primary)
+  if (display_ctx->hw_block_id == kHWPrimary) {
+    display_bw = MAX(display_bw, last_primary_bw_);
+    last_primary_bw_ = left_mixer_bw + right_mixer_bw;
+  }
+
+  // If system has Video mode panel, use max_bandwidth_low, else use max_bandwidth_high
+  if ((display_bw + bw_claimed_) > hw_res_info_.max_bandwidth_low) {
+    DLOGV_IF(kTagResources, "Overlap bandwidth exceeds limit!");
+    return false;
+  }
+
+  // Max clock requirement of display
+  float display_clk = MAX(left_max_clk, right_max_clk);
+
+  // Check max clock requirement of system
+  float system_clk = MAX(display_clk, clk_claimed_);
+
+  // Apply fudge factor to consider in-efficieny
+  if ((system_clk * clk_fudge_factor) > max_sde_clk) {
+    DLOGV_IF(kTagResources, "Clock requirement exceeds limit!");
+    return false;
+  }
+
+  // If Primary display, reset claimed bw & clk for next cycle
+  if (display_ctx->hw_block_id == kHWPrimary) {
+    bw_claimed_ = 0.0f;
+    clk_claimed_ = 0.0f;
+  } else {
+    bw_claimed_ = display_bw;
+    clk_claimed_ = display_clk;
+  }
+
+  return true;
+}
+
+float ResManager::GetPipeBw(DisplayResourceContext *display_ctx, HWPipeInfo *pipe, float bpp) {
+  HWDisplayAttributes &display_attributes = display_ctx->display_attributes;
+  float v_total = 2600.0f;  // From MDP to display_attributes (vBP + vFP + v_active)
+  float fps = 60.0f;  // display_attributes.fps;
+
+  float src_w = pipe->src_roi.right - pipe->src_roi.left;
+  float src_h = pipe->src_roi.bottom - pipe->src_roi.top;
+  float dst_h = pipe->dst_roi.bottom - pipe->dst_roi.top;
+
+  // Adjust src_h with pipe decimation
+  src_h /= FLOAT(pipe->decimation);
+
+  float bw = src_w * src_h * bpp * fps;
+
+  // Consider panel dimension
+  // (v_total / v_active) * (v_active / dst_h)
+  bw *= (v_total / dst_h);
+
+  // Bandwidth in GBps
+  return (bw / 1000000000.0f);
+}
+
+float ResManager::GetClockForPipe(DisplayResourceContext *display_ctx, HWPipeInfo *pipe) {
+  HWDisplayAttributes &display_attributes = display_ctx->display_attributes;
+  float v_total = 2600.0f;  // (vBP + vFP + v_active)
+  float fps = 60.0f;  // display_attributes.fps;
+
+  float src_h = pipe->src_roi.bottom - pipe->src_roi.top;
+  float dst_h = pipe->dst_roi.bottom - pipe->dst_roi.top;
+  float dst_w = pipe->dst_roi.right - pipe->dst_roi.left;
+
+  // Adjust src_h with pipe decimation
+  src_h /= FLOAT(pipe->decimation);
+
+  // SDE Clock requirement in MHz
+  float clk = (dst_w * v_total * fps) / 1000000.0f;
+
+  // Consider down-scaling
+  if (src_h > dst_h)
+    clk *= (src_h / dst_h);
+
+  return clk;
+}
+
+float ResManager::GetOverlapBw(HWLayers *hw_layers, float *pipe_bw, bool left_mixer) {
+  uint32_t count = hw_layers->info.count;
+  float overlap_bw[count][count];
+  float overall_max = 0;
+
+  memset(overlap_bw, 0, sizeof(overlap_bw));
+
+  // Algorithm:
+  // 1.Create an 'n' by 'n' sized 2D array, overlap_bw[n][n] (n = # of layers).
+  // 2.Get overlap_bw between two layers, i and j, and account for other overlaps (prev_max) if any.
+  //   This will fill the bottom-left half of the array including diagonal (0 <= i < n, 0 <= j <= i)
+  //                      {1. pipe_bw[i],                         where i == j
+  //   overlap_bw[i][j] = {2. 0,                                  where i != j && !Overlap(i, j)
+  //                      {3. pipe_bw[i] + pipe_bw[j] + prev_max, where i != j && Overlap(i, j)
+  //
+  //   Overlap(i, j) = !(bottom_i <= top_j || top_i >= bottom_j)
+  //   prev_max = max(prev_max, overlap_bw[j, k]), where 0 <= k < j and prev_max initially 0
+  //   prev_max = prev_max ? (prev_max - pipe_bw[j]) : 0; (to account for "double counting")
+  // 3.Get the max value in 2D array, overlap_bw[n][n], for the final overall_max bandwidth.
+  //   overall_max = max(overlap_bw[i, j]), where 0 <= i < n, 0 <= j <= i
+
+  for (uint32_t i = 0; i < count; i++) {
+    HWPipeInfo &pipe1 = left_mixer ? hw_layers->config[i].left_pipe :
+                        hw_layers->config[i].right_pipe;
+
+    // Non existing pipe never overlaps
+    if (pipe_bw[i] == 0)
+      continue;
+
+    float top1 = pipe1.dst_roi.top;
+    float bottom1 = pipe1.dst_roi.bottom;
+    float row_max = 0;
+
+    for (uint32_t j = 0; j <= i; j++) {
+      HWPipeInfo &pipe2 = left_mixer ? hw_layers->config[j].left_pipe :
+                          hw_layers->config[j].right_pipe;
+
+      if ((pipe_bw[j] == 0) || (i == j)) {
+        overlap_bw[i][j] = pipe_bw[j];
+        row_max = MAX(pipe_bw[j], row_max);
+        continue;
+      }
+
+      float top2 = pipe2.dst_roi.top;
+      float bottom2 = pipe2.dst_roi.bottom;
+
+      if ((bottom1 <= top2) || (top1 >= bottom2)) {
+        overlap_bw[i][j] = 0;
+        continue;
+      }
+
+      overlap_bw[i][j] = pipe_bw[i] + pipe_bw[j];
+
+      float prev_max = 0;
+      for (uint32_t k = 0; k < j; k++) {
+        if (overlap_bw[j][k])
+          prev_max = MAX(overlap_bw[j][k], prev_max);
+      }
+      overlap_bw[i][j] += (prev_max > 0) ? (prev_max - pipe_bw[j]) : 0;
+      row_max = MAX(overlap_bw[i][j], row_max);
+    }
+
+    overall_max = MAX(row_max, overall_max);
+  }
+
+  return overall_max;
+}
+
+float ResManager::GetBpp(LayerBufferFormat format) {
+  switch (format) {
+    case kFormatARGB8888:
+    case kFormatRGBA8888:
+    case kFormatBGRA8888:
+    case kFormatXRGB8888:
+    case kFormatRGBX8888:
+    case kFormatBGRX8888:
+      return 4.0f;
+    case kFormatRGB888:
+      return 3.0f;
+    case kFormatRGB565:
+    case kFormatYCbCr422Packed:
+      return 2.0f;
+    case kFormatYCbCr420Planar:
+    case kFormatYCrCb420Planar:
+    case kFormatYCbCr420SemiPlanar:
+    case kFormatYCrCb420SemiPlanar:
+    case kFormatYCbCr420SemiPlanarVenus:
+      return 1.5f;
+    default:
+      DLOGE("GetBpp: Invalid buffer format: %x", format);
+      return 0.0f;
+  }
+}
+
 void ResManager::PostCommit(Handle display_ctx, HWLayers *hw_layers) {
   DisplayResourceContext *display_resource_ctx =
                           reinterpret_cast<DisplayResourceContext *>(display_ctx);
diff --git a/displayengine/libs/core/res_manager.h b/displayengine/libs/core/res_manager.h
index c4d425b..ab1c9c7 100644
--- a/displayengine/libs/core/res_manager.h
+++ b/displayengine/libs/core/res_manager.h
@@ -131,6 +131,12 @@
                           const LayerRect &scissor, const LayerTransform &transform);
   bool IsNonIntegralSrcCrop(const LayerRect &crop);
   void IntegerizeRect(LayerRect *dst_rect, const LayerRect &src_rect);
+  bool CheckBandwidth(DisplayResourceContext *display_ctx, HWLayers *hw_layers);
+  float GetPipeBw(DisplayResourceContext *display_ctx, HWPipeInfo *pipe, float bpp);
+  float GetClockForPipe(DisplayResourceContext *display_ctx, HWPipeInfo *pipe);
+  float GetOverlapBw(HWLayers *hw_layers, float *pipe_bw, bool left_mixer);
+  void SetDecimationFactor(HWPipeInfo *pipe);
+  float GetBpp(LayerBufferFormat format);
 
   template <class T>
   inline void Swap(T &a, T &b) {
@@ -148,6 +154,9 @@
   SourcePipe *rgb_pipes_;
   SourcePipe *dma_pipes_;
   bool frame_start_;
+  float bw_claimed_;  // Bandwidth claimed by other display
+  float clk_claimed_;  // Clock claimed by other display
+  float last_primary_bw_;
 };
 
 }  // namespace sde