sde: resource manager: rotator support

Use rotator when 90 rotation or downscaling is needed.
Configure rotator input/output ROI, acquire source pipe
and writeback block for the rotator.

Change-Id: I4b8348714eade9a57e553f0f23e6b0b62dd32bad
diff --git a/displayengine/libs/core/hw_interface.h b/displayengine/libs/core/hw_interface.h
index bbedc65..99d392d 100644
--- a/displayengine/libs/core/hw_interface.h
+++ b/displayengine/libs/core/hw_interface.h
@@ -92,6 +92,20 @@
       has_non_scalar_rgb(false), is_src_split(false), always_src_split(false) { }
 };
 
+struct HWRotateInfo {
+  uint32_t pipe_id;
+  LayerRect src_roi;
+  LayerRect dst_roi;
+  HWBlockType writeback_id;
+  float downscale_ratio_x;
+  float downscale_ratio_y;
+
+  HWRotateInfo() : pipe_id(0), writeback_id(kHWWriteback0), downscale_ratio_x(1.0f),
+      downscale_ratio_y(1.0f) { }
+
+  inline void Reset() { *this = HWRotateInfo(); }
+};
+
 struct HWPipeInfo {
   uint32_t pipe_id;
   LayerRect src_roi;
@@ -108,6 +122,8 @@
   bool is_right_pipe;  // indicate if right pipe is valid
   HWPipeInfo left_pipe;  // pipe for left side of the buffer
   HWPipeInfo right_pipe;  // pipe for right side of the buffer
+  HWRotateInfo left_rotate;  // rotation for left side of the buffer
+  HWRotateInfo right_rotate;  // rotation for right side of the buffer
 
   HWLayerConfig() : use_non_dma_pipe(false), is_right_pipe(false) { }
 };
