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

#include "hmm.h"

#include "sh_css_sp.h"

#include "input_formatter.h"

#include "dma.h"        /* N_DMA_CHANNEL_ID */

#include "ia_css_buffer.h"
#include "ia_css_binary.h"
#include "sh_css_hrt.h"
#include "sh_css_defs.h"
#include "sh_css_internal.h"
#include "ia_css_control.h"
#include "ia_css_debug.h"
#include "ia_css_debug_pipe.h"
#include "ia_css_event_public.h"
#include "ia_css_mmu.h"
#include "ia_css_stream.h"
#include "ia_css_isp_param.h"
#include "sh_css_params.h"
#include "sh_css_legacy.h"
#include "ia_css_frame_comm.h"
#include "ia_css_isys.h"

#include "gdc_device.h"                         /* HRT_GDC_N */

/*#include "sp.h"*/     /* host2sp_enqueue_frame_data() */


#include "assert_support.h"

#include "sw_event_global.h"                    /* Event IDs.*/
#include "ia_css_event.h"
#include "mmu_device.h"
#include "ia_css_spctrl.h"
#include "atomisp_internal.h"

#ifndef offsetof
#define offsetof(T, x) ((unsigned int)&(((T *)0)->x))
#endif

#define IA_CSS_INCLUDE_CONFIGURATIONS
#include "ia_css_isp_configs.h"
#define IA_CSS_INCLUDE_STATES
#include "ia_css_isp_states.h"

#include "isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.h"

struct sh_css_sp_group          sh_css_sp_group;
struct sh_css_sp_stage          sh_css_sp_stage;
struct sh_css_isp_stage         sh_css_isp_stage;
static struct sh_css_sp_output          sh_css_sp_output;
static struct sh_css_sp_per_frame_data per_frame_data;

/* true if SP supports frame loop and host2sp_commands */
/* For the moment there is only code that sets this bool to true */
/* TODO: add code that sets this bool to false */
static bool sp_running;

static int
set_output_frame_buffer(const struct ia_css_frame *frame,
                        unsigned int idx);

static void
sh_css_copy_buffer_attr_to_spbuffer(struct ia_css_buffer_sp *dest_buf,
                                    const enum sh_css_queue_id queue_id,
                                    const ia_css_ptr xmem_addr,
                                    const enum ia_css_buffer_type buf_type);

static void
initialize_frame_buffer_attribute(struct ia_css_buffer_sp *buf_attr);

static void
initialize_stage_frames(struct ia_css_frames_sp *frames);

/* This data is stored every frame */
void
store_sp_group_data(void)
{
        per_frame_data.sp_group_addr = sh_css_store_sp_group_to_ddr();
}

static void
copy_isp_stage_to_sp_stage(void)
{
        /* [WW07.5]type casting will cause potential issues */
        sh_css_sp_stage.num_stripes = (uint8_t)
                                      sh_css_isp_stage.binary_info.iterator.num_stripes;
        sh_css_sp_stage.row_stripes_height = (uint16_t)
                                             sh_css_isp_stage.binary_info.iterator.row_stripes_height;
        sh_css_sp_stage.row_stripes_overlap_lines = (uint16_t)
                sh_css_isp_stage.binary_info.iterator.row_stripes_overlap_lines;
        sh_css_sp_stage.top_cropping = (uint16_t)
                                       sh_css_isp_stage.binary_info.pipeline.top_cropping;
        sh_css_sp_stage.enable.sdis = sh_css_isp_stage.binary_info.enable.dis;
        sh_css_sp_stage.enable.s3a = sh_css_isp_stage.binary_info.enable.s3a;
}

void
store_sp_stage_data(enum ia_css_pipe_id id, unsigned int pipe_num,
                    unsigned int stage)
{
        unsigned int thread_id;

        ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
        copy_isp_stage_to_sp_stage();
        if (id != IA_CSS_PIPE_ID_COPY)
                sh_css_sp_stage.isp_stage_addr =
                    sh_css_store_isp_stage_to_ddr(pipe_num, stage);
        sh_css_sp_group.pipe[thread_id].sp_stage_addr[stage] =
            sh_css_store_sp_stage_to_ddr(pipe_num, stage);

        /* Clear for next frame */
        sh_css_sp_stage.program_input_circuit = false;
}

static void
store_sp_per_frame_data(const struct ia_css_fw_info *fw)
{
        unsigned int HIVE_ADDR_sp_per_frame_data = 0;

        assert(fw);

        switch (fw->type) {
        case ia_css_sp_firmware:
                HIVE_ADDR_sp_per_frame_data = fw->info.sp.per_frame_data;
                break;
        case ia_css_acc_firmware:
                HIVE_ADDR_sp_per_frame_data = fw->info.acc.per_frame_data;
                break;
        case ia_css_isp_firmware:
                return;
        }

        sp_dmem_store(SP0_ID,
                      (unsigned int)sp_address_of(sp_per_frame_data),
                      &per_frame_data,
                      sizeof(per_frame_data));
}

static void
sh_css_store_sp_per_frame_data(enum ia_css_pipe_id pipe_id,
                               unsigned int pipe_num,
                               const struct ia_css_fw_info *sp_fw)
{
        if (!sp_fw)
                sp_fw = &sh_css_sp_fw;

        store_sp_stage_data(pipe_id, pipe_num, 0);
        store_sp_group_data();
        store_sp_per_frame_data(sp_fw);
}

#if SP_DEBUG != SP_DEBUG_NONE

void
sh_css_sp_get_debug_state(struct sh_css_sp_debug_state *state)
{
        const struct ia_css_fw_info *fw = &sh_css_sp_fw;
        unsigned int HIVE_ADDR_sp_output = fw->info.sp.output;
        unsigned int i;
        unsigned int offset = (unsigned int)offsetof(struct sh_css_sp_output,
                              debug) / sizeof(int);

        assert(state);

        (void)HIVE_ADDR_sp_output; /* To get rid of warning in CRUN */
        for (i = 0; i < sizeof(*state) / sizeof(int); i++)
                ((unsigned int *)state)[i] = load_sp_array_uint(sp_output, i + offset);
}

#endif

void
sh_css_sp_start_binary_copy(unsigned int pipe_num,
                            struct ia_css_frame *out_frame,
                            unsigned int two_ppc)
{
        enum ia_css_pipe_id pipe_id;
        unsigned int thread_id;
        struct sh_css_sp_pipeline *pipe;
        u8 stage_num = 0;

        assert(out_frame);
        pipe_id = IA_CSS_PIPE_ID_CAPTURE;
        ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
        pipe = &sh_css_sp_group.pipe[thread_id];

        pipe->copy.bin.bytes_available = out_frame->data_bytes;
        pipe->num_stages = 1;
        pipe->pipe_id = pipe_id;
        pipe->pipe_num = pipe_num;
        pipe->thread_id = thread_id;
        pipe->pipe_config = 0x0; /* No parameters */
        pipe->pipe_qos_config = QOS_INVALID;

        if (pipe->inout_port_config == 0) {
                SH_CSS_PIPE_PORT_CONFIG_SET(pipe->inout_port_config,
                                            (uint8_t)SH_CSS_PORT_INPUT,
                                            (uint8_t)SH_CSS_HOST_TYPE, 1);
                SH_CSS_PIPE_PORT_CONFIG_SET(pipe->inout_port_config,
                                            (uint8_t)SH_CSS_PORT_OUTPUT,
                                            (uint8_t)SH_CSS_HOST_TYPE, 1);
        }
        IA_CSS_LOG("pipe_id %d port_config %08x",
                   pipe->pipe_id, pipe->inout_port_config);

        if (!IS_ISP2401)
                sh_css_sp_group.config.input_formatter.isp_2ppc = (uint8_t)two_ppc;

        sh_css_sp_stage.num = stage_num;
        sh_css_sp_stage.stage_type = SH_CSS_SP_STAGE_TYPE;
        sh_css_sp_stage.func =
            (unsigned int)IA_CSS_PIPELINE_BIN_COPY;

        set_output_frame_buffer(out_frame, 0);

        /* sp_bin_copy_init on the SP does not deal with dynamica/static yet */
        /* For now always update the dynamic data from out frames. */
        sh_css_store_sp_per_frame_data(pipe_id, pipe_num, &sh_css_sp_fw);
}

