491 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c4 *5 * Copyright (c) 2010 Samsung Electronics Co., Ltd.6 * http://www.samsung.com/7 */8 9#include <linux/delay.h>10#include <linux/err.h>11#include <linux/firmware.h>12#include <linux/jiffies.h>13#include <linux/sched.h>14#include "s5p_mfc_cmd.h"15#include "s5p_mfc_common.h"16#include "s5p_mfc_debug.h"17#include "s5p_mfc_intr.h"18#include "s5p_mfc_opr.h"19#include "s5p_mfc_pm.h"20#include "s5p_mfc_ctrl.h"21 22/* Allocate memory for firmware */23int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)24{25 struct s5p_mfc_priv_buf *fw_buf = &dev->fw_buf;26 int err;27 28 fw_buf->size = dev->variant->buf_size->fw;29 30 if (fw_buf->virt) {31 mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n");32 return -ENOMEM;33 }34 35 err = s5p_mfc_alloc_priv_buf(dev, BANK_L_CTX, &dev->fw_buf);36 if (err) {37 mfc_err("Allocating bitprocessor buffer failed\n");38 return err;39 }40 41 return 0;42}43 44/* Load firmware */45int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev)46{47 struct firmware *fw_blob;48 int i, err = -EINVAL;49 50 /* Firmware has to be present as a separate file or compiled51 * into kernel. */52 mfc_debug_enter();53 54 /* In case of MFC v12, RET_SYS_INIT response from hardware fails due to55 * incorrect firmware transfer and therefore it is not able to initialize56 * the hardware. This causes failed response for SYS_INIT command when57 * MFC runs for second time. So, load the MFC v12 firmware for each run.58 */59 if (!IS_MFCV12(dev))60 if (dev->fw_get_done)61 return 0;62 63 for (i = MFC_FW_MAX_VERSIONS - 1; i >= 0; i--) {64 if (!dev->variant->fw_name[i])65 continue;66 err = request_firmware((const struct firmware **)&fw_blob,67 dev->variant->fw_name[i], &dev->plat_dev->dev);68 if (!err) {69 dev->fw_ver = (enum s5p_mfc_fw_ver) i;70 break;71 }72 }73 74 if (err != 0) {75 mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");76 return -EINVAL;77 }78 if (fw_blob->size > dev->fw_buf.size) {79 mfc_err("MFC firmware is too big to be loaded\n");80 release_firmware(fw_blob);81 return -ENOMEM;82 }83 memcpy(dev->fw_buf.virt, fw_blob->data, fw_blob->size);84 wmb();85 dev->fw_get_done = true;86 release_firmware(fw_blob);87 mfc_debug_leave();88 return 0;89}90 91/* Release firmware memory */92int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev)93{94 /* Before calling this function one has to make sure95 * that MFC is no longer processing */96 s5p_mfc_release_priv_buf(dev, &dev->fw_buf);97 dev->fw_get_done = false;98 return 0;99}100 101static int s5p_mfc_bus_reset(struct s5p_mfc_dev *dev)102{103 unsigned int status;104 unsigned long timeout;105 106 /* Reset */107 mfc_write(dev, 0x1, S5P_FIMV_MFC_BUS_RESET_CTRL);108 timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);109 /* Check bus status */110 do {111 if (time_after(jiffies, timeout)) {112 mfc_err("Timeout while resetting MFC.\n");113 return -EIO;114 }115 status = mfc_read(dev, S5P_FIMV_MFC_BUS_RESET_CTRL);116 } while ((status & 0x2) == 0);117 return 0;118}119 120/* Reset the device */121int s5p_mfc_reset(struct s5p_mfc_dev *dev)122{123 unsigned int mc_status;124 unsigned long timeout;125 int i;126 127 mfc_debug_enter();128 129 if (IS_MFCV6_PLUS(dev)) {130 /* Zero Initialization of MFC registers */131 mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD_V6);132 mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD_V6);133 mfc_write(dev, 0, S5P_FIMV_FW_VERSION_V6);134 135 for (i = 0; i < S5P_FIMV_REG_CLEAR_COUNT_V6; i++)136 mfc_write(dev, 0, S5P_FIMV_REG_CLEAR_BEGIN_V6 + (i*4));137 138 /* check bus reset control before reset */139 if (dev->risc_on && !IS_MFCV12(dev))140 if (s5p_mfc_bus_reset(dev))141 return -EIO;142 /* Reset143 * set RISC_ON to 0 during power_on & wake_up.144 * V6 needs RISC_ON set to 0 during reset also.145 */146 if ((!dev->risc_on) || (!IS_MFCV7_PLUS(dev)))147 mfc_write(dev, 0, S5P_FIMV_RISC_ON_V6);148 149 mfc_write(dev, 0x1FFF, S5P_FIMV_MFC_RESET_V6);150 mfc_write(dev, 0, S5P_FIMV_MFC_RESET_V6);151 } else {152 /* Stop procedure */153 /* reset RISC */154 mfc_write(dev, 0x3f6, S5P_FIMV_SW_RESET);155 /* All reset except for MC */156 mfc_write(dev, 0x3e2, S5P_FIMV_SW_RESET);157 mdelay(10);158 159 timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);160 /* Check MC status */161 do {162 if (time_after(jiffies, timeout)) {163 mfc_err("Timeout while resetting MFC\n");164 return -EIO;165 }166 167 mc_status = mfc_read(dev, S5P_FIMV_MC_STATUS);168 169 } while (mc_status & 0x3);170 171 mfc_write(dev, 0x0, S5P_FIMV_SW_RESET);172 mfc_write(dev, 0x3fe, S5P_FIMV_SW_RESET);173 }174 175 mfc_debug_leave();176 return 0;177}178 179static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)180{181 if (IS_MFCV6_PLUS(dev)) {182 mfc_write(dev, dev->dma_base[BANK_L_CTX],183 S5P_FIMV_RISC_BASE_ADDRESS_V6);184 mfc_debug(2, "Base Address : %pad\n",185 &dev->dma_base[BANK_L_CTX]);186 } else {187 mfc_write(dev, dev->dma_base[BANK_L_CTX],188 S5P_FIMV_MC_DRAMBASE_ADR_A);189 mfc_write(dev, dev->dma_base[BANK_R_CTX],190 S5P_FIMV_MC_DRAMBASE_ADR_B);191 mfc_debug(2, "Bank1: %pad, Bank2: %pad\n",192 &dev->dma_base[BANK_L_CTX],193 &dev->dma_base[BANK_R_CTX]);194 }195}196 197static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev)198{199 if (IS_MFCV6_PLUS(dev)) {200 /* Zero initialization should be done before RESET.201 * Nothing to do here. */202 } else {203 mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH0_INST_ID);204 mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH1_INST_ID);205 mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);206 mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD);207 }208}209 210/* Initialize hardware */211int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)212{213 unsigned int ver;214 int ret;215 216 mfc_debug_enter();217 if (!dev->fw_buf.virt) {218 mfc_err("Firmware memory is not allocated.\n");219 return -EINVAL;220 }221 222 /* 0. MFC reset */223 mfc_debug(2, "MFC reset..\n");224 s5p_mfc_clock_on(dev);225 dev->risc_on = 0;226 ret = s5p_mfc_reset(dev);227 if (ret) {228 mfc_err("Failed to reset MFC - timeout\n");229 return ret;230 }231 mfc_debug(2, "Done MFC reset..\n");232 /* 1. Set DRAM base Addr */233 s5p_mfc_init_memctrl(dev);234 /* 2. Initialize registers of channel I/F */235 s5p_mfc_clear_cmds(dev);236 /* 3. Release reset signal to the RISC */237 s5p_mfc_clean_dev_int_flags(dev);238 if (IS_MFCV6_PLUS(dev)) {239 dev->risc_on = 1;240 mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);241 }242 else243 mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);244 245 if (IS_MFCV10_PLUS(dev))246 mfc_write(dev, 0x0, S5P_FIMV_MFC_CLOCK_OFF_V10);247 248 mfc_debug(2, "Will now wait for completion of firmware transfer\n");249 if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_FW_STATUS_RET)) {250 mfc_err("Failed to load firmware\n");251 s5p_mfc_reset(dev);252 s5p_mfc_clock_off(dev);253 return -EIO;254 }255 s5p_mfc_clean_dev_int_flags(dev);256 /* 4. Initialize firmware */257 ret = s5p_mfc_hw_call(dev->mfc_cmds, sys_init_cmd, dev);258 if (ret) {259 mfc_err("Failed to send command to MFC - timeout\n");260 s5p_mfc_reset(dev);261 s5p_mfc_clock_off(dev);262 return ret;263 }264 mfc_debug(2, "Ok, now will wait for completion of hardware init\n");265 if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_SYS_INIT_RET)) {266 mfc_err("Failed to init hardware\n");267 s5p_mfc_reset(dev);268 s5p_mfc_clock_off(dev);269 return -EIO;270 }271 dev->int_cond = 0;272 if (dev->int_err != 0 || dev->int_type !=273 S5P_MFC_R2H_CMD_SYS_INIT_RET) {274 /* Failure. */275 mfc_err("Failed to init firmware - error: %d int: %d\n",276 dev->int_err, dev->int_type);277 s5p_mfc_reset(dev);278 s5p_mfc_clock_off(dev);279 return -EIO;280 }281 if (IS_MFCV6_PLUS(dev))282 ver = mfc_read(dev, S5P_FIMV_FW_VERSION_V6);283 else284 ver = mfc_read(dev, S5P_FIMV_FW_VERSION);285 286 mfc_debug(2, "MFC F/W version : %02xyy, %02xmm, %02xdd\n",287 (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);288 s5p_mfc_clock_off(dev);289 mfc_debug_leave();290 return 0;291}292 293 294/* Deinitialize hardware */295void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev)296{297 s5p_mfc_clock_on(dev);298 299 s5p_mfc_reset(dev);300 s5p_mfc_hw_call(dev->mfc_ops, release_dev_context_buffer, dev);301 302 s5p_mfc_clock_off(dev);303}304 305int s5p_mfc_sleep(struct s5p_mfc_dev *dev)306{307 int ret;308 309 mfc_debug_enter();310 s5p_mfc_clock_on(dev);311 s5p_mfc_clean_dev_int_flags(dev);312 ret = s5p_mfc_hw_call(dev->mfc_cmds, sleep_cmd, dev);313 if (ret) {314 mfc_err("Failed to send command to MFC - timeout\n");315 return ret;316 }317 if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_SLEEP_RET)) {318 mfc_err("Failed to sleep\n");319 return -EIO;320 }321 s5p_mfc_clock_off(dev);322 dev->int_cond = 0;323 if (dev->int_err != 0 || dev->int_type !=324 S5P_MFC_R2H_CMD_SLEEP_RET) {325 /* Failure. */326 mfc_err("Failed to sleep - error: %d int: %d\n", dev->int_err,327 dev->int_type);328 return -EIO;329 }330 mfc_debug_leave();331 return ret;332}333 334static int s5p_mfc_v8_wait_wakeup(struct s5p_mfc_dev *dev)335{336 int ret;337 338 /* Release reset signal to the RISC */339 dev->risc_on = 1;340 mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);341 342 if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_FW_STATUS_RET)) {343 mfc_err("Failed to reset MFCV8\n");344 return -EIO;345 }346 mfc_debug(2, "Write command to wakeup MFCV8\n");347 ret = s5p_mfc_hw_call(dev->mfc_cmds, wakeup_cmd, dev);348 if (ret) {349 mfc_err("Failed to send command to MFCV8 - timeout\n");350 return ret;351 }352 353 if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_WAKEUP_RET)) {354 mfc_err("Failed to wakeup MFC\n");355 return -EIO;356 }357 return ret;358}359 360static int s5p_mfc_wait_wakeup(struct s5p_mfc_dev *dev)361{362 int ret;363 364 /* Send MFC wakeup command */365 ret = s5p_mfc_hw_call(dev->mfc_cmds, wakeup_cmd, dev);366 if (ret) {367 mfc_err("Failed to send command to MFC - timeout\n");368 return ret;369 }370 371 /* Release reset signal to the RISC */372 if (IS_MFCV6_PLUS(dev)) {373 dev->risc_on = 1;374 mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);375 } else {376 mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);377 }378 379 if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_WAKEUP_RET)) {380 mfc_err("Failed to wakeup MFC\n");381 return -EIO;382 }383 return ret;384}385 386int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)387{388 int ret;389 390 mfc_debug_enter();391 /* 0. MFC reset */392 mfc_debug(2, "MFC reset..\n");393 s5p_mfc_clock_on(dev);394 dev->risc_on = 0;395 ret = s5p_mfc_reset(dev);396 if (ret) {397 mfc_err("Failed to reset MFC - timeout\n");398 s5p_mfc_clock_off(dev);399 return ret;400 }401 mfc_debug(2, "Done MFC reset..\n");402 /* 1. Set DRAM base Addr */403 s5p_mfc_init_memctrl(dev);404 /* 2. Initialize registers of channel I/F */405 s5p_mfc_clear_cmds(dev);406 s5p_mfc_clean_dev_int_flags(dev);407 /* 3. Send MFC wakeup command and wait for completion*/408 if (IS_MFCV8_PLUS(dev))409 ret = s5p_mfc_v8_wait_wakeup(dev);410 else411 ret = s5p_mfc_wait_wakeup(dev);412 413 s5p_mfc_clock_off(dev);414 if (ret)415 return ret;416 417 dev->int_cond = 0;418 if (dev->int_err != 0 || dev->int_type !=419 S5P_MFC_R2H_CMD_WAKEUP_RET) {420 /* Failure. */421 mfc_err("Failed to wakeup - error: %d int: %d\n", dev->int_err,422 dev->int_type);423 return -EIO;424 }425 mfc_debug_leave();426 return 0;427}428 429int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)430{431 int ret = 0;432 433 ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, ctx);434 if (ret) {435 mfc_err("Failed allocating instance buffer\n");436 goto err;437 }438 439 if (ctx->type == MFCINST_DECODER) {440 ret = s5p_mfc_hw_call(dev->mfc_ops,441 alloc_dec_temp_buffers, ctx);442 if (ret) {443 mfc_err("Failed allocating temporary buffers\n");444 goto err_free_inst_buf;445 }446 }447 448 set_work_bit_irqsave(ctx);449 s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);450 if (s5p_mfc_wait_for_done_ctx(ctx,451 S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) {452 /* Error or timeout */453 mfc_err("Error getting instance from hardware\n");454 ret = -EIO;455 goto err_free_desc_buf;456 }457 458 mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);459 return ret;460 461err_free_desc_buf:462 if (ctx->type == MFCINST_DECODER)463 s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);464err_free_inst_buf:465 s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);466err:467 return ret;468}469 470void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)471{472 ctx->state = MFCINST_RETURN_INST;473 set_work_bit_irqsave(ctx);474 s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);475 /* Wait until instance is returned or timeout occurred */476 if (s5p_mfc_wait_for_done_ctx(ctx,477 S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0)){478 clear_work_bit_irqsave(ctx);479 mfc_err("Err returning instance\n");480 }481 482 /* Free resources */483 s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);484 s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);485 if (ctx->type == MFCINST_DECODER)486 s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);487 488 ctx->inst_no = MFC_NO_INSTANCE_SET;489 ctx->state = MFCINST_FREE;490}491