Commit | Line | Data |
---|---|---|
d3f5168a EA |
1 | /* |
2 | * Copyright (c) 2014 The Linux Foundation. All rights reserved. | |
3 | * Copyright (C) 2013 Red Hat | |
4 | * Author: Rob Clark <robdclark@gmail.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License version 2 as published by | |
8 | * the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along with | |
16 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include "linux/component.h" | |
001bdb55 | 20 | #include "linux/pm_runtime.h" |
d3f5168a EA |
21 | #include "vc4_drv.h" |
22 | #include "vc4_regs.h" | |
23 | ||
24 | #ifdef CONFIG_DEBUG_FS | |
25 | #define REGDEF(reg) { reg, #reg } | |
26 | static const struct { | |
27 | uint32_t reg; | |
28 | const char *name; | |
29 | } vc4_reg_defs[] = { | |
30 | REGDEF(V3D_IDENT0), | |
31 | REGDEF(V3D_IDENT1), | |
32 | REGDEF(V3D_IDENT2), | |
33 | REGDEF(V3D_SCRATCH), | |
34 | REGDEF(V3D_L2CACTL), | |
35 | REGDEF(V3D_SLCACTL), | |
36 | REGDEF(V3D_INTCTL), | |
37 | REGDEF(V3D_INTENA), | |
38 | REGDEF(V3D_INTDIS), | |
39 | REGDEF(V3D_CT0CS), | |
40 | REGDEF(V3D_CT1CS), | |
41 | REGDEF(V3D_CT0EA), | |
42 | REGDEF(V3D_CT1EA), | |
43 | REGDEF(V3D_CT0CA), | |
44 | REGDEF(V3D_CT1CA), | |
45 | REGDEF(V3D_CT00RA0), | |
46 | REGDEF(V3D_CT01RA0), | |
47 | REGDEF(V3D_CT0LC), | |
48 | REGDEF(V3D_CT1LC), | |
49 | REGDEF(V3D_CT0PC), | |
50 | REGDEF(V3D_CT1PC), | |
51 | REGDEF(V3D_PCS), | |
52 | REGDEF(V3D_BFC), | |
53 | REGDEF(V3D_RFC), | |
54 | REGDEF(V3D_BPCA), | |
55 | REGDEF(V3D_BPCS), | |
56 | REGDEF(V3D_BPOA), | |
57 | REGDEF(V3D_BPOS), | |
58 | REGDEF(V3D_BXCF), | |
59 | REGDEF(V3D_SQRSV0), | |
60 | REGDEF(V3D_SQRSV1), | |
61 | REGDEF(V3D_SQCNTL), | |
62 | REGDEF(V3D_SRQPC), | |
63 | REGDEF(V3D_SRQUA), | |
64 | REGDEF(V3D_SRQUL), | |
65 | REGDEF(V3D_SRQCS), | |
66 | REGDEF(V3D_VPACNTL), | |
67 | REGDEF(V3D_VPMBASE), | |
68 | REGDEF(V3D_PCTRC), | |
69 | REGDEF(V3D_PCTRE), | |
70 | REGDEF(V3D_PCTR0), | |
71 | REGDEF(V3D_PCTRS0), | |
72 | REGDEF(V3D_PCTR1), | |
73 | REGDEF(V3D_PCTRS1), | |
74 | REGDEF(V3D_PCTR2), | |
75 | REGDEF(V3D_PCTRS2), | |
76 | REGDEF(V3D_PCTR3), | |
77 | REGDEF(V3D_PCTRS3), | |
78 | REGDEF(V3D_PCTR4), | |
79 | REGDEF(V3D_PCTRS4), | |
80 | REGDEF(V3D_PCTR5), | |
81 | REGDEF(V3D_PCTRS5), | |
82 | REGDEF(V3D_PCTR6), | |
83 | REGDEF(V3D_PCTRS6), | |
84 | REGDEF(V3D_PCTR7), | |
85 | REGDEF(V3D_PCTRS7), | |
86 | REGDEF(V3D_PCTR8), | |
87 | REGDEF(V3D_PCTRS8), | |
88 | REGDEF(V3D_PCTR9), | |
89 | REGDEF(V3D_PCTRS9), | |
90 | REGDEF(V3D_PCTR10), | |
91 | REGDEF(V3D_PCTRS10), | |
92 | REGDEF(V3D_PCTR11), | |
93 | REGDEF(V3D_PCTRS11), | |
94 | REGDEF(V3D_PCTR12), | |
95 | REGDEF(V3D_PCTRS12), | |
96 | REGDEF(V3D_PCTR13), | |
97 | REGDEF(V3D_PCTRS13), | |
98 | REGDEF(V3D_PCTR14), | |
99 | REGDEF(V3D_PCTRS14), | |
100 | REGDEF(V3D_PCTR15), | |
101 | REGDEF(V3D_PCTRS15), | |
102 | REGDEF(V3D_DBGE), | |
103 | REGDEF(V3D_FDBGO), | |
104 | REGDEF(V3D_FDBGB), | |
105 | REGDEF(V3D_FDBGR), | |
106 | REGDEF(V3D_FDBGS), | |
107 | REGDEF(V3D_ERRSTAT), | |
108 | }; | |
109 | ||
110 | int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused) | |
111 | { | |
112 | struct drm_info_node *node = (struct drm_info_node *)m->private; | |
113 | struct drm_device *dev = node->minor->dev; | |
114 | struct vc4_dev *vc4 = to_vc4_dev(dev); | |
115 | int i; | |
116 | ||
117 | for (i = 0; i < ARRAY_SIZE(vc4_reg_defs); i++) { | |
118 | seq_printf(m, "%s (0x%04x): 0x%08x\n", | |
119 | vc4_reg_defs[i].name, vc4_reg_defs[i].reg, | |
120 | V3D_READ(vc4_reg_defs[i].reg)); | |
121 | } | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
126 | int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) | |
127 | { | |
128 | struct drm_info_node *node = (struct drm_info_node *)m->private; | |
129 | struct drm_device *dev = node->minor->dev; | |
130 | struct vc4_dev *vc4 = to_vc4_dev(dev); | |
131 | uint32_t ident1 = V3D_READ(V3D_IDENT1); | |
132 | uint32_t nslc = VC4_GET_FIELD(ident1, V3D_IDENT1_NSLC); | |
133 | uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS); | |
134 | uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS); | |
135 | ||
136 | seq_printf(m, "Revision: %d\n", | |
137 | VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); | |
138 | seq_printf(m, "Slices: %d\n", nslc); | |
139 | seq_printf(m, "TMUs: %d\n", nslc * tups); | |
140 | seq_printf(m, "QPUs: %d\n", nslc * qups); | |
141 | seq_printf(m, "Semaphores: %d\n", | |
142 | VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); | |
143 | ||
144 | return 0; | |
145 | } | |
146 | #endif /* CONFIG_DEBUG_FS */ | |
147 | ||
148 | static void vc4_v3d_init_hw(struct drm_device *dev) | |
149 | { | |
150 | struct vc4_dev *vc4 = to_vc4_dev(dev); | |
151 | ||
152 | /* Take all the memory that would have been reserved for user | |
153 | * QPU programs, since we don't have an interface for running | |
154 | * them, anyway. | |
155 | */ | |
156 | V3D_WRITE(V3D_VPMBASE, 0); | |
157 | } | |
158 | ||
001bdb55 EA |
159 | #ifdef CONFIG_PM |
160 | static int vc4_v3d_runtime_suspend(struct device *dev) | |
161 | { | |
162 | struct vc4_v3d *v3d = dev_get_drvdata(dev); | |
163 | struct vc4_dev *vc4 = v3d->vc4; | |
164 | ||
165 | vc4_irq_uninstall(vc4->dev); | |
166 | ||
167 | return 0; | |
168 | } | |
169 | ||
170 | static int vc4_v3d_runtime_resume(struct device *dev) | |
171 | { | |
172 | struct vc4_v3d *v3d = dev_get_drvdata(dev); | |
173 | struct vc4_dev *vc4 = v3d->vc4; | |
174 | ||
175 | vc4_v3d_init_hw(vc4->dev); | |
176 | vc4_irq_postinstall(vc4->dev); | |
177 | ||
178 | return 0; | |
179 | } | |
180 | #endif | |
181 | ||
d3f5168a EA |
182 | static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) |
183 | { | |
184 | struct platform_device *pdev = to_platform_device(dev); | |
185 | struct drm_device *drm = dev_get_drvdata(master); | |
186 | struct vc4_dev *vc4 = to_vc4_dev(drm); | |
187 | struct vc4_v3d *v3d = NULL; | |
d5b1a78a | 188 | int ret; |
d3f5168a EA |
189 | |
190 | v3d = devm_kzalloc(&pdev->dev, sizeof(*v3d), GFP_KERNEL); | |
191 | if (!v3d) | |
192 | return -ENOMEM; | |
193 | ||
001bdb55 EA |
194 | dev_set_drvdata(dev, v3d); |
195 | ||
d3f5168a EA |
196 | v3d->pdev = pdev; |
197 | ||
198 | v3d->regs = vc4_ioremap_regs(pdev, 0); | |
199 | if (IS_ERR(v3d->regs)) | |
200 | return PTR_ERR(v3d->regs); | |
201 | ||
202 | vc4->v3d = v3d; | |
001bdb55 | 203 | v3d->vc4 = vc4; |
d3f5168a EA |
204 | |
205 | if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) { | |
206 | DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n", | |
207 | V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0); | |
208 | return -EINVAL; | |
209 | } | |
210 | ||
d5b1a78a EA |
211 | /* Reset the binner overflow address/size at setup, to be sure |
212 | * we don't reuse an old one. | |
213 | */ | |
214 | V3D_WRITE(V3D_BPOA, 0); | |
215 | V3D_WRITE(V3D_BPOS, 0); | |
216 | ||
d3f5168a EA |
217 | vc4_v3d_init_hw(drm); |
218 | ||
d5b1a78a EA |
219 | ret = drm_irq_install(drm, platform_get_irq(pdev, 0)); |
220 | if (ret) { | |
221 | DRM_ERROR("Failed to install IRQ handler\n"); | |
222 | return ret; | |
223 | } | |
224 | ||
001bdb55 EA |
225 | pm_runtime_enable(dev); |
226 | ||
d3f5168a EA |
227 | return 0; |
228 | } | |
229 | ||
230 | static void vc4_v3d_unbind(struct device *dev, struct device *master, | |
231 | void *data) | |
232 | { | |
233 | struct drm_device *drm = dev_get_drvdata(master); | |
234 | struct vc4_dev *vc4 = to_vc4_dev(drm); | |
235 | ||
001bdb55 EA |
236 | pm_runtime_disable(dev); |
237 | ||
d5b1a78a EA |
238 | drm_irq_uninstall(drm); |
239 | ||
240 | /* Disable the binner's overflow memory address, so the next | |
241 | * driver probe (if any) doesn't try to reuse our old | |
242 | * allocation. | |
243 | */ | |
244 | V3D_WRITE(V3D_BPOA, 0); | |
245 | V3D_WRITE(V3D_BPOS, 0); | |
246 | ||
d3f5168a EA |
247 | vc4->v3d = NULL; |
248 | } | |
249 | ||
001bdb55 EA |
250 | static const struct dev_pm_ops vc4_v3d_pm_ops = { |
251 | SET_RUNTIME_PM_OPS(vc4_v3d_runtime_suspend, vc4_v3d_runtime_resume, NULL) | |
252 | }; | |
253 | ||
d3f5168a EA |
254 | static const struct component_ops vc4_v3d_ops = { |
255 | .bind = vc4_v3d_bind, | |
256 | .unbind = vc4_v3d_unbind, | |
257 | }; | |
258 | ||
259 | static int vc4_v3d_dev_probe(struct platform_device *pdev) | |
260 | { | |
261 | return component_add(&pdev->dev, &vc4_v3d_ops); | |
262 | } | |
263 | ||
264 | static int vc4_v3d_dev_remove(struct platform_device *pdev) | |
265 | { | |
266 | component_del(&pdev->dev, &vc4_v3d_ops); | |
267 | return 0; | |
268 | } | |
269 | ||
270 | static const struct of_device_id vc4_v3d_dt_match[] = { | |
271 | { .compatible = "brcm,vc4-v3d" }, | |
272 | {} | |
273 | }; | |
274 | ||
275 | struct platform_driver vc4_v3d_driver = { | |
276 | .probe = vc4_v3d_dev_probe, | |
277 | .remove = vc4_v3d_dev_remove, | |
278 | .driver = { | |
279 | .name = "vc4_v3d", | |
280 | .of_match_table = vc4_v3d_dt_match, | |
001bdb55 | 281 | .pm = &vc4_v3d_pm_ops, |
d3f5168a EA |
282 | }, |
283 | }; |