static void
sh_css_sp_start_raw_copy(struct ia_css_frame *out_frame,
                         unsigned int pipe_num,
                         unsigned int two_ppc,
                         unsigned int max_input_width,
                         enum sh_css_pipe_config_override pipe_conf_override,
                         unsigned int if_config_index)
{
        enum ia_css_pipe_id pipe_id;
        unsigned int thread_id;
        u8 stage_num = 0;
        struct sh_css_sp_pipeline *pipe;

        assert(out_frame);

        {
                /*
                 * Clear sh_css_sp_stage for easy debugging.
                 * program_input_circuit must be saved as it is set outside
                 * this function.
                 */
                u8 program_input_circuit;

                program_input_circuit = sh_css_sp_stage.program_input_circuit;
                memset(&sh_css_sp_stage, 0, sizeof(sh_css_sp_stage));
                sh_css_sp_stage.program_input_circuit = program_input_circuit;
        }

        pipe_id = IA_CSS_PIPE_ID_COPY;
        ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
        pipe = &sh_css_sp_group.pipe[thread_id];

        pipe->copy.raw.height       = out_frame->frame_info.res.height;
        pipe->copy.raw.width        = out_frame->frame_info.res.width;
        pipe->copy.raw.padded_width  = out_frame->frame_info.padded_width;
        pipe->copy.raw.raw_bit_depth = out_frame->frame_info.raw_bit_depth;
        pipe->copy.raw.max_input_width = max_input_width;
        pipe->num_stages = 1;
        pipe->pipe_id = pipe_id;
        /* TODO: next indicates from which queues parameters need to be
                 sampled, needs checking/improvement */
        if (pipe_conf_override == SH_CSS_PIPE_CONFIG_OVRD_NO_OVRD)
                pipe->pipe_config =
                    (SH_CSS_PIPE_CONFIG_SAMPLE_PARAMS << thread_id);
        else
                pipe->pipe_config = pipe_conf_override;

        pipe->pipe_qos_config = QOS_INVALID;

        if (pipe->inout_port_config == 0) {
                SH_CSS_PIPE_PORT_CONFIG_SET(pipe->inout_port_config,
                                            (uint8_t)SH_CSS_PORT_INPUT,
                                            (uint8_t)SH_CSS_HOST_TYPE, 1);
                SH_CSS_PIPE_PORT_CONFIG_SET(pipe->inout_port_config,
                                            (uint8_t)SH_CSS_PORT_OUTPUT,
                                            (uint8_t)SH_CSS_HOST_TYPE, 1);
        }
        IA_CSS_LOG("pipe_id %d port_config %08x",
                   pipe->pipe_id, pipe->inout_port_config);

        if (!IS_ISP2401)
                sh_css_sp_group.config.input_formatter.isp_2ppc = (uint8_t)two_ppc;

        sh_css_sp_stage.num = stage_num;
        sh_css_sp_stage.xmem_bin_addr = 0x0;
        sh_css_sp_stage.stage_type = SH_CSS_SP_STAGE_TYPE;
        sh_css_sp_stage.func = (unsigned int)IA_CSS_PIPELINE_RAW_COPY;
        sh_css_sp_stage.if_config_index = (uint8_t)if_config_index;
        set_output_frame_buffer(out_frame, 0);

        ia_css_debug_pipe_graph_dump_sp_raw_copy(out_frame);
}

static void
sh_css_sp_start_isys_copy(struct ia_css_frame *out_frame,
                          unsigned int pipe_num, unsigned int max_input_width,
                          unsigned int if_config_index)
{
        enum ia_css_pipe_id pipe_id;
        unsigned int thread_id;
        u8 stage_num = 0;
        struct sh_css_sp_pipeline *pipe;
        enum sh_css_queue_id queue_id;

        assert(out_frame);

        {
                /*
                 * Clear sh_css_sp_stage for easy debugging.
                 * program_input_circuit must be saved as it is set outside
                 * this function.
                 */
                u8 program_input_circuit;

                program_input_circuit = sh_css_sp_stage.program_input_circuit;
                memset(&sh_css_sp_stage, 0, sizeof(sh_css_sp_stage));
                sh_css_sp_stage.program_input_circuit = program_input_circuit;
        }

        pipe_id = IA_CSS_PIPE_ID_COPY;
        ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
        pipe = &sh_css_sp_group.pipe[thread_id];

        pipe->copy.raw.height           = out_frame->frame_info.res.height;
        pipe->copy.raw.width            = out_frame->frame_info.res.width;
        pipe->copy.raw.padded_width     = out_frame->frame_info.padded_width;
        pipe->copy.raw.raw_bit_depth    = out_frame->frame_info.raw_bit_depth;
        pipe->copy.raw.max_input_width  = max_input_width;
        pipe->num_stages                = 1;
        pipe->pipe_id                   = pipe_id;
        pipe->pipe_config               = 0x0;  /* No parameters */
        pipe->pipe_qos_config           = QOS_INVALID;

        initialize_stage_frames(&sh_css_sp_stage.frames);
        sh_css_sp_stage.num = stage_num;
        sh_css_sp_stage.xmem_bin_addr = 0x0;
        sh_css_sp_stage.stage_type = SH_CSS_SP_STAGE_TYPE;
        sh_css_sp_stage.func = (unsigned int)IA_CSS_PIPELINE_ISYS_COPY;
        sh_css_sp_stage.if_config_index = (uint8_t)if_config_index;

        set_output_frame_buffer(out_frame, 0);

        if (pipe->metadata.height > 0) {
                ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_METADATA, thread_id,
                                               &queue_id);
                sh_css_copy_buffer_attr_to_spbuffer(&sh_css_sp_stage.frames.metadata_buf,
                                                    queue_id, mmgr_EXCEPTION,
                                                    IA_CSS_BUFFER_TYPE_METADATA);
        }

        ia_css_debug_pipe_graph_dump_sp_raw_copy(out_frame);
}

unsigned int
sh_css_sp_get_binary_copy_size(void)
{
        const struct ia_css_fw_info *fw = &sh_css_sp_fw;
        unsigned int HIVE_ADDR_sp_output = fw->info.sp.output;
        unsigned int offset = (unsigned int)offsetof(struct sh_css_sp_output,
                              bin_copy_bytes_copied) / sizeof(int);
        (void)HIVE_ADDR_sp_output; /* To get rid of warning in CRUN */
        return load_sp_array_uint(sp_output, offset);
}

unsigned int
sh_css_sp_get_sw_interrupt_value(unsigned int irq)
{
        const struct ia_css_fw_info *fw = &sh_css_sp_fw;
        unsigned int HIVE_ADDR_sp_output = fw->info.sp.output;
        unsigned int offset = (unsigned int)offsetof(struct sh_css_sp_output,
                              sw_interrupt_value)
                              / sizeof(int);
        (void)HIVE_ADDR_sp_output; /* To get rid of warning in CRUN */
        return load_sp_array_uint(sp_output, offset + irq);
}

static void
sh_css_copy_buffer_attr_to_spbuffer(struct ia_css_buffer_sp *dest_buf,
                                    const enum sh_css_queue_id queue_id,
                                    const ia_css_ptr xmem_addr,
                                    const enum ia_css_buffer_type buf_type)
{
        assert(buf_type < IA_CSS_NUM_BUFFER_TYPE);
        if (queue_id > SH_CSS_INVALID_QUEUE_ID) {
                /*
                 * value >=0 indicates that function init_frame_pointers()
                 * should use the dynamic data address
                 */
                assert(queue_id < SH_CSS_MAX_NUM_QUEUES);

                /*
                 * Klocwork assumes assert can be disabled;
                 * Since we can get there with any type, and it does not
                 * know that frame_in->dynamic_data_index can only be set
                 * for one of the types in the assert) it has to assume we
                 * can get here for any type. however this could lead to an
                 * out of bounds reference when indexing buf_type about 10
                 * lines below. In order to satisfy KW an additional if
                 * has been added. This one will always yield true.
                 */
                if (queue_id < SH_CSS_MAX_NUM_QUEUES)
                        dest_buf->buf_src.queue_id = queue_id;
        } else {
                assert(xmem_addr != mmgr_EXCEPTION);
                dest_buf->buf_src.xmem_addr = xmem_addr;
        }
        dest_buf->buf_type = buf_type;
}

