/* * 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 #include #include #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 "atomisp_acc.h" #include "hrt/hive_isp_css_mm_hrt.h" #include #include "ia_css_debug.h" #include "ia_css_isp_param.h" #include "sh_css_hrt.h" #include "ia_css_isys.h" #include /* 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; }; void atomisp_css_debug_dump_sp_sw_debug_info(void) { ia_css_debug_dump_sp_sw_debug_info(); } void atomisp_css_debug_dump_debug_info(const char *context) { ia_css_debug_dump_debug_info(context); } void atomisp_css_debug_set_dtrace_level(const unsigned int trace_level) { ia_css_debug_set_dtrace_level(trace_level); } unsigned int atomisp_css_debug_get_dtrace_level(void) { return ia_css_debug_trace_level; } void atomisp_css2_hw_store_8(hrt_address addr, uint8_t data) { unsigned long flags; spin_lock_irqsave(&mmio_lock, flags); _hrt_master_port_store_8(addr, data); spin_unlock_irqrestore(&mmio_lock, flags); } static void atomisp_css2_hw_store_16(hrt_address addr, uint16_t data) { unsigned long flags; spin_lock_irqsave(&mmio_lock, flags); _hrt_master_port_store_16(addr, data); spin_unlock_irqrestore(&mmio_lock, flags); } static void atomisp_css2_hw_store_32(hrt_address addr, uint32_t data) { unsigned long flags; spin_lock_irqsave(&mmio_lock, flags); _hrt_master_port_store_32(addr, data); spin_unlock_irqrestore(&mmio_lock, flags); } static uint8_t atomisp_css2_hw_load_8(hrt_address addr) { unsigned long flags; uint8_t ret; spin_lock_irqsave(&mmio_lock, flags); ret = _hrt_master_port_load_8(addr); spin_unlock_irqrestore(&mmio_lock, flags); return ret; } uint16_t atomisp_css2_hw_load_16(hrt_address addr) { unsigned long flags; uint16_t ret; spin_lock_irqsave(&mmio_lock, flags); ret = _hrt_master_port_load_16(addr); spin_unlock_irqrestore(&mmio_lock, flags); return ret; } uint32_t atomisp_css2_hw_load_32(hrt_address addr) { unsigned long flags; uint32_t ret; spin_lock_irqsave(&mmio_lock, flags); ret = _hrt_master_port_load_32(addr); spin_unlock_irqrestore(&mmio_lock, flags); return ret; } static void atomisp_css2_hw_store(hrt_address addr, const void *from, uint32_t n) { unsigned long flags; unsigned int i; unsigned int _to = (unsigned int)addr; const char *_from = (const char *)from; spin_lock_irqsave(&mmio_lock, flags); for (i = 0; i < n; i++, _to++, _from++) _hrt_master_port_store_8(_to , *_from); spin_unlock_irqrestore(&mmio_lock, flags); } static void atomisp_css2_hw_load(hrt_address addr, void *to, uint32_t n) { unsigned long flags; unsigned int i; char *_to = (char *)to; unsigned int _from = (unsigned int)addr; spin_lock_irqsave(&mmio_lock, flags); for (i = 0; i < n; i++, _to++, _from++) *_to = _hrt_master_port_load_8(_from); spin_unlock_irqrestore(&mmio_lock, flags); } static int atomisp_css2_dbg_print(const char *fmt, va_list args) { vprintk(fmt, args); return 0; } static int atomisp_css2_dbg_ftrace_print(const char *fmt, va_list args) { ftrace_vprintk(fmt, args); return 0; } static int atomisp_css2_err_print(const char *fmt, va_list args) { vprintk(fmt, args); return 0; } void atomisp_store_uint32(hrt_address addr, uint32_t data) { atomisp_css2_hw_store_32(addr, data); } void atomisp_load_uint32(hrt_address addr, uint32_t *data) { *data = atomisp_css2_hw_load_32(addr); } static int hmm_get_mmu_base_addr(unsigned int *mmu_base_addr) { if (sh_mmu_mrfld.get_pd_base == NULL) { dev_err(atomisp_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 atomisp_isp_parameters_clean_up( struct atomisp_css_isp_config *config) { /* * Set NULL to configs pointer to avoid they are set into isp again when * some configs are changed and need to be updated later. */ memset(config, 0, sizeof(*config)); } 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, bool force) { struct atomisp_device *isp = asd->isp; int i; unsigned long timeout; if (!stream_env->stream) return 0; if (!force) { for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) if (stream_env->update_pipe[i]) break; if (i == IA_CSS_PIPE_ID_NUM) return 0; } if (stream_env->stream_state == CSS_STREAM_STARTED && ia_css_stream_stop(stream_env->stream) != IA_CSS_SUCCESS) { 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) != IA_CSS_SUCCESS) { 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, bool force) { int ret, i; for (i = 0; i < ATOMISP_INPUT_STREAM_NUM; i++) { ret = __destroy_stream(asd, &asd->stream_env[i], force); 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) != IA_CSS_SUCCESS) return -EINVAL; if (ia_css_stream_get_info(stream_env->stream, &stream_env->stream_info) != IA_CSS_SUCCESS) { 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], true); return ret; } static int __destroy_stream_pipes(struct atomisp_sub_device *asd, struct atomisp_stream_env *stream_env, bool force) { 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] || !(force || stream_env->update_pipe[i])) continue; if (ia_css_pipe_destroy(stream_env->pipes[i]) != IA_CSS_SUCCESS) { 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, bool force) { 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], force); if (ret) return ret; } return 0; } void atomisp_destroy_pipes_stream_force(struct atomisp_sub_device *asd) { __destroy_streams(asd, true); __destroy_pipes(asd, true); } 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 == CSS_CAPTURE_MODE_RAW) stream_env->pipe_configs[pipe_id].enable_dz = false; #ifdef ISP2401 /* the isp default to use ISP2.2 and the camera hal will * control whether use isp2.7 */ if (asd->select_isp_version->val == ATOMISP_CSS_ISP_PIPE_VERSION_2_7) stream_env->pipe_configs[pipe_id].isp_pipe_version = SH_CSS_ISP_PIPE_VERSION_2_7; else stream_env->pipe_configs[pipe_id].isp_pipe_version = SH_CSS_ISP_PIPE_VERSION_2_2; #endif 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 == CSS_PIPE_ID_ACC || pipe_id == 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; else return false; case ATOMISP_RUN_MODE_PREVIEW: if (!asd->continuous_mode->val) { if (pipe_id == IA_CSS_PIPE_ID_PREVIEW) return true; else return false; } /* fall through to ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE */ case ATOMISP_RUN_MODE_CONTINUOUS_CAPTURE: if (pipe_id == IA_CSS_PIPE_ID_CAPTURE || pipe_id == IA_CSS_PIPE_ID_PREVIEW) return true; else 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; } /* fall through to ATOMISP_RUN_MODE_SDV */ case ATOMISP_RUN_MODE_SDV: if (pipe_id == IA_CSS_PIPE_ID_CAPTURE || pipe_id == IA_CSS_PIPE_ID_VIDEO) return true; else 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; enum ia_css_err ret; if (pipe_id >= IA_CSS_PIPE_ID_NUM) return -EINVAL; if (pipe_id != CSS_PIPE_ID_ACC && !stream_env->pipe_configs[pipe_id].output_info[0].res.width) return 0; if (pipe_id == 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 != IA_CSS_SUCCESS) dev_err(isp->dev, "create pipe[%d] error.\n", pipe_id); return ret; } static int __create_pipes(struct atomisp_sub_device *asd) { enum ia_css_err 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 != IA_CSS_SUCCESS) 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; } void atomisp_create_pipes_stream(struct atomisp_sub_device *asd) { __create_pipes(asd); __create_streams(asd); } int atomisp_css_update_stream(struct atomisp_sub_device *asd) { int ret; struct atomisp_device *isp = asd->isp; if (__destroy_streams(asd, true) != IA_CSS_SUCCESS) dev_warn(isp->dev, "destroy stream failed.\n"); if (__destroy_pipes(asd, true) != IA_CSS_SUCCESS) dev_warn(isp->dev, "destroy pipe failed.\n"); ret = __create_pipes(asd); if (ret != IA_CSS_SUCCESS) { dev_err(isp->dev, "create pipe failed %d.\n", ret); return -EIO; } ret = __create_streams(asd); if (ret != IA_CSS_SUCCESS) { dev_warn(isp->dev, "create stream failed %d.\n", ret); __destroy_pipes(asd, true); return -EIO; } return 0; } int atomisp_css_init(struct atomisp_device *isp) { unsigned int mmu_base_addr; int ret; enum ia_css_err err; ret = hmm_get_mmu_base_addr(&mmu_base_addr); if (ret) return ret; /* Init ISP */ err = ia_css_init(&isp->css_env.isp_css_env, NULL, (uint32_t)mmu_base_addr, IA_CSS_IRQ_TYPE_PULSE); if (err != IA_CSS_SUCCESS) { 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_css2_dbg_print; else ret = -EINVAL; return ret; } int atomisp_css_check_firmware_version(struct atomisp_device *isp) { if (!sh_css_check_firmware_version((void *)isp->firmware->data)) { dev_err(isp->dev, "Fw version check failed.\n"); return -EINVAL; } return 0; } int atomisp_css_load_firmware(struct atomisp_device *isp) { enum ia_css_err 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_css2_err_print; /* load isp fw into ISP memory */ err = ia_css_load_firmware(&isp->css_env.isp_css_env, &isp->css_env.isp_css_fw); if (err != IA_CSS_SUCCESS) { dev_err(isp->dev, "css load fw failed.\n"); return -EINVAL; } return 0; } void atomisp_css_unload_firmware(struct atomisp_device *isp) { ia_css_unload_firmware(); } 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]; atomisp_isp_parameters_clean_up(&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(&mmu_base_addr); if (ret) { dev_err(isp->dev, "get base address error.\n"); return -EINVAL; } ret = ia_css_init(&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 != IA_CSS_SUCCESS) { 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 ia_css_csi2_port port, unsigned int *infos) { #ifndef ISP2401_NEW_INPUT_SYSTEM ia_css_isys_rx_get_irq_info(port, infos); #else *infos = 0; #endif } void atomisp_css_rx_clear_irq_info(enum ia_css_csi2_port port, unsigned int infos) { #ifndef ISP2401_NEW_INPUT_SYSTEM ia_css_isys_rx_clear_irq_info(port, infos); #endif } int atomisp_css_irq_enable(struct atomisp_device *isp, enum atomisp_css_irq_info info, bool enable) { if (ia_css_irq_enable(info, enable) != IA_CSS_SUCCESS) { dev_warn(isp->dev, "%s:Invalid irq info.\n", __func__); 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 atomisp_css_buffer_type css_buf_type, enum atomisp_css_pipe_id css_pipe_id) { struct atomisp_stream_env *stream_env = &asd->stream_env[stream_id]; struct ia_css_buffer css_buf = {0}; enum ia_css_err 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 != IA_CSS_SUCCESS) 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 atomisp_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 atomisp_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 atomisp_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; } void atomisp_css_mmu_invalidate_cache(void) { ia_css_mmu_invalidate_cache(); } void atomisp_css_mmu_invalidate_tlb(void) { ia_css_mmu_invalidate_cache(); } void atomisp_css_mmu_set_page_table_base_index(unsigned long base_index) { } /* * Check whether currently running MIPI buffer size fulfill * the requirement of the stream to be run */ bool __need_realloc_mipi_buffer(struct atomisp_device *isp) { unsigned int i; for (i = 0; i < isp->num_of_streams; i++) { struct atomisp_sub_device *asd = &isp->asd[i]; if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) continue; if (asd->mipi_frame_size < isp->mipi_frame_size) return true; } return false; } int atomisp_css_start(struct atomisp_sub_device *asd, enum atomisp_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) { if (__destroy_streams(asd, true)) dev_warn(isp->dev, "destroy stream failed.\n"); if (__destroy_pipes(asd, true)) dev_warn(isp->dev, "destroy pipe failed.\n"); if (__create_pipes(asd)) { dev_err(isp->dev, "create pipe error.\n"); return -EINVAL; } if (__create_streams(asd)) { dev_err(isp->dev, "create stream error.\n"); ret = -EINVAL; goto stream_err; } /* in_reset == true, extension firmwares are reloaded after the recovery */ atomisp_acc_load_extensions(asd); } /* * 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 == false) { if (__create_pipes(asd)) { dev_err(isp->dev, "create pipe error.\n"); return -EINVAL; } if (__create_streams(asd)) { dev_err(isp->dev, "create stream error.\n"); ret = -EINVAL; goto stream_err; } } /* * 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() != IA_CSS_SUCCESS) { 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) != IA_CSS_SUCCESS) { 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: __destroy_streams(asd, true); stream_err: __destroy_pipes(asd, true); /* 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); atomisp_isp_parameters_clean_up(&asd->params.config); } void atomisp_css_update_isp_params_on_pipe(struct atomisp_sub_device *asd, struct ia_css_pipe *pipe) { enum ia_css_err 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 != IA_CSS_SUCCESS) dev_warn(asd->isp->dev, "%s: ia_css_stream_set_isp_config_on_pipe failed %d\n", __func__, ret); atomisp_isp_parameters_clean_up(&asd->params.config); } int atomisp_css_queue_buffer(struct atomisp_sub_device *asd, enum atomisp_input_stream_id stream_id, enum atomisp_css_pipe_id pipe_id, enum atomisp_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) != IA_CSS_SUCCESS) return -EINVAL; return 0; } int atomisp_css_dequeue_buffer(struct atomisp_sub_device *asd, enum atomisp_input_stream_id stream_id, enum atomisp_css_pipe_id pipe_id, enum atomisp_css_buffer_type buf_type, struct atomisp_css_buffer *isp_css_buffer) { struct atomisp_device *isp = asd->isp; enum ia_css_err err; err = ia_css_pipe_dequeue_buffer( asd->stream_env[stream_id].pipes[pipe_id], &isp_css_buffer->css_buffer); if (err != IA_CSS_SUCCESS) { 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, uint16_t 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 atomisp_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 atomisp_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 atomisp_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 stream_index = atomisp_source_pad_to_stream_id(asd, source_pad); 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[stream_index].pipes[pipe_id], &p_info) != IA_CSS_SUCCESS) { 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 != 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 atomisp_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_get_dis_statistics(struct atomisp_sub_device *asd, struct atomisp_css_buffer *isp_css_buffer, struct ia_css_isp_dvs_statistics_map *dvs_map) { if (asd->params.dvs_stat) { if (dvs_map) ia_css_translate_dvs2_statistics( asd->params.dvs_stat, dvs_map); else ia_css_get_dvs2_statistics(asd->params.dvs_stat, isp_css_buffer->css_buffer.data.stats_dvs); } } int atomisp_css_dequeue_event(struct atomisp_css_event *current_event) { if (ia_css_dequeue_event(¤t_event->event) != IA_CSS_SUCCESS) return -EINVAL; return 0; } 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 atomisp_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_css_stream_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_css_stream_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_css_stream_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 = IA_CSS_STREAM_FORMAT_USER_DEF1; s_config->isys_config[IA_CSS_STREAM_ISYS_STREAM_1].format = IA_CSS_STREAM_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_css_stream_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_css_stream_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 atomisp_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 atomisp_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) != IA_CSS_SUCCESS) { if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER) 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, mipi_port_ID_t port, unsigned int num_lanes, unsigned int timeout, unsigned int mipi_freq, enum atomisp_css_stream_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; } int atomisp_css_frame_allocate(struct atomisp_css_frame **frame, unsigned int width, unsigned int height, enum atomisp_css_frame_format format, unsigned int padded_width, unsigned int raw_bit_depth) { if (ia_css_frame_allocate(frame, width, height, format, padded_width, raw_bit_depth) != IA_CSS_SUCCESS) return -ENOMEM; return 0; } int atomisp_css_frame_allocate_from_info(struct atomisp_css_frame **frame, const struct atomisp_css_frame_info *info) { if (ia_css_frame_allocate_from_info(frame, info) != IA_CSS_SUCCESS) return -ENOMEM; return 0; } void atomisp_css_frame_free(struct atomisp_css_frame *frame) { ia_css_frame_free(frame); } int atomisp_css_frame_map(struct atomisp_css_frame **frame, const struct atomisp_css_frame_info *info, const void *data, uint16_t attribute, void *context) { if (ia_css_frame_map(frame, info, data, attribute, context) != IA_CSS_SUCCESS) return -ENOMEM; return 0; } int atomisp_css_set_black_frame(struct atomisp_sub_device *asd, const struct atomisp_css_frame *raw_black_frame) { if (sh_css_set_black_frame( asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, raw_black_frame) != IA_CSS_SUCCESS) return -ENOMEM; return 0; } int atomisp_css_allocate_continuous_frames(bool init_time, struct atomisp_sub_device *asd) { if (ia_css_alloc_continuous_frame_remain( asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) != IA_CSS_SUCCESS) return -EINVAL; return 0; } void atomisp_css_update_continuous_frames(struct atomisp_sub_device *asd) { ia_css_update_continuous_frames( asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream); } int atomisp_css_stop(struct atomisp_sub_device *asd, enum atomisp_css_pipe_id pipe_id, bool in_reset) { struct atomisp_device *isp = asd->isp; struct atomisp_s3a_buf *s3a_buf; struct atomisp_dis_buf *dis_buf; struct atomisp_metadata_buf *md_buf; unsigned long irqflags; unsigned int i; /* if is called in atomisp_reset(), force destroy stream */ if (__destroy_streams(asd, true)) dev_err(isp->dev, "destroy stream failed.\n"); /* if is called in atomisp_reset(), force destroy all pipes */ if (__destroy_pipes(asd, true)) dev_err(isp->dev, "destroy pipes failed.\n"); 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); } atomisp_isp_parameters_clean_up(&asd->params.config); asd->params.css_update_params_needed = false; } /* move stats buffers to free queue list */ while (!list_empty(&asd->s3a_stats_in_css)) { s3a_buf = list_entry(asd->s3a_stats_in_css.next, struct atomisp_s3a_buf, list); list_del(&s3a_buf->list); list_add_tail(&s3a_buf->list, &asd->s3a_stats); } while (!list_empty(&asd->s3a_stats_ready)) { s3a_buf = list_entry(asd->s3a_stats_ready.next, struct atomisp_s3a_buf, list); list_del(&s3a_buf->list); list_add_tail(&s3a_buf->list, &asd->s3a_stats); } spin_lock_irqsave(&asd->dis_stats_lock, irqflags); while (!list_empty(&asd->dis_stats_in_css)) { dis_buf = list_entry(asd->dis_stats_in_css.next, struct atomisp_dis_buf, list); list_del(&dis_buf->list); list_add_tail(&dis_buf->list, &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++) { while (!list_empty(&asd->metadata_in_css[i])) { md_buf = list_entry(asd->metadata_in_css[i].next, struct atomisp_metadata_buf, list); list_del(&md_buf->list); list_add_tail(&md_buf->list, &asd->metadata[i]); } while (!list_empty(&asd->metadata_ready[i])) { md_buf = list_entry(asd->metadata_ready[i].next, struct atomisp_metadata_buf, list); list_del(&md_buf->list); list_add_tail(&md_buf->list, &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)); return 0; } int 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; return 0; } void atomisp_css_disable_vf_pp(struct atomisp_sub_device *asd, bool disable) { int i; for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL] .pipe_extra_configs[i].disable_vf_pp = !!disable; } 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; 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}; */ 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: arbitary 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; 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 atomisp_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 atomisp_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 atomisp_css_frame_info *info, enum frame_info_type type, enum ia_css_pipe_id pipe_id) { struct atomisp_device *isp = asd->isp; enum ia_css_err ret; struct ia_css_pipe_info p_info; /* FIXME! No need to destroy/recreate all streams */ if (__destroy_streams(asd, true)) dev_warn(isp->dev, "destroy stream failed.\n"); if (__destroy_pipes(asd, true)) dev_warn(isp->dev, "destroy pipe failed.\n"); if (__create_pipes(asd)) return -EINVAL; if (__create_streams(asd)) goto stream_err; ret = ia_css_pipe_get_info( asd->stream_env[stream_index] .pipes[pipe_id], &p_info); if (ret == IA_CSS_SUCCESS) { 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; case ATOMISP_CSS_RAW_FRAME: *info = p_info.raw_output_info; dev_dbg(isp->dev, "getting raw frame info.\n"); } 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; } stream_err: __destroy_pipes(asd, true); return -EINVAL; } 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; else 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; 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; else 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, uint16_t source_pad, struct atomisp_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_source_pad_to_stream_id(asd, source_pad); else { stream_index = (pipe_index == IA_CSS_PIPE_ID_YUVPP) ? ATOMISP_INPUT_STREAM_VIDEO : atomisp_source_pad_to_stream_id(asd, source_pad); } if (IA_CSS_SUCCESS != 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 atomisp_css_frame_format format) { asd->stream_env[stream_index].pipe_configs[IA_CSS_PIPE_ID_COPY]. default_capture_config.mode = 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 atomisp_css_frame_format format) { asd->stream_env[stream_index].pipe_configs[IA_CSS_PIPE_ID_YUVPP]. default_capture_config.mode = 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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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) { enum ia_css_err ret; #ifdef ISP2401 dev_dbg(asd->isp->dev, "%s num_capture:%d skip:%d offset:%d\n", __func__, num_captures, skip, offset); #endif ret = ia_css_stream_capture( asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, num_captures, skip, offset); if (ret != IA_CSS_SUCCESS) return -EINVAL; return 0; } int atomisp_css_exp_id_capture(struct atomisp_sub_device *asd, int exp_id) { enum ia_css_err ret; ret = ia_css_stream_capture_frame( asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, exp_id); if (ret == IA_CSS_ERR_QUEUE_IS_FULL) { /* capture cmd queue is full */ return -EBUSY; } else if (ret != IA_CSS_SUCCESS) { return -EIO; } return 0; } int atomisp_css_exp_id_unlock(struct atomisp_sub_device *asd, int exp_id) { enum ia_css_err ret; ret = ia_css_unlock_raw_frame( asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, exp_id); if (ret == IA_CSS_ERR_QUEUE_IS_FULL) return -EAGAIN; else if (ret != IA_CSS_SUCCESS) 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_send_input_frame(struct atomisp_sub_device *asd, unsigned short *data, unsigned int width, unsigned int height) { ia_css_stream_send_input_frame( asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, data, width, height); } bool atomisp_css_isp_has_started(void) { return ia_css_isp_has_started(); } void atomisp_css_request_flash(struct atomisp_sub_device *asd) { ia_css_stream_request_flash( asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream); } void atomisp_css_set_wb_config(struct atomisp_sub_device *asd, struct atomisp_css_wb_config *wb_config) { asd->params.config.wb_config = wb_config; } void atomisp_css_set_ob_config(struct atomisp_sub_device *asd, struct atomisp_css_ob_config *ob_config) { asd->params.config.ob_config = ob_config; } void atomisp_css_set_dp_config(struct atomisp_sub_device *asd, struct atomisp_css_dp_config *dp_config) { asd->params.config.dp_config = dp_config; } void atomisp_css_set_de_config(struct atomisp_sub_device *asd, struct atomisp_css_de_config *de_config) { asd->params.config.de_config = de_config; } void atomisp_css_set_dz_config(struct atomisp_sub_device *asd, struct atomisp_css_dz_config *dz_config) { asd->params.config.dz_config = dz_config; } void atomisp_css_set_default_de_config(struct atomisp_sub_device *asd) { asd->params.config.de_config = NULL; } void atomisp_css_set_ce_config(struct atomisp_sub_device *asd, struct atomisp_css_ce_config *ce_config) { asd->params.config.ce_config = ce_config; } void atomisp_css_set_nr_config(struct atomisp_sub_device *asd, struct atomisp_css_nr_config *nr_config) { asd->params.config.nr_config = nr_config; } void atomisp_css_set_ee_config(struct atomisp_sub_device *asd, struct atomisp_css_ee_config *ee_config) { asd->params.config.ee_config = ee_config; } void atomisp_css_set_tnr_config(struct atomisp_sub_device *asd, struct atomisp_css_tnr_config *tnr_config) { asd->params.config.tnr_config = tnr_config; } void atomisp_css_set_cc_config(struct atomisp_sub_device *asd, struct atomisp_css_cc_config *cc_config) { asd->params.config.cc_config = cc_config; } void atomisp_css_set_macc_table(struct atomisp_sub_device *asd, struct atomisp_css_macc_table *macc_table) { asd->params.config.macc_table = macc_table; } void atomisp_css_set_macc_config(struct atomisp_sub_device *asd, struct atomisp_css_macc_config *macc_config) { asd->params.config.macc_config = macc_config; } void atomisp_css_set_ecd_config(struct atomisp_sub_device *asd, struct atomisp_css_ecd_config *ecd_config) { asd->params.config.ecd_config = ecd_config; } void atomisp_css_set_ynr_config(struct atomisp_sub_device *asd, struct atomisp_css_ynr_config *ynr_config) { asd->params.config.ynr_config = ynr_config; } void atomisp_css_set_fc_config(struct atomisp_sub_device *asd, struct atomisp_css_fc_config *fc_config) { asd->params.config.fc_config = fc_config; } void atomisp_css_set_ctc_config(struct atomisp_sub_device *asd, struct atomisp_css_ctc_config *ctc_config) { asd->params.config.ctc_config = ctc_config; } void atomisp_css_set_cnr_config(struct atomisp_sub_device *asd, struct atomisp_css_cnr_config *cnr_config) { asd->params.config.cnr_config = cnr_config; } void atomisp_css_set_aa_config(struct atomisp_sub_device *asd, struct atomisp_css_aa_config *aa_config) { asd->params.config.aa_config = aa_config; } void atomisp_css_set_baa_config(struct atomisp_sub_device *asd, struct atomisp_css_baa_config *baa_config) { asd->params.config.baa_config = baa_config; } void atomisp_css_set_anr_config(struct atomisp_sub_device *asd, struct atomisp_css_anr_config *anr_config) { asd->params.config.anr_config = anr_config; } void atomisp_css_set_xnr_config(struct atomisp_sub_device *asd, struct atomisp_css_xnr_config *xnr_config) { asd->params.config.xnr_config = xnr_config; } void atomisp_css_set_yuv2rgb_cc_config(struct atomisp_sub_device *asd, struct atomisp_css_cc_config *yuv2rgb_cc_config) { asd->params.config.yuv2rgb_cc_config = yuv2rgb_cc_config; } void atomisp_css_set_rgb2yuv_cc_config(struct atomisp_sub_device *asd, struct atomisp_css_cc_config *rgb2yuv_cc_config) { asd->params.config.rgb2yuv_cc_config = rgb2yuv_cc_config; } void atomisp_css_set_xnr_table(struct atomisp_sub_device *asd, struct atomisp_css_xnr_table *xnr_table) { asd->params.config.xnr_table = xnr_table; } void atomisp_css_set_r_gamma_table(struct atomisp_sub_device *asd, struct atomisp_css_rgb_gamma_table *r_gamma_table) { asd->params.config.r_gamma_table = r_gamma_table; } void atomisp_css_set_g_gamma_table(struct atomisp_sub_device *asd, struct atomisp_css_rgb_gamma_table *g_gamma_table) { asd->params.config.g_gamma_table = g_gamma_table; } void atomisp_css_set_b_gamma_table(struct atomisp_sub_device *asd, struct atomisp_css_rgb_gamma_table *b_gamma_table) { asd->params.config.b_gamma_table = b_gamma_table; } void atomisp_css_set_gamma_table(struct atomisp_sub_device *asd, struct atomisp_css_gamma_table *gamma_table) { asd->params.config.gamma_table = gamma_table; } void atomisp_css_set_ctc_table(struct atomisp_sub_device *asd, struct atomisp_css_ctc_table *ctc_table) { int i; uint16_t *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 atomisp_css_anr_thres *anr_thres) { asd->params.config.anr_thres = anr_thres; } void atomisp_css_set_dvs_6axis(struct atomisp_sub_device *asd, struct atomisp_css_dvs_6axis *dvs_6axis) { asd->params.config.dvs_6axis_config = dvs_6axis; } void atomisp_css_set_gc_config(struct atomisp_sub_device *asd, struct atomisp_css_gc_config *gc_config) { asd->params.config.gc_config = gc_config; } void atomisp_css_set_3a_config(struct atomisp_sub_device *asd, struct atomisp_css_3a_config *s3a_config) { asd->params.config.s3a_config = s3a_config; } 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 atomisp_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 == NULL || coefs->hor_coefs.odd_imag == NULL || coefs->hor_coefs.even_real == NULL || coefs->hor_coefs.even_imag == NULL || coefs->ver_coefs.odd_real == NULL || coefs->ver_coefs.odd_imag == NULL || coefs->ver_coefs.even_real == NULL || coefs->ver_coefs.even_imag == NULL || asd->params.css_param.dvs2_coeff->hor_coefs.odd_real == NULL || asd->params.css_param.dvs2_coeff->hor_coefs.odd_imag == NULL || asd->params.css_param.dvs2_coeff->hor_coefs.even_real == NULL || asd->params.css_param.dvs2_coeff->hor_coefs.even_imag == NULL || asd->params.css_param.dvs2_coeff->ver_coefs.odd_real == NULL || asd->params.css_param.dvs2_coeff->ver_coefs.odd_imag == NULL || asd->params.css_param.dvs2_coeff->ver_coefs.even_real == NULL || asd->params.css_param.dvs2_coeff->ver_coefs.even_imag == NULL) 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_dvs2_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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 atomisp_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; if (asd->params.dvs_stat->hor_prod.odd_real == NULL || asd->params.dvs_stat->hor_prod.odd_imag == NULL || asd->params.dvs_stat->hor_prod.even_real == NULL || asd->params.dvs_stat->hor_prod.even_imag == NULL || asd->params.dvs_stat->ver_prod.odd_real == NULL || asd->params.dvs_stat->ver_prod.odd_imag == NULL || asd->params.dvs_stat->ver_prod.even_real == NULL || asd->params.dvs_stat->ver_prod.even_imag == NULL) return -EINVAL; /* isp needs to be streaming to get DIS statistics */ spin_lock_irqsave(&isp->lock, flags); if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) { spin_unlock_irqrestore(&isp->lock, flags); return -EINVAL; } spin_unlock_irqrestore(&isp->lock, flags); 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 atomisp_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 atomisp_css_shading_table *table) { asd->params.config.shading_table = table; } void atomisp_css_shading_table_free(struct atomisp_css_shading_table *table) { ia_css_shading_table_free(table); } struct atomisp_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 atomisp_css_morph_table *table) { asd->params.config.morph_table = table; } void atomisp_css_get_morph_table(struct atomisp_sub_device *asd, struct atomisp_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 atomisp_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 atomisp_css_morph_table *table) { ia_css_morph_table_free(table); } void atomisp_css_set_cont_prev_start_time(struct atomisp_device *isp, unsigned int overlap) { /* CSS 2.0 doesn't support this API. */ dev_dbg(isp->dev, "set cont prev start time is not supported.\n"); return; } void atomisp_css_acc_done(struct atomisp_sub_device *asd) { complete(&asd->acc.acc_done); } int atomisp_css_wait_acc_finish(struct atomisp_sub_device *asd) { int ret = 0; struct atomisp_device *isp = asd->isp; /* Unlock the isp mutex taken in IOCTL handler before sleeping! */ rt_mutex_unlock(&isp->mutex); if (wait_for_completion_interruptible_timeout(&asd->acc.acc_done, ATOMISP_ISP_TIMEOUT_DURATION) == 0) { dev_err(isp->dev, "<%s: completion timeout\n", __func__); atomisp_css_debug_dump_sp_sw_debug_info(); atomisp_css_debug_dump_debug_info(__func__); ret = -EIO; } rt_mutex_lock(&isp->mutex); return ret; } /* Set the ACC binary arguments */ int atomisp_css_set_acc_parameters(struct atomisp_acc_fw *acc_fw) { unsigned int mem; for (mem = 0; mem < ATOMISP_ACC_NR_MEMORY; mem++) { if (acc_fw->args[mem].length == 0) continue; ia_css_isp_param_set_css_mem_init(&acc_fw->fw->mem_initializers, IA_CSS_PARAM_CLASS_PARAM, mem, acc_fw->args[mem].css_ptr, acc_fw->args[mem].length); } return 0; } /* Load acc binary extension */ int atomisp_css_load_acc_extension(struct atomisp_sub_device *asd, struct atomisp_css_fw_info *fw, enum atomisp_css_pipe_id pipe_id, unsigned int type) { struct atomisp_css_fw_info **hd; fw->next = NULL; hd = &(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL] .pipe_configs[pipe_id].acc_extension); while (*hd) hd = &(*hd)->next; *hd = fw; asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL] .update_pipe[pipe_id] = true; return 0; } /* Unload acc binary extension */ void atomisp_css_unload_acc_extension(struct atomisp_sub_device *asd, struct atomisp_css_fw_info *fw, enum atomisp_css_pipe_id pipe_id) { struct atomisp_css_fw_info **hd; hd = &(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL] .pipe_configs[pipe_id].acc_extension); while (*hd && *hd != fw) hd = &(*hd)->next; if (!*hd) { dev_err(asd->isp->dev, "did not find acc fw for removal\n"); return; } *hd = fw->next; fw->next = NULL; asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL] .update_pipe[pipe_id] = true; } int atomisp_css_create_acc_pipe(struct atomisp_sub_device *asd) { struct atomisp_device *isp = asd->isp; struct ia_css_pipe_config *pipe_config; struct atomisp_stream_env *stream_env = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]; if (stream_env->acc_stream) { if (stream_env->acc_stream_state == CSS_STREAM_STARTED) { if (ia_css_stream_stop(stream_env->acc_stream) != IA_CSS_SUCCESS) { dev_err(isp->dev, "stop acc_stream failed.\n"); return -EBUSY; } } if (ia_css_stream_destroy(stream_env->acc_stream) != IA_CSS_SUCCESS) { dev_err(isp->dev, "destroy acc_stream failed.\n"); return -EBUSY; } stream_env->acc_stream = NULL; } pipe_config = &stream_env->pipe_configs[CSS_PIPE_ID_ACC]; ia_css_pipe_config_defaults(pipe_config); asd->acc.acc_stages = kzalloc(MAX_ACC_STAGES * sizeof(void *), GFP_KERNEL); if (!asd->acc.acc_stages) return -ENOMEM; pipe_config->acc_stages = asd->acc.acc_stages; pipe_config->mode = IA_CSS_PIPE_MODE_ACC; pipe_config->num_acc_stages = 0; /* * We delay the ACC pipeline creation to atomisp_css_start_acc_pipe, * because pipe configuration will soon be changed by * atomisp_css_load_acc_binary() */ return 0; } int atomisp_css_start_acc_pipe(struct atomisp_sub_device *asd) { struct atomisp_device *isp = asd->isp; struct atomisp_stream_env *stream_env = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]; struct ia_css_pipe_config *pipe_config = &stream_env->pipe_configs[IA_CSS_PIPE_ID_ACC]; if (ia_css_pipe_create(pipe_config, &stream_env->pipes[IA_CSS_PIPE_ID_ACC]) != IA_CSS_SUCCESS) { dev_err(isp->dev, "%s: ia_css_pipe_create failed\n", __func__); return -EBADE; } memset(&stream_env->acc_stream_config, 0, sizeof(struct ia_css_stream_config)); if (ia_css_stream_create(&stream_env->acc_stream_config, 1, &stream_env->pipes[IA_CSS_PIPE_ID_ACC], &stream_env->acc_stream) != IA_CSS_SUCCESS) { dev_err(isp->dev, "%s: create acc_stream error.\n", __func__); return -EINVAL; } stream_env->acc_stream_state = CSS_STREAM_CREATED; init_completion(&asd->acc.acc_done); asd->acc.pipeline = stream_env->pipes[IA_CSS_PIPE_ID_ACC]; atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_MAX, false); if (ia_css_start_sp() != IA_CSS_SUCCESS) { dev_err(isp->dev, "start sp error.\n"); return -EIO; } if (ia_css_stream_start(stream_env->acc_stream) != IA_CSS_SUCCESS) { dev_err(isp->dev, "acc_stream start error.\n"); return -EIO; } stream_env->acc_stream_state = CSS_STREAM_STARTED; return 0; } int atomisp_css_stop_acc_pipe(struct atomisp_sub_device *asd) { struct atomisp_stream_env *stream_env = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]; if (stream_env->acc_stream_state == CSS_STREAM_STARTED) { ia_css_stream_stop(stream_env->acc_stream); stream_env->acc_stream_state = CSS_STREAM_STOPPED; } return 0; } void atomisp_css_destroy_acc_pipe(struct atomisp_sub_device *asd) { struct atomisp_stream_env *stream_env = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL]; if (stream_env->acc_stream) { if (ia_css_stream_destroy(stream_env->acc_stream) != IA_CSS_SUCCESS) dev_warn(asd->isp->dev, "destroy acc_stream failed.\n"); stream_env->acc_stream = NULL; } if (stream_env->pipes[IA_CSS_PIPE_ID_ACC]) { if (ia_css_pipe_destroy(stream_env->pipes[IA_CSS_PIPE_ID_ACC]) != IA_CSS_SUCCESS) dev_warn(asd->isp->dev, "destroy ACC pipe failed.\n"); stream_env->pipes[IA_CSS_PIPE_ID_ACC] = NULL; stream_env->update_pipe[IA_CSS_PIPE_ID_ACC] = false; ia_css_pipe_config_defaults( &stream_env->pipe_configs[IA_CSS_PIPE_ID_ACC]); ia_css_pipe_extra_config_defaults( &stream_env->pipe_extra_configs[IA_CSS_PIPE_ID_ACC]); } asd->acc.pipeline = NULL; /* css 2.0 API limitation: ia_css_stop_sp() could be only called after * destroy all pipes */ ia_css_stop_sp(); kfree(asd->acc.acc_stages); asd->acc.acc_stages = NULL; atomisp_freq_scaling(asd->isp, ATOMISP_DFS_MODE_LOW, false); } int atomisp_css_load_acc_binary(struct atomisp_sub_device *asd, struct atomisp_css_fw_info *fw, unsigned int index) { struct ia_css_pipe_config *pipe_config = &asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL] .pipe_configs[IA_CSS_PIPE_ID_ACC]; if (index >= MAX_ACC_STAGES) { dev_dbg(asd->isp->dev, "%s: index(%d) out of range\n", __func__, index); return -ENOMEM; } pipe_config->acc_stages[index] = fw; pipe_config->num_acc_stages = index + 1; pipe_config->acc_num_execs = 1; return 0; } 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 && !asd->acc.pipeline) 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 = &isp->asd[0]; #ifndef ISP2401 bool reset_wdt_timer[MAX_STREAM_NUM] = {false}; #endif int i; while (!atomisp_css_dequeue_event(¤t_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); for (i = 0; i < isp->num_of_streams; i++) atomisp_wdt_stop(&isp->asd[i], 0); #ifndef ISP2401 atomisp_wdt(&isp->asd[0].wdt); #else queue_work(isp->wdt_work_queue, &isp->wdt_work); #endif 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 == CSS_EVENT_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 CSS_EVENT_OUTPUT_FRAME_DONE: frame_done_found[asd->index] = true; atomisp_buf_done(asd, 0, CSS_BUFFER_TYPE_OUTPUT_FRAME, current_event.pipe, true, stream_id); #ifndef ISP2401 reset_wdt_timer[asd->index] = true; /* ISP running */ #endif break; case CSS_EVENT_SEC_OUTPUT_FRAME_DONE: frame_done_found[asd->index] = true; atomisp_buf_done(asd, 0, CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME, current_event.pipe, true, stream_id); #ifndef ISP2401 reset_wdt_timer[asd->index] = true; /* ISP running */ #endif break; case CSS_EVENT_3A_STATISTICS_DONE: atomisp_buf_done(asd, 0, CSS_BUFFER_TYPE_3A_STATISTICS, current_event.pipe, false, stream_id); break; case CSS_EVENT_METADATA_DONE: atomisp_buf_done(asd, 0, CSS_BUFFER_TYPE_METADATA, current_event.pipe, false, stream_id); break; case CSS_EVENT_VF_OUTPUT_FRAME_DONE: atomisp_buf_done(asd, 0, CSS_BUFFER_TYPE_VF_OUTPUT_FRAME, current_event.pipe, true, stream_id); #ifndef ISP2401 reset_wdt_timer[asd->index] = true; /* ISP running */ #endif break; case CSS_EVENT_SEC_VF_OUTPUT_FRAME_DONE: atomisp_buf_done(asd, 0, CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME, current_event.pipe, true, stream_id); #ifndef ISP2401 reset_wdt_timer[asd->index] = true; /* ISP running */ #endif break; case CSS_EVENT_DIS_STATISTICS_DONE: atomisp_buf_done(asd, 0, CSS_BUFFER_TYPE_DIS_STATISTICS, current_event.pipe, false, stream_id); break; case CSS_EVENT_PIPELINE_DONE: css_pipe_done[asd->index] = true; break; case CSS_EVENT_ACC_STAGE_COMPLETE: atomisp_acc_done(asd, current_event.event.fw_handle); break; default: dev_dbg(isp->dev, "unhandled css stored event: 0x%x\n", current_event.event.type); break; } } #ifndef ISP2401 /* If there are no buffers queued then * delete wdt timer. */ for (i = 0; i < isp->num_of_streams; i++) { asd = &isp->asd[i]; if (!asd) continue; if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED) continue; if (!atomisp_buffers_queued(asd)) atomisp_wdt_stop(asd, false); else if (reset_wdt_timer[i]) /* SOF irq should not reset wdt timer. */ atomisp_wdt_refresh(asd, ATOMISP_WDT_KEEP_CURRENT_DELAY); } #endif 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 && 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; } int atomisp_css_dump_blob_infor(void) { 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 == NULL) return -EPERM; for (i = 1; i < sh_css_num_binaries; i++) dev_dbg(atomisp_dev, "Num%d binary id is %d, name is %s\n", i, bd[i-1].header.info.isp.sp.id, bd[i-1].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 atomisp_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 atomisp_css_dvs_grid_info *atomisp_css_get_dvs_grid_info( struct atomisp_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 }