// 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 #ifndef ISP2401 #include "ia_css_ifmtr.h" #include #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