Add the max-tile tool with uniform tile support

This adds the tool CONFIG_MAX_TILE
Uniform tiles are implemented using power of two number of rows or columns.
Maximum number of rows or columns is 64
Explicit tiles will follow in a later commit once the command line API is updated for this

Change-Id: Ia8d22e7f4aa779448211496c644937afdf037228
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index fe1c7c2..eef4aa7 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -305,8 +305,13 @@
 #endif  // CONFIG_EXT_PARTITION
   } else {
 #endif  // CONFIG_EXT_TILE
+#if CONFIG_MAX_TILE
     RANGE_CHECK_HI(extra_cfg, tile_columns, 6);
-    RANGE_CHECK_HI(extra_cfg, tile_rows, 2);
+    RANGE_CHECK_HI(extra_cfg, tile_rows, 6);
+#else   // CONFIG_MAX_TILE
+  RANGE_CHECK_HI(extra_cfg, tile_columns, 6);
+  RANGE_CHECK_HI(extra_cfg, tile_rows, 2);
+#endif  // CONFIG_MAX_TILE
 #if CONFIG_EXT_TILE
   }
 #endif  // CONFIG_EXT_TILE
diff --git a/av1/common/enums.h b/av1/common/enums.h
index 15e7d9c..843e290 100644
--- a/av1/common/enums.h
+++ b/av1/common/enums.h
@@ -67,8 +67,13 @@
 #define MAX_TILE_ROWS 1024
 #define MAX_TILE_COLS 1024
 #else
+#if CONFIG_MAX_TILE
+#define MAX_TILE_ROWS 64
+#define MAX_TILE_COLS 64
+#else
 #define MAX_TILE_ROWS 4
 #define MAX_TILE_COLS 64
+#endif
 #endif  // CONFIG_EXT_TILE
 
 #if CONFIG_VAR_TX
diff --git a/av1/common/onyxc_int.h b/av1/common/onyxc_int.h
index 1a792fb..e538502 100644
--- a/av1/common/onyxc_int.h
+++ b/av1/common/onyxc_int.h
@@ -415,10 +415,27 @@
 
   int error_resilient_mode;
 
-  int log2_tile_cols, log2_tile_rows;  // Used in non-large_scale_tile_coding.
   int tile_cols, tile_rows;
   int last_tile_cols, last_tile_rows;
-  int tile_width, tile_height;  // In MI units
+
+#if CONFIG_MAX_TILE
+  int min_log2_tile_cols;
+  int max_log2_tile_cols;
+  int max_log2_tile_rows;
+  int min_log2_tile_rows;
+  int min_log2_tiles;
+  int max_tile_width_sb;
+  int max_tile_height_sb;
+  int uniform_tile_spacing_flag;
+  int log2_tile_cols;                        // only valid for uniform tiles
+  int log2_tile_rows;                        // only valid for uniform tiles
+  int tile_col_start_sb[MAX_TILE_COLS + 1];  // valid for 0 <= i <= tile_cols
+  int tile_row_start_sb[MAX_TILE_ROWS + 1];  // valid for 0 <= i <= tile_rows
+#else
+  int log2_tile_cols, log2_tile_rows;  // Used in non-large_scale_tile_coding.
+  int tile_width, tile_height;         // In MI units
+#endif  // CONFIG_MAX_TILE
+
 #if CONFIG_EXT_TILE
   unsigned int large_scale_tile;
   unsigned int single_tile_decoding;
diff --git a/av1/common/tile_common.c b/av1/common/tile_common.c
index 75e7d6a..f73c39c 100644
--- a/av1/common/tile_common.c
+++ b/av1/common/tile_common.c
@@ -13,16 +13,6 @@
 #include "av1/common/onyxc_int.h"
 #include "aom_dsp/aom_dsp_common.h"
 
-void av1_tile_set_row(TileInfo *tile, const AV1_COMMON *cm, int row) {
-  tile->mi_row_start = row * cm->tile_height;
-  tile->mi_row_end = AOMMIN(tile->mi_row_start + cm->tile_height, cm->mi_rows);
-}
-
-void av1_tile_set_col(TileInfo *tile, const AV1_COMMON *cm, int col) {
-  tile->mi_col_start = col * cm->tile_width;
-  tile->mi_col_end = AOMMIN(tile->mi_col_start + cm->tile_width, cm->mi_cols);
-}
-
 #if CONFIG_DEPENDENT_HORZTILES
 void av1_tile_set_tg_boundary(TileInfo *tile, const AV1_COMMON *const cm,
                               int row, int col) {
@@ -46,6 +36,112 @@
 #endif
 }
 
