tegrakernel/kernel/nvidia/include/soc/tegra/camrtc-isp5-tiling.h

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 */