root/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Support for Clovertrail PNW Camera Imaging ISP subsystem.
 *
 * Copyright (c) 2013 Intel Corporation. All Rights Reserved.
 */

#include <media/v4l2-dev.h>
#include <media/v4l2-event.h>

#include "mmu/isp_mmu.h"
#include "mmu/sh_mmu_mrfld.h"
#include "hmm/hmm_bo.h"
#include "hmm/hmm.h"

#include "atomisp_compat.h"
#include "atomisp_internal.h"
#include "atomisp_cmd.h"
#include "atomisp-regs.h"
#include "atomisp_fops.h"
#include "atomisp_ioctl.h"

#include "ia_css_debug.h"
#include "ia_css_isp_param.h"
#include "sh_css_hrt.h"
#include "ia_css_isys.h"

#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/string_choices.h>

/* Assume max number of ACC stages */
#define MAX_ACC_STAGES  20

/* Ideally, this should come from CSS headers */
#define NO_LINK -1

/*
 * to serialize MMIO access , this is due to ISP2400 silicon issue Sighting
 * #4684168, if concurrency access happened, system may hard hang.
 */
static DEFINE_SPINLOCK(mmio_lock);

enum frame_info_type {
        ATOMISP_CSS_VF_FRAME,
        ATOMISP_CSS_SECOND_VF_FRAME,
        ATOMISP_CSS_OUTPUT_FRAME,
        ATOMISP_CSS_SECOND_OUTPUT_FRAME,
        ATOMISP_CSS_RAW_FRAME,
};

struct bayer_ds_factor {
        unsigned int numerator;
        unsigned int denominator;
};

static void atomisp_css2_hw_store_8(hrt_address addr, uint8_t data)
{
        struct atomisp_device *isp = dev_get_drvdata(atomisp_dev);
        unsigned long flags;

        spin_lock_irqsave(&mmio_lock, flags);
        writeb(data, isp->base + (addr & 0x003FFFFF));
        spin_unlock_irqrestore(&mmio_lock, flags);
}

static void atomisp_css2_hw_store_16(hrt_address addr, uint16_t data)
{
        struct atomisp_device *isp = dev_get_drvdata(atomisp_dev);
        unsigned long flags;

        spin_lock_irqsave(&mmio_lock, flags);
        writew(data, isp->base + (addr & 0x003FFFFF));
        spin_unlock_irqrestore(&mmio_lock, flags);
}

void atomisp_css2_hw_store_32(hrt_address addr, uint32_t data)
{
        struct atomisp_device *isp = dev_get_drvdata(atomisp_dev);
        unsigned long flags;

        spin_lock_irqsave(&mmio_lock, flags);
        writel(data, isp->base + (addr & 0x003FFFFF));
        spin_unlock_irqrestore(&mmio_lock, flags);
}

static uint8_t atomisp_css2_hw_load_8(hrt_address addr)
{
        struct atomisp_device *isp = dev_get_drvdata(atomisp_dev);
        unsigned long flags;
        u8 ret;

        spin_lock_irqsave(&mmio_lock, flags);
        ret = readb(isp->base + (addr & 0x003FFFFF));
        spin_unlock_irqrestore(&mmio_lock, flags);
        return ret;
}

static uint16_t atomisp_css2_hw_load_16(hrt_address addr)
{
        struct atomisp_device *isp = dev_get_drvdata(atomisp_dev);
        unsigned long flags;
        u16 ret;

        spin_lock_irqsave(&mmio_lock, flags);
        ret = readw(isp->base + (addr & 0x003FFFFF));
        spin_unlock_irqrestore(&mmio_lock, flags);
        return ret;
}

static uint32_t atomisp_css2_hw_load_32(hrt_address addr)
{
        struct atomisp_device *isp = dev_get_drvdata(atomisp_dev);
        unsigned long flags;
        u32 ret;

        spin_lock_irqsave(&mmio_lock, flags);
        ret = readl(isp->base + (addr & 0x003FFFFF));
        spin_unlock_irqrestore(&mmio_lock, flags);
        return ret;
}

static void atomisp_css2_hw_store(hrt_address addr, const void *from, uint32_t n)
{
        struct atomisp_device *isp = dev_get_drvdata(atomisp_dev);
        unsigned long flags;
        unsigned int i;

        addr &= 0x003FFFFF;
        spin_lock_irqsave(&mmio_lock, flags);
        for (i = 0; i < n; i++, from++)
                writeb(*(s8 *)from, isp->base + addr + i);

        spin_unlock_irqrestore(&mmio_lock, flags);
}

static void atomisp_css2_hw_load(hrt_address addr, void *to, uint32_t n)
{
        struct atomisp_device *isp = dev_get_drvdata(atomisp_dev);
        unsigned long flags;
        unsigned int i;

        addr &= 0x003FFFFF;
        spin_lock_irqsave(&mmio_lock, flags);
        for (i = 0; i < n; i++, to++)
                *(s8 *)to = readb(isp->base + addr + i);
        spin_unlock_irqrestore(&mmio_lock, flags);
}

static int  __printf(1, 0) atomisp_vprintk(const char *fmt, va_list args)
{
        vprintk(fmt, args);
        return 0;
}

void atomisp_load_uint32(hrt_address addr, uint32_t *data)
{
        *data = atomisp_css2_hw_load_32(addr);
}

static int hmm_get_mmu_base_addr(struct device *dev, unsigned int *mmu_base_addr)
{
        if (!sh_mmu_mrfld.get_pd_base) {
                dev_err(dev, "get mmu base address failed.\n");
                return -EINVAL;
        }

        *mmu_base_addr = sh_mmu_mrfld.get_pd_base(&bo_device.mmu,
                         bo_device.mmu.base_address);
        return 0;
}

static void __dump_pipe_config(struct atomisp_sub_device *asd,
                               struct atomisp_stream_env *stream_env,
                               unsigned int pipe_id)
{
        struct atomisp_device *isp = asd->isp;

        if (stream_env->pipes[pipe_id]) {
                struct ia_css_pipe_config *p_config;
                struct ia_css_pipe_extra_config *pe_config;

                p_config = &stream_env->pipe_configs[pipe_id];
                pe_config = &stream_env->pipe_extra_configs[pipe_id];
                dev_dbg(isp->dev, "dumping pipe[%d] config:\n", pipe_id);
                dev_dbg(isp->dev,
                        "pipe_config.pipe_mode:%d.\n", p_config->mode);
                dev_dbg(isp->dev,
                        "pipe_config.output_info[0] w=%d, h=%d.\n",
                        p_config->output_info[0].res.width,
                        p_config->output_info[0].res.height);
                dev_dbg(isp->dev,
                        "pipe_config.vf_pp_in_res w=%d, h=%d.\n",
                        p_config->vf_pp_in_res.width,
                        p_config->vf_pp_in_res.height);
                dev_dbg(isp->dev,
                        "pipe_config.capt_pp_in_res w=%d, h=%d.\n",
                        p_config->capt_pp_in_res.width,
                        p_config->capt_pp_in_res.height);
                dev_dbg(isp->dev,
                        "pipe_config.output.padded w=%d.\n",
                        p_config->output_info[0].padded_width);
                dev_dbg(isp->dev,
                        "pipe_config.vf_output_info[0] w=%d, h=%d.\n",
                        p_config->vf_output_info[0].res.width,
                        p_config->vf_output_info[0].res.height);
                dev_dbg(isp->dev,
                        "pipe_config.bayer_ds_out_res w=%d, h=%d.\n",
                        p_config->bayer_ds_out_res.width,
                        p_config->bayer_ds_out_res.height);
                dev_dbg(isp->dev,
                        "pipe_config.envelope w=%d, h=%d.\n",
                        p_config->dvs_envelope.width,
                        p_config->dvs_envelope.height);
                dev_dbg(isp->dev,
                        "pipe_config.dvs_frame_delay=%d.\n",
                        p_config->dvs_frame_delay);
                dev_dbg(isp->dev,
                        "pipe_config.isp_pipe_version:%d.\n",
                        p_config->isp_pipe_version);
                dev_dbg(isp->dev,
                        "pipe_config.default_capture_config.capture_mode=%d.\n",
                        p_config->default_capture_config.mode);
                dev_dbg(isp->dev,
                        "pipe_config.enable_dz=%d.\n",
                        p_config->enable_dz);
                dev_dbg(isp->dev,
                        "pipe_config.default_capture_config.enable_xnr=%d.\n",
                        p_config->default_capture_config.enable_xnr);
                dev_dbg(isp->dev,
                        "dumping pipe[%d] extra config:\n", pipe_id);
                dev_dbg(isp->dev,
                        "pipe_extra_config.enable_raw_binning:%d.\n",
                        pe_config->enable_raw_binning);
                dev_dbg(isp->dev,
                        "pipe_extra_config.enable_yuv_ds:%d.\n",
                        pe_config->enable_yuv_ds);
                dev_dbg(isp->dev,
                        "pipe_extra_config.enable_high_speed:%d.\n",
                        pe_config->enable_high_speed);
                dev_dbg(isp->dev,
                        "pipe_extra_config.enable_dvs_6axis:%d.\n",
                        pe_config->enable_dvs_6axis);
                dev_dbg(isp->dev,
                        "pipe_extra_config.enable_reduced_pipe:%d.\n",
                        pe_config->enable_reduced_pipe);
                dev_dbg(isp->dev,
                        "pipe_(extra_)config.enable_dz:%d.\n",
                        p_config->enable_dz);
                dev_dbg(isp->dev,
                        "pipe_extra_config.disable_vf_pp:%d.\n",
                        pe_config->disable_vf_pp);
        }
}

static void __dump_stream_config(struct atomisp_sub_device *asd,
                                 struct atomisp_stream_env *stream_env)
{
        struct atomisp_device *isp = asd->isp;
        struct ia_css_stream_config *s_config;
        int j;
        bool valid_stream = false;

        for (j = 0; j < IA_CSS_PIPE_ID_NUM; j++) {
                if (stream_env->pipes[j]) {
                        __dump_pipe_config(asd, stream_env, j);
                        valid_stream = true;
                }
        }
        if (!valid_stream)
                return;
        s_config = &stream_env->stream_config;
        dev_dbg(isp->dev, "stream_config.mode=%d.\n", s_config->mode);

        if (s_config->mode == IA_CSS_INPUT_MODE_SENSOR ||
            s_config->mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) {
                dev_dbg(isp->dev, "stream_config.source.port.port=%d.\n",
                        s_config->source.port.port);
                dev_dbg(isp->dev, "stream_config.source.port.num_lanes=%d.\n",
                        s_config->source.port.num_lanes);
                dev_dbg(isp->dev, "stream_config.source.port.timeout=%d.\n",
                        s_config->source.port.timeout);
                dev_dbg(isp->dev, "stream_config.source.port.rxcount=0x%x.\n",
                        s_config->source.port.rxcount);
                dev_dbg(isp->dev, "stream_config.source.port.compression.type=%d.\n",
                        s_config->source.port.compression.type);
                dev_dbg(isp->dev,
                        "stream_config.source.port.compression.compressed_bits_per_pixel=%d.\n",
                        s_config->source.port.compression.
                        compressed_bits_per_pixel);
                dev_dbg(isp->dev,
                        "stream_config.source.port.compression.uncompressed_bits_per_pixel=%d.\n",
                        s_config->source.port.compression.
                        uncompressed_bits_per_pixel);
        } else if (s_config->mode == IA_CSS_INPUT_MODE_PRBS) {
                dev_dbg(isp->dev, "stream_config.source.prbs.id=%d.\n",
                        s_config->source.prbs.id);
                dev_dbg(isp->dev, "stream_config.source.prbs.h_blank=%d.\n",
                        s_config->source.prbs.h_blank);
                dev_dbg(isp->dev, "stream_config.source.prbs.v_blank=%d.\n",
                        s_config->source.prbs.v_blank);
                dev_dbg(isp->dev, "stream_config.source.prbs.seed=%d.\n",
                        s_config->source.prbs.seed);
                dev_dbg(isp->dev, "stream_config.source.prbs.seed1=%d.\n",
                        s_config->source.prbs.seed1);
        }

        for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) {
                dev_dbg(isp->dev, "stream_configisys_config[%d].input_res w=%d, h=%d.\n",
                        j,
                        s_config->isys_config[j].input_res.width,
                        s_config->isys_config[j].input_res.height);

                dev_dbg(isp->dev, "stream_configisys_config[%d].linked_isys_stream_id=%d\n",
                        j,
                        s_config->isys_config[j].linked_isys_stream_id);

                dev_dbg(isp->dev, "stream_configisys_config[%d].format=%d\n",
                        j,
                        s_config->isys_config[j].format);

                dev_dbg(isp->dev, "stream_configisys_config[%d].valid=%d.\n",
                        j,
                        s_config->isys_config[j].valid);
        }

        dev_dbg(isp->dev, "stream_config.input_config.input_res w=%d, h=%d.\n",
                s_config->input_config.input_res.width,
                s_config->input_config.input_res.height);

        dev_dbg(isp->dev, "stream_config.input_config.effective_res w=%d, h=%d.\n",
                s_config->input_config.effective_res.width,
                s_config->input_config.effective_res.height);

        dev_dbg(isp->dev, "stream_config.input_config.format=%d\n",
                s_config->input_config.format);

        dev_dbg(isp->dev, "stream_config.input_config.bayer_order=%d.\n",
                s_config->input_config.bayer_order);

        dev_dbg(isp->dev, "stream_config.pixels_per_clock=%d.\n",
                s_config->pixels_per_clock);
        dev_dbg(isp->dev, "stream_config.online=%d.\n", s_config->online);
        dev_dbg(isp->dev, "stream_config.continuous=%d.\n",
                s_config->continuous);
        dev_dbg(isp->dev, "stream_config.disable_cont_viewfinder=%d.\n",
                s_config->disable_cont_viewfinder);
        dev_dbg(isp->dev, "stream_config.channel_id=%d.\n",
                s_config->channel_id);
        dev_dbg(isp->dev, "stream_config.init_num_cont_raw_buf=%d.\n",
                s_config->init_num_cont_raw_buf);
        dev_dbg(isp->dev, "stream_config.target_num_cont_raw_buf=%d.\n",
                s_config->target_num_cont_raw_buf);
        dev_dbg(isp->dev, "stream_config.left_padding=%d.\n",
                s_config->left_padding);
        dev_dbg(isp->dev, "stream_config.sensor_binning_factor=%d.\n",
                s_config->sensor_binning_factor);
        dev_dbg(isp->dev, "stream_config.pixels_per_clock=%d.\n",
                s_config->pixels_per_clock);
        dev_dbg(isp->dev, "stream_config.pack_raw_pixels=%d.\n",
                s_config->pack_raw_pixels);
        dev_dbg(isp->dev, "stream_config.flash_gpio_pin=%d.\n",
                s_config->flash_gpio_pin);
        dev_dbg(isp->dev, "stream_config.mipi_buffer_config.size_mem_words=%d.\n",
                s_config->mipi_buffer_config.size_mem_words);
        dev_dbg(isp->dev, "stream_config.mipi_buffer_config.contiguous=%d.\n",
                s_config->mipi_buffer_config.contiguous);
        dev_dbg(isp->dev, "stream_config.metadata_config.data_type=%d.\n",
                s_config->metadata_config.data_type);
        dev_dbg(isp->dev, "stream_config.metadata_config.resolution w=%d, h=%d.\n",
                s_config->metadata_config.resolution.width,
                s_config->metadata_config.resolution.height);
}