static void
sh_css_copy_frame_to_spframe(struct ia_css_frame_sp *sp_frame_out,
                             const struct ia_css_frame *frame_in)
{
        assert(frame_in);

        ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
                            "sh_css_copy_frame_to_spframe():\n");

        sh_css_copy_buffer_attr_to_spbuffer(&sp_frame_out->buf_attr,
                                            frame_in->dynamic_queue_id,
                                            frame_in->data,
                                            frame_in->buf_type);

        ia_css_frame_info_to_frame_sp_info(&sp_frame_out->info, &frame_in->frame_info);

        switch (frame_in->frame_info.format) {
        case IA_CSS_FRAME_FORMAT_RAW_PACKED:
        case IA_CSS_FRAME_FORMAT_RAW:
                sp_frame_out->planes.raw.offset = frame_in->planes.raw.offset;
                break;
        case IA_CSS_FRAME_FORMAT_RGB565:
        case IA_CSS_FRAME_FORMAT_RGBA888:
                sp_frame_out->planes.rgb.offset = frame_in->planes.rgb.offset;
                break;
        case IA_CSS_FRAME_FORMAT_PLANAR_RGB888:
                sp_frame_out->planes.planar_rgb.r.offset =
                    frame_in->planes.planar_rgb.r.offset;
                sp_frame_out->planes.planar_rgb.g.offset =
                    frame_in->planes.planar_rgb.g.offset;
                sp_frame_out->planes.planar_rgb.b.offset =
                    frame_in->planes.planar_rgb.b.offset;
                break;
        case IA_CSS_FRAME_FORMAT_YUYV:
        case IA_CSS_FRAME_FORMAT_UYVY:
        case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8:
        case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8:
        case IA_CSS_FRAME_FORMAT_YUV_LINE:
                sp_frame_out->planes.yuyv.offset = frame_in->planes.yuyv.offset;
                break;
        case IA_CSS_FRAME_FORMAT_NV11:
        case IA_CSS_FRAME_FORMAT_NV12:
        case IA_CSS_FRAME_FORMAT_NV12_16:
        case IA_CSS_FRAME_FORMAT_NV12_TILEY:
        case IA_CSS_FRAME_FORMAT_NV21:
        case IA_CSS_FRAME_FORMAT_NV16:
        case IA_CSS_FRAME_FORMAT_NV61:
                sp_frame_out->planes.nv.y.offset =
                    frame_in->planes.nv.y.offset;
                sp_frame_out->planes.nv.uv.offset =
                    frame_in->planes.nv.uv.offset;
                break;
        case IA_CSS_FRAME_FORMAT_YUV420:
        case IA_CSS_FRAME_FORMAT_YUV422:
        case IA_CSS_FRAME_FORMAT_YUV444:
        case IA_CSS_FRAME_FORMAT_YUV420_16:
        case IA_CSS_FRAME_FORMAT_YUV422_16:
        case IA_CSS_FRAME_FORMAT_YV12:
        case IA_CSS_FRAME_FORMAT_YV16:
                sp_frame_out->planes.yuv.y.offset =
                    frame_in->planes.yuv.y.offset;
                sp_frame_out->planes.yuv.u.offset =
                    frame_in->planes.yuv.u.offset;
                sp_frame_out->planes.yuv.v.offset =
                    frame_in->planes.yuv.v.offset;
                break;
        case IA_CSS_FRAME_FORMAT_QPLANE6:
                sp_frame_out->planes.plane6.r.offset =
                    frame_in->planes.plane6.r.offset;
                sp_frame_out->planes.plane6.r_at_b.offset =
                    frame_in->planes.plane6.r_at_b.offset;
                sp_frame_out->planes.plane6.gr.offset =
                    frame_in->planes.plane6.gr.offset;
                sp_frame_out->planes.plane6.gb.offset =
                    frame_in->planes.plane6.gb.offset;
                sp_frame_out->planes.plane6.b.offset =
                    frame_in->planes.plane6.b.offset;
                sp_frame_out->planes.plane6.b_at_r.offset =
                    frame_in->planes.plane6.b_at_r.offset;
                break;
        case IA_CSS_FRAME_FORMAT_BINARY_8:
                sp_frame_out->planes.binary.data.offset =
                    frame_in->planes.binary.data.offset;
                break;
        default:
                /*
                 * This should not happen, but in case it does,
                 * nullify the planes
                 */
                memset(&sp_frame_out->planes, 0, sizeof(sp_frame_out->planes));
                break;
        }
}

static int
set_input_frame_buffer(const struct ia_css_frame *frame)
{
        if (!frame)
                return -EINVAL;

        switch (frame->frame_info.format) {
        case IA_CSS_FRAME_FORMAT_QPLANE6:
        case IA_CSS_FRAME_FORMAT_YUV420_16:
        case IA_CSS_FRAME_FORMAT_RAW_PACKED:
        case IA_CSS_FRAME_FORMAT_RAW:
        case IA_CSS_FRAME_FORMAT_YUV420:
        case IA_CSS_FRAME_FORMAT_YUYV:
        case IA_CSS_FRAME_FORMAT_YUV_LINE:
        case IA_CSS_FRAME_FORMAT_NV12:
        case IA_CSS_FRAME_FORMAT_NV12_16:
        case IA_CSS_FRAME_FORMAT_NV12_TILEY:
        case IA_CSS_FRAME_FORMAT_NV21:
        case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8:
        case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8:
        case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_10:
                break;
        default:
                return -EINVAL;
        }
        sh_css_copy_frame_to_spframe(&sh_css_sp_stage.frames.in, frame);

        return 0;
}

static int
set_output_frame_buffer(const struct ia_css_frame *frame,
                        unsigned int idx)
{
        if (!frame)
                return -EINVAL;

        switch (frame->frame_info.format) {
        case IA_CSS_FRAME_FORMAT_YUV420:
        case IA_CSS_FRAME_FORMAT_YUV422:
        case IA_CSS_FRAME_FORMAT_YUV444:
        case IA_CSS_FRAME_FORMAT_YV12:
        case IA_CSS_FRAME_FORMAT_YV16:
        case IA_CSS_FRAME_FORMAT_YUV420_16:
        case IA_CSS_FRAME_FORMAT_YUV422_16:
        case IA_CSS_FRAME_FORMAT_NV11:
        case IA_CSS_FRAME_FORMAT_NV12:
        case IA_CSS_FRAME_FORMAT_NV12_16:
        case IA_CSS_FRAME_FORMAT_NV12_TILEY:
        case IA_CSS_FRAME_FORMAT_NV16:
        case IA_CSS_FRAME_FORMAT_NV21:
        case IA_CSS_FRAME_FORMAT_NV61:
        case IA_CSS_FRAME_FORMAT_YUYV:
        case IA_CSS_FRAME_FORMAT_UYVY:
        case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8:
        case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8:
        case IA_CSS_FRAME_FORMAT_YUV_LINE:
        case IA_CSS_FRAME_FORMAT_RGB565:
        case IA_CSS_FRAME_FORMAT_RGBA888:
        case IA_CSS_FRAME_FORMAT_PLANAR_RGB888:
        case IA_CSS_FRAME_FORMAT_RAW:
        case IA_CSS_FRAME_FORMAT_RAW_PACKED:
        case IA_CSS_FRAME_FORMAT_QPLANE6:
        case IA_CSS_FRAME_FORMAT_BINARY_8:
                break;
        default:
                return -EINVAL;
        }
        sh_css_copy_frame_to_spframe(&sh_css_sp_stage.frames.out[idx], frame);
        return 0;
}

