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

#include "platform_support.h"

#include "ia_css_inputfifo.h"

#include "device_access.h"

#define __INLINE_SP__
#include "sp.h"
#define __INLINE_ISP__
#include "isp.h"
#define __INLINE_IRQ__
#include "irq.h"
#define __INLINE_FIFO_MONITOR__
#include "fifo_monitor.h"

#define __INLINE_EVENT__
#include "event_fifo.h"
#define __INLINE_SP__

#include "input_system.h"       /* MIPI_PREDICTOR_NONE,... */

#include "assert_support.h"

/* System independent */
#include "sh_css_internal.h"
#include "ia_css_isys.h"

#define HBLANK_CYCLES (187)
#define MARKER_CYCLES (6)

#include <hive_isp_css_streaming_to_mipi_types_hrt.h>

/* The data type is used to send special cases:
 * yuv420: odd lines (1, 3 etc) are twice as wide as even
 *         lines (0, 2, 4 etc).
 * rgb: for two pixels per clock, the R and B values are sent
 *      to output_0 while only G is sent to output_1. This means
 *      that output_1 only gets half the number of values of output_0.
 *      WARNING: This type should also be used for Legacy YUV420.
 * regular: used for all other data types (RAW, YUV422, etc)
 */
enum inputfifo_mipi_data_type {
        inputfifo_mipi_data_type_regular,
        inputfifo_mipi_data_type_yuv420,
        inputfifo_mipi_data_type_yuv420_legacy,
        inputfifo_mipi_data_type_rgb,
};

static unsigned int inputfifo_curr_ch_id, inputfifo_curr_fmt_type;
struct inputfifo_instance {
        unsigned int                            ch_id;
        enum atomisp_input_format       input_format;
        bool                                            two_ppc;
        bool                                            streaming;
        unsigned int                            hblank_cycles;
        unsigned int                            marker_cycles;
        unsigned int                            fmt_type;
        enum inputfifo_mipi_data_type   type;
};

/*
 * Maintain a basic streaming to Mipi administration with ch_id as index
 * ch_id maps on the "Mipi virtual channel ID" and can have value 0..3
 */
#define INPUTFIFO_NR_OF_S2M_CHANNELS    (4)
static struct inputfifo_instance
        inputfifo_inst_admin[INPUTFIFO_NR_OF_S2M_CHANNELS];

/* Streaming to MIPI */
static unsigned int inputfifo_wrap_marker(
    /* static inline unsigned inputfifo_wrap_marker( */
    unsigned int marker)
{
        return marker |
               (inputfifo_curr_ch_id << HIVE_STR_TO_MIPI_CH_ID_LSB) |
               (inputfifo_curr_fmt_type << _HIVE_STR_TO_MIPI_FMT_TYPE_LSB);
}

static inline void
_sh_css_fifo_snd(unsigned int token)
{
        while (!can_event_send_token(STR2MIPI_EVENT_ID))
                udelay(1);
        event_send_token(STR2MIPI_EVENT_ID, token);
        return;
}

static void inputfifo_send_data_a(
    /* static inline void inputfifo_send_data_a( */
    unsigned int data)
{
        unsigned int token = (1 << HIVE_STR_TO_MIPI_VALID_A_BIT) |
                             (data << HIVE_STR_TO_MIPI_DATA_A_LSB);
        _sh_css_fifo_snd(token);
        return;
}

static void inputfifo_send_data_b(
    /* static inline void inputfifo_send_data_b( */
    unsigned int data)
{
        unsigned int token = (1 << HIVE_STR_TO_MIPI_VALID_B_BIT) |
                             (data << _HIVE_STR_TO_MIPI_DATA_B_LSB);
        _sh_css_fifo_snd(token);
        return;
}

static void inputfifo_send_data(
    /* static inline void inputfifo_send_data( */
    unsigned int a,
    unsigned int b)
{
        unsigned int token = ((1 << HIVE_STR_TO_MIPI_VALID_A_BIT) |
                              (1 << HIVE_STR_TO_MIPI_VALID_B_BIT) |
                              (a << HIVE_STR_TO_MIPI_DATA_A_LSB) |
                              (b << _HIVE_STR_TO_MIPI_DATA_B_LSB));
        _sh_css_fifo_snd(token);
        return;
}