static int __destroy_stream(struct atomisp_sub_device *asd,
                            struct atomisp_stream_env *stream_env)
{
        struct atomisp_device *isp = asd->isp;
        unsigned long timeout;

        if (!stream_env->stream)
                return 0;

        if (stream_env->stream_state == CSS_STREAM_STARTED
            && ia_css_stream_stop(stream_env->stream) != 0) {
                dev_err(isp->dev, "stop stream failed.\n");
                return -EINVAL;
        }

        if (stream_env->stream_state == CSS_STREAM_STARTED) {
                timeout = jiffies + msecs_to_jiffies(200);
                while (1) {
                        if (ia_css_stream_has_stopped(stream_env->stream))
                                break;

                        if (time_after(jiffies, timeout)) {
                                dev_warn(isp->dev, "stop stream timeout.\n");
                                break;
                        }

                        usleep_range(100, 200);
                }
        }

        stream_env->stream_state = CSS_STREAM_STOPPED;

        if (ia_css_stream_destroy(stream_env->stream)) {
                dev_err(isp->dev, "destroy stream failed.\n");
                return -EINVAL;
        }
        stream_env->stream_state = CSS_STREAM_UNINIT;
        stream_env->stream = NULL;

        return 0;
}

static int __destroy_streams(struct atomisp_sub_device *asd)
{
        int ret, i;

        for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) {
                ret = __destroy_stream(asd, &asd->stream_env[i]);
                if (ret)
                        return ret;
        }
        asd->stream_prepared = false;
        return 0;
}

static int __create_stream(struct atomisp_sub_device *asd,
                           struct atomisp_stream_env *stream_env)
{
        int pipe_index = 0, i;
        struct ia_css_pipe *multi_pipes[IA_CSS_PIPE_ID_NUM];

        for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) {
                if (stream_env->pipes[i])
                        multi_pipes[pipe_index++] = stream_env->pipes[i];
        }
        if (pipe_index == 0)
                return 0;

        stream_env->stream_config.target_num_cont_raw_buf =
            asd->continuous_raw_buffer_size->val;
        stream_env->stream_config.channel_id = stream_env->ch_id;
        stream_env->stream_config.ia_css_enable_raw_buffer_locking =
            asd->enable_raw_buffer_lock->val;

        __dump_stream_config(asd, stream_env);
        if (ia_css_stream_create(&stream_env->stream_config,
                                 pipe_index, multi_pipes, &stream_env->stream) != 0)
                return -EINVAL;
        if (ia_css_stream_get_info(stream_env->stream,
                                   &stream_env->stream_info) != 0) {
                ia_css_stream_destroy(stream_env->stream);
                stream_env->stream = NULL;
                return -EINVAL;
        }

        stream_env->stream_state = CSS_STREAM_CREATED;
        return 0;
}

static int __create_streams(struct atomisp_sub_device *asd)
{
        int ret, i;

        for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) {
                ret = __create_stream(asd, &asd->stream_env[i]);
                if (ret)
                        goto rollback;
        }
        asd->stream_prepared = true;
        return 0;
rollback:
        for (i--; i >= 0; i--)
                __destroy_stream(asd, &asd->stream_env[i]);
        return ret;
}

static int __destroy_stream_pipes(struct atomisp_sub_device *asd,
                                  struct atomisp_stream_env *stream_env)
{
        struct atomisp_device *isp = asd->isp;
        int ret = 0;
        int i;

        for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) {
                if (!stream_env->pipes[i])
                        continue;
                if (ia_css_pipe_destroy(stream_env->pipes[i])
                    != 0) {
                        dev_err(isp->dev,
                                "destroy pipe[%d]failed.cannot recover.\n", i);
                        ret = -EINVAL;
                }
                stream_env->pipes[i] = NULL;
                stream_env->update_pipe[i] = false;
        }
        return ret;
}

static int __destroy_pipes(struct atomisp_sub_device *asd)
{
        struct atomisp_device *isp = asd->isp;
        int i;
        int ret = 0;

        for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) {
                if (asd->stream_env[i].stream) {
                        dev_err(isp->dev,
                                "cannot destroy css pipes for stream[%d].\n",
                                i);
                        continue;
                }

                ret = __destroy_stream_pipes(asd, &asd->stream_env[i]);
                if (ret)
                        return ret;
        }

        return 0;
}

void atomisp_destroy_pipes_stream(struct atomisp_sub_device *asd)
{
        if (__destroy_streams(asd))
                dev_warn(asd->isp->dev, "destroy stream failed.\n");

        if (__destroy_pipes(asd))
                dev_warn(asd->isp->dev, "destroy pipe failed.\n");
}

static void __apply_additional_pipe_config(
    struct atomisp_sub_device *asd,
    struct atomisp_stream_env *stream_env,
    enum ia_css_pipe_id pipe_id)
{
        struct atomisp_device *isp = asd->isp;

        if (pipe_id < 0 || pipe_id >= IA_CSS_PIPE_ID_NUM) {
                dev_err(isp->dev,
                        "wrong pipe_id for additional pipe config.\n");
                return;
        }

        /* apply default pipe config */
        stream_env->pipe_configs[pipe_id].isp_pipe_version = 2;
        stream_env->pipe_configs[pipe_id].enable_dz =
            asd->disable_dz->val ? false : true;
        /* apply isp 2.2 specific config for baytrail*/
        switch (pipe_id) {
        case IA_CSS_PIPE_ID_CAPTURE:
                /* enable capture pp/dz manually or digital zoom would
                 * fail*/
                if (stream_env->pipe_configs[pipe_id].
                    default_capture_config.mode == IA_CSS_CAPTURE_MODE_RAW)
                        stream_env->pipe_configs[pipe_id].enable_dz = false;
                break;
        case IA_CSS_PIPE_ID_VIDEO:
                /* enable reduced pipe to have binary
                 * video_dz_2_min selected*/
                stream_env->pipe_extra_configs[pipe_id]
                .enable_reduced_pipe = true;
                stream_env->pipe_configs[pipe_id]
                .enable_dz = false;

                if (asd->params.video_dis_en) {
                        stream_env->pipe_extra_configs[pipe_id]
                        .enable_dvs_6axis = true;
                        stream_env->pipe_configs[pipe_id]
                        .dvs_frame_delay =
                            ATOMISP_CSS2_NUM_DVS_FRAME_DELAY;
                }
                break;
        case IA_CSS_PIPE_ID_PREVIEW:
                break;
        case IA_CSS_PIPE_ID_YUVPP:
        case IA_CSS_PIPE_ID_COPY:
                stream_env->pipe_configs[pipe_id].enable_dz = false;
                break;
        default:
                break;
        }
}

static bool is_pipe_valid_to_current_run_mode(struct atomisp_sub_device *asd,
        enum ia_css_pipe_id pipe_id)
{
        if (pipe_id == IA_CSS_PIPE_ID_YUVPP)
                return true;

        if (asd->vfpp) {
                if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) {
                        if (pipe_id == IA_CSS_PIPE_ID_VIDEO)
                                return true;
                        else
                                return false;
                } else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) {
                        if (pipe_id == IA_CSS_PIPE_ID_CAPTURE)
                                return true;
                        else
                                return false;
                }
        }

        if (!asd->run_mode)
                return false;

        if (asd->copy_mode && pipe_id == IA_CSS_PIPE_ID_COPY)
                return true;

        switch (asd->run_mode->val) {
        case ATOMISP_RUN_MODE_STILL_CAPTURE:
                if (pipe_id == IA_CSS_PIPE_ID_CAPTURE)
                        return true;

                return false;
        case ATOMISP_RUN_MODE_PREVIEW:
                if (pipe_id == IA_CSS_PIPE_ID_PREVIEW)
                        return true;

                return false;
        case ATOMISP_RUN_MODE_VIDEO:
                if (pipe_id == IA_CSS_PIPE_ID_VIDEO || pipe_id == IA_CSS_PIPE_ID_YUVPP)
                        return true;

                return false;
        }

        return false;
}

static int __create_pipe(struct atomisp_sub_device *asd,
                         struct atomisp_stream_env *stream_env,
                         enum ia_css_pipe_id pipe_id)
{
        struct atomisp_device *isp = asd->isp;
        struct ia_css_pipe_extra_config extra_config;
        int ret;

        if (pipe_id >= IA_CSS_PIPE_ID_NUM)
                return -EINVAL;

        if (!stream_env->pipe_configs[pipe_id].output_info[0].res.width)
                return 0;

        if (!is_pipe_valid_to_current_run_mode(asd, pipe_id))
                return 0;

        ia_css_pipe_extra_config_defaults(&extra_config);

        __apply_additional_pipe_config(asd, stream_env, pipe_id);
        if (!memcmp(&extra_config,
                    &stream_env->pipe_extra_configs[pipe_id],
                    sizeof(extra_config)))
                ret = ia_css_pipe_create(
                          &stream_env->pipe_configs[pipe_id],
                          &stream_env->pipes[pipe_id]);
        else
                ret = ia_css_pipe_create_extra(
                          &stream_env->pipe_configs[pipe_id],
                          &stream_env->pipe_extra_configs[pipe_id],
                          &stream_env->pipes[pipe_id]);
        if (ret)
                dev_err(isp->dev, "create pipe[%d] error.\n", pipe_id);
        return ret;
}

static int __create_pipes(struct atomisp_sub_device *asd)
{
        int ret;
        int i, j;

        for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) {
                for (j = 0; j < IA_CSS_PIPE_ID_NUM; j++) {
                        ret = __create_pipe(asd, &asd->stream_env[i], j);
                        if (ret)
                                break;
                }
                if (j < IA_CSS_PIPE_ID_NUM)
                        goto pipe_err;
        }
        return 0;
pipe_err:
        for (; i >= 0; i--) {
                for (j--; j >= 0; j--) {
                        if (asd->stream_env[i].pipes[j]) {
                                ia_css_pipe_destroy(asd->stream_env[i].pipes[j]);
                                asd->stream_env[i].pipes[j] = NULL;
                        }
                }
                j = IA_CSS_PIPE_ID_NUM;
        }
        return -EINVAL;
}

int atomisp_create_pipes_stream(struct atomisp_sub_device *asd)
{
        int ret;

        ret = __create_pipes(asd);
        if (ret) {
                dev_err(asd->isp->dev, "create pipe failed %d.\n", ret);
                return ret;
        }

        ret = __create_streams(asd);
        if (ret) {
                dev_warn(asd->isp->dev, "create stream failed %d.\n", ret);
                __destroy_pipes(asd);
                return ret;
        }

        return 0;
}

int atomisp_css_update_stream(struct atomisp_sub_device *asd)
{
        atomisp_destroy_pipes_stream(asd);
        return atomisp_create_pipes_stream(asd);
}

int atomisp_css_init(struct atomisp_device *isp)
{
        unsigned int mmu_base_addr;
        int ret;
        int err;

        ret = hmm_get_mmu_base_addr(isp->dev, &mmu_base_addr);
        if (ret)
                return ret;

        /* Init ISP */
        err = ia_css_init(isp->dev, &isp->css_env.isp_css_env,
                          (uint32_t)mmu_base_addr, IA_CSS_IRQ_TYPE_PULSE);
        if (err) {
                dev_err(isp->dev, "css init failed --- bad firmware?\n");
                return -EINVAL;
        }
        ia_css_enable_isys_event_queue(true);

        isp->css_initialized = true;
        dev_dbg(isp->dev, "sh_css_init success\n");

        return 0;
}

static inline int __set_css_print_env(struct atomisp_device *isp, int opt)
{
        int ret = 0;

        if (opt == 0)
                isp->css_env.isp_css_env.print_env.debug_print = NULL;
        else if (opt == 1)
                isp->css_env.isp_css_env.print_env.debug_print = atomisp_vprintk;
        else
                ret = -EINVAL;

        return ret;
}

int atomisp_css_load_firmware(struct atomisp_device *isp)
{
        int err;

        /* set css env */
        isp->css_env.isp_css_fw.data = (void *)isp->firmware->data;
        isp->css_env.isp_css_fw.bytes = isp->firmware->size;

        isp->css_env.isp_css_env.hw_access_env.store_8 =
            atomisp_css2_hw_store_8;
        isp->css_env.isp_css_env.hw_access_env.store_16 =
            atomisp_css2_hw_store_16;
        isp->css_env.isp_css_env.hw_access_env.store_32 =
            atomisp_css2_hw_store_32;

        isp->css_env.isp_css_env.hw_access_env.load_8 = atomisp_css2_hw_load_8;
        isp->css_env.isp_css_env.hw_access_env.load_16 =
            atomisp_css2_hw_load_16;
        isp->css_env.isp_css_env.hw_access_env.load_32 =
            atomisp_css2_hw_load_32;

        isp->css_env.isp_css_env.hw_access_env.load = atomisp_css2_hw_load;
        isp->css_env.isp_css_env.hw_access_env.store = atomisp_css2_hw_store;

        __set_css_print_env(isp, dbg_func);

        isp->css_env.isp_css_env.print_env.error_print = atomisp_vprintk;

        /* load isp fw into ISP memory */
        err = ia_css_load_firmware(isp->dev, &isp->css_env.isp_css_env,
                                   &isp->css_env.isp_css_fw);
        if (err) {
                dev_err(isp->dev, "css load fw failed.\n");
                return -EINVAL;
        }

        return 0;
}

void atomisp_css_uninit(struct atomisp_device *isp)
{
        isp->css_initialized = false;
        ia_css_uninit();
}

int atomisp_css_irq_translate(struct atomisp_device *isp,
                              unsigned int *infos)
{
        int err;

        err = ia_css_irq_translate(infos);
        if (err) {
                dev_warn(isp->dev,
                         "%s:failed to translate irq (err = %d,infos = %d)\n",
                         __func__, err, *infos);
                return -EINVAL;
        }

        return 0;
}

void atomisp_css_rx_get_irq_info(enum mipi_port_id port,
                                 unsigned int *infos)
{
        if (IS_ISP2401)
                *infos = 0;
        else
                ia_css_isys_rx_get_irq_info(port, infos);
}

void atomisp_css_rx_clear_irq_info(enum mipi_port_id port,
                                   unsigned int infos)
{
        if (!IS_ISP2401)
                ia_css_isys_rx_clear_irq_info(port, infos);
}

