488 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013--2024 Intel Corporation4 */5 6#include <linux/cacheflush.h>7#include <linux/delay.h>8#include <linux/device.h>9#include <linux/io.h>10#include <linux/spinlock.h>11#include <linux/types.h>12 13#include "ipu6-bus.h"14#include "ipu6-fw-com.h"15#include "ipu6-isys.h"16#include "ipu6-platform-isys-csi2-reg.h"17#include "ipu6-platform-regs.h"18 19static const char send_msg_types[N_IPU6_FW_ISYS_SEND_TYPE][32] = {20 "STREAM_OPEN",21 "STREAM_START",22 "STREAM_START_AND_CAPTURE",23 "STREAM_CAPTURE",24 "STREAM_STOP",25 "STREAM_FLUSH",26 "STREAM_CLOSE"27};28 29static int handle_proxy_response(struct ipu6_isys *isys, unsigned int req_id)30{31 struct device *dev = &isys->adev->auxdev.dev;32 struct ipu6_fw_isys_proxy_resp_info_abi *resp;33 int ret;34 35 resp = ipu6_recv_get_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);36 if (!resp)37 return 1;38 39 dev_dbg(dev, "Proxy response: id %u, error %u, details %u\n",40 resp->request_id, resp->error_info.error,41 resp->error_info.error_details);42 43 ret = req_id == resp->request_id ? 0 : -EIO;44 45 ipu6_recv_put_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);46 47 return ret;48}49 50int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys,51 unsigned int req_id,52 unsigned int index,53 unsigned int offset, u32 value)54{55 struct ipu6_fw_com_context *ctx = isys->fwcom;56 struct device *dev = &isys->adev->auxdev.dev;57 struct ipu6_fw_proxy_send_queue_token *token;58 unsigned int timeout = 1000;59 int ret;60 61 dev_dbg(dev,62 "proxy send: req_id 0x%x, index %d, offset 0x%x, value 0x%x\n",63 req_id, index, offset, value);64 65 token = ipu6_send_get_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES);66 if (!token)67 return -EBUSY;68 69 token->request_id = req_id;70 token->region_index = index;71 token->offset = offset;72 token->value = value;73 ipu6_send_put_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES);74 75 do {76 usleep_range(100, 110);77 ret = handle_proxy_response(isys, req_id);78 if (!ret)79 break;80 if (ret == -EIO) {81 dev_err(dev, "Proxy respond with unexpected id\n");82 break;83 }84 timeout--;85 } while (ret && timeout);86 87 if (!timeout)88 dev_err(dev, "Proxy response timed out\n");89 90 return ret;91}92 93int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys,94 const unsigned int stream_handle,95 void *cpu_mapped_buf,96 dma_addr_t dma_mapped_buf,97 size_t size, u16 send_type)98{99 struct ipu6_fw_com_context *ctx = isys->fwcom;100 struct device *dev = &isys->adev->auxdev.dev;101 struct ipu6_fw_send_queue_token *token;102 103 if (send_type >= N_IPU6_FW_ISYS_SEND_TYPE)104 return -EINVAL;105 106 dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]);107 108 /*109 * Time to flush cache in case we have some payload. Not all messages110 * have that111 */112 if (cpu_mapped_buf)113 clflush_cache_range(cpu_mapped_buf, size);114 115 token = ipu6_send_get_token(ctx,116 stream_handle + IPU6_BASE_MSG_SEND_QUEUES);117 if (!token)118 return -EBUSY;119 120 token->payload = dma_mapped_buf;121 token->buf_handle = (unsigned long)cpu_mapped_buf;122 token->send_type = send_type;123 124 ipu6_send_put_token(ctx, stream_handle + IPU6_BASE_MSG_SEND_QUEUES);125 126 return 0;127}128 129int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys,130 const unsigned int stream_handle, u16 send_type)131{132 return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0,133 send_type);134}135 136int ipu6_fw_isys_close(struct ipu6_isys *isys)137{138 struct device *dev = &isys->adev->auxdev.dev;139 int retry = IPU6_ISYS_CLOSE_RETRY;140 unsigned long flags;141 void *fwcom;142 int ret;143 144 /*145 * Stop the isys fw. Actual close takes146 * some time as the FW must stop its actions including code fetch147 * to SP icache.148 * spinlock to wait the interrupt handler to be finished149 */150 spin_lock_irqsave(&isys->power_lock, flags);151 ret = ipu6_fw_com_close(isys->fwcom);152 fwcom = isys->fwcom;153 isys->fwcom = NULL;154 spin_unlock_irqrestore(&isys->power_lock, flags);155 if (ret)156 dev_err(dev, "Device close failure: %d\n", ret);157 158 /* release probably fails if the close failed. Let's try still */159 do {160 usleep_range(400, 500);161 ret = ipu6_fw_com_release(fwcom, 0);162 retry--;163 } while (ret && retry);164 165 if (ret) {166 dev_err(dev, "Device release time out %d\n", ret);167 spin_lock_irqsave(&isys->power_lock, flags);168 isys->fwcom = fwcom;169 spin_unlock_irqrestore(&isys->power_lock, flags);170 }171 172 return ret;173}174 175void ipu6_fw_isys_cleanup(struct ipu6_isys *isys)176{177 int ret;178 179 ret = ipu6_fw_com_release(isys->fwcom, 1);180 if (ret < 0)181 dev_warn(&isys->adev->auxdev.dev,182 "Device busy, fw_com release failed.");183 isys->fwcom = NULL;184}185 186static void start_sp(struct ipu6_bus_device *adev)187{188 struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);189 void __iomem *spc_regs_base = isys->pdata->base +190 isys->pdata->ipdata->hw_variant.spc_offset;191 u32 val = IPU6_ISYS_SPC_STATUS_START |192 IPU6_ISYS_SPC_STATUS_RUN |193 IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE;194 195 val |= isys->icache_prefetch ? IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH : 0;196 197 writel(val, spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL);198}199 200static int query_sp(struct ipu6_bus_device *adev)201{202 struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);203 void __iomem *spc_regs_base = isys->pdata->base +204 isys->pdata->ipdata->hw_variant.spc_offset;205 u32 val;206 207 val = readl(spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL);208 /* return true when READY == 1, START == 0 */209 val &= IPU6_ISYS_SPC_STATUS_READY | IPU6_ISYS_SPC_STATUS_START;210 211 return val == IPU6_ISYS_SPC_STATUS_READY;212}213 214static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys,215 struct ipu6_fw_com_cfg *fwcom,216 unsigned int num_streams)217{218 unsigned int max_send_queues, max_sram_blocks, max_devq_size;219 struct ipu6_fw_syscom_queue_config *input_queue_cfg;220 struct ipu6_fw_syscom_queue_config *output_queue_cfg;221 struct device *dev = &isys->adev->auxdev.dev;222 int type_proxy = IPU6_FW_ISYS_QUEUE_TYPE_PROXY;223 int type_dev = IPU6_FW_ISYS_QUEUE_TYPE_DEV;224 int type_msg = IPU6_FW_ISYS_QUEUE_TYPE_MSG;225 int base_dev_send = IPU6_BASE_DEV_SEND_QUEUES;226 int base_msg_send = IPU6_BASE_MSG_SEND_QUEUES;227 int base_msg_recv = IPU6_BASE_MSG_RECV_QUEUES;228 struct ipu6_fw_isys_fw_config *isys_fw_cfg;229 u32 num_in_message_queues;230 unsigned int max_streams;231 unsigned int size;232 unsigned int i;233 234 max_streams = isys->pdata->ipdata->max_streams;235 max_send_queues = isys->pdata->ipdata->max_send_queues;236 max_sram_blocks = isys->pdata->ipdata->max_sram_blocks;237 max_devq_size = isys->pdata->ipdata->max_devq_size;238 num_in_message_queues = clamp(num_streams, 1U, max_streams);239 isys_fw_cfg = devm_kzalloc(dev, sizeof(*isys_fw_cfg), GFP_KERNEL);240 if (!isys_fw_cfg)241 return -ENOMEM;242 243 isys_fw_cfg->num_send_queues[type_proxy] = IPU6_N_MAX_PROXY_SEND_QUEUES;244 isys_fw_cfg->num_send_queues[type_dev] = IPU6_N_MAX_DEV_SEND_QUEUES;245 isys_fw_cfg->num_send_queues[type_msg] = num_in_message_queues;246 isys_fw_cfg->num_recv_queues[type_proxy] = IPU6_N_MAX_PROXY_RECV_QUEUES;247 /* Common msg/dev return queue */248 isys_fw_cfg->num_recv_queues[type_dev] = 0;249 isys_fw_cfg->num_recv_queues[type_msg] = 1;250 251 size = sizeof(*input_queue_cfg) * max_send_queues;252 input_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL);253 if (!input_queue_cfg)254 return -ENOMEM;255 256 size = sizeof(*output_queue_cfg) * IPU6_N_MAX_RECV_QUEUES;257 output_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL);258 if (!output_queue_cfg)259 return -ENOMEM;260 261 fwcom->input = input_queue_cfg;262 fwcom->output = output_queue_cfg;263 264 fwcom->num_input_queues = isys_fw_cfg->num_send_queues[type_proxy] +265 isys_fw_cfg->num_send_queues[type_dev] +266 isys_fw_cfg->num_send_queues[type_msg];267 268 fwcom->num_output_queues = isys_fw_cfg->num_recv_queues[type_proxy] +269 isys_fw_cfg->num_recv_queues[type_dev] +270 isys_fw_cfg->num_recv_queues[type_msg];271 272 /* SRAM partitioning. Equal partitioning is set. */273 for (i = 0; i < max_sram_blocks; i++) {274 if (i < num_in_message_queues)275 isys_fw_cfg->buffer_partition.num_gda_pages[i] =276 (IPU6_DEVICE_GDA_NR_PAGES *277 IPU6_DEVICE_GDA_VIRT_FACTOR) /278 num_in_message_queues;279 else280 isys_fw_cfg->buffer_partition.num_gda_pages[i] = 0;281 }282 283 /* FW assumes proxy interface at fwcom queue 0 */284 for (i = 0; i < isys_fw_cfg->num_send_queues[type_proxy]; i++) {285 input_queue_cfg[i].token_size =286 sizeof(struct ipu6_fw_proxy_send_queue_token);287 input_queue_cfg[i].queue_size = IPU6_ISYS_SIZE_PROXY_SEND_QUEUE;288 }289 290 for (i = 0; i < isys_fw_cfg->num_send_queues[type_dev]; i++) {291 input_queue_cfg[base_dev_send + i].token_size =292 sizeof(struct ipu6_fw_send_queue_token);293 input_queue_cfg[base_dev_send + i].queue_size = max_devq_size;294 }295 296 for (i = 0; i < isys_fw_cfg->num_send_queues[type_msg]; i++) {297 input_queue_cfg[base_msg_send + i].token_size =298 sizeof(struct ipu6_fw_send_queue_token);299 input_queue_cfg[base_msg_send + i].queue_size =300 IPU6_ISYS_SIZE_SEND_QUEUE;301 }302 303 for (i = 0; i < isys_fw_cfg->num_recv_queues[type_proxy]; i++) {304 output_queue_cfg[i].token_size =305 sizeof(struct ipu6_fw_proxy_resp_queue_token);306 output_queue_cfg[i].queue_size =307 IPU6_ISYS_SIZE_PROXY_RECV_QUEUE;308 }309 /* There is no recv DEV queue */310 for (i = 0; i < isys_fw_cfg->num_recv_queues[type_msg]; i++) {311 output_queue_cfg[base_msg_recv + i].token_size =312 sizeof(struct ipu6_fw_resp_queue_token);313 output_queue_cfg[base_msg_recv + i].queue_size =314 IPU6_ISYS_SIZE_RECV_QUEUE;315 }316 317 fwcom->dmem_addr = isys->pdata->ipdata->hw_variant.dmem_offset;318 fwcom->specific_addr = isys_fw_cfg;319 fwcom->specific_size = sizeof(*isys_fw_cfg);320 321 return 0;322}323 324int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams)325{326 struct device *dev = &isys->adev->auxdev.dev;327 int retry = IPU6_ISYS_OPEN_RETRY;328 struct ipu6_fw_com_cfg fwcom = {329 .cell_start = start_sp,330 .cell_ready = query_sp,331 .buttress_boot_offset = SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET,332 };333 int ret;334 335 ipu6_isys_fwcom_cfg_init(isys, &fwcom, num_streams);336 337 isys->fwcom = ipu6_fw_com_prepare(&fwcom, isys->adev,338 isys->pdata->base);339 if (!isys->fwcom) {340 dev_err(dev, "isys fw com prepare failed\n");341 return -EIO;342 }343 344 ret = ipu6_fw_com_open(isys->fwcom);345 if (ret) {346 dev_err(dev, "isys fw com open failed %d\n", ret);347 return ret;348 }349 350 do {351 usleep_range(400, 500);352 if (ipu6_fw_com_ready(isys->fwcom))353 break;354 retry--;355 } while (retry > 0);356 357 if (!retry) {358 dev_err(dev, "isys port open ready failed %d\n", ret);359 ipu6_fw_isys_close(isys);360 ret = -EIO;361 }362 363 return ret;364}365 366struct ipu6_fw_isys_resp_info_abi *367ipu6_fw_isys_get_resp(void *context, unsigned int queue)368{369 return ipu6_recv_get_token(context, queue);370}371 372void ipu6_fw_isys_put_resp(void *context, unsigned int queue)373{374 ipu6_recv_put_token(context, queue);375}376 377void ipu6_fw_isys_dump_stream_cfg(struct device *dev,378 struct ipu6_fw_isys_stream_cfg_data_abi *cfg)379{380 unsigned int i;381 382 dev_dbg(dev, "-----------------------------------------------------\n");383 dev_dbg(dev, "IPU6_FW_ISYS_STREAM_CFG_DATA\n");384 385 dev_dbg(dev, "compfmt = %d\n", cfg->vc);386 dev_dbg(dev, "src = %d\n", cfg->src);387 dev_dbg(dev, "vc = %d\n", cfg->vc);388 dev_dbg(dev, "isl_use = %d\n", cfg->isl_use);389 dev_dbg(dev, "sensor_type = %d\n", cfg->sensor_type);390 391 dev_dbg(dev, "send_irq_sof_discarded = %d\n",392 cfg->send_irq_sof_discarded);393 dev_dbg(dev, "send_irq_eof_discarded = %d\n",394 cfg->send_irq_eof_discarded);395 dev_dbg(dev, "send_resp_sof_discarded = %d\n",396 cfg->send_resp_sof_discarded);397 dev_dbg(dev, "send_resp_eof_discarded = %d\n",398 cfg->send_resp_eof_discarded);399 400 dev_dbg(dev, "crop:\n");401 dev_dbg(dev, "\t.left_top = [%d, %d]\n", cfg->crop.left_offset,402 cfg->crop.top_offset);403 dev_dbg(dev, "\t.right_bottom = [%d, %d]\n", cfg->crop.right_offset,404 cfg->crop.bottom_offset);405 406 dev_dbg(dev, "nof_input_pins = %d\n", cfg->nof_input_pins);407 for (i = 0; i < cfg->nof_input_pins; i++) {408 dev_dbg(dev, "input pin[%d]:\n", i);409 dev_dbg(dev, "\t.dt = 0x%0x\n", cfg->input_pins[i].dt);410 dev_dbg(dev, "\t.mipi_store_mode = %d\n",411 cfg->input_pins[i].mipi_store_mode);412 dev_dbg(dev, "\t.bits_per_pix = %d\n",413 cfg->input_pins[i].bits_per_pix);414 dev_dbg(dev, "\t.mapped_dt = 0x%0x\n",415 cfg->input_pins[i].mapped_dt);416 dev_dbg(dev, "\t.input_res = %dx%d\n",417 cfg->input_pins[i].input_res.width,418 cfg->input_pins[i].input_res.height);419 dev_dbg(dev, "\t.mipi_decompression = %d\n",420 cfg->input_pins[i].mipi_decompression);421 dev_dbg(dev, "\t.capture_mode = %d\n",422 cfg->input_pins[i].capture_mode);423 }424 425 dev_dbg(dev, "nof_output_pins = %d\n", cfg->nof_output_pins);426 for (i = 0; i < cfg->nof_output_pins; i++) {427 dev_dbg(dev, "output_pin[%d]:\n", i);428 dev_dbg(dev, "\t.input_pin_id = %d\n",429 cfg->output_pins[i].input_pin_id);430 dev_dbg(dev, "\t.output_res = %dx%d\n",431 cfg->output_pins[i].output_res.width,432 cfg->output_pins[i].output_res.height);433 dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride);434 dev_dbg(dev, "\t.pt = %d\n", cfg->output_pins[i].pt);435 dev_dbg(dev, "\t.payload_buf_size = %d\n",436 cfg->output_pins[i].payload_buf_size);437 dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft);438 dev_dbg(dev, "\t.watermark_in_lines = %d\n",439 cfg->output_pins[i].watermark_in_lines);440 dev_dbg(dev, "\t.send_irq = %d\n",441 cfg->output_pins[i].send_irq);442 dev_dbg(dev, "\t.reserve_compression = %d\n",443 cfg->output_pins[i].reserve_compression);444 dev_dbg(dev, "\t.snoopable = %d\n",445 cfg->output_pins[i].snoopable);446 dev_dbg(dev, "\t.error_handling_enable = %d\n",447 cfg->output_pins[i].error_handling_enable);448 dev_dbg(dev, "\t.sensor_type = %d\n",449 cfg->output_pins[i].sensor_type);450 }451 dev_dbg(dev, "-----------------------------------------------------\n");452}453 454void455ipu6_fw_isys_dump_frame_buff_set(struct device *dev,456 struct ipu6_fw_isys_frame_buff_set_abi *buf,457 unsigned int outputs)458{459 unsigned int i;460 461 dev_dbg(dev, "-----------------------------------------------------\n");462 dev_dbg(dev, "IPU6_FW_ISYS_FRAME_BUFF_SET\n");463 464 for (i = 0; i < outputs; i++) {465 dev_dbg(dev, "output_pin[%d]:\n", i);466 dev_dbg(dev, "\t.out_buf_id = %llu\n",467 buf->output_pins[i].out_buf_id);468 dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr);469 dev_dbg(dev, "\t.compress = %d\n",470 buf->output_pins[i].compress);471 }472 473 dev_dbg(dev, "send_irq_sof = 0x%x\n", buf->send_irq_sof);474 dev_dbg(dev, "send_irq_eof = 0x%x\n", buf->send_irq_eof);475 dev_dbg(dev, "send_resp_sof = 0x%x\n", buf->send_resp_sof);476 dev_dbg(dev, "send_resp_eof = 0x%x\n", buf->send_resp_eof);477 dev_dbg(dev, "send_irq_capture_ack = 0x%x\n",478 buf->send_irq_capture_ack);479 dev_dbg(dev, "send_irq_capture_done = 0x%x\n",480 buf->send_irq_capture_done);481 dev_dbg(dev, "send_resp_capture_ack = 0x%x\n",482 buf->send_resp_capture_ack);483 dev_dbg(dev, "send_resp_capture_done = 0x%x\n",484 buf->send_resp_capture_done);485 486 dev_dbg(dev, "-----------------------------------------------------\n");487}488