static void inputfifo_send_sol(void)
/* static inline void inputfifo_send_sol(void) */
{
        hrt_data        token = inputfifo_wrap_marker(
                                1 << HIVE_STR_TO_MIPI_SOL_BIT);

        _sh_css_fifo_snd(token);
        return;
}

static void inputfifo_send_eol(void)
/* static inline void inputfifo_send_eol(void) */
{
        hrt_data        token = inputfifo_wrap_marker(
                                1 << HIVE_STR_TO_MIPI_EOL_BIT);
        _sh_css_fifo_snd(token);
        return;
}

static void inputfifo_send_sof(void)
/* static inline void inputfifo_send_sof(void) */
{
        hrt_data        token = inputfifo_wrap_marker(
                                1 << HIVE_STR_TO_MIPI_SOF_BIT);

        _sh_css_fifo_snd(token);
        return;
}

static void inputfifo_send_eof(void)
/* static inline void inputfifo_send_eof(void) */
{
        hrt_data        token = inputfifo_wrap_marker(
                                1 << HIVE_STR_TO_MIPI_EOF_BIT);
        _sh_css_fifo_snd(token);
        return;
}

static void inputfifo_send_ch_id_and_fmt_type(
    /* static inline
    void inputfifo_send_ch_id_and_fmt_type( */
    unsigned int ch_id,
    unsigned int fmt_type)
{
        hrt_data        token;

        inputfifo_curr_ch_id = ch_id & _HIVE_ISP_CH_ID_MASK;
        inputfifo_curr_fmt_type = fmt_type & _HIVE_ISP_FMT_TYPE_MASK;
        /* we send an zero marker, this will wrap the ch_id and
         * fmt_type automatically.
         */
        token = inputfifo_wrap_marker(0);
        _sh_css_fifo_snd(token);
        return;
}

static void inputfifo_send_empty_token(void)
/* static inline void inputfifo_send_empty_token(void) */
{
        hrt_data        token = inputfifo_wrap_marker(0);

        _sh_css_fifo_snd(token);
        return;
}

static void inputfifo_start_frame(
    /* static inline void inputfifo_start_frame( */
    unsigned int ch_id,
    unsigned int fmt_type)
{
        inputfifo_send_ch_id_and_fmt_type(ch_id, fmt_type);
        inputfifo_send_sof();
        return;
}

static void inputfifo_end_frame(
    unsigned int marker_cycles)
{
        unsigned int i;

        for (i = 0; i < marker_cycles; i++)
                inputfifo_send_empty_token();
        inputfifo_send_eof();
        return;
}

static void inputfifo_send_line2(
    const unsigned short *data,
    unsigned int width,
    const unsigned short *data2,
    unsigned int width2,
    unsigned int hblank_cycles,
    unsigned int marker_cycles,
    unsigned int two_ppc,
    enum inputfifo_mipi_data_type type)
{
        unsigned int i, is_rgb = 0, is_legacy = 0;

        assert(data);
        assert((data2) || (width2 == 0));
        if (type == inputfifo_mipi_data_type_rgb)
                is_rgb = 1;

        if (type == inputfifo_mipi_data_type_yuv420_legacy)
                is_legacy = 1;

        for (i = 0; i < hblank_cycles; i++)
                inputfifo_send_empty_token();
        inputfifo_send_sol();
        for (i = 0; i < marker_cycles; i++)
                inputfifo_send_empty_token();
        for (i = 0; i < width; i++, data++) {
                /* for RGB in two_ppc, we only actually send 2 pixels per
                 * clock in the even pixels (0, 2 etc). In the other cycles,
                 * we only send 1 pixel, to data[0].
                 */
                unsigned int send_two_pixels = two_ppc;

                if ((is_rgb || is_legacy) && (i % 3 == 2))
                        send_two_pixels = 0;
                if (send_two_pixels) {
                        if (i + 1 == width) {
                                /* for jpg (binary) copy, this can occur
                                 * if the file contains an odd number of bytes.
                                 */
                                inputfifo_send_data(
                                    data[0], 0);
                        } else {
                                inputfifo_send_data(
                                    data[0], data[1]);
                        }
                        /* Additional increment because we send 2 pixels */
                        data++;
                        i++;
                } else if (two_ppc && is_legacy) {
                        inputfifo_send_data_b(data[0]);
                } else {
                        inputfifo_send_data_a(data[0]);
                }
        }