diff --git a/displayengine/libs/core/res_config.cpp b/displayengine/libs/core/res_config.cpp
index fdf4177..974897c 100644
--- a/displayengine/libs/core/res_config.cpp
+++ b/displayengine/libs/core/res_config.cpp
@@ -32,16 +32,37 @@
 
 namespace sde {
 
+void ResManager::RotationConfig(const LayerTransform &transform, LayerRect *src_rect,
+                              HWRotateInfo *left_rotate, HWRotateInfo *right_rotate,
+                              uint32_t *rotate_count) {
+  float src_width = src_rect->right - src_rect->left;
+  float src_height = src_rect->bottom - src_rect->top;
+  LayerRect dst_rect;
+  // Rotate output is a temp buffer, always output to the top left corner for saving memory
+  dst_rect.top = 0.0f;
+  dst_rect.left = 0.0f;
+  // downscale when doing rotation
+  dst_rect.right = src_height / left_rotate->downscale_ratio_x;
+  dst_rect.bottom = src_width / left_rotate->downscale_ratio_y;
+
+  left_rotate->src_roi = *src_rect;
+  left_rotate->pipe_id = kPipeIdNeedsAssignment;
+  left_rotate->dst_roi = dst_rect;
+  // Always use one rotator for now
+  right_rotate->Reset();
+
+  *src_rect = dst_rect;
+  (*rotate_count)++;
+}
+
 DisplayError ResManager::SrcSplitConfig(DisplayResourceContext *display_resource_ctx,
-                                        const Layer &layer, const LayerRect &src_rect,
+                                        const LayerTransform &transform, const LayerRect &src_rect,
                                         const LayerRect &dst_rect, HWLayerConfig *layer_config) {
   HWDisplayAttributes &display_attributes = display_resource_ctx->display_attributes;
   HWPipeInfo *left_pipe = &layer_config->left_pipe;
   HWPipeInfo *right_pipe = &layer_config->right_pipe;
   layer_config->is_right_pipe = false;
 
-  LayerTransform transform = layer.transform;
-  transform.rotation = 0.0f;
   if ((src_rect.right - src_rect.left) >= kMaxSourcePipeWidth ||
       (dst_rect.right - dst_rect.left) >= kMaxInterfaceWidth || hw_res_info_.always_src_split) {
     SplitRect(transform.flip_horizontal, src_rect, dst_rect, &left_pipe->src_roi,
@@ -59,8 +80,8 @@
 }
 
 DisplayError ResManager::DisplaySplitConfig(DisplayResourceContext *display_resource_ctx,
-                                            const Layer &layer, const LayerRect &src_rect,
-                                            const LayerRect &dst_rect,
+                                            const LayerTransform &transform,
+                                            const LayerRect &src_rect, const LayerRect &dst_rect,
                                             HWLayerConfig *layer_config) {
   HWDisplayAttributes &display_attributes = display_resource_ctx->display_attributes;
   // for display split case
@@ -75,8 +96,8 @@
   dst_left = dst_rect;
   crop_right = crop_left;
   dst_right = dst_left;
-  LayerTransform transform = layer.transform;
   CalculateCropRects(scissor, transform, &crop_left, &dst_left);
+
   scissor.left = FLOAT(display_attributes.split_left);
   scissor.top = 0.0f;
   scissor.right = FLOAT(display_attributes.x_pixels);
@@ -159,11 +180,32 @@
     if (ValidateScaling(layer, src_rect, dst_rect, &rot_scale_x, &rot_scale_y))
       return kErrorNotSupported;
 
+    HWRotateInfo *left_rotate, *right_rotate;
+    // config rotator first
+    left_rotate = &hw_layers->config[i].left_rotate;
+    right_rotate = &hw_layers->config[i].right_rotate;
+
+    LayerTransform transform = layer.transform;
+    if (IsRotationNeeded(transform.rotation) ||
+        UINT32(rot_scale_x) != 1 || UINT32(rot_scale_y) != 1) {
+      left_rotate->downscale_ratio_x = rot_scale_x;
+      right_rotate->downscale_ratio_x = rot_scale_x;
+      left_rotate->downscale_ratio_y = rot_scale_y;
+      right_rotate->downscale_ratio_y = rot_scale_y;
+
+      RotationConfig(layer.transform, &src_rect, left_rotate, right_rotate, rotate_count);
+      // rotator will take care of flipping, reset tranform
+      transform = LayerTransform();
+    } else {
+      left_rotate->Reset();
+      right_rotate->Reset();
+    }
+
     if (hw_res_info_.is_src_split) {
-      error = SrcSplitConfig(display_resource_ctx, layer, src_rect,
+      error = SrcSplitConfig(display_resource_ctx, transform, src_rect,
                              dst_rect, &hw_layers->config[i]);
     } else {
-      error = DisplaySplitConfig(display_resource_ctx, layer, src_rect,
+      error = DisplaySplitConfig(display_resource_ctx, transform, src_rect,
                                  dst_rect, &hw_layers->config[i]);
     }
 
@@ -190,7 +232,7 @@
 DisplayError ResManager::ValidateScaling(const Layer &layer, const LayerRect &crop,
                                          const LayerRect &dst, float *rot_scale_x,
                                          float *rot_scale_y) {
-  bool rotated90 = (UINT32(layer.transform.rotation) == 90);
+  bool rotated90 = IsRotationNeeded(layer.transform.rotation);
   float crop_width = rotated90 ? crop.bottom - crop.top : crop.right - crop.left;
   float crop_height = rotated90 ? crop.right - crop.left : crop.bottom - crop.top;
   float dst_width = dst.right - dst.left;
@@ -212,12 +254,23 @@
 
   if ((UINT32(scale_x) > 1) || (UINT32(scale_y) > 1)) {
     const uint32_t max_scale_down = hw_res_info_.max_scale_down;
+    uint32_t max_downscale_with_rotator;
+
+    if (hw_res_info_.has_rotator_downscale)
+      max_downscale_with_rotator = max_scale_down * kMaxRotateDownScaleRatio;
+    else
+      max_downscale_with_rotator = max_scale_down;
+
     if (((!hw_res_info_.has_decimation) || (IsMacroTileFormat(layer.input_buffer))) &&
         (scale_x > max_scale_down || scale_y > max_scale_down)) {
       DLOGV_IF(kTagResources,
                "Scaling down is over the limit is_tile = %d, scale_x = %d, scale_y = %d",
                IsMacroTileFormat(layer.input_buffer), scale_x, scale_y);
       return kErrorNotSupported;
+    } else if (scale_x > max_downscale_with_rotator || scale_y > max_downscale_with_rotator) {
+      DLOGV_IF(kTagResources, "Scaling down is over the limit scale_x = %d, scale_y = %d",
+               scale_x, scale_y);
+      return kErrorNotSupported;
     }
   }
 
@@ -266,7 +319,7 @@
     Swap(*top_cut_ratio, *bottom_cut_ratio);
   }
 
-  if (UINT32(transform.rotation) == 90) {
+  if (IsRotationNeeded(transform.rotation)) {
     // Anti clock swapping
     float tmp_cut_ratio = *left_cut_ratio;
     *left_cut_ratio = *top_cut_ratio;
diff --git a/displayengine/libs/core/res_manager.cpp b/displayengine/libs/core/res_manager.cpp
index 9579128..38ef319 100644
--- a/displayengine/libs/core/res_manager.cpp
+++ b/displayengine/libs/core/res_manager.cpp
@@ -79,6 +79,20 @@
   DLOGI("hw_rev=%x, DMA=%d RGB=%d VIG=%d", hw_res_info_.hw_revision, hw_res_info_.num_dma_pipe,
     hw_res_info_.num_rgb_pipe, hw_res_info_.num_vig_pipe);
 
+  if (hw_res_info_.num_rotator > kMaxNumRotator) {
+    DLOGE("Number of rotator is over the limit! %d", hw_res_info_.num_rotator);
+    return kErrorParameters;
+  }
+
+  if (hw_res_info_.num_rotator > 0) {
+    rotators_[0].pipe_index = dma_pipes_[0].index;
+    rotators_[0].writeback_id = kHWWriteback0;
+  }
+  if (hw_res_info_.num_rotator > 1) {
+    rotators_[1].pipe_index = dma_pipes_[1].index;
+    rotators_[1].writeback_id = kHWWriteback1;
+  }
+
   // Used by splash screen
   rgb_pipes_[0].state = kPipeStateOwnedByKernel;
   rgb_pipes_[1].state = kPipeStateOwnedByKernel;
@@ -153,6 +167,9 @@
   } else {
     hw_block_ctx_[display_resource_ctx->hw_block_id].is_in_use = false;
   }
+  if (!hw_block_ctx_[display_resource_ctx->hw_block_id].is_in_use)
+    ClearDedicated(display_resource_ctx->hw_block_id);
+
   delete display_resource_ctx;
 
   return kErrorNone;
@@ -181,6 +198,19 @@
     }
   }
 
+  // Clear rotator usage
+  for (uint32_t i = 0; i < hw_res_info_.num_rotator; i++) {
+    uint32_t pipe_index;
+    pipe_index = rotators_[i].pipe_index;
+    if (rotators_[i].client_bit_mask == 0 &&
+        src_pipes_[pipe_index].state == kPipeStateToRelease &&
+        src_pipes_[pipe_index].hw_block_id == rotators_[i].writeback_id) {
+      src_pipes_[pipe_index].dedicated_hw_block = kHWBlockMax;
+      src_pipes_[pipe_index].state = kPipeStateIdle;
+    }
+    CLEAR_BIT(rotators_[i].client_bit_mask, display_resource_ctx->hw_block_id);
+  }
+
   return kErrorNone;
 }
 
@@ -221,6 +251,12 @@
       src_pipes_[i].reserved_hw_block = kHWBlockMax;
   }
 
+  // allocate rotator
+  error = AcquireRotator(display_resource_ctx, rotate_count);
+  if (error != kErrorNone)
+    return error;
+
+  rotate_count = 0;
   for (uint32_t i = 0; i < layer_info.count; i++) {
     Layer &layer = layer_info.stack->layers[layer_info.index[i]];
     bool use_non_dma_pipe = hw_layers->config[i].use_non_dma_pipe;
@@ -230,6 +266,9 @@
       use_non_dma_pipe = true;
     }
 
+    AssignRotator(&hw_layers->config[i].left_rotate, &rotate_count);
+    AssignRotator(&hw_layers->config[i].right_rotate, &rotate_count);
+
     HWPipeInfo *pipe_info = &hw_layers->config[i].left_pipe;
 
     // Should have a generic macro
@@ -539,6 +578,20 @@
       }
     }
   }
