xref: /linux/drivers/media/platform/amd/isp4/isp4_interface.c (revision 8c13415c8a4383447c21ec832b20b3b283f0e01a)
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