root/drivers/media/platform/qcom/venus/firmware.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. venus_reset_cpu
  2. venus_set_hw_state
  3. venus_load_fw
  4. venus_boot_no_tz
  5. venus_shutdown_no_tz
  6. venus_boot
  7. venus_shutdown
  8. venus_firmware_init
  9. venus_firmware_deinit

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

/* [<][>][^][v][top][bottom][index][help] */