static int
set_view_finder_buffer(const struct ia_css_frame *frame)
{
        if (!frame)
                return -EINVAL;

        switch (frame->frame_info.format) {
        /* the dual output pin */
        case IA_CSS_FRAME_FORMAT_NV12:
        case IA_CSS_FRAME_FORMAT_NV12_16:
        case IA_CSS_FRAME_FORMAT_NV21:
        case IA_CSS_FRAME_FORMAT_YUYV:
        case IA_CSS_FRAME_FORMAT_UYVY:
        case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8:
        case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8:
        case IA_CSS_FRAME_FORMAT_YUV420:
        case IA_CSS_FRAME_FORMAT_YV12:
        case IA_CSS_FRAME_FORMAT_NV12_TILEY:

        /* for vf_veceven */
        case IA_CSS_FRAME_FORMAT_YUV_LINE:
                break;
        default:
                return -EINVAL;
        }

        sh_css_copy_frame_to_spframe(&sh_css_sp_stage.frames.out_vf, frame);
        return 0;
}

void sh_css_sp_set_if_configs(
    const input_formatter_cfg_t *config_a,
    const input_formatter_cfg_t *config_b,
    const uint8_t               if_config_index
)
{
        assert(if_config_index < SH_CSS_MAX_IF_CONFIGS);
        assert(config_a);

        sh_css_sp_group.config.input_formatter.set[if_config_index].config_a =
            *config_a;
        sh_css_sp_group.config.input_formatter.a_changed = true;

        if (config_b) {
                sh_css_sp_group.config.input_formatter.set[if_config_index].config_b =
                    *config_b;
                sh_css_sp_group.config.input_formatter.b_changed = true;
        }
}

void
sh_css_sp_program_input_circuit(int fmt_type,
                                int ch_id,
                                enum ia_css_input_mode input_mode)
{
        sh_css_sp_group.config.input_circuit.no_side_band = false;
        sh_css_sp_group.config.input_circuit.fmt_type     = fmt_type;
        sh_css_sp_group.config.input_circuit.ch_id            = ch_id;
        sh_css_sp_group.config.input_circuit.input_mode   = input_mode;
        /*
         * The SP group is only loaded at SP boot time and is read once
         * change flags as "input_circuit_cfg_changed" must be reset on the SP
         */
        sh_css_sp_group.config.input_circuit_cfg_changed = true;
        sh_css_sp_stage.program_input_circuit = true;
}

void
sh_css_sp_configure_sync_gen(int width, int height,
                             int hblank_cycles,
                             int vblank_cycles)
{
        sh_css_sp_group.config.sync_gen.width          = width;
        sh_css_sp_group.config.sync_gen.height         = height;
        sh_css_sp_group.config.sync_gen.hblank_cycles = hblank_cycles;
        sh_css_sp_group.config.sync_gen.vblank_cycles = vblank_cycles;
}

void
sh_css_sp_configure_prbs(int seed)
{
        sh_css_sp_group.config.prbs.seed = seed;
}

void
sh_css_sp_configure_enable_raw_pool_locking(bool lock_all)
{
        sh_css_sp_group.config.enable_raw_pool_locking = true;
        sh_css_sp_group.config.lock_all = lock_all;
}

void
sh_css_sp_enable_isys_event_queue(bool enable)
{
        sh_css_sp_group.config.enable_isys_event_queue = enable;
}

void
sh_css_sp_set_disable_continuous_viewfinder(bool flag)
{
        sh_css_sp_group.config.disable_cont_vf = flag;
}

static int
sh_css_sp_write_frame_pointers(const struct sh_css_binary_args *args)
{
        int err = 0;
        int i;

        assert(args);

        if (args->in_frame)
                err = set_input_frame_buffer(args->in_frame);
        if (!err && args->out_vf_frame)
                err = set_view_finder_buffer(args->out_vf_frame);
        for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
                if (!err && args->out_frame[i])
                        err = set_output_frame_buffer(args->out_frame[i], i);
        }

        /* we don't pass this error back to the upper layer, so we add a assert here
           because we actually hit the error here but it still works by accident... */
        if (err)
                assert(false);
        return err;
}

static void
sh_css_sp_init_group(bool two_ppc,
                     enum atomisp_input_format input_format,
                     bool no_isp_sync,
                     uint8_t if_config_index)
{
        if (!IS_ISP2401)
                sh_css_sp_group.config.input_formatter.isp_2ppc = two_ppc;

        sh_css_sp_group.config.no_isp_sync = (uint8_t)no_isp_sync;
        /* decide whether the frame is processed online or offline */
        if (if_config_index == SH_CSS_IF_CONFIG_NOT_NEEDED)
                return;

        if (!IS_ISP2401) {
                assert(if_config_index < SH_CSS_MAX_IF_CONFIGS);
                sh_css_sp_group.config.input_formatter.set[if_config_index].stream_format =
                    input_format;
        }
}

void
sh_css_stage_write_binary_info(struct ia_css_binary_info *info)
{
        assert(info);
        sh_css_isp_stage.binary_info = *info;
}

static int
copy_isp_mem_if_to_ddr(struct ia_css_binary *binary)
{
        int err;

        err = ia_css_isp_param_copy_isp_mem_if_to_ddr(
            &binary->css_params,
            &binary->mem_params,
            IA_CSS_PARAM_CLASS_CONFIG);
        if (err)
                return err;
        err = ia_css_isp_param_copy_isp_mem_if_to_ddr(
            &binary->css_params,
            &binary->mem_params,
            IA_CSS_PARAM_CLASS_STATE);
        if (err)
                return err;
        return 0;
}

static bool
is_sp_stage(struct ia_css_pipeline_stage *stage)
{
        assert(stage);
        return stage->sp_func != IA_CSS_PIPELINE_NO_FUNC;
}

static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline,
                                   const struct ia_css_binary      *binary,
                                   const struct sh_css_binary_args *args,
                                   bool                            two_ppc,
                                   bool                            deinterleaved)
{
        int ret;

        ret = ia_css_fpn_configure(binary,  &binary->in_frame_info);
        if (ret)
                return ret;
        ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0]));
        if (ret)
                return ret;
        ret = ia_css_qplane_configure(pipeline, binary, &binary->in_frame_info);
        if (ret)
                return ret;
        ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
        if (ret)
                return ret;
        ret = ia_css_output1_configure(binary, ia_css_frame_get_info(args->out_vf_frame));
        if (ret)
                return ret;
        ret = ia_css_copy_output_configure(binary, args->copy_output);
        if (ret)
                return ret;
        ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
        if (ret)
                return ret;
        ret = ia_css_iterator_configure(binary, ia_css_frame_get_info(args->in_frame));
        if (ret)
                return ret;
        ret = ia_css_dvs_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
        if (ret)
                return ret;
        ret = ia_css_output_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
        if (ret)
                return ret;
        ret = ia_css_raw_configure(pipeline, binary, ia_css_frame_get_info(args->in_frame),
                                   &binary->in_frame_info, two_ppc, deinterleaved);
        if (ret)
                return ret;

        /*
         * FIXME: args->delay_frames can be NULL here
         *
         * Somehow, the driver at the Intel Atom Yocto tree doesn't seem to
         * suffer from the same issue.
         *
         * Anyway, the function below should now handle a NULL delay_frames
         * without crashing, but the pipeline should likely be built without
         * adding it at the first place (or there are a hidden bug somewhere)
         */
        ret = ia_css_ref_configure(binary, args->delay_frames, pipeline->dvs_frame_delay);
        if (ret)
                return ret;
        ret = ia_css_tnr_configure(binary, args->tnr_frames);
        if (ret)
                return ret;
        return ia_css_bayer_io_config(binary, args);
}

static void
initialize_isp_states(const struct ia_css_binary *binary)
{
        unsigned int i;

        if (!binary->info->mem_offsets.offsets.state)
                return;

        for (i = 0; i < IA_CSS_NUM_STATE_IDS; i++)
                ia_css_kernel_init_state[i](binary);
}

static void
initialize_frame_buffer_attribute(struct ia_css_buffer_sp *buf_attr)
{
        buf_attr->buf_src.queue_id = SH_CSS_INVALID_QUEUE_ID;
        buf_attr->buf_type = IA_CSS_BUFFER_TYPE_INVALID;
}

static void
initialize_stage_frames(struct ia_css_frames_sp *frames)
{
        unsigned int i;

        initialize_frame_buffer_attribute(&frames->in.buf_attr);
        for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
                initialize_frame_buffer_attribute(&frames->out[i].buf_attr);

        initialize_frame_buffer_attribute(&frames->out_vf.buf_attr);
        initialize_frame_buffer_attribute(&frames->s3a_buf);
        initialize_frame_buffer_attribute(&frames->dvs_buf);
        initialize_frame_buffer_attribute(&frames->metadata_buf);
}