int atomisp_css_irq_enable(struct atomisp_device *isp,
                           enum ia_css_irq_info info, bool enable)
{
        dev_dbg(isp->dev, "%s: css irq info 0x%08x: %s (%d).\n",
                __func__, info,
                str_enable_disable(enable), enable);
        if (ia_css_irq_enable(info, enable)) {
                dev_warn(isp->dev, "%s:Invalid irq info: 0x%08x when %s.\n",
                         __func__, info,
                         enable ? "enabling" : "disabling");
                return -EINVAL;
        }

        return 0;
}

void atomisp_css_init_struct(struct atomisp_sub_device *asd)
{
        int i, j;

        for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) {
                asd->stream_env[i].stream = NULL;
                for (j = 0; j < IA_CSS_PIPE_MODE_NUM; j++) {
                        asd->stream_env[i].pipes[j] = NULL;
                        asd->stream_env[i].update_pipe[j] = false;
                        ia_css_pipe_config_defaults(
                            &asd->stream_env[i].pipe_configs[j]);
                        ia_css_pipe_extra_config_defaults(
                            &asd->stream_env[i].pipe_extra_configs[j]);
                }
                ia_css_stream_config_defaults(&asd->stream_env[i].stream_config);
        }
}

int atomisp_q_video_buffer_to_css(struct atomisp_sub_device *asd,
                                  struct ia_css_frame *frame,
                                  enum atomisp_input_stream_id stream_id,
                                  enum ia_css_buffer_type css_buf_type,
                                  enum ia_css_pipe_id css_pipe_id)
{
        struct atomisp_stream_env *stream_env = &asd->stream_env[stream_id];
        struct ia_css_buffer css_buf = {0};
        int err;

        css_buf.type = css_buf_type;
        css_buf.data.frame = frame;

        err = ia_css_pipe_enqueue_buffer(
                  stream_env->pipes[css_pipe_id], &css_buf);
        if (err)
                return -EINVAL;

        return 0;
}

int atomisp_q_metadata_buffer_to_css(struct atomisp_sub_device *asd,
                                     struct atomisp_metadata_buf *metadata_buf,
                                     enum atomisp_input_stream_id stream_id,
                                     enum ia_css_pipe_id css_pipe_id)
{
        struct atomisp_stream_env *stream_env = &asd->stream_env[stream_id];
        struct ia_css_buffer buffer = {0};
        struct atomisp_device *isp = asd->isp;

        buffer.type = IA_CSS_BUFFER_TYPE_METADATA;
        buffer.data.metadata = metadata_buf->metadata;
        if (ia_css_pipe_enqueue_buffer(stream_env->pipes[css_pipe_id],
                                       &buffer)) {
                dev_err(isp->dev, "failed to q meta data buffer\n");
                return -EINVAL;
        }

        return 0;
}

int atomisp_q_s3a_buffer_to_css(struct atomisp_sub_device *asd,
                                struct atomisp_s3a_buf *s3a_buf,
                                enum atomisp_input_stream_id stream_id,
                                enum ia_css_pipe_id css_pipe_id)
{
        struct atomisp_stream_env *stream_env = &asd->stream_env[stream_id];
        struct ia_css_buffer buffer = {0};
        struct atomisp_device *isp = asd->isp;

        buffer.type = IA_CSS_BUFFER_TYPE_3A_STATISTICS;
        buffer.data.stats_3a = s3a_buf->s3a_data;
        if (ia_css_pipe_enqueue_buffer(
                stream_env->pipes[css_pipe_id],
                &buffer)) {
                dev_dbg(isp->dev, "failed to q s3a stat buffer\n");
                return -EINVAL;
        }

        return 0;
}

int atomisp_q_dis_buffer_to_css(struct atomisp_sub_device *asd,
                                struct atomisp_dis_buf *dis_buf,
                                enum atomisp_input_stream_id stream_id,
                                enum ia_css_pipe_id css_pipe_id)
{
        struct atomisp_stream_env *stream_env = &asd->stream_env[stream_id];
        struct ia_css_buffer buffer = {0};
        struct atomisp_device *isp = asd->isp;

        buffer.type = IA_CSS_BUFFER_TYPE_DIS_STATISTICS;
        buffer.data.stats_dvs = dis_buf->dis_data;
        if (ia_css_pipe_enqueue_buffer(
                stream_env->pipes[css_pipe_id],
                &buffer)) {
                dev_dbg(isp->dev, "failed to q dvs stat buffer\n");
                return -EINVAL;
        }

        return 0;
}

int atomisp_css_start(struct atomisp_sub_device *asd)
{
        struct atomisp_device *isp = asd->isp;
        bool sp_is_started = false;
        int ret = 0, i = 0;

        if (!sh_css_hrt_system_is_idle())
                dev_err(isp->dev, "CSS HW not idle before starting SP\n");

        if (ia_css_start_sp()) {
                dev_err(isp->dev, "start sp error.\n");
                ret = -EINVAL;
                goto start_err;
        }

        sp_is_started = true;

        for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) {
                if (asd->stream_env[i].stream) {
                        if (ia_css_stream_start(asd->stream_env[i]
                                                .stream) != 0) {
                                dev_err(isp->dev, "stream[%d] start error.\n", i);
                                ret = -EINVAL;
                                goto start_err;
                        } else {
                                asd->stream_env[i].stream_state = CSS_STREAM_STARTED;
                                dev_dbg(isp->dev, "stream[%d] started.\n", i);
                        }
                }
        }

        return 0;

start_err:
        /*
         * CSS 2.0 API limitation: ia_css_stop_sp() can only be called after
         * destroying all pipes.
         */
        if (sp_is_started) {
                atomisp_destroy_pipes_stream(asd);
                ia_css_stop_sp();
                atomisp_create_pipes_stream(asd);
        }

        return ret;
}

void atomisp_css_update_isp_params(struct atomisp_sub_device *asd)
{
        /*
         * FIXME!
         * for ISP2401 new input system, this api is under development.
         * Calling it would cause kernel panic.
         *
         * VIED BZ: 1458
         *
         * Check if it is Cherry Trail and also new input system
         */
        if (asd->copy_mode) {
                dev_warn(asd->isp->dev,
                         "%s: ia_css_stream_set_isp_config() not supported in copy mode!.\n",
                         __func__);
                return;
        }

        ia_css_stream_set_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &asd->params.config);
        memset(&asd->params.config, 0, sizeof(asd->params.config));
}

void atomisp_css_update_isp_params_on_pipe(struct atomisp_sub_device *asd,
        struct ia_css_pipe *pipe)
{
        int ret;

        if (!pipe) {
                atomisp_css_update_isp_params(asd);
                return;
        }

        dev_dbg(asd->isp->dev,
                "%s: apply parameter for ia_css_frame %p with isp_config_id %d on pipe %p.\n",
                __func__, asd->params.config.output_frame,
                asd->params.config.isp_config_id, pipe);

        ret = ia_css_stream_set_isp_config_on_pipe(
                  asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
                  &asd->params.config, pipe);
        if (ret)
                dev_warn(asd->isp->dev, "%s: ia_css_stream_set_isp_config_on_pipe failed %d\n",
                         __func__, ret);
        memset(&asd->params.config, 0, sizeof(asd->params.config));
}

int atomisp_css_queue_buffer(struct atomisp_sub_device *asd,
                             enum atomisp_input_stream_id stream_id,
                             enum ia_css_pipe_id pipe_id,
                             enum ia_css_buffer_type buf_type,
                             struct atomisp_css_buffer *isp_css_buffer)
{
        if (ia_css_pipe_enqueue_buffer(
                asd->stream_env[stream_id].pipes[pipe_id],
                &isp_css_buffer->css_buffer)
            != 0)
                return -EINVAL;

        return 0;
}

int atomisp_css_dequeue_buffer(struct atomisp_sub_device *asd,
                               enum atomisp_input_stream_id stream_id,
                               enum ia_css_pipe_id pipe_id,
                               enum ia_css_buffer_type buf_type,
                               struct atomisp_css_buffer *isp_css_buffer)
{
        struct atomisp_device *isp = asd->isp;
        int err;

        err = ia_css_pipe_dequeue_buffer(
                  asd->stream_env[stream_id].pipes[pipe_id],
                  &isp_css_buffer->css_buffer);
        if (err) {
                dev_err(isp->dev,
                        "ia_css_pipe_dequeue_buffer failed: 0x%x\n", err);
                return -EINVAL;
        }

        return 0;
}

int atomisp_css_allocate_stat_buffers(struct atomisp_sub_device   *asd,
                                      u16 stream_id,
                                      struct atomisp_s3a_buf      *s3a_buf,
                                      struct atomisp_dis_buf      *dis_buf,
                                      struct atomisp_metadata_buf *md_buf)
{
        struct atomisp_device *isp = asd->isp;
        struct ia_css_dvs_grid_info *dvs_grid_info =
            atomisp_css_get_dvs_grid_info(&asd->params.curr_grid_info);

        if (s3a_buf && asd->params.curr_grid_info.s3a_grid.enable) {
                void *s3a_ptr;

                s3a_buf->s3a_data = ia_css_isp_3a_statistics_allocate(
                                        &asd->params.curr_grid_info.s3a_grid);
                if (!s3a_buf->s3a_data) {
                        dev_err(isp->dev, "3a buf allocation failed.\n");
                        return -EINVAL;
                }

                s3a_ptr = hmm_vmap(s3a_buf->s3a_data->data_ptr, true);
                s3a_buf->s3a_map = ia_css_isp_3a_statistics_map_allocate(
                                       s3a_buf->s3a_data, s3a_ptr);
        }

        if (dis_buf && dvs_grid_info && dvs_grid_info->enable) {
                void *dvs_ptr;

                dis_buf->dis_data = ia_css_isp_dvs2_statistics_allocate(
                                        dvs_grid_info);
                if (!dis_buf->dis_data) {
                        dev_err(isp->dev, "dvs buf allocation failed.\n");
                        if (s3a_buf) {
                                hmm_vunmap(s3a_buf->s3a_data->data_ptr);
                                ia_css_isp_3a_statistics_map_free(s3a_buf->s3a_map);
                                ia_css_isp_3a_statistics_free(s3a_buf->s3a_data);
                        }
                        return -EINVAL;
                }

                dvs_ptr = hmm_vmap(dis_buf->dis_data->data_ptr, true);
                dis_buf->dvs_map = ia_css_isp_dvs_statistics_map_allocate(
                                       dis_buf->dis_data, dvs_ptr);
        }

        if (asd->stream_env[stream_id].stream_info.
            metadata_info.size && md_buf) {
                md_buf->metadata = ia_css_metadata_allocate(
                                       &asd->stream_env[stream_id].stream_info.metadata_info);
                if (!md_buf->metadata) {
                        if (s3a_buf) {
                                hmm_vunmap(s3a_buf->s3a_data->data_ptr);
                                ia_css_isp_3a_statistics_map_free(s3a_buf->s3a_map);
                                ia_css_isp_3a_statistics_free(s3a_buf->s3a_data);
                        }
                        if (dis_buf) {
                                hmm_vunmap(dis_buf->dis_data->data_ptr);
                                ia_css_isp_dvs_statistics_map_free(dis_buf->dvs_map);
                                ia_css_isp_dvs2_statistics_free(dis_buf->dis_data);
                        }
                        dev_err(isp->dev, "metadata buf allocation failed.\n");
                        return -EINVAL;
                }
                md_buf->md_vptr = hmm_vmap(md_buf->metadata->address, false);
        }

        return 0;
}

void atomisp_css_free_3a_buffer(struct atomisp_s3a_buf *s3a_buf)
{
        if (s3a_buf->s3a_data)
                hmm_vunmap(s3a_buf->s3a_data->data_ptr);

        ia_css_isp_3a_statistics_map_free(s3a_buf->s3a_map);
        s3a_buf->s3a_map = NULL;
        ia_css_isp_3a_statistics_free(s3a_buf->s3a_data);
}

void atomisp_css_free_dis_buffer(struct atomisp_dis_buf *dis_buf)
{
        if (dis_buf->dis_data)
                hmm_vunmap(dis_buf->dis_data->data_ptr);

        ia_css_isp_dvs_statistics_map_free(dis_buf->dvs_map);
        dis_buf->dvs_map = NULL;
        ia_css_isp_dvs2_statistics_free(dis_buf->dis_data);
}

void atomisp_css_free_metadata_buffer(struct atomisp_metadata_buf *metadata_buf)
{
        if (metadata_buf->md_vptr) {
                hmm_vunmap(metadata_buf->metadata->address);
                metadata_buf->md_vptr = NULL;
        }
        ia_css_metadata_free(metadata_buf->metadata);
}

void atomisp_css_free_stat_buffers(struct atomisp_sub_device *asd)
{
        struct atomisp_s3a_buf *s3a_buf, *_s3a_buf;
        struct atomisp_dis_buf *dis_buf, *_dis_buf;
        struct atomisp_metadata_buf *md_buf, *_md_buf;
        struct ia_css_dvs_grid_info *dvs_grid_info =
            atomisp_css_get_dvs_grid_info(&asd->params.curr_grid_info);
        unsigned int i;

        /* 3A statistics use vmalloc, DIS use kmalloc */
        if (dvs_grid_info && dvs_grid_info->enable) {
                ia_css_dvs2_coefficients_free(asd->params.css_param.dvs2_coeff);
                ia_css_dvs2_statistics_free(asd->params.dvs_stat);
                asd->params.css_param.dvs2_coeff = NULL;
                asd->params.dvs_stat = NULL;
                asd->params.dvs_hor_proj_bytes = 0;
                asd->params.dvs_ver_proj_bytes = 0;
                asd->params.dvs_hor_coef_bytes = 0;
                asd->params.dvs_ver_coef_bytes = 0;
                asd->params.dis_proj_data_valid = false;
                list_for_each_entry_safe(dis_buf, _dis_buf,
                                         &asd->dis_stats, list) {
                        atomisp_css_free_dis_buffer(dis_buf);
                        list_del(&dis_buf->list);
                        kfree(dis_buf);
                }
                list_for_each_entry_safe(dis_buf, _dis_buf,
                                         &asd->dis_stats_in_css, list) {
                        atomisp_css_free_dis_buffer(dis_buf);
                        list_del(&dis_buf->list);
                        kfree(dis_buf);
                }
        }
        if (asd->params.curr_grid_info.s3a_grid.enable) {
                ia_css_3a_statistics_free(asd->params.s3a_user_stat);
                asd->params.s3a_user_stat = NULL;
                asd->params.s3a_output_bytes = 0;
                list_for_each_entry_safe(s3a_buf, _s3a_buf,
                                         &asd->s3a_stats, list) {
                        atomisp_css_free_3a_buffer(s3a_buf);
                        list_del(&s3a_buf->list);
                        kfree(s3a_buf);
                }
                list_for_each_entry_safe(s3a_buf, _s3a_buf,
                                         &asd->s3a_stats_in_css, list) {
                        atomisp_css_free_3a_buffer(s3a_buf);
                        list_del(&s3a_buf->list);
                        kfree(s3a_buf);
                }
                list_for_each_entry_safe(s3a_buf, _s3a_buf,
                                         &asd->s3a_stats_ready, list) {
                        atomisp_css_free_3a_buffer(s3a_buf);
                        list_del(&s3a_buf->list);
                        kfree(s3a_buf);
                }
        }

        if (asd->params.css_param.dvs_6axis) {
                ia_css_dvs2_6axis_config_free(asd->params.css_param.dvs_6axis);
                asd->params.css_param.dvs_6axis = NULL;
        }

        for (i = 0; i < ATOMISP_METADATA_TYPE_NUM; i++) {
                list_for_each_entry_safe(md_buf, _md_buf,
                                         &asd->metadata[i], list) {
                        atomisp_css_free_metadata_buffer(md_buf);
                        list_del(&md_buf->list);
                        kfree(md_buf);
                }
                list_for_each_entry_safe(md_buf, _md_buf,
                                         &asd->metadata_in_css[i], list) {
                        atomisp_css_free_metadata_buffer(md_buf);
                        list_del(&md_buf->list);
                        kfree(md_buf);
                }
                list_for_each_entry_safe(md_buf, _md_buf,
                                         &asd->metadata_ready[i], list) {
                        atomisp_css_free_metadata_buffer(md_buf);
                        list_del(&md_buf->list);
                        kfree(md_buf);
                }
        }
        asd->params.metadata_width_size = 0;
        atomisp_free_metadata_output_buf(asd);
}