+#if CONFIG_MAX_TILE
+
+// Find smallest k>=0 such that (blk_size << k) >= target
+static int tile_log2(int blk_size, int target) {
+  int k;
+  for (k = 0; (blk_size << k) < target; k++) {
+  }
+  return k;
+}
+
+void av1_get_tile_limits(AV1_COMMON *const cm) {
+  int mi_cols = ALIGN_POWER_OF_TWO(cm->mi_cols, MAX_MIB_SIZE_LOG2);
+  int mi_rows = ALIGN_POWER_OF_TWO(cm->mi_rows, MAX_MIB_SIZE_LOG2);
+  int sb_cols = mi_cols >> MAX_MIB_SIZE_LOG2;
+  int sb_rows = mi_rows >> MAX_MIB_SIZE_LOG2;
+
+  cm->min_log2_tile_cols = tile_log2(MAX_TILE_WIDTH_SB, sb_cols);
+  cm->max_log2_tile_cols = tile_log2(1, sb_cols);
+  cm->max_log2_tile_rows = tile_log2(1, sb_rows);
+  cm->min_log2_tiles = tile_log2(MAX_TILE_AREA_SB, sb_cols * sb_rows);
+  cm->min_log2_tiles = AOMMAX(cm->min_log2_tiles, cm->min_log2_tile_cols);
+  // TODO(dominic.symes@arm.com):
+  // Add in levelMinLog2Tiles as a lower limit when levels are defined
+}
+
+void av1_calculate_tile_cols(AV1_COMMON *const cm) {
+  int mi_cols = ALIGN_POWER_OF_TWO(cm->mi_cols, MAX_MIB_SIZE_LOG2);
+  int mi_rows = ALIGN_POWER_OF_TWO(cm->mi_rows, MAX_MIB_SIZE_LOG2);
+  int sb_cols = mi_cols >> MAX_MIB_SIZE_LOG2;
+  int sb_rows = mi_rows >> MAX_MIB_SIZE_LOG2;
+  int i;
+
+  if (cm->uniform_tile_spacing_flag) {
+    int start_sb;
+    int size_sb = ALIGN_POWER_OF_TWO(sb_cols, cm->log2_tile_cols);
+    size_sb >>= cm->log2_tile_cols;
+    assert(size_sb > 0);
+    for (i = 0, start_sb = 0; start_sb < sb_cols; i++) {
+      cm->tile_col_start_sb[i] = start_sb;
+      start_sb += size_sb;
+    }
+    cm->tile_cols = i;
+    cm->tile_col_start_sb[i] = start_sb;
+    cm->min_log2_tile_rows = AOMMAX(cm->min_log2_tiles - cm->log2_tile_cols, 0);
+  } else {
+    int max_tile_area_sb = (sb_rows * sb_cols);
+    int max_tile_width_sb = 0;
+    for (i = 0; i < cm->tile_cols; i++) {
+      int size_sb = cm->tile_col_start_sb[i + 1] - cm->tile_col_start_sb[i];
+      max_tile_width_sb = AOMMAX(max_tile_width_sb, size_sb);
+    }
+    if (cm->min_log2_tiles) {
+      max_tile_area_sb >>= (cm->min_log2_tiles + 1);
+    }
+    cm->max_tile_height_sb = AOMMAX(max_tile_area_sb / max_tile_width_sb, 1);
+  }
+}
+
+void av1_calculate_tile_rows(AV1_COMMON *const cm) {
+  int mi_rows = ALIGN_POWER_OF_TWO(cm->mi_rows, MAX_MIB_SIZE_LOG2);
+  int sb_rows = mi_rows >> MAX_MIB_SIZE_LOG2;
+  int start_sb, size_sb, i;
+
+  if (cm->uniform_tile_spacing_flag) {
+    size_sb = ALIGN_POWER_OF_TWO(sb_rows, cm->log2_tile_rows);
+    size_sb >>= cm->log2_tile_rows;
+    assert(size_sb > 0);
+    for (i = 0, start_sb = 0; start_sb < sb_rows; i++) {
+      cm->tile_row_start_sb[i] = start_sb;
+      start_sb += size_sb;
+    }
+    cm->tile_rows = i;
+    cm->tile_row_start_sb[i] = start_sb;
+  } else {
+    // No action
+  }
+}
+
+void av1_tile_set_row(TileInfo *tile, const AV1_COMMON *cm, int row) {
+  assert(row < cm->tile_rows);
+  int mi_row_start = cm->tile_row_start_sb[row] << MAX_MIB_SIZE_LOG2;
+  int mi_row_end = cm->tile_row_start_sb[row + 1] << MAX_MIB_SIZE_LOG2;
+  tile->mi_row_start = mi_row_start;
+  tile->mi_row_end = AOMMIN(mi_row_end, cm->mi_rows);
+}
+
+void av1_tile_set_col(TileInfo *tile, const AV1_COMMON *cm, int col) {
+  assert(col < cm->tile_cols);
+  int mi_col_start = cm->tile_col_start_sb[col] << MAX_MIB_SIZE_LOG2;
+  int mi_col_end = cm->tile_col_start_sb[col + 1] << MAX_MIB_SIZE_LOG2;
+  tile->mi_col_start = mi_col_start;
+  tile->mi_col_end = AOMMIN(mi_col_end, cm->mi_cols);
+}
+
+#else
+
+void av1_tile_set_row(TileInfo *tile, const AV1_COMMON *cm, int row) {
+  tile->mi_row_start = row * cm->tile_height;
+  tile->mi_row_end = AOMMIN(tile->mi_row_start + cm->tile_height, cm->mi_rows);
+}
+
+void av1_tile_set_col(TileInfo *tile, const AV1_COMMON *cm, int col) {
+  tile->mi_col_start = col * cm->tile_width;
+  tile->mi_col_end = AOMMIN(tile->mi_col_start + cm->tile_width, cm->mi_cols);
+}
+
 #if CONFIG_EXT_PARTITION
 #define MIN_TILE_WIDTH_MAX_SB 2
 #define MAX_TILE_WIDTH_MAX_SB 32