+  // set rotator pipes
+  for (uint32_t i = 0; i < hw_res_info_.num_rotator; i++) {
+    uint32_t pipe_index;
+    pipe_index = rotators_[i].pipe_index;
+    if (IS_BIT_SET(rotators_[i].client_bit_mask, hw_block_id)) {
+      src_pipes_[pipe_index].hw_block_id = rotators_[i].writeback_id;
+      src_pipes_[pipe_index].state = kPipeStateAcquired;
+    } else if (!rotators_[i].client_bit_mask) {
+      src_pipes_[pipe_index].dedicated_hw_block = kHWBlockMax;
+      if (src_pipes_[pipe_index].hw_block_id == rotators_[i].writeback_id &&
+          src_pipes_[pipe_index].state == kPipeStateAcquired)
+        src_pipes_[pipe_index].state = kPipeStateToRelease;
+    }
+  }
   display_resource_ctx->frame_start = false;
 }
 
@@ -553,6 +606,7 @@
     if (src_pipes_[i].hw_block_id == hw_block_id)
       src_pipes_[i].ResetState();
   }
+  ClearRotator(display_resource_ctx);
 }
 
 uint32_t ResManager::GetMdssPipeId(PipeType type, uint32_t index) {
@@ -714,5 +768,175 @@
   }
 }
 
