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