        for (i = 0; i < width2; i++, data2++) {
                /* for RGB in two_ppc, we only actually send 2 pixels per
                 * clock in the even pixels (0, 2 etc). In the other cycles,
                 * we only send 1 pixel, to data2[0].
                 */
                unsigned int send_two_pixels = two_ppc;

                if ((is_rgb || is_legacy) && (i % 3 == 2))
                        send_two_pixels = 0;
                if (send_two_pixels) {
                        if (i + 1 == width2) {
                                /* for jpg (binary) copy, this can occur
                                 * if the file contains an odd number of bytes.
                                 */
                                inputfifo_send_data(
                                    data2[0], 0);
                        } else {
                                inputfifo_send_data(
                                    data2[0], data2[1]);
                        }
                        /* Additional increment because we send 2 pixels */
                        data2++;
                        i++;
                } else if (two_ppc && is_legacy) {
                        inputfifo_send_data_b(data2[0]);
                } else {
                        inputfifo_send_data_a(data2[0]);
                }
        }
        for (i = 0; i < hblank_cycles; i++)
                inputfifo_send_empty_token();
        inputfifo_send_eol();
        return;
}

static void
inputfifo_send_line(const unsigned short *data,
                    unsigned int width,
                    unsigned int hblank_cycles,
                    unsigned int marker_cycles,
                    unsigned int two_ppc,
                    enum inputfifo_mipi_data_type type)
{
        assert(data);
        inputfifo_send_line2(data, width, NULL, 0,
                             hblank_cycles,
                             marker_cycles,
                             two_ppc,
                             type);
}

/* Send a frame of data into the input network via the GP FIFO.
 *  Parameters:
 *   - data: array of 16 bit values that contains all data for the frame.
 *   - width: width of a line in number of subpixels, for yuv420 it is the
 *            number of Y components per line.
 *   - height: height of the frame in number of lines.
 *   - ch_id: channel ID.
 *   - fmt_type: format type.
 *   - hblank_cycles: length of horizontal blanking in cycles.
 *   - marker_cycles: number of empty cycles after start-of-line and before
 *                    end-of-frame.
 *   - two_ppc: boolean, describes whether to send one or two pixels per clock
 *              cycle. In this mode, we sent pixels N and N+1 in the same cycle,
 *              to IF_PRIM_A and IF_PRIM_B respectively. The caller must make
 *              sure the input data has been formatted correctly for this.
 *              For example, for RGB formats this means that unused values
 *              must be inserted.
 *   - yuv420: boolean, describes whether (non-legacy) yuv420 data is used. In
 *             this mode, the odd lines (1,3,5 etc) are half as long as the
 *             even lines (2,4,6 etc).
 *             Note that the first line is odd (1) and the second line is even
 *             (2).
 *
 * This function does not do any reordering of pixels, the caller must make
 * sure the data is in the righ format. Please refer to the CSS receiver
 * documentation for details on the data formats.
 */

static void inputfifo_send_frame(
    const unsigned short *data,
    unsigned int width,
    unsigned int height,
    unsigned int ch_id,
    unsigned int fmt_type,
    unsigned int hblank_cycles,
    unsigned int marker_cycles,
    unsigned int two_ppc,
    enum inputfifo_mipi_data_type type)
{
        unsigned int i;

        assert(data);
        inputfifo_start_frame(ch_id, fmt_type);

        for (i = 0; i < height; i++) {
                if ((type == inputfifo_mipi_data_type_yuv420) &&
                    (i & 1) == 1) {
                        inputfifo_send_line(data, 2 * width,
                                            hblank_cycles,
                                            marker_cycles,
                                            two_ppc, type);
                        data += 2 * width;
                } else {
                        inputfifo_send_line(data, width,
                                            hblank_cycles,
                                            marker_cycles,
                                            two_ppc, type);
                        data += width;
                }
        }
        inputfifo_end_frame(marker_cycles);
        return;
}

static enum inputfifo_mipi_data_type inputfifo_determine_type(
    enum atomisp_input_format input_format)
{
        enum inputfifo_mipi_data_type type;

        type = inputfifo_mipi_data_type_regular;
        if (input_format == ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY) {
                type =
                    inputfifo_mipi_data_type_yuv420_legacy;
        } else if (input_format == ATOMISP_INPUT_FORMAT_YUV420_8  ||
                   input_format == ATOMISP_INPUT_FORMAT_YUV420_10 ||
                   input_format == ATOMISP_INPUT_FORMAT_YUV420_16) {
                type =
                    inputfifo_mipi_data_type_yuv420;
        } else if (input_format >= ATOMISP_INPUT_FORMAT_RGB_444 &&
                   input_format <= ATOMISP_INPUT_FORMAT_RGB_888) {
                type =
                    inputfifo_mipi_data_type_rgb;
        }
        return type;
}