int atomisp_css_get_grid_info(struct atomisp_sub_device *asd,
                              enum ia_css_pipe_id pipe_id)
{
        struct ia_css_pipe_info p_info;
        struct ia_css_grid_info old_info;
        struct atomisp_device *isp = asd->isp;
        int md_width = asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].
                       stream_config.metadata_config.resolution.width;

        memset(&p_info, 0, sizeof(struct ia_css_pipe_info));
        memset(&old_info, 0, sizeof(struct ia_css_grid_info));

        if (ia_css_pipe_get_info(
                asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].pipes[pipe_id],
                &p_info) != 0) {
                dev_err(isp->dev, "ia_css_pipe_get_info failed\n");
                return -EINVAL;
        }

        memcpy(&old_info, &asd->params.curr_grid_info,
               sizeof(struct ia_css_grid_info));
        memcpy(&asd->params.curr_grid_info, &p_info.grid_info,
               sizeof(struct ia_css_grid_info));
        /*
         * Record which css pipe enables s3a_grid.
         * Currently would have one css pipe that need it
         */
        if (asd->params.curr_grid_info.s3a_grid.enable) {
                if (asd->params.s3a_enabled_pipe != IA_CSS_PIPE_ID_NUM)
                        dev_dbg(isp->dev, "css pipe %d enabled s3a grid replaced by: %d.\n",
                                asd->params.s3a_enabled_pipe, pipe_id);
                asd->params.s3a_enabled_pipe = pipe_id;
        }

        /* If the grid info has not changed and the buffers for 3A and
         * DIS statistics buffers are allocated or buffer size would be zero
         * then no need to do anything. */
        if (((!memcmp(&old_info, &asd->params.curr_grid_info, sizeof(old_info))
              && asd->params.s3a_user_stat && asd->params.dvs_stat)
             || asd->params.curr_grid_info.s3a_grid.width == 0
             || asd->params.curr_grid_info.s3a_grid.height == 0)
            && asd->params.metadata_width_size == md_width) {
                dev_dbg(isp->dev,
                        "grid info change escape. memcmp=%d, s3a_user_stat=%d,dvs_stat=%d, s3a.width=%d, s3a.height=%d, metadata width =%d\n",
                        !memcmp(&old_info, &asd->params.curr_grid_info,
                                sizeof(old_info)),
                        !!asd->params.s3a_user_stat, !!asd->params.dvs_stat,
                        asd->params.curr_grid_info.s3a_grid.width,
                        asd->params.curr_grid_info.s3a_grid.height,
                        asd->params.metadata_width_size);
                return -EINVAL;
        }
        asd->params.metadata_width_size = md_width;

        return 0;
}

int atomisp_alloc_3a_output_buf(struct atomisp_sub_device *asd)
{
        if (!asd->params.curr_grid_info.s3a_grid.width ||
            !asd->params.curr_grid_info.s3a_grid.height)
                return 0;

        asd->params.s3a_user_stat = ia_css_3a_statistics_allocate(
                                        &asd->params.curr_grid_info.s3a_grid);
        if (!asd->params.s3a_user_stat)
                return -ENOMEM;
        /* 3A statistics. These can be big, so we use vmalloc. */
        asd->params.s3a_output_bytes =
            asd->params.curr_grid_info.s3a_grid.width *
            asd->params.curr_grid_info.s3a_grid.height *
            sizeof(*asd->params.s3a_user_stat->data);

        return 0;
}

int atomisp_alloc_dis_coef_buf(struct atomisp_sub_device *asd)
{
        struct ia_css_dvs_grid_info *dvs_grid =
            atomisp_css_get_dvs_grid_info(&asd->params.curr_grid_info);

        if (!dvs_grid)
                return 0;

        if (!dvs_grid->enable) {
                dev_dbg(asd->isp->dev, "%s: dvs_grid not enabled.\n", __func__);
                return 0;
        }

        /* DIS coefficients. */
        asd->params.css_param.dvs2_coeff = ia_css_dvs2_coefficients_allocate(
                                               dvs_grid);
        if (!asd->params.css_param.dvs2_coeff)
                return -ENOMEM;

        asd->params.dvs_hor_coef_bytes = dvs_grid->num_hor_coefs *
                                         sizeof(*asd->params.css_param.dvs2_coeff->hor_coefs.odd_real);

        asd->params.dvs_ver_coef_bytes = dvs_grid->num_ver_coefs *
                                         sizeof(*asd->params.css_param.dvs2_coeff->ver_coefs.odd_real);

        /* DIS projections. */
        asd->params.dis_proj_data_valid = false;
        asd->params.dvs_stat = ia_css_dvs2_statistics_allocate(dvs_grid);
        if (!asd->params.dvs_stat)
                return -ENOMEM;

        asd->params.dvs_hor_proj_bytes =
            dvs_grid->aligned_height * dvs_grid->aligned_width *
            sizeof(*asd->params.dvs_stat->hor_prod.odd_real);

        asd->params.dvs_ver_proj_bytes =
            dvs_grid->aligned_height * dvs_grid->aligned_width *
            sizeof(*asd->params.dvs_stat->ver_prod.odd_real);

        return 0;
}

int atomisp_alloc_metadata_output_buf(struct atomisp_sub_device *asd)
{
        int i;

        /* We allocate the cpu-side buffer used for communication with user
         * space */
        for (i = 0; i < ATOMISP_METADATA_TYPE_NUM; i++) {
                asd->params.metadata_user[i] = kvmalloc(
                                                   asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].
                                                   stream_info.metadata_info.size, GFP_KERNEL);
                if (!asd->params.metadata_user[i]) {
                        while (--i >= 0) {
                                kvfree(asd->params.metadata_user[i]);
                                asd->params.metadata_user[i] = NULL;
                        }
                        return -ENOMEM;
                }
        }

        return 0;
}

void atomisp_free_metadata_output_buf(struct atomisp_sub_device *asd)
{
        unsigned int i;

        for (i = 0; i < ATOMISP_METADATA_TYPE_NUM; i++) {
                if (asd->params.metadata_user[i]) {
                        kvfree(asd->params.metadata_user[i]);
                        asd->params.metadata_user[i] = NULL;
                }
        }
}

void atomisp_css_temp_pipe_to_pipe_id(struct atomisp_sub_device *asd,
                                      struct atomisp_css_event *current_event)
{
        /*
         * FIXME!
         * Pipe ID reported in CSS event is not correct for new system's
         * copy pipe.
         * VIED BZ: 1463
         */
        ia_css_temp_pipe_to_pipe_id(current_event->event.pipe,
                                    &current_event->pipe);
        if (asd && asd->copy_mode &&
            current_event->pipe == IA_CSS_PIPE_ID_CAPTURE)
                current_event->pipe = IA_CSS_PIPE_ID_COPY;
}

int atomisp_css_isys_set_resolution(struct atomisp_sub_device *asd,
                                    enum atomisp_input_stream_id stream_id,
                                    struct v4l2_mbus_framefmt *ffmt,
                                    int isys_stream)
{
        struct ia_css_stream_config *s_config =
                    &asd->stream_env[stream_id].stream_config;

        if (isys_stream >= IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH)
                return -EINVAL;

        s_config->isys_config[isys_stream].input_res.width = ffmt->width;
        s_config->isys_config[isys_stream].input_res.height = ffmt->height;
        return 0;
}

int atomisp_css_input_set_resolution(struct atomisp_sub_device *asd,
                                     enum atomisp_input_stream_id stream_id,
                                     struct v4l2_mbus_framefmt *ffmt)
{
        struct ia_css_stream_config *s_config =
                    &asd->stream_env[stream_id].stream_config;

        s_config->input_config.input_res.width = ffmt->width;
        s_config->input_config.input_res.height = ffmt->height;
        return 0;
}

void atomisp_css_input_set_binning_factor(struct atomisp_sub_device *asd,
        enum atomisp_input_stream_id stream_id,
        unsigned int bin_factor)
{
        asd->stream_env[stream_id]
        .stream_config.sensor_binning_factor = bin_factor;
}

void atomisp_css_input_set_bayer_order(struct atomisp_sub_device *asd,
                                       enum atomisp_input_stream_id stream_id,
                                       enum ia_css_bayer_order bayer_order)
{
        struct ia_css_stream_config *s_config =
                    &asd->stream_env[stream_id].stream_config;
        s_config->input_config.bayer_order = bayer_order;
}

void atomisp_css_isys_set_link(struct atomisp_sub_device *asd,
                               enum atomisp_input_stream_id stream_id,
                               int link,
                               int isys_stream)
{
        struct ia_css_stream_config *s_config =
                    &asd->stream_env[stream_id].stream_config;

        s_config->isys_config[isys_stream].linked_isys_stream_id = link;
}

void atomisp_css_isys_set_valid(struct atomisp_sub_device *asd,
                                enum atomisp_input_stream_id stream_id,
                                bool valid,
                                int isys_stream)
{
        struct ia_css_stream_config *s_config =
                    &asd->stream_env[stream_id].stream_config;

        s_config->isys_config[isys_stream].valid = valid;
}

void atomisp_css_isys_set_format(struct atomisp_sub_device *asd,
                                 enum atomisp_input_stream_id stream_id,
                                 enum atomisp_input_format format,
                                 int isys_stream)
{
        struct ia_css_stream_config *s_config =
                    &asd->stream_env[stream_id].stream_config;

        s_config->isys_config[isys_stream].format = format;
}

void atomisp_css_input_set_format(struct atomisp_sub_device *asd,
                                  enum atomisp_input_stream_id stream_id,
                                  enum atomisp_input_format format)
{
        struct ia_css_stream_config *s_config =
                    &asd->stream_env[stream_id].stream_config;

        s_config->input_config.format = format;
}

int atomisp_css_set_default_isys_config(struct atomisp_sub_device *asd,
                                        enum atomisp_input_stream_id stream_id,
                                        struct v4l2_mbus_framefmt *ffmt)
{
        int i;
        struct ia_css_stream_config *s_config =
                    &asd->stream_env[stream_id].stream_config;
        /*
         * Set all isys configs to not valid.
         * Currently we support only one stream per channel
         */
        for (i = IA_CSS_STREAM_ISYS_STREAM_0;
             i < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; i++)
                s_config->isys_config[i].valid = false;

        atomisp_css_isys_set_resolution(asd, stream_id, ffmt,
                                        IA_CSS_STREAM_DEFAULT_ISYS_STREAM_IDX);
        atomisp_css_isys_set_format(asd, stream_id,
                                    s_config->input_config.format,
                                    IA_CSS_STREAM_DEFAULT_ISYS_STREAM_IDX);
        atomisp_css_isys_set_link(asd, stream_id, NO_LINK,
                                  IA_CSS_STREAM_DEFAULT_ISYS_STREAM_IDX);
        atomisp_css_isys_set_valid(asd, stream_id, true,
                                   IA_CSS_STREAM_DEFAULT_ISYS_STREAM_IDX);

        return 0;
}

void atomisp_css_isys_two_stream_cfg_update_stream1(
    struct atomisp_sub_device *asd,
    enum atomisp_input_stream_id stream_id,
    enum atomisp_input_format input_format,
    unsigned int width, unsigned int height)
{
        struct ia_css_stream_config *s_config =
                    &asd->stream_env[stream_id].stream_config;

        s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_0].input_res.width =
            width;
        s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_0].input_res.height =
            height;
        s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_0].format =
            input_format;
        s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_0].valid = true;
}

void atomisp_css_isys_two_stream_cfg_update_stream2(
    struct atomisp_sub_device *asd,
    enum atomisp_input_stream_id stream_id,
    enum atomisp_input_format input_format,
    unsigned int width, unsigned int height)
{
        struct ia_css_stream_config *s_config =
                    &asd->stream_env[stream_id].stream_config;

        s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].input_res.width =
            width;
        s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].input_res.height =
            height;
        s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].linked_isys_stream_id
            = IA_CSS_STREAM_ISYS_STREAM_0;
        s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].format =
            input_format;
        s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].valid = true;
}

int atomisp_css_input_set_effective_resolution(
    struct atomisp_sub_device *asd,
    enum atomisp_input_stream_id stream_id,
    unsigned int width, unsigned int height)
{
        struct ia_css_stream_config *s_config =
                    &asd->stream_env[stream_id].stream_config;
        s_config->input_config.effective_res.width = width;
        s_config->input_config.effective_res.height = height;
        return 0;
}

void atomisp_css_video_set_dis_envelope(struct atomisp_sub_device *asd,
                                        unsigned int dvs_w, unsigned int dvs_h)
{
        asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
        .pipe_configs[IA_CSS_PIPE_ID_VIDEO].dvs_envelope.width = dvs_w;
        asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
        .pipe_configs[IA_CSS_PIPE_ID_VIDEO].dvs_envelope.height = dvs_h;
}

void atomisp_css_input_set_two_pixels_per_clock(
    struct atomisp_sub_device *asd,
    bool two_ppc)
{
        int i;

        if (asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
            .stream_config.pixels_per_clock == (two_ppc ? 2 : 1))
                return;

        asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
        .stream_config.pixels_per_clock = (two_ppc ? 2 : 1);
        for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++)
                asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
                .update_pipe[i] = true;
}

void atomisp_css_enable_dz(struct atomisp_sub_device *asd, bool enable)
{
        int i;

        for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++)
                asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
                .pipe_configs[i].enable_dz = enable;
}

void atomisp_css_capture_set_mode(struct atomisp_sub_device *asd,
                                  enum ia_css_capture_mode mode)
{
        struct atomisp_stream_env *stream_env =
                    &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL];

        if (stream_env->pipe_configs[IA_CSS_PIPE_ID_CAPTURE]
            .default_capture_config.mode == mode)
                return;

        stream_env->pipe_configs[IA_CSS_PIPE_ID_CAPTURE].
        default_capture_config.mode = mode;
        stream_env->update_pipe[IA_CSS_PIPE_ID_CAPTURE] = true;
}

