sde: resource manager: rotator resource allocation

Only mark dedicate block for rotator allocation. Use
rotate array other than left_rotate and right_rotate.
Use bit mask to indicate rotation usage because rotation
can be shared by multi displays.

Change-Id: I70bd03250080a24dcf7f38c39dbd6391c0bba97c
diff --git a/displayengine/include/utils/constants.h b/displayengine/include/utils/constants.h
index a0f2bfb..a7b9f0f 100644
--- a/displayengine/include/utils/constants.h
+++ b/displayengine/include/utils/constants.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
@@ -52,6 +52,7 @@
 namespace sde {
 
   const int kThreadPriorityUrgent = -9;
+  const int kMaxRotatePerLayer = 2;
 
   typedef void * Handle;
 
diff --git a/displayengine/libs/core/hw_interface.h b/displayengine/libs/core/hw_interface.h
index 99d392d..7309c32 100644
--- a/displayengine/libs/core/hw_interface.h
+++ b/displayengine/libs/core/hw_interface.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
@@ -122,10 +122,10 @@
   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
+  uint32_t num_rotate;  // number of rotate
+  HWRotateInfo rotates[kMaxRotatePerLayer];  // rotation for the buffer
 
-  HWLayerConfig() : use_non_dma_pipe(false), is_right_pipe(false) { }
+  HWLayerConfig() : use_non_dma_pipe(false), is_right_pipe(false), num_rotate(0) { }
 };
 
 struct HWLayers {
diff --git a/displayengine/libs/core/res_config.cpp b/displayengine/libs/core/res_config.cpp
index 974897c..d093ab2 100644
--- a/displayengine/libs/core/res_config.cpp
+++ b/displayengine/libs/core/res_config.cpp
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
@@ -32,26 +32,30 @@
 
 namespace sde {
 
-void ResManager::RotationConfig(const LayerTransform &transform, LayerRect *src_rect,
-                              HWRotateInfo *left_rotate, HWRotateInfo *right_rotate,
-                              uint32_t *rotate_count) {
+void ResManager::RotationConfig(const LayerTransform &transform, const float &scale_x,
+                                const float &scale_y, LayerRect *src_rect,
+                                struct HWLayerConfig *layer_config, uint32_t *rotate_count) {
+  HWRotateInfo *rotate = &layer_config->rotates[0];
   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();
+  rotate->downscale_ratio_x = scale_x;
+  rotate->downscale_ratio_y = scale_y;
+
+  // downscale when doing rotation
+  dst_rect.right = src_height / rotate->downscale_ratio_x;
+  dst_rect.bottom = src_width / rotate->downscale_ratio_y;
+
+  rotate->src_roi = *src_rect;
+  rotate->pipe_id = kPipeIdNeedsAssignment;
+  rotate->dst_roi = dst_rect;
 
   *src_rect = dst_rect;
+  layer_config->num_rotate = 1;
   (*rotate_count)++;
 }
 
@@ -180,49 +184,44 @@
     if (ValidateScaling(layer, src_rect, dst_rect, &rot_scale_x, &rot_scale_y))
       return kErrorNotSupported;
 
-    HWRotateInfo *left_rotate, *right_rotate;
+    struct HWLayerConfig *layer_config = &hw_layers->config[i];
     // config rotator first
-    left_rotate = &hw_layers->config[i].left_rotate;
-    right_rotate = &hw_layers->config[i].right_rotate;
+    for (uint32_t j = 0; j < kMaxRotatePerLayer; j++) {
+      layer_config->rotates[j].Reset();
+    }
+    layer_config->num_rotate = 0;
 
     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);
+      RotationConfig(layer.transform, rot_scale_x, rot_scale_y, &src_rect, layer_config,
+                     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, transform, src_rect,
-                             dst_rect, &hw_layers->config[i]);
+                             dst_rect, layer_config);
     } else {
       error = DisplaySplitConfig(display_resource_ctx, transform, src_rect,
-                                 dst_rect, &hw_layers->config[i]);
+                                 dst_rect, layer_config);
     }
 
     if (error != kErrorNone)
       break;
 
     DLOGV_IF(kTagResources, "layer = %d, left pipe_id = %x",
-             i, hw_layers->config[i].left_pipe.pipe_id);
+             i, layer_config->left_pipe.pipe_id);
     LogRectVerbose("input layer src_rect", layer.src_rect);
     LogRectVerbose("input layer dst_rect", layer.dst_rect);
     LogRectVerbose("cropped src_rect", src_rect);
     LogRectVerbose("cropped dst_rect", dst_rect);