@@ -74,6 +170,7 @@
   *max_log2_tile_cols = get_max_log2_tile_cols(max_sb_cols);
   assert(*min_log2_tile_cols <= *max_log2_tile_cols);
 }
+#endif  // CONFIG_MAX_TILE
 
 void av1_setup_frame_boundary_info(const AV1_COMMON *const cm) {
   MODE_INFO *mi = cm->mi;
diff --git a/av1/common/tile_common.h b/av1/common/tile_common.h
index 0bc80c9..a455957 100644
--- a/av1/common/tile_common.h
+++ b/av1/common/tile_common.h
@@ -54,6 +54,21 @@
 int av1_disable_loopfilter_on_tile_boundary(const struct AV1Common *cm);
 #endif  // CONFIG_LOOPFILTERING_ACROSS_TILES
 
+#if CONFIG_MAX_TILE
+
+// Define tile maximum width and area
+// There is no maximum height since height is limited by area and width limits
+// The minimum tile width or height is fixed at one superblock
+#define MAX_TILE_WIDTH (4096)  // Max Tile width in pixels
+#define MAX_TILE_WIDTH_SB (MAX_TILE_WIDTH >> MAX_SB_SIZE_LOG2)
+#define MAX_TILE_AREA (4096 * 2304)  // Maximum tile area in pixels
+#define MAX_TILE_AREA_SB (MAX_TILE_AREA >> (2 * MAX_SB_SIZE_LOG2))
+
+void av1_get_tile_limits(struct AV1Common *const cm);
+void av1_calculate_tile_cols(struct AV1Common *const cm);
+void av1_calculate_tile_rows(struct AV1Common *const cm);
+#endif
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index fcad912..f1b19af 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -3154,6 +3154,76 @@
                        "Tile group extends past last tile in frame");
 }
 
