treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 284
[linux-block.git] / drivers / media / platform / qcom / venus / firmware.c
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         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
58 static 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                 return ret;
82
83         ret = request_firmware(&mdt, fwname, dev);
84         if (ret < 0)
85                 return ret;
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);
117 err_release_fw:
118         release_firmware(mdt);
119         return ret;
120 }
121
122 static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys,
123                             size_t mem_size)
124 {
125         struct iommu_domain *iommu;
126         struct device *dev;
127         int ret;
128
129         dev = core->fw.dev;
130         if (!dev)
131                 return -EPROBE_DEFER;
132
133         iommu = core->fw.iommu_domain;
134         core->fw.mapped_mem_size = mem_size;
135
136         ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size,
137                         IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV);
138         if (ret) {
139                 dev_err(dev, "could not map video firmware region\n");
140                 return ret;
141         }
142
143         venus_reset_cpu(core);
144
145         return 0;
146 }
147
148 static int venus_shutdown_no_tz(struct venus_core *core)
149 {
150         const size_t mapped = core->fw.mapped_mem_size;
151         struct iommu_domain *iommu;
152         size_t unmapped;
153         u32 reg;
154         struct device *dev = core->fw.dev;
155         void __iomem *base = core->base;
156
157         /* Assert the reset to ARM9 */
158         reg = readl_relaxed(base + WRAPPER_A9SS_SW_RESET);
159         reg |= WRAPPER_A9SS_SW_RESET_BIT;
160         writel_relaxed(reg, base + WRAPPER_A9SS_SW_RESET);
161
162         /* Make sure reset is asserted before the mapping is removed */
163         mb();
164
165         iommu = core->fw.iommu_domain;
166
167         unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped);
168         if (unmapped != mapped)
169                 dev_err(dev, "failed to unmap firmware\n");
170
171         return 0;
172 }
173
174 int venus_boot(struct venus_core *core)
175 {
176         struct device *dev = core->dev;
177         phys_addr_t mem_phys;
178         size_t mem_size;
179         int ret;
180
181         if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ||
182             (core->use_tz && !qcom_scm_is_available()))
183                 return -EPROBE_DEFER;
184
185         ret = venus_load_fw(core, core->res->fwname, &mem_phys, &mem_size);
186         if (ret) {
187                 dev_err(dev, "fail to load video firmware\n");
188                 return -EINVAL;
189         }
190
191         if (core->use_tz)
192                 ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
193         else
194                 ret = venus_boot_no_tz(core, mem_phys, mem_size);
195
196         return ret;
197 }
198
199 int venus_shutdown(struct venus_core *core)
200 {
201         int ret;
202
203         if (core->use_tz)
204                 ret = qcom_scm_pas_shutdown(VENUS_PAS_ID);
205         else
206                 ret = venus_shutdown_no_tz(core);
207
208         return ret;
209 }
210
211 int venus_firmware_init(struct venus_core *core)
212 {
213         struct platform_device_info info;
214         struct iommu_domain *iommu_dom;
215         struct platform_device *pdev;
216         struct device_node *np;
217         int ret;
218
219         np = of_get_child_by_name(core->dev->of_node, "video-firmware");
220         if (!np) {
221                 core->use_tz = true;
222                 return 0;
223         }
224
225         memset(&info, 0, sizeof(info));
226         info.fwnode = &np->fwnode;
227         info.parent = core->dev;
228         info.name = np->name;
229         info.dma_mask = DMA_BIT_MASK(32);
230
231         pdev = platform_device_register_full(&info);
232         if (IS_ERR(pdev)) {
233                 of_node_put(np);
234                 return PTR_ERR(pdev);
235         }
236
237         pdev->dev.of_node = np;
238
239         ret = of_dma_configure(&pdev->dev, np, true);
240         if (ret) {
241                 dev_err(core->dev, "dma configure fail\n");
242                 goto err_unregister;
243         }
244
245         core->fw.dev = &pdev->dev;
246
247         iommu_dom = iommu_domain_alloc(&platform_bus_type);
248         if (!iommu_dom) {
249                 dev_err(core->fw.dev, "Failed to allocate iommu domain\n");
250                 ret = -ENOMEM;
251                 goto err_unregister;
252         }
253
254         ret = iommu_attach_device(iommu_dom, core->fw.dev);
255         if (ret) {
256                 dev_err(core->fw.dev, "could not attach device\n");
257                 goto err_iommu_free;
258         }
259
260         core->fw.iommu_domain = iommu_dom;
261
262         of_node_put(np);
263
264         return 0;
265
266 err_iommu_free:
267         iommu_domain_free(iommu_dom);
268 err_unregister:
269         platform_device_unregister(pdev);
270         of_node_put(np);
271         return ret;
272 }
273
274 void venus_firmware_deinit(struct venus_core *core)
275 {
276         struct iommu_domain *iommu;
277
278         if (!core->fw.dev)
279                 return;
280
281         iommu = core->fw.iommu_domain;
282
283         iommu_detach_device(iommu, core->fw.dev);
284         iommu_domain_free(iommu);
285
286         platform_device_unregister(to_platform_device(core->fw.dev));
287 }