xref: /linux/drivers/media/platform/amd/isp4/isp4_subdev.c (revision 8c13415c8a4383447c21ec832b20b3b283f0e01a)
14e5e7a7dSBin Du // SPDX-License-Identifier: GPL-2.0+
24e5e7a7dSBin Du /*
34e5e7a7dSBin Du  * Copyright (C) 2025 Advanced Micro Devices, Inc.
44e5e7a7dSBin Du  */
54e5e7a7dSBin Du 
64e5e7a7dSBin Du #include <linux/pm_domain.h>
74e5e7a7dSBin Du #include <linux/units.h>
84e5e7a7dSBin Du 
94e5e7a7dSBin Du #include "isp4.h"
10*ec4bec22SBin Du #include "isp4_debug.h"
114e5e7a7dSBin Du #include "isp4_fw_cmd_resp.h"
124e5e7a7dSBin Du #include "isp4_interface.h"
134e5e7a7dSBin Du 
144e5e7a7dSBin Du #define ISP4SD_MIN_BUF_CNT_BEF_START_STREAM 4
154e5e7a7dSBin Du 
164e5e7a7dSBin Du #define ISP4SD_PERFORMANCE_STATE_LOW 0
174e5e7a7dSBin Du #define ISP4SD_PERFORMANCE_STATE_HIGH 1
184e5e7a7dSBin Du 
194e5e7a7dSBin Du /* align 32KB */
204e5e7a7dSBin Du #define ISP4SD_META_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000)
214e5e7a7dSBin Du 
224e5e7a7dSBin Du #define to_isp4_subdev(sd) container_of(sd, struct isp4_subdev, sdev)
234e5e7a7dSBin Du 
244e5e7a7dSBin Du static const char *isp4sd_entity_name = "amd isp4";
254e5e7a7dSBin Du 
264e5e7a7dSBin Du static const char *isp4sd_thread_name[ISP4SD_MAX_FW_RESP_STREAM_NUM] = {
274e5e7a7dSBin Du 	"amd_isp4_thread_global",
284e5e7a7dSBin Du 	"amd_isp4_thread_stream1",
294e5e7a7dSBin Du };
304e5e7a7dSBin Du 
314e5e7a7dSBin Du static void isp4sd_module_enable(struct isp4_subdev *isp_subdev, bool enable)
324e5e7a7dSBin Du {
334e5e7a7dSBin Du 	if (isp_subdev->enable_gpio) {
344e5e7a7dSBin Du 		gpiod_set_value(isp_subdev->enable_gpio, enable ? 1 : 0);
354e5e7a7dSBin Du 		dev_dbg(isp_subdev->dev, "%s isp_subdev module\n",
364e5e7a7dSBin Du 			enable ? "enable" : "disable");
374e5e7a7dSBin Du 	}
384e5e7a7dSBin Du }
394e5e7a7dSBin Du 
404e5e7a7dSBin Du static int isp4sd_setup_fw_mem_pool(struct isp4_subdev *isp_subdev)
414e5e7a7dSBin Du {
424e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
434e5e7a7dSBin Du 	struct isp4fw_cmd_send_buffer buf_type;
444e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
454e5e7a7dSBin Du 	int ret;
464e5e7a7dSBin Du 
474e5e7a7dSBin Du 	if (!ispif->fw_mem_pool) {
484e5e7a7dSBin Du 		dev_err(dev, "fail to alloc mem pool\n");
494e5e7a7dSBin Du 		return -ENOMEM;
504e5e7a7dSBin Du 	}
514e5e7a7dSBin Du 
524e5e7a7dSBin Du 	/*
534e5e7a7dSBin Du 	 * The struct will be shared with ISP FW, use memset() to guarantee
544e5e7a7dSBin Du 	 * padding bits are zeroed, since this is not guaranteed on all
554e5e7a7dSBin Du 	 * compilers.
564e5e7a7dSBin Du 	 */
574e5e7a7dSBin Du 	memset(&buf_type, 0, sizeof(buf_type));
584e5e7a7dSBin Du 	buf_type.buffer_type = ISP4FW_BUFFER_TYPE_MEM_POOL;
594e5e7a7dSBin Du 	buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
604e5e7a7dSBin Du 	isp4if_split_addr64(ispif->fw_mem_pool->gpu_mc_addr,
614e5e7a7dSBin Du 			    &buf_type.buffer.buf_base_a_lo,
624e5e7a7dSBin Du 			    &buf_type.buffer.buf_base_a_hi);
634e5e7a7dSBin Du 	buf_type.buffer.buf_size_a = ispif->fw_mem_pool->mem_size;
644e5e7a7dSBin Du 
654e5e7a7dSBin Du 	ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER,
664e5e7a7dSBin Du 				  &buf_type, sizeof(buf_type));
674e5e7a7dSBin Du 	if (ret) {
684e5e7a7dSBin Du 		dev_err(dev, "send fw mem pool 0x%llx(%u) fail %d\n",
694e5e7a7dSBin Du 			ispif->fw_mem_pool->gpu_mc_addr,
704e5e7a7dSBin Du 			buf_type.buffer.buf_size_a, ret);
714e5e7a7dSBin Du 		return ret;
724e5e7a7dSBin Du 	}
734e5e7a7dSBin Du 
744e5e7a7dSBin Du 	dev_dbg(dev, "send fw mem pool 0x%llx(%u) suc\n",
754e5e7a7dSBin Du 		ispif->fw_mem_pool->gpu_mc_addr, buf_type.buffer.buf_size_a);
764e5e7a7dSBin Du 
774e5e7a7dSBin Du 	return 0;
784e5e7a7dSBin Du }
794e5e7a7dSBin Du 
804e5e7a7dSBin Du static int isp4sd_set_stream_path(struct isp4_subdev *isp_subdev)
814e5e7a7dSBin Du {
824e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
834e5e7a7dSBin Du 	struct isp4fw_cmd_set_stream_cfg cmd;
844e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
854e5e7a7dSBin Du 
864e5e7a7dSBin Du 	/*
874e5e7a7dSBin Du 	 * The struct will be shared with ISP FW, use memset() to guarantee
884e5e7a7dSBin Du 	 * padding bits are zeroed, since this is not guaranteed on all
894e5e7a7dSBin Du 	 * compilers.
904e5e7a7dSBin Du 	 */
914e5e7a7dSBin Du 	memset(&cmd, 0, sizeof(cmd));
924e5e7a7dSBin Du 	cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id =
934e5e7a7dSBin Du 		ISP4FW_SENSOR_ID_ON_MIPI0;
944e5e7a7dSBin Du 	cmd.stream_cfg.mipi_pipe_path_cfg.b_enable = true;
954e5e7a7dSBin Du 	cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id =
964e5e7a7dSBin Du 		ISP4FW_MIPI0_ISP_PIPELINE_ID;
974e5e7a7dSBin Du 
984e5e7a7dSBin Du 	cmd.stream_cfg.b_enable_tnr = true;
994e5e7a7dSBin Du 	dev_dbg(dev, "isp4fw_sensor_id %d, pipeId 0x%x EnableTnr %u\n",
1004e5e7a7dSBin Du 		cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id,
1014e5e7a7dSBin Du 		cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id,
1024e5e7a7dSBin Du 		cmd.stream_cfg.b_enable_tnr);
1034e5e7a7dSBin Du 
1044e5e7a7dSBin Du 	return isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_STREAM_CONFIG,
1054e5e7a7dSBin Du 				   &cmd, sizeof(cmd));
1064e5e7a7dSBin Du }
1074e5e7a7dSBin Du 
1084e5e7a7dSBin Du static int isp4sd_send_meta_buf(struct isp4_subdev *isp_subdev)
1094e5e7a7dSBin Du {
1104e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
1114e5e7a7dSBin Du 	struct isp4fw_cmd_send_buffer buf_type;
1124e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
1134e5e7a7dSBin Du 
1144e5e7a7dSBin Du 	/*
1154e5e7a7dSBin Du 	 * The struct will be shared with ISP FW, use memset() to guarantee
1164e5e7a7dSBin Du 	 * padding bits are zeroed, since this is not guaranteed on all
1174e5e7a7dSBin Du 	 * compilers.
1184e5e7a7dSBin Du 	 */
1194e5e7a7dSBin Du 	memset(&buf_type, 0, sizeof(buf_type));
1204e5e7a7dSBin Du 	for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) {
1214e5e7a7dSBin Du 		struct isp4if_gpu_mem_info *meta_info_buf =
1224e5e7a7dSBin Du 				isp_subdev->ispif.meta_info_buf[i];
1234e5e7a7dSBin Du 		int ret;
1244e5e7a7dSBin Du 
1254e5e7a7dSBin Du 		if (!meta_info_buf) {
1264e5e7a7dSBin Du 			dev_err(dev, "fail for no meta info buf(%u)\n", i);
1274e5e7a7dSBin Du 			return -ENOMEM;
1284e5e7a7dSBin Du 		}
1294e5e7a7dSBin Du 
1304e5e7a7dSBin Du 		buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO;
1314e5e7a7dSBin Du 		buf_type.buffer.vmid_space.bit.space =
1324e5e7a7dSBin Du 			ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
1334e5e7a7dSBin Du 		isp4if_split_addr64(meta_info_buf->gpu_mc_addr,
1344e5e7a7dSBin Du 				    &buf_type.buffer.buf_base_a_lo,
1354e5e7a7dSBin Du 				    &buf_type.buffer.buf_base_a_hi);
1364e5e7a7dSBin Du 		buf_type.buffer.buf_size_a = meta_info_buf->mem_size;
1374e5e7a7dSBin Du 		ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER,
1384e5e7a7dSBin Du 					  &buf_type, sizeof(buf_type));
1394e5e7a7dSBin Du 		if (ret) {
1404e5e7a7dSBin Du 			dev_err(dev, "send meta info(%u) fail\n", i);
1414e5e7a7dSBin Du 			return ret;
1424e5e7a7dSBin Du 		}
1434e5e7a7dSBin Du 	}
1444e5e7a7dSBin Du 
1454e5e7a7dSBin Du 	dev_dbg(dev, "send meta info suc\n");
1464e5e7a7dSBin Du 	return 0;
1474e5e7a7dSBin Du }
1484e5e7a7dSBin Du 
1494e5e7a7dSBin Du static bool isp4sd_get_str_out_prop(struct isp4_subdev *isp_subdev,
1504e5e7a7dSBin Du 				    struct isp4fw_image_prop *out_prop,
1514e5e7a7dSBin Du 				    struct v4l2_subdev_state *state, u32 pad)
1524e5e7a7dSBin Du {
1534e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
1544e5e7a7dSBin Du 	struct v4l2_mbus_framefmt *format;
1554e5e7a7dSBin Du 
1564e5e7a7dSBin Du 	format = v4l2_subdev_state_get_format(state, pad, 0);
1574e5e7a7dSBin Du 	if (!format) {
1584e5e7a7dSBin Du 		dev_err(dev, "fail get subdev state format\n");
1594e5e7a7dSBin Du 		return false;
1604e5e7a7dSBin Du 	}
1614e5e7a7dSBin Du 
1624e5e7a7dSBin Du 	switch (format->code) {
1634e5e7a7dSBin Du 	case MEDIA_BUS_FMT_YUYV8_1_5X8:
1644e5e7a7dSBin Du 		out_prop->image_format = ISP4FW_IMAGE_FORMAT_NV12;
1654e5e7a7dSBin Du 		out_prop->width = format->width;
1664e5e7a7dSBin Du 		out_prop->height = format->height;
1674e5e7a7dSBin Du 		out_prop->luma_pitch = format->width;
1684e5e7a7dSBin Du 		out_prop->chroma_pitch = out_prop->width;
1694e5e7a7dSBin Du 		break;
1704e5e7a7dSBin Du 	case MEDIA_BUS_FMT_YUYV8_1X16:
1714e5e7a7dSBin Du 		out_prop->image_format = ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED;
1724e5e7a7dSBin Du 		out_prop->width = format->width;
1734e5e7a7dSBin Du 		out_prop->height = format->height;
1744e5e7a7dSBin Du 		out_prop->luma_pitch = format->width * 2;
1754e5e7a7dSBin Du 		out_prop->chroma_pitch = 0;
1764e5e7a7dSBin Du 		break;
1774e5e7a7dSBin Du 	default:
1784e5e7a7dSBin Du 		dev_err(dev, "fail for bad image format:0x%x\n",
1794e5e7a7dSBin Du 			format->code);
1804e5e7a7dSBin Du 		return false;
1814e5e7a7dSBin Du 	}
1824e5e7a7dSBin Du 
1834e5e7a7dSBin Du 	if (!out_prop->width || !out_prop->height)
1844e5e7a7dSBin Du 		return false;
1854e5e7a7dSBin Du 
1864e5e7a7dSBin Du 	return true;
1874e5e7a7dSBin Du }
1884e5e7a7dSBin Du 
1894e5e7a7dSBin Du static int isp4sd_kickoff_stream(struct isp4_subdev *isp_subdev, u32 w, u32 h)
1904e5e7a7dSBin Du {
1914e5e7a7dSBin Du 	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
1924e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
1934e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
1944e5e7a7dSBin Du 
1954e5e7a7dSBin Du 	if (sensor_info->status == ISP4SD_START_STATUS_STARTED)
1964e5e7a7dSBin Du 		return 0;
1974e5e7a7dSBin Du 
1984e5e7a7dSBin Du 	if (sensor_info->status == ISP4SD_START_STATUS_START_FAIL) {
1994e5e7a7dSBin Du 		dev_err(dev, "fail for previous start fail\n");
2004e5e7a7dSBin Du 		return -EINVAL;
2014e5e7a7dSBin Du 	}
2024e5e7a7dSBin Du 
2034e5e7a7dSBin Du 	dev_dbg(dev, "w:%u,h:%u\n", w, h);
2044e5e7a7dSBin Du 
2054e5e7a7dSBin Du 	if (isp4sd_send_meta_buf(isp_subdev)) {
2064e5e7a7dSBin Du 		dev_err(dev, "fail to send meta buf\n");
2074e5e7a7dSBin Du 		sensor_info->status = ISP4SD_START_STATUS_START_FAIL;
2084e5e7a7dSBin Du 		return -EINVAL;
2094e5e7a7dSBin Du 	}
2104e5e7a7dSBin Du 
2114e5e7a7dSBin Du 	sensor_info->status = ISP4SD_START_STATUS_OFF;
2124e5e7a7dSBin Du 
2134e5e7a7dSBin Du 	if (!sensor_info->start_stream_cmd_sent &&
2144e5e7a7dSBin Du 	    sensor_info->buf_sent_cnt >= ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) {
2154e5e7a7dSBin Du 		int ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_START_STREAM,
2164e5e7a7dSBin Du 					      NULL, 0);
2174e5e7a7dSBin Du 		if (ret) {
2184e5e7a7dSBin Du 			dev_err(dev, "fail to start stream\n");
2194e5e7a7dSBin Du 			return ret;
2204e5e7a7dSBin Du 		}
2214e5e7a7dSBin Du 
2224e5e7a7dSBin Du 		sensor_info->start_stream_cmd_sent = true;
2234e5e7a7dSBin Du 	} else {
2244e5e7a7dSBin Du 		dev_dbg(dev,
2254e5e7a7dSBin Du 			"no send START_STREAM, start_sent %u, buf_sent %u\n",
2264e5e7a7dSBin Du 			sensor_info->start_stream_cmd_sent,
2274e5e7a7dSBin Du 			sensor_info->buf_sent_cnt);
2284e5e7a7dSBin Du 	}
2294e5e7a7dSBin Du 
2304e5e7a7dSBin Du 	return 0;
2314e5e7a7dSBin Du }
2324e5e7a7dSBin Du 
2334e5e7a7dSBin Du static int isp4sd_setup_output(struct isp4_subdev *isp_subdev,
2344e5e7a7dSBin Du 			       struct v4l2_subdev_state *state, u32 pad)
2354e5e7a7dSBin Du {
2364e5e7a7dSBin Du 	struct isp4sd_output_info *output_info =
2374e5e7a7dSBin Du 			&isp_subdev->sensor_info.output_info;
2384e5e7a7dSBin Du 	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
2394e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
2404e5e7a7dSBin Du 	struct isp4fw_cmd_set_out_ch_prop cmd_ch_prop;
2414e5e7a7dSBin Du 	struct isp4fw_cmd_enable_out_ch cmd_ch_en;
2424e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
2434e5e7a7dSBin Du 	int ret;
2444e5e7a7dSBin Du 
2454e5e7a7dSBin Du 	if (output_info->start_status == ISP4SD_START_STATUS_STARTED)
2464e5e7a7dSBin Du 		return 0;
2474e5e7a7dSBin Du 
2484e5e7a7dSBin Du 	if (output_info->start_status == ISP4SD_START_STATUS_START_FAIL) {
2494e5e7a7dSBin Du 		dev_err(dev, "fail for previous start fail\n");
2504e5e7a7dSBin Du 		return -EINVAL;
2514e5e7a7dSBin Du 	}
2524e5e7a7dSBin Du 
2534e5e7a7dSBin Du 	/*
2544e5e7a7dSBin Du 	 * The struct will be shared with ISP FW, use memset() to guarantee
2554e5e7a7dSBin Du 	 * padding bits are zeroed, since this is not guaranteed on all
2564e5e7a7dSBin Du 	 * compilers.
2574e5e7a7dSBin Du 	 */
2584e5e7a7dSBin Du 	memset(&cmd_ch_prop, 0, sizeof(cmd_ch_prop));
2594e5e7a7dSBin Du 	cmd_ch_prop.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW;
2604e5e7a7dSBin Du 
2614e5e7a7dSBin Du 	if (!isp4sd_get_str_out_prop(isp_subdev,
2624e5e7a7dSBin Du 				     &cmd_ch_prop.image_prop, state, pad)) {
2634e5e7a7dSBin Du 		dev_err(dev, "fail to get out prop\n");
2644e5e7a7dSBin Du 		return -EINVAL;
2654e5e7a7dSBin Du 	}
2664e5e7a7dSBin Du 
267*ec4bec22SBin Du 	dev_dbg(dev, "channel:%s,fmt %s,w:h=%u:%u,lp:%u,cp%u\n",
268*ec4bec22SBin Du 		isp4dbg_get_out_ch_str(cmd_ch_prop.ch),
269*ec4bec22SBin Du 		isp4dbg_get_img_fmt_str(cmd_ch_prop.image_prop.image_format),
2704e5e7a7dSBin Du 		cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height,
2714e5e7a7dSBin Du 		cmd_ch_prop.image_prop.luma_pitch,
2724e5e7a7dSBin Du 		cmd_ch_prop.image_prop.chroma_pitch);
2734e5e7a7dSBin Du 
2744e5e7a7dSBin Du 	ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_OUT_CHAN_PROP,
2754e5e7a7dSBin Du 				  &cmd_ch_prop, sizeof(cmd_ch_prop));
2764e5e7a7dSBin Du 	if (ret) {
2774e5e7a7dSBin Du 		output_info->start_status = ISP4SD_START_STATUS_START_FAIL;
2784e5e7a7dSBin Du 		dev_err(dev, "fail to set out prop\n");
2794e5e7a7dSBin Du 		return ret;
2804e5e7a7dSBin Du 	}
2814e5e7a7dSBin Du 
2824e5e7a7dSBin Du 	/*
2834e5e7a7dSBin Du 	 * The struct will be shared with ISP FW, use memset() to guarantee
2844e5e7a7dSBin Du 	 * padding bits are zeroed, since this is not guaranteed on all
2854e5e7a7dSBin Du 	 * compilers.
2864e5e7a7dSBin Du 	 */
2874e5e7a7dSBin Du 	memset(&cmd_ch_en, 0, sizeof(cmd_ch_en));
2884e5e7a7dSBin Du 	cmd_ch_en.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW;
2894e5e7a7dSBin Du 	cmd_ch_en.is_enable = true;
2904e5e7a7dSBin Du 	ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_ENABLE_OUT_CHAN,
2914e5e7a7dSBin Du 				  &cmd_ch_en, sizeof(cmd_ch_en));
2924e5e7a7dSBin Du 	if (ret) {
2934e5e7a7dSBin Du 		output_info->start_status = ISP4SD_START_STATUS_START_FAIL;
2944e5e7a7dSBin Du 		dev_err(dev, "fail to enable channel\n");
2954e5e7a7dSBin Du 		return ret;
2964e5e7a7dSBin Du 	}
2974e5e7a7dSBin Du 
298*ec4bec22SBin Du 	dev_dbg(dev, "enable channel %s\n",
299*ec4bec22SBin Du 		isp4dbg_get_out_ch_str(cmd_ch_en.ch));
3004e5e7a7dSBin Du 
3014e5e7a7dSBin Du 	if (!sensor_info->start_stream_cmd_sent) {
3024e5e7a7dSBin Du 		ret = isp4sd_kickoff_stream(isp_subdev,
3034e5e7a7dSBin Du 					    cmd_ch_prop.image_prop.width,
3044e5e7a7dSBin Du 					    cmd_ch_prop.image_prop.height);
3054e5e7a7dSBin Du 		if (ret) {
3064e5e7a7dSBin Du 			dev_err(dev, "kickoff stream fail %d\n", ret);
3074e5e7a7dSBin Du 			return ret;
3084e5e7a7dSBin Du 		}
3094e5e7a7dSBin Du 		/*
3104e5e7a7dSBin Du 		 * sensor_info->start_stream_cmd_sent will be set to true
3114e5e7a7dSBin Du 		 * 1. in isp4sd_kickoff_stream, if app first send buffer then
3124e5e7a7dSBin Du 		 * start stream
3134e5e7a7dSBin Du 		 * 2. in isp_set_stream_buf, if app first start stream, then
3144e5e7a7dSBin Du 		 * send buffer because ISP FW has the requirement, host needs
3154e5e7a7dSBin Du 		 * to send buffer before send start stream cmd
3164e5e7a7dSBin Du 		 */
3174e5e7a7dSBin Du 		if (sensor_info->start_stream_cmd_sent) {
3184e5e7a7dSBin Du 			sensor_info->status = ISP4SD_START_STATUS_STARTED;
3194e5e7a7dSBin Du 			output_info->start_status = ISP4SD_START_STATUS_STARTED;
3204e5e7a7dSBin Du 			dev_dbg(dev, "kickoff stream suc,start cmd sent\n");
3214e5e7a7dSBin Du 		}
3224e5e7a7dSBin Du 	} else {
3234e5e7a7dSBin Du 		dev_dbg(dev, "stream running, no need kickoff\n");
3244e5e7a7dSBin Du 		output_info->start_status = ISP4SD_START_STATUS_STARTED;
3254e5e7a7dSBin Du 	}
3264e5e7a7dSBin Du 
3274e5e7a7dSBin Du 	dev_dbg(dev, "setup output suc\n");
3284e5e7a7dSBin Du 	return 0;
3294e5e7a7dSBin Du }
3304e5e7a7dSBin Du 
3314e5e7a7dSBin Du static int isp4sd_init_stream(struct isp4_subdev *isp_subdev)
3324e5e7a7dSBin Du {
3334e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
3344e5e7a7dSBin Du 	int ret;
3354e5e7a7dSBin Du 
3364e5e7a7dSBin Du 	ret = isp4sd_setup_fw_mem_pool(isp_subdev);
3374e5e7a7dSBin Du 	if (ret) {
3384e5e7a7dSBin Du 		dev_err(dev, "fail to setup fw mem pool\n");
3394e5e7a7dSBin Du 		return ret;
3404e5e7a7dSBin Du 	}
3414e5e7a7dSBin Du 
3424e5e7a7dSBin Du 	ret = isp4sd_set_stream_path(isp_subdev);
3434e5e7a7dSBin Du 	if (ret) {
3444e5e7a7dSBin Du 		dev_err(dev, "fail to setup stream path\n");
3454e5e7a7dSBin Du 		return ret;
3464e5e7a7dSBin Du 	}
3474e5e7a7dSBin Du 
3484e5e7a7dSBin Du 	return 0;
3494e5e7a7dSBin Du }
3504e5e7a7dSBin Du 
3514e5e7a7dSBin Du static void isp4sd_uninit_stream(struct isp4_subdev *isp_subdev,
3524e5e7a7dSBin Du 				 struct v4l2_subdev_state *state, u32 pad)
3534e5e7a7dSBin Du {
3544e5e7a7dSBin Du 	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
3554e5e7a7dSBin Du 	struct isp4sd_output_info *output_info = &sensor_info->output_info;
3564e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
3574e5e7a7dSBin Du 	struct v4l2_mbus_framefmt *format;
3584e5e7a7dSBin Du 
3594e5e7a7dSBin Du 	format = v4l2_subdev_state_get_format(state, pad, 0);
3604e5e7a7dSBin Du 	if (!format) {
3614e5e7a7dSBin Du 		dev_err(isp_subdev->dev, "fail to get v4l2 format\n");
3624e5e7a7dSBin Du 	} else {
3634e5e7a7dSBin Du 		memset(format, 0, sizeof(*format));
3644e5e7a7dSBin Du 		format->code = MEDIA_BUS_FMT_YUYV8_1_5X8;
3654e5e7a7dSBin Du 	}
3664e5e7a7dSBin Du 
3674e5e7a7dSBin Du 	isp4if_clear_bufq(ispif);
3684e5e7a7dSBin Du 	isp4if_clear_cmdq(ispif);
3694e5e7a7dSBin Du 
3704e5e7a7dSBin Du 	sensor_info->start_stream_cmd_sent = false;
3714e5e7a7dSBin Du 	sensor_info->buf_sent_cnt = 0;
3724e5e7a7dSBin Du 
3734e5e7a7dSBin Du 	sensor_info->status = ISP4SD_START_STATUS_OFF;
3744e5e7a7dSBin Du 	output_info->start_status = ISP4SD_START_STATUS_OFF;
3754e5e7a7dSBin Du }
3764e5e7a7dSBin Du 
3774e5e7a7dSBin Du static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev,
3784e5e7a7dSBin Du 				    enum isp4if_stream_id stream_id,
3794e5e7a7dSBin Du 				    struct isp4fw_resp_cmd_done *para)
3804e5e7a7dSBin Du {
3814e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
3824e5e7a7dSBin Du 	struct isp4if_cmd_element *ele =
3834e5e7a7dSBin Du 		isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id);
3844e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
3854e5e7a7dSBin Du 
386*ec4bec22SBin Du 	dev_dbg(dev, "stream %d,cmd %s(0x%08x)(%d),seq %u, ele %p\n",
3874e5e7a7dSBin Du 		stream_id,
388*ec4bec22SBin Du 		isp4dbg_get_cmd_str(para->cmd_id),
3894e5e7a7dSBin Du 		para->cmd_id, para->cmd_status, para->cmd_seq_num,
3904e5e7a7dSBin Du 		ele);
3914e5e7a7dSBin Du 
3924e5e7a7dSBin Du 	if (ele) {
3934e5e7a7dSBin Du 		complete(&ele->cmd_done);
3944e5e7a7dSBin Du 		if (atomic_dec_and_test(&ele->refcnt))
3954e5e7a7dSBin Du 			kfree(ele);
3964e5e7a7dSBin Du 	}
3974e5e7a7dSBin Du }
3984e5e7a7dSBin Du 
3994e5e7a7dSBin Du static struct isp4fw_meta_info *
4004e5e7a7dSBin Du isp4sd_get_meta_by_mc(struct isp4_subdev *isp_subdev, u64 mc)
4014e5e7a7dSBin Du {
4024e5e7a7dSBin Du 	for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) {
4034e5e7a7dSBin Du 		struct isp4if_gpu_mem_info *meta_info_buf =
4044e5e7a7dSBin Du 				isp_subdev->ispif.meta_info_buf[i];
4054e5e7a7dSBin Du 
4064e5e7a7dSBin Du 		if (meta_info_buf->gpu_mc_addr == mc)
4074e5e7a7dSBin Du 			return meta_info_buf->sys_addr;
4084e5e7a7dSBin Du 	}
4094e5e7a7dSBin Du 
4104e5e7a7dSBin Du 	return NULL;
4114e5e7a7dSBin Du }
4124e5e7a7dSBin Du 
4134e5e7a7dSBin Du static void isp4sd_send_meta_info(struct isp4_subdev *isp_subdev,
4144e5e7a7dSBin Du 				  u64 meta_info_mc)
4154e5e7a7dSBin Du {
4164e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
4174e5e7a7dSBin Du 	struct isp4fw_cmd_send_buffer buf_type;
4184e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
4194e5e7a7dSBin Du 
4204e5e7a7dSBin Du 	if (isp_subdev->sensor_info.status != ISP4SD_START_STATUS_STARTED) {
4214e5e7a7dSBin Du 		dev_warn(dev, "not working status %i, meta_info 0x%llx\n",
4224e5e7a7dSBin Du 			 isp_subdev->sensor_info.status, meta_info_mc);
4234e5e7a7dSBin Du 		return;
4244e5e7a7dSBin Du 	}
4254e5e7a7dSBin Du 
4264e5e7a7dSBin Du 	/*
4274e5e7a7dSBin Du 	 * The struct will be shared with ISP FW, use memset() to guarantee
4284e5e7a7dSBin Du 	 * padding bits are zeroed, since this is not guaranteed on all
4294e5e7a7dSBin Du 	 * compilers.
4304e5e7a7dSBin Du 	 */
4314e5e7a7dSBin Du 	memset(&buf_type, 0, sizeof(buf_type));
4324e5e7a7dSBin Du 	buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO;
4334e5e7a7dSBin Du 	buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
4344e5e7a7dSBin Du 	isp4if_split_addr64(meta_info_mc,
4354e5e7a7dSBin Du 			    &buf_type.buffer.buf_base_a_lo,
4364e5e7a7dSBin Du 			    &buf_type.buffer.buf_base_a_hi);
4374e5e7a7dSBin Du 	buf_type.buffer.buf_size_a = ISP4SD_META_BUF_SIZE;
4384e5e7a7dSBin Du 
4394e5e7a7dSBin Du 	if (isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER,
4404e5e7a7dSBin Du 				&buf_type, sizeof(buf_type)))
4414e5e7a7dSBin Du 		dev_err(dev, "fail send meta_info 0x%llx\n",
4424e5e7a7dSBin Du 			meta_info_mc);
4434e5e7a7dSBin Du 	else
4444e5e7a7dSBin Du 		dev_dbg(dev, "resend meta_info 0x%llx\n", meta_info_mc);
4454e5e7a7dSBin Du }
4464e5e7a7dSBin Du 
4474e5e7a7dSBin Du static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev,
4484e5e7a7dSBin Du 				      enum isp4if_stream_id stream_id,
4494e5e7a7dSBin Du 				      struct isp4fw_resp_param_package *para)
4504e5e7a7dSBin Du {
4514e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
4524e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
4534e5e7a7dSBin Du 	struct isp4if_img_buf_node *prev;
4544e5e7a7dSBin Du 	struct isp4fw_meta_info *meta;
4554e5e7a7dSBin Du 	u64 mc;
4564e5e7a7dSBin Du 
4574e5e7a7dSBin Du 	mc = isp4if_join_addr64(para->package_addr_lo, para->package_addr_hi);
4584e5e7a7dSBin Du 	meta = isp4sd_get_meta_by_mc(isp_subdev, mc);
4594e5e7a7dSBin Du 	if (!meta) {
4604e5e7a7dSBin Du 		dev_err(dev, "fail to get meta from mc %llx\n", mc);
4614e5e7a7dSBin Du 		return;
4624e5e7a7dSBin Du 	}
4634e5e7a7dSBin Du 
464*ec4bec22SBin Du 	dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%s(%i)\n",
4654e5e7a7dSBin Du 		ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled,
466*ec4bec22SBin Du 		isp4dbg_get_buf_done_str(meta->preview.status),
4674e5e7a7dSBin Du 		meta->preview.status);
4684e5e7a7dSBin Du 
4694e5e7a7dSBin Du 	if (meta->preview.enabled &&
4704e5e7a7dSBin Du 	    (meta->preview.status == ISP4FW_BUFFER_STATUS_SKIPPED ||
4714e5e7a7dSBin Du 	     meta->preview.status == ISP4FW_BUFFER_STATUS_DONE ||
4724e5e7a7dSBin Du 	     meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) {
4734e5e7a7dSBin Du 		prev = isp4if_dequeue_buffer(ispif);
4742ccf48afSBin Du 		if (prev) {
475*ec4bec22SBin Du 			isp4dbg_show_bufmeta_info(dev, "prev", &meta->preview,
476*ec4bec22SBin Du 						  &prev->buf_info);
4772ccf48afSBin Du 			isp4vid_handle_frame_done(&isp_subdev->isp_vdev,
4782ccf48afSBin Du 						  &prev->buf_info);
4794e5e7a7dSBin Du 			isp4if_dealloc_buffer_node(prev);
4802ccf48afSBin Du 		} else {
4814e5e7a7dSBin Du 			dev_err(dev, "fail null prev buf\n");
4822ccf48afSBin Du 		}
4834e5e7a7dSBin Du 	} else if (meta->preview.enabled) {
484*ec4bec22SBin Du 		dev_err(dev, "fail bad preview status %u(%s)\n",
485*ec4bec22SBin Du 			meta->preview.status,
486*ec4bec22SBin Du 			isp4dbg_get_buf_done_str(meta->preview.status));
4874e5e7a7dSBin Du 	}
4884e5e7a7dSBin Du 
4894e5e7a7dSBin Du 	if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED)
4904e5e7a7dSBin Du 		isp4sd_send_meta_info(isp_subdev, mc);
4914e5e7a7dSBin Du 
4924e5e7a7dSBin Du 	dev_dbg(dev, "stream_id:%d, status:%d\n", stream_id,
4934e5e7a7dSBin Du 		isp_subdev->sensor_info.status);
4944e5e7a7dSBin Du }
4954e5e7a7dSBin Du 
4964e5e7a7dSBin Du static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev,
4974e5e7a7dSBin Du 				enum isp4if_stream_id stream_id)
4984e5e7a7dSBin Du {
4994e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
5004e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
5014e5e7a7dSBin Du 	struct isp4fw_resp resp;
5024e5e7a7dSBin Du 
503*ec4bec22SBin Du 	if (stream_id == ISP4IF_STREAM_ID_1)
504*ec4bec22SBin Du 		isp_fw_log_print(isp_subdev);
505*ec4bec22SBin Du 
5064e5e7a7dSBin Du 	while (true) {
5074e5e7a7dSBin Du 		if (isp4if_f2h_resp(ispif, stream_id, &resp)) {
5084e5e7a7dSBin Du 			/* Re-enable the interrupt */
5094e5e7a7dSBin Du 			isp4_intr_enable(isp_subdev, stream_id, true);
5104e5e7a7dSBin Du 			/*
5114e5e7a7dSBin Du 			 * Recheck to see if there is a new response.
5124e5e7a7dSBin Du 			 * To ensure that an in-flight interrupt is not lost,
5134e5e7a7dSBin Du 			 * enabling the interrupt must occur _before_ checking
5144e5e7a7dSBin Du 			 * for a new response, hence a memory barrier is needed.
5154e5e7a7dSBin Du 			 * Disable the interrupt again if there was a new
5164e5e7a7dSBin Du 			 * response.
5174e5e7a7dSBin Du 			 */
5184e5e7a7dSBin Du 			mb();
5194e5e7a7dSBin Du 			if (likely(isp4if_f2h_resp(ispif, stream_id, &resp)))
5204e5e7a7dSBin Du 				break;
5214e5e7a7dSBin Du 
5224e5e7a7dSBin Du 			isp4_intr_enable(isp_subdev, stream_id, false);
5234e5e7a7dSBin Du 		}
5244e5e7a7dSBin Du 
5254e5e7a7dSBin Du 		switch (resp.resp_id) {
5264e5e7a7dSBin Du 		case ISP4FW_RESP_ID_CMD_DONE:
5274e5e7a7dSBin Du 			isp4sd_fw_resp_cmd_done(isp_subdev, stream_id,
5284e5e7a7dSBin Du 						&resp.param.cmd_done);
5294e5e7a7dSBin Du 			break;
5304e5e7a7dSBin Du 		case ISP4FW_RESP_ID_NOTI_FRAME_DONE:
5314e5e7a7dSBin Du 			isp4sd_fw_resp_frame_done(isp_subdev, stream_id,
5324e5e7a7dSBin Du 						  &resp.param.frame_done);
5334e5e7a7dSBin Du 			break;
5344e5e7a7dSBin Du 		default:
535*ec4bec22SBin Du 			dev_err(dev, "-><- fail respid %s(0x%x)\n",
536*ec4bec22SBin Du 				isp4dbg_get_resp_str(resp.resp_id),
5374e5e7a7dSBin Du 				resp.resp_id);
5384e5e7a7dSBin Du 			break;
5394e5e7a7dSBin Du 		}
5404e5e7a7dSBin Du 	}
5414e5e7a7dSBin Du }
5424e5e7a7dSBin Du 
5434e5e7a7dSBin Du static s32 isp4sd_fw_resp_thread(void *context)
5444e5e7a7dSBin Du {
5454e5e7a7dSBin Du 	struct isp4_subdev_thread_param *para = context;
5464e5e7a7dSBin Du 	struct isp4_subdev *isp_subdev = para->isp_subdev;
5474e5e7a7dSBin Du 	struct isp4sd_thread_handler *thread_ctx =
5484e5e7a7dSBin Du 			&isp_subdev->fw_resp_thread[para->idx];
5494e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
5504e5e7a7dSBin Du 
5514e5e7a7dSBin Du 	dev_dbg(dev, "[%u] fw resp thread started\n", para->idx);
5524e5e7a7dSBin Du 	while (true) {
5534e5e7a7dSBin Du 		wait_event_interruptible(thread_ctx->waitq,
5544e5e7a7dSBin Du 					 thread_ctx->resp_ready);
5554e5e7a7dSBin Du 		thread_ctx->resp_ready = false;
5564e5e7a7dSBin Du 
5574e5e7a7dSBin Du 		if (kthread_should_stop()) {
5584e5e7a7dSBin Du 			dev_dbg(dev, "[%u] fw resp thread quit\n", para->idx);
5594e5e7a7dSBin Du 			break;
5604e5e7a7dSBin Du 		}
5614e5e7a7dSBin Du 
5624e5e7a7dSBin Du 		isp4sd_fw_resp_func(isp_subdev, para->idx);
5634e5e7a7dSBin Du 	}
5644e5e7a7dSBin Du 
5654e5e7a7dSBin Du 	return 0;
5664e5e7a7dSBin Du }
5674e5e7a7dSBin Du 
5684e5e7a7dSBin Du static int isp4sd_stop_resp_proc_threads(struct isp4_subdev *isp_subdev)
5694e5e7a7dSBin Du {
5704e5e7a7dSBin Du 	for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) {
5714e5e7a7dSBin Du 		struct isp4sd_thread_handler *thread_ctx =
5724e5e7a7dSBin Du 				&isp_subdev->fw_resp_thread[i];
5734e5e7a7dSBin Du 
5744e5e7a7dSBin Du 		if (thread_ctx->thread) {
5754e5e7a7dSBin Du 			kthread_stop(thread_ctx->thread);
5764e5e7a7dSBin Du 			thread_ctx->thread = NULL;
5774e5e7a7dSBin Du 		}
5784e5e7a7dSBin Du 	}
5794e5e7a7dSBin Du 
5804e5e7a7dSBin Du 	return 0;
5814e5e7a7dSBin Du }
5824e5e7a7dSBin Du 
5834e5e7a7dSBin Du static int isp4sd_start_resp_proc_threads(struct isp4_subdev *isp_subdev)
5844e5e7a7dSBin Du {
5854e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
5864e5e7a7dSBin Du 
5874e5e7a7dSBin Du 	for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) {
5884e5e7a7dSBin Du 		struct isp4sd_thread_handler *thread_ctx =
5894e5e7a7dSBin Du 				&isp_subdev->fw_resp_thread[i];
5904e5e7a7dSBin Du 
5914e5e7a7dSBin Du 		isp_subdev->isp_resp_para[i].idx = i;
5924e5e7a7dSBin Du 		isp_subdev->isp_resp_para[i].isp_subdev = isp_subdev;
5934e5e7a7dSBin Du 		init_waitqueue_head(&thread_ctx->waitq);
5944e5e7a7dSBin Du 		thread_ctx->resp_ready = false;
5954e5e7a7dSBin Du 
5964e5e7a7dSBin Du 		thread_ctx->thread = kthread_run(isp4sd_fw_resp_thread,
5974e5e7a7dSBin Du 						 &isp_subdev->isp_resp_para[i],
5984e5e7a7dSBin Du 						 isp4sd_thread_name[i]);
5994e5e7a7dSBin Du 		if (IS_ERR(thread_ctx->thread)) {
6004e5e7a7dSBin Du 			dev_err(dev, "create thread [%d] fail\n", i);
6014e5e7a7dSBin Du 			thread_ctx->thread = NULL;
6024e5e7a7dSBin Du 			isp4sd_stop_resp_proc_threads(isp_subdev);
6034e5e7a7dSBin Du 			return -EINVAL;
6044e5e7a7dSBin Du 		}
6054e5e7a7dSBin Du 	}
6064e5e7a7dSBin Du 
6074e5e7a7dSBin Du 	return 0;
6084e5e7a7dSBin Du }
6094e5e7a7dSBin Du 
6104e5e7a7dSBin Du int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd)
6114e5e7a7dSBin Du {
6124e5e7a7dSBin Du 	struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
6134e5e7a7dSBin Du 	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
6144e5e7a7dSBin Du 	unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_LOW;
6154e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
6164e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
6174e5e7a7dSBin Du 	int ret;
6184e5e7a7dSBin Du 
6194e5e7a7dSBin Du 	guard(mutex)(&isp_subdev->ops_mutex);
6204e5e7a7dSBin Du 	if (sensor_info->status == ISP4SD_START_STATUS_STARTED) {
6214e5e7a7dSBin Du 		dev_err(dev, "fail for stream still running\n");
6224e5e7a7dSBin Du 		return -EINVAL;
6234e5e7a7dSBin Du 	}
6244e5e7a7dSBin Du 
6254e5e7a7dSBin Du 	sensor_info->status = ISP4SD_START_STATUS_OFF;
6264e5e7a7dSBin Du 
6274e5e7a7dSBin Du 	if (isp_subdev->irq_enabled) {
6284e5e7a7dSBin Du 		for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++)
6294e5e7a7dSBin Du 			disable_irq(isp_subdev->irq[i]);
6304e5e7a7dSBin Du 		isp_subdev->irq_enabled = false;
6314e5e7a7dSBin Du 	}
6324e5e7a7dSBin Du 
6334e5e7a7dSBin Du 	isp4sd_stop_resp_proc_threads(isp_subdev);
6344e5e7a7dSBin Du 	dev_dbg(dev, "isp_subdev stop resp proc threads suc\n");
6354e5e7a7dSBin Du 
6364e5e7a7dSBin Du 	isp4if_stop(ispif);
6374e5e7a7dSBin Du 
6384e5e7a7dSBin Du 	ret = dev_pm_genpd_set_performance_state(dev, perf_state);
6394e5e7a7dSBin Du 	if (ret)
6404e5e7a7dSBin Du 		dev_err(dev,
6414e5e7a7dSBin Du 			"fail to set isp_subdev performance state %u,ret %d\n",
6424e5e7a7dSBin Du 			perf_state, ret);
6434e5e7a7dSBin Du 
6444e5e7a7dSBin Du 	/* hold ccpu reset */
6454e5e7a7dSBin Du 	isp4hw_wreg(isp_subdev->mmio, ISP_SOFT_RESET, 0);
6464e5e7a7dSBin Du 	isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0);
6474e5e7a7dSBin Du 	ret = pm_runtime_put_sync(dev);
6484e5e7a7dSBin Du 	if (ret)
6494e5e7a7dSBin Du 		dev_err(dev, "power off isp_subdev fail %d\n", ret);
6504e5e7a7dSBin Du 	else
6514e5e7a7dSBin Du 		dev_dbg(dev, "power off isp_subdev suc\n");
6524e5e7a7dSBin Du 
6534e5e7a7dSBin Du 	ispif->status = ISP4IF_STATUS_PWR_OFF;
6544e5e7a7dSBin Du 	isp4if_clear_cmdq(ispif);
6554e5e7a7dSBin Du 	isp4sd_module_enable(isp_subdev, false);
6564e5e7a7dSBin Du 
6574e5e7a7dSBin Du 	/*
6584e5e7a7dSBin Du 	 * When opening the camera, isp4sd_module_enable(isp_subdev, true) is
6594e5e7a7dSBin Du 	 * called. Hardware requires at least a 20ms delay between disabling
6604e5e7a7dSBin Du 	 * and enabling the module, so a sleep is added to ensure ISP stability
6614e5e7a7dSBin Du 	 * during quick reopen scenarios.
6624e5e7a7dSBin Du 	 */
6634e5e7a7dSBin Du 	msleep(20);
6644e5e7a7dSBin Du 
6654e5e7a7dSBin Du 	return 0;
6664e5e7a7dSBin Du }
6674e5e7a7dSBin Du 
6684e5e7a7dSBin Du int isp4sd_pwron_and_init(struct v4l2_subdev *sd)
6694e5e7a7dSBin Du {
6704e5e7a7dSBin Du 	struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
6714e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
6724e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
6734e5e7a7dSBin Du 	int ret;
6744e5e7a7dSBin Du 
6754e5e7a7dSBin Du 	guard(mutex)(&isp_subdev->ops_mutex);
6764e5e7a7dSBin Du 	if (ispif->status == ISP4IF_STATUS_FW_RUNNING) {
6774e5e7a7dSBin Du 		dev_dbg(dev, "camera already opened, do nothing\n");
6784e5e7a7dSBin Du 		return 0;
6794e5e7a7dSBin Du 	}
6804e5e7a7dSBin Du 
6814e5e7a7dSBin Du 	isp4sd_module_enable(isp_subdev, true);
6824e5e7a7dSBin Du 
6834e5e7a7dSBin Du 	if (ispif->status < ISP4IF_STATUS_PWR_ON) {
6844e5e7a7dSBin Du 		unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_HIGH;
6854e5e7a7dSBin Du 
6864e5e7a7dSBin Du 		ret = pm_runtime_resume_and_get(dev);
6874e5e7a7dSBin Du 		if (ret) {
6884e5e7a7dSBin Du 			dev_err(dev, "fail to power on isp_subdev ret %d\n",
6894e5e7a7dSBin Du 				ret);
6904e5e7a7dSBin Du 			goto err_deinit;
6914e5e7a7dSBin Du 		}
6924e5e7a7dSBin Du 
6934e5e7a7dSBin Du 		/* ISPPG ISP Power Status */
6944e5e7a7dSBin Du 		isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0x7FF);
6954e5e7a7dSBin Du 		ret = dev_pm_genpd_set_performance_state(dev, perf_state);
6964e5e7a7dSBin Du 		if (ret) {
6974e5e7a7dSBin Du 			dev_err(dev,
6984e5e7a7dSBin Du 				"fail to set performance state %u, ret %d\n",
6994e5e7a7dSBin Du 				perf_state, ret);
7004e5e7a7dSBin Du 			goto err_deinit;
7014e5e7a7dSBin Du 		}
7024e5e7a7dSBin Du 
7034e5e7a7dSBin Du 		ispif->status = ISP4IF_STATUS_PWR_ON;
7044e5e7a7dSBin Du 	}
7054e5e7a7dSBin Du 
7064e5e7a7dSBin Du 	isp_subdev->sensor_info.start_stream_cmd_sent = false;
7074e5e7a7dSBin Du 	isp_subdev->sensor_info.buf_sent_cnt = 0;
7084e5e7a7dSBin Du 
7094e5e7a7dSBin Du 	ret = isp4if_start(ispif);
7104e5e7a7dSBin Du 	if (ret) {
7114e5e7a7dSBin Du 		dev_err(dev, "fail to start isp_subdev interface\n");
7124e5e7a7dSBin Du 		goto err_deinit;
7134e5e7a7dSBin Du 	}
7144e5e7a7dSBin Du 
7154e5e7a7dSBin Du 	if (isp4sd_start_resp_proc_threads(isp_subdev)) {
7164e5e7a7dSBin Du 		dev_err(dev, "isp_start_resp_proc_threads fail\n");
7174e5e7a7dSBin Du 		goto err_deinit;
7184e5e7a7dSBin Du 	}
7194e5e7a7dSBin Du 
7204e5e7a7dSBin Du 	dev_dbg(dev, "create resp threads ok\n");
7214e5e7a7dSBin Du 
7224e5e7a7dSBin Du 	for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++)
7234e5e7a7dSBin Du 		enable_irq(isp_subdev->irq[i]);
7244e5e7a7dSBin Du 	isp_subdev->irq_enabled = true;
7254e5e7a7dSBin Du 
7264e5e7a7dSBin Du 	return 0;
7274e5e7a7dSBin Du err_deinit:
7284e5e7a7dSBin Du 	isp4sd_pwroff_and_deinit(sd);
7294e5e7a7dSBin Du 	return -EINVAL;
7304e5e7a7dSBin Du }
7314e5e7a7dSBin Du 
7324e5e7a7dSBin Du static int isp4sd_stop_stream(struct isp4_subdev *isp_subdev,
7334e5e7a7dSBin Du 			      struct v4l2_subdev_state *state, u32 pad)
7344e5e7a7dSBin Du {
7354e5e7a7dSBin Du 	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
7364e5e7a7dSBin Du 	struct isp4sd_output_info *output_info = &sensor_info->output_info;
7374e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
7384e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
7394e5e7a7dSBin Du 
7404e5e7a7dSBin Du 	guard(mutex)(&isp_subdev->ops_mutex);
7414e5e7a7dSBin Du 	dev_dbg(dev, "status %i\n", output_info->start_status);
7424e5e7a7dSBin Du 
7434e5e7a7dSBin Du 	if (output_info->start_status == ISP4SD_START_STATUS_STARTED) {
7444e5e7a7dSBin Du 		struct isp4fw_cmd_enable_out_ch cmd_ch_disable;
7454e5e7a7dSBin Du 		int ret;
7464e5e7a7dSBin Du 
7474e5e7a7dSBin Du 		/*
7484e5e7a7dSBin Du 		 * The struct will be shared with ISP FW, use memset() to
7494e5e7a7dSBin Du 		 * guarantee padding bits are zeroed, since this is not
7504e5e7a7dSBin Du 		 * guaranteed on all compilers.
7514e5e7a7dSBin Du 		 */
7524e5e7a7dSBin Du 		memset(&cmd_ch_disable, 0, sizeof(cmd_ch_disable));
7534e5e7a7dSBin Du 		cmd_ch_disable.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW;
7544e5e7a7dSBin Du 		/* `cmd_ch_disable.is_enable` is already false */
7554e5e7a7dSBin Du 		ret = isp4if_send_command_sync(ispif,
7564e5e7a7dSBin Du 					       ISP4FW_CMD_ID_ENABLE_OUT_CHAN,
7574e5e7a7dSBin Du 					       &cmd_ch_disable,
7584e5e7a7dSBin Du 					       sizeof(cmd_ch_disable));
7594e5e7a7dSBin Du 		if (ret)
7604e5e7a7dSBin Du 			dev_err(dev, "fail to disable stream\n");
7614e5e7a7dSBin Du 		else
7624e5e7a7dSBin Du 			dev_dbg(dev, "wait disable stream suc\n");
7634e5e7a7dSBin Du 
7644e5e7a7dSBin Du 		ret = isp4if_send_command_sync(ispif, ISP4FW_CMD_ID_STOP_STREAM,
7654e5e7a7dSBin Du 					       NULL, 0);
7664e5e7a7dSBin Du 		if (ret)
7674e5e7a7dSBin Du 			dev_err(dev, "fail to stop stream\n");
7684e5e7a7dSBin Du 		else
7694e5e7a7dSBin Du 			dev_dbg(dev, "wait stop stream suc\n");
7704e5e7a7dSBin Du 	}
7714e5e7a7dSBin Du 
7724e5e7a7dSBin Du 	isp4sd_uninit_stream(isp_subdev, state, pad);
7734e5e7a7dSBin Du 
7744e5e7a7dSBin Du 	/*
7754e5e7a7dSBin Du 	 * Return success to ensure the stop process proceeds,
7764e5e7a7dSBin Du 	 * and disregard any errors since they are not fatal.
7774e5e7a7dSBin Du 	 */
7784e5e7a7dSBin Du 	return 0;
7794e5e7a7dSBin Du }
7804e5e7a7dSBin Du 
7814e5e7a7dSBin Du static int isp4sd_start_stream(struct isp4_subdev *isp_subdev,
7824e5e7a7dSBin Du 			       struct v4l2_subdev_state *state, u32 pad)
7834e5e7a7dSBin Du {
7844e5e7a7dSBin Du 	struct isp4sd_output_info *output_info =
7854e5e7a7dSBin Du 			&isp_subdev->sensor_info.output_info;
7864e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
7874e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
7884e5e7a7dSBin Du 	int ret;
7894e5e7a7dSBin Du 
7904e5e7a7dSBin Du 	guard(mutex)(&isp_subdev->ops_mutex);
7914e5e7a7dSBin Du 
7924e5e7a7dSBin Du 	if (ispif->status != ISP4IF_STATUS_FW_RUNNING) {
7934e5e7a7dSBin Du 		dev_err(dev, "fail, bad fsm %d\n", ispif->status);
7944e5e7a7dSBin Du 		return -EINVAL;
7954e5e7a7dSBin Du 	}
7964e5e7a7dSBin Du 
7974e5e7a7dSBin Du 	switch (output_info->start_status) {
7984e5e7a7dSBin Du 	case ISP4SD_START_STATUS_OFF:
7994e5e7a7dSBin Du 		break;
8004e5e7a7dSBin Du 	case ISP4SD_START_STATUS_STARTED:
8014e5e7a7dSBin Du 		dev_dbg(dev, "stream already started, do nothing\n");
8024e5e7a7dSBin Du 		return 0;
8034e5e7a7dSBin Du 	case ISP4SD_START_STATUS_START_FAIL:
8044e5e7a7dSBin Du 		dev_err(dev, "stream previously failed to start\n");
8054e5e7a7dSBin Du 		return -EINVAL;
8064e5e7a7dSBin Du 	}
8074e5e7a7dSBin Du 
8084e5e7a7dSBin Du 	ret = isp4sd_init_stream(isp_subdev);
8094e5e7a7dSBin Du 	if (ret) {
8104e5e7a7dSBin Du 		dev_err(dev, "fail to init isp_subdev stream\n");
8114e5e7a7dSBin Du 		goto err_stop_stream;
8124e5e7a7dSBin Du 	}
8134e5e7a7dSBin Du 
8144e5e7a7dSBin Du 	ret = isp4sd_setup_output(isp_subdev, state, pad);
8154e5e7a7dSBin Du 	if (ret) {
8164e5e7a7dSBin Du 		dev_err(dev, "fail to setup output\n");
8174e5e7a7dSBin Du 		goto err_stop_stream;
8184e5e7a7dSBin Du 	}
8194e5e7a7dSBin Du 
8204e5e7a7dSBin Du 	return 0;
8214e5e7a7dSBin Du 
8224e5e7a7dSBin Du err_stop_stream:
8234e5e7a7dSBin Du 	isp4sd_stop_stream(isp_subdev, state, pad);
8244e5e7a7dSBin Du 	return ret;
8254e5e7a7dSBin Du }
8264e5e7a7dSBin Du 
8274e5e7a7dSBin Du int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd,
8284e5e7a7dSBin Du 			    struct isp4if_img_buf_info *buf_info)
8294e5e7a7dSBin Du {
8304e5e7a7dSBin Du 	struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
8314e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
8324e5e7a7dSBin Du 	struct isp4if_img_buf_node *buf_node;
8334e5e7a7dSBin Du 	struct device *dev = isp_subdev->dev;
8344e5e7a7dSBin Du 	int ret;
8354e5e7a7dSBin Du 
8364e5e7a7dSBin Du 	guard(mutex)(&isp_subdev->ops_mutex);
8374e5e7a7dSBin Du 
8384e5e7a7dSBin Du 	if (ispif->status != ISP4IF_STATUS_FW_RUNNING) {
8394e5e7a7dSBin Du 		dev_err(dev, "fail send img buf for bad fsm %d\n",
8404e5e7a7dSBin Du 			ispif->status);
8414e5e7a7dSBin Du 		return -EINVAL;
8424e5e7a7dSBin Du 	}
8434e5e7a7dSBin Du 
8444e5e7a7dSBin Du 	buf_node = isp4if_alloc_buffer_node(buf_info);
8454e5e7a7dSBin Du 	if (!buf_node) {
8464e5e7a7dSBin Du 		dev_err(dev, "fail alloc sys img buf info node\n");
8474e5e7a7dSBin Du 		return -ENOMEM;
8484e5e7a7dSBin Du 	}
8494e5e7a7dSBin Du 
8504e5e7a7dSBin Du 	ret = isp4if_queue_buffer(ispif, buf_node);
8514e5e7a7dSBin Du 	if (ret) {
8524e5e7a7dSBin Du 		dev_err(dev, "fail to queue image buf, %d\n", ret);
8534e5e7a7dSBin Du 		goto error_release_buf_node;
8544e5e7a7dSBin Du 	}
8554e5e7a7dSBin Du 
8564e5e7a7dSBin Du 	if (!isp_subdev->sensor_info.start_stream_cmd_sent) {
8574e5e7a7dSBin Du 		isp_subdev->sensor_info.buf_sent_cnt++;
8584e5e7a7dSBin Du 
8594e5e7a7dSBin Du 		if (isp_subdev->sensor_info.buf_sent_cnt >=
8604e5e7a7dSBin Du 		    ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) {
8614e5e7a7dSBin Du 			ret = isp4if_send_command(ispif,
8624e5e7a7dSBin Du 						  ISP4FW_CMD_ID_START_STREAM,
8634e5e7a7dSBin Du 						  NULL, 0);
8644e5e7a7dSBin Du 			if (ret) {
8654e5e7a7dSBin Du 				dev_err(dev, "fail to START_STREAM");
8664e5e7a7dSBin Du 				goto error_release_buf_node;
8674e5e7a7dSBin Du 			}
8684e5e7a7dSBin Du 			isp_subdev->sensor_info.start_stream_cmd_sent = true;
8694e5e7a7dSBin Du 			isp_subdev->sensor_info.output_info.start_status =
8704e5e7a7dSBin Du 				ISP4SD_START_STATUS_STARTED;
8714e5e7a7dSBin Du 			isp_subdev->sensor_info.status =
8724e5e7a7dSBin Du 				ISP4SD_START_STATUS_STARTED;
8734e5e7a7dSBin Du 		} else {
8744e5e7a7dSBin Du 			dev_dbg(dev,
8754e5e7a7dSBin Du 				"no send start, required %u, buf sent %u\n",
8764e5e7a7dSBin Du 				ISP4SD_MIN_BUF_CNT_BEF_START_STREAM,
8774e5e7a7dSBin Du 				isp_subdev->sensor_info.buf_sent_cnt);
8784e5e7a7dSBin Du 		}
8794e5e7a7dSBin Du 	}
8804e5e7a7dSBin Du 
8814e5e7a7dSBin Du 	return 0;
8824e5e7a7dSBin Du 
8834e5e7a7dSBin Du error_release_buf_node:
8844e5e7a7dSBin Du 	isp4if_dealloc_buffer_node(buf_node);
8854e5e7a7dSBin Du 	return ret;
8864e5e7a7dSBin Du }
8874e5e7a7dSBin Du 
8884e5e7a7dSBin Du static const struct v4l2_subdev_video_ops isp4sd_video_ops = {
8894e5e7a7dSBin Du 	.s_stream = v4l2_subdev_s_stream_helper,
8904e5e7a7dSBin Du };
8914e5e7a7dSBin Du 
8924e5e7a7dSBin Du static int isp4sd_set_pad_format(struct v4l2_subdev *sd,
8934e5e7a7dSBin Du 				 struct v4l2_subdev_state *sd_state,
8944e5e7a7dSBin Du 				 struct v4l2_subdev_format *format)
8954e5e7a7dSBin Du {
8964e5e7a7dSBin Du 	struct isp4sd_output_info *stream_info =
8974e5e7a7dSBin Du 		&(to_isp4_subdev(sd)->sensor_info.output_info);
8984e5e7a7dSBin Du 	struct v4l2_mbus_framefmt *fmt;
8994e5e7a7dSBin Du 
9004e5e7a7dSBin Du 	fmt = v4l2_subdev_state_get_format(sd_state, format->pad);
9014e5e7a7dSBin Du 
9024e5e7a7dSBin Du 	if (!fmt) {
9034e5e7a7dSBin Du 		dev_err(sd->dev, "fail to get state format\n");
9044e5e7a7dSBin Du 		return -EINVAL;
9054e5e7a7dSBin Du 	}
9064e5e7a7dSBin Du 
9074e5e7a7dSBin Du 	*fmt = format->format;
9084e5e7a7dSBin Du 	switch (fmt->code) {
9094e5e7a7dSBin Du 	case MEDIA_BUS_FMT_YUYV8_1X16:
9104e5e7a7dSBin Du 		stream_info->image_size = fmt->width * fmt->height * 2;
9114e5e7a7dSBin Du 		break;
9124e5e7a7dSBin Du 	case MEDIA_BUS_FMT_YUYV8_1_5X8:
9134e5e7a7dSBin Du 	default:
9144e5e7a7dSBin Du 		stream_info->image_size = fmt->width * fmt->height * 3 / 2;
9154e5e7a7dSBin Du 		break;
9164e5e7a7dSBin Du 	}
9174e5e7a7dSBin Du 
9184e5e7a7dSBin Du 	if (!stream_info->image_size) {
9194e5e7a7dSBin Du 		dev_err(sd->dev,
9204e5e7a7dSBin Du 			"fail set pad format,code 0x%x,width %u, height %u\n",
9214e5e7a7dSBin Du 			fmt->code, fmt->width, fmt->height);
9224e5e7a7dSBin Du 		return -EINVAL;
9234e5e7a7dSBin Du 	}
9244e5e7a7dSBin Du 
9254e5e7a7dSBin Du 	dev_dbg(sd->dev, "set pad format suc, code:%x w:%u h:%u size:%u\n",
9264e5e7a7dSBin Du 		fmt->code, fmt->width, fmt->height,
9274e5e7a7dSBin Du 		stream_info->image_size);
9284e5e7a7dSBin Du 
9294e5e7a7dSBin Du 	return 0;
9304e5e7a7dSBin Du }
9314e5e7a7dSBin Du 
9324e5e7a7dSBin Du static int isp4sd_enable_streams(struct v4l2_subdev *sd,
9334e5e7a7dSBin Du 				 struct v4l2_subdev_state *state, u32 pad,
9344e5e7a7dSBin Du 				 u64 streams_mask)
9354e5e7a7dSBin Du {
9364e5e7a7dSBin Du 	struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
9374e5e7a7dSBin Du 
9384e5e7a7dSBin Du 	return isp4sd_start_stream(isp_subdev, state, pad);
9394e5e7a7dSBin Du }
9404e5e7a7dSBin Du 
9414e5e7a7dSBin Du static int isp4sd_disable_streams(struct v4l2_subdev *sd,
9424e5e7a7dSBin Du 				  struct v4l2_subdev_state *state, u32 pad,
9434e5e7a7dSBin Du 				  u64 streams_mask)
9444e5e7a7dSBin Du {
9454e5e7a7dSBin Du 	struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
9464e5e7a7dSBin Du 
9474e5e7a7dSBin Du 	return isp4sd_stop_stream(isp_subdev, state, pad);
9484e5e7a7dSBin Du }
9494e5e7a7dSBin Du 
9504e5e7a7dSBin Du static const struct v4l2_subdev_pad_ops isp4sd_pad_ops = {
9514e5e7a7dSBin Du 	.get_fmt = v4l2_subdev_get_fmt,
9524e5e7a7dSBin Du 	.set_fmt = isp4sd_set_pad_format,
9534e5e7a7dSBin Du 	.enable_streams = isp4sd_enable_streams,
9544e5e7a7dSBin Du 	.disable_streams = isp4sd_disable_streams,
9554e5e7a7dSBin Du };
9564e5e7a7dSBin Du 
9574e5e7a7dSBin Du static const struct v4l2_subdev_ops isp4sd_subdev_ops = {
9584e5e7a7dSBin Du 	.video = &isp4sd_video_ops,
9594e5e7a7dSBin Du 	.pad = &isp4sd_pad_ops,
9604e5e7a7dSBin Du };
9614e5e7a7dSBin Du 
9624e5e7a7dSBin Du int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev,
9634e5e7a7dSBin Du 		int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM])
9644e5e7a7dSBin Du {
9654e5e7a7dSBin Du 	struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
9664e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
9674e5e7a7dSBin Du 	struct device *dev = v4l2_dev->dev;
9684e5e7a7dSBin Du 	int ret;
9694e5e7a7dSBin Du 
9704e5e7a7dSBin Du 	isp_subdev->dev = dev;
9714e5e7a7dSBin Du 	v4l2_subdev_init(&isp_subdev->sdev, &isp4sd_subdev_ops);
9724e5e7a7dSBin Du 	isp_subdev->sdev.owner = THIS_MODULE;
9734e5e7a7dSBin Du 	isp_subdev->sdev.dev = dev;
9744e5e7a7dSBin Du 	snprintf(isp_subdev->sdev.name, sizeof(isp_subdev->sdev.name), "%s",
9754e5e7a7dSBin Du 		 dev_name(dev));
9764e5e7a7dSBin Du 
9774e5e7a7dSBin Du 	isp_subdev->sdev.entity.name = isp4sd_entity_name;
9784e5e7a7dSBin Du 	isp_subdev->sdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
9794e5e7a7dSBin Du 	isp_subdev->sdev_pad.flags = MEDIA_PAD_FL_SOURCE;
9804e5e7a7dSBin Du 	ret = media_entity_pads_init(&isp_subdev->sdev.entity, 1,
9814e5e7a7dSBin Du 				     &isp_subdev->sdev_pad);
9824e5e7a7dSBin Du 	if (ret) {
9834e5e7a7dSBin Du 		dev_err(dev, "fail to init isp4 subdev entity pad %d\n", ret);
9844e5e7a7dSBin Du 		return ret;
9854e5e7a7dSBin Du 	}
9864e5e7a7dSBin Du 
9874e5e7a7dSBin Du 	ret = v4l2_subdev_init_finalize(&isp_subdev->sdev);
9884e5e7a7dSBin Du 	if (ret < 0) {
9894e5e7a7dSBin Du 		dev_err(dev, "fail to init finalize isp4 subdev %d\n",
9904e5e7a7dSBin Du 			ret);
9914e5e7a7dSBin Du 		return ret;
9924e5e7a7dSBin Du 	}
9934e5e7a7dSBin Du 
9944e5e7a7dSBin Du 	ret = v4l2_device_register_subdev(v4l2_dev, &isp_subdev->sdev);
9954e5e7a7dSBin Du 	if (ret) {
9964e5e7a7dSBin Du 		dev_err(dev, "fail to register isp4 subdev to V4L2 device %d\n",
9974e5e7a7dSBin Du 			ret);
9984e5e7a7dSBin Du 		goto err_media_clean_up;
9994e5e7a7dSBin Du 	}
10004e5e7a7dSBin Du 
10014e5e7a7dSBin Du 	isp4if_init(ispif, dev, isp_subdev->mmio);
10024e5e7a7dSBin Du 
10034e5e7a7dSBin Du 	mutex_init(&isp_subdev->ops_mutex);
10044e5e7a7dSBin Du 	sensor_info->status = ISP4SD_START_STATUS_OFF;
10054e5e7a7dSBin Du 
10064e5e7a7dSBin Du 	/* create ISP enable gpio control */
10074e5e7a7dSBin Du 	isp_subdev->enable_gpio = devm_gpiod_get(isp_subdev->dev,
10084e5e7a7dSBin Du 						 "enable_isp",
10094e5e7a7dSBin Du 						 GPIOD_OUT_LOW);
10104e5e7a7dSBin Du 	if (IS_ERR(isp_subdev->enable_gpio)) {
10114e5e7a7dSBin Du 		ret = PTR_ERR(isp_subdev->enable_gpio);
10124e5e7a7dSBin Du 		dev_err(dev, "fail to get gpiod %d\n", ret);
10134e5e7a7dSBin Du 		goto err_subdev_unreg;
10144e5e7a7dSBin Du 	}
10154e5e7a7dSBin Du 
10164e5e7a7dSBin Du 	for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++)
10174e5e7a7dSBin Du 		isp_subdev->irq[i] = irq[i];
10184e5e7a7dSBin Du 
10194e5e7a7dSBin Du 	isp_subdev->host2fw_seq_num = 1;
10204e5e7a7dSBin Du 	ispif->status = ISP4IF_STATUS_PWR_OFF;
10214e5e7a7dSBin Du 
10222ccf48afSBin Du 	ret = isp4vid_dev_init(&isp_subdev->isp_vdev, &isp_subdev->sdev);
10232ccf48afSBin Du 	if (ret)
10242ccf48afSBin Du 		goto err_subdev_unreg;
10252ccf48afSBin Du 
10264e5e7a7dSBin Du 	return 0;
10274e5e7a7dSBin Du 
10284e5e7a7dSBin Du err_subdev_unreg:
10294e5e7a7dSBin Du 	v4l2_device_unregister_subdev(&isp_subdev->sdev);
10304e5e7a7dSBin Du err_media_clean_up:
10314e5e7a7dSBin Du 	v4l2_subdev_cleanup(&isp_subdev->sdev);
10324e5e7a7dSBin Du 	media_entity_cleanup(&isp_subdev->sdev.entity);
10334e5e7a7dSBin Du 	return ret;
10344e5e7a7dSBin Du }
10354e5e7a7dSBin Du 
10364e5e7a7dSBin Du void isp4sd_deinit(struct isp4_subdev *isp_subdev)
10374e5e7a7dSBin Du {
10384e5e7a7dSBin Du 	struct isp4_interface *ispif = &isp_subdev->ispif;
10394e5e7a7dSBin Du 
10402ccf48afSBin Du 	isp4vid_dev_deinit(&isp_subdev->isp_vdev);
10414e5e7a7dSBin Du 	v4l2_device_unregister_subdev(&isp_subdev->sdev);
10424e5e7a7dSBin Du 	media_entity_cleanup(&isp_subdev->sdev.entity);
10434e5e7a7dSBin Du 	isp4if_deinit(ispif);
10444e5e7a7dSBin Du 	isp4sd_module_enable(isp_subdev, false);
10454e5e7a7dSBin Du 
10464e5e7a7dSBin Du 	ispif->status = ISP4IF_STATUS_PWR_OFF;
10474e5e7a7dSBin Du }
1048