+#if CONFIG_MAX_TILE
+
+// Same function as av1_read_uniform but reading from uncompresses header wb
+static int rb_read_uniform(struct aom_read_bit_buffer *const rb, int n) {
+  const int l = get_unsigned_bits(n);
+  const int m = (1 << l) - n;
+  const int v = aom_rb_read_literal(rb, l - 1);
+  assert(l != 0);
+  if (v < m)
+    return v;
+  else
+    return (v << 1) - m + aom_rb_read_literal(rb, 1);
+}
+
+static void read_tile_info_max_tile(AV1_COMMON *const cm,
+                                    struct aom_read_bit_buffer *const rb) {
+  int width_mi = ALIGN_POWER_OF_TWO(cm->mi_cols, MAX_MIB_SIZE_LOG2);
+  int height_mi = ALIGN_POWER_OF_TWO(cm->mi_rows, MAX_MIB_SIZE_LOG2);
+  int width_sb = width_mi >> MAX_MIB_SIZE_LOG2;
+  int height_sb = height_mi >> MAX_MIB_SIZE_LOG2;
+  int start_sb, size_sb, i;
+
+  av1_get_tile_limits(cm);
+  cm->uniform_tile_spacing_flag = aom_rb_read_bit(rb);
+
+  // Read tile columns
+  if (cm->uniform_tile_spacing_flag) {
+    cm->log2_tile_cols = cm->min_log2_tile_cols;
+    while (cm->log2_tile_cols < cm->max_log2_tile_cols) {
+      if (!aom_rb_read_bit(rb)) {
+        break;
+      }
+      cm->log2_tile_cols++;
+    }
+  } else {
+    for (i = 0, start_sb = 0; width_sb > 0; i++) {
+      size_sb = 1 + rb_read_uniform(rb, AOMMIN(width_sb, MAX_TILE_WIDTH_SB));
+      cm->tile_col_start_sb[i] = start_sb;
+      start_sb += size_sb;
+      width_sb -= size_sb;
+    }
+    cm->tile_cols = i;
+    cm->tile_col_start_sb[i] = start_sb;
+  }
+  av1_calculate_tile_cols(cm);
+
+  // Read tile rows
+  if (cm->uniform_tile_spacing_flag) {
+    cm->log2_tile_rows = cm->min_log2_tile_rows;
+    while (cm->log2_tile_rows < cm->max_log2_tile_rows) {
+      if (!aom_rb_read_bit(rb)) {
+        break;
+      }
+      cm->log2_tile_rows++;
+    }
+  } else {
+    for (i = 0, start_sb = 0; height_sb > 0; i++) {
+      size_sb =
+          1 + rb_read_uniform(rb, AOMMIN(height_sb, cm->max_tile_height_sb));
+      cm->tile_row_start_sb[i] = start_sb;
+      start_sb += size_sb;
+      height_sb -= size_sb;
+    }
+    cm->tile_rows = i;
+    cm->tile_row_start_sb[i] = start_sb;
+  }
+  av1_calculate_tile_rows(cm);
+}
+#endif
+
 static void read_tile_info(AV1Decoder *const pbi,
                            struct aom_read_bit_buffer *const rb) {
   AV1_COMMON *const cm = &pbi->common;
@@ -3205,23 +3275,37 @@
 #endif
   } else {
 #endif  // CONFIG_EXT_TILE
-    int min_log2_tile_cols, max_log2_tile_cols, max_ones;
-    av1_get_tile_n_bits(cm->mi_cols, &min_log2_tile_cols, &max_log2_tile_cols);
 
-    // columns
-    max_ones = max_log2_tile_cols - min_log2_tile_cols;
-    cm->log2_tile_cols = min_log2_tile_cols;
-    while (max_ones-- && aom_rb_read_bit(rb)) cm->log2_tile_cols++;
+#if CONFIG_MAX_TILE
+    read_tile_info_max_tile(cm, rb);
+#else
+  int min_log2_tile_cols, max_log2_tile_cols, max_ones;
+  av1_get_tile_n_bits(cm->mi_cols, &min_log2_tile_cols, &max_log2_tile_cols);
 
-    if (cm->log2_tile_cols > 6)
-      aom_internal_error(&cm->error, AOM_CODEC_CORRUPT_FRAME,
-                         "Invalid number of tile columns");
+  // columns
+  max_ones = max_log2_tile_cols - min_log2_tile_cols;
+  cm->log2_tile_cols = min_log2_tile_cols;
+  while (max_ones-- && aom_rb_read_bit(rb)) cm->log2_tile_cols++;
 
-    // rows
-    cm->log2_tile_rows = aom_rb_read_bit(rb);
-    if (cm->log2_tile_rows) cm->log2_tile_rows += aom_rb_read_bit(rb);
+  if (cm->log2_tile_cols > 6)
+    aom_internal_error(&cm->error, AOM_CODEC_CORRUPT_FRAME,
+                       "Invalid number of tile columns");
+
+  // rows
+  cm->log2_tile_rows = aom_rb_read_bit(rb);
+  if (cm->log2_tile_rows) cm->log2_tile_rows += aom_rb_read_bit(rb);
+
+  cm->tile_width = get_tile_size(cm->mi_cols, cm->log2_tile_cols);
+  cm->tile_height = get_tile_size(cm->mi_rows, cm->log2_tile_rows);
+
+  const int max_cols = (cm->mi_cols + cm->tile_width - 1) / cm->tile_width;
+  const int max_rows = (cm->mi_rows + cm->tile_height - 1) / cm->tile_height;
+
+  cm->tile_cols = AOMMIN(1 << cm->log2_tile_cols, max_cols);
+  cm->tile_rows = AOMMIN(1 << cm->log2_tile_rows, max_rows);
+#endif  // CONFIG_MAX_TILE
 #if CONFIG_DEPENDENT_HORZTILES
-    if (cm->log2_tile_rows != 0)
+    if (cm->tile_rows > 1)
       cm->dependent_horz_tiles = aom_rb_read_bit(rb);
     else
       cm->dependent_horz_tiles = 0;
@@ -3230,15 +3314,6 @@
     cm->loop_filter_across_tiles_enabled = aom_rb_read_bit(rb);
 #endif  // CONFIG_LOOPFILTERING_ACROSS_TILES
 
-    cm->tile_width = get_tile_size(cm->mi_cols, cm->log2_tile_cols);
-    cm->tile_height = get_tile_size(cm->mi_rows, cm->log2_tile_rows);
-
-    const int max_cols = (cm->mi_cols + cm->tile_width - 1) / cm->tile_width;
-    const int max_rows = (cm->mi_rows + cm->tile_height - 1) / cm->tile_height;
-
-    cm->tile_cols = AOMMIN(1 << cm->log2_tile_cols, max_cols);
-    cm->tile_rows = AOMMIN(1 << cm->log2_tile_rows, max_rows);
-
     // tile size magnitude
     pbi->tile_size_bytes = aom_rb_read_literal(rb, 2) + 1;
 #if CONFIG_EXT_TILE
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 4b72fb2..07a28e4 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -3601,6 +3601,72 @@
   }
 }
 
