// SPDX-License-Identifier: GPL-2.0+/* * Copyright (C) 2025 Advanced Micro Devices, Inc. */ #include <linux/pm_domain.h>#include <linux/units.h> #include "isp4.h"#include "isp4_debug.h"#include "isp4_fw_cmd_resp.h"#include "isp4_interface.h" #define ISP4SD_MIN_BUF_CNT_BEF_START_STREAM 4 #define ISP4SD_PERFORMANCE_STATE_LOW 0#define ISP4SD_PERFORMANCE_STATE_HIGH 1 /* align 32KB */#define ISP4SD_META_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000) #define to_isp4_subdev(sd) container_of(sd, struct isp4_subdev, sdev) static const char *isp4sd_entity_name = "amd isp4"; static const char *isp4sd_thread_name[ISP4SD_MAX_FW_RESP_STREAM_NUM] = { "amd_isp4_thread_global", "amd_isp4_thread_stream1",}; static void isp4sd_module_enable(struct isp4_subdev *isp_subdev, bool enable){ if (isp_subdev->enable_gpio) { gpiod_set_value(isp_subdev->enable_gpio, enable ? 1 : 0); dev_dbg(isp_subdev->dev, "%s isp_subdev module\n", enable ? "enable" : "disable"); }} static int isp4sd_setup_fw_mem_pool(struct isp4_subdev *isp_subdev){ struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4fw_cmd_send_buffer buf_type; struct device *dev = isp_subdev->dev; int ret; if (!ispif->fw_mem_pool) { dev_err(dev, "fail to alloc mem pool\n"); return -ENOMEM; } /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&buf_type, 0, sizeof(buf_type)); buf_type.buffer_type = ISP4FW_BUFFER_TYPE_MEM_POOL; buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; isp4if_split_addr64(ispif->fw_mem_pool->gpu_mc_addr, &buf_type.buffer.buf_base_a_lo, &buf_type.buffer.buf_base_a_hi); buf_type.buffer.buf_size_a = ispif->fw_mem_pool->mem_size; ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &buf_type, sizeof(buf_type)); if (ret) { dev_err(dev, "send fw mem pool 0x%llx(%u) fail %d\n", ispif->fw_mem_pool->gpu_mc_addr, buf_type.buffer.buf_size_a, ret); return ret; } dev_dbg(dev, "send fw mem pool 0x%llx(%u) suc\n", ispif->fw_mem_pool->gpu_mc_addr, buf_type.buffer.buf_size_a); return 0;} static int isp4sd_set_stream_path(struct isp4_subdev *isp_subdev){ struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4fw_cmd_set_stream_cfg cmd; struct device *dev = isp_subdev->dev; /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&cmd, 0, sizeof(cmd)); cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id = ISP4FW_SENSOR_ID_ON_MIPI0; cmd.stream_cfg.mipi_pipe_path_cfg.b_enable = true; cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id = ISP4FW_MIPI0_ISP_PIPELINE_ID; cmd.stream_cfg.b_enable_tnr = true; dev_dbg(dev, "isp4fw_sensor_id %d, pipeId 0x%x EnableTnr %u\n", cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id, cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id, cmd.stream_cfg.b_enable_tnr); return isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_STREAM_CONFIG, &cmd, sizeof(cmd));} static int isp4sd_send_meta_buf(struct isp4_subdev *isp_subdev){ struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4fw_cmd_send_buffer buf_type; struct device *dev = isp_subdev->dev; /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&buf_type, 0, sizeof(buf_type)); for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { struct isp4if_gpu_mem_info *meta_info_buf = isp_subdev->ispif.meta_info_buf[i]; int ret; if (!meta_info_buf) { dev_err(dev, "fail for no meta info buf(%u)\n", i); return -ENOMEM; } buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; isp4if_split_addr64(meta_info_buf->gpu_mc_addr, &buf_type.buffer.buf_base_a_lo, &buf_type.buffer.buf_base_a_hi); buf_type.buffer.buf_size_a = meta_info_buf->mem_size; ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &buf_type, sizeof(buf_type)); if (ret) { dev_err(dev, "send meta info(%u) fail\n", i); return ret; } } dev_dbg(dev, "send meta info suc\n"); return 0;} static bool isp4sd_get_str_out_prop(struct isp4_subdev *isp_subdev, struct isp4fw_image_prop *out_prop, struct v4l2_subdev_state *state, u32 pad){ struct device *dev = isp_subdev->dev; struct v4l2_mbus_framefmt *format; format = v4l2_subdev_state_get_format(state, pad, 0); if (!format) { dev_err(dev, "fail get subdev state format\n"); return false; } switch (format->code) { case MEDIA_BUS_FMT_YUYV8_1_5X8: out_prop->image_format = ISP4FW_IMAGE_FORMAT_NV12; out_prop->width = format->width; out_prop->height = format->height; out_prop->luma_pitch = format->width; out_prop->chroma_pitch = out_prop->width; break; case MEDIA_BUS_FMT_YUYV8_1X16: out_prop->image_format = ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED; out_prop->width = format->width; out_prop->height = format->height; out_prop->luma_pitch = format->width * 2; out_prop->chroma_pitch = 0; break; default: dev_err(dev, "fail for bad image format:0x%x\n", format->code); return false; } if (!out_prop->width || !out_prop->height) return false; return true;} static int isp4sd_kickoff_stream(struct isp4_subdev *isp_subdev, u32 w, u32 h){ struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; struct isp4_interface *ispif = &isp_subdev->ispif; struct device *dev = isp_subdev->dev; if (sensor_info->status == ISP4SD_START_STATUS_STARTED) return 0; if (sensor_info->status == ISP4SD_START_STATUS_START_FAIL) { dev_err(dev, "fail for previous start fail\n"); return -EINVAL; } dev_dbg(dev, "w:%u,h:%u\n", w, h); if (isp4sd_send_meta_buf(isp_subdev)) { dev_err(dev, "fail to send meta buf\n"); sensor_info->status = ISP4SD_START_STATUS_START_FAIL; return -EINVAL; } sensor_info->status = ISP4SD_START_STATUS_OFF; if (!sensor_info->start_stream_cmd_sent && sensor_info->buf_sent_cnt >= ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) { int ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_START_STREAM, NULL, 0); if (ret) { dev_err(dev, "fail to start stream\n"); return ret; } sensor_info->start_stream_cmd_sent = true; } else { dev_dbg(dev, "no send START_STREAM, start_sent %u, buf_sent %u\n", sensor_info->start_stream_cmd_sent, sensor_info->buf_sent_cnt); } return 0;} static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, struct v4l2_subdev_state *state, u32 pad){ struct isp4sd_output_info *output_info = &isp_subdev->sensor_info.output_info; struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4fw_cmd_set_out_ch_prop cmd_ch_prop; struct isp4fw_cmd_enable_out_ch cmd_ch_en; struct device *dev = isp_subdev->dev; int ret; if (output_info->start_status == ISP4SD_START_STATUS_STARTED) return 0; if (output_info->start_status == ISP4SD_START_STATUS_START_FAIL) { dev_err(dev, "fail for previous start fail\n"); return -EINVAL; } /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&cmd_ch_prop, 0, sizeof(cmd_ch_prop)); cmd_ch_prop.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; if (!isp4sd_get_str_out_prop(isp_subdev, &cmd_ch_prop.image_prop, state, pad)) { dev_err(dev, "fail to get out prop\n"); return -EINVAL; } dev_dbg(dev, "channel:%s,fmt %s,w:h=%u:%u,lp:%u,cp%u\n", isp4dbg_get_out_ch_str(cmd_ch_prop.ch), isp4dbg_get_img_fmt_str(cmd_ch_prop.image_prop.image_format), cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height, cmd_ch_prop.image_prop.luma_pitch, cmd_ch_prop.image_prop.chroma_pitch); ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_OUT_CHAN_PROP, &cmd_ch_prop, sizeof(cmd_ch_prop)); if (ret) { output_info->start_status = ISP4SD_START_STATUS_START_FAIL; dev_err(dev, "fail to set out prop\n"); return ret; } /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&cmd_ch_en, 0, sizeof(cmd_ch_en)); cmd_ch_en.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; cmd_ch_en.is_enable = true; ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_ENABLE_OUT_CHAN, &cmd_ch_en, sizeof(cmd_ch_en)); if (ret) { output_info->start_status = ISP4SD_START_STATUS_START_FAIL; dev_err(dev, "fail to enable channel\n"); return ret; } dev_dbg(dev, "enable channel %s\n", isp4dbg_get_out_ch_str(cmd_ch_en.ch)); if (!sensor_info->start_stream_cmd_sent) { ret = isp4sd_kickoff_stream(isp_subdev, cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height); if (ret) { dev_err(dev, "kickoff stream fail %d\n", ret); return ret; } /* * sensor_info->start_stream_cmd_sent will be set to true * 1. in isp4sd_kickoff_stream, if app first send buffer then * start stream * 2. in isp_set_stream_buf, if app first start stream, then * send buffer because ISP FW has the requirement, host needs * to send buffer before send start stream cmd */ if (sensor_info->start_stream_cmd_sent) { sensor_info->status = ISP4SD_START_STATUS_STARTED; output_info->start_status = ISP4SD_START_STATUS_STARTED; dev_dbg(dev, "kickoff stream suc,start cmd sent\n"); } } else { dev_dbg(dev, "stream running, no need kickoff\n"); output_info->start_status = ISP4SD_START_STATUS_STARTED; } dev_dbg(dev, "setup output suc\n"); return 0;} static int isp4sd_init_stream(struct isp4_subdev *isp_subdev){ struct device *dev = isp_subdev->dev; int ret; ret = isp4sd_setup_fw_mem_pool(isp_subdev); if (ret) { dev_err(dev, "fail to setup fw mem pool\n"); return ret; } ret = isp4sd_set_stream_path(isp_subdev); if (ret) { dev_err(dev, "fail to setup stream path\n"); return ret; } return 0;} static void isp4sd_uninit_stream(struct isp4_subdev *isp_subdev, struct v4l2_subdev_state *state, u32 pad){ struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; struct isp4sd_output_info *output_info = &sensor_info->output_info; struct isp4_interface *ispif = &isp_subdev->ispif; struct v4l2_mbus_framefmt *format; format = v4l2_subdev_state_get_format(state, pad, 0); if (!format) { dev_err(isp_subdev->dev, "fail to get v4l2 format\n"); } else { memset(format, 0, sizeof(*format)); format->code = MEDIA_BUS_FMT_YUYV8_1_5X8; } isp4if_clear_bufq(ispif); isp4if_clear_cmdq(ispif); sensor_info->start_stream_cmd_sent = false; sensor_info->buf_sent_cnt = 0; sensor_info->status = ISP4SD_START_STATUS_OFF; output_info->start_status = ISP4SD_START_STATUS_OFF;} static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev, enum isp4if_stream_id stream_id, struct isp4fw_resp_cmd_done *para){ struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4if_cmd_element *ele = isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id); struct device *dev = isp_subdev->dev; dev_dbg(dev, "stream %d,cmd %s(0x%08x)(%d),seq %u, ele %p\n", stream_id, isp4dbg_get_cmd_str(para->cmd_id), para->cmd_id, para->cmd_status, para->cmd_seq_num, ele); if (ele) { complete(&ele->cmd_done); if (refcount_dec_and_test(&ele->refcnt)) kfree(ele); }} static struct isp4fw_meta_info *isp4sd_get_meta_by_mc(struct isp4_subdev *isp_subdev, u64 mc){ for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { struct isp4if_gpu_mem_info *meta_info_buf = isp_subdev->ispif.meta_info_buf[i]; if (meta_info_buf->gpu_mc_addr == mc) return meta_info_buf->sys_addr; } return NULL;} static void isp4sd_send_meta_info(struct isp4_subdev *isp_subdev, u64 meta_info_mc){ struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4fw_cmd_send_buffer buf_type; struct device *dev = isp_subdev->dev; if (isp_subdev->sensor_info.status != ISP4SD_START_STATUS_STARTED) { dev_warn(dev, "not working status %i, meta_info 0x%llx\n", isp_subdev->sensor_info.status, meta_info_mc); return; } /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&buf_type, 0, sizeof(buf_type)); buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; isp4if_split_addr64(meta_info_mc, &buf_type.buffer.buf_base_a_lo, &buf_type.buffer.buf_base_a_hi); buf_type.buffer.buf_size_a = ISP4SD_META_BUF_SIZE; if (isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &buf_type, sizeof(buf_type))) dev_err(dev, "fail send meta_info 0x%llx\n", meta_info_mc); else dev_dbg(dev, "resend meta_info 0x%llx\n", meta_info_mc);} static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, enum isp4if_stream_id stream_id, struct isp4fw_resp_param_package *para){ struct isp4_interface *ispif = &isp_subdev->ispif; struct device *dev = isp_subdev->dev; struct isp4if_img_buf_node *prev; struct isp4fw_meta_info *meta; u64 mc; mc = isp4if_join_addr64(para->package_addr_lo, para->package_addr_hi); meta = isp4sd_get_meta_by_mc(isp_subdev, mc); if (!meta) { dev_err(dev, "fail to get meta from mc %llx\n", mc); return; } dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%s(%i)\n", ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled, isp4dbg_get_buf_done_str(meta->preview.status), meta->preview.status); if (meta->preview.enabled && (meta->preview.status == ISP4FW_BUFFER_STATUS_SKIPPED || meta->preview.status == ISP4FW_BUFFER_STATUS_DONE || meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { prev = isp4if_dequeue_buffer(ispif); if (prev) { isp4dbg_show_bufmeta_info(dev, "prev", &meta->preview, &prev->buf_info); isp4vid_handle_frame_done(&isp_subdev->isp_vdev, &prev->buf_info); isp4if_dealloc_buffer_node(prev); } else { dev_err(dev, "fail null prev buf\n"); } } else if (meta->preview.enabled) { dev_err(dev, "fail bad preview status %u(%s)\n", meta->preview.status, isp4dbg_get_buf_done_str(meta->preview.status)); } if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED) isp4sd_send_meta_info(isp_subdev, mc); dev_dbg(dev, "stream_id:%d, status:%d\n", stream_id, isp_subdev->sensor_info.status);} static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, enum isp4if_stream_id stream_id){ struct isp4_interface *ispif = &isp_subdev->ispif; struct device *dev = isp_subdev->dev;Lines 1–500 of 1063. Load more
// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2025 Advanced Micro Devices, Inc. */ #include <linux/pm_domain.h> #include <linux/units.h> #include "isp4.h" #include "isp4_debug.h" #include "isp4_fw_cmd_resp.h" #include "isp4_interface.h" #define ISP4SD_MIN_BUF_CNT_BEF_START_STREAM 4 #define ISP4SD_PERFORMANCE_STATE_LOW 0 #define ISP4SD_PERFORMANCE_STATE_HIGH 1 /* align 32KB */ #define ISP4SD_META_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000) #define to_isp4_subdev(sd) container_of(sd, struct isp4_subdev, sdev) static const char *isp4sd_entity_name = "amd isp4"; static const char *isp4sd_thread_name[ISP4SD_MAX_FW_RESP_STREAM_NUM] = { "amd_isp4_thread_global", "amd_isp4_thread_stream1", }; static void isp4sd_module_enable(struct isp4_subdev *isp_subdev, bool enable) { if (isp_subdev->enable_gpio) { gpiod_set_value(isp_subdev->enable_gpio, enable ? 1 : 0); dev_dbg(isp_subdev->dev, "%s isp_subdev module\n", enable ? "enable" : "disable"); } } static int isp4sd_setup_fw_mem_pool(struct isp4_subdev *isp_subdev) { struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4fw_cmd_send_buffer buf_type; struct device *dev = isp_subdev->dev; int ret; if (!ispif->fw_mem_pool) { dev_err(dev, "fail to alloc mem pool\n"); return -ENOMEM; } /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&buf_type, 0, sizeof(buf_type)); buf_type.buffer_type = ISP4FW_BUFFER_TYPE_MEM_POOL; buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; isp4if_split_addr64(ispif->fw_mem_pool->gpu_mc_addr, &buf_type.buffer.buf_base_a_lo, &buf_type.buffer.buf_base_a_hi); buf_type.buffer.buf_size_a = ispif->fw_mem_pool->mem_size; ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &buf_type, sizeof(buf_type)); if (ret) { dev_err(dev, "send fw mem pool 0x%llx(%u) fail %d\n", ispif->fw_mem_pool->gpu_mc_addr, buf_type.buffer.buf_size_a, ret); return ret; } dev_dbg(dev, "send fw mem pool 0x%llx(%u) suc\n", ispif->fw_mem_pool->gpu_mc_addr, buf_type.buffer.buf_size_a); return 0; } static int isp4sd_set_stream_path(struct isp4_subdev *isp_subdev) { struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4fw_cmd_set_stream_cfg cmd; struct device *dev = isp_subdev->dev; /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&cmd, 0, sizeof(cmd)); cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id = ISP4FW_SENSOR_ID_ON_MIPI0; cmd.stream_cfg.mipi_pipe_path_cfg.b_enable = true; cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id = ISP4FW_MIPI0_ISP_PIPELINE_ID; cmd.stream_cfg.b_enable_tnr = true; dev_dbg(dev, "isp4fw_sensor_id %d, pipeId 0x%x EnableTnr %u\n", cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id, cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id, cmd.stream_cfg.b_enable_tnr); return isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_STREAM_CONFIG, &cmd, sizeof(cmd)); } static int isp4sd_send_meta_buf(struct isp4_subdev *isp_subdev) { struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4fw_cmd_send_buffer buf_type; struct device *dev = isp_subdev->dev; /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&buf_type, 0, sizeof(buf_type)); for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { struct isp4if_gpu_mem_info *meta_info_buf = isp_subdev->ispif.meta_info_buf[i]; int ret; if (!meta_info_buf) { dev_err(dev, "fail for no meta info buf(%u)\n", i); return -ENOMEM; } buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; isp4if_split_addr64(meta_info_buf->gpu_mc_addr, &buf_type.buffer.buf_base_a_lo, &buf_type.buffer.buf_base_a_hi); buf_type.buffer.buf_size_a = meta_info_buf->mem_size; ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &buf_type, sizeof(buf_type)); if (ret) { dev_err(dev, "send meta info(%u) fail\n", i); return ret; } } dev_dbg(dev, "send meta info suc\n"); return 0; } static bool isp4sd_get_str_out_prop(struct isp4_subdev *isp_subdev, struct isp4fw_image_prop *out_prop, struct v4l2_subdev_state *state, u32 pad) { struct device *dev = isp_subdev->dev; struct v4l2_mbus_framefmt *format; format = v4l2_subdev_state_get_format(state, pad, 0); if (!format) { dev_err(dev, "fail get subdev state format\n"); return false; } switch (format->code) { case MEDIA_BUS_FMT_YUYV8_1_5X8: out_prop->image_format = ISP4FW_IMAGE_FORMAT_NV12; out_prop->width = format->width; out_prop->height = format->height; out_prop->luma_pitch = format->width; out_prop->chroma_pitch = out_prop->width; break; case MEDIA_BUS_FMT_YUYV8_1X16: out_prop->image_format = ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED; out_prop->width = format->width; out_prop->height = format->height; out_prop->luma_pitch = format->width * 2; out_prop->chroma_pitch = 0; break; default: dev_err(dev, "fail for bad image format:0x%x\n", format->code); return false; } if (!out_prop->width || !out_prop->height) return false; return true; } static int isp4sd_kickoff_stream(struct isp4_subdev *isp_subdev, u32 w, u32 h) { struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; struct isp4_interface *ispif = &isp_subdev->ispif; struct device *dev = isp_subdev->dev; if (sensor_info->status == ISP4SD_START_STATUS_STARTED) return 0; if (sensor_info->status == ISP4SD_START_STATUS_START_FAIL) { dev_err(dev, "fail for previous start fail\n"); return -EINVAL; } dev_dbg(dev, "w:%u,h:%u\n", w, h); if (isp4sd_send_meta_buf(isp_subdev)) { dev_err(dev, "fail to send meta buf\n"); sensor_info->status = ISP4SD_START_STATUS_START_FAIL; return -EINVAL; } sensor_info->status = ISP4SD_START_STATUS_OFF; if (!sensor_info->start_stream_cmd_sent && sensor_info->buf_sent_cnt >= ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) { int ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_START_STREAM, NULL, 0); if (ret) { dev_err(dev, "fail to start stream\n"); return ret; } sensor_info->start_stream_cmd_sent = true; } else { dev_dbg(dev, "no send START_STREAM, start_sent %u, buf_sent %u\n", sensor_info->start_stream_cmd_sent, sensor_info->buf_sent_cnt); } return 0; } static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, struct v4l2_subdev_state *state, u32 pad) { struct isp4sd_output_info *output_info = &isp_subdev->sensor_info.output_info; struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4fw_cmd_set_out_ch_prop cmd_ch_prop; struct isp4fw_cmd_enable_out_ch cmd_ch_en; struct device *dev = isp_subdev->dev; int ret; if (output_info->start_status == ISP4SD_START_STATUS_STARTED) return 0; if (output_info->start_status == ISP4SD_START_STATUS_START_FAIL) { dev_err(dev, "fail for previous start fail\n"); return -EINVAL; } /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&cmd_ch_prop, 0, sizeof(cmd_ch_prop)); cmd_ch_prop.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; if (!isp4sd_get_str_out_prop(isp_subdev, &cmd_ch_prop.image_prop, state, pad)) { dev_err(dev, "fail to get out prop\n"); return -EINVAL; } dev_dbg(dev, "channel:%s,fmt %s,w:h=%u:%u,lp:%u,cp%u\n", isp4dbg_get_out_ch_str(cmd_ch_prop.ch), isp4dbg_get_img_fmt_str(cmd_ch_prop.image_prop.image_format), cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height, cmd_ch_prop.image_prop.luma_pitch, cmd_ch_prop.image_prop.chroma_pitch); ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_OUT_CHAN_PROP, &cmd_ch_prop, sizeof(cmd_ch_prop)); if (ret) { output_info->start_status = ISP4SD_START_STATUS_START_FAIL; dev_err(dev, "fail to set out prop\n"); return ret; } /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&cmd_ch_en, 0, sizeof(cmd_ch_en)); cmd_ch_en.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; cmd_ch_en.is_enable = true; ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_ENABLE_OUT_CHAN, &cmd_ch_en, sizeof(cmd_ch_en)); if (ret) { output_info->start_status = ISP4SD_START_STATUS_START_FAIL; dev_err(dev, "fail to enable channel\n"); return ret; } dev_dbg(dev, "enable channel %s\n", isp4dbg_get_out_ch_str(cmd_ch_en.ch)); if (!sensor_info->start_stream_cmd_sent) { ret = isp4sd_kickoff_stream(isp_subdev, cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height); if (ret) { dev_err(dev, "kickoff stream fail %d\n", ret); return ret; } /* * sensor_info->start_stream_cmd_sent will be set to true * 1. in isp4sd_kickoff_stream, if app first send buffer then * start stream * 2. in isp_set_stream_buf, if app first start stream, then * send buffer because ISP FW has the requirement, host needs * to send buffer before send start stream cmd */ if (sensor_info->start_stream_cmd_sent) { sensor_info->status = ISP4SD_START_STATUS_STARTED; output_info->start_status = ISP4SD_START_STATUS_STARTED; dev_dbg(dev, "kickoff stream suc,start cmd sent\n"); } } else { dev_dbg(dev, "stream running, no need kickoff\n"); output_info->start_status = ISP4SD_START_STATUS_STARTED; } dev_dbg(dev, "setup output suc\n"); return 0; } static int isp4sd_init_stream(struct isp4_subdev *isp_subdev) { struct device *dev = isp_subdev->dev; int ret; ret = isp4sd_setup_fw_mem_pool(isp_subdev); if (ret) { dev_err(dev, "fail to setup fw mem pool\n"); return ret; } ret = isp4sd_set_stream_path(isp_subdev); if (ret) { dev_err(dev, "fail to setup stream path\n"); return ret; } return 0; } static void isp4sd_uninit_stream(struct isp4_subdev *isp_subdev, struct v4l2_subdev_state *state, u32 pad) { struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; struct isp4sd_output_info *output_info = &sensor_info->output_info; struct isp4_interface *ispif = &isp_subdev->ispif; struct v4l2_mbus_framefmt *format; format = v4l2_subdev_state_get_format(state, pad, 0); if (!format) { dev_err(isp_subdev->dev, "fail to get v4l2 format\n"); } else { memset(format, 0, sizeof(*format)); format->code = MEDIA_BUS_FMT_YUYV8_1_5X8; } isp4if_clear_bufq(ispif); isp4if_clear_cmdq(ispif); sensor_info->start_stream_cmd_sent = false; sensor_info->buf_sent_cnt = 0; sensor_info->status = ISP4SD_START_STATUS_OFF; output_info->start_status = ISP4SD_START_STATUS_OFF; } static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev, enum isp4if_stream_id stream_id, struct isp4fw_resp_cmd_done *para) { struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4if_cmd_element *ele = isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id); struct device *dev = isp_subdev->dev; dev_dbg(dev, "stream %d,cmd %s(0x%08x)(%d),seq %u, ele %p\n", stream_id, isp4dbg_get_cmd_str(para->cmd_id), para->cmd_id, para->cmd_status, para->cmd_seq_num, ele); if (ele) { complete(&ele->cmd_done); if (refcount_dec_and_test(&ele->refcnt)) kfree(ele); } } static struct isp4fw_meta_info * isp4sd_get_meta_by_mc(struct isp4_subdev *isp_subdev, u64 mc) { for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { struct isp4if_gpu_mem_info *meta_info_buf = isp_subdev->ispif.meta_info_buf[i]; if (meta_info_buf->gpu_mc_addr == mc) return meta_info_buf->sys_addr; } return NULL; } static void isp4sd_send_meta_info(struct isp4_subdev *isp_subdev, u64 meta_info_mc) { struct isp4_interface *ispif = &isp_subdev->ispif; struct isp4fw_cmd_send_buffer buf_type; struct device *dev = isp_subdev->dev; if (isp_subdev->sensor_info.status != ISP4SD_START_STATUS_STARTED) { dev_warn(dev, "not working status %i, meta_info 0x%llx\n", isp_subdev->sensor_info.status, meta_info_mc); return; } /* * The struct will be shared with ISP FW, use memset() to guarantee * padding bits are zeroed, since this is not guaranteed on all * compilers. */ memset(&buf_type, 0, sizeof(buf_type)); buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; isp4if_split_addr64(meta_info_mc, &buf_type.buffer.buf_base_a_lo, &buf_type.buffer.buf_base_a_hi); buf_type.buffer.buf_size_a = ISP4SD_META_BUF_SIZE; if (isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &buf_type, sizeof(buf_type))) dev_err(dev, "fail send meta_info 0x%llx\n", meta_info_mc); else dev_dbg(dev, "resend meta_info 0x%llx\n", meta_info_mc); } static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, enum isp4if_stream_id stream_id, struct isp4fw_resp_param_package *para) { struct isp4_interface *ispif = &isp_subdev->ispif; struct device *dev = isp_subdev->dev; struct isp4if_img_buf_node *prev; struct isp4fw_meta_info *meta; u64 mc; mc = isp4if_join_addr64(para->package_addr_lo, para->package_addr_hi); meta = isp4sd_get_meta_by_mc(isp_subdev, mc); if (!meta) { dev_err(dev, "fail to get meta from mc %llx\n", mc); return; } dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%s(%i)\n", ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled, isp4dbg_get_buf_done_str(meta->preview.status), meta->preview.status); if (meta->preview.enabled && (meta->preview.status == ISP4FW_BUFFER_STATUS_SKIPPED || meta->preview.status == ISP4FW_BUFFER_STATUS_DONE || meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { prev = isp4if_dequeue_buffer(ispif); if (prev) { isp4dbg_show_bufmeta_info(dev, "prev", &meta->preview, &prev->buf_info); isp4vid_handle_frame_done(&isp_subdev->isp_vdev, &prev->buf_info); isp4if_dealloc_buffer_node(prev); } else { dev_err(dev, "fail null prev buf\n"); } } else if (meta->preview.enabled) { dev_err(dev, "fail bad preview status %u(%s)\n", meta->preview.status, isp4dbg_get_buf_done_str(meta->preview.status)); } if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED) isp4sd_send_meta_info(isp_subdev, mc); dev_dbg(dev, "stream_id:%d, status:%d\n", stream_id, isp_subdev->sensor_info.status); } static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, enum isp4if_stream_id stream_id) { struct isp4_interface *ispif = &isp_subdev->ispif; struct device *dev = isp_subdev->dev;