-    LogRectVerbose("left pipe src", hw_layers->config[i].left_pipe.src_roi);
-    LogRectVerbose("left pipe dst", hw_layers->config[i].left_pipe.dst_roi);
+    LogRectVerbose("left pipe src", layer_config->left_pipe.src_roi);
+    LogRectVerbose("left pipe dst", layer_config->left_pipe.dst_roi);
     if (hw_layers->config[i].right_pipe.pipe_id) {
-      LogRectVerbose("right pipe src", hw_layers->config[i].right_pipe.src_roi);
-      LogRectVerbose("right pipe dst", hw_layers->config[i].right_pipe.dst_roi);
+      LogRectVerbose("right pipe src", layer_config->right_pipe.src_roi);
+      LogRectVerbose("right pipe dst", layer_config->right_pipe.dst_roi);
     }
   }
 
@@ -473,7 +472,7 @@
 }
 
 void ResManager::LogRectVerbose(const char *prefix, const LayerRect &roi) {
-  DLOGV_IF(kTagResources,"%s: left = %.0f, top = %.0f, right = %.0f, bottom = %.0f",
+  DLOGV_IF(kTagResources, "%s: left = %.0f, top = %.0f, right = %.0f, bottom = %.0f",
            prefix, roi.left, roi.top, roi.right, roi.bottom);
 }
 
diff --git a/displayengine/libs/core/res_manager.cpp b/displayengine/libs/core/res_manager.cpp
index 38ef319..7b36563 100644
--- a/displayengine/libs/core/res_manager.cpp
+++ b/displayengine/libs/core/res_manager.cpp
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
@@ -40,7 +40,7 @@
 
   DisplayError error = kErrorNone;
 
-  // TODO: Remove this. Disable src_split as kernel not supported yet
+  // TODO(user): Remove this. Disable src_split as kernel not supported yet
   hw_res_info_.is_src_split = false;
   num_pipe_ = hw_res_info_.num_vig_pipe + hw_res_info_.num_rgb_pipe + hw_res_info_.num_dma_pipe;
 
@@ -123,7 +123,7 @@
     break;
 
   case kVirtual:
-    // assume only WB2 can be used for virtual display
+    // TODO(user): read this block id from kernel
     virtual_count_++;
     hw_block_id = kHWWriteback2;
     break;
@@ -159,7 +159,6 @@
   DisplayResourceContext *display_resource_ctx =
                           reinterpret_cast<DisplayResourceContext *>(display_ctx);
 
-  Purge(display_ctx);
   if (display_resource_ctx->hw_block_id == kHWWriteback2) {
     virtual_count_--;
     if (!virtual_count_)
@@ -167,8 +166,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);
+    Purge(display_ctx);
 
   delete display_resource_ctx;
 
@@ -202,13 +202,13 @@
   for (uint32_t i = 0; i < hw_res_info_.num_rotator; i++) {
     uint32_t pipe_index;
     pipe_index = rotators_[i].pipe_index;
+    rotators_[i].ClearState(display_resource_ctx->hw_block_id);
     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;
@@ -259,17 +259,19 @@
   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;
+    struct HWLayerConfig &layer_config = hw_layers->config[i];
+    bool use_non_dma_pipe = layer_config.use_non_dma_pipe;
 
-    // Temp setting, this should be set by comp_manager
+    // TODO(user): set this from comp_manager
     if (hw_block_id == kHWPrimary) {
       use_non_dma_pipe = true;
     }
 
-    AssignRotator(&hw_layers->config[i].left_rotate, &rotate_count);
-    AssignRotator(&hw_layers->config[i].right_rotate, &rotate_count);
+    for (uint32_t j = 0; j < layer_config.num_rotate; j++) {
+      AssignRotator(&layer_config.rotates[j], &rotate_count);
+    }
 
-    HWPipeInfo *pipe_info = &hw_layers->config[i].left_pipe;
+    HWPipeInfo *pipe_info = &layer_config.left_pipe;
 
     // Should have a generic macro
     bool is_yuv = IsYuvFormat(layer.input_buffer->format);
@@ -286,11 +288,11 @@
 
     SetDecimationFactor(pipe_info);
 
-    pipe_info =  &hw_layers->config[i].right_pipe;
+    pipe_info =  &layer_config.right_pipe;
     if (pipe_info->pipe_id == 0) {
       // assign single pipe
       if (left_index < num_pipe_) {
-        hw_layers->config[i].left_pipe.pipe_id = src_pipes_[left_index].mdss_pipe_id;
+        layer_config.left_pipe.pipe_id = src_pipes_[left_index].mdss_pipe_id;
         src_pipes_[left_index].at_right = false;
       }
       continue;
@@ -315,11 +317,11 @@
     src_pipes_[right_index].at_right = true;
     src_pipes_[left_index].reserved_hw_block = hw_block_id;
     src_pipes_[left_index].at_right = false;
-    hw_layers->config[i].left_pipe.pipe_id = src_pipes_[left_index].mdss_pipe_id;
+    layer_config.left_pipe.pipe_id = src_pipes_[left_index].mdss_pipe_id;
     SetDecimationFactor(pipe_info);
 
     DLOGV_IF(kTagResources, "Pipe acquired, layer index = %d, left_pipe = %x, right_pipe = %x",
-            i, hw_layers->config[i].left_pipe.pipe_id,  pipe_info->pipe_id);
+            i, layer_config.left_pipe.pipe_id,  pipe_info->pipe_id);
   }
 
   if (!CheckBandwidth(display_resource_ctx, hw_layers)) {
@@ -580,16 +582,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;
+    uint32_t 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) {
+    } else if (!rotators_[i].client_bit_mask &&
+               src_pipes_[pipe_index].hw_block_id == rotators_[i].writeback_id &&
+               src_pipes_[pipe_index].state == kPipeStateAcquired) {
+      src_pipes_[pipe_index].state = kPipeStateToRelease;
+      src_pipes_[pipe_index].state_frame_count = frame_count;
+    }
+    // If no request on the rotation, release the pipe.
+    if (!rotators_[i].request_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;
@@ -762,116 +768,9 @@
   for (uint32_t i = 0; i < num_pipe_; i++) {
     SourcePipe *src_pipe = &src_pipes_[i];
     AppendString(buffer, length,
-                 "\nindex = %d, id = %x, reserved = %d, state = %d, at_right = %d, dedicated = %d",
+                 "\nindex = %d, id = %x, reserved = %d, state = %d, hw_block = %d, dedicated = %d",
                  src_pipe->index, src_pipe->mdss_pipe_id, src_pipe->reserved_hw_block,
-                 src_pipe->state, src_pipe->at_right, src_pipe->dedicated_hw_block);
-  }
-}
-
-// 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;
+                 src_pipe->state, src_pipe->hw_block_id, src_pipe->dedicated_hw_block);
   }
 }
 