+// This is to reserve resources for the device or rotator
+DisplayError ResManager::MarkDedicated(DisplayResourceContext *display_resource_ctx,
+                                       struct HWRotator *rotator) {
+  uint32_t num_base_pipe = 0, base_pipe_index = 0, num_vig_pipe = 0, i, vig_pipe_index = 0;
+  bool force = false;
+  SourcePipe *src_pipe = NULL;
+  HWBlockType hw_block_id;
+  if (rotator == NULL) {
+    hw_block_id = display_resource_ctx->hw_block_id;
+    switch (display_resource_ctx->display_type) {
+    case kPrimary:
+      src_pipe= rgb_pipes_;
+      if (display_resource_ctx->display_attributes.is_device_split ||
+          hw_res_info_.is_src_split) {
+        num_base_pipe = 2;
+        num_vig_pipe = 2;
+      } else {
+        num_base_pipe = 1;
+        num_vig_pipe = 1;
+      }
+      src_pipe = rgb_pipes_;
+      force = true;
+      break;
+    // HDMI and Virtual are using the same strategy
+    case kHDMI:
+    case kVirtual:
+      if (display_resource_ctx && display_resource_ctx->display_attributes.is_device_split) {
+        num_base_pipe = 2;
+        num_vig_pipe = 2;
+      } else {
+        num_base_pipe = 1;
+        num_vig_pipe = 1;
+      }
+      break;
+    default:
+      DLOGE("Wrong device type %d", display_resource_ctx->display_type);
+      break;
+    }
+  } else {
+    hw_block_id = rotator->writeback_id;
+    num_base_pipe = 1;
+    base_pipe_index = rotator->pipe_index;
+    src_pipe = &src_pipes_[base_pipe_index];
+    force = true;
+  }
+
+  if (!num_base_pipe) {
+    DLOGE("Cannot reserve dedicated pipe %d", hw_block_id);
+    return kErrorResources;
+  }
+
+  // Only search the assigned pipe type
+  if (force) {
+    for (i = 0; i < num_base_pipe; i++) {
+      if (src_pipe->dedicated_hw_block != kHWBlockMax)
+        DLOGV_IF(kTagResources, "Overwrite dedicated block %d", src_pipe->dedicated_hw_block);
+      src_pipe->dedicated_hw_block = hw_block_id;
+      src_pipe++;
+    }
+    num_base_pipe = 0;
+  } else {
+    // search available pipes
+    src_pipe = rgb_pipes_;
+    uint32_t num_pipe = hw_res_info_.num_rgb_pipe + hw_res_info_.num_vig_pipe;
+    for (i = 0; i < num_pipe; i++) {
+      if (src_pipe->dedicated_hw_block == kHWBlockMax ||
+          src_pipe->dedicated_hw_block == hw_block_id) {
+        src_pipe->dedicated_hw_block = hw_block_id;
+        num_base_pipe--;
+        if (!num_base_pipe)
+          break;
+      }
+      src_pipe++;
+    }
+    if (num_base_pipe) {
+      for (i = 0; i < num_pipe; i++) {
+        if (src_pipe->dedicated_hw_block == hw_block_id)
+          src_pipe->dedicated_hw_block = kHWBlockMax;
+        src_pipe++;
+      }
+      DLOGE("Cannot reserve dedicated pipe %d", hw_block_id);
+      return kErrorResources;
+    }
+  }
+  // optional for vig pipes
+  src_pipe= vig_pipes_;
+  for (i = 0; i < num_vig_pipe; i++) {
+    if (src_pipe->dedicated_hw_block == kHWBlockMax ||
+        src_pipe->dedicated_hw_block == hw_block_id) {
+      src_pipe->dedicated_hw_block = hw_block_id;
+      num_vig_pipe--;
+      if (!num_vig_pipe)
+        break;
+    }
+    src_pipe++;
+  }
+  return kErrorNone;
+}
+
+void ResManager::ClearDedicated(HWBlockType hw_block_id) {
+  SourcePipe *src_pipe = src_pipes_;
+  for (uint32_t i = 0; i < num_pipe_; i++) {
+    if (src_pipe->dedicated_hw_block == hw_block_id)
+      src_pipe->dedicated_hw_block = kHWBlockMax;
+  }
+}
+
+DisplayError ResManager::AcquireRotator(DisplayResourceContext *display_resource_ctx,
+                                        const uint32_t rotate_count) {
+  if (rotate_count == 0)
+    return kErrorNone;
+  if (hw_res_info_.num_rotator == 0)
+    return kErrorResources;
+
+  uint32_t i, j, pipe_index, num_rotator;
+  if (rotate_count > hw_res_info_.num_rotator)
+    num_rotator = hw_res_info_.num_rotator;
+  else
+    num_rotator = rotate_count;
+
+  for (i = 0; i < num_rotator; i++) {
+    uint32_t rotate_pipe_index = rotators_[i].pipe_index;
+    MarkDedicated(display_resource_ctx, &rotators_[i]);
+    if (src_pipes_[rotate_pipe_index].reserved_hw_block != kHWBlockMax) {
+      DLOGV_IF(kTagResources, "pipe %x is reserved by block:%d",
+               src_pipes_[rotate_pipe_index].mdss_pipe_id,
+               src_pipes_[rotate_pipe_index].reserved_hw_block);
+      return kErrorResources;
+    }
+    pipe_index = SearchPipe(rotators_[i].writeback_id, &src_pipes_[rotate_pipe_index], 1, false);
+    if (pipe_index >= num_pipe_) {
+      DLOGV_IF(kTagResources, "pipe %x is not ready for rotator",
+               src_pipes_[rotate_pipe_index].mdss_pipe_id);
+      return kErrorResources;
+    }
+  }
+
+  for (i = 0; i < num_rotator; i++)
+    SET_BIT(rotators_[i].client_bit_mask, display_resource_ctx->hw_block_id);
+
+  return kErrorNone;
+}
+
+void ResManager::AssignRotator(HWRotateInfo *rotate, uint32_t *rotate_count) {
+  if (!rotate->pipe_id)
+    return;
+  // Interleave rotator assignment
+  if ((*rotate_count & 0x1) && (hw_res_info_.num_rotator > 1)) {
+    rotate->pipe_id = src_pipes_[rotators_[1].pipe_index].mdss_pipe_id;
+    rotate->writeback_id = rotators_[1].writeback_id;
+  } else {
+    rotate->pipe_id = src_pipes_[rotators_[0].pipe_index].mdss_pipe_id;
+    rotate->writeback_id = rotators_[0].writeback_id;
+  }
+  (*rotate_count)++;
+}
+
+void ResManager::ClearRotator(DisplayResourceContext *display_resource_ctx) {
+  for (uint32_t i = 0; i < hw_res_info_.num_rotator; i++) {
+    uint32_t pipe_index;
+    pipe_index = rotators_[i].pipe_index;
+    CLEAR_BIT(rotators_[i].client_bit_mask, display_resource_ctx->hw_block_id);
+    if (rotators_[i].client_bit_mask == 0 &&
+        src_pipes_[pipe_index].dedicated_hw_block == rotators_[i].writeback_id) {
+      src_pipes_[pipe_index].dedicated_hw_block = kHWBlockMax;
+      src_pipes_[pipe_index].state = kPipeStateIdle;
+    }
+  }
+}
+
 }  // namespace sde
 