+#if CONFIG_MAX_TILE
+
+// Same function as write_uniform but writing to uncompresses header wb
+static void wb_write_uniform(struct aom_write_bit_buffer *wb, int n, int v) {
+  const int l = get_unsigned_bits(n);
+  const int m = (1 << l) - n;
+  if (l == 0) return;
+  if (v < m) {
+    aom_wb_write_literal(wb, v, l - 1);
+  } else {
+    aom_wb_write_literal(wb, m + ((v - m) >> 1), l - 1);
+    aom_wb_write_literal(wb, (v - m) & 1, 1);
+  }
+}
+
+static void write_tile_info_max_tile(const AV1_COMMON *const cm,
+                                     struct aom_write_bit_buffer *wb) {
+  int width_mi = ALIGN_POWER_OF_TWO(cm->mi_cols, MAX_MIB_SIZE_LOG2);
+  int height_mi = ALIGN_POWER_OF_TWO(cm->mi_rows, MAX_MIB_SIZE_LOG2);
+  int width_sb = width_mi >> MAX_MIB_SIZE_LOG2;
+  int height_sb = height_mi >> MAX_MIB_SIZE_LOG2;
+  int size_sb, i;
+
+  aom_wb_write_bit(wb, cm->uniform_tile_spacing_flag);
+
+  if (cm->uniform_tile_spacing_flag) {
+    // Uniform spaced tiles with power-of-two number of rows and columns
+    // tile columns
+    int ones = cm->log2_tile_cols - cm->min_log2_tile_cols;
+    while (ones--) {
+      aom_wb_write_bit(wb, 1);
+    }
+    if (cm->log2_tile_cols < cm->max_log2_tile_cols) {
+      aom_wb_write_bit(wb, 0);
+    }
+
+    // rows
+    ones = cm->log2_tile_rows - cm->min_log2_tile_rows;
+    while (ones--) {
+      aom_wb_write_bit(wb, 1);
+    }
+    if (cm->log2_tile_rows < cm->max_log2_tile_rows) {
+      aom_wb_write_bit(wb, 0);
+    }
+  } else {
+    // Explicit tiles with configurable tile widths and heights
+    // columns
+    for (i = 0; i < cm->tile_cols; i++) {
+      size_sb = cm->tile_col_start_sb[i + 1] - cm->tile_col_start_sb[i];
+      wb_write_uniform(wb, AOMMIN(width_sb, MAX_TILE_WIDTH_SB), size_sb - 1);
+      width_sb -= size_sb;
+    }
+    assert(width_sb == 0);
+
+    // rows
+    for (i = 0; i < cm->tile_rows; i++) {
+      size_sb = cm->tile_row_start_sb[i + 1] - cm->tile_row_start_sb[i];
+      wb_write_uniform(wb, AOMMIN(height_sb, cm->max_tile_height_sb),
+                       size_sb - 1);
+      height_sb -= size_sb;
+    }
+    assert(height_sb == 0);
+  }
+}
+#endif
+
 static void write_tile_info(const AV1_COMMON *const cm,
                             struct aom_write_bit_buffer *wb) {
 #if CONFIG_EXT_TILE
@@ -3633,20 +3699,25 @@
 #endif  // CONFIG_EXT_PARTITION
   } else {
 #endif  // CONFIG_EXT_TILE
-    int min_log2_tile_cols, max_log2_tile_cols, ones;
-    av1_get_tile_n_bits(cm->mi_cols, &min_log2_tile_cols, &max_log2_tile_cols);
 
-    // columns
-    ones = cm->log2_tile_cols - min_log2_tile_cols;
-    while (ones--) aom_wb_write_bit(wb, 1);
+#if CONFIG_MAX_TILE
+    write_tile_info_max_tile(cm, wb);
+#else
+  int min_log2_tile_cols, max_log2_tile_cols, ones;
+  av1_get_tile_n_bits(cm->mi_cols, &min_log2_tile_cols, &max_log2_tile_cols);
 
-    if (cm->log2_tile_cols < max_log2_tile_cols) aom_wb_write_bit(wb, 0);
+  // columns
+  ones = cm->log2_tile_cols - min_log2_tile_cols;
+  while (ones--) aom_wb_write_bit(wb, 1);
 
-    // rows
-    aom_wb_write_bit(wb, cm->log2_tile_rows != 0);
-    if (cm->log2_tile_rows != 0) aom_wb_write_bit(wb, cm->log2_tile_rows != 1);
+  if (cm->log2_tile_cols < max_log2_tile_cols) aom_wb_write_bit(wb, 0);
+
+  // rows
+  aom_wb_write_bit(wb, cm->log2_tile_rows != 0);
+  if (cm->log2_tile_rows != 0) aom_wb_write_bit(wb, cm->log2_tile_rows != 1);
+#endif
 #if CONFIG_DEPENDENT_HORZTILES
-    if (cm->log2_tile_rows != 0) aom_wb_write_bit(wb, cm->dependent_horz_tiles);
+    if (cm->tile_rows > 1) aom_wb_write_bit(wb, cm->dependent_horz_tiles);
 #endif
 #if CONFIG_EXT_TILE
   }
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 858c9af..0fcc8e6 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -863,6 +863,33 @@
 #endif
 }
 
