| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (c) 2016 MediaTek Inc. |
|---|
| 3 | 4 | * Author: Andrew-CT Chen <andrew-ct.chen@mediatek.com> |
|---|
| 4 | | -* |
|---|
| 5 | | -* This program is free software; you can redistribute it and/or modify |
|---|
| 6 | | -* it under the terms of the GNU General Public License version 2 as |
|---|
| 7 | | -* published by the Free Software Foundation. |
|---|
| 8 | | -* |
|---|
| 9 | | -* This program is distributed in the hope that it will be useful, |
|---|
| 10 | | -* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 11 | | -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 12 | | -* GNU General Public License for more details. |
|---|
| 13 | 5 | */ |
|---|
| 14 | 6 | #include <linux/clk.h> |
|---|
| 15 | 7 | #include <linux/debugfs.h> |
|---|
| .. | .. |
|---|
| 54 | 46 | /* binary firmware name */ |
|---|
| 55 | 47 | #define VPU_P_FW "vpu_p.bin" |
|---|
| 56 | 48 | #define VPU_D_FW "vpu_d.bin" |
|---|
| 49 | +#define VPU_P_FW_NEW "mediatek/mt8173/vpu_p.bin" |
|---|
| 50 | +#define VPU_D_FW_NEW "mediatek/mt8173/vpu_d.bin" |
|---|
| 57 | 51 | |
|---|
| 58 | 52 | #define VPU_RESET 0x0 |
|---|
| 59 | 53 | #define VPU_TCM_CFG 0x0008 |
|---|
| .. | .. |
|---|
| 211 | 205 | struct vpu_run run; |
|---|
| 212 | 206 | struct vpu_wdt wdt; |
|---|
| 213 | 207 | struct vpu_ipi_desc ipi_desc[IPI_MAX]; |
|---|
| 214 | | - struct share_obj *recv_buf; |
|---|
| 215 | | - struct share_obj *send_buf; |
|---|
| 208 | + struct share_obj __iomem *recv_buf; |
|---|
| 209 | + struct share_obj __iomem *send_buf; |
|---|
| 216 | 210 | struct device *dev; |
|---|
| 217 | 211 | struct clk *clk; |
|---|
| 218 | 212 | bool fw_loaded; |
|---|
| .. | .. |
|---|
| 281 | 275 | return -EPROBE_DEFER; |
|---|
| 282 | 276 | } |
|---|
| 283 | 277 | |
|---|
| 284 | | - if (id >= 0 && id < IPI_MAX && handler) { |
|---|
| 278 | + if (id < IPI_MAX && handler) { |
|---|
| 285 | 279 | ipi_desc = vpu->ipi_desc; |
|---|
| 286 | 280 | ipi_desc[id].name = name; |
|---|
| 287 | 281 | ipi_desc[id].handler = handler; |
|---|
| .. | .. |
|---|
| 300 | 294 | unsigned int len) |
|---|
| 301 | 295 | { |
|---|
| 302 | 296 | struct mtk_vpu *vpu = platform_get_drvdata(pdev); |
|---|
| 303 | | - struct share_obj *send_obj = vpu->send_buf; |
|---|
| 297 | + struct share_obj __iomem *send_obj = vpu->send_buf; |
|---|
| 304 | 298 | unsigned long timeout; |
|---|
| 305 | 299 | int ret = 0; |
|---|
| 306 | 300 | |
|---|
| .. | .. |
|---|
| 333 | 327 | } |
|---|
| 334 | 328 | } while (vpu_cfg_readl(vpu, HOST_TO_VPU)); |
|---|
| 335 | 329 | |
|---|
| 336 | | - memcpy((void *)send_obj->share_buf, buf, len); |
|---|
| 337 | | - send_obj->len = len; |
|---|
| 338 | | - send_obj->id = id; |
|---|
| 330 | + memcpy_toio(send_obj->share_buf, buf, len); |
|---|
| 331 | + writel(len, &send_obj->len); |
|---|
| 332 | + writel(id, &send_obj->id); |
|---|
| 339 | 333 | |
|---|
| 340 | 334 | vpu->ipi_id_ack[id] = false; |
|---|
| 341 | 335 | /* send the command to VPU */ |
|---|
| .. | .. |
|---|
| 406 | 400 | |
|---|
| 407 | 401 | handler = vpu->wdt.handler; |
|---|
| 408 | 402 | |
|---|
| 409 | | - if (id >= 0 && id < VPU_RST_MAX && wdt_reset) { |
|---|
| 403 | + if (id < VPU_RST_MAX && wdt_reset) { |
|---|
| 410 | 404 | dev_dbg(vpu->dev, "wdt register id %d\n", id); |
|---|
| 411 | 405 | mutex_lock(&vpu->vpu_mutex); |
|---|
| 412 | 406 | handler[id].reset_func = wdt_reset; |
|---|
| .. | .. |
|---|
| 468 | 462 | } |
|---|
| 469 | 463 | |
|---|
| 470 | 464 | vpu_pdev = of_find_device_by_node(vpu_node); |
|---|
| 465 | + of_node_put(vpu_node); |
|---|
| 471 | 466 | if (WARN_ON(!vpu_pdev)) { |
|---|
| 472 | 467 | dev_err(dev, "vpu pdev failed\n"); |
|---|
| 473 | | - of_node_put(vpu_node); |
|---|
| 474 | 468 | return NULL; |
|---|
| 475 | 469 | } |
|---|
| 476 | 470 | |
|---|
| .. | .. |
|---|
| 480 | 474 | |
|---|
| 481 | 475 | /* load vpu program/data memory */ |
|---|
| 482 | 476 | static int load_requested_vpu(struct mtk_vpu *vpu, |
|---|
| 483 | | - const struct firmware *vpu_fw, |
|---|
| 484 | 477 | u8 fw_type) |
|---|
| 485 | 478 | { |
|---|
| 486 | 479 | size_t tcm_size = fw_type ? VPU_DTCM_SIZE : VPU_PTCM_SIZE; |
|---|
| 487 | 480 | size_t fw_size = fw_type ? VPU_D_FW_SIZE : VPU_P_FW_SIZE; |
|---|
| 488 | 481 | char *fw_name = fw_type ? VPU_D_FW : VPU_P_FW; |
|---|
| 482 | + char *fw_new_name = fw_type ? VPU_D_FW_NEW : VPU_P_FW_NEW; |
|---|
| 483 | + const struct firmware *vpu_fw; |
|---|
| 489 | 484 | size_t dl_size = 0; |
|---|
| 490 | 485 | size_t extra_fw_size = 0; |
|---|
| 491 | 486 | void *dest; |
|---|
| 492 | 487 | int ret; |
|---|
| 493 | 488 | |
|---|
| 494 | | - ret = request_firmware(&vpu_fw, fw_name, vpu->dev); |
|---|
| 489 | + ret = request_firmware(&vpu_fw, fw_new_name, vpu->dev); |
|---|
| 495 | 490 | if (ret < 0) { |
|---|
| 496 | | - dev_err(vpu->dev, "Failed to load %s, %d\n", fw_name, ret); |
|---|
| 497 | | - return ret; |
|---|
| 491 | + dev_info(vpu->dev, "Failed to load %s, %d, retry\n", |
|---|
| 492 | + fw_new_name, ret); |
|---|
| 493 | + |
|---|
| 494 | + ret = request_firmware(&vpu_fw, fw_name, vpu->dev); |
|---|
| 495 | + if (ret < 0) { |
|---|
| 496 | + dev_err(vpu->dev, "Failed to load %s, %d\n", fw_name, |
|---|
| 497 | + ret); |
|---|
| 498 | + return ret; |
|---|
| 499 | + } |
|---|
| 498 | 500 | } |
|---|
| 499 | 501 | dl_size = vpu_fw->size; |
|---|
| 500 | 502 | if (dl_size > fw_size) { |
|---|
| .. | .. |
|---|
| 537 | 539 | int vpu_load_firmware(struct platform_device *pdev) |
|---|
| 538 | 540 | { |
|---|
| 539 | 541 | struct mtk_vpu *vpu; |
|---|
| 540 | | - struct device *dev = &pdev->dev; |
|---|
| 542 | + struct device *dev; |
|---|
| 541 | 543 | struct vpu_run *run; |
|---|
| 542 | | - const struct firmware *vpu_fw = NULL; |
|---|
| 543 | 544 | int ret; |
|---|
| 544 | 545 | |
|---|
| 545 | 546 | if (!pdev) { |
|---|
| 546 | | - dev_err(dev, "VPU platform device is invalid\n"); |
|---|
| 547 | + pr_err("VPU platform device is invalid\n"); |
|---|
| 547 | 548 | return -EINVAL; |
|---|
| 548 | 549 | } |
|---|
| 550 | + |
|---|
| 551 | + dev = &pdev->dev; |
|---|
| 549 | 552 | |
|---|
| 550 | 553 | vpu = platform_get_drvdata(pdev); |
|---|
| 551 | 554 | run = &vpu->run; |
|---|
| .. | .. |
|---|
| 568 | 571 | run->signaled = false; |
|---|
| 569 | 572 | dev_dbg(vpu->dev, "firmware request\n"); |
|---|
| 570 | 573 | /* Downloading program firmware to device*/ |
|---|
| 571 | | - ret = load_requested_vpu(vpu, vpu_fw, P_FW); |
|---|
| 574 | + ret = load_requested_vpu(vpu, P_FW); |
|---|
| 572 | 575 | if (ret < 0) { |
|---|
| 573 | 576 | dev_err(dev, "Failed to request %s, %d\n", VPU_P_FW, ret); |
|---|
| 574 | 577 | goto OUT_LOAD_FW; |
|---|
| 575 | 578 | } |
|---|
| 576 | 579 | |
|---|
| 577 | 580 | /* Downloading data firmware to device */ |
|---|
| 578 | | - ret = load_requested_vpu(vpu, vpu_fw, D_FW); |
|---|
| 581 | + ret = load_requested_vpu(vpu, D_FW); |
|---|
| 579 | 582 | if (ret < 0) { |
|---|
| 580 | 583 | dev_err(dev, "Failed to request %s, %d\n", VPU_D_FW, ret); |
|---|
| 581 | 584 | goto OUT_LOAD_FW; |
|---|
| .. | .. |
|---|
| 609 | 612 | } |
|---|
| 610 | 613 | EXPORT_SYMBOL_GPL(vpu_load_firmware); |
|---|
| 611 | 614 | |
|---|
| 612 | | -static void vpu_init_ipi_handler(void *data, unsigned int len, void *priv) |
|---|
| 615 | +static void vpu_init_ipi_handler(const void *data, unsigned int len, void *priv) |
|---|
| 613 | 616 | { |
|---|
| 614 | | - struct mtk_vpu *vpu = (struct mtk_vpu *)priv; |
|---|
| 615 | | - struct vpu_run *run = (struct vpu_run *)data; |
|---|
| 617 | + struct mtk_vpu *vpu = priv; |
|---|
| 618 | + const struct vpu_run *run = data; |
|---|
| 616 | 619 | |
|---|
| 617 | 620 | vpu->run.signaled = run->signaled; |
|---|
| 618 | | - strncpy(vpu->run.fw_ver, run->fw_ver, VPU_FW_VER_LEN); |
|---|
| 621 | + strscpy(vpu->run.fw_ver, run->fw_ver, sizeof(vpu->run.fw_ver)); |
|---|
| 619 | 622 | vpu->run.dec_capability = run->dec_capability; |
|---|
| 620 | 623 | vpu->run.enc_capability = run->enc_capability; |
|---|
| 621 | 624 | wake_up_interruptible(&vpu->run.wq); |
|---|
| .. | .. |
|---|
| 709 | 712 | |
|---|
| 710 | 713 | static void vpu_ipi_handler(struct mtk_vpu *vpu) |
|---|
| 711 | 714 | { |
|---|
| 712 | | - struct share_obj *rcv_obj = vpu->recv_buf; |
|---|
| 715 | + struct share_obj __iomem *rcv_obj = vpu->recv_buf; |
|---|
| 713 | 716 | struct vpu_ipi_desc *ipi_desc = vpu->ipi_desc; |
|---|
| 717 | + unsigned char data[SHARE_BUF_SIZE]; |
|---|
| 718 | + s32 id = readl(&rcv_obj->id); |
|---|
| 714 | 719 | |
|---|
| 715 | | - if (rcv_obj->id < IPI_MAX && ipi_desc[rcv_obj->id].handler) { |
|---|
| 716 | | - ipi_desc[rcv_obj->id].handler(rcv_obj->share_buf, |
|---|
| 717 | | - rcv_obj->len, |
|---|
| 718 | | - ipi_desc[rcv_obj->id].priv); |
|---|
| 719 | | - if (rcv_obj->id > IPI_VPU_INIT) { |
|---|
| 720 | | - vpu->ipi_id_ack[rcv_obj->id] = true; |
|---|
| 720 | + memcpy_fromio(data, rcv_obj->share_buf, sizeof(data)); |
|---|
| 721 | + if (id < IPI_MAX && ipi_desc[id].handler) { |
|---|
| 722 | + ipi_desc[id].handler(data, readl(&rcv_obj->len), |
|---|
| 723 | + ipi_desc[id].priv); |
|---|
| 724 | + if (id > IPI_VPU_INIT) { |
|---|
| 725 | + vpu->ipi_id_ack[id] = true; |
|---|
| 721 | 726 | wake_up(&vpu->ack_wq); |
|---|
| 722 | 727 | } |
|---|
| 723 | 728 | } else { |
|---|
| 724 | | - dev_err(vpu->dev, "No such ipi id = %d\n", rcv_obj->id); |
|---|
| 729 | + dev_err(vpu->dev, "No such ipi id = %d\n", id); |
|---|
| 725 | 730 | } |
|---|
| 726 | 731 | } |
|---|
| 727 | 732 | |
|---|
| .. | .. |
|---|
| 731 | 736 | vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST); |
|---|
| 732 | 737 | |
|---|
| 733 | 738 | /* shared buffer initialization */ |
|---|
| 734 | | - vpu->recv_buf = (__force struct share_obj *)(vpu->reg.tcm + |
|---|
| 735 | | - VPU_DTCM_OFFSET); |
|---|
| 739 | + vpu->recv_buf = vpu->reg.tcm + VPU_DTCM_OFFSET; |
|---|
| 736 | 740 | vpu->send_buf = vpu->recv_buf + 1; |
|---|
| 737 | | - memset(vpu->recv_buf, 0, sizeof(struct share_obj)); |
|---|
| 738 | | - memset(vpu->send_buf, 0, sizeof(struct share_obj)); |
|---|
| 741 | + memset_io(vpu->recv_buf, 0, sizeof(struct share_obj)); |
|---|
| 742 | + memset_io(vpu->send_buf, 0, sizeof(struct share_obj)); |
|---|
| 739 | 743 | |
|---|
| 740 | 744 | return 0; |
|---|
| 741 | 745 | } |
|---|
| .. | .. |
|---|
| 848 | 852 | #ifdef CONFIG_DEBUG_FS |
|---|
| 849 | 853 | vpu_debugfs = debugfs_create_file("mtk_vpu", S_IRUGO, NULL, (void *)dev, |
|---|
| 850 | 854 | &vpu_debug_fops); |
|---|
| 851 | | - if (!vpu_debugfs) { |
|---|
| 852 | | - ret = -ENOMEM; |
|---|
| 853 | | - goto cleanup_ipi; |
|---|
| 854 | | - } |
|---|
| 855 | 855 | #endif |
|---|
| 856 | 856 | |
|---|
| 857 | 857 | /* Set PTCM to 96K and DTCM to 32K */ |
|---|
| 858 | 858 | vpu_cfg_writel(vpu, 0x2, VPU_TCM_CFG); |
|---|
| 859 | 859 | |
|---|
| 860 | | - vpu->enable_4GB = !!(totalram_pages > (SZ_2G >> PAGE_SHIFT)); |
|---|
| 860 | + vpu->enable_4GB = !!(totalram_pages() > (SZ_2G >> PAGE_SHIFT)); |
|---|
| 861 | 861 | dev_info(dev, "4GB mode %u\n", vpu->enable_4GB); |
|---|
| 862 | 862 | |
|---|
| 863 | 863 | if (vpu->enable_4GB) { |
|---|
| .. | .. |
|---|
| 909 | 909 | of_reserved_mem_device_release(dev); |
|---|
| 910 | 910 | #ifdef CONFIG_DEBUG_FS |
|---|
| 911 | 911 | debugfs_remove(vpu_debugfs); |
|---|
| 912 | | -cleanup_ipi: |
|---|
| 913 | 912 | #endif |
|---|
| 914 | 913 | memset(vpu->ipi_desc, 0, sizeof(struct vpu_ipi_desc) * IPI_MAX); |
|---|
| 915 | 914 | vpu_mutex_destroy: |
|---|