hc
2024-05-10 23fa18eaa71266feff7ba8d83022d9e1cc83c65a
kernel/drivers/media/platform/qcom/venus/firmware.c
....@@ -1,46 +1,81 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * Copyright (C) 2017 Linaro Ltd.
3
- *
4
- * This program is free software; you can redistribute it and/or modify
5
- * it under the terms of the GNU General Public License version 2 and
6
- * only version 2 as published by the Free Software Foundation.
7
- *
8
- * This program is distributed in the hope that it will be useful,
9
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
- * GNU General Public License for more details.
12
- *
134 */
145
156 #include <linux/device.h>
167 #include <linux/firmware.h>
178 #include <linux/kernel.h>
9
+#include <linux/iommu.h>
1810 #include <linux/io.h>
1911 #include <linux/of.h>
2012 #include <linux/of_address.h>
13
+#include <linux/platform_device.h>
14
+#include <linux/of_device.h>
2115 #include <linux/qcom_scm.h>
2216 #include <linux/sizes.h>
2317 #include <linux/soc/qcom/mdt_loader.h>
2418
19
+#include "core.h"
2520 #include "firmware.h"
21
+#include "hfi_venus_io.h"
2622
2723 #define VENUS_PAS_ID 9
2824 #define VENUS_FW_MEM_SIZE (6 * SZ_1M)
25
+#define VENUS_FW_START_ADDR 0x0
2926
30
-int venus_boot(struct device *dev, const char *fwname)
27
+static 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
+
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->wrapper_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)
3166 {
3267 const struct firmware *mdt;
3368 struct device_node *node;
34
- phys_addr_t mem_phys;
69
+ struct device *dev;
3570 struct resource r;
3671 ssize_t fw_size;
37
- size_t mem_size;
3872 void *mem_va;
3973 int ret;
4074
41
- if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) || !qcom_scm_is_available())
42
- return -EPROBE_DEFER;
75
+ *mem_phys = 0;
76
+ *mem_size = 0;
4377
78
+ dev = core->dev;
4479 node = of_parse_phandle(dev->of_node, "memory-region", 0);
4580 if (!node) {
4681 dev_err(dev, "no memory-region specified\n");
....@@ -49,50 +84,228 @@
4984
5085 ret = of_address_to_resource(node, 0, &r);
5186 if (ret)
52
- return ret;
53
-
54
- mem_phys = r.start;
55
- mem_size = resource_size(&r);
56
-
57
- if (mem_size < VENUS_FW_MEM_SIZE)
58
- return -EINVAL;
59
-
60
- mem_va = memremap(r.start, mem_size, MEMREMAP_WC);
61
- if (!mem_va) {
62
- dev_err(dev, "unable to map memory region: %pa+%zx\n",
63
- &r.start, mem_size);
64
- return -ENOMEM;
65
- }
87
+ goto err_put_node;
6688
6789 ret = request_firmware(&mdt, fwname, dev);
6890 if (ret < 0)
69
- goto err_unmap;
91
+ goto err_put_node;
7092
7193 fw_size = qcom_mdt_get_size(mdt);
7294 if (fw_size < 0) {
7395 ret = fw_size;
74
- release_firmware(mdt);
75
- goto err_unmap;
96
+ goto err_release_fw;
7697 }
7798
78
- ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID, mem_va, mem_phys,
79
- mem_size, NULL);
99
+ *mem_phys = r.start;
100
+ *mem_size = resource_size(&r);
80101
81
- release_firmware(mdt);
102
+ if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) {
103
+ ret = -EINVAL;
104
+ goto err_release_fw;
105
+ }
82106
83
- if (ret)
84
- goto err_unmap;
107
+ mem_va = memremap(r.start, *mem_size, MEMREMAP_WC);
108
+ if (!mem_va) {
109
+ dev_err(dev, "unable to map memory region: %pR\n", &r);
110
+ ret = -ENOMEM;
111
+ goto err_release_fw;
112
+ }
85113
86
- ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
87
- if (ret)
88
- goto err_unmap;
114
+ if (core->use_tz)
115
+ ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID,
116
+ mem_va, *mem_phys, *mem_size, NULL);
117
+ else
118
+ ret = qcom_mdt_load_no_init(dev, mdt, fwname, VENUS_PAS_ID,
119
+ mem_va, *mem_phys, *mem_size, NULL);
89120
90
-err_unmap:
91121 memunmap(mem_va);
122
+err_release_fw:
123
+ release_firmware(mdt);
124
+err_put_node:
125
+ of_node_put(node);
92126 return ret;
93127 }
94128
95
-int venus_shutdown(struct device *dev)
129
+static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys,
130
+ size_t mem_size)
96131 {
97
- return qcom_scm_pas_shutdown(VENUS_PAS_ID);
132
+ struct iommu_domain *iommu;
133
+ struct device *dev;
134
+ int ret;
135
+
136
+ dev = core->fw.dev;
137
+ if (!dev)
138
+ return -EPROBE_DEFER;
139
+
140
+ iommu = core->fw.iommu_domain;
141
+ core->fw.mapped_mem_size = mem_size;
142
+
143
+ ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size,
144
+ IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV);
145
+ if (ret) {
146
+ dev_err(dev, "could not map video firmware region\n");
147
+ return ret;
148
+ }
149
+
150
+ venus_reset_cpu(core);
151
+
152
+ return 0;
153
+}
154
+
155
+static int venus_shutdown_no_tz(struct venus_core *core)
156
+{
157
+ const size_t mapped = core->fw.mapped_mem_size;
158
+ struct iommu_domain *iommu;
159
+ size_t unmapped;
160
+ u32 reg;
161
+ struct device *dev = core->fw.dev;
162
+ void __iomem *wrapper_base = core->wrapper_base;
163
+
164
+ /* Assert the reset to ARM9 */
165
+ reg = readl_relaxed(wrapper_base + WRAPPER_A9SS_SW_RESET);
166
+ reg |= WRAPPER_A9SS_SW_RESET_BIT;
167
+ writel_relaxed(reg, wrapper_base + WRAPPER_A9SS_SW_RESET);
168
+
169
+ /* Make sure reset is asserted before the mapping is removed */
170
+ mb();
171
+
172
+ iommu = core->fw.iommu_domain;
173
+
174
+ unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped);
175
+ if (unmapped != mapped)
176
+ dev_err(dev, "failed to unmap firmware\n");
177
+
178
+ return 0;
179
+}
180
+
181
+int venus_boot(struct venus_core *core)
182
+{
183
+ struct device *dev = core->dev;
184
+ const struct venus_resources *res = core->res;
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
+ if (ret)
205
+ return ret;
206
+
207
+ if (core->use_tz && res->cp_size) {
208
+ ret = qcom_scm_mem_protect_video_var(res->cp_start,
209
+ res->cp_size,
210
+ res->cp_nonpixel_start,
211
+ res->cp_nonpixel_size);
212
+ if (ret) {
213
+ qcom_scm_pas_shutdown(VENUS_PAS_ID);
214
+ dev_err(dev, "set virtual address ranges fail (%d)\n",
215
+ ret);
216
+ return ret;
217
+ }
218
+ }
219
+
220
+ return 0;
221
+}
222
+
223
+int venus_shutdown(struct venus_core *core)
224
+{
225
+ int ret;
226
+
227
+ if (core->use_tz)
228
+ ret = qcom_scm_pas_shutdown(VENUS_PAS_ID);
229
+ else
230
+ ret = venus_shutdown_no_tz(core);
231
+
232
+ return ret;
233
+}
234
+
235
+int venus_firmware_init(struct venus_core *core)
236
+{
237
+ struct platform_device_info info;
238
+ struct iommu_domain *iommu_dom;
239
+ struct platform_device *pdev;
240
+ struct device_node *np;
241
+ int ret;
242
+
243
+ np = of_get_child_by_name(core->dev->of_node, "video-firmware");
244
+ if (!np) {
245
+ core->use_tz = true;
246
+ return 0;
247
+ }
248
+
249
+ memset(&info, 0, sizeof(info));
250
+ info.fwnode = &np->fwnode;
251
+ info.parent = core->dev;
252
+ info.name = np->name;
253
+ info.dma_mask = DMA_BIT_MASK(32);
254
+
255
+ pdev = platform_device_register_full(&info);
256
+ if (IS_ERR(pdev)) {
257
+ of_node_put(np);
258
+ return PTR_ERR(pdev);
259
+ }
260
+
261
+ pdev->dev.of_node = np;
262
+
263
+ ret = of_dma_configure(&pdev->dev, np, true);
264
+ if (ret) {
265
+ dev_err(core->dev, "dma configure fail\n");
266
+ goto err_unregister;
267
+ }
268
+
269
+ core->fw.dev = &pdev->dev;
270
+
271
+ iommu_dom = iommu_domain_alloc(&platform_bus_type);
272
+ if (!iommu_dom) {
273
+ dev_err(core->fw.dev, "Failed to allocate iommu domain\n");
274
+ ret = -ENOMEM;
275
+ goto err_unregister;
276
+ }
277
+
278
+ ret = iommu_attach_device(iommu_dom, core->fw.dev);
279
+ if (ret) {
280
+ dev_err(core->fw.dev, "could not attach device\n");
281
+ goto err_iommu_free;
282
+ }
283
+
284
+ core->fw.iommu_domain = iommu_dom;
285
+
286
+ of_node_put(np);
287
+
288
+ return 0;
289
+
290
+err_iommu_free:
291
+ iommu_domain_free(iommu_dom);
292
+err_unregister:
293
+ platform_device_unregister(pdev);
294
+ of_node_put(np);
295
+ return ret;
296
+}
297
+
298
+void venus_firmware_deinit(struct venus_core *core)
299
+{
300
+ struct iommu_domain *iommu;
301
+
302
+ if (!core->fw.dev)
303
+ return;
304
+
305
+ iommu = core->fw.iommu_domain;
306
+
307
+ iommu_detach_device(iommu, core->fw.dev);
308
+ iommu_domain_free(iommu);
309
+
310
+ platform_device_unregister(to_platform_device(core->fw.dev));
98311 }