14c5feef6SBin Du // SPDX-License-Identifier: GPL-2.0+ 24c5feef6SBin Du /* 34c5feef6SBin Du * Copyright (C) 2025 Advanced Micro Devices, Inc. 44c5feef6SBin Du */ 54c5feef6SBin Du 64c5feef6SBin Du #include <linux/iopoll.h> 74c5feef6SBin Du 8*ec4bec22SBin Du #include "isp4_debug.h" 94c5feef6SBin Du #include "isp4_fw_cmd_resp.h" 104c5feef6SBin Du #include "isp4_hw_reg.h" 114c5feef6SBin Du #include "isp4_interface.h" 124c5feef6SBin Du 134c5feef6SBin Du #define ISP4IF_FW_RESP_RB_IRQ_EN_MASK \ 144c5feef6SBin Du (ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK\ 154c5feef6SBin Du | ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK) 164c5feef6SBin Du 174c5feef6SBin Du #define ISP4IF_FW_CMD_TIMEOUT (HZ / 2) 184c5feef6SBin Du 194c5feef6SBin Du struct isp4if_rb_config { 204c5feef6SBin Du const char *name; 214c5feef6SBin Du u32 index; 224c5feef6SBin Du u32 reg_rptr; 234c5feef6SBin Du u32 reg_wptr; 244c5feef6SBin Du u32 reg_base_lo; 254c5feef6SBin Du u32 reg_base_hi; 264c5feef6SBin Du u32 reg_size; 274c5feef6SBin Du u32 val_size; 284c5feef6SBin Du u64 base_mc_addr; 294c5feef6SBin Du void *base_sys_addr; 304c5feef6SBin Du }; 314c5feef6SBin Du 324c5feef6SBin Du /* FW cmd ring buffer configuration */ 334c5feef6SBin Du static struct isp4if_rb_config isp4if_cmd_rb_config[ISP4IF_STREAM_ID_MAX] = { 344c5feef6SBin Du { 354c5feef6SBin Du .name = "CMD_RB_GBL0", 364c5feef6SBin Du .index = 3, 374c5feef6SBin Du .reg_rptr = ISP_RB_RPTR4, 384c5feef6SBin Du .reg_wptr = ISP_RB_WPTR4, 394c5feef6SBin Du .reg_base_lo = ISP_RB_BASE_LO4, 404c5feef6SBin Du .reg_base_hi = ISP_RB_BASE_HI4, 414c5feef6SBin Du .reg_size = ISP_RB_SIZE4, 424c5feef6SBin Du }, 434c5feef6SBin Du { 444c5feef6SBin Du .name = "CMD_RB_STR1", 454c5feef6SBin Du .index = 0, 464c5feef6SBin Du .reg_rptr = ISP_RB_RPTR1, 474c5feef6SBin Du .reg_wptr = ISP_RB_WPTR1, 484c5feef6SBin Du .reg_base_lo = ISP_RB_BASE_LO1, 494c5feef6SBin Du .reg_base_hi = ISP_RB_BASE_HI1, 504c5feef6SBin Du .reg_size = ISP_RB_SIZE1, 514c5feef6SBin Du }, 524c5feef6SBin Du { 534c5feef6SBin Du .name = "CMD_RB_STR2", 544c5feef6SBin Du .index = 1, 554c5feef6SBin Du .reg_rptr = ISP_RB_RPTR2, 564c5feef6SBin Du .reg_wptr = ISP_RB_WPTR2, 574c5feef6SBin Du .reg_base_lo = ISP_RB_BASE_LO2, 584c5feef6SBin Du .reg_base_hi = ISP_RB_BASE_HI2, 594c5feef6SBin Du .reg_size = ISP_RB_SIZE2, 604c5feef6SBin Du }, 614c5feef6SBin Du { 624c5feef6SBin Du .name = "CMD_RB_STR3", 634c5feef6SBin Du .index = 2, 644c5feef6SBin Du .reg_rptr = ISP_RB_RPTR3, 654c5feef6SBin Du .reg_wptr = ISP_RB_WPTR3, 664c5feef6SBin Du .reg_base_lo = ISP_RB_BASE_LO3, 674c5feef6SBin Du .reg_base_hi = ISP_RB_BASE_HI3, 684c5feef6SBin Du .reg_size = ISP_RB_SIZE3, 694c5feef6SBin Du }, 704c5feef6SBin Du }; 714c5feef6SBin Du 724c5feef6SBin Du /* FW resp ring buffer configuration */ 734c5feef6SBin Du static struct isp4if_rb_config isp4if_resp_rb_config[ISP4IF_STREAM_ID_MAX] = { 744c5feef6SBin Du { 754c5feef6SBin Du .name = "RES_RB_GBL0", 764c5feef6SBin Du .index = 3, 774c5feef6SBin Du .reg_rptr = ISP_RB_RPTR12, 784c5feef6SBin Du .reg_wptr = ISP_RB_WPTR12, 794c5feef6SBin Du .reg_base_lo = ISP_RB_BASE_LO12, 804c5feef6SBin Du .reg_base_hi = ISP_RB_BASE_HI12, 814c5feef6SBin Du .reg_size = ISP_RB_SIZE12, 824c5feef6SBin Du }, 834c5feef6SBin Du { 844c5feef6SBin Du .name = "RES_RB_STR1", 854c5feef6SBin Du .index = 0, 864c5feef6SBin Du .reg_rptr = ISP_RB_RPTR9, 874c5feef6SBin Du .reg_wptr = ISP_RB_WPTR9, 884c5feef6SBin Du .reg_base_lo = ISP_RB_BASE_LO9, 894c5feef6SBin Du .reg_base_hi = ISP_RB_BASE_HI9, 904c5feef6SBin Du .reg_size = ISP_RB_SIZE9, 914c5feef6SBin Du }, 924c5feef6SBin Du { 934c5feef6SBin Du .name = "RES_RB_STR2", 944c5feef6SBin Du .index = 1, 954c5feef6SBin Du .reg_rptr = ISP_RB_RPTR10, 964c5feef6SBin Du .reg_wptr = ISP_RB_WPTR10, 974c5feef6SBin Du .reg_base_lo = ISP_RB_BASE_LO10, 984c5feef6SBin Du .reg_base_hi = ISP_RB_BASE_HI10, 994c5feef6SBin Du .reg_size = ISP_RB_SIZE10, 1004c5feef6SBin Du }, 1014c5feef6SBin Du { 1024c5feef6SBin Du .name = "RES_RB_STR3", 1034c5feef6SBin Du .index = 2, 1044c5feef6SBin Du .reg_rptr = ISP_RB_RPTR11, 1054c5feef6SBin Du .reg_wptr = ISP_RB_WPTR11, 1064c5feef6SBin Du .reg_base_lo = ISP_RB_BASE_LO11, 1074c5feef6SBin Du .reg_base_hi = ISP_RB_BASE_HI11, 1084c5feef6SBin Du .reg_size = ISP_RB_SIZE11, 1094c5feef6SBin Du }, 1104c5feef6SBin Du }; 1114c5feef6SBin Du 1124c5feef6SBin Du /* FW log ring buffer configuration */ 1134c5feef6SBin Du static struct isp4if_rb_config isp4if_log_rb_config = { 1144c5feef6SBin Du .name = "LOG_RB", 1154c5feef6SBin Du .index = 0, 1164c5feef6SBin Du .reg_rptr = ISP_LOG_RB_RPTR0, 1174c5feef6SBin Du .reg_wptr = ISP_LOG_RB_WPTR0, 1184c5feef6SBin Du .reg_base_lo = ISP_LOG_RB_BASE_LO0, 1194c5feef6SBin Du .reg_base_hi = ISP_LOG_RB_BASE_HI0, 1204c5feef6SBin Du .reg_size = ISP_LOG_RB_SIZE0, 1214c5feef6SBin Du }; 1224c5feef6SBin Du 1234c5feef6SBin Du static struct isp4if_gpu_mem_info * 1244c5feef6SBin Du isp4if_gpu_mem_alloc(struct isp4_interface *ispif, u32 mem_size) 1254c5feef6SBin Du { 1264c5feef6SBin Du struct isp4if_gpu_mem_info *mem_info; 1274c5feef6SBin Du struct device *dev = ispif->dev; 1284c5feef6SBin Du int ret; 1294c5feef6SBin Du 1304c5feef6SBin Du mem_info = kmalloc_obj(*mem_info, GFP_KERNEL); 1314c5feef6SBin Du if (!mem_info) 1324c5feef6SBin Du return NULL; 1334c5feef6SBin Du 1344c5feef6SBin Du mem_info->mem_size = mem_size; 1354c5feef6SBin Du ret = isp_kernel_buffer_alloc(dev, mem_info->mem_size, 1364c5feef6SBin Du &mem_info->mem_handle, 1374c5feef6SBin Du &mem_info->gpu_mc_addr, 1384c5feef6SBin Du &mem_info->sys_addr); 1394c5feef6SBin Du if (ret) { 1404c5feef6SBin Du kfree(mem_info); 1414c5feef6SBin Du return NULL; 1424c5feef6SBin Du } 1434c5feef6SBin Du 1444c5feef6SBin Du return mem_info; 1454c5feef6SBin Du } 1464c5feef6SBin Du 1474c5feef6SBin Du static void isp4if_gpu_mem_free(struct isp4_interface *ispif, 1484c5feef6SBin Du struct isp4if_gpu_mem_info **mem_info_ptr) 1494c5feef6SBin Du { 1504c5feef6SBin Du struct isp4if_gpu_mem_info *mem_info = *mem_info_ptr; 1514c5feef6SBin Du struct device *dev = ispif->dev; 1524c5feef6SBin Du 1534c5feef6SBin Du if (!mem_info) { 1544c5feef6SBin Du dev_err(dev, "invalid mem_info\n"); 1554c5feef6SBin Du return; 1564c5feef6SBin Du } 1574c5feef6SBin Du 1584c5feef6SBin Du *mem_info_ptr = NULL; 1594c5feef6SBin Du isp_kernel_buffer_free(&mem_info->mem_handle, &mem_info->gpu_mc_addr, 1604c5feef6SBin Du &mem_info->sys_addr); 1614c5feef6SBin Du kfree(mem_info); 1624c5feef6SBin Du } 1634c5feef6SBin Du 1644c5feef6SBin Du static void isp4if_dealloc_fw_gpumem(struct isp4_interface *ispif) 1654c5feef6SBin Du { 1664c5feef6SBin Du isp4if_gpu_mem_free(ispif, &ispif->fw_mem_pool); 1674c5feef6SBin Du isp4if_gpu_mem_free(ispif, &ispif->fw_cmd_resp_buf); 1684c5feef6SBin Du isp4if_gpu_mem_free(ispif, &ispif->fw_log_buf); 1694c5feef6SBin Du 1704c5feef6SBin Du for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) 1714c5feef6SBin Du isp4if_gpu_mem_free(ispif, &ispif->meta_info_buf[i]); 1724c5feef6SBin Du } 1734c5feef6SBin Du 1744c5feef6SBin Du static int isp4if_alloc_fw_gpumem(struct isp4_interface *ispif) 1754c5feef6SBin Du { 1764c5feef6SBin Du struct device *dev = ispif->dev; 1774c5feef6SBin Du 1784c5feef6SBin Du ispif->fw_mem_pool = isp4if_gpu_mem_alloc(ispif, 1794c5feef6SBin Du ISP4FW_MEMORY_POOL_SIZE); 1804c5feef6SBin Du if (!ispif->fw_mem_pool) 1814c5feef6SBin Du goto error_no_memory; 1824c5feef6SBin Du 1834c5feef6SBin Du ispif->fw_cmd_resp_buf = 1844c5feef6SBin Du isp4if_gpu_mem_alloc(ispif, ISP4IF_RB_PMBMAP_MEM_SIZE); 1854c5feef6SBin Du if (!ispif->fw_cmd_resp_buf) 1864c5feef6SBin Du goto error_no_memory; 1874c5feef6SBin Du 1884c5feef6SBin Du ispif->fw_log_buf = 1894c5feef6SBin Du isp4if_gpu_mem_alloc(ispif, ISP4IF_FW_LOG_RINGBUF_SIZE); 1904c5feef6SBin Du if (!ispif->fw_log_buf) 1914c5feef6SBin Du goto error_no_memory; 1924c5feef6SBin Du 1934c5feef6SBin Du for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { 1944c5feef6SBin Du ispif->meta_info_buf[i] = 1954c5feef6SBin Du isp4if_gpu_mem_alloc(ispif, ISP4IF_META_INFO_BUF_SIZE); 1964c5feef6SBin Du if (!ispif->meta_info_buf[i]) 1974c5feef6SBin Du goto error_no_memory; 1984c5feef6SBin Du } 1994c5feef6SBin Du 2004c5feef6SBin Du return 0; 2014c5feef6SBin Du 2024c5feef6SBin Du error_no_memory: 2034c5feef6SBin Du dev_err(dev, "failed to allocate gpu memory\n"); 2044c5feef6SBin Du return -ENOMEM; 2054c5feef6SBin Du } 2064c5feef6SBin Du 2074c5feef6SBin Du static u32 isp4if_compute_check_sum(const void *buf, size_t buf_size) 2084c5feef6SBin Du { 2094c5feef6SBin Du const u8 *surplus_ptr; 2104c5feef6SBin Du const u32 *buffer; 2114c5feef6SBin Du u32 checksum = 0; 2124c5feef6SBin Du size_t i; 2134c5feef6SBin Du 2144c5feef6SBin Du buffer = (const u32 *)buf; 2154c5feef6SBin Du for (i = 0; i < buf_size / sizeof(u32); i++) 2164c5feef6SBin Du checksum += buffer[i]; 2174c5feef6SBin Du 2184c5feef6SBin Du surplus_ptr = (const u8 *)&buffer[i]; 2194c5feef6SBin Du /* add surplus data crc checksum */ 2204c5feef6SBin Du for (i = 0; i < buf_size % sizeof(u32); i++) 2214c5feef6SBin Du checksum += surplus_ptr[i]; 2224c5feef6SBin Du 2234c5feef6SBin Du return checksum; 2244c5feef6SBin Du } 2254c5feef6SBin Du 2264c5feef6SBin Du void isp4if_clear_cmdq(struct isp4_interface *ispif) 2274c5feef6SBin Du { 2284c5feef6SBin Du struct isp4if_cmd_element *buf_node, *tmp_node; 2294c5feef6SBin Du LIST_HEAD(free_list); 2304c5feef6SBin Du 2314c5feef6SBin Du scoped_guard(spinlock, &ispif->cmdq_lock) 2324c5feef6SBin Du list_splice_init(&ispif->cmdq, &free_list); 2334c5feef6SBin Du 2344c5feef6SBin Du list_for_each_entry_safe(buf_node, tmp_node, &free_list, list) 2354c5feef6SBin Du kfree(buf_node); 2364c5feef6SBin Du } 2374c5feef6SBin Du 2384c5feef6SBin Du static bool isp4if_is_cmdq_rb_full(struct isp4_interface *ispif, 2394c5feef6SBin Du enum isp4if_stream_id stream) 2404c5feef6SBin Du { 2414c5feef6SBin Du struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream]; 2424c5feef6SBin Du u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; 2434c5feef6SBin Du u32 len = rb_config->val_size; 2444c5feef6SBin Du u32 rd_ptr, wr_ptr; 2454c5feef6SBin Du u32 bytes_free; 2464c5feef6SBin Du 2474c5feef6SBin Du rd_ptr = isp4hw_rreg(ispif->mmio, rreg); 2484c5feef6SBin Du wr_ptr = isp4hw_rreg(ispif->mmio, wreg); 2494c5feef6SBin Du 2504c5feef6SBin Du /* 2514c5feef6SBin Du * Read and write pointers are equal, indicating the ring buffer 2524c5feef6SBin Du * is empty 2534c5feef6SBin Du */ 2544c5feef6SBin Du if (wr_ptr == rd_ptr) 2554c5feef6SBin Du return false; 2564c5feef6SBin Du 2574c5feef6SBin Du if (wr_ptr > rd_ptr) 2584c5feef6SBin Du bytes_free = len - (wr_ptr - rd_ptr); 2594c5feef6SBin Du else 2604c5feef6SBin Du bytes_free = rd_ptr - wr_ptr; 2614c5feef6SBin Du 2624c5feef6SBin Du /* 2634c5feef6SBin Du * Ignore one byte from the bytes free to prevent rd_ptr from equaling 2644c5feef6SBin Du * wr_ptr when the ring buffer is full, because rd_ptr == wr_ptr is 2654c5feef6SBin Du * supposed to indicate that the ring buffer is empty. 2664c5feef6SBin Du */ 2674c5feef6SBin Du return bytes_free <= sizeof(struct isp4fw_cmd); 2684c5feef6SBin Du } 2694c5feef6SBin Du 2704c5feef6SBin Du struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif, 2714c5feef6SBin Du u32 seq_num, u32 cmd_id) 2724c5feef6SBin Du { 2734c5feef6SBin Du struct isp4if_cmd_element *ele; 2744c5feef6SBin Du 2754c5feef6SBin Du guard(spinlock)(&ispif->cmdq_lock); 2764c5feef6SBin Du 2774c5feef6SBin Du list_for_each_entry(ele, &ispif->cmdq, list) { 2784c5feef6SBin Du if (ele->seq_num == seq_num && ele->cmd_id == cmd_id) { 2794c5feef6SBin Du list_del(&ele->list); 2804c5feef6SBin Du return ele; 2814c5feef6SBin Du } 2824c5feef6SBin Du } 2834c5feef6SBin Du 2844c5feef6SBin Du return NULL; 2854c5feef6SBin Du } 2864c5feef6SBin Du 2874c5feef6SBin Du /* Must check that isp4if_is_cmdq_rb_full() == false before calling */ 2884c5feef6SBin Du static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif, 2894c5feef6SBin Du enum isp4if_stream_id stream, 2904c5feef6SBin Du const struct isp4fw_cmd *cmd) 2914c5feef6SBin Du { 2924c5feef6SBin Du struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream]; 2934c5feef6SBin Du u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; 2944c5feef6SBin Du void *mem_sys = rb_config->base_sys_addr; 2954c5feef6SBin Du const u32 cmd_sz = sizeof(*cmd); 2964c5feef6SBin Du struct device *dev = ispif->dev; 2974c5feef6SBin Du u32 len = rb_config->val_size; 2984c5feef6SBin Du const void *src = cmd; 2994c5feef6SBin Du u32 rd_ptr, wr_ptr; 3004c5feef6SBin Du u32 bytes_to_end; 3014c5feef6SBin Du 3024c5feef6SBin Du rd_ptr = isp4hw_rreg(ispif->mmio, rreg); 3034c5feef6SBin Du wr_ptr = isp4hw_rreg(ispif->mmio, wreg); 3044c5feef6SBin Du if (rd_ptr >= len || wr_ptr >= len) { 3054c5feef6SBin Du dev_err(dev, 306*ec4bec22SBin Du "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, cmd_sz=%u\n", 307*ec4bec22SBin Du stream, isp4dbg_get_if_stream_str(stream), rd_ptr, 308*ec4bec22SBin Du wr_ptr, len, cmd_sz); 3094c5feef6SBin Du return -EINVAL; 3104c5feef6SBin Du } 3114c5feef6SBin Du 3124c5feef6SBin Du bytes_to_end = len - wr_ptr; 3134c5feef6SBin Du if (bytes_to_end >= cmd_sz) { 3144c5feef6SBin Du /* FW cmd is just a straight copy to the write pointer */ 3154c5feef6SBin Du memcpy(mem_sys + wr_ptr, src, cmd_sz); 3164c5feef6SBin Du isp4hw_wreg(ispif->mmio, wreg, (wr_ptr + cmd_sz) % len); 3174c5feef6SBin Du } else { 3184c5feef6SBin Du /* 3194c5feef6SBin Du * FW cmd is split because the ring buffer needs to wrap 3204c5feef6SBin Du * around 3214c5feef6SBin Du */ 3224c5feef6SBin Du memcpy(mem_sys + wr_ptr, src, bytes_to_end); 3234c5feef6SBin Du memcpy(mem_sys, src + bytes_to_end, cmd_sz - bytes_to_end); 3244c5feef6SBin Du isp4hw_wreg(ispif->mmio, wreg, cmd_sz - bytes_to_end); 3254c5feef6SBin Du } 3264c5feef6SBin Du 3274c5feef6SBin Du return 0; 3284c5feef6SBin Du } 3294c5feef6SBin Du 3304c5feef6SBin Du static inline enum isp4if_stream_id isp4if_get_fw_stream(u32 cmd_id) 3314c5feef6SBin Du { 3324c5feef6SBin Du return ISP4IF_STREAM_ID_1; 3334c5feef6SBin Du } 3344c5feef6SBin Du 3354c5feef6SBin Du static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, 3364c5feef6SBin Du const void *package, 3374c5feef6SBin Du u32 package_size, bool sync) 3384c5feef6SBin Du { 3394c5feef6SBin Du enum isp4if_stream_id stream = isp4if_get_fw_stream(cmd_id); 3404c5feef6SBin Du struct isp4if_cmd_element *ele = NULL; 3414c5feef6SBin Du struct device *dev = ispif->dev; 3424c5feef6SBin Du struct isp4fw_cmd cmd; 3434c5feef6SBin Du u32 seq_num; 3444c5feef6SBin Du int ret; 3454c5feef6SBin Du 3464c5feef6SBin Du if (package_size > sizeof(cmd.cmd_param)) { 3474c5feef6SBin Du dev_err(dev, "fail pkgsize(%u) > %zu cmd:0x%x, stream %d\n", 3484c5feef6SBin Du package_size, sizeof(cmd.cmd_param), cmd_id, stream); 3494c5feef6SBin Du return -EINVAL; 3504c5feef6SBin Du } 3514c5feef6SBin Du 3524c5feef6SBin Du /* 3534c5feef6SBin Du * The struct will be shared with ISP FW, use memset() to guarantee 3544c5feef6SBin Du * padding bits are zeroed, since this is not guaranteed on all 3554c5feef6SBin Du * compilers. 3564c5feef6SBin Du */ 3574c5feef6SBin Du memset(&cmd, 0, sizeof(cmd)); 3584c5feef6SBin Du cmd.cmd_id = cmd_id; 3594c5feef6SBin Du switch (stream) { 3604c5feef6SBin Du case ISP4IF_STREAM_ID_GLOBAL: 3614c5feef6SBin Du cmd.cmd_stream_id = ISP4FW_STREAM_ID_INVALID; 3624c5feef6SBin Du break; 3634c5feef6SBin Du case ISP4IF_STREAM_ID_1: 3644c5feef6SBin Du cmd.cmd_stream_id = ISP4FW_STREAM_ID_1; 3654c5feef6SBin Du break; 3664c5feef6SBin Du default: 3674c5feef6SBin Du dev_err(dev, "fail bad stream id %d\n", stream); 3684c5feef6SBin Du return -EINVAL; 3694c5feef6SBin Du } 3704c5feef6SBin Du 3714c5feef6SBin Du /* Allocate the sync command object early and outside of the lock */ 3724c5feef6SBin Du if (sync) { 3734c5feef6SBin Du ele = kmalloc_obj(*ele, GFP_KERNEL); 3744c5feef6SBin Du if (!ele) 3754c5feef6SBin Du return -ENOMEM; 3764c5feef6SBin Du 3774c5feef6SBin Du /* Get two references: one for the resp thread, one for us */ 3784c5feef6SBin Du atomic_set(&ele->refcnt, 2); 3794c5feef6SBin Du init_completion(&ele->cmd_done); 3804c5feef6SBin Du } 3814c5feef6SBin Du 3824c5feef6SBin Du if (package && package_size) 3834c5feef6SBin Du memcpy(cmd.cmd_param, package, package_size); 3844c5feef6SBin Du 3854c5feef6SBin Du scoped_guard(mutex, &ispif->isp4if_mutex) { 3864c5feef6SBin Du ret = read_poll_timeout(isp4if_is_cmdq_rb_full, ret, !ret, 3874c5feef6SBin Du ISP4IF_RB_FULL_SLEEP_US, 3884c5feef6SBin Du ISP4IF_RB_FULL_TIMEOUT_US, false, ispif, 3894c5feef6SBin Du stream); 3904c5feef6SBin Du if (ret) { 3914c5feef6SBin Du struct isp4if_rb_config *rb_config = 3924c5feef6SBin Du &isp4if_resp_rb_config[stream]; 3934c5feef6SBin Du u32 rd_ptr = isp4hw_rreg(ispif->mmio, 3944c5feef6SBin Du rb_config->reg_rptr); 3954c5feef6SBin Du u32 wr_ptr = isp4hw_rreg(ispif->mmio, 3964c5feef6SBin Du rb_config->reg_wptr); 3974c5feef6SBin Du 3984c5feef6SBin Du dev_err(dev, 399*ec4bec22SBin Du "failed to get free cmdq slot, stream %s(%d),rd %u, wr %u\n", 400*ec4bec22SBin Du isp4dbg_get_if_stream_str(stream), stream, 401*ec4bec22SBin Du rd_ptr, wr_ptr); 4024c5feef6SBin Du ret = -ETIMEDOUT; 4034c5feef6SBin Du goto free_ele; 4044c5feef6SBin Du } 4054c5feef6SBin Du 4064c5feef6SBin Du seq_num = ispif->host2fw_seq_num++; 4074c5feef6SBin Du cmd.cmd_seq_num = seq_num; 4084c5feef6SBin Du cmd.cmd_check_sum = isp4if_compute_check_sum(&cmd, sizeof(cmd) 4094c5feef6SBin Du - sizeof(u32)); 4104c5feef6SBin Du 4114c5feef6SBin Du /* 4124c5feef6SBin Du * only append the fw cmd to queue when its response needs to 4134c5feef6SBin Du * be waited for, currently there are only two such commands, 4144c5feef6SBin Du * disable channel and stop stream which are only sent after 4154c5feef6SBin Du * close camera 4164c5feef6SBin Du */ 4174c5feef6SBin Du if (ele) { 4184c5feef6SBin Du ele->seq_num = seq_num; 4194c5feef6SBin Du ele->cmd_id = cmd_id; 4204c5feef6SBin Du scoped_guard(spinlock, &ispif->cmdq_lock) 4214c5feef6SBin Du list_add_tail(&ele->list, &ispif->cmdq); 4224c5feef6SBin Du } 4234c5feef6SBin Du 4244c5feef6SBin Du ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd); 4254c5feef6SBin Du if (ret) { 4264c5feef6SBin Du dev_err(dev, 427*ec4bec22SBin Du "fail for insert_isp_fw_cmd cmd_id %s(0x%08x)\n", 428*ec4bec22SBin Du isp4dbg_get_cmd_str(cmd_id), cmd_id); 4294c5feef6SBin Du goto err_dequeue_ele; 4304c5feef6SBin Du } 4314c5feef6SBin Du } 4324c5feef6SBin Du 4334c5feef6SBin Du if (ele) { 4344c5feef6SBin Du ret = wait_for_completion_timeout(&ele->cmd_done, 4354c5feef6SBin Du ISP4IF_FW_CMD_TIMEOUT); 4364c5feef6SBin Du if (!ret) { 4374c5feef6SBin Du ret = -ETIMEDOUT; 4384c5feef6SBin Du goto err_dequeue_ele; 4394c5feef6SBin Du } 4404c5feef6SBin Du 4414c5feef6SBin Du ret = 0; 4424c5feef6SBin Du goto put_ele_ref; 4434c5feef6SBin Du } 4444c5feef6SBin Du 4454c5feef6SBin Du return 0; 4464c5feef6SBin Du 4474c5feef6SBin Du err_dequeue_ele: 4484c5feef6SBin Du /* 4494c5feef6SBin Du * Try to remove the command from the queue. If that fails, then it 4504c5feef6SBin Du * means the response thread is currently using the object, and we need 4514c5feef6SBin Du * to use the refcount to avoid a use-after-free by either side. 4524c5feef6SBin Du */ 4534c5feef6SBin Du if (ele && isp4if_rm_cmd_from_cmdq(ispif, seq_num, cmd_id)) 4544c5feef6SBin Du goto free_ele; 4554c5feef6SBin Du 4564c5feef6SBin Du put_ele_ref: 4574c5feef6SBin Du /* Don't free the command if we didn't put the last reference */ 4584c5feef6SBin Du if (ele && atomic_dec_return(&ele->refcnt)) 4594c5feef6SBin Du ele = NULL; 4604c5feef6SBin Du 4614c5feef6SBin Du free_ele: 4624c5feef6SBin Du kfree(ele); 4634c5feef6SBin Du return ret; 4644c5feef6SBin Du } 4654c5feef6SBin Du 4664c5feef6SBin Du static int isp4if_send_buffer(struct isp4_interface *ispif, 4674c5feef6SBin Du struct isp4if_img_buf_info *buf_info) 4684c5feef6SBin Du { 4694c5feef6SBin Du struct isp4fw_cmd_send_buffer cmd; 4704c5feef6SBin Du 4714c5feef6SBin Du /* 4724c5feef6SBin Du * The struct will be shared with ISP FW, use memset() to guarantee 4734c5feef6SBin Du * padding bits are zeroed, since this is not guaranteed on all 4744c5feef6SBin Du * compilers. 4754c5feef6SBin Du */ 4764c5feef6SBin Du memset(&cmd, 0, sizeof(cmd)); 4774c5feef6SBin Du cmd.buffer_type = ISP4FW_BUFFER_TYPE_PREVIEW; 4784c5feef6SBin Du cmd.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; 4794c5feef6SBin Du isp4if_split_addr64(buf_info->planes[0].mc_addr, 4804c5feef6SBin Du &cmd.buffer.buf_base_a_lo, 4814c5feef6SBin Du &cmd.buffer.buf_base_a_hi); 4824c5feef6SBin Du cmd.buffer.buf_size_a = buf_info->planes[0].len; 4834c5feef6SBin Du 4844c5feef6SBin Du isp4if_split_addr64(buf_info->planes[1].mc_addr, 4854c5feef6SBin Du &cmd.buffer.buf_base_b_lo, 4864c5feef6SBin Du &cmd.buffer.buf_base_b_hi); 4874c5feef6SBin Du cmd.buffer.buf_size_b = buf_info->planes[1].len; 4884c5feef6SBin Du 4894c5feef6SBin Du isp4if_split_addr64(buf_info->planes[2].mc_addr, 4904c5feef6SBin Du &cmd.buffer.buf_base_c_lo, 4914c5feef6SBin Du &cmd.buffer.buf_base_c_hi); 4924c5feef6SBin Du cmd.buffer.buf_size_c = buf_info->planes[2].len; 4934c5feef6SBin Du 4944c5feef6SBin Du return isp4if_send_fw_cmd(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &cmd, 4954c5feef6SBin Du sizeof(cmd), false); 4964c5feef6SBin Du } 4974c5feef6SBin Du 4984c5feef6SBin Du static void isp4if_init_rb_config(struct isp4_interface *ispif, 4994c5feef6SBin Du struct isp4if_rb_config *rb_config) 5004c5feef6SBin Du { 5014c5feef6SBin Du isp4hw_wreg(ispif->mmio, rb_config->reg_rptr, 0x0); 5024c5feef6SBin Du isp4hw_wreg(ispif->mmio, rb_config->reg_wptr, 0x0); 5034c5feef6SBin Du isp4hw_wreg(ispif->mmio, rb_config->reg_base_lo, 5044c5feef6SBin Du rb_config->base_mc_addr); 5054c5feef6SBin Du isp4hw_wreg(ispif->mmio, rb_config->reg_base_hi, 5064c5feef6SBin Du rb_config->base_mc_addr >> 32); 5074c5feef6SBin Du isp4hw_wreg(ispif->mmio, rb_config->reg_size, rb_config->val_size); 5084c5feef6SBin Du } 5094c5feef6SBin Du 5104c5feef6SBin Du static int isp4if_fw_init(struct isp4_interface *ispif) 5114c5feef6SBin Du { 5124c5feef6SBin Du u32 aligned_rb_chunk_size = ISP4IF_RB_PMBMAP_MEM_CHUNK & 0xffffffc0; 5134c5feef6SBin Du struct isp4if_rb_config *rb_config; 5144c5feef6SBin Du u32 offset; 5154c5feef6SBin Du unsigned int i; 5164c5feef6SBin Du 5174c5feef6SBin Du /* initialize CMD_RB streams */ 5184c5feef6SBin Du for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { 5194c5feef6SBin Du rb_config = (isp4if_cmd_rb_config + i); 5204c5feef6SBin Du offset = aligned_rb_chunk_size * rb_config->index; 5214c5feef6SBin Du 5224c5feef6SBin Du rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; 5234c5feef6SBin Du rb_config->base_sys_addr = 5244c5feef6SBin Du ispif->fw_cmd_resp_buf->sys_addr + offset; 5254c5feef6SBin Du rb_config->base_mc_addr = 5264c5feef6SBin Du ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; 5274c5feef6SBin Du 5284c5feef6SBin Du isp4if_init_rb_config(ispif, rb_config); 5294c5feef6SBin Du } 5304c5feef6SBin Du 5314c5feef6SBin Du /* initialize RESP_RB streams */ 5324c5feef6SBin Du for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { 5334c5feef6SBin Du rb_config = (isp4if_resp_rb_config + i); 5344c5feef6SBin Du offset = aligned_rb_chunk_size * 5354c5feef6SBin Du (rb_config->index + ISP4IF_RESP_CHAN_TO_RB_OFFSET - 1); 5364c5feef6SBin Du 5374c5feef6SBin Du rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; 5384c5feef6SBin Du rb_config->base_sys_addr = 5394c5feef6SBin Du ispif->fw_cmd_resp_buf->sys_addr + offset; 5404c5feef6SBin Du rb_config->base_mc_addr = 5414c5feef6SBin Du ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; 5424c5feef6SBin Du 5434c5feef6SBin Du isp4if_init_rb_config(ispif, rb_config); 5444c5feef6SBin Du } 5454c5feef6SBin Du 5464c5feef6SBin Du /* initialize LOG_RB stream */ 5474c5feef6SBin Du rb_config = &isp4if_log_rb_config; 5484c5feef6SBin Du rb_config->val_size = ISP4IF_FW_LOG_RINGBUF_SIZE; 5494c5feef6SBin Du rb_config->base_mc_addr = ispif->fw_log_buf->gpu_mc_addr; 5504c5feef6SBin Du rb_config->base_sys_addr = ispif->fw_log_buf->sys_addr; 5514c5feef6SBin Du 5524c5feef6SBin Du isp4if_init_rb_config(ispif, rb_config); 5534c5feef6SBin Du 5544c5feef6SBin Du return 0; 5554c5feef6SBin Du } 5564c5feef6SBin Du 5574c5feef6SBin Du static int isp4if_wait_fw_ready(struct isp4_interface *ispif, 5584c5feef6SBin Du u32 isp_status_addr) 5594c5feef6SBin Du { 5604c5feef6SBin Du struct device *dev = ispif->dev; 5614c5feef6SBin Du u32 timeout_ms = 100; 5624c5feef6SBin Du u32 interval_ms = 1; 5634c5feef6SBin Du u32 reg_val; 5644c5feef6SBin Du 5654c5feef6SBin Du /* wait for FW initialize done! */ 5664c5feef6SBin Du if (!read_poll_timeout(isp4hw_rreg, reg_val, reg_val 5674c5feef6SBin Du & ISP_STATUS__CCPU_REPORT_MASK, 5684c5feef6SBin Du interval_ms * 1000, timeout_ms * 1000, false, 5694c5feef6SBin Du ispif->mmio, isp_status_addr)) 5704c5feef6SBin Du return 0; 5714c5feef6SBin Du 5724c5feef6SBin Du dev_err(dev, "ISP CCPU FW boot failed\n"); 5734c5feef6SBin Du 5744c5feef6SBin Du return -ETIME; 5754c5feef6SBin Du } 5764c5feef6SBin Du 5774c5feef6SBin Du static void isp4if_enable_ccpu(struct isp4_interface *ispif) 5784c5feef6SBin Du { 5794c5feef6SBin Du u32 reg_val; 5804c5feef6SBin Du 5814c5feef6SBin Du reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET); 5824c5feef6SBin Du reg_val &= (~ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK); 5834c5feef6SBin Du isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val); 5844c5feef6SBin Du 5854c5feef6SBin Du usleep_range(100, 150); 5864c5feef6SBin Du 5874c5feef6SBin Du reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL); 5884c5feef6SBin Du reg_val &= (~ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK); 5894c5feef6SBin Du isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val); 5904c5feef6SBin Du } 5914c5feef6SBin Du 5924c5feef6SBin Du static void isp4if_disable_ccpu(struct isp4_interface *ispif) 5934c5feef6SBin Du { 5944c5feef6SBin Du u32 reg_val; 5954c5feef6SBin Du 5964c5feef6SBin Du reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL); 5974c5feef6SBin Du reg_val |= ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK; 5984c5feef6SBin Du isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val); 5994c5feef6SBin Du 6004c5feef6SBin Du usleep_range(100, 150); 6014c5feef6SBin Du 6024c5feef6SBin Du reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET); 6034c5feef6SBin Du reg_val |= ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK; 6044c5feef6SBin Du isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val); 6054c5feef6SBin Du } 6064c5feef6SBin Du 6074c5feef6SBin Du static int isp4if_fw_boot(struct isp4_interface *ispif) 6084c5feef6SBin Du { 6094c5feef6SBin Du struct device *dev = ispif->dev; 6104c5feef6SBin Du 6114c5feef6SBin Du if (ispif->status != ISP4IF_STATUS_PWR_ON) { 6124c5feef6SBin Du dev_err(dev, "invalid isp power status %d\n", ispif->status); 6134c5feef6SBin Du return -EINVAL; 6144c5feef6SBin Du } 6154c5feef6SBin Du 6164c5feef6SBin Du isp4if_disable_ccpu(ispif); 6174c5feef6SBin Du 6184c5feef6SBin Du isp4if_fw_init(ispif); 6194c5feef6SBin Du 6204c5feef6SBin Du /* clear ccpu status */ 6214c5feef6SBin Du isp4hw_wreg(ispif->mmio, ISP_STATUS, 0x0); 6224c5feef6SBin Du 6234c5feef6SBin Du isp4if_enable_ccpu(ispif); 6244c5feef6SBin Du 6254c5feef6SBin Du if (isp4if_wait_fw_ready(ispif, ISP_STATUS)) { 6264c5feef6SBin Du isp4if_disable_ccpu(ispif); 6274c5feef6SBin Du return -EINVAL; 6284c5feef6SBin Du } 6294c5feef6SBin Du 6304c5feef6SBin Du /* enable interrupts */ 6314c5feef6SBin Du isp4hw_wreg(ispif->mmio, ISP_SYS_INT0_EN, 6324c5feef6SBin Du ISP4IF_FW_RESP_RB_IRQ_EN_MASK); 6334c5feef6SBin Du 6344c5feef6SBin Du ispif->status = ISP4IF_STATUS_FW_RUNNING; 6354c5feef6SBin Du 6364c5feef6SBin Du dev_dbg(dev, "ISP CCPU FW boot success\n"); 6374c5feef6SBin Du 6384c5feef6SBin Du return 0; 6394c5feef6SBin Du } 6404c5feef6SBin Du 6414c5feef6SBin Du int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, 6424c5feef6SBin Du struct isp4fw_resp *resp) 6434c5feef6SBin Du { 6444c5feef6SBin Du struct isp4if_rb_config *rb_config = &isp4if_resp_rb_config[stream]; 6454c5feef6SBin Du u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; 6464c5feef6SBin Du void *mem_sys = rb_config->base_sys_addr; 6474c5feef6SBin Du const u32 resp_sz = sizeof(*resp); 6484c5feef6SBin Du struct device *dev = ispif->dev; 6494c5feef6SBin Du u32 len = rb_config->val_size; 6504c5feef6SBin Du u32 rd_ptr, wr_ptr; 6514c5feef6SBin Du u32 bytes_to_end; 6524c5feef6SBin Du void *dst = resp; 6534c5feef6SBin Du u32 checksum; 6544c5feef6SBin Du 6554c5feef6SBin Du rd_ptr = isp4hw_rreg(ispif->mmio, rreg); 6564c5feef6SBin Du wr_ptr = isp4hw_rreg(ispif->mmio, wreg); 6574c5feef6SBin Du if (rd_ptr >= len || wr_ptr >= len) 6584c5feef6SBin Du goto err_rb_invalid; 6594c5feef6SBin Du 6604c5feef6SBin Du /* 6614c5feef6SBin Du * Read and write pointers are equal, indicating the ring buffer is 6624c5feef6SBin Du * empty 6634c5feef6SBin Du */ 6644c5feef6SBin Du if (rd_ptr == wr_ptr) 6654c5feef6SBin Du return -ENODATA; 6664c5feef6SBin Du 6674c5feef6SBin Du bytes_to_end = len - rd_ptr; 6684c5feef6SBin Du if (bytes_to_end >= resp_sz) { 6694c5feef6SBin Du /* FW response is just a straight copy from the read pointer */ 6704c5feef6SBin Du if (wr_ptr > rd_ptr && wr_ptr - rd_ptr < resp_sz) 6714c5feef6SBin Du goto err_rb_invalid; 6724c5feef6SBin Du 6734c5feef6SBin Du memcpy(dst, mem_sys + rd_ptr, resp_sz); 6744c5feef6SBin Du isp4hw_wreg(ispif->mmio, rreg, (rd_ptr + resp_sz) % len); 6754c5feef6SBin Du } else { 6764c5feef6SBin Du /* 6774c5feef6SBin Du * FW response is split because the ring buffer wrapped 6784c5feef6SBin Du * around 6794c5feef6SBin Du */ 6804c5feef6SBin Du if (wr_ptr > rd_ptr || wr_ptr < resp_sz - bytes_to_end) 6814c5feef6SBin Du goto err_rb_invalid; 6824c5feef6SBin Du 6834c5feef6SBin Du memcpy(dst, mem_sys + rd_ptr, bytes_to_end); 6844c5feef6SBin Du memcpy(dst + bytes_to_end, mem_sys, resp_sz - bytes_to_end); 6854c5feef6SBin Du isp4hw_wreg(ispif->mmio, rreg, resp_sz - bytes_to_end); 6864c5feef6SBin Du } 6874c5feef6SBin Du 6884c5feef6SBin Du checksum = isp4if_compute_check_sum(resp, resp_sz - sizeof(u32)); 6894c5feef6SBin Du if (checksum != resp->resp_check_sum) { 6904c5feef6SBin Du dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n", 6914c5feef6SBin Du checksum, resp->resp_check_sum, rd_ptr, wr_ptr); 692*ec4bec22SBin Du dev_err(dev, "%s(%u), seqNo %u, resp_id %s(0x%x)\n", 693*ec4bec22SBin Du isp4dbg_get_if_stream_str(stream), stream, 694*ec4bec22SBin Du resp->resp_seq_num, isp4dbg_get_resp_str(resp->resp_id), 6954c5feef6SBin Du resp->resp_id); 6964c5feef6SBin Du return -EINVAL; 6974c5feef6SBin Du } 6984c5feef6SBin Du 6994c5feef6SBin Du return 0; 7004c5feef6SBin Du 7014c5feef6SBin Du err_rb_invalid: 7024c5feef6SBin Du dev_err(dev, 703*ec4bec22SBin Du "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, resp_sz=%u\n", 704*ec4bec22SBin Du stream, isp4dbg_get_if_stream_str(stream), rd_ptr, wr_ptr, len, 705*ec4bec22SBin Du resp_sz); 7064c5feef6SBin Du return -EINVAL; 7074c5feef6SBin Du } 7084c5feef6SBin Du 7094c5feef6SBin Du int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id, 7104c5feef6SBin Du const void *package, u32 package_size) 7114c5feef6SBin Du { 7124c5feef6SBin Du return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, false); 7134c5feef6SBin Du } 7144c5feef6SBin Du 7154c5feef6SBin Du int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id, 7164c5feef6SBin Du const void *package, u32 package_size) 7174c5feef6SBin Du { 7184c5feef6SBin Du return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, true); 7194c5feef6SBin Du } 7204c5feef6SBin Du 7214c5feef6SBin Du void isp4if_clear_bufq(struct isp4_interface *ispif) 7224c5feef6SBin Du { 7234c5feef6SBin Du struct isp4if_img_buf_node *buf_node, *tmp_node; 7244c5feef6SBin Du LIST_HEAD(free_list); 7254c5feef6SBin Du 7264c5feef6SBin Du scoped_guard(spinlock, &ispif->bufq_lock) 7274c5feef6SBin Du list_splice_init(&ispif->bufq, &free_list); 7284c5feef6SBin Du 7294c5feef6SBin Du list_for_each_entry_safe(buf_node, tmp_node, &free_list, node) 7304c5feef6SBin Du kfree(buf_node); 7314c5feef6SBin Du } 7324c5feef6SBin Du 7334c5feef6SBin Du void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node) 7344c5feef6SBin Du { 7354c5feef6SBin Du kfree(buf_node); 7364c5feef6SBin Du } 7374c5feef6SBin Du 7384c5feef6SBin Du struct isp4if_img_buf_node * 7394c5feef6SBin Du isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info) 7404c5feef6SBin Du { 7414c5feef6SBin Du struct isp4if_img_buf_node *node; 7424c5feef6SBin Du 7434c5feef6SBin Du node = kmalloc_obj(*node, GFP_KERNEL); 7444c5feef6SBin Du if (node) 7454c5feef6SBin Du node->buf_info = *buf_info; 7464c5feef6SBin Du 7474c5feef6SBin Du return node; 7484c5feef6SBin Du } 7494c5feef6SBin Du 7504c5feef6SBin Du struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif) 7514c5feef6SBin Du { 7524c5feef6SBin Du struct isp4if_img_buf_node *buf_node; 7534c5feef6SBin Du 7544c5feef6SBin Du guard(spinlock)(&ispif->bufq_lock); 7554c5feef6SBin Du 7564c5feef6SBin Du buf_node = list_first_entry_or_null(&ispif->bufq, typeof(*buf_node), 7574c5feef6SBin Du node); 7584c5feef6SBin Du if (buf_node) 7594c5feef6SBin Du list_del(&buf_node->node); 7604c5feef6SBin Du 7614c5feef6SBin Du return buf_node; 7624c5feef6SBin Du } 7634c5feef6SBin Du 7644c5feef6SBin Du int isp4if_queue_buffer(struct isp4_interface *ispif, 7654c5feef6SBin Du struct isp4if_img_buf_node *buf_node) 7664c5feef6SBin Du { 7674c5feef6SBin Du int ret; 7684c5feef6SBin Du 7694c5feef6SBin Du ret = isp4if_send_buffer(ispif, &buf_node->buf_info); 7704c5feef6SBin Du if (ret) 7714c5feef6SBin Du return ret; 7724c5feef6SBin Du 7734c5feef6SBin Du scoped_guard(spinlock, &ispif->bufq_lock) 7744c5feef6SBin Du list_add_tail(&buf_node->node, &ispif->bufq); 7754c5feef6SBin Du 7764c5feef6SBin Du return 0; 7774c5feef6SBin Du } 7784c5feef6SBin Du 7794c5feef6SBin Du int isp4if_stop(struct isp4_interface *ispif) 7804c5feef6SBin Du { 7814c5feef6SBin Du isp4if_disable_ccpu(ispif); 7824c5feef6SBin Du 7834c5feef6SBin Du isp4if_dealloc_fw_gpumem(ispif); 7844c5feef6SBin Du 7854c5feef6SBin Du return 0; 7864c5feef6SBin Du } 7874c5feef6SBin Du 7884c5feef6SBin Du int isp4if_start(struct isp4_interface *ispif) 7894c5feef6SBin Du { 7904c5feef6SBin Du int ret; 7914c5feef6SBin Du 7924c5feef6SBin Du ret = isp4if_alloc_fw_gpumem(ispif); 7934c5feef6SBin Du if (ret) 7944c5feef6SBin Du return ret; 7954c5feef6SBin Du 7964c5feef6SBin Du ret = isp4if_fw_boot(ispif); 7974c5feef6SBin Du if (ret) 7984c5feef6SBin Du goto failed_fw_boot; 7994c5feef6SBin Du 8004c5feef6SBin Du return 0; 8014c5feef6SBin Du 8024c5feef6SBin Du failed_fw_boot: 8034c5feef6SBin Du isp4if_dealloc_fw_gpumem(ispif); 8044c5feef6SBin Du return ret; 8054c5feef6SBin Du } 8064c5feef6SBin Du 8074c5feef6SBin Du int isp4if_deinit(struct isp4_interface *ispif) 8084c5feef6SBin Du { 8094c5feef6SBin Du isp4if_clear_cmdq(ispif); 8104c5feef6SBin Du 8114c5feef6SBin Du isp4if_clear_bufq(ispif); 8124c5feef6SBin Du 8134c5feef6SBin Du mutex_destroy(&ispif->isp4if_mutex); 8144c5feef6SBin Du 8154c5feef6SBin Du return 0; 8164c5feef6SBin Du } 8174c5feef6SBin Du 8184c5feef6SBin Du int isp4if_init(struct isp4_interface *ispif, struct device *dev, 8194c5feef6SBin Du void __iomem *isp_mmio) 8204c5feef6SBin Du { 8214c5feef6SBin Du ispif->dev = dev; 8224c5feef6SBin Du ispif->mmio = isp_mmio; 8234c5feef6SBin Du 8244c5feef6SBin Du spin_lock_init(&ispif->cmdq_lock); /* used for cmdq access */ 8254c5feef6SBin Du spin_lock_init(&ispif->bufq_lock); /* used for bufq access */ 8264c5feef6SBin Du mutex_init(&ispif->isp4if_mutex); /* used for commands sent to ispfw */ 8274c5feef6SBin Du 8284c5feef6SBin Du INIT_LIST_HEAD(&ispif->cmdq); 8294c5feef6SBin Du INIT_LIST_HEAD(&ispif->bufq); 8304c5feef6SBin Du 8314c5feef6SBin Du return 0; 8324c5feef6SBin Du } 833