linux/drivers/media/platform/qcom/venus/firmware.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2017 Linaro Ltd.
   4 */
   5
   6#include <linux/device.h>
   7#include <linux/firmware.h>
   8#include <linux/kernel.h>
   9#include <linux/iommu.h>
  10#include <linux/io.h>
  11#include <linux/of.h>
  12#include <linux/of_address.h>
  13#include <linux/platform_device.h>
  14#include <linux/of_device.h>
  15#include <linux/qcom_scm.h>
  16#include <linux/sizes.h>
  17#include <linux/soc/qcom/mdt_loader.h>
  18
  19#include "core.h"
  20#include "firmware.h"
  21#include "hfi_venus_io.h"
  22
  23#define VENUS_PAS_ID                    9
  24#define VENUS_FW_MEM_SIZE               (6 * SZ_1M)
  25#define VENUS_FW_START_ADDR             0x0
  26
  27static void venus_reset_cpu(struct venus_core *core)
  28{
  29        u32 fw_size = core->fw.mapped_mem_size;
  30        void __iomem *base = core->base;
  31
  32        writel(0, base + WRAPPER_FW_START_ADDR);
  33        writel(fw_size, base + WRAPPER_FW_END_ADDR);
  34        writel(0, base + WRAPPER_CPA_START_ADDR);
  35        writel(fw_size, base + WRAPPER_CPA_END_ADDR);
  36        writel(fw_size, base + WRAPPER_NONPIX_START_ADDR);
  37        writel(fw_size, base + WRAPPER_NONPIX_END_ADDR);
  38        writel(0x0, base + WRAPPER_CPU_CGC_DIS);
  39        writel(0x0, base + WRAPPER_CPU_CLOCK_CONFIG);
  40
  41        /* Bring ARM9 out of reset */
  42        writel(0, base + WRAPPER_A9SS_SW_RESET);
  43}
  44
  45int venus_set_hw_state(struct venus_core *core, bool resume)
  46{
  47        if (core->use_tz)
  48                return qcom_scm_set_remote_state(resume, 0);
  49
  50        if (resume)
  51                venus_reset_cpu(core);
  52        else
  53                writel(1, core->base + WRAPPER_A9SS_SW_RESET);
  54
  55        return 0;
  56}
  57
  58static int venus_load_fw(struct venus_core *core, const char *fwname,
  59                         phys_addr_t *mem_phys, size_t *mem_size)
  60{
  61        const struct firmware *mdt;
  62        struct device_node *node;
  63        struct device *dev;
  64        struct resource r;
  65        ssize_t fw_size;
  66        void *mem_va;
  67        int ret;
  68
  69        *mem_phys = 0;
  70        *mem_size = 0;
  71
  72        dev = core->dev;
  73        node = of_parse_phandle(dev->of_node, "memory-region", 0);
  74        if (!node) {
  75                dev_err(dev, "no memory-region specified\n");
  76                return -EINVAL;
  77        }
  78
  79        ret = of_address_to_resource(node, 0, &r);
  80        if (ret)
  81                goto err_put_node;
  82
  83        ret = request_firmware(&mdt, fwname, dev);
  84        if (ret < 0)
  85                goto err_put_node;
  86
  87        fw_size = qcom_mdt_get_size(mdt);
  88        if (fw_size < 0) {
  89                ret = fw_size;
  90                goto err_release_fw;
  91        }
  92
  93        *mem_phys = r.start;
  94        *mem_size = resource_size(&r);
  95
  96        if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) {
  97                ret = -EINVAL;
  98                goto err_release_fw;
  99        }
 100
 101        mem_va = memremap(r.start, *mem_size, MEMREMAP_WC);
 102        if (!mem_va) {
 103                dev_err(dev, "unable to map memory region: %pa+%zx\n",
 104                        &r.start, *mem_size);
 105                ret = -ENOMEM;
 106                goto err_release_fw;
 107        }
 108
 109        if (core->use_tz)
 110                ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID,
 111                                    mem_va, *mem_phys, *mem_size, NULL);
 112        else
 113                ret = qcom_mdt_load_no_init(dev, mdt, fwname, VENUS_PAS_ID,
 114                                            mem_va, *mem_phys, *mem_size, NULL);
 115
 116        memunmap(mem_va);
 117err_release_fw:
 118        release_firmware(mdt);
 119err_put_node:
 120        of_node_put(node);
 121        return ret;
 122}
 123
 124static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys,
 125                            size_t mem_size)
 126{
 127        struct iommu_domain *iommu;
 128        struct device *dev;
 129        int ret;
 130
 131        dev = core->fw.dev;
 132        if (!dev)
 133                return -EPROBE_DEFER;
 134
 135        iommu = core->fw.iommu_domain;
 136        core->fw.mapped_mem_size = mem_size;
 137
 138        ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size,
 139                        IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV);
 140        if (ret) {
 141                dev_err(dev, "could not map video firmware region\n");
 142                return ret;
 143        }
 144
 145        venus_reset_cpu(core);
 146
 147        return 0;
 148}
 149
 150static int venus_shutdown_no_tz(struct venus_core *core)
 151{
 152        const size_t mapped = core->fw.mapped_mem_size;
 153        struct iommu_domain *iommu;
 154        size_t unmapped;
 155        u32 reg;
 156        struct device *dev = core->fw.dev;
 157        void __iomem *base = core->base;
 158
 159        /* Assert the reset to ARM9 */
 160        reg = readl_relaxed(base + WRAPPER_A9SS_SW_RESET);
 161        reg |= WRAPPER_A9SS_SW_RESET_BIT;
 162        writel_relaxed(reg, base + WRAPPER_A9SS_SW_RESET);
 163
 164        /* Make sure reset is asserted before the mapping is removed */
 165        mb();
 166
 167        iommu = core->fw.iommu_domain;
 168
 169        unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped);
 170        if (unmapped != mapped)
 171                dev_err(dev, "failed to unmap firmware\n");
 172
 173        return 0;
 174}
 175
 176int venus_boot(struct venus_core *core)
 177{
 178        struct device *dev = core->dev;
 179        phys_addr_t mem_phys;
 180        size_t mem_size;
 181        int ret;
 182
 183        if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ||
 184            (core->use_tz && !qcom_scm_is_available()))
 185                return -EPROBE_DEFER;
 186
 187        ret = venus_load_fw(core, core->res->fwname, &mem_phys, &mem_size);
 188        if (ret) {
 189                dev_err(dev, "fail to load video firmware\n");
 190                return -EINVAL;
 191        }
 192
 193        if (core->use_tz)
 194                ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
 195        else
 196                ret = venus_boot_no_tz(core, mem_phys, mem_size);
 197
 198        return ret;
 199}
 200
 201int venus_shutdown(struct venus_core *core)
 202{
 203        int ret;
 204
 205        if (core->use_tz)
 206                ret = qcom_scm_pas_shutdown(VENUS_PAS_ID);
 207        else
 208                ret = venus_shutdown_no_tz(core);
 209
 210        return ret;
 211}
 212
 213int venus_firmware_init(struct venus_core *core)
 214{
 215        struct platform_device_info info;
 216        struct iommu_domain *iommu_dom;
 217        struct platform_device *pdev;
 218        struct device_node *np;
 219        int ret;
 220
 221        np = of_get_child_by_name(core->dev->of_node, "video-firmware");
 222        if (!np) {
 223                core->use_tz = true;
 224                return 0;
 225        }
 226
 227        memset(&info, 0, sizeof(info));
 228        info.fwnode = &np->fwnode;
 229        info.parent = core->dev;
 230        info.name = np->name;
 231        info.dma_mask = DMA_BIT_MASK(32);
 232
 233        pdev = platform_device_register_full(&info);
 234        if (IS_ERR(pdev)) {
 235                of_node_put(np);
 236                return PTR_ERR(pdev);
 237        }
 238
 239        pdev->dev.of_node = np;
 240
 241        ret = of_dma_configure(&pdev->dev, np, true);
 242        if (ret) {
 243                dev_err(core->dev, "dma configure fail\n");
 244                goto err_unregister;
 245        }
 246
 247        core->fw.dev = &pdev->dev;
 248
 249        iommu_dom = iommu_domain_alloc(&platform_bus_type);
 250        if (!iommu_dom) {
 251                dev_err(core->fw.dev, "Failed to allocate iommu domain\n");
 252                ret = -ENOMEM;
 253                goto err_unregister;
 254        }
 255
 256        ret = iommu_attach_device(iommu_dom, core->fw.dev);
 257        if (ret) {
 258                dev_err(core->fw.dev, "could not attach device\n");
 259                goto err_iommu_free;
 260        }
 261
 262        core->fw.iommu_domain = iommu_dom;
 263
 264        of_node_put(np);
 265
 266        return 0;
 267
 268err_iommu_free:
 269        iommu_domain_free(iommu_dom);
 270err_unregister:
 271        platform_device_unregister(pdev);
 272        of_node_put(np);
 273        return ret;
 274}
 275
 276void venus_firmware_deinit(struct venus_core *core)
 277{
 278        struct iommu_domain *iommu;
 279
 280        if (!core->fw.dev)
 281                return;
 282
 283        iommu = core->fw.iommu_domain;
 284
 285        iommu_detach_device(iommu, core->fw.dev);
 286        iommu_domain_free(iommu);
 287
 288        platform_device_unregister(to_platform_device(core->fw.dev));
 289}
 290