blob: 6d9f47629fbce7b014e603eaa96208cd6bd2bccf [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Support for Intel Camera Imaging ISP subsystem.
* Copyright (c) 2010 - 2015, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include "system_global.h"
#include <linux/kernel.h>
#ifndef ISP2401
#include "ia_css_ifmtr.h"
#include <math_support.h>
#include "sh_css_internal.h"
#include "input_formatter.h"
#include "assert_support.h"
#include "sh_css_sp.h"
#include "isp/modes/interface/input_buf.isp.h"
/************************************************************
* Static functions declarations
************************************************************/
static int ifmtr_start_column(
const struct ia_css_stream_config *config,
unsigned int bin_in,
unsigned int *start_column);
static int ifmtr_input_start_line(
const struct ia_css_stream_config *config,
unsigned int bin_in,
unsigned int *start_line);
static void ifmtr_set_if_blocking_mode(
const input_formatter_cfg_t *const config_a,
const input_formatter_cfg_t *const config_b);
/************************************************************
* Public functions
************************************************************/
/* ISP expects GRBG bayer order, we skip one line and/or one row
* to correct in case the input bayer order is different.
*/
unsigned int ia_css_ifmtr_lines_needed_for_bayer_order(
const struct ia_css_stream_config *config)
{
assert(config);
if ((config->input_config.bayer_order == IA_CSS_BAYER_ORDER_BGGR)
|| (config->input_config.bayer_order == IA_CSS_BAYER_ORDER_GBRG))
return 1;
return 0;
}
unsigned int ia_css_ifmtr_columns_needed_for_bayer_order(
const struct ia_css_stream_config *config)
{
assert(config);
if ((config->input_config.bayer_order == IA_CSS_BAYER_ORDER_RGGB)
|| (config->input_config.bayer_order == IA_CSS_BAYER_ORDER_GBRG))
return 1;
return 0;
}
int ia_css_ifmtr_configure(struct ia_css_stream_config *config,
struct ia_css_binary *binary)
{
unsigned int start_line, start_column = 0,
cropped_height,
cropped_width,
num_vectors,
buffer_height = 2,
buffer_width,
two_ppc,
vmem_increment = 0,
deinterleaving = 0,
deinterleaving_b = 0,
width_a = 0,
width_b = 0,
bits_per_pixel,
vectors_per_buffer,
vectors_per_line = 0,
buffers_per_line = 0,
buf_offset_a = 0,
buf_offset_b = 0,
line_width = 0,
width_b_factor = 1, start_column_b,
left_padding = 0;
input_formatter_cfg_t if_a_config, if_b_config;
enum atomisp_input_format input_format;
int err = 0;
u8 if_config_index;
/* Determine which input formatter config set is targeted. */
/* Index is equal to the CSI-2 port used. */
enum mipi_port_id port;
if (binary) {
cropped_height = binary->in_frame_info.res.height;
cropped_width = binary->in_frame_info.res.width;
/* This should correspond to the input buffer definition for
ISP binaries in input_buf.isp.h */
if (binary->info->sp.enable.continuous &&
binary->info->sp.pipeline.mode != IA_CSS_BINARY_MODE_COPY)
buffer_width = MAX_VECTORS_PER_INPUT_LINE_CONT * ISP_VEC_NELEMS;
else
buffer_width = binary->info->sp.input.max_width;
input_format = binary->input_format;
} else {
/* sp raw copy pipe (IA_CSS_PIPE_MODE_COPY): binary is NULL */
cropped_height = config->input_config.input_res.height;
cropped_width = config->input_config.input_res.width;
buffer_width = MAX_VECTORS_PER_INPUT_LINE_CONT * ISP_VEC_NELEMS;
input_format = config->input_config.format;
}
two_ppc = config->pixels_per_clock == 2;
if (config->mode == IA_CSS_INPUT_MODE_SENSOR
|| config->mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) {
port = config->source.port.port;
if_config_index = (uint8_t)(port - MIPI_PORT0_ID);
} else if (config->mode == IA_CSS_INPUT_MODE_MEMORY) {
if_config_index = SH_CSS_IF_CONFIG_NOT_NEEDED;
} else {
if_config_index = 0;
}
assert(if_config_index <= SH_CSS_MAX_IF_CONFIGS
|| if_config_index == SH_CSS_IF_CONFIG_NOT_NEEDED);
/* TODO: check to see if input is RAW and if current mode interprets
* RAW data in any particular bayer order. copy binary with output
* format other than raw should not result in dropping lines and/or
* columns.
*/
err = ifmtr_input_start_line(config, cropped_height, &start_line);
if (err)
return err;
err = ifmtr_start_column(config, cropped_width, &start_column);
if (err)
return err;
if (config->left_padding == -1)
if (!binary)
/* sp raw copy pipe: set left_padding value */
left_padding = 0;
else
left_padding = binary->left_padding;
else
left_padding = 2 * ISP_VEC_NELEMS - config->left_padding;
if (left_padding) {
num_vectors = CEIL_DIV(cropped_width + left_padding,
ISP_VEC_NELEMS);
} else {
num_vectors = CEIL_DIV(cropped_width, ISP_VEC_NELEMS);
num_vectors *= buffer_height;
/* todo: in case of left padding,
num_vectors is vectors per line,
otherwise vectors per line * buffer_height. */
}
start_column_b = start_column;
bits_per_pixel = input_formatter_get_alignment(INPUT_FORMATTER0_ID)
* 8 / ISP_VEC_NELEMS;
switch (input_format) {
case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
if (two_ppc) {
vmem_increment = 1;
deinterleaving = 1;
deinterleaving_b = 1;
/* half lines */
width_a = cropped_width * deinterleaving / 2;
width_b_factor = 2;
/* full lines */
width_b = width_a * width_b_factor;
buffer_width *= deinterleaving * 2;
/* Patch from bayer to yuv */
num_vectors *= deinterleaving;
buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
vectors_per_line = num_vectors / buffer_height;
/* Even lines are half size */
line_width = vectors_per_line *
input_formatter_get_alignment(INPUT_FORMATTER0_ID) /
2;
start_column /= 2;
} else {
vmem_increment = 1;
deinterleaving = 3;
width_a = cropped_width * deinterleaving / 2;
buffer_width = buffer_width * deinterleaving / 2;
/* Patch from bayer to yuv */
num_vectors = num_vectors / 2 * deinterleaving;
start_column = start_column * deinterleaving / 2;
}
break;
case ATOMISP_INPUT_FORMAT_YUV420_8:
case ATOMISP_INPUT_FORMAT_YUV420_10:
case ATOMISP_INPUT_FORMAT_YUV420_16:
if (two_ppc) {
vmem_increment = 1;
deinterleaving = 1;
width_a = width_b = cropped_width * deinterleaving / 2;
buffer_width *= deinterleaving * 2;
num_vectors *= deinterleaving;
buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
vectors_per_line = num_vectors / buffer_height;
/* Even lines are half size */
line_width = vectors_per_line *
input_formatter_get_alignment(INPUT_FORMATTER0_ID) /
2;
start_column *= deinterleaving;
start_column /= 2;
start_column_b = start_column;
} else {
vmem_increment = 1;
deinterleaving = 1;
width_a = cropped_width * deinterleaving;
buffer_width *= deinterleaving * 2;
num_vectors *= deinterleaving;
start_column *= deinterleaving;
}
break;
case ATOMISP_INPUT_FORMAT_YUV422_8:
case ATOMISP_INPUT_FORMAT_YUV422_10:
case ATOMISP_INPUT_FORMAT_YUV422_16:
if (two_ppc) {
vmem_increment = 1;
deinterleaving = 1;
width_a = width_b = cropped_width * deinterleaving;
buffer_width *= deinterleaving * 2;
num_vectors *= deinterleaving;
start_column *= deinterleaving;
buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
start_column_b = start_column;
} else {
vmem_increment = 1;
deinterleaving = 2;
width_a = cropped_width * deinterleaving;
buffer_width *= deinterleaving;
num_vectors *= deinterleaving;
start_column *= deinterleaving;
}
break;
case ATOMISP_INPUT_FORMAT_RGB_444:
case ATOMISP_INPUT_FORMAT_RGB_555:
case ATOMISP_INPUT_FORMAT_RGB_565:
case ATOMISP_INPUT_FORMAT_RGB_666:
case ATOMISP_INPUT_FORMAT_RGB_888:
num_vectors *= 2;
if (two_ppc) {
deinterleaving = 2; /* BR in if_a, G in if_b */
deinterleaving_b = 1; /* BR in if_a, G in if_b */
buffers_per_line = 4;
start_column_b = start_column;
start_column *= deinterleaving;
start_column_b *= deinterleaving_b;
} else {
deinterleaving = 3; /* BGR */
buffers_per_line = 3;
start_column *= deinterleaving;
}
vmem_increment = 1;
width_a = cropped_width * deinterleaving;
width_b = cropped_width * deinterleaving_b;
buffer_width *= buffers_per_line;
/* Patch from bayer to rgb */
num_vectors = num_vectors / 2 * deinterleaving;
buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
break;
case ATOMISP_INPUT_FORMAT_RAW_6:
case ATOMISP_INPUT_FORMAT_RAW_7:
case ATOMISP_INPUT_FORMAT_RAW_8:
case ATOMISP_INPUT_FORMAT_RAW_10:
case ATOMISP_INPUT_FORMAT_RAW_12:
if (two_ppc) {
int crop_col = (start_column % 2) == 1;
vmem_increment = 2;
deinterleaving = 1;
width_a = width_b = cropped_width / 2;
/* When two_ppc is enabled AND we need to crop one extra
* column, if_a crops by one extra and we swap the
* output offsets to interleave the bayer pattern in
* the correct order.
*/
buf_offset_a = crop_col ? 1 : 0;
buf_offset_b = crop_col ? 0 : 1;
start_column_b = start_column / 2;
start_column = start_column / 2 + crop_col;
} else {
vmem_increment = 1;
deinterleaving = 2;
if ((!binary) || (config->continuous && binary
&& binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_COPY)) {
/* !binary -> sp raw copy pipe, no deinterleaving */
deinterleaving = 1;
}
width_a = cropped_width;
/* Must be multiple of deinterleaving */
num_vectors = CEIL_MUL(num_vectors, deinterleaving);
}
buffer_height *= 2;
if ((!binary) || config->continuous)
/* !binary -> sp raw copy pipe */
buffer_height *= 2;
vectors_per_line = CEIL_DIV(cropped_width, ISP_VEC_NELEMS);
vectors_per_line = CEIL_MUL(vectors_per_line, deinterleaving);
break;
case ATOMISP_INPUT_FORMAT_RAW_14:
case ATOMISP_INPUT_FORMAT_RAW_16:
if (two_ppc) {
num_vectors *= 2;
vmem_increment = 1;
deinterleaving = 2;
width_a = width_b = cropped_width;
/* B buffer is one line further */
buf_offset_b = buffer_width / ISP_VEC_NELEMS;
bits_per_pixel *= 2;
} else {
vmem_increment = 1;
deinterleaving = 2;
width_a = cropped_width;
start_column /= deinterleaving;
}
buffer_height *= 2;
break;
case ATOMISP_INPUT_FORMAT_BINARY_8:
case ATOMISP_INPUT_FORMAT_GENERIC_SHORT1:
case ATOMISP_INPUT_FORMAT_GENERIC_SHORT2:
case ATOMISP_INPUT_FORMAT_GENERIC_SHORT3:
case ATOMISP_INPUT_FORMAT_GENERIC_SHORT4:
case ATOMISP_INPUT_FORMAT_GENERIC_SHORT5:
case ATOMISP_INPUT_FORMAT_GENERIC_SHORT6:
case ATOMISP_INPUT_FORMAT_GENERIC_SHORT7:
case ATOMISP_INPUT_FORMAT_GENERIC_SHORT8:
case ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT:
case ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT:
case ATOMISP_INPUT_FORMAT_EMBEDDED:
case ATOMISP_INPUT_FORMAT_USER_DEF1:
case ATOMISP_INPUT_FORMAT_USER_DEF2:
case ATOMISP_INPUT_FORMAT_USER_DEF3:
case ATOMISP_INPUT_FORMAT_USER_DEF4:
case ATOMISP_INPUT_FORMAT_USER_DEF5:
case ATOMISP_INPUT_FORMAT_USER_DEF6:
case ATOMISP_INPUT_FORMAT_USER_DEF7:
case ATOMISP_INPUT_FORMAT_USER_DEF8:
break;
}
if (width_a == 0)
return -EINVAL;
if (two_ppc)
left_padding /= 2;
/* Default values */
if (left_padding)
vectors_per_line = num_vectors;
if (!vectors_per_line) {
vectors_per_line = CEIL_MUL(num_vectors / buffer_height,
deinterleaving);
line_width = 0;
}
if (!line_width)
line_width = vectors_per_line *
input_formatter_get_alignment(INPUT_FORMATTER0_ID);
if (!buffers_per_line)
buffers_per_line = deinterleaving;
line_width = CEIL_MUL(line_width,
input_formatter_get_alignment(INPUT_FORMATTER0_ID)
* vmem_increment);
vectors_per_buffer = buffer_height * buffer_width / ISP_VEC_NELEMS;
if (config->mode == IA_CSS_INPUT_MODE_TPG &&
((binary && binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_VIDEO) ||
(!binary))) {
/* !binary -> sp raw copy pipe */
/* workaround for TPG in video mode */
start_line = 0;
start_column = 0;
cropped_height -= start_line;
width_a -= start_column;
}
if_a_config.start_line = start_line;
if_a_config.start_column = start_column;
if_a_config.left_padding = left_padding / deinterleaving;
if_a_config.cropped_height = cropped_height;
if_a_config.cropped_width = width_a;
if_a_config.deinterleaving = deinterleaving;
if_a_config.buf_vecs = vectors_per_buffer;
if_a_config.buf_start_index = buf_offset_a;
if_a_config.buf_increment = vmem_increment;
if_a_config.buf_eol_offset =
buffer_width * bits_per_pixel / 8 - line_width;
if_a_config.is_yuv420_format =
(input_format == ATOMISP_INPUT_FORMAT_YUV420_8)
|| (input_format == ATOMISP_INPUT_FORMAT_YUV420_10)
|| (input_format == ATOMISP_INPUT_FORMAT_YUV420_16);
if_a_config.block_no_reqs = (config->mode != IA_CSS_INPUT_MODE_SENSOR);
if (two_ppc) {
if (deinterleaving_b) {
deinterleaving = deinterleaving_b;
width_b = cropped_width * deinterleaving;
buffer_width *= deinterleaving;
/* Patch from bayer to rgb */
num_vectors = num_vectors / 2 *
deinterleaving * width_b_factor;
vectors_per_line = num_vectors / buffer_height;
line_width = vectors_per_line *
input_formatter_get_alignment(INPUT_FORMATTER0_ID);
}
if_b_config.start_line = start_line;
if_b_config.start_column = start_column_b;
if_b_config.left_padding = left_padding / deinterleaving;
if_b_config.cropped_height = cropped_height;
if_b_config.cropped_width = width_b;
if_b_config.deinterleaving = deinterleaving;
if_b_config.buf_vecs = vectors_per_buffer;
if_b_config.buf_start_index = buf_offset_b;
if_b_config.buf_increment = vmem_increment;
if_b_config.buf_eol_offset =
buffer_width * bits_per_pixel / 8 - line_width;
if_b_config.is_yuv420_format =
input_format == ATOMISP_INPUT_FORMAT_YUV420_8
|| input_format == ATOMISP_INPUT_FORMAT_YUV420_10
|| input_format == ATOMISP_INPUT_FORMAT_YUV420_16;
if_b_config.block_no_reqs =
(config->mode != IA_CSS_INPUT_MODE_SENSOR);
if (if_config_index != SH_CSS_IF_CONFIG_NOT_NEEDED) {
assert(if_config_index <= SH_CSS_MAX_IF_CONFIGS);
ifmtr_set_if_blocking_mode(&if_a_config, &if_b_config);
/* Set the ifconfigs to SP group */
sh_css_sp_set_if_configs(&if_a_config, &if_b_config,
if_config_index);
}
} else {
if (if_config_index != SH_CSS_IF_CONFIG_NOT_NEEDED) {
assert(if_config_index <= SH_CSS_MAX_IF_CONFIGS);
ifmtr_set_if_blocking_mode(&if_a_config, NULL);
/* Set the ifconfigs to SP group */
sh_css_sp_set_if_configs(&if_a_config, NULL,
if_config_index);
}
}
return 0;
}
bool ifmtr_set_if_blocking_mode_reset = true;
/************************************************************
* Static functions
************************************************************/
static void ifmtr_set_if_blocking_mode(
const input_formatter_cfg_t *const config_a,
const input_formatter_cfg_t *const config_b)
{
int i;
bool block[] = { false, false, false, false };
assert(N_INPUT_FORMATTER_ID <= (ARRAY_SIZE(block)));
block[INPUT_FORMATTER0_ID] = (bool)config_a->block_no_reqs;
if (config_b)
block[INPUT_FORMATTER1_ID] = (bool)config_b->block_no_reqs;
/* TODO: next could cause issues when streams are started after
* eachother. */
/*IF should not be reconfigured/reset from host */
if (ifmtr_set_if_blocking_mode_reset) {
ifmtr_set_if_blocking_mode_reset = false;
for (i = 0; i < N_INPUT_FORMATTER_ID; i++) {
input_formatter_ID_t id = (input_formatter_ID_t)i;
input_formatter_rst(id);
input_formatter_set_fifo_blocking_mode(id, block[id]);
}
}
return;
}
static int ifmtr_start_column(
const struct ia_css_stream_config *config,
unsigned int bin_in,
unsigned int *start_column)
{
unsigned int in = config->input_config.input_res.width, start,
for_bayer = ia_css_ifmtr_columns_needed_for_bayer_order(config);
if (bin_in + 2 * for_bayer > in)
return -EINVAL;
/* On the hardware, we want to use the middle of the input, so we
* divide the start column by 2. */
start = (in - bin_in) / 2;
/* in case the number of extra columns is 2 or odd, we round the start
* column down */
start &= ~0x1;
/* now we add the one column (if needed) to correct for the bayer
* order).
*/
start += for_bayer;
*start_column = start;
return 0;
}
static int ifmtr_input_start_line(
const struct ia_css_stream_config *config,
unsigned int bin_in,
unsigned int *start_line)
{
unsigned int in = config->input_config.input_res.height, start,
for_bayer = ia_css_ifmtr_lines_needed_for_bayer_order(config);
if (bin_in + 2 * for_bayer > in)
return -EINVAL;
/* On the hardware, we want to use the middle of the input, so we
* divide the start line by 2. On the simulator, we cannot handle extra
* lines at the end of the frame.
*/
start = (in - bin_in) / 2;
/* in case the number of extra lines is 2 or odd, we round the start
* line down.
*/
start &= ~0x1;
/* now we add the one line (if needed) to correct for the bayer order */
start += for_bayer;
*start_line = start;
return 0;
}
#endif