diff options
Diffstat (limited to 'drivers/staging/media/atomisp/pci/atomisp_compat_css20.c')
-rw-r--r-- | drivers/staging/media/atomisp/pci/atomisp_compat_css20.c | 4016 |
1 files changed, 4016 insertions, 0 deletions
diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c new file mode 100644 index 000000000..fdc05548d --- /dev/null +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -0,0 +1,4016 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for Clovertrail PNW Camera Imaging ISP subsystem. + * + * Copyright (c) 2013 Intel Corporation. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + */ + +#include <media/videobuf-vmalloc.h> +#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> + +/* 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_css2_dbg_ftrace_print(const char *fmt, + va_list args) +{ + ftrace_vprintk(fmt, args); + return 0; +} + +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.acc_extension=%p.\n", + p_config->acc_extension); + dev_dbg(isp->dev, + "pipe_config.acc_stages=%p.\n", + p_config->acc_stages); + dev_dbg(isp->dev, + "pipe_config.num_acc_stages=%d.\n", + p_config->num_acc_stages); + dev_dbg(isp->dev, + "pipe_config.acc_num_execs=%d.\n", + p_config->acc_num_execs); + 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_TPG) { + dev_dbg(isp->dev, "stream_config.source.tpg.id=%d.\n", + s_config->source.tpg.id); + dev_dbg(isp->dev, "stream_config.source.tpg.mode=%d.\n", + s_config->source.tpg.mode); + dev_dbg(isp->dev, "stream_config.source.tpg.x_mask=%d.\n", + s_config->source.tpg.x_mask); + dev_dbg(isp->dev, "stream_config.source.tpg.x_delta=%d.\n", + s_config->source.tpg.x_delta); + dev_dbg(isp->dev, "stream_config.source.tpg.y_mask=%d.\n", + s_config->source.tpg.y_mask); + dev_dbg(isp->dev, "stream_config.source.tpg.y_delta=%d.\n", + s_config->source.tpg.y_delta); + dev_dbg(isp->dev, "stream_config.source.tpg.xy_mask=%d.\n", + s_config->source.tpg.xy_mask); + } 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(40); + 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_force(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 (ATOMISP_SOC_CAMERA(asd)) + stream_env->pipe_configs[pipe_id].enable_dz = true; + + 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: + if (ATOMISP_SOC_CAMERA(asd)) + stream_env->pipe_configs[pipe_id].enable_dz = true; + else + stream_env->pipe_configs[pipe_id].enable_dz = false; + break; + case IA_CSS_PIPE_ID_ACC: + stream_env->pipe_configs[pipe_id].mode = IA_CSS_PIPE_MODE_ACC; + 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 (!asd) + return false; + + if (pipe_id == IA_CSS_PIPE_ID_ACC || 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 (!asd->continuous_mode->val) { + if (pipe_id == IA_CSS_PIPE_ID_PREVIEW) + return true; + + return false; + } + fallthrough; + case ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE: + if (pipe_id == IA_CSS_PIPE_ID_CAPTURE || + pipe_id == IA_CSS_PIPE_ID_PREVIEW) + return true; + + return false; + case ATOMISP_RUN_MODE_VIDEO: + if (!asd->continuous_mode->val) { + if (pipe_id == IA_CSS_PIPE_ID_VIDEO || + pipe_id == IA_CSS_PIPE_ID_YUVPP) + return true; + else + return false; + } + fallthrough; + case ATOMISP_RUN_MODE_SDV: + if (pipe_id == IA_CSS_PIPE_ID_CAPTURE || + pipe_id == IA_CSS_PIPE_ID_VIDEO) + 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 (pipe_id != IA_CSS_PIPE_ID_ACC && + !stream_env->pipe_configs[pipe_id].output_info[0].res.width) + return 0; + + if (pipe_id == IA_CSS_PIPE_ID_ACC && + !stream_env->pipe_configs[pipe_id].acc_extension) + 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_force(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, NULL, + (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_css2_dbg_ftrace_print; + else if (opt == 2) + 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) +{ + struct atomisp_sub_device *asd; + unsigned int i; + + for (i = 0; i < isp->num_of_streams; i++) { + asd = &isp->asd[i]; + memset(&asd->params.config, 0, sizeof(asd->params.config)); + asd->params.css_update_params_needed = false; + } + + isp->css_initialized = false; + ia_css_uninit(); +} + +void atomisp_css_suspend(struct atomisp_device *isp) +{ + isp->css_initialized = false; + ia_css_uninit(); +} + +int atomisp_css_resume(struct atomisp_device *isp) +{ + unsigned int mmu_base_addr; + int ret; + + ret = hmm_get_mmu_base_addr(isp->dev, &mmu_base_addr); + if (ret) { + dev_err(isp->dev, "get base address error.\n"); + return -EINVAL; + } + + ret = ia_css_init(isp->dev, &isp->css_env.isp_css_env, NULL, + mmu_base_addr, IA_CSS_IRQ_TYPE_PULSE); + if (ret) { + dev_err(isp->dev, "re-init css failed.\n"); + return -EINVAL; + } + ia_css_enable_isys_event_queue(true); + + isp->css_initialized = true; + return 0; +} + +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) +{ +#ifndef ISP2401 + ia_css_isys_rx_get_irq_info(port, infos); +#else + *infos = 0; +#endif +} + +void atomisp_css_rx_clear_irq_info(enum mipi_port_id port, + unsigned int infos) +{ +#ifndef ISP2401 + ia_css_isys_rx_clear_irq_info(port, infos); +#endif +} + +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, + enable ? "enable" : "disable", 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 videobuf_vmalloc_memory *vm_mem, + 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 = vm_mem->vaddr; + + 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, + enum ia_css_pipe_id pipe_id, bool in_reset) +{ + struct atomisp_device *isp = asd->isp; + bool sp_is_started = false; + int ret = 0, i = 0; + + if (in_reset) { + ret = atomisp_css_update_stream(asd); + if (ret) + return ret; + + /* Invalidate caches. FIXME: should flush only necessary buffers */ + wbinvd(); + } + + /* + * For dual steam case, it is possible that: + * 1: for this stream, it is at the stage that: + * - after set_fmt is called + * - before stream on is called + * 2: for the other stream, the stream off is called which css reset + * has been done. + * + * Thus the stream created in set_fmt get destroyed and need to be + * recreated in the next stream on. + */ + if (!asd->stream_prepared) { + ret = atomisp_create_pipes_stream(asd); + if (ret) + return ret; + } + /* + * SP can only be started one time + * if atomisp_subdev_streaming_count() tell there already has some + * subdev at streamming, then SP should already be started previously, + * so need to skip start sp procedure + */ + if (atomisp_streaming_count(isp)) { + dev_dbg(isp->dev, "skip start sp\n"); + } else { + 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; + } else { + 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: + atomisp_destroy_pipes_stream_force(asd); + + /* css 2.0 API limitation: ia_css_stop_sp() could be only called after + * destroy all pipes + */ + /* + * SP can not be stop if other streams are in use + */ + if ((atomisp_streaming_count(isp) == 0) && sp_is_started) + ia_css_stop_sp(); + + 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) + 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) + ia_css_isp_3a_statistics_free(s3a_buf->s3a_data); + if (dis_buf) + 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, + int source_pad) +{ + 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, + ¤t_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; +} + +int atomisp_css_isys_two_stream_cfg(struct atomisp_sub_device *asd, + enum atomisp_input_stream_id stream_id, + enum atomisp_input_format input_format) +{ + 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 = + s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_0].input_res.width; + + s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].input_res.height = + s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_0].input_res.height / 2; + + 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_0].format = + ATOMISP_INPUT_FORMAT_USER_DEF1; + s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].format = + ATOMISP_INPUT_FORMAT_USER_DEF2; + s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].valid = true; + 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_raw_binning(struct atomisp_sub_device *asd, + bool enable) +{ + struct atomisp_stream_env *stream_env = + &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]; + unsigned int pipe; + + if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) + pipe = IA_CSS_PIPE_ID_VIDEO; + else + pipe = IA_CSS_PIPE_ID_PREVIEW; + + stream_env->pipe_extra_configs[pipe].enable_raw_binning = enable; + stream_env->update_pipe[pipe] = true; + if (enable) + stream_env->pipe_configs[pipe].output_info[0].padded_width = + stream_env->stream_config.input_config.effective_res.width; +} + +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) +{ + int i; + struct atomisp_device *isp = asd->isp; + unsigned int size_mem_words; + + for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) + asd->stream_env[i].stream_config.mode = mode; + + if (isp->inputs[asd->input_curr].type == TEST_PATTERN) { + struct ia_css_stream_config *s_config = + &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream_config; + s_config->mode = IA_CSS_INPUT_MODE_TPG; + s_config->source.tpg.mode = IA_CSS_TPG_MODE_CHECKERBOARD; + s_config->source.tpg.x_mask = (1 << 4) - 1; + s_config->source.tpg.x_delta = -2; + s_config->source.tpg.y_mask = (1 << 4) - 1; + s_config->source.tpg.y_delta = 3; + s_config->source.tpg.xy_mask = (1 << 8) - 1; + return; + } + + 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; + } +} + +void atomisp_css_video_enable_online(struct atomisp_sub_device *asd, + bool enable) +{ + struct atomisp_stream_env *stream_env = + &asd->stream_env[ATOMISP_INPUT_STREAM_VIDEO]; + 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; + } +} + +void atomisp_css_enable_continuous(struct atomisp_sub_device *asd, + bool enable) +{ + struct atomisp_stream_env *stream_env = + &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]; + int i; + + /* + * To SOC camera, there is only one YUVPP pipe in any case + * including ZSL/SDV/continuous viewfinder, so always set + * stream_config.continuous to 0. + */ + if (ATOMISP_USE_YUVPP(asd)) { + stream_env->stream_config.continuous = 0; + stream_env->stream_config.online = 1; + return; + } + + if (stream_env->stream_config.continuous != !!enable) { + stream_env->stream_config.continuous = !!enable; + stream_env->stream_config.pack_raw_pixels = true; + for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) + stream_env->update_pipe[i] = true; + } +} + +void atomisp_css_enable_cvf(struct atomisp_sub_device *asd, + bool enable) +{ + struct atomisp_stream_env *stream_env = + &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]; + int i; + + if (stream_env->stream_config.disable_cont_viewfinder != !enable) { + stream_env->stream_config.disable_cont_viewfinder = !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, + enum ia_css_pipe_id pipe_id, bool in_reset) +{ + struct atomisp_device *isp = asd->isp; + unsigned long irqflags; + unsigned int i; + + /* if is called in atomisp_reset(), force destroy streams and pipes */ + atomisp_destroy_pipes_stream_force(asd); + + atomisp_init_raw_buffer_bitmap(asd); + + /* + * SP can not be stop if other streams are in use + */ + if (atomisp_streaming_count(isp) == 0) + 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_capture); + atomisp_flush_params_queue(&asd->video_out_vf); + atomisp_flush_params_queue(&asd->video_out_preview); + atomisp_flush_params_queue(&asd->video_out_video_capture); + 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].camera); + + 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_ACC: + return IA_CSS_PIPE_MODE_ACC; + 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); +} + +static void __configure_video_preview_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_frame_info *css_output_info; + struct ia_css_stream_config *stream_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; + + /* + * second_output will be as video main output in SDV mode + * with SOC camera. output will be as video main output in + * normal video mode. + */ + if (asd->continuous_mode->val) + css_output_info = &stream_env->pipe_configs[pipe_id]. + output_info[ATOMISP_CSS_OUTPUT_SECOND_INDEX]; + else + css_output_info = &stream_env->pipe_configs[pipe_id]. + output_info[ATOMISP_CSS_OUTPUT_DEFAULT_INDEX]; + + css_output_info->res.width = width; + css_output_info->res.height = height; + css_output_info->format = format; + css_output_info->padded_width = min_width; + + /* isp binary 2.2 specific setting*/ + if (width > stream_config->input_config.effective_res.width || + height > stream_config->input_config.effective_res.height) { + stream_config->input_config.effective_res.width = width; + stream_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 addtional 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 facor: + * 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 addtional 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 void __configure_video_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]; + struct ia_css_frame_info *css_output_info; + + stream_env->pipe_configs[pipe_id].mode = + __pipe_id_to_pipe_mode(asd, pipe_id); + stream_env->update_pipe[pipe_id] = true; + + /* + * second_vf_output will be as video viewfinder in SDV mode + * with SOC camera. vf_output will be as video viewfinder in + * normal video mode. + */ + if (asd->continuous_mode->val) + css_output_info = &stream_env->pipe_configs[pipe_id]. + vf_output_info[ATOMISP_CSS_OUTPUT_SECOND_INDEX]; + else + css_output_info = &stream_env->pipe_configs[pipe_id]. + vf_output_info[ATOMISP_CSS_OUTPUT_DEFAULT_INDEX]; + + css_output_info->res.width = width; + css_output_info->res.height = height; + css_output_info->format = format; + css_output_info->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_force(asd); + return -EINVAL; +} + +static unsigned int atomisp_get_pipe_index(struct atomisp_sub_device *asd, + uint16_t source_pad) +{ + struct atomisp_device *isp = asd->isp; + /* + * to SOC camera, use yuvpp pipe. + */ + if (ATOMISP_USE_YUVPP(asd)) + return IA_CSS_PIPE_ID_YUVPP; + + switch (source_pad) { + case ATOMISP_SUBDEV_PAD_SOURCE_VIDEO: + if (asd->yuvpp_mode) + return IA_CSS_PIPE_ID_YUVPP; + if (asd->copy_mode) + return IA_CSS_PIPE_ID_COPY; + if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO + || asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) + return IA_CSS_PIPE_ID_VIDEO; + + return IA_CSS_PIPE_ID_CAPTURE; + case ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE: + if (asd->copy_mode) + return IA_CSS_PIPE_ID_COPY; + + return IA_CSS_PIPE_ID_CAPTURE; + case ATOMISP_SUBDEV_PAD_SOURCE_VF: + if (!atomisp_is_mbuscode_raw(asd->fmt[asd->capture_pad].fmt.code)) { + return IA_CSS_PIPE_ID_CAPTURE; + } + fallthrough; + case ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW: + if (asd->yuvpp_mode) + return IA_CSS_PIPE_ID_YUVPP; + if (asd->copy_mode) + return IA_CSS_PIPE_ID_COPY; + if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) + return IA_CSS_PIPE_ID_VIDEO; + + return IA_CSS_PIPE_ID_PREVIEW; + } + dev_warn(isp->dev, + "invalid source pad:%d, return default preview pipe index.\n", + source_pad); + return IA_CSS_PIPE_ID_PREVIEW; +} + +int atomisp_get_css_frame_info(struct atomisp_sub_device *asd, + u16 source_pad, + struct ia_css_frame_info *frame_info) +{ + struct ia_css_pipe_info info; + int pipe_index = atomisp_get_pipe_index(asd, source_pad); + int stream_index; + struct atomisp_device *isp = asd->isp; + + if (ATOMISP_SOC_CAMERA(asd)) { + stream_index = ATOMISP_INPUT_STREAM_GENERAL; + } else { + 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_err(isp->dev, "ia_css_pipe_get_info FAILED"); + return -EINVAL; + } + + switch (source_pad) { + case ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE: + *frame_info = info.output_info[0]; + break; + case ATOMISP_SUBDEV_PAD_SOURCE_VIDEO: + if (ATOMISP_USE_YUVPP(asd) && asd->continuous_mode->val) + *frame_info = info. + output_info[ATOMISP_CSS_OUTPUT_SECOND_INDEX]; + else + *frame_info = info. + output_info[ATOMISP_CSS_OUTPUT_DEFAULT_INDEX]; + break; + case ATOMISP_SUBDEV_PAD_SOURCE_VF: + if (stream_index == ATOMISP_INPUT_STREAM_POSTVIEW) + *frame_info = info.output_info[0]; + else + *frame_info = info.vf_output_info[0]; + break; + case ATOMISP_SUBDEV_PAD_SOURCE_PREVIEW: + if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO && + (pipe_index == IA_CSS_PIPE_ID_VIDEO || + pipe_index == IA_CSS_PIPE_ID_YUVPP)) + if (ATOMISP_USE_YUVPP(asd) && asd->continuous_mode->val) + *frame_info = info. + vf_output_info[ATOMISP_CSS_OUTPUT_SECOND_INDEX]; + else + *frame_info = info. + vf_output_info[ATOMISP_CSS_OUTPUT_DEFAULT_INDEX]; + else if (ATOMISP_USE_YUVPP(asd) && asd->continuous_mode->val) + *frame_info = + info.output_info[ATOMISP_CSS_OUTPUT_SECOND_INDEX]; + else + *frame_info = + info.output_info[ATOMISP_CSS_OUTPUT_DEFAULT_INDEX]; + + break; + default: + frame_info = NULL; + break; + } + return frame_info ? 0 : -EINVAL; +} + +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_yuvpp_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_YUVPP]. + default_capture_config.mode = + IA_CSS_CAPTURE_MODE_RAW; + + __configure_output(asd, stream_index, width, height, padded_width, + format, IA_CSS_PIPE_ID_YUVPP); + return 0; +} + +int atomisp_css_yuvpp_configure_viewfinder( + 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) +{ + struct atomisp_stream_env *stream_env = + &asd->stream_env[stream_index]; + enum ia_css_pipe_id pipe_id = IA_CSS_PIPE_ID_YUVPP; + + 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; + return 0; +} + +int atomisp_css_yuvpp_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_YUVPP); +} + +int atomisp_css_yuvpp_get_viewfinder_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_VF_FRAME, IA_CSS_PIPE_ID_YUVPP); +} + +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) +{ + /* + * to SOC camera, use yuvpp pipe. + */ + if (ATOMISP_USE_YUVPP(asd)) + __configure_video_preview_output(asd, ATOMISP_INPUT_STREAM_GENERAL, width, + height, + min_width, format, IA_CSS_PIPE_ID_YUVPP); + else + __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) +{ + enum ia_css_pipe_id pipe_id; + + /* + * to SOC camera, use yuvpp pipe. + */ + if (ATOMISP_USE_YUVPP(asd)) + pipe_id = IA_CSS_PIPE_ID_YUVPP; + else + pipe_id = IA_CSS_PIPE_ID_CAPTURE; + + __configure_output(asd, ATOMISP_INPUT_STREAM_GENERAL, width, height, + min_width, format, pipe_id); + 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) +{ + /* + * to SOC camera, use yuvpp pipe. + */ + if (ATOMISP_USE_YUVPP(asd)) + __configure_video_preview_output(asd, ATOMISP_INPUT_STREAM_GENERAL, width, + height, + min_width, format, IA_CSS_PIPE_ID_YUVPP); + else + __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) +{ + /* + * to SOC camera, video will use yuvpp pipe. + */ + if (ATOMISP_USE_YUVPP(asd)) + __configure_video_vf_output(asd, width, height, min_width, format, + IA_CSS_PIPE_ID_YUVPP); + else + __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) +{ + enum ia_css_pipe_id pipe_id; + + /* + * to SOC camera, video will use yuvpp pipe. + */ + if (ATOMISP_USE_YUVPP(asd)) + pipe_id = IA_CSS_PIPE_ID_YUVPP; + else + pipe_id = IA_CSS_PIPE_ID_CAPTURE; + + __configure_vf_output(asd, width, height, min_width, format, + pipe_id); + return 0; +} + +int atomisp_css_video_get_viewfinder_frame_info( + struct atomisp_sub_device *asd, + struct ia_css_frame_info *info) +{ + enum ia_css_pipe_id pipe_id; + enum frame_info_type frame_type = ATOMISP_CSS_VF_FRAME; + + if (ATOMISP_USE_YUVPP(asd)) { + pipe_id = IA_CSS_PIPE_ID_YUVPP; + if (asd->continuous_mode->val) + frame_type = ATOMISP_CSS_SECOND_VF_FRAME; + } else { + pipe_id = IA_CSS_PIPE_ID_VIDEO; + } + + return __get_frame_info(asd, ATOMISP_INPUT_STREAM_GENERAL, info, + frame_type, pipe_id); +} + +int atomisp_css_capture_get_viewfinder_frame_info( + struct atomisp_sub_device *asd, + struct ia_css_frame_info *info) +{ + enum ia_css_pipe_id pipe_id; + + if (ATOMISP_USE_YUVPP(asd)) + pipe_id = IA_CSS_PIPE_ID_YUVPP; + else + pipe_id = IA_CSS_PIPE_ID_CAPTURE; + + return __get_frame_info(asd, ATOMISP_INPUT_STREAM_GENERAL, info, + ATOMISP_CSS_VF_FRAME, pipe_id); +} + +int atomisp_css_capture_get_output_raw_frame_info( + struct atomisp_sub_device *asd, + struct ia_css_frame_info *info) +{ + if (ATOMISP_USE_YUVPP(asd)) + return 0; + + return __get_frame_info(asd, ATOMISP_INPUT_STREAM_GENERAL, info, + ATOMISP_CSS_RAW_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) +{ + enum ia_css_pipe_id pipe_id; + enum frame_info_type frame_type = ATOMISP_CSS_OUTPUT_FRAME; + + if (ATOMISP_USE_YUVPP(asd)) { + pipe_id = IA_CSS_PIPE_ID_YUVPP; + if (asd->continuous_mode->val) + frame_type = ATOMISP_CSS_SECOND_OUTPUT_FRAME; + } else { + pipe_id = IA_CSS_PIPE_ID_PREVIEW; + } + + return __get_frame_info(asd, ATOMISP_INPUT_STREAM_GENERAL, info, + frame_type, pipe_id); +} + +int atomisp_css_capture_get_output_frame_info( + struct atomisp_sub_device *asd, + struct ia_css_frame_info *info) +{ + enum ia_css_pipe_id pipe_id; + + if (ATOMISP_USE_YUVPP(asd)) + pipe_id = IA_CSS_PIPE_ID_YUVPP; + else + pipe_id = IA_CSS_PIPE_ID_CAPTURE; + + return __get_frame_info(asd, ATOMISP_INPUT_STREAM_GENERAL, info, + ATOMISP_CSS_OUTPUT_FRAME, pipe_id); +} + +int atomisp_css_video_get_output_frame_info( + struct atomisp_sub_device *asd, + struct ia_css_frame_info *info) +{ + enum ia_css_pipe_id pipe_id; + enum frame_info_type frame_type = ATOMISP_CSS_OUTPUT_FRAME; + + if (ATOMISP_USE_YUVPP(asd)) { + pipe_id = IA_CSS_PIPE_ID_YUVPP; + if (asd->continuous_mode->val) + frame_type = ATOMISP_CSS_SECOND_OUTPUT_FRAME; + } else { + pipe_id = IA_CSS_PIPE_ID_VIDEO; + } + + return __get_frame_info(asd, ATOMISP_INPUT_STREAM_GENERAL, info, + frame_type, pipe_id); +} + +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, + ATOMISP_USE_YUVPP(asd) ? + IA_CSS_PIPE_ID_YUVPP : 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, + ATOMISP_USE_YUVPP(asd) ? + IA_CSS_PIPE_ID_YUVPP : 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, + ATOMISP_USE_YUVPP(asd) ? + IA_CSS_PIPE_ID_YUVPP : 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, + ATOMISP_USE_YUVPP(asd) ? + IA_CSS_PIPE_ID_YUVPP : 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, + ATOMISP_USE_YUVPP(asd) ? + IA_CSS_PIPE_ID_YUVPP : 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 mis-match!\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 stablization 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 != ATOMISP_DEVICE_STREAMING_ENABLED) + 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 struct atomisp_sub_device *__get_atomisp_subdev( + struct ia_css_pipe *css_pipe, + struct atomisp_device *isp, + enum atomisp_input_stream_id *stream_id) +{ + int i, j, k; + struct atomisp_sub_device *asd; + struct atomisp_stream_env *stream_env; + + for (i = 0; i < isp->num_of_streams; i++) { + asd = &isp->asd[i]; + if (asd->streaming == ATOMISP_DEVICE_STREAMING_DISABLED) + continue; + for (j = 0; j < ATOMISP_INPUT_STREAM_NUM; j++) { + stream_env = &asd->stream_env[j]; + for (k = 0; k < IA_CSS_PIPE_ID_NUM; k++) { + if (stream_env->pipes[k] && + stream_env->pipes[k] == css_pipe) { + *stream_id = j; + return asd; + } + } + } + } + + return NULL; +} + +int atomisp_css_isr_thread(struct atomisp_device *isp, + bool *frame_done_found, + bool *css_pipe_done) +{ + enum atomisp_input_stream_id stream_id = 0; + struct atomisp_css_event current_event; + struct atomisp_sub_device *asd; + + lockdep_assert_held(&isp->mutex); + + while (!ia_css_dequeue_psys_event(¤t_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; + } + + asd = __get_atomisp_subdev(current_event.event.pipe, + isp, &stream_id); + if (!asd) { + 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(asd, ¤t_event); + switch (current_event.event.type) { + case IA_CSS_EVENT_TYPE_OUTPUT_FRAME_DONE: + dev_dbg(isp->dev, "event: Output frame done"); + frame_done_found[asd->index] = true; + atomisp_buf_done(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"); + frame_done_found[asd->index] = true; + atomisp_buf_done(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(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(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(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(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(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"); + css_pipe_done[asd->index] = true; + 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, j; + + /* Loop for each css stream */ + for (i = 0; i < isp->num_of_streams; i++) { + struct atomisp_sub_device *asd = &isp->asd[i]; + /* Loop for each css vc stream */ + for (j = 0; j < ATOMISP_INPUT_STREAM_NUM; j++) { + if (!asd->stream_env[j].stream) + continue; + + dev_dbg(isp->dev, + "stream #%d: mode: %d\n", j, + asd->stream_env[j].stream_config.mode); + if (asd->stream_env[j].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 +} |