void atomisp_css_input_set_mode(struct atomisp_sub_device *asd,
                                enum ia_css_input_mode mode)
{
        unsigned int size_mem_words;
        int i;

        for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++)
                asd->stream_env[i].stream_config.mode = mode;

        if (mode != IA_CSS_INPUT_MODE_BUFFERED_SENSOR)
                return;

        for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) {
                /*
                 * TODO: sensor needs to export the embedded_data_size_words
                 * information to atomisp for each setting.
                 * Here using a large safe value.
                 */
                struct ia_css_stream_config *s_config =
                            &asd->stream_env[i].stream_config;

                if (s_config->input_config.input_res.width == 0)
                        continue;

                if (ia_css_mipi_frame_calculate_size(
                        s_config->input_config.input_res.width,
                        s_config->input_config.input_res.height,
                        s_config->input_config.format,
                        true,
                        0x13000,
                        &size_mem_words) != 0) {
                        if (IS_MRFD)
                                size_mem_words = CSS_MIPI_FRAME_BUFFER_SIZE_2;
                        else
                                size_mem_words = CSS_MIPI_FRAME_BUFFER_SIZE_1;
                        dev_warn(asd->isp->dev,
                                 "ia_css_mipi_frame_calculate_size failed,applying pre-defined MIPI buffer size %u.\n",
                                 size_mem_words);
                }
                s_config->mipi_buffer_config.size_mem_words = size_mem_words;
                s_config->mipi_buffer_config.nof_mipi_buffers = 2;
        }
}

void atomisp_css_capture_enable_online(struct atomisp_sub_device *asd,
                                       unsigned short stream_index, bool enable)
{
        struct atomisp_stream_env *stream_env =
                    &asd->stream_env[stream_index];

        if (stream_env->stream_config.online == !!enable)
                return;

        stream_env->stream_config.online = !!enable;
        stream_env->update_pipe[IA_CSS_PIPE_ID_CAPTURE] = true;
}

void atomisp_css_preview_enable_online(struct atomisp_sub_device *asd,
                                       unsigned short stream_index, bool enable)
{
        struct atomisp_stream_env *stream_env =
                    &asd->stream_env[stream_index];
        int i;

        if (stream_env->stream_config.online != !!enable) {
                stream_env->stream_config.online = !!enable;
                for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++)
                        stream_env->update_pipe[i] = true;
        }
}

int atomisp_css_input_configure_port(
    struct atomisp_sub_device *asd,
    enum mipi_port_id port,
    unsigned int num_lanes,
    unsigned int timeout,
    unsigned int mipi_freq,
    enum atomisp_input_format metadata_format,
    unsigned int metadata_width,
    unsigned int metadata_height)
{
        int i;
        struct atomisp_stream_env *stream_env;
        /*
         * Calculate rx_count as follows:
         * Input: mipi_freq                 : CSI-2 bus frequency in Hz
         * UI = 1 / (2 * mipi_freq)         : period of one bit on the bus
         * min = 85e-9 + 6 * UI             : Limits for rx_count in seconds
         * max = 145e-9 + 10 * UI
         * rxcount0 = min / (4 / mipi_freq) : convert seconds to byte clocks
         * rxcount = rxcount0 - 2           : adjust for better results
         * The formula below is simplified version of the above with
         * 10-bit fixed points for improved accuracy.
         */
        const unsigned int rxcount =
            min(((mipi_freq / 46000) - 1280) >> 10, 0xffU) * 0x01010101U;

        for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) {
                stream_env = &asd->stream_env[i];
                stream_env->stream_config.source.port.port = port;
                stream_env->stream_config.source.port.num_lanes = num_lanes;
                stream_env->stream_config.source.port.timeout = timeout;
                if (mipi_freq)
                        stream_env->stream_config.source.port.rxcount = rxcount;
                stream_env->stream_config.
                metadata_config.data_type = metadata_format;
                stream_env->stream_config.
                metadata_config.resolution.width = metadata_width;
                stream_env->stream_config.
                metadata_config.resolution.height = metadata_height;
        }

        return 0;
}

void atomisp_css_stop(struct atomisp_sub_device *asd, bool in_reset)
{
        unsigned long irqflags;
        unsigned int i;

        /*
         * CSS 2.0 API limitation: ia_css_stop_sp() can only be called after
         * destroying all pipes.
         */
        atomisp_destroy_pipes_stream(asd);

        atomisp_init_raw_buffer_bitmap(asd);

        ia_css_stop_sp();

        if (!in_reset) {
                struct atomisp_stream_env *stream_env;
                int i, j;

                for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) {
                        stream_env = &asd->stream_env[i];
                        for (j = 0; j < IA_CSS_PIPE_ID_NUM; j++) {
                                ia_css_pipe_config_defaults(
                                    &stream_env->pipe_configs[j]);
                                ia_css_pipe_extra_config_defaults(
                                    &stream_env->pipe_extra_configs[j]);
                        }
                        ia_css_stream_config_defaults(
                            &stream_env->stream_config);
                }
                memset(&asd->params.config, 0, sizeof(asd->params.config));
                asd->params.css_update_params_needed = false;
        }

        /* move stats buffers to free queue list */
        list_splice_init(&asd->s3a_stats_in_css, &asd->s3a_stats);
        list_splice_init(&asd->s3a_stats_ready, &asd->s3a_stats);

        spin_lock_irqsave(&asd->dis_stats_lock, irqflags);
        list_splice_init(&asd->dis_stats_in_css, &asd->dis_stats);
        asd->params.dis_proj_data_valid = false;
        spin_unlock_irqrestore(&asd->dis_stats_lock, irqflags);

        for (i = 0; i < ATOMISP_METADATA_TYPE_NUM; i++) {
                list_splice_init(&asd->metadata_in_css[i], &asd->metadata[i]);
                list_splice_init(&asd->metadata_ready[i], &asd->metadata[i]);
        }

        atomisp_flush_params_queue(&asd->video_out);
        atomisp_free_css_parameters(&asd->params.css_param);
        memset(&asd->params.css_param, 0, sizeof(asd->params.css_param));
}

void atomisp_css_continuous_set_num_raw_frames(
     struct atomisp_sub_device *asd,
     int num_frames)
{
        if (asd->enable_raw_buffer_lock->val) {
                asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
                .stream_config.init_num_cont_raw_buf =
                    ATOMISP_CSS2_NUM_OFFLINE_INIT_CONTINUOUS_FRAMES_LOCK_EN;
                if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO &&
                    asd->params.video_dis_en)
                        asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
                        .stream_config.init_num_cont_raw_buf +=
                            ATOMISP_CSS2_NUM_DVS_FRAME_DELAY;
        } else {
                asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
                .stream_config.init_num_cont_raw_buf =
                    ATOMISP_CSS2_NUM_OFFLINE_INIT_CONTINUOUS_FRAMES;
        }

        if (asd->params.video_dis_en)
                asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
                .stream_config.init_num_cont_raw_buf +=
                    ATOMISP_CSS2_NUM_DVS_FRAME_DELAY;

        asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
        .stream_config.target_num_cont_raw_buf = num_frames;
}

static enum ia_css_pipe_mode __pipe_id_to_pipe_mode(
    struct atomisp_sub_device *asd,
    enum ia_css_pipe_id pipe_id)
{
        struct atomisp_device *isp = asd->isp;
        struct camera_mipi_info *mipi_info = atomisp_to_sensor_mipi_info(
                isp->inputs[asd->input_curr].sensor);

        switch (pipe_id) {
        case IA_CSS_PIPE_ID_COPY:
                /* Currently only YUVPP mode supports YUV420_Legacy format.
                 * Revert this when other pipe modes can support
                 * YUV420_Legacy format.
                 */
                if (mipi_info && mipi_info->input_format ==
                    ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY)
                        return IA_CSS_PIPE_MODE_YUVPP;
                return IA_CSS_PIPE_MODE_COPY;
        case IA_CSS_PIPE_ID_PREVIEW:
                return IA_CSS_PIPE_MODE_PREVIEW;
        case IA_CSS_PIPE_ID_CAPTURE:
                return IA_CSS_PIPE_MODE_CAPTURE;
        case IA_CSS_PIPE_ID_VIDEO:
                return IA_CSS_PIPE_MODE_VIDEO;
        case IA_CSS_PIPE_ID_YUVPP:
                return IA_CSS_PIPE_MODE_YUVPP;
        default:
                WARN_ON(1);
                return IA_CSS_PIPE_MODE_PREVIEW;
        }
}

static void __configure_output(struct atomisp_sub_device *asd,
                               unsigned int stream_index,
                               unsigned int width, unsigned int height,
                               unsigned int min_width,
                               enum ia_css_frame_format format,
                               enum ia_css_pipe_id pipe_id)
{
        struct atomisp_device *isp = asd->isp;
        struct atomisp_stream_env *stream_env =
                    &asd->stream_env[stream_index];
        struct ia_css_stream_config *s_config = &stream_env->stream_config;

        stream_env->pipe_configs[pipe_id].mode =
            __pipe_id_to_pipe_mode(asd, pipe_id);
        stream_env->update_pipe[pipe_id] = true;

        stream_env->pipe_configs[pipe_id].output_info[0].res.width = width;
        stream_env->pipe_configs[pipe_id].output_info[0].res.height = height;
        stream_env->pipe_configs[pipe_id].output_info[0].format = format;
        stream_env->pipe_configs[pipe_id].output_info[0].padded_width = min_width;

        /* isp binary 2.2 specific setting*/
        if (width > s_config->input_config.effective_res.width ||
            height > s_config->input_config.effective_res.height) {
                s_config->input_config.effective_res.width = width;
                s_config->input_config.effective_res.height = height;
        }

        dev_dbg(isp->dev, "configuring pipe[%d] output info w=%d.h=%d.f=%d.\n",
                pipe_id, width, height, format);
}

/*
 * For CSS2.1, capture pipe uses capture_pp_in_res to configure yuv
 * downscaling input resolution.
 */
static void __configure_capture_pp_input(struct atomisp_sub_device *asd,
        unsigned int width, unsigned int height,
        enum ia_css_pipe_id pipe_id)
{
        struct atomisp_device *isp = asd->isp;
        struct atomisp_stream_env *stream_env =
                    &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL];
        struct ia_css_stream_config *stream_config = &stream_env->stream_config;
        struct ia_css_pipe_config *pipe_configs =
                    &stream_env->pipe_configs[pipe_id];
        struct ia_css_pipe_extra_config *pipe_extra_configs =
                    &stream_env->pipe_extra_configs[pipe_id];
        unsigned int hor_ds_factor = 0, ver_ds_factor = 0;

        if (width == 0 && height == 0)
                return;

        if (width * 9 / 10 < pipe_configs->output_info[0].res.width ||
            height * 9 / 10 < pipe_configs->output_info[0].res.height)
                return;
        /* here just copy the calculation in css */
        hor_ds_factor = CEIL_DIV(width >> 1,
                                 pipe_configs->output_info[0].res.width);
        ver_ds_factor = CEIL_DIV(height >> 1,
                                 pipe_configs->output_info[0].res.height);

        if ((asd->isp->media_dev.hw_revision <
             (ATOMISP_HW_REVISION_ISP2401 << ATOMISP_HW_REVISION_SHIFT) ||
             IS_CHT) && hor_ds_factor != ver_ds_factor) {
                dev_warn(asd->isp->dev,
                         "Cropping for capture due to FW limitation");
                return;
        }

        pipe_configs->mode = __pipe_id_to_pipe_mode(asd, pipe_id);
        stream_env->update_pipe[pipe_id] = true;

        pipe_extra_configs->enable_yuv_ds = true;

        pipe_configs->capt_pp_in_res.width =
            stream_config->input_config.effective_res.width;
        pipe_configs->capt_pp_in_res.height =
            stream_config->input_config.effective_res.height;

        dev_dbg(isp->dev, "configuring pipe[%d]capture pp input w=%d.h=%d.\n",
                pipe_id, width, height);
}

/*
 * For CSS2.1, preview pipe could support bayer downscaling, yuv decimation and
 * yuv downscaling, which needs additional configurations.
 */
static void __configure_preview_pp_input(struct atomisp_sub_device *asd,
        unsigned int width, unsigned int height,
        enum ia_css_pipe_id pipe_id)
{
        struct atomisp_device *isp = asd->isp;
        int out_width, out_height, yuv_ds_in_width, yuv_ds_in_height;
        struct atomisp_stream_env *stream_env =
                    &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL];
        struct ia_css_stream_config *stream_config = &stream_env->stream_config;
        struct ia_css_pipe_config *pipe_configs =
                    &stream_env->pipe_configs[pipe_id];
        struct ia_css_pipe_extra_config *pipe_extra_configs =
                    &stream_env->pipe_extra_configs[pipe_id];
        struct ia_css_resolution *bayer_ds_out_res =
                    &pipe_configs->bayer_ds_out_res;
        struct ia_css_resolution *vf_pp_in_res =
                    &pipe_configs->vf_pp_in_res;
        struct ia_css_resolution  *effective_res =
                    &stream_config->input_config.effective_res;

        static const struct bayer_ds_factor bds_fct[] = {{2, 1}, {3, 2}, {5, 4} };
        /*
         * BZ201033: YUV decimation factor of 4 causes couple of rightmost
         * columns to be shaded. Remove this factor to work around the CSS bug.
         * const unsigned int yuv_dec_fct[] = {4, 2};
         */
        static const unsigned int yuv_dec_fct[] = { 2 };
        unsigned int i;

        if (width == 0 && height == 0)
                return;

        pipe_configs->mode = __pipe_id_to_pipe_mode(asd, pipe_id);
        stream_env->update_pipe[pipe_id] = true;

        out_width = pipe_configs->output_info[0].res.width;
        out_height = pipe_configs->output_info[0].res.height;

        /*
         * The ISP could do bayer downscaling, yuv decimation and yuv
         * downscaling:
         * 1: Bayer Downscaling: between effective resolution and
         * bayer_ds_res_out;
         * 2: YUV Decimation: between bayer_ds_res_out and vf_pp_in_res;
         * 3: YUV Downscaling: between vf_pp_in_res and final vf output
         *
         * Rule for Bayer Downscaling: support factor 2, 1.5 and 1.25
         * Rule for YUV Decimation: support factor 2, 4
         * Rule for YUV Downscaling: arbitrary value below 2
         *
         * General rule of factor distribution among these stages:
         * 1: try to do Bayer downscaling first if not in online mode.
         * 2: try to do maximum of 2 for YUV downscaling
         * 3: the remainling for YUV decimation
         *
         * Note:
         * Do not configure bayer_ds_out_res if:
         * online == 1 or continuous == 0 or raw_binning = 0
         */
        if (stream_config->online || !stream_config->continuous ||
            !pipe_extra_configs->enable_raw_binning) {
                bayer_ds_out_res->width = 0;
                bayer_ds_out_res->height = 0;
        } else {
                bayer_ds_out_res->width = effective_res->width;
                bayer_ds_out_res->height = effective_res->height;

                for (i = 0; i < ARRAY_SIZE(bds_fct); i++) {
                        if (effective_res->width >= out_width *
                            bds_fct[i].numerator / bds_fct[i].denominator &&
                            effective_res->height >= out_height *
                            bds_fct[i].numerator / bds_fct[i].denominator) {
                                bayer_ds_out_res->width =
                                    effective_res->width *
                                    bds_fct[i].denominator /
                                    bds_fct[i].numerator;
                                bayer_ds_out_res->height =
                                    effective_res->height *
                                    bds_fct[i].denominator /
                                    bds_fct[i].numerator;
                                break;
                        }
                }
        }
        /*
         * calculate YUV Decimation, YUV downscaling factor:
         * YUV Downscaling factor must not exceed 2.
         * YUV Decimation factor could be 2, 4.
         */
        /* first decide the yuv_ds input resolution */
        if (bayer_ds_out_res->width == 0) {
                yuv_ds_in_width = effective_res->width;
                yuv_ds_in_height = effective_res->height;
        } else {
                yuv_ds_in_width = bayer_ds_out_res->width;
                yuv_ds_in_height = bayer_ds_out_res->height;
        }

        vf_pp_in_res->width = yuv_ds_in_width;
        vf_pp_in_res->height = yuv_ds_in_height;

        /* find out the yuv decimation factor */
        for (i = 0; i < ARRAY_SIZE(yuv_dec_fct); i++) {
                if (yuv_ds_in_width >= out_width * yuv_dec_fct[i] &&
                    yuv_ds_in_height >= out_height * yuv_dec_fct[i]) {
                        vf_pp_in_res->width = yuv_ds_in_width / yuv_dec_fct[i];
                        vf_pp_in_res->height = yuv_ds_in_height / yuv_dec_fct[i];
                        break;
                }
        }

        if (vf_pp_in_res->width == out_width &&
            vf_pp_in_res->height == out_height) {
                pipe_extra_configs->enable_yuv_ds = false;
                vf_pp_in_res->width = 0;
                vf_pp_in_res->height = 0;
        } else {
                pipe_extra_configs->enable_yuv_ds = true;
        }

        dev_dbg(isp->dev, "configuring pipe[%d]preview pp input w=%d.h=%d.\n",
                pipe_id, width, height);
}