static struct inputfifo_instance *inputfifo_get_inst(
    unsigned int ch_id)
{
        return &inputfifo_inst_admin[ch_id];
}

void ia_css_inputfifo_send_input_frame(
    const unsigned short *data,
    unsigned int width,
    unsigned int height,
    unsigned int ch_id,
    enum atomisp_input_format input_format,
    bool two_ppc)
{
        unsigned int fmt_type, hblank_cycles, marker_cycles;
        enum inputfifo_mipi_data_type type;

        assert(data);
        hblank_cycles = HBLANK_CYCLES;
        marker_cycles = MARKER_CYCLES;
        ia_css_isys_convert_stream_format_to_mipi_format(input_format,
                MIPI_PREDICTOR_NONE,
                &fmt_type);

        type = inputfifo_determine_type(input_format);

        inputfifo_send_frame(data, width, height,
                             ch_id, fmt_type, hblank_cycles, marker_cycles,
                             two_ppc, type);
}

void ia_css_inputfifo_start_frame(
    unsigned int ch_id,
    enum atomisp_input_format input_format,
    bool two_ppc)
{
        struct inputfifo_instance *s2mi;

        s2mi = inputfifo_get_inst(ch_id);

        s2mi->ch_id = ch_id;
        ia_css_isys_convert_stream_format_to_mipi_format(input_format,
                MIPI_PREDICTOR_NONE,
                &s2mi->fmt_type);
        s2mi->two_ppc = two_ppc;
        s2mi->type = inputfifo_determine_type(input_format);
        s2mi->hblank_cycles = HBLANK_CYCLES;
        s2mi->marker_cycles = MARKER_CYCLES;
        s2mi->streaming = true;

        inputfifo_start_frame(ch_id, s2mi->fmt_type);
        return;
}

void ia_css_inputfifo_send_line(
    unsigned int ch_id,
    const unsigned short *data,
    unsigned int width,
    const unsigned short *data2,
    unsigned int width2)
{
        struct inputfifo_instance *s2mi;

        assert(data);
        assert((data2) || (width2 == 0));
        s2mi = inputfifo_get_inst(ch_id);

        /* Set global variables that indicate channel_id and format_type */
        inputfifo_curr_ch_id = (s2mi->ch_id) & _HIVE_ISP_CH_ID_MASK;
        inputfifo_curr_fmt_type = (s2mi->fmt_type) & _HIVE_ISP_FMT_TYPE_MASK;

        inputfifo_send_line2(data, width, data2, width2,
                             s2mi->hblank_cycles,
                             s2mi->marker_cycles,
                             s2mi->two_ppc,
                             s2mi->type);
}

void ia_css_inputfifo_send_embedded_line(
    unsigned int        ch_id,
    enum atomisp_input_format   data_type,
    const unsigned short        *data,
    unsigned int        width)
{
        struct inputfifo_instance *s2mi;
        unsigned int fmt_type;

        assert(data);
        s2mi = inputfifo_get_inst(ch_id);
        ia_css_isys_convert_stream_format_to_mipi_format(data_type,
                MIPI_PREDICTOR_NONE, &fmt_type);

        /* Set format_type for metadata line. */
        inputfifo_curr_fmt_type = fmt_type & _HIVE_ISP_FMT_TYPE_MASK;

        inputfifo_send_line(data, width, s2mi->hblank_cycles, s2mi->marker_cycles,
                            s2mi->two_ppc, inputfifo_mipi_data_type_regular);
}

void ia_css_inputfifo_end_frame(
    unsigned int        ch_id)
{
        struct inputfifo_instance *s2mi;

        s2mi = inputfifo_get_inst(ch_id);

        /* Set global variables that indicate channel_id and format_type */
        inputfifo_curr_ch_id = (s2mi->ch_id) & _HIVE_ISP_CH_ID_MASK;
        inputfifo_curr_fmt_type = (s2mi->fmt_type) & _HIVE_ISP_FMT_TYPE_MASK;

        /* Call existing HRT function */
        inputfifo_end_frame(s2mi->marker_cycles);

        s2mi->streaming = false;
        return;
}