root/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Support for Intel Camera Imaging ISP subsystem.
 * Copyright (c) 2010 - 2015, Intel Corporation.
 */

#include <linux/math.h>

#include "system_global.h"

#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 = DIV_ROUND_UP(cropped_width + left_padding, ISP_VEC_NELEMS);
        } else {
                num_vectors = DIV_ROUND_UP(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 = DIV_ROUND_UP(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_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;
}