/*
 * For CSS2.1, offline video pipe could support bayer decimation, and
 * yuv downscaling, which needs additional configurations.
 */
static void __configure_video_pp_input(struct atomisp_sub_device *asd,
                                       unsigned int width, unsigned int height,
                                       enum ia_css_pipe_id pipe_id)
{
        struct atomisp_device *isp = asd->isp;
        int out_width, out_height;
        struct atomisp_stream_env *stream_env =
                    &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL];
        struct ia_css_stream_config *stream_config = &stream_env->stream_config;
        struct ia_css_pipe_config *pipe_configs =
                    &stream_env->pipe_configs[pipe_id];
        struct ia_css_pipe_extra_config *pipe_extra_configs =
                    &stream_env->pipe_extra_configs[pipe_id];
        struct ia_css_resolution *bayer_ds_out_res =
                    &pipe_configs->bayer_ds_out_res;
        struct ia_css_resolution  *effective_res =
                    &stream_config->input_config.effective_res;

        static const struct bayer_ds_factor bds_factors[] = {
                {8, 1}, {6, 1}, {4, 1}, {3, 1}, {2, 1}, {3, 2}
        };
        unsigned int i;

        if (width == 0 && height == 0)
                return;

        pipe_configs->mode = __pipe_id_to_pipe_mode(asd, pipe_id);
        stream_env->update_pipe[pipe_id] = true;

        pipe_extra_configs->enable_yuv_ds = false;

        /*
         * If DVS is enabled,  video binary will take care the dvs envelope
         * and usually the bayer_ds_out_res should be larger than 120% of
         * destination resolution, the extra 20% will be cropped as DVS
         * envelope. But,  if the bayer_ds_out_res is less than 120% of the
         * destination. The ISP can still work,  but DVS quality is not good.
         */
        /* taking at least 10% as envelope */
        if (asd->params.video_dis_en) {
                out_width = pipe_configs->output_info[0].res.width * 110 / 100;
                out_height = pipe_configs->output_info[0].res.height * 110 / 100;
        } else {
                out_width = pipe_configs->output_info[0].res.width;
                out_height = pipe_configs->output_info[0].res.height;
        }

        /*
         * calculate bayer decimate factor:
         * 1: only 1.5, 2, 4 and 8 get supported
         * 2: Do not configure bayer_ds_out_res if:
         *    online == 1 or continuous == 0 or raw_binning = 0
         */
        if (stream_config->online || !stream_config->continuous) {
                bayer_ds_out_res->width = 0;
                bayer_ds_out_res->height = 0;
                goto done;
        }

        pipe_extra_configs->enable_raw_binning = true;
        bayer_ds_out_res->width = effective_res->width;
        bayer_ds_out_res->height = effective_res->height;

        for (i = 0; i < sizeof(bds_factors) / sizeof(struct bayer_ds_factor);
             i++) {
                if (effective_res->width >= out_width *
                    bds_factors[i].numerator / bds_factors[i].denominator &&
                    effective_res->height >= out_height *
                    bds_factors[i].numerator / bds_factors[i].denominator) {
                        bayer_ds_out_res->width = effective_res->width *
                                                  bds_factors[i].denominator /
                                                  bds_factors[i].numerator;
                        bayer_ds_out_res->height = effective_res->height *
                                                   bds_factors[i].denominator /
                                                   bds_factors[i].numerator;
                        break;
                }
        }

        /*
         * DVS is cropped from BDS output, so we do not really need to set the
         * envelope to 20% of output resolution here. always set it to 12x12
         * per firmware requirement.
         */
        pipe_configs->dvs_envelope.width = 12;
        pipe_configs->dvs_envelope.height = 12;

done:
        if (pipe_id == IA_CSS_PIPE_ID_YUVPP)
                stream_config->left_padding = -1;
        else
                stream_config->left_padding = 12;
        dev_dbg(isp->dev, "configuring pipe[%d]video pp input w=%d.h=%d.\n",
                pipe_id, width, height);
}

static void __configure_vf_output(struct atomisp_sub_device *asd,
                                  unsigned int width, unsigned int height,
                                  unsigned int min_width,
                                  enum ia_css_frame_format format,
                                  enum ia_css_pipe_id pipe_id)
{
        struct atomisp_device *isp = asd->isp;
        struct atomisp_stream_env *stream_env =
                    &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL];
        stream_env->pipe_configs[pipe_id].mode =
            __pipe_id_to_pipe_mode(asd, pipe_id);
        stream_env->update_pipe[pipe_id] = true;

        stream_env->pipe_configs[pipe_id].vf_output_info[0].res.width = width;
        stream_env->pipe_configs[pipe_id].vf_output_info[0].res.height = height;
        stream_env->pipe_configs[pipe_id].vf_output_info[0].format = format;
        stream_env->pipe_configs[pipe_id].vf_output_info[0].padded_width =
            min_width;
        dev_dbg(isp->dev,
                "configuring pipe[%d] vf output info w=%d.h=%d.f=%d.\n",
                pipe_id, width, height, format);
}

static int __get_frame_info(struct atomisp_sub_device *asd,
                            unsigned int stream_index,
                            struct ia_css_frame_info *info,
                            enum frame_info_type type,
                            enum ia_css_pipe_id pipe_id)
{
        struct atomisp_device *isp = asd->isp;
        int ret;
        struct ia_css_pipe_info p_info;

        /* FIXME! No need to destroy/recreate all streams */
        ret = atomisp_css_update_stream(asd);
        if (ret)
                return ret;

        ret = ia_css_pipe_get_info(asd->stream_env[stream_index].pipes[pipe_id],
                                   &p_info);
        if (ret) {
                dev_err(isp->dev, "can't get info from pipe\n");
                goto get_info_err;
        }

        switch (type) {
        case ATOMISP_CSS_VF_FRAME:
                *info = p_info.vf_output_info[0];
                dev_dbg(isp->dev, "getting vf frame info.\n");
                break;
        case ATOMISP_CSS_SECOND_VF_FRAME:
                *info = p_info.vf_output_info[1];
                dev_dbg(isp->dev, "getting second vf frame info.\n");
                break;
        case ATOMISP_CSS_OUTPUT_FRAME:
                *info = p_info.output_info[0];
                dev_dbg(isp->dev, "getting main frame info.\n");
                break;
        case ATOMISP_CSS_SECOND_OUTPUT_FRAME:
                *info = p_info.output_info[1];
                dev_dbg(isp->dev, "getting second main frame info.\n");
                break;
        default:
        case ATOMISP_CSS_RAW_FRAME:
                *info = p_info.raw_output_info;
                dev_dbg(isp->dev, "getting raw frame info.\n");
                break;
        }
        dev_dbg(isp->dev, "get frame info: w=%d, h=%d, num_invalid_frames %d.\n",
                info->res.width, info->res.height, p_info.num_invalid_frames);

        return 0;

get_info_err:
        atomisp_destroy_pipes_stream(asd);
        return -EINVAL;
}

static unsigned int atomisp_get_pipe_index(struct atomisp_sub_device *asd)
{
        if (asd->copy_mode)
                return IA_CSS_PIPE_ID_COPY;

        switch (asd->run_mode->val) {
        case ATOMISP_RUN_MODE_VIDEO:
                return IA_CSS_PIPE_ID_VIDEO;
        case ATOMISP_RUN_MODE_STILL_CAPTURE:
                return IA_CSS_PIPE_ID_CAPTURE;
        case ATOMISP_RUN_MODE_PREVIEW:
                return IA_CSS_PIPE_ID_PREVIEW;
        }

        dev_warn(asd->isp->dev, "cannot determine pipe-index return default preview pipe\n");
        return IA_CSS_PIPE_ID_PREVIEW;
}

int atomisp_get_css_frame_info(struct atomisp_sub_device *asd,
                               struct ia_css_frame_info *frame_info)
{
        struct ia_css_pipe_info info;
        int pipe_index = atomisp_get_pipe_index(asd);
        int stream_index;
        struct atomisp_device *isp = asd->isp;

        stream_index = (pipe_index == IA_CSS_PIPE_ID_YUVPP) ?
                               ATOMISP_INPUT_STREAM_VIDEO :
                               ATOMISP_INPUT_STREAM_GENERAL;

        if (0 != ia_css_pipe_get_info(asd->stream_env[stream_index]
                .pipes[pipe_index], &info)) {
                dev_dbg(isp->dev, "ia_css_pipe_get_info FAILED");
                return -EINVAL;
        }

        *frame_info = info.output_info[0];
        return 0;
}

int atomisp_css_copy_configure_output(struct atomisp_sub_device *asd,
                                      unsigned int stream_index,
                                      unsigned int width, unsigned int height,
                                      unsigned int padded_width,
                                      enum ia_css_frame_format format)
{
        asd->stream_env[stream_index].pipe_configs[IA_CSS_PIPE_ID_COPY].
        default_capture_config.mode =
            IA_CSS_CAPTURE_MODE_RAW;

        __configure_output(asd, stream_index, width, height, padded_width,
                           format, IA_CSS_PIPE_ID_COPY);
        return 0;
}

int atomisp_css_preview_configure_output(struct atomisp_sub_device *asd,
        unsigned int width, unsigned int height,
        unsigned int min_width,
        enum ia_css_frame_format format)
{
        __configure_output(asd, ATOMISP_INPUT_STREAM_GENERAL, width, height,
                           min_width, format, IA_CSS_PIPE_ID_PREVIEW);
        return 0;
}

int atomisp_css_capture_configure_output(struct atomisp_sub_device *asd,
        unsigned int width, unsigned int height,
        unsigned int min_width,
        enum ia_css_frame_format format)
{
        __configure_output(asd, ATOMISP_INPUT_STREAM_GENERAL, width, height,
                           min_width, format, IA_CSS_PIPE_ID_CAPTURE);
        return 0;
}

int atomisp_css_video_configure_output(struct atomisp_sub_device *asd,
                                       unsigned int width, unsigned int height,
                                       unsigned int min_width,
                                       enum ia_css_frame_format format)
{
        __configure_output(asd, ATOMISP_INPUT_STREAM_GENERAL, width, height,
                           min_width, format, IA_CSS_PIPE_ID_VIDEO);
        return 0;
}

int atomisp_css_video_configure_viewfinder(
    struct atomisp_sub_device *asd,
    unsigned int width, unsigned int height,
    unsigned int min_width,
    enum ia_css_frame_format format)
{
        __configure_vf_output(asd, width, height, min_width, format,
                              IA_CSS_PIPE_ID_VIDEO);
        return 0;
}

int atomisp_css_capture_configure_viewfinder(
    struct atomisp_sub_device *asd,
    unsigned int width, unsigned int height,
    unsigned int min_width,
    enum ia_css_frame_format format)
{
        __configure_vf_output(asd, width, height, min_width, format, IA_CSS_PIPE_ID_CAPTURE);
        return 0;
}

int atomisp_css_video_get_viewfinder_frame_info(
    struct atomisp_sub_device *asd,
    struct ia_css_frame_info *info)
{
        return __get_frame_info(asd, ATOMISP_INPUT_STREAM_GENERAL, info,
                                ATOMISP_CSS_VF_FRAME, IA_CSS_PIPE_ID_VIDEO);
}

int atomisp_css_capture_get_viewfinder_frame_info(
    struct atomisp_sub_device *asd,
    struct ia_css_frame_info *info)
{
        return __get_frame_info(asd, ATOMISP_INPUT_STREAM_GENERAL, info,
                                ATOMISP_CSS_VF_FRAME, IA_CSS_PIPE_ID_CAPTURE);
}

int atomisp_css_copy_get_output_frame_info(
    struct atomisp_sub_device *asd,
    unsigned int stream_index,
    struct ia_css_frame_info *info)
{
        return __get_frame_info(asd, stream_index, info,
                                ATOMISP_CSS_OUTPUT_FRAME, IA_CSS_PIPE_ID_COPY);
}

int atomisp_css_preview_get_output_frame_info(
    struct atomisp_sub_device *asd,
    struct ia_css_frame_info *info)
{
        return __get_frame_info(asd, ATOMISP_INPUT_STREAM_GENERAL, info,
                                ATOMISP_CSS_OUTPUT_FRAME, IA_CSS_PIPE_ID_PREVIEW);
}

int atomisp_css_capture_get_output_frame_info(
    struct atomisp_sub_device *asd,
    struct ia_css_frame_info *info)
{
        return __get_frame_info(asd, ATOMISP_INPUT_STREAM_GENERAL, info,
                                ATOMISP_CSS_OUTPUT_FRAME, IA_CSS_PIPE_ID_CAPTURE);
}