static int
sh_css_sp_init_stage(struct ia_css_binary *binary,
                     const char *binary_name,
                     const struct ia_css_blob_info *blob_info,
                     const struct sh_css_binary_args *args,
                     unsigned int pipe_num,
                     unsigned int stage,
                     bool xnr,
                     const struct ia_css_isp_param_css_segments *isp_mem_if,
                     unsigned int if_config_index,
                     bool two_ppc)
{
        const struct ia_css_binary_xinfo *xinfo;
        const struct ia_css_binary_info  *info;
        int err = 0;
        int i;
        struct ia_css_pipe *pipe = NULL;
        unsigned int thread_id;
        enum sh_css_queue_id queue_id;
        bool continuous = sh_css_continuous_is_enabled((uint8_t)pipe_num);

        assert(binary);
        assert(blob_info);
        assert(args);
        assert(isp_mem_if);

        xinfo = binary->info;
        info  = &xinfo->sp;
        {
                /*
                 * Clear sh_css_sp_stage for easy debugging.
                 * program_input_circuit must be saved as it is set outside
                 * this function.
                 */
                u8 program_input_circuit;

                program_input_circuit = sh_css_sp_stage.program_input_circuit;
                memset(&sh_css_sp_stage, 0, sizeof(sh_css_sp_stage));
                sh_css_sp_stage.program_input_circuit = (uint8_t)program_input_circuit;
        }

        ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);

        if (!info) {
                sh_css_sp_group.pipe[thread_id].sp_stage_addr[stage] = mmgr_NULL;
                return 0;
        }

        if (IS_ISP2401)
                sh_css_sp_stage.deinterleaved = 0;
        else
                sh_css_sp_stage.deinterleaved = ((stage == 0) && continuous);

        initialize_stage_frames(&sh_css_sp_stage.frames);
        /*
         * TODO: Make the Host dynamically determine
         * the stage type.
         */
        sh_css_sp_stage.stage_type = SH_CSS_ISP_STAGE_TYPE;
        sh_css_sp_stage.num             = (uint8_t)stage;
        sh_css_sp_stage.isp_online      = (uint8_t)binary->online;
        sh_css_sp_stage.isp_copy_vf     = (uint8_t)args->copy_vf;
        sh_css_sp_stage.isp_copy_output = (uint8_t)args->copy_output;
        sh_css_sp_stage.enable.vf_output = (args->out_vf_frame != NULL);

        /*
         * Copy the frame infos first, to be overwritten by the frames,
         * if these are present.
         */
        sh_css_sp_stage.frames.effective_in_res.width = binary->effective_in_frame_res.width;
        sh_css_sp_stage.frames.effective_in_res.height = binary->effective_in_frame_res.height;

        ia_css_frame_info_to_frame_sp_info(&sh_css_sp_stage.frames.in.info,
                                           &binary->in_frame_info);
        for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
                ia_css_frame_info_to_frame_sp_info(&sh_css_sp_stage.frames.out[i].info,
                                                   &binary->out_frame_info[i]);
        }
        ia_css_frame_info_to_frame_sp_info(&sh_css_sp_stage.frames.internal_frame_info,
                                           &binary->internal_frame_info);
        sh_css_sp_stage.dvs_envelope.width    = binary->dvs_envelope.width;
        sh_css_sp_stage.dvs_envelope.height   = binary->dvs_envelope.height;
        sh_css_sp_stage.isp_pipe_version      = (uint8_t)info->pipeline.isp_pipe_version;
        sh_css_sp_stage.isp_deci_log_factor   = (uint8_t)binary->deci_factor_log2;
        sh_css_sp_stage.isp_vf_downscale_bits = (uint8_t)binary->vf_downscale_log2;

        sh_css_sp_stage.if_config_index = (uint8_t)if_config_index;

        sh_css_sp_stage.sp_enable_xnr = (uint8_t)xnr;
        sh_css_sp_stage.xmem_bin_addr = xinfo->xmem_addr;
        sh_css_sp_stage.xmem_map_addr = sh_css_params_ddr_address_map();
        sh_css_isp_stage.blob_info = *blob_info;
        sh_css_stage_write_binary_info((struct ia_css_binary_info *)info);

        /* Make sure binary name is smaller than allowed string size */
        assert(strlen(binary_name) < SH_CSS_MAX_BINARY_NAME - 1);
        strscpy(sh_css_isp_stage.binary_name, binary_name, SH_CSS_MAX_BINARY_NAME);
        sh_css_isp_stage.mem_initializers = *isp_mem_if;

        /*
         * Even when a stage does not need uds and does not params,
         * ia_css_uds_sp_scale_params() seems to be called (needs
         * further investigation). This function can not deal with
         * dx, dy = {0, 0}
         */

        err = sh_css_sp_write_frame_pointers(args);
        /* TODO: move it to a better place */
        if (binary->info->sp.enable.s3a) {
                ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_3A_STATISTICS, thread_id,
                                               &queue_id);
                sh_css_copy_buffer_attr_to_spbuffer(&sh_css_sp_stage.frames.s3a_buf, queue_id,
                                                    mmgr_EXCEPTION,
                                                    IA_CSS_BUFFER_TYPE_3A_STATISTICS);
        }
        if (binary->info->sp.enable.dis) {
                ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_DIS_STATISTICS, thread_id,
                                               &queue_id);
                sh_css_copy_buffer_attr_to_spbuffer(&sh_css_sp_stage.frames.dvs_buf, queue_id,
                                                    mmgr_EXCEPTION,
                                                    IA_CSS_BUFFER_TYPE_DIS_STATISTICS);
        }
        ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_METADATA, thread_id, &queue_id);
        sh_css_copy_buffer_attr_to_spbuffer(&sh_css_sp_stage.frames.metadata_buf, queue_id, mmgr_EXCEPTION, IA_CSS_BUFFER_TYPE_METADATA);
        if (err)
                return err;

        if (IS_ISP2401) {
                pipe = find_pipe_by_num(sh_css_sp_group.pipe[thread_id].pipe_num);
                if (!pipe)
                        return -EINVAL;

                if (args->in_frame)
                        ia_css_get_crop_offsets(pipe, &args->in_frame->frame_info);
                else
                        ia_css_get_crop_offsets(pipe, &binary->in_frame_info);
        }

        err = configure_isp_from_args(&sh_css_sp_group.pipe[thread_id],
                                      binary, args, two_ppc, sh_css_sp_stage.deinterleaved);
        if (err)
                return err;

        initialize_isp_states(binary);

        /*
         * We do this only for preview pipe because in fill_binary_info function
         * we assign vf_out res to out res, but for ISP internal processing, we need
         * the original out res. for video pipe, it has two output pins --- out and
         * vf_out, so it can keep these two resolutions already.
         */
        if (binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_PREVIEW &&
            (binary->vf_downscale_log2 > 0)) {
                /*
                 * TODO: Remove this after preview output decimation is fixed
                 * by configuring out&vf info fields properly.
                 */
                sh_css_sp_stage.frames.out[0].info.padded_width
                <<= binary->vf_downscale_log2;
                sh_css_sp_stage.frames.out[0].info.res.width
                <<= binary->vf_downscale_log2;
                sh_css_sp_stage.frames.out[0].info.res.height
                <<= binary->vf_downscale_log2;
        }
        err = copy_isp_mem_if_to_ddr(binary);
        if (err)
                return err;

        return 0;
}