+#if CONFIG_MAX_TILE
+
+static void set_tile_info_max_tile(AV1_COMP *cpi) {
+  AV1_COMMON *const cm = &cpi->common;
+
+  av1_get_tile_limits(cm);
+  // Configure uniform spaced tiles
+  // (API is not yet upgraded to general tiles)
+  cm->uniform_tile_spacing_flag = 1;
+
+  // configure tile columns
+  if (cm->uniform_tile_spacing_flag) {
+    cm->log2_tile_cols = AOMMAX(cpi->oxcf.tile_columns, cm->min_log2_tile_cols);
+    cm->log2_tile_cols = AOMMIN(cm->log2_tile_cols, cm->max_log2_tile_cols);
+  }
+  av1_calculate_tile_cols(cm);
+
+  // configure tile rows
+  if (cm->uniform_tile_spacing_flag) {
+    cm->log2_tile_rows = AOMMAX(cpi->oxcf.tile_rows, cm->min_log2_tile_rows);
+    cm->log2_tile_rows = AOMMIN(cm->log2_tile_rows, cm->max_log2_tile_rows);
+  }
+  av1_calculate_tile_rows(cm);
+}
+
+#endif
+
 static void set_tile_info(AV1_COMP *cpi) {
   AV1_COMMON *const cm = &cpi->common;
 #if CONFIG_DEPENDENT_HORZTILES
@@ -904,21 +931,26 @@
     while (cm->tile_rows * cm->tile_height < cm->mi_rows) ++cm->tile_rows;
   } else {
 #endif  // CONFIG_EXT_TILE
-    int min_log2_tile_cols, max_log2_tile_cols;
-    av1_get_tile_n_bits(cm->mi_cols, &min_log2_tile_cols, &max_log2_tile_cols);
 
-    cm->log2_tile_cols =
-        clamp(cpi->oxcf.tile_columns, min_log2_tile_cols, max_log2_tile_cols);
-    cm->log2_tile_rows = cpi->oxcf.tile_rows;
+#if CONFIG_MAX_TILE
+    set_tile_info_max_tile(cpi);
+#else
+  int min_log2_tile_cols, max_log2_tile_cols;
+  av1_get_tile_n_bits(cm->mi_cols, &min_log2_tile_cols, &max_log2_tile_cols);
 
-    cm->tile_width = get_tile_size(cm->mi_cols, cm->log2_tile_cols);
-    cm->tile_height = get_tile_size(cm->mi_rows, cm->log2_tile_rows);
+  cm->log2_tile_cols =
+      clamp(cpi->oxcf.tile_columns, min_log2_tile_cols, max_log2_tile_cols);
+  cm->log2_tile_rows = cpi->oxcf.tile_rows;
 
-    const int max_cols = (cm->mi_cols + cm->tile_width - 1) / cm->tile_width;
-    const int max_rows = (cm->mi_rows + cm->tile_height - 1) / cm->tile_height;
+  cm->tile_width = get_tile_size(cm->mi_cols, cm->log2_tile_cols);
+  cm->tile_height = get_tile_size(cm->mi_rows, cm->log2_tile_rows);
 
-    cm->tile_cols = AOMMIN(1 << cm->log2_tile_cols, max_cols);
-    cm->tile_rows = AOMMIN(1 << cm->log2_tile_rows, max_rows);
+  const int max_cols = (cm->mi_cols + cm->tile_width - 1) / cm->tile_width;
+  const int max_rows = (cm->mi_rows + cm->tile_height - 1) / cm->tile_height;
+
+  cm->tile_cols = AOMMIN(1 << cm->log2_tile_cols, max_cols);
+  cm->tile_rows = AOMMIN(1 << cm->log2_tile_rows, max_rows);
+#endif  // CONFIG_MAX_TILE
 #if CONFIG_EXT_TILE
   }
 #endif  // CONFIG_EXT_TILE
diff --git a/build/cmake/aom_config_defaults.cmake b/build/cmake/aom_config_defaults.cmake
index 05934e9..9ee628b 100644
--- a/build/cmake/aom_config_defaults.cmake
+++ b/build/cmake/aom_config_defaults.cmake
@@ -165,6 +165,7 @@
 set(CONFIG_LPF_DIRECT 0 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_LV_MAP 0 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_MASKED_TX 0 CACHE NUMBER "AV1 experiment flag.")
+set(CONFIG_MAX_TILE 0 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_MFMV 0 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_MOTION_VAR 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_MRC_TX 0 CACHE NUMBER "AV1 experiment flag.")
diff --git a/configure b/configure
index 1af3e85..273b6a0 100755
--- a/configure
+++ b/configure
@@ -342,6 +342,7 @@
     loopfilter_level
     no_frame_context_signaling
     txmg
+    max_tile
     hash_me
     colorspace_headers
     mfmv