diff --git a/displayengine/libs/core/res_manager.h b/displayengine/libs/core/res_manager.h
index 52d0464..18900f3 100644
--- a/displayengine/libs/core/res_manager.h
+++ b/displayengine/libs/core/res_manager.h
@@ -146,12 +146,12 @@
   DisplayError Config(DisplayResourceContext *display_resource_ctx, HWLayers *hw_layers,
                       uint32_t *rotate_count);
   DisplayError DisplaySplitConfig(DisplayResourceContext *display_resource_ctx,
-                                  const Layer &layer, const LayerRect &src_rect,
+                                  const LayerTransform &transform, const LayerRect &src_rect,
                                   const LayerRect &dst_rect, HWLayerConfig *layer_config);
   DisplayError ValidateScaling(const Layer &layer, const LayerRect &crop,
                                const LayerRect &dst, float *rot_scale_x, float *rot_scale_y);
   DisplayError SrcSplitConfig(DisplayResourceContext *display_resource_ctx,
-                              const Layer &layer, const LayerRect &src_rect,
+                              const LayerTransform &transform, const LayerRect &src_rect,
                               const LayerRect &dst_rect, HWLayerConfig *layer_config);
   void CalculateCut(const LayerTransform &transform, float *left_cut_ratio, float *top_cut_ratio,
                     float *right_cut_ratio, float *bottom_cut_ratio);