static int
sp_init_stage(struct ia_css_pipeline_stage *stage,
              unsigned int pipe_num,
              bool xnr,
              unsigned int if_config_index,
              bool two_ppc)
{
        struct ia_css_binary *binary;
        const struct ia_css_fw_info *firmware;
        const struct sh_css_binary_args *args;
        unsigned int stage_num;
        /*
         * Initialiser required because of the "else" path below.
         * Is this a valid path ?
         */
        const char *binary_name = "";
        const struct ia_css_binary_xinfo *info = NULL;
        /*
         * Note: the var below is made static as it is quite large;
         * if it is not static it ends up on the stack which could
         * cause issues for drivers
         */
        static struct ia_css_binary tmp_binary;
        const struct ia_css_blob_info *blob_info = NULL;
        struct ia_css_isp_param_css_segments isp_mem_if;
        /*
         * LA: should be ia_css_data, should not contain host pointer.
         * However, CSS/DDR pointer is not available yet.
         * Hack is to store it in params->ddr_ptrs and then copy it late in
         * the SP just before vmem init.
         * TODO: Call this after CSS/DDR allocation and store that pointer.
         * Best is to allocate it at stage creation time together with host
         * pointer. Remove vmem from params.
         */
        struct ia_css_isp_param_css_segments *mem_if = &isp_mem_if;

        int err = 0;

        assert(stage);

        binary = stage->binary;
        firmware = stage->firmware;
        args = &stage->args;
        stage_num = stage->stage_num;

        if (binary) {
                info = binary->info;
                binary_name = (const char *)(info->blob->name);
                blob_info = &info->blob->header.blob;
                ia_css_init_memory_interface(mem_if, &binary->mem_params, &binary->css_params);
        } else if (firmware) {
                const struct ia_css_frame_info *out_infos[IA_CSS_BINARY_MAX_OUTPUT_PORTS] = {NULL};

                if (args->out_frame[0])
                        out_infos[0] = &args->out_frame[0]->frame_info;
                info = &firmware->info.isp;
                ia_css_binary_fill_info(info, false, false,
                                        ATOMISP_INPUT_FORMAT_RAW_10,
                                        ia_css_frame_get_info(args->in_frame),
                                        NULL,
                                        out_infos,
                                        ia_css_frame_get_info(args->out_vf_frame),
                                        &tmp_binary,
                                        NULL,
                                        -1, true);
                binary = &tmp_binary;
                binary->info = info;
                binary_name = IA_CSS_EXT_ISP_PROG_NAME(firmware);
                blob_info = &firmware->blob;
                mem_if = (struct ia_css_isp_param_css_segments *)&firmware->mem_initializers;
        } else {
                /* SP stage */
                assert(stage->sp_func != IA_CSS_PIPELINE_NO_FUNC);
                /*
                 * binary and blob_info are now NULL.
                 * These will be passed to sh_css_sp_init_stage
                 * and dereferenced there, so passing a NULL
                 * pointer is no good. return an error
                 */
                return -EINVAL;
        }

        err = sh_css_sp_init_stage(binary,
                                   (const char *)binary_name,
                                   blob_info,
                                   args,
                                   pipe_num,
                                   stage_num,
                                   xnr,
                                   mem_if,
                                   if_config_index,
                                   two_ppc);
        return err;
}

static void
sp_init_sp_stage(struct ia_css_pipeline_stage *stage,
                 unsigned int pipe_num,
                 bool two_ppc,
                 enum sh_css_pipe_config_override copy_ovrd,
                 unsigned int if_config_index)
{
        const struct sh_css_binary_args *args = &stage->args;

        assert(stage);
        switch (stage->sp_func) {
        case IA_CSS_PIPELINE_RAW_COPY:
                sh_css_sp_start_raw_copy(args->out_frame[0],
                                         pipe_num, two_ppc,
                                         stage->max_input_width,
                                         copy_ovrd, if_config_index);
                break;
        case IA_CSS_PIPELINE_BIN_COPY:
                assert(false); /* TBI */
                break;
        case IA_CSS_PIPELINE_ISYS_COPY:
                sh_css_sp_start_isys_copy(args->out_frame[0],
                                          pipe_num, stage->max_input_width, if_config_index);
                break;
        case IA_CSS_PIPELINE_NO_FUNC:
                assert(false);
                break;
        }
}

void
sh_css_sp_init_pipeline(struct ia_css_pipeline *me,
                        enum ia_css_pipe_id id,
                        u8 pipe_num,
                        bool xnr,
                        bool two_ppc,
                        bool continuous,
                        bool offline,
                        unsigned int required_bds_factor,
                        enum sh_css_pipe_config_override copy_ovrd,
                        enum ia_css_input_mode input_mode,
                        const struct ia_css_metadata_config *md_config,
                        const struct ia_css_metadata_info *md_info,
                        const enum mipi_port_id port_id)
{
        /* Get first stage */
        struct ia_css_pipeline_stage *stage        = NULL;
        struct ia_css_binary         *first_binary = NULL;
        struct ia_css_pipe *pipe = NULL;
        unsigned int num;
        enum ia_css_pipe_id pipe_id = id;
        unsigned int thread_id;
        u8 if_config_index, tmp_if_config_index;

        if (!me->stages) {
                dev_err(atomisp_dev, "%s called on a pipeline without stages\n",
                        __func__);
                return; /* FIXME should be able to return an error */
        }

        first_binary = me->stages->binary;

        if (input_mode == IA_CSS_INPUT_MODE_SENSOR ||
            input_mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) {
                assert(port_id < N_MIPI_PORT_ID);
                if (port_id >= N_MIPI_PORT_ID) /* should not happen but KW does not know */
                        return; /* we should be able to return an error */
                if_config_index  = (uint8_t)(port_id - MIPI_PORT0_ID);
        } else if (input_mode == IA_CSS_INPUT_MODE_MEMORY) {
                if_config_index = SH_CSS_IF_CONFIG_NOT_NEEDED;
        } else {
                if_config_index = 0x0;
        }

        ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
        memset(&sh_css_sp_group.pipe[thread_id], 0, sizeof(struct sh_css_sp_pipeline));

        /* Count stages */
        for (stage = me->stages, num = 0; stage; stage = stage->next, num++) {
                stage->stage_num = num;
                ia_css_debug_pipe_graph_dump_stage(stage, id);
        }
        me->num_stages = num;

        if (first_binary) {
                /* Init pipeline data */
                sh_css_sp_init_group(two_ppc, first_binary->input_format,
                                     offline, if_config_index);
        } /* if (first_binary != NULL) */

        /* Signal the host immediately after start for SP_ISYS_COPY only */
        if (me->num_stages == 1 &&
            me->stages->sp_func == IA_CSS_PIPELINE_ISYS_COPY)
                sh_css_sp_group.config.no_isp_sync = true;

        /* Init stage data */
        sh_css_init_host2sp_frame_data();

        sh_css_sp_group.pipe[thread_id].num_stages = 0;
        sh_css_sp_group.pipe[thread_id].pipe_id = pipe_id;
        sh_css_sp_group.pipe[thread_id].thread_id = thread_id;
        sh_css_sp_group.pipe[thread_id].pipe_num = pipe_num;
        sh_css_sp_group.pipe[thread_id].num_execs = me->num_execs;
        sh_css_sp_group.pipe[thread_id].pipe_qos_config = QOS_INVALID;
        sh_css_sp_group.pipe[thread_id].required_bds_factor = required_bds_factor;
        sh_css_sp_group.pipe[thread_id].input_system_mode
        = (uint32_t)input_mode;
        sh_css_sp_group.pipe[thread_id].port_id = port_id;
        sh_css_sp_group.pipe[thread_id].dvs_frame_delay = (uint32_t)me->dvs_frame_delay;

        /* TODO: next indicates from which queues parameters need to be
                 sampled, needs checking/improvement */
        if (ia_css_pipeline_uses_params(me)) {
                sh_css_sp_group.pipe[thread_id].pipe_config =
                SH_CSS_PIPE_CONFIG_SAMPLE_PARAMS << thread_id;
        }

        /*
         * For continuous use-cases, SP copy is responsible for sampling the
         * parameters
         */
        if (continuous)
                sh_css_sp_group.pipe[thread_id].pipe_config = 0;

        sh_css_sp_group.pipe[thread_id].inout_port_config = me->inout_port_config;

        pipe = find_pipe_by_num(pipe_num);
        assert(pipe);
        if (!pipe)
                return;

        sh_css_sp_group.pipe[thread_id].scaler_pp_lut = sh_css_pipe_get_pp_gdc_lut(pipe);

        if (md_info && md_info->size > 0) {
                sh_css_sp_group.pipe[thread_id].metadata.width  = md_info->resolution.width;
                sh_css_sp_group.pipe[thread_id].metadata.height = md_info->resolution.height;
                sh_css_sp_group.pipe[thread_id].metadata.stride = md_info->stride;
                sh_css_sp_group.pipe[thread_id].metadata.size   = md_info->size;
                ia_css_isys_convert_stream_format_to_mipi_format(
                    md_config->data_type, MIPI_PREDICTOR_NONE,
                    &sh_css_sp_group.pipe[thread_id].metadata.format);
        }

        sh_css_sp_group.pipe[thread_id].output_frame_queue_id = (uint32_t)SH_CSS_INVALID_QUEUE_ID;
        if (pipe_id != IA_CSS_PIPE_ID_COPY) {
                ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, thread_id,
                                               (enum sh_css_queue_id *)(
                                                   &sh_css_sp_group.pipe[thread_id].output_frame_queue_id));
        }

        IA_CSS_LOG("pipe_id %d port_config %08x",
                   pipe_id, sh_css_sp_group.pipe[thread_id].inout_port_config);

        for (stage = me->stages, num = 0; stage; stage = stage->next, num++) {
                sh_css_sp_group.pipe[thread_id].num_stages++;
                if (is_sp_stage(stage)) {
                        sp_init_sp_stage(stage, pipe_num, two_ppc,
                                         copy_ovrd, if_config_index);
                } else {
                        if ((stage->stage_num != 0) ||
                            SH_CSS_PIPE_PORT_CONFIG_IS_CONTINUOUS(me->inout_port_config))
                                tmp_if_config_index = SH_CSS_IF_CONFIG_NOT_NEEDED;
                        else
                                tmp_if_config_index = if_config_index;
                        sp_init_stage(stage, pipe_num,
                                      xnr, tmp_if_config_index, two_ppc);
                }

                store_sp_stage_data(pipe_id, pipe_num, num);
        }
        sh_css_sp_group.pipe[thread_id].pipe_config |= (uint32_t)
                (me->acquire_isp_each_stage << IA_CSS_ACQUIRE_ISP_POS);
        store_sp_group_data();
}