int atomisp_css_video_get_output_frame_info(
    struct atomisp_sub_device *asd,
    struct ia_css_frame_info *info)
{
        return __get_frame_info(asd, ATOMISP_INPUT_STREAM_GENERAL, info,
                                ATOMISP_CSS_OUTPUT_FRAME, IA_CSS_PIPE_ID_VIDEO);
}

int atomisp_css_preview_configure_pp_input(
    struct atomisp_sub_device *asd,
    unsigned int width, unsigned int height)
{
        struct atomisp_stream_env *stream_env =
                    &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL];
        __configure_preview_pp_input(asd, width, height, IA_CSS_PIPE_ID_PREVIEW);

        if (width > stream_env->pipe_configs[IA_CSS_PIPE_ID_CAPTURE].
            capt_pp_in_res.width)
                __configure_capture_pp_input(asd, width, height, IA_CSS_PIPE_ID_CAPTURE);

        return 0;
}

int atomisp_css_capture_configure_pp_input(
    struct atomisp_sub_device *asd,
    unsigned int width, unsigned int height)
{
        __configure_capture_pp_input(asd, width, height, IA_CSS_PIPE_ID_CAPTURE);
        return 0;
}

int atomisp_css_video_configure_pp_input(
    struct atomisp_sub_device *asd,
    unsigned int width, unsigned int height)
{
        struct atomisp_stream_env *stream_env =
                    &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL];

        __configure_video_pp_input(asd, width, height, IA_CSS_PIPE_ID_VIDEO);

        if (width > stream_env->pipe_configs[IA_CSS_PIPE_ID_CAPTURE].
            capt_pp_in_res.width)
                __configure_capture_pp_input(asd, width, height, IA_CSS_PIPE_ID_CAPTURE);

        return 0;
}

int atomisp_css_offline_capture_configure(struct atomisp_sub_device *asd,
        int num_captures, unsigned int skip, int offset)
{
        int ret;

        dev_dbg(asd->isp->dev, "%s num_capture:%d skip:%d offset:%d\n",
                __func__, num_captures, skip, offset);

        ret = ia_css_stream_capture(
                  asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
                  num_captures, skip, offset);
        if (ret)
                return -EINVAL;

        return 0;
}

int atomisp_css_exp_id_capture(struct atomisp_sub_device *asd, int exp_id)
{
        int ret;

        ret = ia_css_stream_capture_frame(
                  asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
                  exp_id);
        if (ret == -ENOBUFS) {
                /* capture cmd queue is full */
                return -EBUSY;
        } else if (ret) {
                return -EIO;
        }

        return 0;
}

int atomisp_css_exp_id_unlock(struct atomisp_sub_device *asd, int exp_id)
{
        int ret;

        ret = ia_css_unlock_raw_frame(
                  asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
                  exp_id);
        if (ret == -ENOBUFS)
                return -EAGAIN;
        else if (ret)
                return -EIO;

        return 0;
}

int atomisp_css_capture_enable_xnr(struct atomisp_sub_device *asd,
                                   bool enable)
{
        asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
        .pipe_configs[IA_CSS_PIPE_ID_CAPTURE]
        .default_capture_config.enable_xnr = enable;
        asd->params.capture_config.enable_xnr = enable;
        asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]
        .update_pipe[IA_CSS_PIPE_ID_CAPTURE] = true;

        return 0;
}

void atomisp_css_set_ctc_table(struct atomisp_sub_device *asd,
                               struct ia_css_ctc_table *ctc_table)
{
        int i;
        u16 *vamem_ptr = ctc_table->data.vamem_1;
        int data_size = IA_CSS_VAMEM_1_CTC_TABLE_SIZE;
        bool valid = false;

        /* workaround: if ctc_table is all 0, do not apply it */
        if (ctc_table->vamem_type == IA_CSS_VAMEM_TYPE_2) {
                vamem_ptr = ctc_table->data.vamem_2;
                data_size = IA_CSS_VAMEM_2_CTC_TABLE_SIZE;
        }

        for (i = 0; i < data_size; i++) {
                if (*(vamem_ptr + i)) {
                        valid = true;
                        break;
                }
        }

        if (valid)
                asd->params.config.ctc_table = ctc_table;
        else
                dev_warn(asd->isp->dev, "Bypass the invalid ctc_table.\n");
}

void atomisp_css_set_anr_thres(struct atomisp_sub_device *asd,
                               struct ia_css_anr_thres *anr_thres)
{
        asd->params.config.anr_thres = anr_thres;
}

void atomisp_css_set_dvs_6axis(struct atomisp_sub_device *asd,
                               struct ia_css_dvs_6axis_config *dvs_6axis)
{
        asd->params.config.dvs_6axis_config = dvs_6axis;
}

void atomisp_css_video_set_dis_vector(struct atomisp_sub_device *asd,
                                      struct atomisp_dis_vector *vector)
{
        if (!asd->params.config.motion_vector)
                asd->params.config.motion_vector = &asd->params.css_param.motion_vector;

        memset(asd->params.config.motion_vector,
               0, sizeof(struct ia_css_vector));
        asd->params.css_param.motion_vector.x = vector->x;
        asd->params.css_param.motion_vector.y = vector->y;
}

static int atomisp_compare_dvs_grid(struct atomisp_sub_device *asd,
                                    struct atomisp_dvs_grid_info *atomgrid)
{
        struct ia_css_dvs_grid_info *cur =
            atomisp_css_get_dvs_grid_info(&asd->params.curr_grid_info);

        if (!cur) {
                dev_err(asd->isp->dev, "dvs grid not available!\n");
                return -EINVAL;
        }

        if (sizeof(*cur) != sizeof(*atomgrid)) {
                dev_err(asd->isp->dev, "dvs grid mismatch!\n");
                return -EINVAL;
        }

        if (!cur->enable) {
                dev_err(asd->isp->dev, "dvs not enabled!\n");
                return -EINVAL;
        }

        return memcmp(atomgrid, cur, sizeof(*cur));
}

void  atomisp_css_set_dvs2_coefs(struct atomisp_sub_device *asd,
                                 struct ia_css_dvs2_coefficients *coefs)
{
        asd->params.config.dvs2_coefs = coefs;
}

int atomisp_css_set_dis_coefs(struct atomisp_sub_device *asd,
                              struct atomisp_dis_coefficients *coefs)
{
        if (atomisp_compare_dvs_grid(asd, &coefs->grid_info) != 0)
                /* If the grid info in the argument differs from the current
                   grid info, we tell the caller to reset the grid size and
                   try again. */
                return -EAGAIN;

        if (!coefs->hor_coefs.odd_real ||
            !coefs->hor_coefs.odd_imag ||
            !coefs->hor_coefs.even_real ||
            !coefs->hor_coefs.even_imag ||
            !coefs->ver_coefs.odd_real ||
            !coefs->ver_coefs.odd_imag ||
            !coefs->ver_coefs.even_real ||
            !coefs->ver_coefs.even_imag ||
            !asd->params.css_param.dvs2_coeff->hor_coefs.odd_real ||
            !asd->params.css_param.dvs2_coeff->hor_coefs.odd_imag ||
            !asd->params.css_param.dvs2_coeff->hor_coefs.even_real ||
            !asd->params.css_param.dvs2_coeff->hor_coefs.even_imag ||
            !asd->params.css_param.dvs2_coeff->ver_coefs.odd_real ||
            !asd->params.css_param.dvs2_coeff->ver_coefs.odd_imag ||
            !asd->params.css_param.dvs2_coeff->ver_coefs.even_real ||
            !asd->params.css_param.dvs2_coeff->ver_coefs.even_imag)
                return -EINVAL;

        if (copy_from_user(asd->params.css_param.dvs2_coeff->hor_coefs.odd_real,
                           coefs->hor_coefs.odd_real, asd->params.dvs_hor_coef_bytes))
                return -EFAULT;
        if (copy_from_user(asd->params.css_param.dvs2_coeff->hor_coefs.odd_imag,
                           coefs->hor_coefs.odd_imag, asd->params.dvs_hor_coef_bytes))
                return -EFAULT;
        if (copy_from_user(asd->params.css_param.dvs2_coeff->hor_coefs.even_real,
                           coefs->hor_coefs.even_real, asd->params.dvs_hor_coef_bytes))
                return -EFAULT;
        if (copy_from_user(asd->params.css_param.dvs2_coeff->hor_coefs.even_imag,
                           coefs->hor_coefs.even_imag, asd->params.dvs_hor_coef_bytes))
                return -EFAULT;

        if (copy_from_user(asd->params.css_param.dvs2_coeff->ver_coefs.odd_real,
                           coefs->ver_coefs.odd_real, asd->params.dvs_ver_coef_bytes))
                return -EFAULT;
        if (copy_from_user(asd->params.css_param.dvs2_coeff->ver_coefs.odd_imag,
                           coefs->ver_coefs.odd_imag, asd->params.dvs_ver_coef_bytes))
                return -EFAULT;
        if (copy_from_user(asd->params.css_param.dvs2_coeff->ver_coefs.even_real,
                           coefs->ver_coefs.even_real, asd->params.dvs_ver_coef_bytes))
                return -EFAULT;
        if (copy_from_user(asd->params.css_param.dvs2_coeff->ver_coefs.even_imag,
                           coefs->ver_coefs.even_imag, asd->params.dvs_ver_coef_bytes))
                return -EFAULT;

        asd->params.css_param.update_flag.dvs2_coefs =
                (struct atomisp_dis_coefficients *)
                asd->params.css_param.dvs2_coeff;
        /* FIXME! */
        /*      asd->params.dis_proj_data_valid = false; */
        asd->params.css_update_params_needed = true;

        return 0;
}

void atomisp_css_set_zoom_factor(struct atomisp_sub_device *asd,
                                 unsigned int zoom)
{
        struct atomisp_device *isp = asd->isp;

        if (zoom == asd->params.css_param.dz_config.dx &&
            zoom == asd->params.css_param.dz_config.dy) {
                dev_dbg(isp->dev, "same zoom scale. skipped.\n");
                return;
        }

        memset(&asd->params.css_param.dz_config, 0,
               sizeof(struct ia_css_dz_config));
        asd->params.css_param.dz_config.dx = zoom;
        asd->params.css_param.dz_config.dy = zoom;

        asd->params.css_param.update_flag.dz_config =
            (struct atomisp_dz_config *)&asd->params.css_param.dz_config;
        asd->params.css_update_params_needed = true;
}

void atomisp_css_set_formats_config(struct atomisp_sub_device *asd,
                                    struct ia_css_formats_config *formats_config)
{
        asd->params.config.formats_config = formats_config;
}

int atomisp_css_get_wb_config(struct atomisp_sub_device *asd,
                              struct atomisp_wb_config *config)
{
        struct ia_css_wb_config wb_config;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }
        memset(&wb_config, 0, sizeof(struct ia_css_wb_config));
        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.wb_config = &wb_config;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        memcpy(config, &wb_config, sizeof(*config));

        return 0;
}

int atomisp_css_get_ob_config(struct atomisp_sub_device *asd,
                              struct atomisp_ob_config *config)
{
        struct ia_css_ob_config ob_config;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }
        memset(&ob_config, 0, sizeof(struct ia_css_ob_config));
        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.ob_config = &ob_config;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        memcpy(config, &ob_config, sizeof(*config));

        return 0;
}

int atomisp_css_get_dp_config(struct atomisp_sub_device *asd,
                              struct atomisp_dp_config *config)
{
        struct ia_css_dp_config dp_config;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }
        memset(&dp_config, 0, sizeof(struct ia_css_dp_config));
        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.dp_config = &dp_config;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        memcpy(config, &dp_config, sizeof(*config));

        return 0;
}

int atomisp_css_get_de_config(struct atomisp_sub_device *asd,
                              struct atomisp_de_config *config)
{
        struct ia_css_de_config de_config;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }
        memset(&de_config, 0, sizeof(struct ia_css_de_config));
        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.de_config = &de_config;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        memcpy(config, &de_config, sizeof(*config));

        return 0;
}

int atomisp_css_get_nr_config(struct atomisp_sub_device *asd,
                              struct atomisp_nr_config *config)
{
        struct ia_css_nr_config nr_config;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }
        memset(&nr_config, 0, sizeof(struct ia_css_nr_config));
        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));

        isp_config.nr_config = &nr_config;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        memcpy(config, &nr_config, sizeof(*config));

        return 0;
}

int atomisp_css_get_ee_config(struct atomisp_sub_device *asd,
                              struct atomisp_ee_config *config)
{
        struct ia_css_ee_config ee_config;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }
        memset(&ee_config, 0, sizeof(struct ia_css_ee_config));
        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.ee_config = &ee_config;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        memcpy(config, &ee_config, sizeof(*config));

        return 0;
}

int atomisp_css_get_tnr_config(struct atomisp_sub_device *asd,
                               struct atomisp_tnr_config *config)
{
        struct ia_css_tnr_config tnr_config;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }
        memset(&tnr_config, 0, sizeof(struct ia_css_tnr_config));
        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.tnr_config = &tnr_config;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        memcpy(config, &tnr_config, sizeof(*config));

        return 0;
}

int atomisp_css_get_ctc_table(struct atomisp_sub_device *asd,
                              struct atomisp_ctc_table *config)
{
        struct ia_css_ctc_table *tab;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }

        tab = vzalloc(sizeof(struct ia_css_ctc_table));
        if (!tab)
                return -ENOMEM;

        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.ctc_table = tab;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        memcpy(config, tab, sizeof(*tab));
        vfree(tab);

        return 0;
}

int atomisp_css_get_gamma_table(struct atomisp_sub_device *asd,
                                struct atomisp_gamma_table *config)
{
        struct ia_css_gamma_table *tab;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }

        tab = vzalloc(sizeof(struct ia_css_gamma_table));
        if (!tab)
                return -ENOMEM;

        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.gamma_table = tab;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        memcpy(config, tab, sizeof(*tab));
        vfree(tab);

        return 0;
}

int atomisp_css_get_gc_config(struct atomisp_sub_device *asd,
                              struct atomisp_gc_config *config)
{
        struct ia_css_gc_config gc_config;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }
        memset(&gc_config, 0, sizeof(struct ia_css_gc_config));
        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.gc_config = &gc_config;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        /* Get gamma correction params from current setup */
        memcpy(config, &gc_config, sizeof(*config));

        return 0;
}

int atomisp_css_get_3a_config(struct atomisp_sub_device *asd,
                              struct atomisp_3a_config *config)
{
        struct ia_css_3a_config s3a_config;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }
        memset(&s3a_config, 0, sizeof(struct ia_css_3a_config));
        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.s3a_config = &s3a_config;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        /* Get white balance from current setup */
        memcpy(config, &s3a_config, sizeof(*config));

        return 0;
}

int atomisp_css_get_formats_config(struct atomisp_sub_device *asd,
                                   struct atomisp_formats_config *config)
{
        struct ia_css_formats_config formats_config;
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }
        memset(&formats_config, 0, sizeof(formats_config));
        memset(&isp_config, 0, sizeof(isp_config));
        isp_config.formats_config = &formats_config;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        /* Get narrow gamma from current setup */
        memcpy(config, &formats_config, sizeof(*config));

        return 0;
}