@@ -169,7 +169,18 @@
                  LayerRect *dst_right);
   bool IsMacroTileFormat(const LayerBuffer *buffer) { return buffer->flags.macro_tile; }
   bool IsYuvFormat(LayerBufferFormat format) { return (format >= kFormatYCbCr420Planar); }
+  bool IsRotationNeeded(float rotation)
+         { return (UINT32(rotation) == 90 || UINT32(rotation) == 270); }
   void LogRectVerbose(const char *prefix, const LayerRect &roi);
+  DisplayError MarkDedicated(DisplayResourceContext *display_resource_ctx,
+                             struct HWRotator *rotator);
+  void ClearDedicated(HWBlockType hw_block_id);
+  void RotationConfig(const LayerTransform &transform, LayerRect *src_rect,
+                      HWRotateInfo *left_rotate, HWRotateInfo *right_rotate, uint32_t *rotate_cnt);
+  DisplayError AcquireRotator(DisplayResourceContext *display_resource_ctx,
+                              const uint32_t roate_cnt);
+  void AssignRotator(HWRotateInfo *rotate, uint32_t *rotate_cnt);
+  void ClearRotator(DisplayResourceContext *display_resource_ctx);
 
   template <class T>
   inline void Swap(T &a, T &b) {
@@ -191,6 +202,7 @@
   float clk_claimed_;  // Clock claimed by other display
   float last_primary_bw_;
   uint32_t virtual_count_;
+  struct HWRotator rotators_[kMaxNumRotator];
 };
 
 }  // namespace sde