void
sh_css_sp_uninit_pipeline(unsigned int pipe_num)
{
        unsigned int thread_id;

        ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
        /*memset(&sh_css_sp_group.pipe[thread_id], 0, sizeof(struct sh_css_sp_pipeline));*/
        sh_css_sp_group.pipe[thread_id].num_stages = 0;
}

bool sh_css_write_host2sp_command(enum host2sp_commands host2sp_command)
{
        unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
        unsigned int offset = (unsigned int)offsetof(struct host_sp_communication,
                              host2sp_command)
                              / sizeof(int);
        enum host2sp_commands last_cmd = host2sp_cmd_error;
        (void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */

        /* Previous command must be handled by SP (by design) */
        last_cmd = load_sp_array_uint(host_sp_com, offset);
        if (last_cmd != host2sp_cmd_ready)
                IA_CSS_ERROR("last host command not handled by SP(%d)", last_cmd);

        store_sp_array_uint(host_sp_com, offset, host2sp_command);

        return (last_cmd == host2sp_cmd_ready);
}

enum host2sp_commands
sh_css_read_host2sp_command(void)
{
        unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
        unsigned int offset = (unsigned int)offsetof(struct host_sp_communication, host2sp_command)
        / sizeof(int);
        (void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */
        return (enum host2sp_commands)load_sp_array_uint(host_sp_com, offset);
}

/*
 * Frame data is no longer part of the sp_stage structure but part of a
 * separate structure. The aim is to make the sp_data struct static
 * (it defines a pipeline) and that the dynamic (per frame) data is stored
 * separately.
 *
 * This function must be called first every where were you start constructing
 * a new pipeline by defining one or more stages with use of variable
 * sh_css_sp_stage. Even the special cases like accelerator and copy_frame
 * These have a pipeline of just 1 stage.
 */
void
sh_css_init_host2sp_frame_data(void)
{
        /* Clean table */
        unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;

        (void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */
        /*
         * rvanimme: don't clean it to save static frame info line ref_in
         * ref_out, and tnr_frames. Once this static data is in a
         * separate data struct, this may be enable (but still, there is
         * no need for it)
         */
}

/*
 * @brief Update the offline frame information in host_sp_communication.
 * Refer to "sh_css_sp.h" for more details.
 */
void
sh_css_update_host2sp_offline_frame(
    unsigned int frame_num,
    struct ia_css_frame *frame,
    struct ia_css_metadata *metadata)
{
        unsigned int HIVE_ADDR_host_sp_com;
        unsigned int offset;

        assert(frame_num < NUM_CONTINUOUS_FRAMES);

        /* Write new frame data into SP DMEM */
        HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
        offset = (unsigned int)offsetof(struct host_sp_communication,
                                        host2sp_offline_frames)
                 / sizeof(int);
        offset += frame_num;
        store_sp_array_uint(host_sp_com, offset, frame ? frame->data : 0);

        /* Write metadata buffer into SP DMEM */
        offset = (unsigned int)offsetof(struct host_sp_communication,
                                        host2sp_offline_metadata)
                 / sizeof(int);
        offset += frame_num;
        store_sp_array_uint(host_sp_com, offset, metadata ? metadata->address : 0);
}

/*
 * @brief Update the mipi frame information in host_sp_communication.
 * Refer to "sh_css_sp.h" for more details.
 */
void
sh_css_update_host2sp_mipi_frame(
    unsigned int frame_num,
    struct ia_css_frame *frame)
{
        unsigned int HIVE_ADDR_host_sp_com;
        unsigned int offset;

        /* MIPI buffers are dedicated to port, so now there are more of them. */
        assert(frame_num < (N_CSI_PORTS * NUM_MIPI_FRAMES_PER_STREAM));

        /* Write new frame data into SP DMEM */
        HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
        offset = (unsigned int)offsetof(struct host_sp_communication,
                                        host2sp_mipi_frames)
                 / sizeof(int);
        offset += frame_num;

        store_sp_array_uint(host_sp_com, offset,
                            frame ? frame->data : 0);
}

/*
 * @brief Update the mipi metadata information in host_sp_communication.
 * Refer to "sh_css_sp.h" for more details.
 */
void
sh_css_update_host2sp_mipi_metadata(
    unsigned int frame_num,
    struct ia_css_metadata *metadata)
{
        unsigned int HIVE_ADDR_host_sp_com;
        unsigned int o;

        /* MIPI buffers are dedicated to port, so now there are more of them. */
        assert(frame_num < (N_CSI_PORTS * NUM_MIPI_FRAMES_PER_STREAM));

        /* Write new frame data into SP DMEM */
        HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
        o = offsetof(struct host_sp_communication, host2sp_mipi_metadata)
            / sizeof(int);
        o += frame_num;
        store_sp_array_uint(host_sp_com, o,
                            metadata ? metadata->address : 0);
}

void
sh_css_update_host2sp_num_mipi_frames(unsigned int num_frames)
{
        unsigned int HIVE_ADDR_host_sp_com;
        unsigned int offset;

        /* Write new frame data into SP DMEM */
        HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
        offset = (unsigned int)offsetof(struct host_sp_communication,
                                        host2sp_num_mipi_frames)
                 / sizeof(int);

        store_sp_array_uint(host_sp_com, offset, num_frames);
}

void
sh_css_update_host2sp_cont_num_raw_frames(unsigned int num_frames,
        bool set_avail)
{
        const struct ia_css_fw_info *fw;
        unsigned int HIVE_ADDR_host_sp_com;
        unsigned int extra_num_frames, avail_num_frames;
        unsigned int offset, offset_extra;

        /* Write new frame data into SP DMEM */
        fw = &sh_css_sp_fw;
        HIVE_ADDR_host_sp_com = fw->info.sp.host_sp_com;
        if (set_avail) {
                offset = (unsigned int)offsetof(struct host_sp_communication,
                                                host2sp_cont_avail_num_raw_frames)
                         / sizeof(int);
                avail_num_frames = load_sp_array_uint(host_sp_com, offset);
                extra_num_frames = num_frames - avail_num_frames;
                offset_extra = (unsigned int)offsetof(struct host_sp_communication,
                                                      host2sp_cont_extra_num_raw_frames)
                               / sizeof(int);
                store_sp_array_uint(host_sp_com, offset_extra, extra_num_frames);
        } else
                offset = (unsigned int)offsetof(struct host_sp_communication,
                                                host2sp_cont_target_num_raw_frames)
                         / sizeof(int);

        store_sp_array_uint(host_sp_com, offset, num_frames);
}

void
sh_css_event_init_irq_mask(void)
{
        int i;
        unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
        unsigned int offset;
        struct sh_css_event_irq_mask event_irq_mask_init;

        event_irq_mask_init.or_mask  = IA_CSS_EVENT_TYPE_ALL;
        event_irq_mask_init.and_mask = IA_CSS_EVENT_TYPE_NONE;
        (void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */

        assert(sizeof(event_irq_mask_init) % HRT_BUS_BYTES == 0);
        for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) {
                offset = (unsigned int)offsetof(struct host_sp_communication,
                                                host2sp_event_irq_mask[i]);
                assert(offset % HRT_BUS_BYTES == 0);
                sp_dmem_store(SP0_ID,
                              (unsigned int)sp_address_of(host_sp_com) + offset,
                              &event_irq_mask_init, sizeof(event_irq_mask_init));
        }
}

int
ia_css_pipe_set_irq_mask(struct ia_css_pipe *pipe,
                         unsigned int or_mask,
                         unsigned int and_mask)
{
        unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
        unsigned int offset;
        struct sh_css_event_irq_mask event_irq_mask;
        unsigned int pipe_num;

        assert(pipe);

        assert(IA_CSS_PIPE_ID_NUM == NR_OF_PIPELINES);
        /*
         * Linux kernel does not have UINT16_MAX
         * Therefore decided to comment out these 2 asserts for Linux
         * Alternatives that were not chosen:
         * - add a conditional #define for UINT16_MAX
         * - compare with (uint16_t)~0 or 0xffff
         * - different assert for Linux and Windows
         */

        (void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */

        IA_CSS_LOG("or_mask=%x, and_mask=%x", or_mask, and_mask);
        event_irq_mask.or_mask  = (uint16_t)or_mask;
        event_irq_mask.and_mask = (uint16_t)and_mask;

        pipe_num = ia_css_pipe_get_pipe_num(pipe);
        if (pipe_num >= IA_CSS_PIPE_ID_NUM)
                return -EINVAL;
        offset = (unsigned int)offsetof(struct host_sp_communication,
                                        host2sp_event_irq_mask[pipe_num]);
        assert(offset % HRT_BUS_BYTES == 0);
        sp_dmem_store(SP0_ID,
                      (unsigned int)sp_address_of(host_sp_com) + offset,
                      &event_irq_mask, sizeof(event_irq_mask));

        return 0;
}

int
ia_css_event_get_irq_mask(const struct ia_css_pipe *pipe,
                          unsigned int *or_mask,
                          unsigned int *and_mask)
{
        unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
        unsigned int offset;
        struct sh_css_event_irq_mask event_irq_mask;
        unsigned int pipe_num;

        (void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */

        IA_CSS_ENTER_LEAVE("");

        assert(pipe);
        assert(IA_CSS_PIPE_ID_NUM == NR_OF_PIPELINES);

        pipe_num = ia_css_pipe_get_pipe_num(pipe);
        if (pipe_num >= IA_CSS_PIPE_ID_NUM)
                return -EINVAL;
        offset = (unsigned int)offsetof(struct host_sp_communication,
                                        host2sp_event_irq_mask[pipe_num]);
        assert(offset % HRT_BUS_BYTES == 0);
        sp_dmem_load(SP0_ID,
                     (unsigned int)sp_address_of(host_sp_com) + offset,
                     &event_irq_mask, sizeof(event_irq_mask));

        if (or_mask)
                *or_mask = event_irq_mask.or_mask;

        if (and_mask)
                *and_mask = event_irq_mask.and_mask;

        return 0;
}

void
sh_css_sp_set_sp_running(bool flag)
{
        sp_running = flag;
}

bool
sh_css_sp_is_running(void)
{
        return sp_running;
}

void
sh_css_sp_start_isp(void)
{
        const struct ia_css_fw_info *fw;
        unsigned int HIVE_ADDR_sp_sw_state;

        fw = &sh_css_sp_fw;
        HIVE_ADDR_sp_sw_state = fw->info.sp.sw_state;

        if (sp_running)
                return;

        (void)HIVE_ADDR_sp_sw_state; /* Suppress warnings in CRUN */

        /* no longer here, sp started immediately */
        /*ia_css_debug_pipe_graph_dump_epilogue();*/

        store_sp_group_data();
        store_sp_per_frame_data(fw);

        sp_dmem_store_uint32(SP0_ID,
                             (unsigned int)sp_address_of(sp_sw_state),
                             (uint32_t)(IA_CSS_SP_SW_TERMINATED));

        /*
         * Note 1: The sp_start_isp function contains a wait till
         * the input network is configured by the SP.
         * Note 2: Not all SP binaries supports host2sp_commands.
         * In case a binary does support it, the host2sp_command
         * will have status cmd_ready after return of the function
         * sh_css_hrt_sp_start_isp. There is no race-condition here
         * because only after the process_frame command has been
         * received, the SP starts configuring the input network.
         */

        /*
         * We need to set sp_running before we call ia_css_mmu_invalidate_cache
         * as ia_css_mmu_invalidate_cache checks on sp_running to
         * avoid that it accesses dmem while the SP is not powered
         */
        sp_running = true;
        ia_css_mmu_invalidate_cache();
        /* Invalidate all MMU caches */
        mmu_invalidate_cache_all();

        ia_css_spctrl_start(SP0_ID);
}

bool
ia_css_isp_has_started(void)
{
        const struct ia_css_fw_info *fw = &sh_css_sp_fw;
        unsigned int HIVE_ADDR_ia_css_ispctrl_sp_isp_started = fw->info.sp.isp_started;
        (void)HIVE_ADDR_ia_css_ispctrl_sp_isp_started; /* Suppress warnings in CRUN */

        return (bool)load_sp_uint(ia_css_ispctrl_sp_isp_started);
}

/*
 * @brief Initialize the DMA software-mask in the debug mode.
 * Refer to "sh_css_sp.h" for more details.
 */
bool
sh_css_sp_init_dma_sw_reg(int dma_id)
{
        int i;

        /* enable all the DMA channels */
        for (i = 0; i < N_DMA_CHANNEL_ID; i++) {
                /* enable the writing request */
                sh_css_sp_set_dma_sw_reg(dma_id,
                                         i,
                                         0,
                                         true);
                /* enable the reading request */
                sh_css_sp_set_dma_sw_reg(dma_id,
                                         i,
                                         1,
                                         true);
        }

        return true;
}

/*
 * @brief Set the DMA software-mask in the debug mode.
 * Refer to "sh_css_sp.h" for more details.
 */
bool
sh_css_sp_set_dma_sw_reg(int dma_id,
                         int channel_id,
                         int request_type,
                         bool enable)
{
        u32 sw_reg;
        u32 bit_val;
        u32 bit_offset;
        u32 bit_mask;

        (void)dma_id;

        assert(channel_id >= 0 && channel_id < N_DMA_CHANNEL_ID);
        assert(request_type >= 0);

        /* get the software-mask */
        sw_reg =
            sh_css_sp_group.debug.dma_sw_reg;

        /* get the offset of the target bit */
        bit_offset = (8 * request_type) + channel_id;

        /* clear the value of the target bit */
        bit_mask = ~(1 << bit_offset);
        sw_reg &= bit_mask;

        /* set the value of the bit for the DMA channel */
        bit_val = enable ? 1 : 0;
        bit_val <<= bit_offset;
        sw_reg |= bit_val;

        /* update the software status of DMA channels */
        sh_css_sp_group.debug.dma_sw_reg = sw_reg;

        return true;
}

void
sh_css_sp_reset_global_vars(void)
{
        memset(&sh_css_sp_group, 0, sizeof(struct sh_css_sp_group));
        memset(&sh_css_sp_stage, 0, sizeof(struct sh_css_sp_stage));
        memset(&sh_css_isp_stage, 0, sizeof(struct sh_css_isp_stage));
        memset(&sh_css_sp_output, 0, sizeof(struct sh_css_sp_output));
        memset(&per_frame_data, 0, sizeof(struct sh_css_sp_per_frame_data));
}