307 lines
8.3 KiB
C
307 lines
8.3 KiB
C
/*
|
|
* Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* NVIDIA CORPORATION and its licensors retain all intellectual property
|
|
* and proprietary rights in and to this software, related documentation
|
|
* and any modifications thereto. Any use, reproduction, disclosure or
|
|
* distribution of this software and related documentation without an express
|
|
* license agreement from NVIDIA CORPORATION is strictly prohibited.
|
|
*/
|
|
|
|
#ifndef INCLUDE_CAMRTC_ISP5_TILING_H
|
|
#define INCLUDE_CAMRTC_ISP5_TILING_H
|
|
|
|
#include "camrtc-common.h"
|
|
#include "camrtc-capture.h"
|
|
|
|
struct isp5_tile_width {
|
|
uint16_t tile_width_first;
|
|
uint16_t tile_width_middle;
|
|
uint16_t tiles_in_slice;
|
|
};
|
|
|
|
struct isp5_slice_height {
|
|
uint16_t slice_height;
|
|
uint16_t vi_first_slice_height;
|
|
uint16_t slices_in_image;
|
|
};
|
|
|
|
#define ISP5_MIN_TILE_WIDTH U16_C(128)
|
|
#define ISP5_MAX_TILE_WIDTH U16_C(1024)
|
|
#define ISP5_MIN_SLICE_HEIGHT U16_C(128)
|
|
#define ISP5_MAX_SLICE_HEIGHT U16_C(540)
|
|
|
|
static inline uint16_t isp5_min_u16(uint16_t a, uint16_t b)
|
|
{
|
|
if (a < b)
|
|
return a;
|
|
else
|
|
return b;
|
|
}
|
|
|
|
static inline uint16_t isp5_max_u16(uint16_t a, uint16_t b)
|
|
{
|
|
if (a > b)
|
|
return a;
|
|
else
|
|
return b;
|
|
}
|
|
|
|
static inline uint16_t isp5_align_down(uint16_t val, uint16_t alignment)
|
|
{
|
|
uint16_t rem = val % alignment;
|
|
|
|
return (rem > 0U) ? (val - rem) : val;
|
|
}
|
|
|
|
static inline uint16_t isp5_align_up(uint16_t val, uint16_t alignment)
|
|
{
|
|
uint16_t rem = val % alignment;
|
|
|
|
return (rem > 0U) ? (isp5_align_down(val, alignment) + alignment) : val;
|
|
}
|
|
|
|
static inline uint16_t isp5_div_round_up(uint16_t x, uint16_t y)
|
|
{
|
|
return (x + y - 1U) / y;
|
|
}
|
|
|
|
/**
|
|
* Calculate suitable tile width for given capture descriptor and ISP program
|
|
*/
|
|
__attribute__((unused))
|
|
static bool isp5_find_tile_width(const struct isp5_program *prg,
|
|
const struct isp_capture_descriptor *cd,
|
|
struct isp5_tile_width *tiling)
|
|
{
|
|
const uint16_t img_width = cd->surface_configs.mr_width;
|
|
const uint16_t alignment = prg->overfetch.alignment;
|
|
|
|
if (img_width <= ISP5_MAX_TILE_WIDTH) {
|
|
tiling->tile_width_first = img_width;
|
|
tiling->tile_width_middle = 0U;
|
|
tiling->tiles_in_slice = 1;
|
|
return true;
|
|
}
|
|
|
|
if (alignment == 0) {
|
|
return false;
|
|
}
|
|
|
|
const uint16_t max_width_first = isp5_align_down(ISP5_MAX_TILE_WIDTH -
|
|
prg->overfetch.right +
|
|
prg->overfetch.pru_ovf_h, alignment) -
|
|
prg->overfetch.right + prg->overfetch.pru_ovf_h;
|
|
|
|
const uint16_t max_width_middle = isp5_align_down(ISP5_MAX_TILE_WIDTH -
|
|
prg->overfetch.right -
|
|
prg->overfetch.left,
|
|
alignment);
|
|
|
|
/* Last tile right edge does not need to be aligned */
|
|
const uint16_t max_width_last = ISP5_MAX_TILE_WIDTH - prg->overfetch.left;
|
|
const uint16_t min_width = isp5_max_u16(ISP5_MIN_TILE_WIDTH, prg->overfetch.right);
|
|
|
|
uint16_t tile_count = 2;
|
|
|
|
if (img_width > max_width_first + max_width_last) {
|
|
const uint16_t pixels_left = img_width - max_width_first - max_width_last;
|
|
const uint16_t middle_tiles = isp5_div_round_up(pixels_left,
|
|
isp5_min_u16(max_width_middle, max_width_first));
|
|
tile_count += middle_tiles;
|
|
}
|
|
|
|
/* Divide image into roughly evenly spaced aligned tiles */
|
|
uint16_t tile_width = (isp5_div_round_up(img_width, alignment) / tile_count) * alignment;
|
|
|
|
/*
|
|
* The right edge of a tile as seen by AP must be aligned
|
|
* correctly for CAR filter. When first tile width fulfills
|
|
* this condition, the rest of tiles are simple to andle by
|
|
* just aligning their active width
|
|
*/
|
|
uint16_t first_width = isp5_min_u16(max_width_first,
|
|
isp5_align_down(
|
|
tile_width + prg->overfetch.right -
|
|
prg->overfetch.pru_ovf_h, alignment) -
|
|
prg->overfetch.right + prg->overfetch.pru_ovf_h);
|
|
uint16_t middle_width = (tile_count > 2) ? isp5_min_u16(max_width_middle, tile_width) : 0U;
|
|
uint16_t last_width = img_width - first_width - (tile_count - 2) * middle_width;
|
|
|
|
/*
|
|
* Ensure that last tile is wide enough. Width of the first
|
|
* tile a this point is guaranteed to be greater than:
|
|
*
|
|
* ((max_tile_width - total overfetch - 2*alignment) / 2) - alignment >= 407 pixels
|
|
*
|
|
* So there is no risk that this correction will cause it to
|
|
* be too narrow.
|
|
*/
|
|
if (last_width < min_width) {
|
|
uint16_t corr = isp5_align_up(min_width-last_width, alignment);
|
|
|
|
first_width -= corr;
|
|
last_width += corr;
|
|
|
|
} else if (last_width > max_width_last) {
|
|
const uint16_t corr = last_width - max_width_last;
|
|
|
|
/* Try first increasing middle tile width */
|
|
if (tile_count > 2) {
|
|
const uint16_t max_middle_corr = max_width_middle - middle_width;
|
|
const uint16_t middle_corr =
|
|
isp5_min_u16(max_middle_corr,
|
|
isp5_align_up(isp5_div_round_up(corr, tile_count-2),
|
|
alignment));
|
|
middle_width += middle_corr;
|
|
last_width -= middle_corr * (tile_count-2);
|
|
}
|
|
|
|
if (last_width > max_width_last) {
|
|
const uint16_t first_corr = isp5_align_up(last_width - max_width_last, alignment);
|
|
|
|
first_width += first_corr;
|
|
last_width -= first_corr;
|
|
}
|
|
}
|
|
|
|
if (first_width < min_width) {
|
|
return false;
|
|
}
|
|
|
|
if (first_width > max_width_first) {
|
|
return false;
|
|
}
|
|
|
|
if (last_width < min_width) {
|
|
return false;
|
|
}
|
|
|
|
if (last_width > max_width_last) {
|
|
return false;
|
|
}
|
|
|
|
if (tile_count > 2U) {
|
|
|
|
if (middle_width < min_width) {
|
|
return false;
|
|
}
|
|
|
|
if (middle_width > max_width_middle) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
tiling->tile_width_first = first_width;
|
|
tiling->tile_width_middle = middle_width;
|
|
tiling->tiles_in_slice = tile_count;
|
|
|
|
return true;
|
|
}
|
|
|
|
__attribute__((unused))
|
|
static bool isp5_find_tile_width_dpcm(const struct isp5_program *prg,
|
|
const struct isp_capture_descriptor *cd,
|
|
struct isp5_tile_width *tiling)
|
|
{
|
|
const uint16_t alignment = isp5_max_u16(prg->overfetch.alignment, 8);
|
|
|
|
if (alignment == 0) {
|
|
return false;
|
|
}
|
|
|
|
const uint16_t max_width_first = isp5_align_down(ISP5_MAX_TILE_WIDTH - prg->overfetch.right,
|
|
alignment);
|
|
const uint16_t max_width_middle = isp5_align_down(ISP5_MAX_TILE_WIDTH - prg->overfetch.right -
|
|
prg->overfetch.left,
|
|
alignment);
|
|
const uint16_t max_width_last = ISP5_MAX_TILE_WIDTH - prg->overfetch.left;
|
|
const uint16_t min_width = isp5_max_u16(ISP5_MIN_TILE_WIDTH, prg->overfetch.right);
|
|
|
|
if (cd->surface_configs.chunk_width_middle > max_width_middle) {
|
|
return false;
|
|
}
|
|
|
|
tiling->tile_width_middle = cd->surface_configs.chunk_width_middle;
|
|
|
|
/*
|
|
* Width of first tile must set so that left overfetch area of
|
|
* 2nd tile fits into 2nd chunk.
|
|
*/
|
|
tiling->tile_width_first = isp5_align_up(cd->surface_configs.chunk_width_first +
|
|
prg->overfetch.left +
|
|
prg->overfetch.right -
|
|
prg->overfetch.pru_ovf_h, alignment) -
|
|
prg->overfetch.right +
|
|
prg->overfetch.pru_ovf_h;
|
|
|
|
if (tiling->tile_width_first < min_width) {
|
|
return false;
|
|
}
|
|
|
|
if (tiling->tile_width_first > max_width_first) {
|
|
return false;
|
|
}
|
|
|
|
if (tiling->tile_width_first + prg->overfetch.right >
|
|
cd->surface_configs.chunk_width_first + cd->surface_configs.chunk_overfetch_width) {
|
|
return false;
|
|
}
|
|
|
|
tiling->tiles_in_slice = 1U + isp5_div_round_up(cd->surface_configs.mr_width -
|
|
cd->surface_configs.chunk_width_first,
|
|
cd->surface_configs.chunk_width_middle);
|
|
const uint16_t last_width = cd->surface_configs.mr_width -
|
|
tiling->tile_width_first -
|
|
(tiling->tiles_in_slice - 1) * tiling->tile_width_middle;
|
|
|
|
if (last_width < min_width) {
|
|
return false;
|
|
}
|
|
|
|
if (last_width > max_width_last) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
__attribute__((unused))
|
|
static bool isp5_find_slice_height(uint16_t img_height,
|
|
struct isp5_slice_height *slicing)
|
|
{
|
|
if (img_height < ISP5_MIN_SLICE_HEIGHT) {
|
|
return false;
|
|
}
|
|
|
|
if (img_height % 2 != 0U) {
|
|
return false;
|
|
}
|
|
|
|
if (img_height <= ISP5_MAX_SLICE_HEIGHT) {
|
|
slicing->slices_in_image = U16_C(1);
|
|
slicing->slice_height = img_height;
|
|
slicing->vi_first_slice_height = img_height;
|
|
return true;
|
|
}
|
|
|
|
uint16_t slice_height = ISP5_MAX_SLICE_HEIGHT;
|
|
uint16_t slice_count = isp5_div_round_up(img_height, ISP5_MAX_SLICE_HEIGHT);
|
|
uint16_t last_height = img_height - ISP5_MAX_SLICE_HEIGHT * (slice_count - 1);
|
|
|
|
if (last_height < ISP5_MIN_SLICE_HEIGHT) {
|
|
const uint16_t corr = ISP5_MIN_SLICE_HEIGHT - last_height;
|
|
const uint16_t slice_corr = isp5_align_up(isp5_div_round_up(corr, slice_count - 1), 2U);
|
|
|
|
slice_height -= slice_corr;
|
|
}
|
|
|
|
slicing->slice_height = slice_height;
|
|
slicing->slices_in_image = slice_count;
|
|
slicing->vi_first_slice_height = (slice_count == 1U) ? slice_height : slice_height + 18U;
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif /* INCLUDE_CAMRTC_ISP5_TILING_H */
|