/* * drivers/media/video/exynos/fimc-is-mc2/fimc-is-interface.c * * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * * The header file related to camera * * 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. */ #include #include #include #include #include "fimc-is-core.h" #include "fimc-is-regs.h" #include "fimc-is-err.h" #include "fimc-is-groupmgr.h" #include "fimc-is-subdev-ctrl.h" #include "fimc-is-interface.h" #include "fimc-is-debug.h" #include "fimc-is-clk-gate.h" u32 __iomem *notify_fcount_sen0; u32 __iomem *notify_fcount_sen1; u32 __iomem *notify_fcount_sen2; u32 __iomem *notify_fcount_sen3; #define RESUME_BIT (0x0) #define SUSPEND_BIT (0x2) #define SENSOR_INIT (0x4) #define LCD_ON (0x6) #define init_request_barrier(itf) mutex_init(&itf->request_barrier) #define enter_request_barrier(itf) mutex_lock(&itf->request_barrier); #define exit_request_barrier(itf) mutex_unlock(&itf->request_barrier); #define init_process_barrier(itf) spin_lock_init(&itf->process_barrier); #define enter_process_barrier(itf) spin_lock_irq(&itf->process_barrier); #define exit_process_barrier(itf) spin_unlock_irq(&itf->process_barrier); extern struct fimc_is_sysfs_debug sysfs_debug; /* func to register error report callback */ int fimc_is_set_err_report_vendor(struct fimc_is_interface *itf, void *err_report_data, int (*err_report_vendor)(void *data, u32 err_report_type)) { if (itf) { itf->err_report_data = err_report_data; itf->err_report_vendor = err_report_vendor; } return 0; } /* main func to handle error report */ static int fimc_is_err_report_handler(struct fimc_is_interface *itf, struct fimc_is_msg *msg) { struct fimc_is_core *core; struct fimc_is_device_ischain *device; #ifdef ENABLE_DTP struct fimc_is_device_sensor *sensor; #endif core = itf->core; device = &core->ischain[msg->instance]; err("IHC_REPORT_ERR(%d,%d,%d,%d,%d) is occured", msg->instance, msg->group, msg->param1, msg->param2, msg->param3); /* * if vendor error report func was registered, * call the func. */ if (itf->err_report_vendor) itf->err_report_vendor(itf->err_report_data, msg->param2); switch (msg->param2) { case REPORT_ERR_CIS_ID: warn("Occured the ERR_ID"); break; case REPORT_ERR_CIS_ECC: warn("Occured the ERR_ECC"); case REPORT_ERR_CIS_CRC: warn("Occured the ERR_CRC"); #ifdef ENABLE_DTP sensor = device->sensor; if (sensor && sensor->dtp_check) set_bit(FIMC_IS_BAD_FRAME_STOP, &sensor->force_stop); #endif break; case REPORT_ERR_CIS_OVERFLOW_VC0: warn("Occured the OVERFLOW_VC0"); break; case REPORT_ERR_CIS_LOST_FE_VC0: warn("Occured the LOST_FE_VC0"); break; case REPORT_ERR_CIS_LOST_FS_VC0: warn("Occured the LOST_FS_VC0"); break; case REPORT_ERR_CIS_SOT_VC0: warn("Occured the SOT_VC0"); break; case REPORT_ERR_CIS_SOT_VC1: warn("Occured the SOT_VC1"); break; case REPORT_ERR_CIS_SOT_VC2: warn("Occured the SOT_VC2"); break; case REPORT_ERR_CIS_SOT_VC3: warn("Occured the SOT_VC3"); break; case REPORT_ERR_3AA_OVERFLOW: warn("Occured the 3AA_OVERFLOW"); #ifdef OVERFLOW_PANIC_ENABLE_ISCHAIN panic("3AA overflow"); #else fimc_is_resource_dump(); #endif break; case REPORT_ERR_FLITE_D_OVERFLOW: warn("Occured the FLITE_D_OVERFLOW"); break; case REPORT_ERR_PREPROCESSOR_LOST_FRAME: warn("Occured the PREPROCESSOR_LOST_FRAME"); break; case REPORT_ERR_3A_ALGORITHM_DELAYED: warn("Occured the 3A_ALGORITHM_DELAYED"); break; default: warn("parameter is default"); break; } return 0; } void fimc_is_storefirm(struct fimc_is_interface *this) { struct fimc_is_core *core = this->core; size_t fw_size = FW_MEM_SIZE; info("%s\n", __func__); CALL_BUFOP(core->resourcemgr.minfo.pb_fw, sync_for_cpu, core->resourcemgr.minfo.pb_fw, 0, FW_BACKUP_SIZE, DMA_BIDIRECTIONAL); memcpy((void *)(this->itf_kvaddr + fw_size), (void *)(this->itf_kvaddr), FW_BACKUP_SIZE); } void fimc_is_restorefirm(struct fimc_is_interface *this) { struct fimc_is_core *core = this->core; size_t fw_size = FW_MEM_SIZE; info("%s\n", __func__); memcpy((void *)(this->itf_kvaddr), (void *)(this->itf_kvaddr + fw_size), FW_BACKUP_SIZE); CALL_BUFOP(core->resourcemgr.minfo.pb_fw, sync_for_device, core->resourcemgr.minfo.pb_fw, 0, FW_BACKUP_SIZE, DMA_BIDIRECTIONAL); } int fimc_is_set_fwboot(struct fimc_is_interface *this, int val) { int ret = 0; u32 set = 0; FIMC_BUG(!this); #ifdef FW_SUSPEND_RESUME switch (val) { case FIRST_LAUNCHING: /* first launching */ set &= ~(1 << RESUME_BIT); set |= (1 << SUSPEND_BIT); clear_bit(IS_IF_RESUME, &this->fw_boot); set_bit(IS_IF_SUSPEND, &this->fw_boot); break; case WARM_BOOT: /* TWIZ & main camera*/ set |= (1 << RESUME_BIT) | (1 << SUSPEND_BIT); set_bit(IS_IF_RESUME, &this->fw_boot); set_bit(IS_IF_SUSPEND, &this->fw_boot); break; case COLD_BOOT: /* front camera | !TWIZ camera | dual camera */ set = 0; clear_bit(IS_IF_RESUME, &this->fw_boot); clear_bit(IS_IF_SUSPEND, &this->fw_boot); break; default: err("unsupported val(0x%X)", val); set = 0; clear_bit(IS_IF_RESUME, &this->fw_boot); clear_bit(IS_IF_SUSPEND, &this->fw_boot); ret = -EINVAL; break; } #endif this->com_regs->set_fwboot = set; info(" fwboot : 0x%d (val:%d/0x%lX)\n", this->com_regs->set_fwboot, val, this->launch_state); return ret; } int print_fre_work_list(struct fimc_is_work_list *this) { struct fimc_is_work *work, *temp; if (!(this->id & TRACE_WORK_ID_MASK)) return 0; printk(KERN_ERR "[INF] fre(%02X, %02d) :", this->id, this->work_free_cnt); list_for_each_entry_safe(work, temp, &this->work_free_head, list) { printk(KERN_CONT "%X(%d)->", work->msg.command, work->fcount); } printk(KERN_CONT "X\n"); return 0; } static int set_free_work(struct fimc_is_work_list *this, struct fimc_is_work *work) { int ret = 0; unsigned long flags; if (work) { spin_lock_irqsave(&this->slock_free, flags); list_add_tail(&work->list, &this->work_free_head); this->work_free_cnt++; #ifdef TRACE_WORK print_fre_work_list(this); #endif spin_unlock_irqrestore(&this->slock_free, flags); } else { ret = -EFAULT; err("item is null ptr\n"); } return ret; } static int get_free_work(struct fimc_is_work_list *this, struct fimc_is_work **work) { int ret = 0; unsigned long flags; if (work) { spin_lock_irqsave(&this->slock_free, flags); if (this->work_free_cnt) { *work = container_of(this->work_free_head.next, struct fimc_is_work, list); list_del(&(*work)->list); this->work_free_cnt--; } else *work = NULL; spin_unlock_irqrestore(&this->slock_free, flags); } else { ret = -EFAULT; err("item is null ptr"); } return ret; } static int get_free_work_irq(struct fimc_is_work_list *this, struct fimc_is_work **work) { int ret = 0; if (work) { spin_lock(&this->slock_free); if (this->work_free_cnt) { *work = container_of(this->work_free_head.next, struct fimc_is_work, list); list_del(&(*work)->list); this->work_free_cnt--; } else *work = NULL; spin_unlock(&this->slock_free); } else { ret = -EFAULT; err("item is null ptr"); } return ret; } int print_req_work_list(struct fimc_is_work_list *this) { struct fimc_is_work *work, *temp; if (!(this->id & TRACE_WORK_ID_MASK)) return 0; printk(KERN_ERR "[INF] req(%02X, %02d) :", this->id, this->work_request_cnt); list_for_each_entry_safe(work, temp, &this->work_request_head, list) { printk(KERN_CONT "%X(%d)->", work->msg.command, work->fcount); } printk(KERN_CONT "X\n"); return 0; } static int print_work_data_state(struct fimc_is_interface *this) { struct work_struct *work; unsigned long *bits = NULL; work = &this->work_wq[INTR_GENERAL]; bits = (work_data_bits(work)); info("INTR_GENERAL wq state : (0x%lx)", *bits); work = &this->work_wq[INTR_30C_FDONE]; bits = (work_data_bits(work)); info("INTR_30C_FDONE wq state : (0x%lx)", *bits); work = &this->work_wq[INTR_31C_FDONE]; bits = (work_data_bits(work)); info("INTR_31C_FDONE wq state : (0x%lx)", *bits); work = &this->work_wq[INTR_30P_FDONE]; bits = (work_data_bits(work)); info("INTR_30P_FDONE wq state : (0x%lx)", *bits); work = &this->work_wq[INTR_31P_FDONE]; bits = (work_data_bits(work)); info("INTR_31P_FDONE wq state : (0x%lx)", *bits); work = &this->work_wq[INTR_I0X_FDONE]; bits = (work_data_bits(work)); info("INTR_I0X_FDONE wq state : (0x%lx)", *bits); work = &this->work_wq[INTR_I1X_FDONE]; bits = (work_data_bits(work)); info("INTR_I1X_FDONE wq state : (0x%lx)", *bits); work = &this->work_wq[INTR_SCX_FDONE]; bits = (work_data_bits(work)); info("INTR_SCC_FDONE wq state : (0x%lx)", *bits); work = &this->work_wq[INTR_M0X_FDONE]; bits = (work_data_bits(work)); info("INTR_M0X_FDONE wq state : (0x%lx)", *bits); work = &this->work_wq[INTR_M1X_FDONE]; bits = (work_data_bits(work)); info("INTR_M1X_FDONE wq state : (0x%lx)", *bits); work = &this->work_wq[INTR_SHOT_DONE]; bits = (work_data_bits(work)); info("INTR_SHOT_DONE wq state : (0x%lx)", *bits); return 0; } static int set_req_work(struct fimc_is_work_list *this, struct fimc_is_work *work) { int ret = 0; unsigned long flags; if (work) { spin_lock_irqsave(&this->slock_request, flags); list_add_tail(&work->list, &this->work_request_head); this->work_request_cnt++; #ifdef TRACE_WORK print_req_work_list(this); #endif spin_unlock_irqrestore(&this->slock_request, flags); } else { ret = -EFAULT; err("item is null ptr\n"); } return ret; } static int set_req_work_irq(struct fimc_is_work_list *this, struct fimc_is_work *work) { int ret = 0; if (work) { spin_lock(&this->slock_request); list_add_tail(&work->list, &this->work_request_head); this->work_request_cnt++; #ifdef TRACE_WORK print_req_work_list(this); #endif spin_unlock(&this->slock_request); } else { ret = -EFAULT; err("item is null ptr\n"); } return ret; } static int get_req_work(struct fimc_is_work_list *this, struct fimc_is_work **work) { int ret = 0; unsigned long flags; if (work) { spin_lock_irqsave(&this->slock_request, flags); if (this->work_request_cnt) { *work = container_of(this->work_request_head.next, struct fimc_is_work, list); list_del(&(*work)->list); this->work_request_cnt--; } else *work = NULL; spin_unlock_irqrestore(&this->slock_request, flags); } else { ret = -EFAULT; err("item is null ptr\n"); } return ret; } static void init_work_list(struct fimc_is_work_list *this, u32 id, u32 count) { u32 i; this->id = id; this->work_free_cnt = 0; this->work_request_cnt = 0; INIT_LIST_HEAD(&this->work_free_head); INIT_LIST_HEAD(&this->work_request_head); spin_lock_init(&this->slock_free); spin_lock_init(&this->slock_request); for (i = 0; i < count; ++i) set_free_work(this, &this->work[i]); init_waitqueue_head(&this->wait_queue); } static int set_busystate(struct fimc_is_interface *this, u32 command) { int ret; ret = test_and_set_bit(IS_IF_STATE_BUSY, &this->state); if (ret) warn("%d command : busy state is already set", command); return ret; } static int clr_busystate(struct fimc_is_interface *this, u32 command) { int ret; ret = test_and_clear_bit(IS_IF_STATE_BUSY, &this->state); if (!ret) warn("%d command : busy state is already clr", command); return !ret; } static int test_busystate(struct fimc_is_interface *this) { int ret = 0; ret = test_bit(IS_IF_STATE_BUSY, &this->state); return ret; } static int wait_lockstate(struct fimc_is_interface *this) { int ret = 0; ret = wait_event_timeout(this->lock_wait_queue, !atomic_read(&this->lock_pid), FIMC_IS_COMMAND_TIMEOUT); if (ret) { ret = 0; } else { err("timeout"); ret = -ETIME; } return ret; } static int wait_idlestate(struct fimc_is_interface *this) { int ret = 0; ret = wait_event_timeout(this->idle_wait_queue, !test_busystate(this), FIMC_IS_COMMAND_TIMEOUT); if (ret) { ret = 0; } else { err("timeout"); ret = -ETIME; } return ret; } static int wait_initstate(struct fimc_is_interface *this) { int ret = 0; ret = wait_event_timeout(this->init_wait_queue, test_bit(IS_IF_STATE_START, &this->state), FIMC_IS_STARTUP_TIMEOUT); if (ret) { ret = 0; } else { err("timeout"); ret = -ETIME; } return ret; } static void testnclr_wakeup(struct fimc_is_interface *this, u32 command) { int ret = 0; ret = clr_busystate(this, command); if (ret) err("current state is invalid(%ld)", this->state); wake_up(&this->idle_wait_queue); } static int waiting_is_ready(struct fimc_is_interface *itf) { int ret = 0; u32 try_count, log_count, status; FIMC_BUG(!itf); log_count = 0; try_count = 0; status = INTMSR0_GET_INTMSD0(readl(itf->regs + INTMSR0)); while (status) { udelay(1); if ((try_count / 100) > log_count) { log_count = try_count / 100; warn("command is being blocked for %dms", log_count * 100); } if (try_count >= TRY_RECV_AWARE_COUNT) { err("command is blocked"); fimc_is_hw_logdump(itf); ret = -EINVAL; break; } try_count++; status = INTMSR0_GET_INTMSD0(readl(itf->regs + INTMSR0)); } return ret; } static void send_interrupt(struct fimc_is_interface *interface) { writel(INTGR0_INTGD0, interface->regs + INTGR0); } #ifdef ENABLE_SYNC_REPROCESSING static inline void fimc_is_sync_reprocessing_queue(struct fimc_is_groupmgr *groupmgr, struct fimc_is_group *group) { int i; struct fimc_is_group_task *gtask; struct fimc_is_group *rgroup; struct fimc_is_framemgr *rframemgr; struct fimc_is_frame *rframe; ulong flags; if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &group->device->state)) return; gtask = &groupmgr->gtask[group->id]; if (atomic_read(>ask->rep_tick) < REPROCESSING_TICK_COUNT) { if (atomic_read(>ask->rep_tick) > 0) atomic_dec(>ask->rep_tick); return; } for (i = 0; i < FIMC_IS_STREAM_COUNT; ++i) { if (!groupmgr->group[i][group->slot]) continue; if (test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &groupmgr->group[i][group->slot]->device->state)) { rgroup = groupmgr->group[i][group->slot]; break; } } if (i == FIMC_IS_STREAM_COUNT) { mgwarn("reprocessing group not exists", group, group); return; } rframemgr = GET_HEAD_GROUP_FRAMEMGR(rgroup); if (rframemgr) { framemgr_e_barrier_irqs(rframemgr, 0, flags); rframe = peek_frame(rframemgr, FS_REQUEST); framemgr_x_barrier_irqr(rframemgr, 0, flags); if (rframe) kthread_queue_work(>ask->worker, &rframe->work); } else { mgerr("failed to get reprocessing frame manager", group, group); } } #endif static int fimc_is_set_cmd(struct fimc_is_interface *itf, struct fimc_is_msg *msg, struct fimc_is_msg *reply) { int ret = 0; int wait_lock = 0; u32 lock_pid = 0; u32 id; volatile struct is_common_reg __iomem *com_regs; #ifdef MEASURE_TIME #ifdef INTERFACE_TIME struct timeval measure_str, measure_end; #endif #endif FIMC_BUG(!itf); FIMC_BUG(!msg); FIMC_BUG(msg->instance >= FIMC_IS_STREAM_COUNT); FIMC_BUG(msg->command >= HIC_COMMAND_END); FIMC_BUG(!reply); if (!test_bit(IS_IF_STATE_OPEN, &itf->state)) { warn("interface close, %d cmd is cancel", msg->command); goto exit; } lock_pid = atomic_read(&itf->lock_pid); if (lock_pid && (lock_pid != current->pid)) { pr_info("itf LOCK, %d(%d) wait\n", current->pid, msg->command); wait_lock = wait_lockstate(itf); pr_info("itf UNLOCK, %d(%d) go\n", current->pid, msg->command); if (wait_lock) { err("wait_lockstate is fail, lock reset"); atomic_set(&itf->lock_pid, 0); } } enter_request_barrier(itf); itf->request_msg = *msg; #ifdef MEASURE_TIME #ifdef INTERFACE_TIME do_gettimeofday(&measure_str); #endif #endif switch (msg->command) { case HIC_STREAM_ON: if (itf->streaming[msg->instance] == IS_IF_STREAMING_ON) mwarn("already stream on", msg); break; case HIC_STREAM_OFF: if (itf->streaming[msg->instance] == IS_IF_STREAMING_OFF) mwarn("already stream off", msg); break; case HIC_PROCESS_START: if (itf->processing[msg->instance] == IS_IF_PROCESSING_ON) mwarn("already process start", msg); break; case HIC_PROCESS_STOP: if (itf->processing[msg->instance] == IS_IF_PROCESSING_OFF) mwarn("already process stop", msg); break; case HIC_POWER_DOWN: if (itf->pdown_ready == IS_IF_POWER_DOWN_READY) mwarn("already powerdown ready", msg); break; default: if (itf->pdown_ready == IS_IF_POWER_DOWN_READY) { exit_request_barrier(itf); mwarn("already powerdown ready, %d cmd is cancel", msg, msg->command); goto exit; } break; } enter_process_barrier(itf); itf->process_msg = *msg; ret = waiting_is_ready(itf); if (ret) { exit_request_barrier(itf); exit_process_barrier(itf); err("waiting for ready is fail"); ret = -EBUSY; goto exit; } set_busystate(itf, msg->command); com_regs = itf->com_regs; id = (msg->group << GROUP_ID_SHIFT) | msg->instance; writel(msg->command, &com_regs->hicmd); writel(id, &com_regs->hic_stream); writel(msg->param1, &com_regs->hic_param1); writel(msg->param2, &com_regs->hic_param2); writel(msg->param3, &com_regs->hic_param3); writel(msg->param4, &com_regs->hic_param4); send_interrupt(itf); itf->process_msg.command = 0; exit_process_barrier(itf); ret = wait_idlestate(itf); if (ret) { exit_request_barrier(itf); err("%d command is timeout", msg->command); fimc_is_hw_regdump(itf); print_req_work_list(&itf->work_list[INTR_GENERAL]); print_work_data_state(itf); clr_busystate(itf, msg->command); ret = -ETIME; goto exit; } reply->command = itf->reply.command; reply->group = itf->reply.group; reply->instance = itf->reply.instance; reply->param1 = itf->reply.param1; reply->param2 = itf->reply.param2; reply->param3 = itf->reply.param3; reply->param4 = itf->reply.param4; if (reply->command == ISR_DONE) { if (msg->command != reply->param1) { exit_request_barrier(itf); err("invalid command reply(%d != %d)", msg->command, reply->param1); ret = -EINVAL; goto exit; } switch (msg->command) { case HIC_STREAM_ON: itf->streaming[msg->instance] = IS_IF_STREAMING_ON; break; case HIC_STREAM_OFF: itf->streaming[msg->instance] = IS_IF_STREAMING_OFF; break; case HIC_PROCESS_START: itf->processing[msg->instance] = IS_IF_PROCESSING_ON; break; case HIC_PROCESS_STOP: itf->processing[msg->instance] = IS_IF_PROCESSING_OFF; break; case HIC_POWER_DOWN: itf->pdown_ready = IS_IF_POWER_DOWN_READY; break; case HIC_OPEN_SENSOR: if (reply->param1 == HIC_POWER_DOWN) { err("firmware power down"); itf->pdown_ready = IS_IF_POWER_DOWN_READY; ret = -ECANCELED; } else { itf->pdown_ready = IS_IF_POWER_DOWN_NREADY; } break; default: break; } } else { fimc_is_itf_fwboot_init(itf); err("ISR_NDONE is occured"); ret = -EINVAL; } #ifdef MEASURE_TIME #ifdef INTERFACE_TIME do_gettimeofday(&measure_end); measure_time(&itf->time[msg->command], msg->instance, msg->group, &measure_str, &measure_end); #endif #endif itf->request_msg.command = 0; exit_request_barrier(itf); exit: if (ret) fimc_is_hw_logdump(itf); return ret; } static int fimc_is_set_cmd_shot(struct fimc_is_interface *itf, struct fimc_is_msg *msg) { int ret = 0; u32 id; volatile struct is_common_reg __iomem *com_regs; ulong flags; FIMC_BUG(!itf); FIMC_BUG(!msg); FIMC_BUG(msg->instance >= FIMC_IS_STREAM_COUNT); if (!test_bit(IS_IF_STATE_OPEN, &itf->state)) { warn("interface close, %d cmd is cancel", msg->command); goto exit; } enter_process_barrier(itf); itf->process_msg = *msg; ret = waiting_is_ready(itf); if (ret) { exit_process_barrier(itf); err("waiting for ready is fail"); ret = -EBUSY; goto exit; } spin_lock_irqsave(&itf->shot_check_lock, flags); atomic_set(&itf->shot_check[msg->instance], 1); spin_unlock_irqrestore(&itf->shot_check_lock, flags); com_regs = itf->com_regs; id = (msg->group << GROUP_ID_SHIFT) | msg->instance; writel(msg->command, &com_regs->hicmd); writel(id, &com_regs->hic_stream); writel(msg->param1, &com_regs->hic_param1); writel(msg->param2, &com_regs->hic_param2); writel(msg->param3, &com_regs->hic_param3); writel(msg->param4, &com_regs->hic_param4); send_interrupt(itf); itf->process_msg.command = 0; exit_process_barrier(itf); exit: return ret; } static int fimc_is_set_cmd_nblk(struct fimc_is_interface *this, struct fimc_is_work *work) { int ret = 0; u32 id; struct fimc_is_msg *msg; volatile struct is_common_reg __iomem *com_regs; msg = &work->msg; switch (msg->command) { case HIC_SET_CAM_CONTROL: set_req_work(&this->nblk_cam_ctrl, work); break; case HIC_MSG_TEST: break; default: err("unresolved command\n"); break; } enter_process_barrier(this); ret = waiting_is_ready(this); if (ret) { err("waiting for ready is fail"); ret = -EBUSY; goto exit; } com_regs = this->com_regs; id = (msg->group << GROUP_ID_SHIFT) | msg->instance; writel(msg->command, &com_regs->hicmd); writel(id, &com_regs->hic_stream); writel(msg->param1, &com_regs->hic_param1); writel(msg->param2, &com_regs->hic_param2); writel(msg->param3, &com_regs->hic_param3); writel(msg->param4, &com_regs->hic_param4); send_interrupt(this); exit: exit_process_barrier(this); return ret; } static inline void fimc_is_get_cmd(struct fimc_is_interface *itf, struct fimc_is_msg *msg, u32 index) { volatile struct is_common_reg __iomem *com_regs = itf->com_regs; switch (index) { case INTR_GENERAL: msg->id = 0; msg->command = readl(&com_regs->ihcmd); msg->instance = readl(&com_regs->ihc_stream); msg->param1 = readl(&com_regs->ihc_param1); msg->param2 = readl(&com_regs->ihc_param2); msg->param3 = readl(&com_regs->ihc_param3); msg->param4 = readl(&com_regs->ihc_param4); break; case INTR_SHOT_DONE: msg->id = 0; msg->command = IHC_FRAME_DONE; msg->instance = readl(&com_regs->shot_stream); msg->param1 = readl(&com_regs->shot_param1); msg->param2 = readl(&com_regs->shot_param2); msg->param3 = 0; msg->param4 = 0; break; case INTR_30C_FDONE: msg->id = 0; msg->command = IHC_FRAME_DONE; msg->instance = readl(&com_regs->t0c_stream); msg->param1 = readl(&com_regs->t0c_param1); msg->param2 = readl(&com_regs->t0c_param2); msg->param3 = (msg->param2 == 0xFFFFFFFF) ? 1 : 0; msg->param4 = 0; break; case INTR_30P_FDONE: msg->id = 0; msg->command = IHC_FRAME_DONE; msg->instance = readl(&com_regs->t0p_stream); msg->param1 = readl(&com_regs->t0p_param1); msg->param2 = readl(&com_regs->t0p_param2); msg->param3 = (msg->param2 == 0xFFFFFFFF) ? 1 : 0; msg->param4 = 0; break; case INTR_31C_FDONE: msg->id = 0; msg->command = IHC_FRAME_DONE; msg->instance = readl(&com_regs->t1c_stream); msg->param1 = readl(&com_regs->t1c_param1); msg->param2 = readl(&com_regs->t1c_param2); msg->param3 = (msg->param2 == 0xFFFFFFFF) ? 1 : 0; msg->param4 = 0; break; case INTR_31P_FDONE: msg->id = 0; msg->command = IHC_FRAME_DONE; msg->instance = readl(&com_regs->t1p_stream); msg->param1 = readl(&com_regs->t1p_param1); msg->param2 = readl(&com_regs->t1p_param2); msg->param3 = (msg->param2 == 0xFFFFFFFF) ? 1 : 0; msg->param4 = 0; break; case INTR_I0X_FDONE: msg->id = 0; msg->command = IHC_FRAME_DONE; msg->instance = readl(&com_regs->i0x_stream); msg->param1 = readl(&com_regs->i0x_param1); msg->param2 = readl(&com_regs->i0x_param2); msg->param3 = (msg->param2 == 0xFFFFFFFF) ? 1 : 0; msg->param4 = 0; break; case INTR_I1X_FDONE: msg->id = 0; msg->command = IHC_FRAME_DONE; msg->instance = readl(&com_regs->i1x_stream); msg->param1 = readl(&com_regs->i1x_param1); msg->param2 = readl(&com_regs->i1x_param2); msg->param3 = (msg->param2 == 0xFFFFFFFF) ? 1 : 0; msg->param4 = 0; break; case INTR_SCX_FDONE: msg->id = 0; msg->command = IHC_FRAME_DONE; msg->instance = readl(&com_regs->scx_stream); msg->param1 = readl(&com_regs->scx_param1); msg->param2 = readl(&com_regs->scx_param2); msg->param3 = (msg->param2 == 0xFFFFFFFF) ? 1 : 0; msg->param4 = 0; break; case INTR_M0X_FDONE: msg->id = 0; msg->command = IHC_FRAME_DONE; msg->instance = readl(&com_regs->m0x_stream); msg->param1 = readl(&com_regs->m0x_param1); msg->param2 = readl(&com_regs->m0x_param2); msg->param3 = (msg->param2 == 0xFFFFFFFF) ? 1 : 0; msg->param4 = 0; break; case INTR_M1X_FDONE: msg->id = 0; msg->command = IHC_FRAME_DONE; msg->instance = readl(&com_regs->m1x_stream); msg->param1 = readl(&com_regs->m1x_param1); msg->param2 = readl(&com_regs->m1x_param2); msg->param3 = (msg->param2 == 0xFFFFFFFF) ? 1 : 0; msg->param4 = 0; break; default: msg->id = 0; msg->command = 0; msg->instance = 0; msg->param1 = 0; msg->param2 = 0; msg->param3 = 0; msg->param4 = 0; err("unknown command getting\n"); break; } msg->group = msg->instance >> GROUP_ID_SHIFT; msg->instance = msg->instance & GROUP_ID_MASK; } static inline u32 fimc_is_get_intr(struct fimc_is_interface *itf) { return readl(itf->regs + INTMSR1); } static inline void fimc_is_clr_intr(struct fimc_is_interface *itf, u32 index) { writel((1 << index), itf->regs + INTCR1); } static inline void wq_func_schedule(struct fimc_is_interface *itf, struct work_struct *work_wq) { if (itf->workqueue) queue_work(itf->workqueue, work_wq); else schedule_work(work_wq); } static void wq_func_general(struct work_struct *data) { struct fimc_is_interface *itf; struct fimc_is_msg *msg; struct fimc_is_work *work; struct fimc_is_work *nblk_work; itf = container_of(data, struct fimc_is_interface, work_wq[INTR_GENERAL]); get_req_work(&itf->work_list[INTR_GENERAL], &work); while (work) { msg = &work->msg; switch (msg->command) { case IHC_GET_SENSOR_NUMBER: info("IS Version: %d.%d [0x%02x]\n", ISDRV_VERSION, msg->param1, get_drv_clock_gate() | get_drv_dvfs()); itf->pdown_ready = IS_IF_POWER_DOWN_NREADY; set_bit(IS_IF_STATE_START, &itf->state); wake_up(&itf->init_wait_queue); break; case ISR_DONE: switch (msg->param1) { case HIC_OPEN_SENSOR: dbg_interface(1, "open done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_CLOSE_SENSOR: dbg_interface(1, "close done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_GET_SET_FILE_ADDR: dbg_interface(1, "saddr(%x) done\n", msg->param2); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_LOAD_SET_FILE: dbg_interface(1, "setfile done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_SET_A5_MAP: dbg_interface(1, "mapping done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_SET_A5_UNMAP: dbg_interface(1, "unmapping done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_PROCESS_START: dbg_interface(1, "process_on done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_PROCESS_STOP: dbg_interface(1, "process_off done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_STREAM_ON: dbg_interface(1, "stream_on done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_STREAM_OFF: dbg_interface(1, "stream_off done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_SET_PARAMETER: dbg_interface(1, "s_param done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_GET_STATIC_METADATA: dbg_interface(1, "g_capability done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_PREVIEW_STILL: dbg_interface(1, "a_param(%dx%d) done\n", msg->param2, msg->param3); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_POWER_DOWN: dbg_interface(1, "powerdown done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_I2C_CONTROL_LOCK: dbg_interface(1, "i2c lock done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_SYSTEM_CONTROL: dbg_interface(1, "system control done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; case HIC_SENSOR_MODE_CHANGE: dbg_interface(1, "sensor mode change done\n"); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; /*non-blocking command*/ case HIC_SHOT: err("shot done is not acceptable\n"); break; case HIC_SET_CAM_CONTROL: /* this code will be used latter */ #if 0 dbg_interface(1, "camctrl done\n"); get_req_work(&itf->nblk_cam_ctrl , &nblk_work); if (nblk_work) { nblk_work->msg.command = ISR_DONE; set_free_work(&itf->nblk_cam_ctrl, nblk_work); } else { err("nblk camctrl request is empty"); print_fre_work_list( &itf->nblk_cam_ctrl); print_req_work_list( &itf->nblk_cam_ctrl); } #else err("camctrl is not acceptable\n"); #endif break; default: err("unknown done is invokded\n"); break; } break; case ISR_NDONE: fimc_is_hw_logdump(itf); switch (msg->param1) { case HIC_SHOT: err("[ITF:%d] shot NDONE is not acceptable", msg->instance); break; case HIC_SET_CAM_CONTROL: dbg_interface(1, "camctrl NOT done\n"); get_req_work(&itf->nblk_cam_ctrl , &nblk_work); nblk_work->msg.command = ISR_NDONE; set_free_work(&itf->nblk_cam_ctrl, nblk_work); break; case HIC_SET_PARAMETER: err("s_param NOT done"); err("param2 : 0x%08X", msg->param2); err("param3 : 0x%08X", msg->param3); err("param4 : 0x%08X", msg->param4); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; default: err("a command(%d) not done", msg->param1); memcpy(&itf->reply, msg, sizeof(struct fimc_is_msg)); testnclr_wakeup(itf, msg->param1); break; } break; case IHC_SET_FACE_MARK: err("FACE_MARK(%d,%d,%d) is not acceptable\n", msg->param1, msg->param2, msg->param3); break; case IHC_AA_DONE: err("AA_DONE(%d,%d,%d) is not acceptable\n", msg->param1, msg->param2, msg->param3); break; case IHC_FLASH_READY: err("IHC_FLASH_READY is not acceptable"); break; case IHC_NOT_READY: err("IHC_NOT_READY is occured, need reset"); fimc_is_hw_logdump(itf); break; case IHC_REPORT_ERR: err("IHC_REPORT_ERR is occured"); fimc_is_err_report_handler(itf, msg); break; default: err("func_general unknown(0x%08X) end\n", msg->command); fimc_is_hw_logdump(itf); BUG(); break; } set_free_work(&itf->work_list[INTR_GENERAL], work); get_req_work(&itf->work_list[INTR_GENERAL], &work); } } static void wq_func_subdev(struct fimc_is_subdev *leader, struct fimc_is_subdev *subdev, struct fimc_is_frame *sub_frame, u32 fcount, u32 rcount, u32 status) { u32 done_state = VB2_BUF_STATE_DONE; u32 findex, mindex; struct fimc_is_video_ctx *ldr_vctx, *sub_vctx; struct fimc_is_framemgr *ldr_framemgr, *sub_framemgr; struct fimc_is_frame *ldr_frame; struct camera2_node *capture; FIMC_BUG(!sub_frame); ldr_vctx = leader->vctx; if (unlikely(!ldr_vctx)) { mserr("ldr_vctx is NULL", subdev, subdev); return; } sub_vctx = subdev->vctx; if (unlikely(!sub_vctx)) { mserr("ldr_vctx is NULL", subdev, subdev); return; } ldr_framemgr = GET_FRAMEMGR(ldr_vctx); sub_framemgr = GET_FRAMEMGR(sub_vctx); findex = sub_frame->stream->findex; mindex = ldr_vctx->queue.buf_maxcount; if (findex >= mindex) { mserr("findex(%d) is invalid(max %d)", subdev, subdev, findex, mindex); done_state = VB2_BUF_STATE_ERROR; sub_frame->stream->fvalid = 0; goto complete; } ldr_frame = &ldr_framemgr->frames[findex]; if (ldr_frame->fcount != fcount) { mserr("frame mismatched(ldr%d, sub%d)", subdev, subdev, ldr_frame->fcount, fcount); done_state = VB2_BUF_STATE_ERROR; sub_frame->stream->fvalid = 0; goto complete; } if (status) { msrinfo("[ERR] NDONE(%d, E%X)\n", subdev, subdev, ldr_frame, sub_frame->index, status); done_state = VB2_BUF_STATE_ERROR; sub_frame->stream->fvalid = 0; } else { mdbgs_sr(" DONE(%d)\n", subdev, subdev, ldr_frame, sub_frame->index); sub_frame->stream->fvalid = 1; } capture = &ldr_frame->shot_ext->node_group.capture[subdev->cid]; if (likely(capture->vid == subdev->vid)) { sub_frame->stream->input_crop_region[0] = capture->input.cropRegion[0]; sub_frame->stream->input_crop_region[1] = capture->input.cropRegion[1]; sub_frame->stream->input_crop_region[2] = capture->input.cropRegion[2]; sub_frame->stream->input_crop_region[3] = capture->input.cropRegion[3]; sub_frame->stream->output_crop_region[0] = capture->output.cropRegion[0]; sub_frame->stream->output_crop_region[1] = capture->output.cropRegion[1]; sub_frame->stream->output_crop_region[2] = capture->output.cropRegion[2]; sub_frame->stream->output_crop_region[3] = capture->output.cropRegion[3]; } else { mserr("capture vid is changed(%d != %d)", subdev, subdev, subdev->vid, capture->vid); sub_frame->stream->input_crop_region[0] = 0; sub_frame->stream->input_crop_region[1] = 0; sub_frame->stream->input_crop_region[2] = 0; sub_frame->stream->input_crop_region[3] = 0; sub_frame->stream->output_crop_region[0] = 0; sub_frame->stream->output_crop_region[1] = 0; sub_frame->stream->output_crop_region[2] = 0; sub_frame->stream->output_crop_region[3] = 0; } clear_bit(subdev->id, &ldr_frame->out_flag); /* for debug */ DBG_DIGIT_TAG((ldr_frame->group) ? ((struct fimc_is_group *)ldr_frame->group)->slot : 0, 0, GET_QUEUE(sub_vctx), sub_frame, fcount - sub_frame->num_buffers + 1, 1); complete: sub_frame->stream->fcount = fcount; sub_frame->stream->rcount = rcount; sub_frame->fcount = fcount; sub_frame->rcount = rcount; trans_frame(sub_framemgr, sub_frame, FS_COMPLETE); CALL_VOPS(sub_vctx, done, sub_frame->index, done_state); } static void wq_func_frame(struct fimc_is_subdev *leader, struct fimc_is_subdev *subdev, u32 fcount, u32 rcount, u32 status) { unsigned long flags; struct fimc_is_framemgr *framemgr; struct fimc_is_frame *frame; FIMC_BUG(!leader); FIMC_BUG(!subdev); FIMC_BUG(!leader->vctx); FIMC_BUG(!subdev->vctx); FIMC_BUG(!leader->vctx->video); FIMC_BUG(!subdev->vctx->video); framemgr = GET_FRAMEMGR(subdev->vctx); framemgr_e_barrier_irqs(framemgr, FMGR_IDX_4, flags); frame = peek_frame(framemgr, FS_PROCESS); if (frame) { if (!frame->stream) { mserr("stream is NULL", subdev, subdev); BUG(); } if (unlikely(frame->stream->fcount != fcount)) { while (frame) { if (fcount == frame->stream->fcount) { wq_func_subdev(leader, subdev, frame, fcount, rcount, status); break; } else if (fcount > frame->stream->fcount) { wq_func_subdev(leader, subdev, frame, frame->stream->fcount, rcount, 0xF); /* get next subdev frame */ frame = peek_frame(framemgr, FS_PROCESS); } else { warn("%d frame done is ignored", frame->stream->fcount); break; } } } else { wq_func_subdev(leader, subdev, frame, fcount, rcount, status); } } else { mserr("frame done(%p) is occured without request", subdev, subdev, frame); } framemgr_x_barrier_irqr(framemgr, FMGR_IDX_4, flags); } static void wq_func_30c(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_30C_FDONE]); get_req_work(&itf->work_list[WORK_30C_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->txc; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_30C_FDONE], work); get_req_work(&itf->work_list[WORK_30C_FDONE], &work); } } static void wq_func_30p(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_30P_FDONE]); get_req_work(&itf->work_list[WORK_30P_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->txp; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_30P_FDONE], work); get_req_work(&itf->work_list[WORK_30P_FDONE], &work); } } static void wq_func_31c(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_31C_FDONE]); get_req_work(&itf->work_list[WORK_31C_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->txc; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_31C_FDONE], work); get_req_work(&itf->work_list[WORK_31C_FDONE], &work); } } static void wq_func_31p(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_31P_FDONE]); get_req_work(&itf->work_list[WORK_31P_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->txp; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_31P_FDONE], work); get_req_work(&itf->work_list[WORK_31P_FDONE], &work); } } static void wq_func_i0c(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_I0C_FDONE]); get_req_work(&itf->work_list[WORK_I0C_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->ixc; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_I0C_FDONE], work); get_req_work(&itf->work_list[WORK_I0C_FDONE], &work); } } static void wq_func_i0p(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_I0P_FDONE]); get_req_work(&itf->work_list[WORK_I0P_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->ixp; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_I0P_FDONE], work); get_req_work(&itf->work_list[WORK_I0P_FDONE], &work); } } static void wq_func_i1c(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_I1C_FDONE]); get_req_work(&itf->work_list[WORK_I1C_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->ixc; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_I1C_FDONE], work); get_req_work(&itf->work_list[WORK_I1C_FDONE], &work); } } static void wq_func_i1p(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_I1P_FDONE]); get_req_work(&itf->work_list[WORK_I1P_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->ixp; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_I1P_FDONE], work); get_req_work(&itf->work_list[WORK_I1P_FDONE], &work); } } static void wq_func_scc(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_SCC_FDONE]); get_req_work(&itf->work_list[WORK_SCC_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->scc; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_SCC_FDONE], work); get_req_work(&itf->work_list[WORK_SCC_FDONE], &work); } } static void wq_func_scp(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_SCP_FDONE]); get_req_work(&itf->work_list[WORK_SCP_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->scp; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_SCP_FDONE], work); get_req_work(&itf->work_list[WORK_SCP_FDONE], &work); } } static void wq_func_m0p(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_M0P_FDONE]); get_req_work(&itf->work_list[WORK_M0P_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->m0p; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } /* For supporting multi input to single output */ if (subdev->vctx->video->try_smp) { subdev->vctx->video->try_smp = false; up(&subdev->vctx->video->smp_multi_input); } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_M0P_FDONE], work); get_req_work(&itf->work_list[WORK_M0P_FDONE], &work); } } static void wq_func_m1p(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_M1P_FDONE]); get_req_work(&itf->work_list[WORK_M1P_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->m1p; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } /* For supporting multi input to single output */ if (subdev->vctx->video->try_smp) { subdev->vctx->video->try_smp = false; up(&subdev->vctx->video->smp_multi_input); } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_M1P_FDONE], work); get_req_work(&itf->work_list[WORK_M1P_FDONE], &work); } } static void wq_func_m2p(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_M2P_FDONE]); get_req_work(&itf->work_list[WORK_M2P_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->m2p; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } /* For supporting multi input to single output */ if (subdev->vctx->video->try_smp) { subdev->vctx->video->try_smp = false; up(&subdev->vctx->video->smp_multi_input); } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_M2P_FDONE], work); get_req_work(&itf->work_list[WORK_M2P_FDONE], &work); } } static void wq_func_m3p(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_M3P_FDONE]); get_req_work(&itf->work_list[WORK_M3P_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->m3p; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } /* For supporting multi input to single output */ if (subdev->vctx->video->try_smp) { subdev->vctx->video->try_smp = false; up(&subdev->vctx->video->smp_multi_input); } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_M3P_FDONE], work); get_req_work(&itf->work_list[WORK_M3P_FDONE], &work); } } static void wq_func_m4p(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_M4P_FDONE]); get_req_work(&itf->work_list[WORK_M4P_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->m4p; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } /* For supporting multi input to single output */ if (subdev->vctx->video->try_smp) { subdev->vctx->video->try_smp = false; up(&subdev->vctx->video->smp_multi_input); } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_M4P_FDONE], work); get_req_work(&itf->work_list[WORK_M4P_FDONE], &work); } } static void wq_func_m5p(struct work_struct *data) { u32 instance, fcount, rcount, status; struct fimc_is_interface *itf; struct fimc_is_device_ischain *device; struct fimc_is_subdev *leader, *subdev; struct fimc_is_work *work; struct fimc_is_msg *msg; itf = container_of(data, struct fimc_is_interface, work_wq[WORK_M5P_FDONE]); get_req_work(&itf->work_list[WORK_M5P_FDONE], &work); while (work) { msg = &work->msg; instance = msg->instance; fcount = msg->param1; rcount = msg->param2; status = msg->param3; if (instance >= FIMC_IS_STREAM_COUNT) { err("instance is invalid(%d)", instance); goto p_err; } device = &((struct fimc_is_core *)itf->core)->ischain[instance]; if (!device) { err("device is NULL"); goto p_err; } if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &device->state)) { merr("device is not open", device); goto p_err; } subdev = &device->m5p; if (!test_bit(FIMC_IS_SUBDEV_START, &subdev->state)) { merr("subdev is not start", device); goto p_err; } leader = subdev->leader; if (!leader) { merr("leader is NULL", device); goto p_err; } /* For supporting multi input to single output */ if (subdev->vctx->video->try_smp) { subdev->vctx->video->try_smp = false; up(&subdev->vctx->video->smp_multi_input); } wq_func_frame(leader, subdev, fcount, rcount, status); p_err: set_free_work(&itf->work_list[WORK_M5P_FDONE], work); get_req_work(&itf->work_list[WORK_M5P_FDONE], &work); } } static void wq_func_group_xxx(struct fimc_is_groupmgr *groupmgr, struct fimc_is_group *group, struct fimc_is_framemgr *framemgr, struct fimc_is_frame *frame, struct fimc_is_video_ctx *vctx, u32 status, u32 lindex, u32 hindex) { u32 done_state = VB2_BUF_STATE_DONE; FIMC_BUG(!vctx); FIMC_BUG(!framemgr); FIMC_BUG(!frame); /* perframe error control */ if (test_bit(FIMC_IS_SUBDEV_PARAM_ERR, &group->leader.state)) { if (!status) { if (frame->lindex || frame->hindex) clear_bit(FIMC_IS_SUBDEV_PARAM_ERR, &group->leader.state); else status = SHOT_ERR_PERFRAME; } } else { if (status && (frame->lindex || frame->hindex)) set_bit(FIMC_IS_SUBDEV_PARAM_ERR, &group->leader.state); } if (status) { mgrinfo("[ERR] NDONE(%d, E%X(L%X H%X))\n", group, group, frame, frame->index, status, lindex, hindex); done_state = VB2_BUF_STATE_ERROR; if (status == IS_SHOT_OVERFLOW) { #ifdef OVERFLOW_PANIC_ENABLE_ISCHAIN panic("G%d overflow", group->id); #else err("G%d overflow", group->id); fimc_is_resource_dump(); #endif } } else { mgrdbgs(1, " DONE(%d)\n", group, group, frame, frame->index); } #ifdef ENABLE_SYNC_REPROCESSING /* Sync Reprocessing */ if ((atomic_read(&groupmgr->gtask[group->id].refcount) > 1) && (group->device->resourcemgr->hal_version == IS_HAL_VER_1_0)) fimc_is_sync_reprocessing_queue(groupmgr, group); #endif /* Cache Invalidation */ fimc_is_ischain_meta_invalid(frame); frame->result = status; clear_bit(group->leader.id, &frame->out_flag); fimc_is_group_done(groupmgr, group, frame, done_state); trans_frame(framemgr, frame, FS_COMPLETE); CALL_VOPS(vctx, done, frame->index, done_state); } void wq_func_group(struct fimc_is_device_ischain *device, struct fimc_is_groupmgr *groupmgr, struct fimc_is_group *group, struct fimc_is_framemgr *framemgr, struct fimc_is_frame *frame, struct fimc_is_video_ctx *vctx, u32 status, u32 fcount) { u32 lindex = 0; u32 hindex = 0; FIMC_BUG(!groupmgr); FIMC_BUG(!group); FIMC_BUG(!framemgr); FIMC_BUG(!frame); FIMC_BUG(!vctx); TIME_SHOT(TMS_SDONE); /* * complete count should be lower than 3 when * buffer is queued or overflow can be occured */ if (framemgr->queued_count[FS_COMPLETE] >= DIV_ROUND_UP(framemgr->num_frames, 2) && (!test_bit(FIMC_IS_GROUP_PIPE_INPUT, &group->state)) && (!test_bit(FIMC_IS_GROUP_SEMI_PIPE_INPUT, &group->state))) mgwarn(" complete bufs : %d", device, group, (framemgr->queued_count[FS_COMPLETE] + 1)); if (status) { lindex = frame->shot->ctl.vendor_entry.lowIndexParam; lindex &= ~frame->shot->dm.vendor_entry.lowIndexParam; hindex = frame->shot->ctl.vendor_entry.highIndexParam; hindex &= ~frame->shot->dm.vendor_entry.highIndexParam; } if (unlikely(fcount != frame->fcount)) { if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) { while (frame) { if (fcount == frame->fcount) { wq_func_group_xxx(groupmgr, group, framemgr, frame, vctx, status, lindex, hindex); break; } else if (fcount > frame->fcount) { wq_func_group_xxx(groupmgr, group, framemgr, frame, vctx, SHOT_ERR_MISMATCH, lindex, hindex); /* get next leader frame */ frame = peek_frame(framemgr, FS_PROCESS); } else { warn("%d shot done is ignored", fcount); break; } } } else { wq_func_group_xxx(groupmgr, group, framemgr, frame, vctx, SHOT_ERR_MISMATCH, lindex, hindex); } } else { wq_func_group_xxx(groupmgr, group, framemgr, frame, vctx, status, lindex, hindex); } } static void wq_func_shot(struct work_struct *data) { struct fimc_is_device_ischain *device; struct fimc_is_interface *itf; struct fimc_is_msg *msg; struct fimc_is_framemgr *framemgr; struct fimc_is_frame *frame; struct fimc_is_groupmgr *groupmgr; struct fimc_is_group *group; struct fimc_is_work_list *work_list; struct fimc_is_work *work; struct fimc_is_video_ctx *vctx; unsigned long flags; u32 fcount, status; int instance; int group_id; struct fimc_is_core *core; FIMC_BUG(!data); itf = container_of(data, struct fimc_is_interface, work_wq[INTR_SHOT_DONE]); work_list = &itf->work_list[INTR_SHOT_DONE]; group = NULL; vctx = NULL; framemgr = NULL; get_req_work(work_list, &work); while (work) { core = (struct fimc_is_core *)itf->core; instance = work->msg.instance; group_id = work->msg.group; device = &((struct fimc_is_core *)itf->core)->ischain[instance]; groupmgr = device->groupmgr; msg = &work->msg; fcount = msg->param1; status = msg->param2; switch (group_id) { case GROUP_ID(GROUP_ID_3AA0): case GROUP_ID(GROUP_ID_3AA1): group = &device->group_3aa; break; case GROUP_ID(GROUP_ID_ISP0): case GROUP_ID(GROUP_ID_ISP1): group = &device->group_isp; break; case GROUP_ID(GROUP_ID_DIS0): case GROUP_ID(GROUP_ID_DIS1): group = &device->group_dis; break; case GROUP_ID(GROUP_ID_MCS0): case GROUP_ID(GROUP_ID_MCS1): group = &device->group_mcs; break; case GROUP_ID(GROUP_ID_VRA0): group = &device->group_vra; break; default: merr("unresolved group id %d", device, group_id); group = NULL; vctx = NULL; framemgr = NULL; goto remain; } if (!group) { merr("group is NULL", device); goto remain; } vctx = group->leader.vctx; if (!vctx) { merr("vctx is NULL", device); goto remain; } framemgr = GET_FRAMEMGR(vctx); if (!framemgr) { merr("framemgr is NULL", device); goto remain; } framemgr_e_barrier_irqs(framemgr, FMGR_IDX_5, flags); frame = peek_frame(framemgr, FS_PROCESS); if (frame) { #ifdef MEASURE_TIME #ifdef EXTERNAL_TIME do_gettimeofday(&frame->tzone[TM_SHOT_D]); #endif #endif #ifdef ENABLE_CLOCK_GATE /* dynamic clock off */ if (sysfs_debug.en_clk_gate && sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) fimc_is_clk_gate_set(core, group->id, false, false, true); #endif wq_func_group(device, groupmgr, group, framemgr, frame, vctx, status, fcount); } else { mgerr("invalid shot done(%d)", device, group, fcount); frame_manager_print_queues(framemgr); } framemgr_x_barrier_irqr(framemgr, FMGR_IDX_5, flags); #ifdef ENABLE_CLOCK_GATE if (fcount == 1 && sysfs_debug.en_clk_gate && sysfs_debug.clk_gate_mode == CLOCK_GATE_MODE_HOST) fimc_is_clk_gate_lock_set(core, instance, false); #endif remain: set_free_work(work_list, work); get_req_work(work_list, &work); } } static inline void print_framemgr_spinlock_usage(struct fimc_is_core *core) { u32 i; struct fimc_is_device_ischain *ischain; struct fimc_is_device_sensor *sensor; struct fimc_is_framemgr *framemgr; struct fimc_is_subdev *subdev; for (i = 0; i < FIMC_IS_SENSOR_COUNT; ++i) { sensor = &core->sensor[i]; if (test_bit(FIMC_IS_SENSOR_OPEN, &sensor->state) && (framemgr = &sensor->vctx->queue.framemgr)) info("[@] framemgr(%s) sindex : 0x%08lX\n", framemgr->name, framemgr->sindex); } for (i = 0; i < FIMC_IS_STREAM_COUNT; ++i) { ischain = &core->ischain[i]; if (test_bit(FIMC_IS_ISCHAIN_OPEN, &ischain->state)) { /* 3AA GROUP */ subdev = &ischain->group_3aa.leader; if (test_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state) && (framemgr = GET_SUBDEV_FRAMEMGR(subdev))) info("[@] framemgr(%s) sindex : 0x%08lX\n", framemgr->name, framemgr->sindex); subdev = &ischain->txc; if (test_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state) && (framemgr = GET_SUBDEV_FRAMEMGR(subdev))) info("[@] framemgr(%s) sindex : 0x%08lX\n", framemgr->name, framemgr->sindex); subdev = &ischain->txp; if (test_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state) && (framemgr = GET_SUBDEV_FRAMEMGR(subdev))) info("[@] framemgr(%s) sindex : 0x%08lX\n", framemgr->name, framemgr->sindex); /* ISP GROUP */ subdev = &ischain->group_isp.leader; if (test_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state) && (framemgr = GET_SUBDEV_FRAMEMGR(subdev))) info("[@] framemgr(%s) sindex : 0x%08lX\n", framemgr->name, framemgr->sindex); subdev = &ischain->ixc; if (test_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state) && (framemgr = GET_SUBDEV_FRAMEMGR(subdev))) info("[@] framemgr(%s) sindex : 0x%08lX\n", framemgr->name, framemgr->sindex); subdev = &ischain->ixp; if (test_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state) && (framemgr = GET_SUBDEV_FRAMEMGR(subdev))) info("[@] framemgr(%s) sindex : 0x%08lX\n", framemgr->name, framemgr->sindex); /* DIS GROUP */ subdev = &ischain->group_dis.leader; if (test_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state) && (framemgr = GET_SUBDEV_FRAMEMGR(subdev))) info("[@] framemgr(%s) sindex : 0x%08lX\n", framemgr->name, framemgr->sindex); subdev = &ischain->scc; if (test_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state) && (framemgr = GET_SUBDEV_FRAMEMGR(subdev))) info("[@] framemgr(%s) sindex : 0x%08lX\n", framemgr->name, framemgr->sindex); subdev = &ischain->scp; if (test_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state) && (framemgr = GET_SUBDEV_FRAMEMGR(subdev))) info("[@] framemgr(%s) sindex : 0x%08lX\n", framemgr->name, framemgr->sindex); /* VRA GROUP */ subdev = &ischain->group_vra.leader; if (test_bit(FIMC_IS_SUBDEV_OPEN, &subdev->state) && (framemgr = GET_SUBDEV_FRAMEMGR(subdev))) info("[@] framemgr(%s) sindex : 0x%08lX\n", framemgr->name, framemgr->sindex); } } } static void interface_timer(unsigned long data) { u32 shot_count, scount_3ax, scount_isp; u32 fcount, i; unsigned long flags; struct fimc_is_interface *itf = (struct fimc_is_interface *)data; struct fimc_is_core *core; struct fimc_is_device_ischain *device; struct fimc_is_device_sensor *sensor; struct fimc_is_framemgr *framemgr; struct fimc_is_work_list *work_list; struct work_struct *work_wq; FIMC_BUG(!itf); FIMC_BUG(!itf->core); if (!test_bit(IS_IF_STATE_OPEN, &itf->state)) { pr_info("shot timer is terminated\n"); return; } core = itf->core; for (i = 0; i < FIMC_IS_STREAM_COUNT; ++i) { device = &core->ischain[i]; shot_count = 0; scount_3ax = 0; scount_isp = 0; sensor = device->sensor; if (!sensor) continue; if (!test_bit(FIMC_IS_SENSOR_FRONT_START, &sensor->state)) continue; if (test_bit(FIMC_IS_ISCHAIN_OPEN_STREAM, &device->state)) { spin_lock_irqsave(&itf->shot_check_lock, flags); if (atomic_read(&itf->shot_check[i])) { atomic_set(&itf->shot_check[i], 0); atomic_set(&itf->shot_timeout[i], 0); spin_unlock_irqrestore(&itf->shot_check_lock, flags); continue; } spin_unlock_irqrestore(&itf->shot_check_lock, flags); if (test_bit(FIMC_IS_GROUP_START, &device->group_3aa.state)) { framemgr = GET_HEAD_GROUP_FRAMEMGR(&device->group_3aa); if (framemgr) { framemgr_e_barrier_irqs(framemgr, FMGR_IDX_6, flags); scount_3ax = framemgr->queued_count[FS_PROCESS]; shot_count += scount_3ax; framemgr_x_barrier_irqr(framemgr, FMGR_IDX_6, flags); } else { minfo("\n### 3aa framemgr is null ###\n", device); } } if (test_bit(FIMC_IS_GROUP_START, &device->group_isp.state)) { framemgr = GET_HEAD_GROUP_FRAMEMGR(&device->group_isp); if (framemgr) { framemgr_e_barrier_irqs(framemgr, FMGR_IDX_7, flags); scount_isp = framemgr->queued_count[FS_PROCESS]; shot_count += scount_isp; framemgr_x_barrier_irqr(framemgr, FMGR_IDX_7, flags); } else { minfo("\n### isp framemgr is null ###\n", device); } } if (test_bit(FIMC_IS_GROUP_START, &device->group_dis.state)) { framemgr = GET_HEAD_GROUP_FRAMEMGR(&device->group_dis); if (framemgr) { framemgr_e_barrier_irqs(framemgr, FMGR_IDX_8, flags); shot_count += framemgr->queued_count[FS_PROCESS]; framemgr_x_barrier_irqr(framemgr, FMGR_IDX_8, flags); } else { minfo("\n### dis framemgr is null ###\n", device); } } if (test_bit(FIMC_IS_GROUP_START, &device->group_vra.state)) { framemgr = GET_HEAD_GROUP_FRAMEMGR(&device->group_vra); if (framemgr) { framemgr_e_barrier_irqs(framemgr, FMGR_IDX_31, flags); shot_count += framemgr->queued_count[FS_PROCESS]; framemgr_x_barrier_irqr(framemgr, FMGR_IDX_31, flags); } else { minfo("\n### vra framemgr is null ###\n", device); } } } if (shot_count) { atomic_inc(&itf->shot_timeout[i]); minfo("shot timer[%d] is increased to %d\n", device, i, atomic_read(&itf->shot_timeout[i])); } if (atomic_read(&itf->shot_timeout[i]) > TRY_TIMEOUT_COUNT) { merr("shot command is timeout(%d, %d(%d+%d))", device, atomic_read(&itf->shot_timeout[i]), shot_count, scount_3ax, scount_isp); minfo("\n### 3ax framemgr info ###\n", device); if (scount_3ax) { framemgr = GET_HEAD_GROUP_FRAMEMGR(&device->group_3aa); if (framemgr) { framemgr_e_barrier_irqs(framemgr, 0, flags); frame_manager_print_queues(framemgr); framemgr_x_barrier_irqr(framemgr, 0, flags); } else { minfo("\n### 3ax framemgr is null ###\n", device); } } minfo("\n### isp framemgr info ###\n", device); if (scount_isp) { framemgr = GET_HEAD_GROUP_FRAMEMGR(&device->group_isp); if (framemgr) { framemgr_e_barrier_irqs(framemgr, 0, flags); frame_manager_print_queues(framemgr); framemgr_x_barrier_irqr(framemgr, 0, flags); } else { minfo("\n### isp framemgr is null ###\n", device); } } minfo("\n### work list info ###\n", device); work_list = &itf->work_list[INTR_SHOT_DONE]; print_fre_work_list(work_list); print_req_work_list(work_list); if (work_list->work_request_cnt > 0) { pr_err("\n### processing work lately ###\n"); work_wq = &itf->work_wq[INTR_SHOT_DONE]; wq_func_shot(work_wq); atomic_set(&itf->shot_check[i], 0); atomic_set(&itf->shot_timeout[i], 0); } else { /* framemgr spinlock check */ print_framemgr_spinlock_usage(core); #ifdef FW_PANIC_ENABLE /* if panic happened, fw log dump should be happened by panic handler */ mdelay(2000); panic("[@] camera firmware panic!!!"); #else fimc_is_resource_dump(); #endif return; } } } for (i = 0; i < FIMC_IS_SENSOR_COUNT; ++i) { sensor = &core->sensor[i]; if (!test_bit(FIMC_IS_SENSOR_BACK_START, &sensor->state)) continue; if (!test_bit(FIMC_IS_SENSOR_FRONT_START, &sensor->state)) continue; fcount = fimc_is_sensor_g_fcount(sensor); if (fcount == atomic_read(&itf->sensor_check[i])) { atomic_inc(&itf->sensor_timeout[i]); pr_err ("sensor timer[%d] is increased to %d(fcount : %d)\n", i, atomic_read(&itf->sensor_timeout[i]), fcount); } else { atomic_set(&itf->sensor_timeout[i], 0); atomic_set(&itf->sensor_check[i], fcount); } if (atomic_read(&itf->sensor_timeout[i]) > SENSOR_TIMEOUT_COUNT) { merr("sensor is timeout(%d, %d)", sensor, atomic_read(&itf->sensor_timeout[i]), atomic_read(&itf->sensor_check[i])); /* framemgr spinlock check */ print_framemgr_spinlock_usage(core); #ifdef SENSOR_PANIC_ENABLE /* if panic happened, fw log dump should be happened by panic handler */ mdelay(2000); panic("[@] camera sensor panic!!!"); #else fimc_is_resource_dump(); #endif return; } } mod_timer(&itf->timer, jiffies + (FIMC_IS_COMMAND_TIMEOUT/TRY_TIMEOUT_COUNT)); } static irqreturn_t interface_isr(int irq, void *data) { ulong i; struct fimc_is_interface *itf; struct work_struct *work_wq; struct fimc_is_work_list *work_list; struct fimc_is_work *work; u32 status; u32 param3; itf = (struct fimc_is_interface *)data; status = fimc_is_get_intr(itf); if (status & (1<work_wq[WORK_SHOT_DONE]; work_list = &itf->work_list[WORK_SHOT_DONE]; get_free_work_irq(work_list, &work); if (work) { fimc_is_get_cmd(itf, &work->msg, INTR_SHOT_DONE); work->fcount = work->msg.param1; set_req_work_irq(work_list, work); if (!work_pending(work_wq)) wq_func_schedule(itf, work_wq); } else { err("shot free work is empty"); } status &= ~(1<work_wq[WORK_GENERAL]; work_list = &itf->work_list[WORK_GENERAL]; get_free_work_irq(work_list, &work); if (work) { fimc_is_get_cmd(itf, &work->msg, INTR_GENERAL); set_req_work_irq(work_list, work); if (!work_pending(work_wq)) wq_func_schedule(itf, work_wq); } else { err("general free work is empty"); } status &= ~(1<work_wq[WORK_30C_FDONE]; work_list = &itf->work_list[WORK_30C_FDONE]; get_free_work_irq(work_list, &work); if (work) { fimc_is_get_cmd(itf, &work->msg, INTR_30C_FDONE); set_req_work_irq(work_list, work); if (!work_pending(work_wq)) wq_func_schedule(itf, work_wq); } else { err("30c free work is empty"); } status &= ~(1<work_wq[WORK_30P_FDONE]; work_list = &itf->work_list[WORK_30P_FDONE]; get_free_work_irq(work_list, &work); if (work) { fimc_is_get_cmd(itf, &work->msg, INTR_30P_FDONE); set_req_work_irq(work_list, work); if (!work_pending(work_wq)) wq_func_schedule(itf, work_wq); } else { err("30p free work is empty"); } status &= ~(1<work_wq[WORK_31C_FDONE]; work_list = &itf->work_list[WORK_31C_FDONE]; get_free_work_irq(work_list, &work); if (work) { fimc_is_get_cmd(itf, &work->msg, INTR_31C_FDONE); set_req_work_irq(work_list, work); if (!work_pending(work_wq)) wq_func_schedule(itf, work_wq); } else { err("31c free work is empty"); } status &= ~(1<work_wq[WORK_31P_FDONE]; work_list = &itf->work_list[WORK_31P_FDONE]; get_free_work_irq(work_list, &work); if (work) { fimc_is_get_cmd(itf, &work->msg, INTR_31P_FDONE); set_req_work_irq(work_list, work); if (!work_pending(work_wq)) wq_func_schedule(itf, work_wq); } else { err("31p free work is empty"); } status &= ~(1<com_regs->i0x_param3; do { if (param3 & 1) { work_wq = &itf->work_wq[WORK_I0C_FDONE + i]; work_list = &itf->work_list[WORK_I0C_FDONE + i]; get_free_work_irq(work_list, &work); if (work) { fimc_is_get_cmd(itf, &work->msg, INTR_I0X_FDONE); set_req_work_irq(work_list, work); if (!work_pending(work_wq)) wq_func_schedule(itf, work_wq); } else { err("i0x free work is empty"); } } i++; param3 >>= 1; } while (param3); status &= ~(1<com_regs->i1x_param3; do { if (param3 & 1) { work_wq = &itf->work_wq[WORK_I1C_FDONE + i]; work_list = &itf->work_list[WORK_I1C_FDONE + i]; get_free_work_irq(work_list, &work); if (work) { fimc_is_get_cmd(itf, &work->msg, INTR_I1X_FDONE); set_req_work_irq(work_list, work); if (!work_pending(work_wq)) wq_func_schedule(itf, work_wq); } else { err("i1x free work is empty"); } } i++; param3 >>= 1; } while (param3); status &= ~(1<com_regs->scx_param3; do { if (param3 & 1) { work_wq = &itf->work_wq[WORK_SCC_FDONE + i]; work_list = &itf->work_list[WORK_SCC_FDONE + i]; get_free_work_irq(work_list, &work); if (work) { fimc_is_get_cmd(itf, &work->msg, INTR_SCX_FDONE); set_req_work_irq(work_list, work); if (!work_pending(work_wq)) wq_func_schedule(itf, work_wq); } else { err("scx free work is empty"); } } i++; param3 >>= 1; } while (param3); status &= ~(1<com_regs->m0x_param3; do { if (param3 & 1) { work_wq = &itf->work_wq[WORK_M0P_FDONE + i]; work_list = &itf->work_list[WORK_M0P_FDONE + i]; get_free_work_irq(work_list, &work); if (work) { fimc_is_get_cmd(itf, &work->msg, INTR_M0X_FDONE); set_req_work_irq(work_list, work); if (!work_pending(work_wq)) wq_func_schedule(itf, work_wq); } else { err("m0x free work is empty"); } } i++; param3 >>= 1; } while (param3); status &= ~(1<com_regs->m1x_param3; do { if (param3 & 1) { work_wq = &itf->work_wq[WORK_M0P_FDONE + i]; work_list = &itf->work_list[WORK_M0P_FDONE + i]; get_free_work_irq(work_list, &work); if (work) { fimc_is_get_cmd(itf, &work->msg, INTR_M1X_FDONE); set_req_work_irq(work_list, work); if (!work_pending(work_wq)) wq_func_schedule(itf, work_wq); } else { err("m1x free work is empty"); } } i++; param3 >>= 1; } while (param3); status &= ~(1<launch_state); clear_bit(IS_IF_LAUNCH_SUCCESS, &this->launch_state); clear_bit(IS_IF_RESUME, &this->fw_boot); clear_bit(IS_IF_SUSPEND, &this->fw_boot); this->fw_boot_mode = COLD_BOOT; } #define VERSION_OF_NO_NEED_IFLAG 221 int fimc_is_interface_probe(struct fimc_is_interface *this, struct fimc_is_minfo *minfo, ulong regs, u32 irq, void *core_data) { int ret = 0; struct fimc_is_core *core = (struct fimc_is_core *)core_data; dbg_interface(1, "%s\n", __func__); init_request_barrier(this); init_process_barrier(this); init_waitqueue_head(&this->lock_wait_queue); init_waitqueue_head(&this->init_wait_queue); init_waitqueue_head(&this->idle_wait_queue); spin_lock_init(&this->shot_check_lock); this->workqueue = alloc_workqueue("fimc-is/[H/U]", WQ_HIGHPRI | WQ_UNBOUND, 0); if (!this->workqueue) probe_warn("failed to alloc own workqueue, will be use global one"); INIT_WORK(&this->work_wq[WORK_GENERAL], wq_func_general); INIT_WORK(&this->work_wq[WORK_SHOT_DONE], wq_func_shot); INIT_WORK(&this->work_wq[WORK_30C_FDONE], wq_func_30c); INIT_WORK(&this->work_wq[WORK_30P_FDONE], wq_func_30p); INIT_WORK(&this->work_wq[WORK_31C_FDONE], wq_func_31c); INIT_WORK(&this->work_wq[WORK_31P_FDONE], wq_func_31p); INIT_WORK(&this->work_wq[WORK_I0C_FDONE], wq_func_i0c); INIT_WORK(&this->work_wq[WORK_I0P_FDONE], wq_func_i0p); INIT_WORK(&this->work_wq[WORK_I1C_FDONE], wq_func_i1c); INIT_WORK(&this->work_wq[WORK_I1P_FDONE], wq_func_i1p); INIT_WORK(&this->work_wq[WORK_SCC_FDONE], wq_func_scc); INIT_WORK(&this->work_wq[WORK_SCP_FDONE], wq_func_scp); INIT_WORK(&this->work_wq[WORK_M0P_FDONE], wq_func_m0p); INIT_WORK(&this->work_wq[WORK_M1P_FDONE], wq_func_m1p); INIT_WORK(&this->work_wq[WORK_M2P_FDONE], wq_func_m2p); INIT_WORK(&this->work_wq[WORK_M3P_FDONE], wq_func_m3p); INIT_WORK(&this->work_wq[WORK_M4P_FDONE], wq_func_m4p); INIT_WORK(&this->work_wq[WORK_M5P_FDONE], wq_func_m5p); this->regs = (void *)regs; this->com_regs = (struct is_common_reg *)(regs + ISSR0); this->itf_kvaddr = minfo->kvaddr; ret = request_irq(irq, interface_isr, 0, "mcuctl", this); if (ret) probe_err("request_irq failed\n"); notify_fcount_sen0 = &this->com_regs->fcount_sen0; notify_fcount_sen1 = &this->com_regs->fcount_sen1; notify_fcount_sen2 = &this->com_regs->fcount_sen2; notify_fcount_sen3 = &this->com_regs->fcount_sen3; this->core = (void *)core; clear_bit(IS_IF_STATE_OPEN, &this->state); clear_bit(IS_IF_STATE_START, &this->state); clear_bit(IS_IF_STATE_BUSY, &this->state); clear_bit(IS_IF_STATE_READY, &this->state); clear_bit(IS_IF_STATE_LOGGING, &this->state); /* clear fw suspend state */ fimc_is_itf_fwboot_init(this); init_work_list(&this->nblk_cam_ctrl, TRACE_WORK_ID_CAMCTRL, MAX_NBLOCKING_COUNT); init_work_list(&this->work_list[WORK_GENERAL], TRACE_WORK_ID_GENERAL, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_SHOT_DONE], TRACE_WORK_ID_SHOT, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_30C_FDONE], TRACE_WORK_ID_30C, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_30P_FDONE], TRACE_WORK_ID_30P, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_31C_FDONE], TRACE_WORK_ID_31C, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_31P_FDONE], TRACE_WORK_ID_31P, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_I0C_FDONE], TRACE_WORK_ID_I0C, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_I0P_FDONE], TRACE_WORK_ID_I0P, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_I1C_FDONE], TRACE_WORK_ID_I1C, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_I1P_FDONE], TRACE_WORK_ID_I1P, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_SCC_FDONE], TRACE_WORK_ID_SCC, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_SCP_FDONE], TRACE_WORK_ID_SCP, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_M0P_FDONE], TRACE_WORK_ID_M0P, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_M1P_FDONE], TRACE_WORK_ID_M1P, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_M2P_FDONE], TRACE_WORK_ID_M2P, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_M3P_FDONE], TRACE_WORK_ID_M3P, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_M4P_FDONE], TRACE_WORK_ID_M4P, MAX_WORK_COUNT); init_work_list(&this->work_list[WORK_M5P_FDONE], TRACE_WORK_ID_M5P, MAX_WORK_COUNT); this->err_report_vendor = NULL; #ifdef MEASURE_TIME #ifdef INTERFACE_TIME { u32 i; for (i = 0; i < HIC_COMMAND_END; ++i) measure_init(&this->time[i], i); } #endif #endif return ret; } int fimc_is_interface_open(struct fimc_is_interface *this) { int i; int ret = 0; if (test_bit(IS_IF_STATE_OPEN, &this->state)) { err("already open"); ret = -EMFILE; goto exit; } dbg_interface(1, "%s\n", __func__); for (i = 0; i < FIMC_IS_STREAM_COUNT; i++) { this->streaming[i] = IS_IF_STREAMING_INIT; this->processing[i] = IS_IF_PROCESSING_INIT; atomic_set(&this->shot_check[i], 0); atomic_set(&this->shot_timeout[i], 0); atomic_set(&this->sensor_check[i], 0); atomic_set(&this->sensor_timeout[i], 0); } this->process_msg.command = 0; this->request_msg.command = 0; this->pdown_ready = IS_IF_POWER_DOWN_READY; atomic_set(&this->lock_pid, 0); clear_bit(IS_IF_STATE_START, &this->state); clear_bit(IS_IF_STATE_BUSY, &this->state); clear_bit(IS_IF_STATE_READY, &this->state); clear_bit(IS_IF_STATE_LOGGING, &this->state); init_timer(&this->timer); this->timer.expires = jiffies + (FIMC_IS_COMMAND_TIMEOUT/TRY_TIMEOUT_COUNT); this->timer.data = (unsigned long)this; this->timer.function = interface_timer; add_timer(&this->timer); set_bit(IS_IF_STATE_OPEN, &this->state); exit: return ret; } int fimc_is_interface_close(struct fimc_is_interface *this) { int ret = 0; int retry; if (!test_bit(IS_IF_STATE_OPEN, &this->state)) { err("already close"); ret = -EMFILE; goto exit; } retry = 10; while (test_busystate(this) && retry) { err("interface is busy"); msleep(20); retry--; } if (!retry) err("waiting idle is fail"); del_timer_sync(&this->timer); dbg_interface(1, "%s\n", __func__); clear_bit(IS_IF_STATE_OPEN, &this->state); exit: return ret; } void fimc_is_interface_lock(struct fimc_is_interface *this) { atomic_set(&this->lock_pid, current->pid); } void fimc_is_interface_unlock(struct fimc_is_interface *this) { atomic_set(&this->lock_pid, 0); wake_up(&this->lock_wait_queue); } void fimc_is_interface_reset(struct fimc_is_interface *this) { } int fimc_is_hw_logdump(struct fimc_is_interface *this) { size_t write_vptr, read_vptr; size_t read_cnt, read_cnt1, read_cnt2; void *read_ptr; struct fimc_is_core *core; struct fimc_is_minfo *minfo; FIMC_BUG(!this); FIMC_BUG(!this->core); if (!test_bit(IS_IF_STATE_OPEN, &this->state)) { warn("interface is closed"); read_cnt = -EINVAL; goto p_err; } if (test_and_set_bit(IS_IF_STATE_LOGGING, &this->state)) { warn("already logging"); read_cnt = -EINVAL; goto p_err; } core = (struct fimc_is_core *)this->core; minfo = &core->resourcemgr.minfo; read_cnt = 0; CALL_BUFOP(minfo->pb_fw, sync_for_cpu, minfo->pb_fw, DEBUG_REGION_OFFSET, DEBUG_REGION_SIZE + 4, DMA_FROM_DEVICE); write_vptr = *((int *)(minfo->kvaddr + DEBUGCTL_OFFSET)) - DEBUG_REGION_OFFSET; read_vptr = fimc_is_debug.read_vptr; if (write_vptr >= read_vptr) { read_cnt1 = write_vptr - read_vptr; read_cnt2 = 0; } else { read_cnt1 = DEBUG_REGION_SIZE - read_vptr; read_cnt2 = write_vptr; } read_cnt = read_cnt1 + read_cnt2; info("firmware message start(%zd)\n", read_cnt); if (read_cnt1) { read_ptr = (void *)(minfo->kvaddr + DEBUG_REGION_OFFSET + fimc_is_debug.read_vptr); fimc_is_print_buffer(read_ptr, read_cnt1); fimc_is_debug.read_vptr += read_cnt1; } if (fimc_is_debug.read_vptr >= DEBUG_REGION_SIZE) { if (fimc_is_debug.read_vptr > DEBUG_REGION_SIZE) err("[DBG] read_vptr(%zd) is invalid", fimc_is_debug.read_vptr); fimc_is_debug.read_vptr = 0; } if (read_cnt2) { read_ptr = (void *)(minfo->kvaddr + DEBUG_REGION_OFFSET + fimc_is_debug.read_vptr); fimc_is_print_buffer(read_ptr, read_cnt2); fimc_is_debug.read_vptr += read_cnt2; } info("end\n"); clear_bit(IS_IF_STATE_LOGGING, &this->state); p_err: return read_cnt; } int fimc_is_hw_regdump(struct fimc_is_interface *this) { int ret = 0; u32 i; void __iomem *regs; if (!test_bit(IS_IF_STATE_OPEN, &this->state)) { warn("interface is closed"); ret = -EINVAL; goto p_err; } info("\n### MCUCTL Raw Dump ###\n"); regs = (this->regs); for (i = 0; i < 16; ++i) info("MCTL Raw[%d] : %08X\n", i, readl(regs + (4 * i))); info("\n### COMMON REGS dump ###\n"); regs = this->com_regs; for (i = 0; i < 64; ++i) info("MCTL[%d] : %08X\n", i, readl(regs + (4 * i))); p_err: return ret; } int fimc_is_hw_memdump(struct fimc_is_interface *this, ulong start, ulong end) { struct fimc_is_core *core; struct fimc_is_minfo *minfo; int ret = 0; ulong *cur; u32 items, offset; char term[50], sentence[250]; if (!test_bit(IS_IF_STATE_OPEN, &this->state)) { warn("interface is closed"); ret = -EINVAL; goto p_err; } cur = (ulong *)start; items = 0; offset = 0; core = (struct fimc_is_core *)this->core; minfo = &core->resourcemgr.minfo; CALL_BUFOP(minfo->pb_fw, sync_for_cpu, minfo->pb_fw, start, (end - start), DMA_FROM_DEVICE); memset(sentence, 0, sizeof(sentence)); printk(KERN_DEBUG "[@] Memory Dump(0x%08lX ~ 0x%08lX)\n", start, end); while ((ulong)cur <= end) { if (!(items % 8)) { printk(KERN_DEBUG "%s\n", sentence); offset = 0; snprintf(term, sizeof(term), "[@] %p: ", cur); snprintf(&sentence[offset], sizeof(sentence) - offset, "%s", term); offset += strlen(term); } snprintf(term, sizeof(term), "%016lX ", *cur); snprintf(&sentence[offset], sizeof(sentence) - offset, "%s", term); offset += strlen(term); cur++; items++; } ret = (ulong)cur - end; p_err: return ret; } int fimc_is_hw_enum(struct fimc_is_interface *this) { int ret = 0; struct fimc_is_msg msg; volatile struct is_common_reg __iomem *com_regs; dbg_interface(1, "enum()\n"); /* check if hw_enum is already operated */ if (test_bit(IS_IF_STATE_READY, &this->state)) goto p_err; ret = wait_initstate(this); if (ret) { err("enum time out"); ret = -ETIME; goto p_err; } msg.id = 0; msg.command = ISR_DONE; msg.instance = 0; msg.group = 0; /* * param1 : Command id to reply to F/W * param2 : The max count of streams */ msg.param1 = IHC_GET_SENSOR_NUMBER; msg.param2 = FIMC_IS_STREAM_COUNT; msg.param3 = 0; msg.param4 = 0; ret = waiting_is_ready(this); if (ret) { err("waiting for ready is fail"); ret = -EBUSY; goto p_err; } com_regs = this->com_regs; writel(msg.command, &com_regs->hicmd); writel(msg.instance, &com_regs->hic_stream); writel(msg.param1, &com_regs->hic_param1); writel(msg.param2, &com_regs->hic_param2); writel(msg.param3, &com_regs->hic_param3); writel(msg.param4, &com_regs->hic_param4); send_interrupt(this); set_bit(IS_IF_STATE_READY, &this->state); p_err: return ret; } int fimc_is_hw_fault(struct fimc_is_interface *this) { int ret = 0; volatile struct is_common_reg __iomem *com_regs; dbg_interface(1, "fault\n"); com_regs = this->com_regs; enter_process_barrier(this); ret = waiting_is_ready(this); if (ret) { exit_process_barrier(this); err("waiting for ready is fail"); ret = -EBUSY; goto p_err; } writel(HIC_FAULT, &com_regs->hicmd); writel(0, &com_regs->hic_stream); writel(0, &com_regs->hic_param1); writel(0, &com_regs->hic_param2); writel(0, &com_regs->hic_param3); writel(0, &com_regs->hic_param4); send_interrupt(this); exit_process_barrier(this); p_err: return ret; } int fimc_is_hw_saddr(struct fimc_is_interface *this, u32 instance, u32 *setfile_addr) { int ret = 0; struct fimc_is_msg msg, reply; dbg_interface(1, "saddr(%d)\n", instance); msg.id = 0; msg.command = HIC_GET_SET_FILE_ADDR; msg.instance = instance; msg.group = 0; msg.param1 = 0; msg.param2 = 0; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); *setfile_addr = reply.param2; return ret; } int fimc_is_hw_setfile(struct fimc_is_interface *this, u32 instance) { int ret = 0; struct fimc_is_msg msg, reply; dbg_interface(1, "setfile(%d)\n", instance); msg.id = 0; msg.command = HIC_LOAD_SET_FILE; msg.instance = instance; msg.group = 0; msg.param1 = 0; msg.param2 = 0; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_open(struct fimc_is_interface *this, u32 instance, u32 module_id, u32 info, u32 path, u32 flag, u32 *mwidth, u32 *mheight) { int ret; struct fimc_is_msg msg, reply; dbg_interface(1, "open(%d,%08X)\n", module_id, flag); msg.id = 0; msg.command = HIC_OPEN_SENSOR; msg.instance = instance; msg.group = 0; msg.param1 = module_id; msg.param2 = info; msg.param3 = path; msg.param4 = flag; ret = fimc_is_set_cmd(this, &msg, &reply); *mwidth = reply.param2; *mheight = reply.param3; return ret; } int fimc_is_hw_close(struct fimc_is_interface *this, u32 instance) { int ret; struct fimc_is_msg msg, reply; dbg_interface(1, "close sensor(instance:%d)\n", instance); msg.id = 0; msg.command = HIC_CLOSE_SENSOR; msg.instance = instance; msg.group = 0; msg.param1 = 0; msg.param2 = 0; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_stream_on(struct fimc_is_interface *this, u32 instance) { int ret; struct fimc_is_msg msg, reply; FIMC_BUG(!this); dbg_interface(1, "stream_on(%d)\n", instance); msg.id = 0; msg.command = HIC_STREAM_ON; msg.instance = instance; msg.group = 0; msg.param1 = 0; msg.param2 = 0; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_stream_off(struct fimc_is_interface *this, u32 instance) { int ret; struct fimc_is_msg msg, reply; dbg_interface(1, "stream_off(%d)\n", instance); msg.id = 0; msg.command = HIC_STREAM_OFF; msg.instance = instance; msg.group = 0; msg.param1 = 0; msg.param2 = 0; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_process_on(struct fimc_is_interface *this, u32 instance, u32 group) { int ret; struct fimc_is_msg msg, reply; dbg_interface(1, "process_on(%d)\n", instance); msg.id = 0; msg.command = HIC_PROCESS_START; msg.instance = instance; msg.group = group; msg.param1 = 0; msg.param2 = 0; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_process_off(struct fimc_is_interface *this, u32 instance, u32 group, u32 mode) { int ret; struct fimc_is_msg msg, reply; WARN_ON(mode >= 2); dbg_interface(1, "process_off(%d)\n", instance); msg.id = 0; msg.command = HIC_PROCESS_STOP; msg.instance = instance; msg.group = group; msg.param1 = mode; msg.param2 = 0; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_i2c_lock(struct fimc_is_interface *this, u32 instance, int i2c_clk, bool lock) { int ret; struct fimc_is_msg msg, reply; dbg_interface(1, "i2c_lock(%d)\n", instance); msg.id = 0; msg.command = HIC_I2C_CONTROL_LOCK; msg.instance = instance; msg.group = 0; msg.param1 = lock; msg.param2 = i2c_clk; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_s_param(struct fimc_is_interface *this, u32 instance, u32 lindex, u32 hindex, u32 indexes) { int ret; struct fimc_is_msg msg, reply; dbg_interface(1, "s_param(%d)\n", instance); msg.id = 0; msg.command = HIC_SET_PARAMETER; msg.instance = instance; msg.group = 0; msg.param1 = ISS_PREVIEW_STILL; msg.param2 = indexes; msg.param3 = lindex; msg.param4 = hindex; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_a_param(struct fimc_is_interface *this, u32 instance, u32 group, u32 sub_mode) { int ret = 0; struct fimc_is_msg msg, reply; dbg_interface(1, "a_param(%d)\n", instance); msg.id = 0; msg.command = HIC_PREVIEW_STILL; msg.instance = instance; msg.group = group; msg.param1 = sub_mode; msg.param2 = 0; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_g_capability(struct fimc_is_interface *this, u32 instance, u32 address) { int ret; struct fimc_is_msg msg, reply; dbg_interface(1, "g_capability(%d)\n", instance); msg.id = 0; msg.command = HIC_GET_STATIC_METADATA; msg.instance = instance; msg.group = 0; msg.param1 = address; msg.param2 = 0; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_map(struct fimc_is_interface *this, u32 instance, u32 group, u32 address, u32 size) { int ret; struct fimc_is_msg msg, reply; dbg_interface(1, "[%d][%d] map(%08X, %X)\n", instance, group, address, size); msg.id = 0; msg.command = HIC_SET_A5_MAP; msg.instance = instance; msg.group = group; msg.param1 = address; msg.param2 = size; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_unmap(struct fimc_is_interface *this, u32 instance, u32 group) { int ret; struct fimc_is_msg msg, reply; dbg_interface(1, "[%d][%d] unmap\n", instance, group); msg.id = 0; msg.command = HIC_SET_A5_UNMAP; msg.instance = instance; msg.group = group; msg.param1 = 0; msg.param2 = 0; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_power_down(struct fimc_is_interface *this, u32 instance) { int ret = 0; struct fimc_is_msg msg, reply; dbg_interface(1, "pwr_down(%d)\n", instance); if (!test_bit(IS_IF_STATE_START, &this->state)) { warn("instance(%d): FW is not initialized, wait\n", instance); fimc_is_itf_fwboot_init(this); ret = fimc_is_hw_enum(this); if (ret) err("fimc_is_itf_enum is fail(%d)", ret); } msg.id = 0; msg.command = HIC_POWER_DOWN; msg.instance = instance; msg.group = 0; msg.param1 = 0; msg.param2 = 0; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_sys_ctl(struct fimc_is_interface *this, u32 instance, int cmd, int val) { int ret; struct fimc_is_msg msg, reply; dbg_interface(1, "sys_ctl(cmd(%d), val(%d))\n", cmd, val); msg.id = 0; msg.command = HIC_SYSTEM_CONTROL; msg.instance = instance; msg.group = 0; msg.param1 = cmd; msg.param2 = val; msg.param3 = 0; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_sensor_mode(struct fimc_is_interface *this, u32 instance, int cfg) { int ret; struct fimc_is_msg msg, reply; bool deinit_flag = 0; dbg_interface(1, "sensor mode(%d): %d\n", instance, cfg); if (SENSOR_MODE_DEINIT == ((cfg & SENSOR_MODE_MASK) >> SENSOR_MODE_SHIFT)) { deinit_flag = true; cfg &= ~SENSOR_MODE_MASK; } msg.id = 0; msg.command = HIC_SENSOR_MODE_CHANGE; msg.instance = instance; msg.group = 0; msg.param1 = cfg; msg.param2 = 0; msg.param3 = deinit_flag; msg.param4 = 0; ret = fimc_is_set_cmd(this, &msg, &reply); return ret; } int fimc_is_hw_shot_nblk(struct fimc_is_interface *this, u32 instance, u32 group, u32 shot, u32 fcount, u32 rcount) { int ret = 0; struct fimc_is_msg msg; /*dbg_interface(1, "shot_nblk(%d, %d)\n", instance, fcount);*/ msg.id = 0; msg.command = HIC_SHOT; msg.instance = instance; msg.group = group; msg.param1 = shot; msg.param2 = fcount; msg.param3 = rcount; msg.param4 = 0; ret = fimc_is_set_cmd_shot(this, &msg); return ret; } int fimc_is_hw_s_camctrl_nblk(struct fimc_is_interface *this, u32 instance, u32 address, u32 fcount) { int ret = 0; struct fimc_is_work *work; struct fimc_is_msg *msg; dbg_interface(1, "cam_ctrl_nblk(%d)\n", instance); get_free_work(&this->nblk_cam_ctrl, &work); if (work) { work->fcount = fcount; msg = &work->msg; msg->id = 0; msg->command = HIC_SET_CAM_CONTROL; msg->instance = instance; msg->group = 0; msg->param1 = address; msg->param2 = fcount; msg->param3 = 0; msg->param4 = 0; ret = fimc_is_set_cmd_nblk(this, work); } else { err("g_free_nblk return NULL"); print_fre_work_list(&this->nblk_cam_ctrl); print_req_work_list(&this->nblk_cam_ctrl); ret = 1; } return ret; } int fimc_is_hw_msg_test(struct fimc_is_interface *this, u32 sync_id, u32 msg_test_id) { int ret = 0; struct fimc_is_work work; struct fimc_is_msg *msg; dbg_interface(1, "msg_test_nblk(%d)\n", msg_test_id); msg = &work.msg; msg->id = 0; msg->command = HIC_MSG_TEST; msg->instance = 0; msg->group = 0; msg->param1 = msg_test_id; msg->param2 = sync_id; msg->param3 = 0; msg->param4 = 0; ret = fimc_is_set_cmd_nblk(this, &work); return ret; }