@@ -890,7 +789,16 @@
 
   for (i = 0; i < num_rotator; i++) {
     uint32_t rotate_pipe_index = rotators_[i].pipe_index;
-    MarkDedicated(display_resource_ctx, &rotators_[i]);
+    SourcePipe *src_pipe = &src_pipes_[rotate_pipe_index];
+
+    if (src_pipe->dedicated_hw_block != kHWBlockMax)
+      DLOGV_IF(kTagResources, "Overwrite dedicated block %d", src_pipe->dedicated_hw_block);
+    src_pipe->dedicated_hw_block = rotators_[i].writeback_id;
+    SET_BIT(rotators_[i].request_bit_mask, display_resource_ctx->hw_block_id);
+  }
+
+  for (i = 0; i < num_rotator; i++) {
+    uint32_t rotate_pipe_index = rotators_[i].pipe_index;
     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,
@@ -929,11 +837,14 @@
   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) {
+    rotators_[i].ClearState(display_resource_ctx->hw_block_id);
+    if (rotators_[i].client_bit_mask)
+      continue;
+
+    if (src_pipes_[pipe_index].hw_block_id == rotators_[i].writeback_id) {
+      src_pipes_[pipe_index].ResetState();
+    } else if (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;
     }
   }
 }
diff --git a/displayengine/libs/core/res_manager.h b/displayengine/libs/core/res_manager.h
index 18900f3..5b63299 100644
--- a/displayengine/libs/core/res_manager.h
+++ b/displayengine/libs/core/res_manager.h
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014 - 2015, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
@@ -107,7 +107,6 @@
 
     inline void ResetState() { state = kPipeStateIdle; hw_block_id = kHWBlockMax;
         at_right = false; reserved_hw_block = kHWBlockMax; dedicated_hw_block = kHWBlockMax; }
-
   };
 
   struct DisplayResourceContext {
@@ -131,7 +130,12 @@
     uint32_t pipe_index;
     HWBlockType writeback_id;
     uint32_t client_bit_mask;
-    HWRotator() : pipe_index(0), writeback_id(kHWBlockMax), client_bit_mask(0) { }
+    uint32_t request_bit_mask;
+    HWRotator() : pipe_index(0), writeback_id(kHWBlockMax), client_bit_mask(0),
+                     request_bit_mask(0) { }
+
+    inline void ClearState(HWBlockType block) { CLEAR_BIT(client_bit_mask, block);
+        CLEAR_BIT(request_bit_mask, block); }
   };
 
   static const int kPipeIdNeedsAssignment = -1;
@@ -172,11 +176,9 @@
   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);
+  void RotationConfig(const LayerTransform &transform, const float &scale_x,
+                      const float &scale_y,LayerRect *src_rect,
+                      struct HWLayerConfig *layer_config, uint32_t *rotate_count);
   DisplayError AcquireRotator(DisplayResourceContext *display_resource_ctx,
                               const uint32_t roate_cnt);
   void AssignRotator(HWRotateInfo *rotate, uint32_t *rotate_cnt);