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 *wrapper_base = core->wrapper_base;
  31
  32        writel(0, wrapper_base + WRAPPER_FW_START_ADDR);
  33        writel(fw_size, wrapper_base + WRAPPER_FW_END_ADDR);
  34        writel(0, wrapper_base + WRAPPER_CPA_START_ADDR);
  35        writel(fw_size, wrapper_base + WRAPPER_CPA_END_ADDR);
  36        writel(fw_size, wrapper_base + WRAPPER_NONPIX_START_ADDR);
  37        writel(fw_size, wrapper_base + WRAPPER_NONPIX_END_ADDR);
  38        writel(0x0, wrapper_base + WRAPPER_CPU_CGC_DIS);
  39        writel(0x0, wrapper_base + WRAPPER_CPU_CLOCK_CONFIG);
  40
  41        /* Bring ARM9 out of reset */
  42        writel(0, wrapper_base + WRAPPER_A9SS_SW_RESET);
  43}
  44
  45int venus_set_hw_state(struct venus_core *core, bool resume)
  46{
  47        int ret;
  48
  49        if (core->use_tz) {
  50                ret = qcom_scm_set_remote_state(resume, 0);
  51                if (resume && ret == -EINVAL)
  52                        ret = 0;
  53                return ret;
  54        }
  55
  56        if (resume) {
  57                venus_reset_cpu(core);
  58        } else {
  59                if (!IS_V6(core))
  60                        writel(1, core->wrapper_base + WRAPPER_A9SS_SW_RESET);
  61        }
  62
  63        return 0;
  64}
  65
  66static int venus_load_fw(struct venus_core *core, const char *fwname,
  67                         phys_addr_t *mem_phys, size_t *mem_size)
  68{
  69        const struct firmware *mdt;
  70        struct device_node *node;
  71        struct device *dev;
  72        struct resource r;
  73        ssize_t fw_size;
  74        void *mem_va;
  75        int ret;
  76
  77        *mem_phys = 0;
  78        *mem_size = 0;
  79
  80        dev = core->dev;
  81        node = of_parse_phandle(dev->of_node, "memory-region", 0);
  82        if (!node) {
  83                dev_err(dev, "no memory-region specified\n");
  84                return -EINVAL;
  85        }
  86
  87        ret = of_address_to_resource(node, 0, &r);
  88        if (ret)
  89                goto err_put_node;
  90
  91        ret = request_firmware(&mdt, fwname, dev);
  92        if (ret < 0)
  93                goto err_put_node;
  94
  95        fw_size = qcom_mdt_get_size(mdt);
  96        if (fw_size < 0) {
  97                ret = fw_size;
  98                goto err_release_fw;
  99        }
 100
 101        *mem_phys = r.start;
 102        *mem_size = resource_size(&r);
 103
 104        if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) {
 105                ret = -EINVAL;
 106                goto err_release_fw;
 107        }
 108
 109        mem_va = memremap(r.start, *mem_size, MEMREMAP_WC);
 110        if (!mem_va) {
 111                dev_err(dev, "unable to map memory region: %pR\n", &r);
 112                ret = -ENOMEM;
 113                goto err_release_fw;
 114        }
 115
 116        if (core->use_tz)
 117                ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID,
 118                                    mem_va, *mem_phys, *mem_size, NULL);
 119        else
 120                ret = qcom_mdt_load_no_init(dev, mdt, fwname, VENUS_PAS_ID,
 121                                            mem_va, *mem_phys, *mem_size, NULL);
 122
 123        memunmap(mem_va);
 124err_release_fw:
 125        release_firmware(mdt);
 126err_put_node:
 127        of_node_put(node);
 128        return ret;
 129}
 130
 131static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys,
 132                            size_t mem_size)
 133{
 134        struct iommu_domain *iommu;
 135        struct device *dev;
 136        int ret;
 137
 138        dev = core->fw.dev;
 139        if (!dev)
 140                return -EPROBE_DEFER;
 141
 142        iommu = core->fw.iommu_domain;
 143        core->fw.mapped_mem_size = mem_size;
 144
 145        ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size,
 146                        IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV);
 147        if (ret) {
 148                dev_err(dev, "could not map video firmware region\n");
 149                return ret;
 150        }
 151
 152        venus_reset_cpu(core);
 153
 154        return 0;
 155}
 156
 157static int venus_shutdown_no_tz(struct venus_core *core)
 158{
 159        const size_t mapped = core->fw.mapped_mem_size;
 160        struct iommu_domain *iommu;
 161        size_t unmapped;
 162        u32 reg;
 163        struct device *dev = core->fw.dev;
 164        void __iomem *wrapper_base = core->wrapper_base;
 165
 166        /* Assert the reset to ARM9 */
 167        reg = readl_relaxed(wrapper_base + WRAPPER_A9SS_SW_RESET);
 168        reg |= WRAPPER_A9SS_SW_RESET_BIT;
 169        writel_relaxed(reg, wrapper_base + WRAPPER_A9SS_SW_RESET);
 170
 171        /* Make sure reset is asserted before the mapping is removed */
 172        mb();
 173
 174        iommu = core->fw.iommu_domain;
 175
 176        if (core->fw.mapped_mem_size && iommu) {
 177                unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped);
 178
 179                if (unmapped != mapped)
 180                        dev_err(dev, "failed to unmap firmware\n");
 181                else
 182                        core->fw.mapped_mem_size = 0;
 183        }
 184
 185        return 0;
 186}
 187
 188int venus_boot(struct venus_core *core)
 189{
 190        struct device *dev = core->dev;
 191        const struct venus_resources *res = core->res;
 192        const char *fwpath = NULL;
 193        phys_addr_t mem_phys;
 194        size_t mem_size;
 195        int ret;
 196
 197        if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ||
 198            (core->use_tz && !qcom_scm_is_available()))
 199                return -EPROBE_DEFER;
 200
 201        ret = of_property_read_string_index(dev->of_node, "firmware-name", 0,
 202                                            &fwpath);
 203        if (ret)
 204                fwpath = core->res->fwname;
 205
 206        ret = venus_load_fw(core, fwpath, &mem_phys, &mem_size);
 207        if (ret) {
 208                dev_err(dev, "fail to load video firmware\n");
 209                return -EINVAL;
 210        }
 211
 212        core->fw.mem_size = mem_size;
 213        core->fw.mem_phys = mem_phys;
 214
 215        if (core->use_tz)
 216                ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
 217        else
 218                ret = venus_boot_no_tz(core, mem_phys, mem_size);
 219
 220        if (ret)
 221                return ret;
 222
 223        if (core->use_tz && res->cp_size) {
 224                ret = qcom_scm_mem_protect_video_var(res->cp_start,
 225                                                     res->cp_size,
 226                                                     res->cp_nonpixel_start,
 227                                                     res->cp_nonpixel_size);
 228                if (ret) {
 229                        qcom_scm_pas_shutdown(VENUS_PAS_ID);
 230                        dev_err(dev, "set virtual address ranges fail (%d)\n",
 231                                ret);
 232                        return ret;
 233                }
 234        }
 235
 236        return 0;
 237}
 238
 239int venus_shutdown(struct venus_core *core)
 240{
 241        int ret;
 242
 243        if (core->use_tz)
 244                ret = qcom_scm_pas_shutdown(VENUS_PAS_ID);
 245        else
 246                ret = venus_shutdown_no_tz(core);
 247
 248        return ret;
 249}
 250
 251int venus_firmware_init(struct venus_core *core)
 252{
 253        struct platform_device_info info;
 254        struct iommu_domain *iommu_dom;
 255        struct platform_device *pdev;
 256        struct device_node *np;
 257        int ret;
 258
 259        np = of_get_child_by_name(core->dev->of_node, "video-firmware");
 260        if (!np) {
 261                core->use_tz = true;
 262                return 0;
 263        }
 264
 265        memset(&info, 0, sizeof(info));
 266        info.fwnode = &np->fwnode;
 267        info.parent = core->dev;
 268        info.name = np->name;
 269        info.dma_mask = DMA_BIT_MASK(32);
 270
 271        pdev = platform_device_register_full(&info);
 272        if (IS_ERR(pdev)) {
 273                of_node_put(np);
 274                return PTR_ERR(pdev);
 275        }
 276
 277        pdev->dev.of_node = np;
 278
 279        ret = of_dma_configure(&pdev->dev, np, true);
 280        if (ret) {
 281                dev_err(core->dev, "dma configure fail\n");
 282                goto err_unregister;
 283        }
 284
 285        core->fw.dev = &pdev->dev;
 286
 287        iommu_dom = iommu_domain_alloc(&platform_bus_type);
 288        if (!iommu_dom) {
 289                dev_err(core->fw.dev, "Failed to allocate iommu domain\n");
 290                ret = -ENOMEM;
 291                goto err_unregister;
 292        }
 293
 294        ret = iommu_attach_device(iommu_dom, core->fw.dev);
 295        if (ret) {
 296                dev_err(core->fw.dev, "could not attach device\n");
 297                goto err_iommu_free;
 298        }
 299
 300        core->fw.iommu_domain = iommu_dom;
 301
 302        of_node_put(np);
 303
 304        return 0;
 305
 306err_iommu_free:
 307        iommu_domain_free(iommu_dom);
 308err_unregister:
 309        platform_device_unregister(pdev);
 310        of_node_put(np);
 311        return ret;
 312}
 313
 314void venus_firmware_deinit(struct venus_core *core)
 315{
 316        struct iommu_domain *iommu;
 317
 318        if (!core->fw.dev)
 319                return;
 320
 321        iommu = core->fw.iommu_domain;
 322
 323        iommu_detach_device(iommu, core->fw.dev);
 324
 325        if (core->fw.iommu_domain) {
 326                iommu_domain_free(iommu);
 327                core->fw.iommu_domain = NULL;
 328        }
 329
 330        platform_device_unregister(to_platform_device(core->fw.dev));
 331}
 332