int atomisp_css_get_zoom_factor(struct atomisp_sub_device *asd,
                                unsigned int *zoom)
{
        struct ia_css_dz_config dz_config;  /** Digital Zoom */
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev, "%s called after streamoff, skipping.\n",
                        __func__);
                return -EINVAL;
        }
        memset(&dz_config, 0, sizeof(struct ia_css_dz_config));
        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.dz_config = &dz_config;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
        *zoom = dz_config.dx;

        return 0;
}

/*
 * Function to set/get image stabilization statistics
 */
int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd,
                             struct atomisp_dis_statistics *stats)
{
        struct atomisp_device *isp = asd->isp;
        struct atomisp_dis_buf *dis_buf;
        unsigned long flags;

        lockdep_assert_held(&isp->mutex);

        if (!asd->params.dvs_stat->hor_prod.odd_real ||
            !asd->params.dvs_stat->hor_prod.odd_imag ||
            !asd->params.dvs_stat->hor_prod.even_real ||
            !asd->params.dvs_stat->hor_prod.even_imag ||
            !asd->params.dvs_stat->ver_prod.odd_real ||
            !asd->params.dvs_stat->ver_prod.odd_imag ||
            !asd->params.dvs_stat->ver_prod.even_real ||
            !asd->params.dvs_stat->ver_prod.even_imag)
                return -EINVAL;

        /* isp needs to be streaming to get DIS statistics */
        if (!asd->streaming)
                return -EINVAL;

        if (atomisp_compare_dvs_grid(asd, &stats->dvs2_stat.grid_info) != 0)
                /* If the grid info in the argument differs from the current
                   grid info, we tell the caller to reset the grid size and
                   try again. */
                return -EAGAIN;

        spin_lock_irqsave(&asd->dis_stats_lock, flags);
        if (!asd->params.dis_proj_data_valid || list_empty(&asd->dis_stats)) {
                spin_unlock_irqrestore(&asd->dis_stats_lock, flags);
                dev_err(isp->dev, "dis statistics is not valid.\n");
                return -EAGAIN;
        }

        dis_buf = list_entry(asd->dis_stats.next,
                             struct atomisp_dis_buf, list);
        list_del_init(&dis_buf->list);
        spin_unlock_irqrestore(&asd->dis_stats_lock, flags);

        if (dis_buf->dvs_map)
                ia_css_translate_dvs2_statistics(
                    asd->params.dvs_stat, dis_buf->dvs_map);
        else
                ia_css_get_dvs2_statistics(asd->params.dvs_stat,
                                           dis_buf->dis_data);
        stats->exp_id = dis_buf->dis_data->exp_id;

        spin_lock_irqsave(&asd->dis_stats_lock, flags);
        list_add_tail(&dis_buf->list, &asd->dis_stats);
        spin_unlock_irqrestore(&asd->dis_stats_lock, flags);

        if (copy_to_user(stats->dvs2_stat.ver_prod.odd_real,
                         asd->params.dvs_stat->ver_prod.odd_real,
                         asd->params.dvs_ver_proj_bytes))
                return -EFAULT;
        if (copy_to_user(stats->dvs2_stat.ver_prod.odd_imag,
                         asd->params.dvs_stat->ver_prod.odd_imag,
                         asd->params.dvs_ver_proj_bytes))
                return -EFAULT;
        if (copy_to_user(stats->dvs2_stat.ver_prod.even_real,
                         asd->params.dvs_stat->ver_prod.even_real,
                         asd->params.dvs_ver_proj_bytes))
                return -EFAULT;
        if (copy_to_user(stats->dvs2_stat.ver_prod.even_imag,
                         asd->params.dvs_stat->ver_prod.even_imag,
                         asd->params.dvs_ver_proj_bytes))
                return -EFAULT;
        if (copy_to_user(stats->dvs2_stat.hor_prod.odd_real,
                         asd->params.dvs_stat->hor_prod.odd_real,
                         asd->params.dvs_hor_proj_bytes))
                return -EFAULT;
        if (copy_to_user(stats->dvs2_stat.hor_prod.odd_imag,
                         asd->params.dvs_stat->hor_prod.odd_imag,
                         asd->params.dvs_hor_proj_bytes))
                return -EFAULT;
        if (copy_to_user(stats->dvs2_stat.hor_prod.even_real,
                         asd->params.dvs_stat->hor_prod.even_real,
                         asd->params.dvs_hor_proj_bytes))
                return -EFAULT;
        if (copy_to_user(stats->dvs2_stat.hor_prod.even_imag,
                         asd->params.dvs_stat->hor_prod.even_imag,
                         asd->params.dvs_hor_proj_bytes))
                return -EFAULT;

        return 0;
}

struct ia_css_shading_table *atomisp_css_shading_table_alloc(
    unsigned int width, unsigned int height)
{
        return ia_css_shading_table_alloc(width, height);
}

void atomisp_css_set_shading_table(struct atomisp_sub_device *asd,
                                   struct ia_css_shading_table *table)
{
        asd->params.config.shading_table = table;
}

void atomisp_css_shading_table_free(struct ia_css_shading_table *table)
{
        ia_css_shading_table_free(table);
}

struct ia_css_morph_table *atomisp_css_morph_table_allocate(
    unsigned int width, unsigned int height)
{
        return ia_css_morph_table_allocate(width, height);
}

void atomisp_css_set_morph_table(struct atomisp_sub_device *asd,
                                 struct ia_css_morph_table *table)
{
        asd->params.config.morph_table = table;
}

void atomisp_css_get_morph_table(struct atomisp_sub_device *asd,
                                 struct ia_css_morph_table *table)
{
        struct ia_css_isp_config isp_config;
        struct atomisp_device *isp = asd->isp;

        if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) {
                dev_err(isp->dev,
                        "%s called after streamoff, skipping.\n", __func__);
                return;
        }
        memset(table, 0, sizeof(struct ia_css_morph_table));
        memset(&isp_config, 0, sizeof(struct ia_css_isp_config));
        isp_config.morph_table = table;
        ia_css_stream_get_isp_config(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            &isp_config);
}

void atomisp_css_morph_table_free(struct ia_css_morph_table *table)
{
        ia_css_morph_table_free(table);
}

static bool atomisp_css_isr_get_stream_id(struct ia_css_pipe *css_pipe,
                                          struct atomisp_device *isp,
                                          enum atomisp_input_stream_id *stream_id)
{
        struct atomisp_stream_env *stream_env;
        int i, j;

        if (!isp->asd.streaming)
                return false;

        for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) {
                stream_env = &isp->asd.stream_env[i];
                for (j = 0; j < IA_CSS_PIPE_ID_NUM; j++) {
                        if (stream_env->pipes[j] && stream_env->pipes[j] == css_pipe) {
                                *stream_id = i;
                                return true;
                        }
                }
        }

        return false;
}

int atomisp_css_isr_thread(struct atomisp_device *isp)
{
        enum atomisp_input_stream_id stream_id = 0;
        struct atomisp_css_event current_event;

        lockdep_assert_held(&isp->mutex);

        while (!ia_css_dequeue_psys_event(&current_event.event)) {
                if (current_event.event.type ==
                    IA_CSS_EVENT_TYPE_FW_ASSERT) {
                        /*
                         * Received FW assertion signal,
                         * trigger WDT to recover
                         */
                        dev_err(isp->dev,
                                "%s: ISP reports FW_ASSERT event! fw_assert_module_id %d fw_assert_line_no %d\n",
                                __func__,
                                current_event.event.fw_assert_module_id,
                                current_event.event.fw_assert_line_no);

                        queue_work(system_long_wq, &isp->assert_recovery_work);
                        return -EINVAL;
                } else if (current_event.event.type == IA_CSS_EVENT_TYPE_FW_WARNING) {
                        dev_warn(isp->dev, "%s: ISP reports warning, code is %d, exp_id %d\n",
                                 __func__, current_event.event.fw_warning,
                                 current_event.event.exp_id);
                        continue;
                }

                if (!atomisp_css_isr_get_stream_id(current_event.event.pipe, isp, &stream_id)) {
                        if (current_event.event.type == IA_CSS_EVENT_TYPE_TIMER)
                                dev_dbg(isp->dev,
                                        "event: Timer event.");
                        else
                                dev_warn(isp->dev, "%s:no subdev.event:%d",
                                         __func__,
                                         current_event.event.type);
                        continue;
                }

                atomisp_css_temp_pipe_to_pipe_id(&isp->asd, &current_event);
                switch (current_event.event.type) {
                case IA_CSS_EVENT_TYPE_OUTPUT_FRAME_DONE:
                        dev_dbg(isp->dev, "event: Output frame done");
                        atomisp_buf_done(&isp->asd, 0, IA_CSS_BUFFER_TYPE_OUTPUT_FRAME,
                                         current_event.pipe, true, stream_id);
                        break;
                case IA_CSS_EVENT_TYPE_SECOND_OUTPUT_FRAME_DONE:
                        dev_dbg(isp->dev, "event: Second output frame done");
                        atomisp_buf_done(&isp->asd, 0, IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME,
                                         current_event.pipe, true, stream_id);
                        break;
                case IA_CSS_EVENT_TYPE_3A_STATISTICS_DONE:
                        dev_dbg(isp->dev, "event: 3A stats frame done");
                        atomisp_buf_done(&isp->asd, 0,
                                         IA_CSS_BUFFER_TYPE_3A_STATISTICS,
                                         current_event.pipe,
                                         false, stream_id);
                        break;
                case IA_CSS_EVENT_TYPE_METADATA_DONE:
                        dev_dbg(isp->dev, "event: metadata frame done");
                        atomisp_buf_done(&isp->asd, 0,
                                         IA_CSS_BUFFER_TYPE_METADATA,
                                         current_event.pipe,
                                         false, stream_id);
                        break;
                case IA_CSS_EVENT_TYPE_VF_OUTPUT_FRAME_DONE:
                        dev_dbg(isp->dev, "event: VF output frame done");
                        atomisp_buf_done(&isp->asd, 0,
                                         IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME,
                                         current_event.pipe, true, stream_id);
                        break;
                case IA_CSS_EVENT_TYPE_SECOND_VF_OUTPUT_FRAME_DONE:
                        dev_dbg(isp->dev, "event: second VF output frame done");
                        atomisp_buf_done(&isp->asd, 0,
                                         IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME,
                                         current_event.pipe, true, stream_id);
                        break;
                case IA_CSS_EVENT_TYPE_DIS_STATISTICS_DONE:
                        dev_dbg(isp->dev, "event: dis stats frame done");
                        atomisp_buf_done(&isp->asd, 0,
                                         IA_CSS_BUFFER_TYPE_DIS_STATISTICS,
                                         current_event.pipe,
                                         false, stream_id);
                        break;
                case IA_CSS_EVENT_TYPE_PIPELINE_DONE:
                        dev_dbg(isp->dev, "event: pipeline done");
                        break;
                case IA_CSS_EVENT_TYPE_ACC_STAGE_COMPLETE:
                        dev_warn(isp->dev, "unexpected event: acc stage done");
                        break;
                default:
                        dev_dbg(isp->dev, "unhandled css stored event: 0x%x\n",
                                current_event.event.type);
                        break;
                }
        }

        return 0;
}

bool atomisp_css_valid_sof(struct atomisp_device *isp)
{
        unsigned int i;

        /* Loop for each css vc stream */
        for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) {
                if (!isp->asd.stream_env[i].stream)
                        continue;

                dev_dbg(isp->dev, "stream #%d: mode: %d\n",
                        i, isp->asd.stream_env[i].stream_config.mode);
                if (isp->asd.stream_env[i].stream_config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR)
                        return false;
        }

        return true;
}

int atomisp_css_debug_dump_isp_binary(void)
{
        ia_css_debug_dump_isp_binary();
        return 0;
}

int atomisp_css_dump_sp_raw_copy_linecount(bool reduced)
{
        sh_css_dump_sp_raw_copy_linecount(reduced);
        return 0;
}

static const char * const fw_type_name[] = {
        [ia_css_sp_firmware]            = "SP",
        [ia_css_isp_firmware]           = "ISP",
        [ia_css_bootloader_firmware]    = "BootLoader",
        [ia_css_acc_firmware]           = "accel",
};

static const char * const fw_acc_type_name[] = {
        [IA_CSS_ACC_NONE] =             "Normal",
        [IA_CSS_ACC_OUTPUT] =           "Accel stage on output",
        [IA_CSS_ACC_VIEWFINDER] =       "Accel stage on viewfinder",
        [IA_CSS_ACC_STANDALONE] =       "Stand-alone acceleration",
};

int atomisp_css_dump_blob_infor(struct atomisp_device *isp)
{
        struct ia_css_blob_descr *bd = sh_css_blob_info;
        unsigned int i, nm = sh_css_num_binaries;

        if (nm == 0)
                return -EPERM;
        if (!bd)
                return -EPERM;

        /*
         * The sh_css_load_firmware function discard the initial
         * "SPS" binaries
         */
        for (i = 0; i < sh_css_num_binaries - NUM_OF_SPS; i++) {
                switch (bd[i].header.type) {
                case ia_css_isp_firmware:
                        dev_dbg(isp->dev, "Num%2d type %s (%s), binary id is %2d, name is %s\n",
                                i + NUM_OF_SPS,
                                fw_type_name[bd[i].header.type],
                                fw_acc_type_name[bd[i].header.info.isp.type],
                                bd[i].header.info.isp.sp.id,
                                bd[i].name);
                        break;
                default:
                        dev_dbg(isp->dev, "Num%2d type %s, name is %s\n",
                                i + NUM_OF_SPS, fw_type_name[bd[i].header.type],
                                bd[i].name);
                }
        }

        return 0;
}

void atomisp_css_set_isp_config_id(struct atomisp_sub_device *asd,
                                   uint32_t isp_config_id)
{
        asd->params.config.isp_config_id = isp_config_id;
}

void atomisp_css_set_isp_config_applied_frame(struct atomisp_sub_device *asd,
        struct ia_css_frame *output_frame)
{
        asd->params.config.output_frame = output_frame;
}

int atomisp_get_css_dbgfunc(void)
{
        return dbg_func;
}

int atomisp_set_css_dbgfunc(struct atomisp_device *isp, int opt)
{
        int ret;

        ret = __set_css_print_env(isp, opt);
        if (ret == 0)
                dbg_func = opt;

        return ret;
}

void atomisp_en_dz_capt_pipe(struct atomisp_sub_device *asd, bool enable)
{
        ia_css_en_dz_capt_pipe(
            asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
            enable);
}

struct ia_css_dvs_grid_info *atomisp_css_get_dvs_grid_info(
    struct ia_css_grid_info *grid_info)
{
        if (!grid_info)
                return NULL;

#ifdef IA_CSS_DVS_STAT_GRID_INFO_SUPPORTED
        return &grid_info->dvs_grid.dvs_grid_info;
#else
        return &grid_info->dvs_grid;
#endif
}