/* * Samsung Exynos5 SoC series FIMC-IS driver * * exynos5 fimc-is video functions * * Copyright (c) 2011 Samsung Electronics Co., Ltd * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_ARM_FREQ_QOS_TRACER) #include #else #include #include #endif #if IS_ENABLED(CONFIG_EXYNOS_THERMAL) || IS_ENABLED(CONFIG_EXYNOS_THERMAL_V2) #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) #include #else #include #endif #endif #include #include #include #if IS_ENABLED(CONFIG_CMU_EWF) #include #endif #include #include #include #if IS_ENABLED(CONFIG_EXYNOS_SNAPSHOT) #include #elif defined(CONFIG_DEBUG_SNAPSHOT) #include #endif #include #include #include #include "is-resourcemgr.h" #include "is-hw.h" #include "is-debug.h" #include "is-core.h" #include "is-dvfs.h" #include "is-interface-library.h" #include "votf/camerapp-votf.h" #include "is-device-camif-dma.h" #ifdef CONFIG_RKP #include #include #endif #define CLUSTER_MIN_MASK 0x0000FFFF #define CLUSTER_MIN_SHIFT 0 #define CLUSTER_MAX_MASK 0xFFFF0000 #define CLUSTER_MAX_SHIFT 16 #if IS_ENABLED(CONFIG_CMU_EWF) static unsigned int idx_ewf; #endif static struct is_pm_qos_request exynos_isp_pm_qos[IS_PM_QOS_MAX]; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) static struct freq_qos_request exynos_isp_freq_qos[IS_FREQ_QOS_MAX]; #endif static struct emstune_mode_request emstune_req; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) #define IS_GET_CPU_QOS(cpu) \ (cpufreq_cpu_get(cpu) ? &(cpufreq_cpu_get(cpu)->constraints) : NULL) #if IS_ENABLED(CONFIG_ARM_FREQ_QOS_TRACER) #define C0MIN_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_tracer_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[0]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MIN], FREQ_QOS_MIN, freq * 1000); \ } while (0) #define C0MIN_QOS_DEL() \ freq_qos_tracer_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MIN]) #define C0MIN_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MIN], freq * 1000) #define C0MAX_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_tracer_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[0]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MAX], FREQ_QOS_MAX, freq * 1000); \ } while (0) #define C0MAX_QOS_DEL() \ freq_qos_tracer_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MAX]) #define C0MAX_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MAX], freq * 1000) #define C1MIN_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_tracer_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[1]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MIN], FREQ_QOS_MIN, freq * 1000); \ } while (0) #define C1MIN_QOS_DEL() \ freq_qos_tracer_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MIN]) #define C1MIN_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MIN], freq * 1000) #define C1MAX_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_tracer_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[1]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MAX], FREQ_QOS_MAX, freq * 1000); \ } while (0) #define C1MAX_QOS_DEL() \ freq_qos_tracer_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MAX]) #define C1MAX_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MAX], freq * 1000) #define C2MIN_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_tracer_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[2]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MIN], FREQ_QOS_MIN, freq * 1000); \ } while (0) #define C2MIN_QOS_DEL() \ freq_qos_tracer_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MIN]) #define C2MIN_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MIN], freq * 1000) #define C2MAX_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_tracer_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[2]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MAX], FREQ_QOS_MAX, freq * 1000); \ } while (0) #define C2MAX_QOS_DEL() \ freq_qos_tracer_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MAX]) #define C2MAX_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MAX], freq * 1000) #else #define C0MIN_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[0]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MIN], FREQ_QOS_MIN, freq * 1000); \ } while (0) #define C0MIN_QOS_DEL() freq_qos_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MIN]) #define C0MIN_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MIN], freq * 1000) #define C0MAX_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[0]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MAX], FREQ_QOS_MAX, freq * 1000); \ } while (0) #define C0MAX_QOS_DEL() freq_qos_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MAX]) #define C0MAX_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER0_MAX], freq * 1000) #define C1MIN_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[1]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MIN], FREQ_QOS_MIN, freq * 1000); \ } while (0) #define C1MIN_QOS_DEL() freq_qos_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MIN]) #define C1MIN_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MIN], freq * 1000) #define C1MAX_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[1]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MAX], FREQ_QOS_MAX, freq * 1000); \ } while (0) #define C1MAX_QOS_DEL() freq_qos_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MAX]) #define C1MAX_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER1_MAX], freq * 1000) #define C2MIN_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[2]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MIN], FREQ_QOS_MIN, freq * 1000); \ } while (0) #define C2MIN_QOS_DEL() freq_qos_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MIN]) #define C2MIN_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MIN], freq * 1000) #define C2MAX_QOS_ADD(freq) \ do { \ struct exynos_platform_is *pdata = dev_get_platdata(is_get_is_dev()); \ freq_qos_add_request(IS_GET_CPU_QOS(pdata->cpu_cluster[2]), \ &exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MAX], FREQ_QOS_MAX, freq * 1000); \ } while (0) #define C2MAX_QOS_DEL() \ freq_qos_remove_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MAX]) #define C2MAX_QOS_UPDATE(freq) \ freq_qos_update_request(&exynos_isp_freq_qos[IS_FREQ_QOS_CLUSTER2_MAX], freq * 1000) #endif /* #if IS_ENABLED(CONFIG_ARM_FREQ_QOS_TRACER) */ #else #define C0MIN_QOS_ADD(freq) \ is_pm_qos_add_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER0_MIN], \ PM_QOS_CLUSTER0_FREQ_MIN, freq * 1000) #define C0MIN_QOS_DEL() is_pm_qos_remove_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER0_MIN]) #define C0MIN_QOS_UPDATE(freq) \ is_pm_qos_update_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER0_MIN], freq * 1000) #define C0MAX_QOS_ADD(freq) \ is_pm_qos_add_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER0_MAX], \ PM_QOS_CLUSTER0_FREQ_MAX, freq * 1000) #define C0MAX_QOS_DEL() is_pm_qos_remove_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER0_MAX]) #define C0MAX_QOS_UPDATE(freq) \ is_pm_qos_update_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER0_MAX], freq * 1000) #define C1MIN_QOS_ADD(freq) \ is_pm_qos_add_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER1_MIN], \ PM_QOS_CLUSTER1_FREQ_MIN, freq * 1000) #define C1MIN_QOS_DEL() is_pm_qos_remove_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER1_MIN]) #define C1MIN_QOS_UPDATE(freq) \ is_pm_qos_update_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER1_MIN], freq * 1000) #define C1MAX_QOS_ADD(freq) \ is_pm_qos_add_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER1_MAX], \ PM_QOS_CLUSTER1_FREQ_MAX, freq * 1000) #define C1MAX_QOS_DEL() is_pm_qos_remove_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER1_MAX]) #define C1MAX_QOS_UPDATE(freq) \ is_pm_qos_update_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER1_MAX], freq * 1000) #define C2MIN_QOS_ADD(freq) \ is_pm_qos_add_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER2_MIN], \ PM_QOS_CLUSTER2_FREQ_MIN, freq * 1000) #define C2MIN_QOS_DEL() is_pm_qos_remove_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER2_MIN]) #define C2MIN_QOS_UPDATE(freq) \ is_pm_qos_update_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER2_MIN], freq * 1000) #define C2MAX_QOS_ADD(freq) \ is_pm_qos_add_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER2_MAX], \ PM_QOS_CLUSTER2_FREQ_MAX, freq * 1000) #define C2MAX_QOS_DEL() is_pm_qos_remove_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER2_MAX]) #define C2MAX_QOS_UPDATE(freq) \ is_pm_qos_update_request(&exynos_isp_pm_qos[IS_PM_QOS_CLUSTER2_MAX], freq * 1000) #endif /* #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ extern int is_sensor_runtime_suspend(struct device *dev); extern int is_sensor_runtime_resume(struct device *dev); extern void is_vendor_resource_clean(struct is_core *core); static unsigned long pablo_vmap(unsigned long addr, unsigned int size) { int i; unsigned int npages = size >> PAGE_SHIFT; pgprot_t prot = pgprot_writecombine(PAGE_KERNEL); struct page **pages; void *vaddr; pages = kmalloc_array(npages, sizeof(struct page *), GFP_ATOMIC); if (!pages) return -ENOMEM; for (i = 0; i < npages; i++) { pages[i] = phys_to_page(addr); addr += PAGE_SIZE; } vaddr = vmap(pages, npages, VM_MAP, prot); kfree(pages); return (unsigned long)vaddr; } static int pablo_rscmgr_init_log_rmem(struct is_resourcemgr *rscmgr, struct reserved_mem *rmem) { rscmgr->minfo.phaddr_debug = rmem->base; rscmgr->minfo.kvaddr_debug = pablo_vmap(rmem->base, rmem->size); if (!rscmgr->minfo.kvaddr_debug) { probe_err("failed to map [%s]", rmem->name); return -EINVAL; } memset((void *)rscmgr->minfo.kvaddr_debug, 0x0, rmem->size - 1); dbg_snapshot_add_bl_item_info("log_camera", rmem->base, rmem->size); probe_info("[RSC]log rmem(V/P/S): 0x%pK/%pap/%pap\n", rscmgr->minfo.kvaddr_debug, &rmem->base, &rmem->size); return 0; } static struct vm_struct pablo_bin_vm; static int pablo_rscmgr_init_bin_rmem(struct is_resourcemgr *rscmgr, struct reserved_mem *rmem) { struct vm_struct *vm; unsigned int npages; int i; struct page *page; struct page **pages; pgprot_t prot = PAGE_KERNEL_EXEC; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) vm = __get_vm_area_caller(PAGE_ALIGN(rmem->size), 0, LIB_START, VMALLOC_END, __builtin_return_address(0)); #else vm = __get_vm_area(PAGE_ALIGN(rmem->size), 0, LIB_START, VMALLOC_END); #endif if (vm->size < LIB_SIZE) { probe_err("insufficient bin rmem (0x%lx < 0x%lx)", vm->size, LIB_SIZE); return -ENOMEM; } pablo_bin_vm.phys_addr = rmem->base; pablo_bin_vm.addr = vm->addr; pablo_bin_vm.size = vm->size; npages = pablo_bin_vm.size >> PAGE_SHIFT; page = phys_to_page(pablo_bin_vm.phys_addr); pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); if (!pages) { probe_err("failed to alloc pages: %d", npages); return -ENOMEM; } for (i = 0; i < npages; i++) pages[i] = page++; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) if (map_kernel_range((unsigned long)pablo_bin_vm.addr, get_vm_area_size(&pablo_bin_vm), prot, pages) < 0) { #else if (map_vm_area(&pablo_bin_vm, prot, pages)) { #endif probe_err("failed to map rmem for binary"); kfree(pages); return -ENOMEM; } #ifdef CONFIG_RKP uh_call2(UH_APP_RKP, RKP_DYNAMIC_LOAD, RKP_DYN_COMMAND_PREPARE, 0, (u64)vm->addr, (u64)get_vm_area_size(&pablo_bin_vm)); #endif probe_info("[RSC]bin rmem(V/P/S): 0x%pK/%pap/%pap\n", pablo_bin_vm.addr, &rmem->base, &rmem->size); kfree(pages); return 0; } static int pablo_rscmgr_init_rmem(struct is_resourcemgr *rscmgr, struct device_node *np) { struct of_phandle_iterator i; struct reserved_mem *rmem; if (of_phandle_iterator_init(&i, np, "memory-region", NULL, 0)) { probe_warn("no memory-region for reserved memory"); return 0; } while (!of_phandle_iterator_next(&i)) { if (!i.node) { probe_warn("invalid memory-region phandle"); continue; } rmem = of_reserved_mem_lookup(i.node); if (!rmem) { probe_err("failed to get [%s] reserved memory", i.node->name); return -EINVAL; } if (!strcmp(i.node->name, "camera_rmem")) pablo_rscmgr_init_log_rmem(rscmgr, rmem); else if (!strcmp(i.node->name, "camera_ddk") || !strcmp(i.node->name, "camera-bin")) pablo_rscmgr_init_bin_rmem(rscmgr, rmem); else probe_warn("callback not found for [%s], skipping", i.node->name); } return 0; } static int pablo_alloc_n_map(struct is_mem *mem, struct vm_struct *vm, pgprot_t prot) { int npages = PAGE_ALIGN(vm->size) / PAGE_SIZE; struct is_priv_buf *pb; struct scatterlist *sg; int i, j; struct page **pages; struct page **tpages; pages = vmalloc(sizeof(struct page *) * npages); if (!pages) { probe_err("failed to alloc pages: %d", npages); return -ENOMEM; } tpages = pages; pb = CALL_PTR_MEMOP(mem, alloc, mem->priv, vm->size, NULL, 0); if (IS_ERR_OR_NULL(pb)) { probe_err("failed to alloc buffer - addr: 0x%pK, size: 0x%lx", vm->addr, vm->size); vfree(pages); return -ENOMEM; } for_each_sg(pb->sgt->sgl, sg, pb->sgt->orig_nents, i) { int npages_this_entry = PAGE_ALIGN(sg->length) / PAGE_SIZE; struct page *page = sg_page(sg); if (i >= npages) { probe_err("failed to setup pages: %d", npages); CALL_VOID_BUFOP(pb, free, pb); vfree(pages); return -EINVAL; } for (j = 0; j < npages_this_entry; j++) *(tpages++) = page++; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) if (map_kernel_range((unsigned long)vm->addr, get_vm_area_size(vm), prot, pages) < 0) { #else if (map_vm_area(vm, prot, pages)) { #endif probe_err("failed to map buffer - addr: 0x%pK, size: 0x%lx", vm->addr, vm->size); CALL_VOID_BUFOP(pb, free, pb); vfree(pages); return -ENOMEM; } probe_info("[RSC]alloc & map(V/S): 0x%pK/0x%lx\n", vm->addr, vm->size); vfree(pages); return 0; } static int is_resourcemgr_alloc_mem(struct is_resourcemgr *resourcemgr) { struct is_mem *mem = &resourcemgr->mem; struct is_minfo *minfo = &resourcemgr->minfo; #if !defined(ENABLE_DYNAMIC_MEM) && defined(ENABLE_TNR) size_t tnr_size = TNR_DMA_SIZE; #endif int i; minfo->total_size = 0; for (i = 0; i < SENSOR_POSITION_MAX; i++) { /* calibration data for each sensor postion */ minfo->pb_cal[i] = CALL_PTR_MEMOP(mem, alloc, mem->priv, TOTAL_CAL_DATA_SIZE, NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_cal[i])) { err("failed to allocate buffer for TOTAL_CAL_DATA"); return -ENOMEM; } minfo->total_size += minfo->pb_cal[i]->size; info("[RSC]memory_alloc(TOTAL_CAL_DATA_SIZE[%d]): 0x%08lx\n", i, minfo->pb_cal[i]->size); } /* library logging */ if (!IS_ENABLED(CLOG_RESERVED_MEM)) { minfo->pb_debug = mem->contig_alloc(DEBUG_REGION_SIZE + 0x10); if (IS_ERR_OR_NULL(minfo->pb_debug)) { /* retry by ION */ minfo->pb_debug = CALL_PTR_MEMOP(mem, alloc, mem->priv, DEBUG_REGION_SIZE + 0x10, NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_debug)) { err("failed to allocate buffer for DEBUG_REGION"); return -ENOMEM; } } minfo->total_size += minfo->pb_debug->size; info("[RSC]memory_alloc(DEBUG_REGION_SIZE): 0x%08lx\n", minfo->pb_debug->size); } /* library event logging */ minfo->pb_event = mem->contig_alloc(EVENT_REGION_SIZE + 0x10); if (IS_ERR_OR_NULL(minfo->pb_event)) { /* retry by ION */ minfo->pb_event = CALL_PTR_MEMOP(mem, alloc, mem->priv, EVENT_REGION_SIZE + 0x10, NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_event)) { err("failed to allocate buffer for EVENT_REGION"); return -ENOMEM; } } minfo->total_size += minfo->pb_event->size; info("[RSC]memory_alloc(EVENT_REGION_SIZE): 0x%08lx\n", minfo->pb_event->size); /* parameter region */ minfo->pb_pregion = CALL_PTR_MEMOP(mem, alloc, mem->priv, (IS_STREAM_COUNT * PARAM_REGION_SIZE), NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_pregion)) { err("failed to allocate buffer for PARAM_REGION"); return -ENOMEM; } minfo->total_size += minfo->pb_pregion->size; info("[RSC]memory_alloc(PARAM_REGION_SIZE x %d): 0x%08lx\n", IS_STREAM_COUNT, minfo->pb_pregion->size); /* sfr dump addr region */ minfo->pb_sfr_dump_addr = CALL_PTR_MEMOP(mem, alloc, mem->priv, SFR_DUMP_SIZE, NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_sfr_dump_addr)) { err("failed to allocate buffer for SFR_DUMP_ADDR"); return -ENOMEM; } minfo->total_size += minfo->pb_sfr_dump_addr->size; info("[RSC]memory_alloc(SFR_DUMP_ADDR_SIZE): 0x%08lx\n", minfo->pb_sfr_dump_addr->size); /* sfr dump value region */ minfo->pb_sfr_dump_value = CALL_PTR_MEMOP(mem, alloc, mem->priv, SFR_DUMP_SIZE, NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_sfr_dump_value)) { err("failed to allocate buffer for SFR_DUMP_VALUE"); return -ENOMEM; } minfo->total_size += minfo->pb_sfr_dump_value->size; info("[RSC]memory_alloc(SFR_DUMP_VALUE_SIZE): 0x%08lx\n", minfo->pb_sfr_dump_value->size); #if !defined(ENABLE_DYNAMIC_MEM) /* 3aa/isp internal DMA buffer */ minfo->pb_taaisp = CALL_PTR_MEMOP(mem, alloc, mem->priv, TAAISP_DMA_SIZE, NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_taaisp)) { err("failed to allocate buffer for TAAISP_DMA"); return -ENOMEM; } minfo->total_size += minfo->pb_taaisp->size; info("[RSC]memory_alloc(TAAISP_DMA_SIZE): 0x%08lx\n", minfo->pb_taaisp->size); #if defined(ENABLE_TNR) /* TNR internal DMA buffer */ minfo->pb_tnr = CALL_PTR_MEMOP(mem, alloc, mem->priv, tnr_size, NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_tnr)) { err("failed to allocate buffer for TNR DMA"); return -ENOMEM; } minfo->total_size += minfo->pb_tnr->size; info("[RSC]memory_alloc(TNR_DMA_SIZE): 0x%08lx\n", minfo->pb_tnr->size); #endif #if (ORBMCH_DMA_SIZE > 0) /* ORBMCH internal DMA buffer */ minfo->pb_orbmch = CALL_PTR_MEMOP(mem, alloc, mem->priv, ORBMCH_DMA_SIZE, NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_orbmch)) { err("failed to allocate buffer for ORBMCH DMA"); return -ENOMEM; } minfo->total_size += minfo->pb_orbmch->size; info("[RSC]memory_alloc(ORBMCH_DMA_SIZE): 0x%08lx\n", minfo->pb_orbmch->size); #endif #endif if (DUMMY_DMA_SIZE) { minfo->pb_dummy = CALL_PTR_MEMOP(mem, alloc, mem->priv, DUMMY_DMA_SIZE, "camera_heap", 0); if (IS_ERR_OR_NULL(minfo->pb_dummy)) { err("failed to allocate buffer for dummy"); return -ENOMEM; } minfo->total_size += minfo->pb_dummy->size; info("[RSC]memory_alloc(DUMMY_DMA_SIZE): 0x%08lx\n", minfo->pb_dummy->size); } probe_info("[RSC]memory_alloc(Internal total): 0x%08lx\n", minfo->total_size); return 0; } static int is_resourcemgr_init_mem(struct is_resourcemgr *resourcemgr) { struct is_minfo *minfo = NULL; int ret = 0; int i; probe_info("%s\n", __func__); ret = is_resourcemgr_alloc_mem(resourcemgr); if (ret) { err("Couldn't alloc for FIMC-IS\n"); ret = -ENOMEM; goto p_err; } minfo = &resourcemgr->minfo; /* set information */ if (!resourcemgr->minfo.kvaddr_debug) { resourcemgr->minfo.kvaddr_debug = CALL_BUFOP(minfo->pb_debug, kvaddr, minfo->pb_debug); resourcemgr->minfo.phaddr_debug = CALL_BUFOP(minfo->pb_debug, phaddr, minfo->pb_debug); } resourcemgr->minfo.kvaddr_event = CALL_BUFOP(minfo->pb_event, kvaddr, minfo->pb_event); resourcemgr->minfo.phaddr_event = CALL_BUFOP(minfo->pb_event, phaddr, minfo->pb_event); resourcemgr->minfo.kvaddr_sfr_dump_addr = CALL_BUFOP(minfo->pb_sfr_dump_addr, kvaddr, minfo->pb_sfr_dump_addr); resourcemgr->minfo.kvaddr_sfr_dump_value = CALL_BUFOP(minfo->pb_sfr_dump_value, kvaddr, minfo->pb_sfr_dump_value); resourcemgr->minfo.kvaddr_region = CALL_BUFOP(minfo->pb_pregion, kvaddr, minfo->pb_pregion); resourcemgr->minfo.kvaddr_debug_cnt = resourcemgr->minfo.kvaddr_debug + DEBUG_REGION_SIZE; resourcemgr->minfo.kvaddr_event_cnt = resourcemgr->minfo.kvaddr_event + EVENT_REGION_SIZE; for (i = 0; i < SENSOR_POSITION_MAX; i++) resourcemgr->minfo.kvaddr_cal[i] = CALL_BUFOP(minfo->pb_cal[i], kvaddr, minfo->pb_cal[i]); if (DUMMY_DMA_SIZE) { resourcemgr->minfo.dvaddr_dummy = CALL_BUFOP(minfo->pb_dummy, dvaddr, minfo->pb_dummy); resourcemgr->minfo.kvaddr_dummy = CALL_BUFOP(minfo->pb_dummy, kvaddr, minfo->pb_dummy); resourcemgr->minfo.phaddr_dummy = CALL_BUFOP(minfo->pb_dummy, phaddr, minfo->pb_dummy); probe_info("[RSC] Dummy buffer: dva(0x%pad) kva(0x%pK) pha(0x%pad)\n", &resourcemgr->minfo.dvaddr_dummy, resourcemgr->minfo.kvaddr_dummy, &resourcemgr->minfo.phaddr_dummy); } probe_info("[RSC] Kernel virtual for debug: 0x%pK\n", resourcemgr->minfo.kvaddr_debug); probe_info("[RSC] is_init_mem done\n"); p_err: return ret; } #ifdef ENABLE_DYNAMIC_MEM static int is_resourcemgr_alloc_dynamic_mem(struct is_resourcemgr *resourcemgr) { struct is_mem *mem = &resourcemgr->mem; struct is_minfo *minfo = &resourcemgr->minfo; int ret; if (TAAISP_DMA_SIZE > 0) { /* 3aa/isp internal DMA buffer */ minfo->pb_taaisp = CALL_PTR_MEMOP(mem, alloc, mem->priv, TAAISP_DMA_SIZE, NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_taaisp)) { err("failed to allocate buffer for TAAISP_DMA memory"); ret = -ENOMEM; goto err_alloc_taaisp; } info("[RSC]memory_alloc(TAAISP_DMA_SIZE): 0x%08lx\n", minfo->pb_taaisp->size); } else { minfo->pb_taaisp = NULL; } if (TNR_DMA_SIZE > 0) { /* TNR internal DMA buffer */ #if defined(USE_CAMERA_HEAP) minfo->pb_tnr = CALL_PTR_MEMOP(mem, alloc, mem->priv, TNR_DMA_SIZE, CAMERA_HEAP_NAME, 0); #else minfo->pb_tnr = CALL_PTR_MEMOP(mem, alloc, mem->priv, TNR_DMA_SIZE, NULL, 0); #endif if (IS_ERR_OR_NULL(minfo->pb_tnr)) { err("failed to allocate buffer for TNR DMA"); ret = -ENOMEM; goto err_alloc_tnr; } info("[RSC]memory_alloc(TNR_DMA_SIZE): 0x%08lx\n", minfo->pb_tnr->size); } else { minfo->pb_tnr = NULL; } if (ORBMCH_DMA_SIZE > 0) { /* ORBMCH internal DMA buffer */ minfo->pb_orbmch = CALL_PTR_MEMOP(mem, alloc, mem->priv, ORBMCH_DMA_SIZE, NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_orbmch)) { err("failed to allocate buffer for ORBMCH DMA"); ret = -ENOMEM; goto err_alloc_orbmch; } info("[RSC]memory_alloc(ORBMCH_DMA_SIZE): 0x%08lx\n", minfo->pb_orbmch->size); } else { minfo->pb_orbmch = NULL; } return 0; err_alloc_orbmch: if (minfo->pb_tnr) CALL_VOID_BUFOP(minfo->pb_tnr, free, minfo->pb_tnr); err_alloc_tnr: err_alloc_taaisp: minfo->pb_orbmch = NULL; minfo->pb_tnr = NULL; minfo->pb_taaisp = NULL; return ret; } static int is_resourcemgr_init_dynamic_mem(struct is_resourcemgr *resourcemgr) { struct is_minfo *minfo = &resourcemgr->minfo; int ret = 0; unsigned long kva; dma_addr_t dva; probe_info("is_init_mem - ION\n"); ret = is_resourcemgr_alloc_dynamic_mem(resourcemgr); if (ret) { err("Couldn't alloc for FIMC-IS\n"); ret = -ENOMEM; goto p_err; } if (minfo->pb_taaisp) { kva = CALL_BUFOP(minfo->pb_taaisp, kvaddr, minfo->pb_taaisp); dva = CALL_BUFOP(minfo->pb_taaisp, dvaddr, minfo->pb_taaisp); info("[RSC] TAAISP_DMA memory kva:0x%pK, dva: %pad\n", kva, &dva); } if (minfo->pb_tnr) { kva = CALL_BUFOP(minfo->pb_tnr, kvaddr, minfo->pb_tnr); dva = CALL_BUFOP(minfo->pb_tnr, dvaddr, minfo->pb_tnr); info("[RSC] TNR_DMA memory kva:0x%pK, dva: %pad\n", kva, &dva); } if (minfo->pb_orbmch) { kva = CALL_BUFOP(minfo->pb_orbmch, kvaddr, minfo->pb_orbmch); dva = CALL_BUFOP(minfo->pb_orbmch, dvaddr, minfo->pb_orbmch); info("[RSC] ORBMCH_DMA memory kva:0x%pK, dva: %pad\n", kva, &dva); } info("[RSC] %s done\n", __func__); p_err: return ret; } static int is_resourcemgr_deinit_dynamic_mem(struct is_resourcemgr *resourcemgr) { struct is_minfo *minfo = &resourcemgr->minfo; if (minfo->pb_orbmch) CALL_VOID_BUFOP(minfo->pb_orbmch, free, minfo->pb_orbmch); if (minfo->pb_tnr) CALL_VOID_BUFOP(minfo->pb_tnr, free, minfo->pb_tnr); if (minfo->pb_taaisp) CALL_VOID_BUFOP(minfo->pb_taaisp, free, minfo->pb_taaisp); minfo->pb_orbmch = NULL; minfo->pb_tnr = NULL; minfo->pb_taaisp = NULL; return 0; } #endif /* #ifdef ENABLE_DYNAMIC_MEM */ #if IS_ENABLED(CONFIG_PABLO_KUNIT_TEST) int pablo_kunit_resourcemgr_init_dynamic_mem(struct is_resourcemgr *resourcemgr) { return is_resourcemgr_init_dynamic_mem(resourcemgr); } KUNIT_EXPORT_SYMBOL(pablo_kunit_resourcemgr_init_dynamic_mem); int pablo_kunit_resourcemgr_deinit_dynamic_mem(struct is_resourcemgr *resourcemgr) { return is_resourcemgr_deinit_dynamic_mem(resourcemgr); } KUNIT_EXPORT_SYMBOL(pablo_kunit_resourcemgr_deinit_dynamic_mem); #endif static int is_resourcemgr_alloc_secure_mem(struct is_resourcemgr *resourcemgr) { struct is_mem *mem = &resourcemgr->mem; struct is_minfo *minfo = &resourcemgr->minfo; int ret; if (IS_ENABLED(SECURE_CAMERA_TAAISP)) { if (TAAISP_DMA_SIZE > 0) { /* 3aa/isp internal DMA buffer */ minfo->pb_taaisp_s = CALL_PTR_MEMOP(mem, alloc, mem->priv, TAAISP_DMA_SIZE, "camera_heap", ION_FLAG_CACHED | ION_EXYNOS_FLAG_PROTECTED); if (IS_ERR_OR_NULL(minfo->pb_taaisp_s)) { err("failed to allocate buffer for TAAISP_DMA_S"); ret = -ENOMEM; goto err_alloc_taaisp_s; } info("[RSC]memory_alloc(TAAISP_DMA_S): %08lx\n", TAAISP_DMA_SIZE); } else { minfo->pb_taaisp_s = NULL; } } if (IS_ENABLED(SECURE_CAMERA_TNR)) { if (TNR_S_DMA_SIZE > 0) { minfo->pb_tnr_s = CALL_PTR_MEMOP(mem, alloc, mem->priv, TNR_S_DMA_SIZE, "secure_camera_heap", ION_EXYNOS_FLAG_PROTECTED); if (IS_ERR_OR_NULL(minfo->pb_tnr_s)) { err("failed to allocate buffer for TNR_DMA_S"); ret = -ENOMEM; goto err_alloc_tnr_s; } info("[RSC]memory_alloc(TNR_DMA_S): %08lx\n", TNR_S_DMA_SIZE); } else { minfo->pb_tnr_s = NULL; } } return 0; err_alloc_tnr_s: err_alloc_taaisp_s: minfo->pb_tnr_s = NULL; minfo->pb_taaisp_s = NULL; return ret; } static int is_resourcemgr_init_secure_mem(struct is_resourcemgr *resourcemgr) { struct is_core *core; int ret = 0; core = container_of(resourcemgr, struct is_core, resourcemgr); FIMC_BUG(!core); if (core->scenario != IS_SCENARIO_SECURE) return ret; info("is_init_secure_mem - ION\n"); ret = is_resourcemgr_alloc_secure_mem(resourcemgr); if (ret) { err("Couldn't alloc for FIMC-IS\n"); ret = -ENOMEM; goto p_err; } info("[RSC] %s done\n", __func__); p_err: return ret; } static int is_resourcemgr_deinit_secure_mem(struct is_resourcemgr *resourcemgr) { struct is_minfo *minfo = &resourcemgr->minfo; struct is_core *core; int ret = 0; core = container_of(resourcemgr, struct is_core, resourcemgr); FIMC_BUG(!core); if (minfo->pb_taaisp_s) CALL_VOID_BUFOP(minfo->pb_taaisp_s, free, minfo->pb_taaisp_s); if (minfo->pb_tnr_s) CALL_VOID_BUFOP(minfo->pb_tnr_s, free, minfo->pb_tnr_s); minfo->pb_taaisp_s = NULL; minfo->pb_tnr_s = NULL; info("[RSC] %s done\n", __func__); return ret; } int is_heap_mem_alloc_dynamic(struct is_resourcemgr *resourcemgr, int type, int heap_size) { struct is_mem *mem = &resourcemgr->mem; struct is_minfo *minfo = &resourcemgr->minfo; if (heap_size == 0) { warn("heap size is zero"); return 0; } if (type == IS_BIN_LIB_HINT_DDK) { if (minfo->kvaddr_heap_ddk) { info_lib("DDK heap is already allocated(addr:0x%pK), use it", minfo->kvaddr_heap_ddk); return 0; } #if defined(USE_CAMERA_HEAP) if (IS_ENABLED(DISABLE_DDK_HEAP_FREE)) minfo->pb_heap_ddk = CALL_PTR_MEMOP(mem, alloc, mem->priv, heap_size, NULL, 0); else minfo->pb_heap_ddk = CALL_PTR_MEMOP(mem, alloc, mem->priv, heap_size, CAMERA_HEAP_NAME, 0); #else minfo->pb_heap_ddk = CALL_PTR_MEMOP(mem, alloc, mem->priv, heap_size, NULL, 0); #endif if (IS_ERR_OR_NULL(minfo->pb_heap_ddk)) { err("failed to allocate buffer for DDK HEAP"); return -ENOMEM; } minfo->kvaddr_heap_ddk = CALL_BUFOP(minfo->pb_heap_ddk, kvaddr, minfo->pb_heap_ddk); info_lib("memory_alloc(DDK heap)(V/S): 0x%pK/0x%x", minfo->kvaddr_heap_ddk, heap_size); } else if (type == IS_BIN_LIB_HINT_RTA) { if (minfo->kvaddr_heap_rta) { info_lib("RTA heap is already allocated(addr:0x%pK), use it", minfo->kvaddr_heap_rta); return 0; } minfo->pb_heap_rta = CALL_PTR_MEMOP(mem, alloc, mem->priv, heap_size, NULL, 0); if (IS_ERR_OR_NULL(minfo->pb_heap_rta)) { err("failed to allocate buffer for RTA HEAP"); return -ENOMEM; } minfo->kvaddr_heap_rta = CALL_BUFOP(minfo->pb_heap_rta, kvaddr, minfo->pb_heap_rta); info_lib("memory_alloc(RTA heap)(V/S): 0x%pK/0x%x", minfo->kvaddr_heap_rta, heap_size); } return 0; } int is_heap_mem_free(struct is_resourcemgr *resourcemgr) { struct is_minfo *minfo = &resourcemgr->minfo; int ret = 0; if (IS_ENABLED(DISABLE_DDK_HEAP_FREE)) return 0; if (minfo->pb_heap_ddk) CALL_VOID_BUFOP(minfo->pb_heap_ddk, free, minfo->pb_heap_ddk); if (minfo->pb_heap_rta) CALL_VOID_BUFOP(minfo->pb_heap_rta, free, minfo->pb_heap_rta); minfo->pb_taaisp_s = NULL; minfo->kvaddr_heap_ddk = 0; minfo->kvaddr_heap_rta = 0; info("[RSC] %s done\n", __func__); return ret; } void is_bts_scen(struct is_resourcemgr *resourcemgr, unsigned int index, bool enable) { #if IS_ENABLED(CONFIG_EXYNOS_BTS) int ret = 0; unsigned int scen_idx; const char *name; if (resourcemgr->bts_scen != NULL) name = resourcemgr->bts_scen[index].name; else { err("%s : null bts_scen struct\n", __func__); return; } scen_idx = bts_get_scenindex(name); if (scen_idx) { if (enable) ret = bts_add_scenario(scen_idx); else ret = bts_del_scenario(scen_idx); if (ret) { err("call bts_%s_scenario is fail (%d:%s)\n", enable ? "add" : "del", scen_idx, name); } else { info("call bts_%s_scenario (%d:%s)\n", enable ? "add" : "del", scen_idx, name); } } #endif } #if IS_ENABLED(CONFIG_EXYNOS_THERMAL) || IS_ENABLED(CONFIG_EXYNOS_THERMAL_V2) static int is_tmu_notifier(struct notifier_block *nb, unsigned long state, void *data) { int ret = 0, fps = 0; struct is_resourcemgr *resourcemgr; struct is_core *core; struct is_dvfs_ctrl *dvfs_ctrl; #if IS_ENABLED(CONFIG_EXYNOS_SNAPSHOT_THERMAL) || IS_ENABLED(CONFIG_DEBUG_SNAPSHOT) char *cooling_device_name = "ISP"; #endif resourcemgr = container_of(nb, struct is_resourcemgr, tmu_notifier); dvfs_ctrl = &(resourcemgr->dvfs_ctrl); core = is_get_is_core(); if (!core) return ret; if (!atomic_read(&core->rsccount)) warn("[RSC] Camera is not opened"); switch (state) { case ISP_NORMAL: resourcemgr->tmu_state = ISP_NORMAL; resourcemgr->limited_fps = 0; break; case ISP_COLD: resourcemgr->tmu_state = ISP_COLD; resourcemgr->limited_fps = 0; break; case ISP_THROTTLING: fps = isp_cooling_get_fps(0, *(unsigned long *)data); set_bit(IS_DVFS_TMU_THROTT, &dvfs_ctrl->state); /* The FPS can be defined to any specific value. */ if (fps >= 60) { resourcemgr->tmu_state = ISP_NORMAL; resourcemgr->limited_fps = 0; clear_bit(IS_DVFS_TICK_THROTT, &dvfs_ctrl->state); warn("[RSC] THROTTLING : Unlimited FPS"); } else { resourcemgr->tmu_state = ISP_THROTTLING; resourcemgr->limited_fps = fps; warn("[RSC] THROTTLING : Limited %d FPS", fps); } break; case ISP_TRIPPING: resourcemgr->tmu_state = ISP_TRIPPING; resourcemgr->limited_fps = 5; warn("[RSC] TRIPPING : Limited 5FPS"); break; default: err("[RSC] invalid tmu state(%ld)", state); break; } #if IS_ENABLED(CONFIG_EXYNOS_SNAPSHOT_THERMAL) exynos_ss_thermal(NULL, 0, cooling_device_name, resourcemgr->limited_fps); #elif IS_ENABLED(CONFIG_DEBUG_SNAPSHOT) dbg_snapshot_thermal(NULL, 0, cooling_device_name, resourcemgr->limited_fps); #endif return ret; } #endif static int due_to_is(const char *desc, struct is_resourcemgr *rscmgr) { unsigned int i; for (i = 0; i < rscmgr->itmon_port_num; i++) { if (desc && (strstr((char *)desc, rscmgr->itmon_port_name[i]))) return 1; } return 0; } static int is_itmon_notifier(struct notifier_block *nb, unsigned long state, void *data) { int i; struct is_core *core; struct is_resourcemgr *resourcemgr; struct itmon_notifier *itmon; info("%s: NOC info\n", __func__); resourcemgr = container_of(nb, struct is_resourcemgr, itmon_notifier); core = container_of(resourcemgr, struct is_core, resourcemgr); itmon = (struct itmon_notifier *)data; if (!itmon) return NOTIFY_DONE; if (due_to_is(itmon->port, resourcemgr) || due_to_is(itmon->dest, resourcemgr) || due_to_is(itmon->master, resourcemgr)) { info("%s: init description : %s\n", __func__, itmon->port); info("%s: target descrition: %s\n", __func__, itmon->dest); info("%s: user description : %s\n", __func__, itmon->master); info("%s: Transaction Type : %s\n", __func__, itmon->read ? "READ" : "WRITE"); info("%s: target address : %lx\n",__func__, itmon->target_addr); info("%s: Power Domain : %s(%d)\n",__func__, itmon->pd_name, itmon->onoff); for (i = 0; i < IS_STREAM_COUNT; ++i) { if (!test_bit(IS_ISCHAIN_POWER_ON, &core->ischain[i].state)) continue; is_debug_s2d(true, "ITMON error"); } return NOTIFY_STOP; } return NOTIFY_DONE; } int is_resource_cdump(void) { struct is_core *core; struct is_group *group; struct is_subdev *subdev; struct is_framemgr *framemgr; struct is_groupmgr *groupmgr; struct is_device_ischain *device = NULL; struct is_device_csi *csi; int i, j; core = is_get_is_core(); if (!core) goto exit; if (atomic_read(&core->rsccount) == 0) goto exit; info("### %s dump start ###\n", __func__); cinfo("###########################\n"); cinfo("###########################\n"); cinfo("### %s dump start ###\n", __func__); groupmgr = &core->groupmgr; #if IS_ENABLED(CONFIG_EXYNOS_MEMORY_LOGGER) /* dump all CR by using memlogger */ is_debug_memlog_dump_cr_all(MEMLOG_LEVEL_EMERG); #endif /* dump per core */ for (i = 0; i < IS_STREAM_COUNT; ++i) { device = &core->ischain[i]; if (!test_bit(IS_ISCHAIN_OPEN, &device->state)) continue; if (test_bit(IS_ISCHAIN_CLOSING, &device->state)) continue; /* clock & gpio dump */ if (!in_interrupt()) { struct exynos_platform_is *pdata = dev_get_platdata(&core->pdev->dev); pdata->clk_dump(&core->pdev->dev); } cinfo("### 2. SFR DUMP ###\n"); break; } /* dump per ischain */ for (i = 0; i < IS_STREAM_COUNT; ++i) { device = &core->ischain[i]; if (!test_bit(IS_ISCHAIN_OPEN, &device->state)) continue; if (test_bit(IS_ISCHAIN_CLOSING, &device->state)) continue; if (device->sensor && !test_bit(IS_ISCHAIN_REPROCESSING, &device->state)) { csi = (struct is_device_csi *)v4l2_get_subdevdata(device->sensor->subdev_csi); if (csi) csi_hw_cdump_all(csi); } cinfo("### 3. DUMP frame manager ###\n"); /* dump all framemgr */ group = groupmgr->leader[i]; while (group) { if (!test_bit(IS_GROUP_OPEN, &group->state)) break; for (j = 0; j < ENTRY_END; j++) { subdev = group->subdev[j]; if (subdev && test_bit(IS_SUBDEV_START, &subdev->state)) { framemgr = GET_SUBDEV_FRAMEMGR(subdev); if (framemgr) { unsigned long flags; cinfo("[%d][%s] framemgr dump\n", subdev->instance, subdev->name); framemgr_e_barrier_irqs(framemgr, 0, flags); frame_manager_dump_queues(framemgr); framemgr_x_barrier_irqr(framemgr, 0, flags); } } } group = group->next; } } /* dump per core */ for (i = 0; i < IS_STREAM_COUNT; ++i) { device = &core->ischain[i]; if (!test_bit(IS_ISCHAIN_OPEN, &device->state)) continue; if (test_bit(IS_ISCHAIN_CLOSING, &device->state)) continue; is_hardware_sfr_dump(&core->hardware, DEV_HW_END, false); break; } cinfo("#####################\n"); cinfo("#####################\n"); cinfo("### %s dump end ###\n", __func__); info("### %s dump end (refer camera dump)###\n", __func__); exit: return 0; } #ifdef ENABLE_KERNEL_LOG_DUMP int is_kernel_log_dump(bool overwrite) { static int dumped = 0; struct is_core *core; struct is_resourcemgr *resourcemgr; void *log_kernel = NULL; unsigned long long when; unsigned long usec; core = is_get_is_core(); if (!core) return -EINVAL; resourcemgr = &core->resourcemgr; if (dumped && !overwrite) { when = resourcemgr->kernel_log_time; usec = do_div(when, NSEC_PER_SEC) / NSEC_PER_USEC; info("kernel log was saved already at [%5lu.%06lu]\n", (unsigned long)when, usec); return -ENOSPC; } #if IS_ENABLED(CONFIG_EXYNOS_SNAPSHOT) log_kernel = (void *)exynos_ss_get_item_vaddr("log_kernel"); #elif IS_ENABLED(CONFIG_DEBUG_SNAPSHOT) log_kernel = (void *)dbg_snapshot_get_item_vaddr("log_kernel"); #endif if (!log_kernel) return -EINVAL; if (resourcemgr->kernel_log_buf) { resourcemgr->kernel_log_time = local_clock(); info("kernel log saved to %p(%p) from %p\n", resourcemgr->kernel_log_buf, (void *)virt_to_phys(resourcemgr->kernel_log_buf), log_kernel); #if IS_ENABLED(CONFIG_EXYNOS_SNAPSHOT) memcpy(resourcemgr->kernel_log_buf, log_kernel, exynos_ss_get_item_size("log_kernel")); #elif IS_ENABLED(CONFIG_DEBUG_SNAPSHOT) memcpy(resourcemgr->kernel_log_buf, log_kernel, dbg_snapshot_get_item_size("log_kernel")); #endif dumped = 1; } return 0; } #endif int is_resource_dump(void) { struct is_core *core; struct is_group *group; struct is_subdev *subdev; struct is_framemgr *framemgr; struct is_groupmgr *groupmgr; struct is_device_ischain *device = NULL; struct is_device_csi *csi; int i, j; core = is_get_is_core(); if (!core) goto exit; info("### %s dump start ###\n", __func__); groupmgr = &core->groupmgr; /* dump per core */ for (i = 0; i < IS_STREAM_COUNT; ++i) { device = &core->ischain[i]; if (!test_bit(IS_ISCHAIN_OPEN, &device->state)) continue; if (test_bit(IS_ISCHAIN_CLOSING, &device->state)) continue; /* clock & gpio dump */ schedule_work(&core->wq_data_print_clk); /* ddk log dump */ is_lib_logdump(); is_hardware_sfr_dump(&core->hardware, DEV_HW_END, false); break; } /* dump per ischain */ for (i = 0; i < IS_STREAM_COUNT; ++i) { device = &core->ischain[i]; if (!test_bit(IS_ISCHAIN_OPEN, &device->state)) continue; if (test_bit(IS_ISCHAIN_CLOSING, &device->state)) continue; if (device->sensor && !test_bit(IS_ISCHAIN_REPROCESSING, &device->state)) { csi = (struct is_device_csi *)v4l2_get_subdevdata(device->sensor->subdev_csi); if (csi) csi_hw_dump_all(csi); } /* dump all framemgr */ group = groupmgr->leader[i]; while (group) { if (!test_bit(IS_GROUP_OPEN, &group->state)) break; for (j = 0; j < ENTRY_END; j++) { subdev = group->subdev[j]; if (subdev && test_bit(IS_SUBDEV_START, &subdev->state)) { framemgr = GET_SUBDEV_FRAMEMGR(subdev); if (framemgr) { unsigned long flags; mserr(" dump framemgr..", subdev, subdev); framemgr_e_barrier_irqs(framemgr, 0, flags); frame_manager_print_queues(framemgr); framemgr_x_barrier_irqr(framemgr, 0, flags); } } } group = group->next; } } info("### %s dump end ###\n", __func__); exit: return 0; } #ifdef ENABLE_PANIC_HANDLER static int is_panic_handler(struct notifier_block *nb, ulong l, void *buf) { if (IS_ENABLED(CLOGGING)) is_resource_cdump(); else is_resource_dump(); return 0; } static struct notifier_block notify_panic_block = { .notifier_call = is_panic_handler, }; #endif #if defined(ENABLE_REBOOT_HANDLER) static int is_reboot_handler(struct notifier_block *nb, ulong l, void *buf) { struct is_core *core; info("%s enter\n", __func__); core = is_get_is_core(); if (core) is_cleanup(core); info("%s exit\n", __func__); return 0; } static struct notifier_block notify_reboot_block = { .notifier_call = is_reboot_handler, }; #endif #if defined(DISABLE_CORE_IDLE_STATE) static void is_resourcemgr_c2_disable_work(struct work_struct *data) { /* CPU idle on/off */ info("%s: call cpuidle_pause()\n", __func__); cpuidle_pause_and_lock(); } #endif struct emstune_mode_request *is_get_emstune_req(void) { return &emstune_req; } struct is_pm_qos_request *is_get_pm_qos(void) { return exynos_isp_pm_qos; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) struct freq_qos_request *is_get_freq_qos(void) { return exynos_isp_freq_qos; } #endif static struct vm_struct pablo_heap_vm; static struct vm_struct pablo_heap_rta_vm; int is_resourcemgr_probe(struct is_resourcemgr *resourcemgr, void *private_data, struct platform_device *pdev) { int ret = 0; struct device_node *np; FIMC_BUG(!resourcemgr); FIMC_BUG(!private_data); np = pdev->dev.of_node; resourcemgr->private_data = private_data; clear_bit(IS_RM_COM_POWER_ON, &resourcemgr->state); clear_bit(IS_RM_SS0_POWER_ON, &resourcemgr->state); clear_bit(IS_RM_SS1_POWER_ON, &resourcemgr->state); clear_bit(IS_RM_SS2_POWER_ON, &resourcemgr->state); clear_bit(IS_RM_SS3_POWER_ON, &resourcemgr->state); clear_bit(IS_RM_SS4_POWER_ON, &resourcemgr->state); clear_bit(IS_RM_SS5_POWER_ON, &resourcemgr->state); clear_bit(IS_RM_ISC_POWER_ON, &resourcemgr->state); clear_bit(IS_RM_POWER_ON, &resourcemgr->state); atomic_set(&resourcemgr->rsccount, 0); atomic_set(&resourcemgr->qos_refcount, 0); atomic_set(&resourcemgr->resource_sensor0.rsccount, 0); atomic_set(&resourcemgr->resource_sensor1.rsccount, 0); atomic_set(&resourcemgr->resource_sensor2.rsccount, 0); atomic_set(&resourcemgr->resource_sensor3.rsccount, 0); atomic_set(&resourcemgr->resource_ischain.rsccount, 0); atomic_set(&resourcemgr->resource_preproc.rsccount, 0); resourcemgr->cluster0 = 0; resourcemgr->cluster1 = 0; resourcemgr->cluster2 = 0; resourcemgr->hal_version = IS_HAL_VER_1_0; /* rsc mutex init */ mutex_init(&resourcemgr->rsc_lock); mutex_init(&resourcemgr->sysreg_lock); mutex_init(&resourcemgr->qos_lock); /* bus monitor unit */ resourcemgr->itmon_notifier.notifier_call = is_itmon_notifier; resourcemgr->itmon_notifier.priority = 0; itmon_notifier_chain_register(&resourcemgr->itmon_notifier); #if IS_ENABLED(CONFIG_EXYNOS_THERMAL) || IS_ENABLED(CONFIG_EXYNOS_THERMAL_V2) /* temperature monitor unit */ resourcemgr->tmu_notifier.notifier_call = is_tmu_notifier; resourcemgr->tmu_notifier.priority = 0; resourcemgr->tmu_state = ISP_NORMAL; resourcemgr->limited_fps = 0; resourcemgr->streaming_cnt = 0; ret = exynos_tmu_isp_add_notifier(&resourcemgr->tmu_notifier); if (ret) { probe_err("exynos_tmu_isp_add_notifier is fail(%d)", ret); goto p_err; } #endif ret = pablo_rscmgr_init_rmem(resourcemgr, np); if (ret) { probe_err("failed to init reserved memory(%d)", ret); goto p_err; } ret = is_resourcemgr_init_mem(resourcemgr); if (ret) { probe_err("is_resourcemgr_init_mem is fail(%d)", ret); goto p_err; } if (!pablo_bin_vm.phys_addr) { pgprot_t prot = PAGE_KERNEL_EXEC; pablo_bin_vm.addr = (void *)LIB_START; pablo_bin_vm.size = LIB_SIZE; ret = pablo_alloc_n_map(&resourcemgr->mem, &pablo_bin_vm, prot); if (ret) { probe_err("failed to alloc and map for binary(%d)", ret); goto p_err; } #ifdef CONFIG_RKP uh_call2(UH_APP_RKP, RKP_DYNAMIC_LOAD, RKP_DYN_COMMAND_PREPARE, 0, (u64)LIB_START, (u64)get_vm_area_size(&pablo_bin_vm)); #endif } if (!IS_ENABLED(DYNAMIC_HEAP_FOR_DDK_RTA)) { pablo_heap_vm.addr = (void *)HEAP_START; pablo_heap_vm.size = HEAP_SIZE; ret = pablo_alloc_n_map(&resourcemgr->mem, &pablo_heap_vm, PAGE_KERNEL); if (ret) { probe_err("failed to alloc and map for heap(%d)", ret); goto p_err; } pablo_heap_rta_vm.addr = (void *)HEAP_RTA_START; pablo_heap_rta_vm.size = HEAP_RTA_SIZE; ret = pablo_alloc_n_map(&resourcemgr->mem, &pablo_heap_rta_vm, PAGE_KERNEL); if (ret) { probe_err("failed to alloc and map for heap_rta(%d)", ret); goto p_err; } } /* dvfs controller init */ ret = is_dvfs_init(resourcemgr); if (ret) { probe_err("%s: is_dvfs_init failed!\n", __func__); goto p_err; } #ifdef ENABLE_PANIC_HANDLER atomic_notifier_chain_register(&panic_notifier_list, ¬ify_panic_block); #endif #if defined(ENABLE_REBOOT_HANDLER) register_reboot_notifier(¬ify_reboot_block); #endif #ifdef ENABLE_SHARED_METADATA spin_lock_init(&resourcemgr->shared_meta_lock); #endif #if IS_ENABLED(CONFIG_CMU_EWF) get_cmuewf_index(np, &idx_ewf); #endif #ifdef ENABLE_KERNEL_LOG_DUMP #if IS_ENABLED(CONFIG_EXYNOS_SNAPSHOT) resourcemgr->kernel_log_buf = kzalloc(exynos_ss_get_item_size("log_kernel"), GFP_KERNEL); #elif IS_ENABLED(CONFIG_DEBUG_SNAPSHOT) resourcemgr->kernel_log_buf = kzalloc(dbg_snapshot_get_item_size("log_kernel"), GFP_KERNEL); #endif #endif mutex_init(&resourcemgr->global_param.lock); resourcemgr->global_param.video_mode = 0; resourcemgr->global_param.state = 0; #if defined(DISABLE_CORE_IDLE_STATE) INIT_WORK(&resourcemgr->c2_disable_work, is_resourcemgr_c2_disable_work); #endif INIT_LIST_HEAD(&resourcemgr->regulator_list); p_err: probe_info("[RSC] %s(%d)\n", __func__, ret); return ret; } int is_resource_open(struct is_resourcemgr *resourcemgr, u32 rsc_type, void **device) { int ret = 0; u32 stream; void *result; struct is_resource *resource; struct is_core *core; struct is_device_ischain *ischain; FIMC_BUG(!resourcemgr); FIMC_BUG(!resourcemgr->private_data); FIMC_BUG(rsc_type >= RESOURCE_TYPE_MAX); result = NULL; core = (struct is_core *)resourcemgr->private_data; resource = GET_RESOURCE(resourcemgr, rsc_type); if (!resource) { err("[RSC] resource is NULL"); ret = -EINVAL; goto p_err; } switch (rsc_type) { case RESOURCE_TYPE_SENSOR0: result = &core->sensor[RESOURCE_TYPE_SENSOR0]; resource->pdev = core->sensor[RESOURCE_TYPE_SENSOR0].pdev; break; case RESOURCE_TYPE_SENSOR1: result = &core->sensor[RESOURCE_TYPE_SENSOR1]; resource->pdev = core->sensor[RESOURCE_TYPE_SENSOR1].pdev; break; case RESOURCE_TYPE_SENSOR2: result = &core->sensor[RESOURCE_TYPE_SENSOR2]; resource->pdev = core->sensor[RESOURCE_TYPE_SENSOR2].pdev; break; case RESOURCE_TYPE_SENSOR3: result = &core->sensor[RESOURCE_TYPE_SENSOR3]; resource->pdev = core->sensor[RESOURCE_TYPE_SENSOR3].pdev; break; #if (IS_SENSOR_COUNT > 4) case RESOURCE_TYPE_SENSOR4: result = &core->sensor[RESOURCE_TYPE_SENSOR4]; resource->pdev = core->sensor[RESOURCE_TYPE_SENSOR4].pdev; break; #if (IS_SENSOR_COUNT > 5) case RESOURCE_TYPE_SENSOR5: result = &core->sensor[RESOURCE_TYPE_SENSOR5]; resource->pdev = core->sensor[RESOURCE_TYPE_SENSOR5].pdev; break; #endif #endif case RESOURCE_TYPE_ISCHAIN: for (stream = 0; stream < IS_STREAM_COUNT; ++stream) { ischain = &core->ischain[stream]; if (!test_bit(IS_ISCHAIN_OPEN, &ischain->state)) { result = ischain; resource->pdev = ischain->pdev; break; } } break; } if (device) *device = result; p_err: dbgd_resource("%s\n", __func__); return ret; } KUNIT_EXPORT_SYMBOL(is_resource_open); static void is_resource_reset(struct is_resourcemgr *resourcemgr) { struct is_lic_sram *lic_sram = &resourcemgr->lic_sram; memset((void *)lic_sram, 0x0, sizeof(struct is_lic_sram)); resourcemgr->cluster0 = 0; resourcemgr->cluster1 = 0; resourcemgr->cluster2 = 0; resourcemgr->global_param.state = 0; resourcemgr->shot_timeout = SHOT_TIMEOUT; resourcemgr->shot_timeout_tick = 0; #if defined(ENABLE_HMP_BOOST) emstune_add_request(&emstune_req); #endif #if IS_ENABLED(CONFIG_CMU_EWF) set_cmuewf(idx_ewf, 1); #endif is_debug_bcm(true, 0); #if IS_ENABLED(CONFIG_EXYNOS_SCI_DBG_AUTO) smc_ppc_enable(1); #endif #ifdef DISABLE_BTS_CALC bts_calc_disable(1); #endif resourcemgr->sfrdump_ptr = 0; votfitf_init(); } static void is_resource_clear(struct is_resourcemgr *resourcemgr) { u32 current_min, current_max; current_min = (resourcemgr->cluster0 & CLUSTER_MIN_MASK) >> CLUSTER_MIN_SHIFT; current_max = (resourcemgr->cluster0 & CLUSTER_MAX_MASK) >> CLUSTER_MAX_SHIFT; if (current_min) { C0MIN_QOS_DEL(); warn("[RSC] cluster0 minfreq is not removed(%dMhz)\n", current_min); } if (current_max) { C0MAX_QOS_DEL(); warn("[RSC] cluster0 maxfreq is not removed(%dMhz)\n", current_max); } current_min = (resourcemgr->cluster1 & CLUSTER_MIN_MASK) >> CLUSTER_MIN_SHIFT; current_max = (resourcemgr->cluster1 & CLUSTER_MAX_MASK) >> CLUSTER_MAX_SHIFT; if (current_min) { C1MIN_QOS_DEL(); warn("[RSC] cluster1 minfreq is not removed(%dMhz)\n", current_min); } if (current_max) { C1MAX_QOS_DEL(); warn("[RSC] cluster1 maxfreq is not removed(%dMhz)\n", current_max); } current_min = (resourcemgr->cluster2 & CLUSTER_MIN_MASK) >> CLUSTER_MIN_SHIFT; current_max = (resourcemgr->cluster2 & CLUSTER_MAX_MASK) >> CLUSTER_MAX_SHIFT; if (current_min) { C2MIN_QOS_DEL(); warn("[RSC] cluster2 minfreq is not removed(%dMhz)\n", current_min); } if (current_max) { C2MAX_QOS_DEL(); warn("[RSC] cluster2 maxfreq is not removed(%dMhz)\n", current_max); } resourcemgr->cluster0 = 0; resourcemgr->cluster1 = 0; resourcemgr->cluster2 = 0; is_mem_check_stats(&resourcemgr->mem); /* clear hal version, default 1.0 */ resourcemgr->hal_version = IS_HAL_VER_1_0; #if defined(ENABLE_HMP_BOOST) if (resourcemgr->dvfs_ctrl.cur_hmp_bst) emstune_boost(&emstune_req, 0); emstune_remove_request(&emstune_req); #endif #if IS_ENABLED(CONFIG_CMU_EWF) set_cmuewf(idx_ewf, 0); #endif is_debug_bcm(false, CAMERA_DRIVER); #if IS_ENABLED(CONFIG_EXYNOS_SCI_DBG_AUTO) smc_ppc_enable(0); #endif #ifdef DISABLE_BTS_CALC bts_calc_disable(0); #endif } int is_resource_get(struct is_resourcemgr *resourcemgr, u32 rsc_type) { int ret = 0; u32 rsccount; struct is_resource *resource; struct is_core *core; int i, id; FIMC_BUG(!resourcemgr); FIMC_BUG(!resourcemgr->private_data); FIMC_BUG(rsc_type >= RESOURCE_TYPE_MAX); core = (struct is_core *)resourcemgr->private_data; mutex_lock(&resourcemgr->rsc_lock); rsccount = atomic_read(&core->rsccount); resource = GET_RESOURCE(resourcemgr, rsc_type); if (!resource) { err("[RSC] resource is NULL"); ret = -EINVAL; goto rsc_err; } if (!core->pdev) { err("[RSC] pdev is NULL"); ret = -EMFILE; goto rsc_err; } if (rsccount >= (IS_STREAM_COUNT + IS_VIDEO_SS5_NUM)) { err("[RSC] Invalid rsccount(%d)", rsccount); ret = -EMFILE; goto rsc_err; } #ifdef ENABLE_KERNEL_LOG_DUMP /* to secure kernel log when there was an instance that remain open */ { struct is_resource *resource_ischain; resource_ischain = GET_RESOURCE(resourcemgr, RESOURCE_TYPE_ISCHAIN); if ((rsc_type != RESOURCE_TYPE_ISCHAIN) && rsccount == 1) { if (atomic_read(&resource_ischain->rsccount) == 1) is_kernel_log_dump(false); } } #endif if (rsccount == 0) { TIME_LAUNCH_STR(LAUNCH_TOTAL); pm_stay_awake(&core->pdev->dev); is_resource_reset(resourcemgr); /* dvfs controller init */ ret = is_dvfs_init(resourcemgr); if (ret) { err("%s: is_dvfs_init failed!\n", __func__); goto rsc_err; } if (IS_ENABLED(SECURE_CAMERA_FACE)) { mutex_init(&core->secure_state_lock); core->secure_state = IS_STATE_UNSECURE; core->scenario = 0; info("%s: is secure state has reset\n", __func__); } core->vender.opening_hint = IS_OPENING_HINT_NONE; core->vender.isp_max_width = 0; core->vender.isp_max_height = 0; for (i = 0; i < MAX_SENSOR_SHARED_RSC; i++) { spin_lock_init(&core->shared_rsc_slock[i]); atomic_set(&core->shared_rsc_count[i], 0); } for (i = 0; i < SENSOR_CONTROL_I2C_MAX; i++) atomic_set(&core->i2c_rsccount[i], 0); #ifdef ENABLE_DYNAMIC_MEM ret = is_resourcemgr_init_dynamic_mem(resourcemgr); if (ret) { err("is_resourcemgr_init_dynamic_mem is fail(%d)\n", ret); goto p_err; } #endif for (i = 0; i < resourcemgr->num_phy_ldos; i++) { ret = regulator_enable(resourcemgr->phy_ldos[i]); if (ret) { err("failed to enable PHY LDO[%d]", i); goto p_err; } usleep_range(100, 101); } } if (atomic_read(&resource->rsccount) == 0) { switch (rsc_type) { case RESOURCE_TYPE_SENSOR0: case RESOURCE_TYPE_SENSOR1: case RESOURCE_TYPE_SENSOR2: case RESOURCE_TYPE_SENSOR3: case RESOURCE_TYPE_SENSOR4: case RESOURCE_TYPE_SENSOR5: id = rsc_type - RESOURCE_TYPE_SENSOR0; #ifdef CONFIG_PM pm_runtime_get_sync(&resource->pdev->dev); #else is_sensor_runtime_resume(&resource->pdev->dev); #endif set_bit(IS_RM_SS0_POWER_ON + id, &resourcemgr->state); break; case RESOURCE_TYPE_ISCHAIN: if (test_bit(IS_RM_POWER_ON, &resourcemgr->state)) { err("all resource is not power off(%lX)", resourcemgr->state); ret = -EINVAL; goto p_err; } ret = is_debug_open(&resourcemgr->minfo); if (ret) { err("is_debug_open is fail(%d)", ret); goto p_err; } ret = is_interface_open(&core->interface); if (ret) { err("is_interface_open is fail(%d)", ret); goto p_err; } if (IS_ENABLED(SECURE_CAMERA_MEM_SHARE)) { ret = is_resourcemgr_init_secure_mem(resourcemgr); if (ret) { err("is_resourcemgr_init_secure_mem is fail(%d)\n", ret); goto p_err; } } ret = is_ischain_power(&core->ischain[0], 1); if (ret) { err("is_ischain_power is fail(%d)", ret); is_ischain_power(&core->ischain[0], 0); goto p_err; } set_bit(IS_RM_ISC_POWER_ON, &resourcemgr->state); set_bit(IS_RM_POWER_ON, &resourcemgr->state); #if defined(DISABLE_CORE_IDLE_STATE) schedule_work(&resourcemgr->c2_disable_work); #endif is_bts_scen(resourcemgr, 0, true); resourcemgr->cur_bts_scen_idx = 0; is_hw_ischain_qe_cfg(); break; default: err("[RSC] resource type(%d) is invalid", rsc_type); BUG(); break; } if (!IS_ENABLED(SKIP_LIB_LOAD) && ((rsc_type == RESOURCE_TYPE_ISCHAIN) && !test_and_set_bit(IS_BINARY_LOADED, &resourcemgr->binary_state))) { TIME_LAUNCH_STR(LAUNCH_DDK_LOAD); ret = is_load_bin(); if (ret < 0) { err("is_load_bin() is fail(%d)", ret); clear_bit(IS_BINARY_LOADED, &resourcemgr->binary_state); goto p_err; } TIME_LAUNCH_END(LAUNCH_DDK_LOAD); } is_vender_resource_get(&core->vender, rsc_type); } if (rsccount == 0) { #if defined(USE_OFFLINE_PROCESSING) ret = is_runtime_resume_post(&resource->pdev->dev); if (ret) err("is_runtime_resume_post is fail(%d)", ret); #endif #ifdef ENABLE_HWACG_CONTROL /* for CSIS HWACG */ is_hw_csi_qchannel_enable_all(true); #endif /* when the first sensor device be opened */ if (rsc_type < RESOURCE_TYPE_ISCHAIN) is_hw_camif_init(); /* It must be done after power on, because of accessing mux register */ is_camif_wdma_init(); } p_err: atomic_inc(&resource->rsccount); atomic_inc(&core->rsccount); info("[RSC] rsctype: %d, rsccount: device[%d], core[%d]\n", rsc_type, atomic_read(&resource->rsccount), rsccount + 1); rsc_err: mutex_unlock(&resourcemgr->rsc_lock); return ret; } int is_resource_put(struct is_resourcemgr *resourcemgr, u32 rsc_type) { int i, id, ret = 0; u32 rsccount; struct is_resource *resource; struct is_core *core; FIMC_BUG(!resourcemgr); FIMC_BUG(!resourcemgr->private_data); FIMC_BUG(rsc_type >= RESOURCE_TYPE_MAX); core = (struct is_core *)resourcemgr->private_data; mutex_lock(&resourcemgr->rsc_lock); rsccount = atomic_read(&core->rsccount); resource = GET_RESOURCE(resourcemgr, rsc_type); if (!resource) { err("[RSC] resource is NULL"); ret = -EINVAL; goto p_err; } if (!core->pdev) { err("[RSC] pdev is NULL"); ret = -EMFILE; goto p_err; } if (rsccount == 0) { err("[RSC] Invalid rsccount(%d)\n", rsccount); ret = -EMFILE; goto p_err; } #if defined(USE_OFFLINE_PROCESSING) if (rsccount == 1) { ret = is_runtime_suspend_pre(&resource->pdev->dev); if (ret) err("is_runtime_suspend_free is fail(%d)", ret); } #endif /* local update */ if (atomic_read(&resource->rsccount) == 1) { switch (rsc_type) { case RESOURCE_TYPE_SENSOR0: case RESOURCE_TYPE_SENSOR1: case RESOURCE_TYPE_SENSOR2: case RESOURCE_TYPE_SENSOR3: case RESOURCE_TYPE_SENSOR4: case RESOURCE_TYPE_SENSOR5: id = rsc_type - RESOURCE_TYPE_SENSOR0; #if defined(CONFIG_PM) pm_runtime_put_sync(&resource->pdev->dev); #else is_sensor_runtime_suspend(&resource->pdev->dev); #endif clear_bit(IS_RM_SS0_POWER_ON + id, &resourcemgr->state); break; case RESOURCE_TYPE_ISCHAIN: if (!IS_ENABLED(SKIP_LIB_LOAD) && test_bit(IS_BINARY_LOADED, &resourcemgr->binary_state)) { is_load_clear(); info("is_load_clear() done\n"); } if (IS_ENABLED(SECURE_CAMERA_FACE)) { if (is_secure_func(core, NULL, IS_SECURE_CAMERA_FACE, core->scenario, SMC_SECCAM_UNPREPARE)) err("Failed to is_secure_func(FACE, UNPREPARE)"); } ret = is_itf_power_down(&core->interface); if (ret) err("power down cmd is fail(%d)", ret); ret = is_ischain_power(&core->ischain[0], 0); if (ret) err("is_ischain_power is fail(%d)", ret); ret = is_interface_close(&core->interface); if (ret) err("is_interface_close is fail(%d)", ret); if (IS_ENABLED(SECURE_CAMERA_MEM_SHARE)) { ret = is_resourcemgr_deinit_secure_mem(resourcemgr); if (ret) err("is_resourcemgr_deinit_secure_mem is fail(%d)", ret); } ret = is_debug_close(); if (ret) err("is_debug_close is fail(%d)", ret); /* IS_BINARY_LOADED must be cleared after ddk shut down */ clear_bit(IS_BINARY_LOADED, &resourcemgr->binary_state); clear_bit(IS_RM_ISC_POWER_ON, &resourcemgr->state); #if defined(DISABLE_CORE_IDLE_STATE) /* CPU idle on/off */ info("%s: call cpuidle_resume()\n", __func__); flush_work(&resourcemgr->c2_disable_work); cpuidle_resume_and_unlock(); #endif if (resourcemgr->cur_bts_scen_idx) { is_bts_scen(resourcemgr, resourcemgr->cur_bts_scen_idx, false); resourcemgr->cur_bts_scen_idx = 0; } is_bts_scen(resourcemgr, 0, false); break; } is_vender_resource_put(&core->vender, rsc_type); } /* global update */ if (atomic_read(&core->rsccount) == 1) { for (i = resourcemgr->num_phy_ldos - 1; i >= 0; i--) { regulator_disable(resourcemgr->phy_ldos[i]); usleep_range(1000, 1001); } is_resource_clear(resourcemgr); #ifdef ENABLE_DYNAMIC_MEM ret = is_resourcemgr_deinit_dynamic_mem(resourcemgr); if (ret) err("is_resourcemgr_deinit_dynamic_mem is fail(%d)", ret); #endif is_vendor_resource_clean(core); core->vender.closing_hint = IS_CLOSING_HINT_NONE; ret = is_runtime_suspend_post(&resource->pdev->dev); if (ret) err("is_runtime_suspend_post is fail(%d)", ret); pm_relax(&core->pdev->dev); clear_bit(IS_RM_POWER_ON, &resourcemgr->state); } atomic_dec(&resource->rsccount); atomic_dec(&core->rsccount); info("[RSC] rsctype: %d, rsccount: device[%d], core[%d]\n", rsc_type, atomic_read(&resource->rsccount), rsccount - 1); p_err: mutex_unlock(&resourcemgr->rsc_lock); return ret; } int is_resource_ioctl(struct is_resourcemgr *resourcemgr, struct v4l2_control *ctrl) { int ret = 0; FIMC_BUG(!resourcemgr); FIMC_BUG(!ctrl); mutex_lock(&resourcemgr->qos_lock); switch (ctrl->id) { /* APOLLO CPU0~3 */ case V4L2_CID_IS_DVFS_CLUSTER0: { u32 current_min, current_max; u32 request_min, request_max; current_min = (resourcemgr->cluster0 & CLUSTER_MIN_MASK) >> CLUSTER_MIN_SHIFT; current_max = (resourcemgr->cluster0 & CLUSTER_MAX_MASK) >> CLUSTER_MAX_SHIFT; request_min = (ctrl->value & CLUSTER_MIN_MASK) >> CLUSTER_MIN_SHIFT; request_max = (ctrl->value & CLUSTER_MAX_MASK) >> CLUSTER_MAX_SHIFT; if (current_min) { if (request_min) C0MIN_QOS_UPDATE(request_min); else C0MIN_QOS_DEL(); } else { if (request_min) C0MIN_QOS_ADD(request_min); } if (current_max) { if (request_max) C0MAX_QOS_UPDATE(request_max); else C0MAX_QOS_DEL(); } else { if (request_max) C0MAX_QOS_ADD(request_max); } info("[RSC] cluster0 minfreq : %dMhz\n", request_min); info("[RSC] cluster0 maxfreq : %dMhz\n", request_max); resourcemgr->cluster0 = (request_max << CLUSTER_MAX_SHIFT) | request_min; } break; /* ATLAS CPU4~5 */ case V4L2_CID_IS_DVFS_CLUSTER1: { u32 current_min, current_max; u32 request_min, request_max; current_min = (resourcemgr->cluster1 & CLUSTER_MIN_MASK) >> CLUSTER_MIN_SHIFT; current_max = (resourcemgr->cluster1 & CLUSTER_MAX_MASK) >> CLUSTER_MAX_SHIFT; request_min = (ctrl->value & CLUSTER_MIN_MASK) >> CLUSTER_MIN_SHIFT; request_max = (ctrl->value & CLUSTER_MAX_MASK) >> CLUSTER_MAX_SHIFT; if (current_min) { if (request_min) C1MIN_QOS_UPDATE(request_min); else C1MIN_QOS_DEL(); } else { if (request_min) C1MIN_QOS_ADD(request_min); } if (current_max) { if (request_max) C1MAX_QOS_UPDATE(request_max); else C1MAX_QOS_DEL(); } else { if (request_max) C1MAX_QOS_ADD(request_max); } info("[RSC] cluster1 minfreq : %dMhz\n", request_min); info("[RSC] cluster1 maxfreq : %dMhz\n", request_max); resourcemgr->cluster1 = (request_max << CLUSTER_MAX_SHIFT) | request_min; } break; /* ATLAS CPU6~7 */ case V4L2_CID_IS_DVFS_CLUSTER2: { #if defined(PM_QOS_CLUSTER2_FREQ_MAX_DEFAULT_VALUE) u32 current_min, current_max; u32 request_min, request_max; current_min = (resourcemgr->cluster2 & CLUSTER_MIN_MASK) >> CLUSTER_MIN_SHIFT; current_max = (resourcemgr->cluster2 & CLUSTER_MAX_MASK) >> CLUSTER_MAX_SHIFT; request_min = (ctrl->value & CLUSTER_MIN_MASK) >> CLUSTER_MIN_SHIFT; request_max = (ctrl->value & CLUSTER_MAX_MASK) >> CLUSTER_MAX_SHIFT; if (current_min) { if (request_min) C2MIN_QOS_UPDATE(request_min); else C2MIN_QOS_DEL(); } else { if (request_min) C2MIN_QOS_ADD(request_min); } if (current_max) { if (request_max) C2MAX_QOS_UPDATE(request_max); else C2MAX_QOS_DEL(); } else { if (request_max) C2MAX_QOS_ADD(request_max); } info("[RSC] cluster2 minfreq : %dMhz\n", request_min); info("[RSC] cluster2 maxfreq : %dMhz\n", request_max); resourcemgr->cluster2 = (request_max << CLUSTER_MAX_SHIFT) | request_min; #endif } break; } mutex_unlock(&resourcemgr->qos_lock); return ret; } void is_resource_set_global_param(struct is_resourcemgr *resourcemgr, void *device) { bool video_mode; struct is_device_ischain *ischain = device; struct is_global_param *global_param = &resourcemgr->global_param; mutex_lock(&global_param->lock); atomic_inc(&resourcemgr->global_param.sensor_cnt); if (!global_param->state) { video_mode = IS_VIDEO_SCENARIO(ischain->setfile & IS_SETFILE_MASK); global_param->video_mode = video_mode; ischain->hardware->video_mode = video_mode; minfo("video mode %d\n", ischain, video_mode); global_param->llc_state = 0; if (ischain->llc_mode == false) set_bit(LLC_DISABLE, &global_param->llc_state); is_hw_configure_llc(true, ischain, &global_param->llc_state); } set_bit(ischain->instance, &global_param->state); mutex_unlock(&global_param->lock); } void is_resource_clear_global_param(struct is_resourcemgr *resourcemgr, void *device) { struct is_device_ischain *ischain = device; struct is_global_param *global_param = &resourcemgr->global_param; mutex_lock(&global_param->lock); clear_bit(ischain->instance, &global_param->state); if (!global_param->state) { global_param->video_mode = false; ischain->hardware->video_mode = false; is_hw_configure_llc(false, ischain, &global_param->llc_state); cancel_delayed_work_sync(&resourcemgr->dvfs_ctrl.dec_dwork); } atomic_dec(&resourcemgr->global_param.sensor_cnt); mutex_unlock(&global_param->lock); } int is_resource_update_lic_sram(struct is_resourcemgr *resourcemgr, void *device, bool on) { struct is_device_ischain *ischain = device; struct is_device_sensor *sensor; struct is_group *group; struct is_lic_sram *lic_sram; u32 taa_id; int taa_sram_sum; FIMC_BUG(!resourcemgr); FIMC_BUG(!device); /* TODO: DT flag for LIC use */ sensor = ischain->sensor; FIMC_BUG(!sensor); lic_sram = &resourcemgr->lic_sram; group = &ischain->group_3aa; if (!test_bit(IS_GROUP_OPEN, &group->state)) { mwarn("[RSC] group_3aa is closed", ischain); return 0; } /* In case of remosic capture that is not use PDP & 3AA, it is returned. */ if (sensor && !test_bit(IS_SENSOR_OTF_OUTPUT, &sensor->state)) goto p_skip_update_sram; taa_id = group->id - GROUP_ID_3AA0; if (on) { lic_sram->taa_sram[taa_id] = sensor->sensor_width; atomic_add(sensor->sensor_width, &lic_sram->taa_sram_sum); } else { lic_sram->taa_sram[taa_id] = 0; taa_sram_sum = atomic_sub_return(sensor->sensor_width, &lic_sram->taa_sram_sum); if (taa_sram_sum < 0) { mwarn("[RSC] Invalid taa_sram_sum %d\n", ischain, taa_sram_sum); atomic_set(&lic_sram->taa_sram_sum, 0); } } p_skip_update_sram: minfo("[RSC] LIC taa_sram([0]%d, [1]%d, [2]%d, [3]%d, [sum]%d)\n", ischain, lic_sram->taa_sram[0], lic_sram->taa_sram[1], lic_sram->taa_sram[2], lic_sram->taa_sram[3], atomic_read(&lic_sram->taa_sram_sum)); return 0; } int is_logsync(struct is_interface *itf, u32 sync_id, u32 msg_test_id) { int ret = 0; /* print kernel sync log */ log_sync(sync_id); #ifdef ENABLE_FW_SYNC_LOG ret = is_hw_msg_test(itf, sync_id, msg_test_id); if (ret) err("is_hw_msg_test(%d)", ret); #endif return ret; } struct is_dbuf_q *is_init_dbuf_q(void) { void *ret; int i_id, i_list; int num_list = MAX_DBUF_LIST; struct is_dbuf_q *dbuf_q; dbuf_q = vzalloc(sizeof(struct is_dbuf_q)); if (!dbuf_q) { err("failed to allocate dbuf_q"); return ERR_PTR(-ENOMEM); } for (i_id = 0; i_id < ID_DBUF_MAX; i_id++) { dbuf_q->dbuf_list[i_id] = vzalloc(sizeof(struct is_dbuf_list) * num_list); if (!dbuf_q->dbuf_list[i_id]) { err("failed to allocate dbuf_list"); ret = ERR_PTR(-ENOMEM); goto err_alloc_list; } mutex_init(&dbuf_q->lock[i_id]); dbuf_q->queu_count[i_id] = 0; INIT_LIST_HEAD(&dbuf_q->queu_list[i_id]); dbuf_q->free_count[i_id] = 0; INIT_LIST_HEAD(&dbuf_q->free_list[i_id]); } /* set free list */ for (i_id = 0; i_id < ID_DBUF_MAX; i_id++) { for (i_list = 0; i_list < num_list; i_list++) { list_add_tail(&dbuf_q->dbuf_list[i_id][i_list].list, &dbuf_q->free_list[i_id]); dbuf_q->free_count[i_id]++; } } return dbuf_q; err_alloc_list: while (i_id-- > 0) { if (dbuf_q->dbuf_list[i_id]) vfree(dbuf_q->dbuf_list[i_id]); } vfree(dbuf_q); return ret; } /* cache maintenance for user buffer */ void is_deinit_dbuf_q(struct is_dbuf_q *dbuf_q) { int i_id; for (i_id = 0; i_id < ID_DBUF_MAX; i_id++) vfree(dbuf_q->dbuf_list[i_id]); vfree(dbuf_q); } static void is_flush_dma_buf(struct is_dbuf_q *dbuf_q, u32 dma_id, u32 qcnt) { struct is_dbuf_list *dbuf_list; while (dbuf_q->queu_count[dma_id] > qcnt) { /* get queue list */ dbuf_list = list_first_entry(&dbuf_q->queu_list[dma_id], struct is_dbuf_list, list); list_del(&dbuf_list->list); dbuf_q->queu_count[dma_id]--; /* put free list */ list_add_tail(&dbuf_list->list, &dbuf_q->free_list[dma_id]); dbuf_q->free_count[dma_id]++; } } void is_q_dbuf_q(struct is_dbuf_q *dbuf_q, struct is_sub_dma_buf *sdbuf, u32 qcnt) { int dma_id, num_planes, p; struct is_dbuf_list *dbuf_list; dma_id = is_get_dma_id(sdbuf->vid); if (dma_id < 0) return; mutex_lock(&dbuf_q->lock[dma_id]); if (dbuf_q->queu_count[dma_id] > qcnt) { warn("dma_id(%d) dbuf qcnt(%d) > vb2 qcnt(%d)", dma_id, dbuf_q->queu_count[dma_id], qcnt); is_flush_dma_buf(dbuf_q, dma_id, qcnt); } if (!dbuf_q->free_count[dma_id] || list_empty(&dbuf_q->free_list[dma_id])) { warn("dma_id(%d) free list is NULL[f(%d)/q(%d)]", dma_id, dbuf_q->free_count[dma_id], dbuf_q->queu_count[dma_id]); mutex_unlock(&dbuf_q->lock[dma_id]); return; } /* get free list */ dbuf_list = list_first_entry(&dbuf_q->free_list[dma_id], struct is_dbuf_list, list); list_del(&dbuf_list->list); dbuf_q->free_count[dma_id]--; /* copy */ num_planes = sdbuf->num_plane * sdbuf->num_buffer; for (p = 0; p < num_planes; p++) dbuf_list->dbuf[p] = sdbuf->dbuf[p]; /* put queue list */ list_add_tail(&dbuf_list->list, &dbuf_q->queu_list[dma_id]); dbuf_q->queu_count[dma_id]++; mutex_unlock(&dbuf_q->lock[dma_id]); } void is_dq_dbuf_q(struct is_dbuf_q *dbuf_q, u32 dma_id, enum dma_data_direction dir) { u32 p; struct is_dbuf_list *dbuf_list; mutex_lock(&dbuf_q->lock[dma_id]); if (!dbuf_q->queu_count[dma_id] || list_empty(&dbuf_q->queu_list[dma_id])) { warn("dma_id(%d) queue list is NULL[f(%d)/q(%d)]", dma_id, dbuf_q->free_count[dma_id], dbuf_q->queu_count[dma_id]); mutex_unlock(&dbuf_q->lock[dma_id]); return; } /* get queue list */ dbuf_list = list_first_entry(&dbuf_q->queu_list[dma_id], struct is_dbuf_list, list); list_del(&dbuf_list->list); dbuf_q->queu_count[dma_id]--; /* put free list */ list_add_tail(&dbuf_list->list, &dbuf_q->free_list[dma_id]); dbuf_q->free_count[dma_id]++; mutex_unlock(&dbuf_q->lock[dma_id]); /* cache maintenance */ for (p = 0; p < IS_MAX_PLANES && dbuf_list->dbuf[p]; p++) { if (dir == DMA_FROM_DEVICE) /* cache inv */ dma_buf_begin_cpu_access(dbuf_list->dbuf[p], DMA_FROM_DEVICE); else if (dir == DMA_TO_DEVICE) /* cache clean */ dma_buf_end_cpu_access(dbuf_list->dbuf[p], DMA_TO_DEVICE); else warn("invalid direction(%d), type(%d)", dir, dma_id); } if (!p) warn("dbuf is NULL. dma_id(%d)", dma_id); }