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