hc
2024-10-22 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5
kernel/drivers/gpu/drm/meson/meson_drv.c
....@@ -1,54 +1,40 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * Copyright (C) 2016 BayLibre, SAS
34 * Author: Neil Armstrong <narmstrong@baylibre.com>
45 * Copyright (C) 2014 Endless Mobile
56 *
6
- * This program is free software; you can redistribute it and/or
7
- * modify it under the terms of the GNU General Public License as
8
- * published by the Free Software Foundation; either version 2 of the
9
- * License, or (at your option) any later version.
10
- *
11
- * This program is distributed in the hope that it will be useful, but
12
- * WITHOUT ANY WARRANTY; without even the implied warranty of
13
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
- * General Public License for more details.
15
- *
16
- * You should have received a copy of the GNU General Public License
17
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
18
- *
197 * Written by:
208 * Jasper St. Pierre <jstpierre@mecheye.net>
219 */
2210
23
-#include <linux/kernel.h>
24
-#include <linux/module.h>
25
-#include <linux/mutex.h>
26
-#include <linux/platform_device.h>
2711 #include <linux/component.h>
12
+#include <linux/module.h>
2813 #include <linux/of_graph.h>
14
+#include <linux/sys_soc.h>
15
+#include <linux/platform_device.h>
16
+#include <linux/soc/amlogic/meson-canvas.h>
2917
30
-#include <drm/drmP.h>
31
-#include <drm/drm_atomic.h>
3218 #include <drm/drm_atomic_helper.h>
33
-#include <drm/drm_flip_work.h>
34
-#include <drm/drm_crtc_helper.h>
35
-#include <drm/drm_plane_helper.h>
19
+#include <drm/drm_drv.h>
20
+#include <drm/drm_fb_helper.h>
3621 #include <drm/drm_gem_cma_helper.h>
3722 #include <drm/drm_gem_framebuffer_helper.h>
38
-#include <drm/drm_fb_cma_helper.h>
39
-#include <drm/drm_rect.h>
40
-#include <drm/drm_fb_helper.h>
23
+#include <drm/drm_irq.h>
24
+#include <drm/drm_modeset_helper_vtables.h>
25
+#include <drm/drm_probe_helper.h>
26
+#include <drm/drm_vblank.h>
4127
42
-#include "meson_drv.h"
43
-#include "meson_plane.h"
4428 #include "meson_crtc.h"
45
-#include "meson_venc_cvbs.h"
46
-
47
-#include "meson_vpp.h"
48
-#include "meson_viu.h"
49
-#include "meson_venc.h"
50
-#include "meson_canvas.h"
29
+#include "meson_drv.h"
30
+#include "meson_overlay.h"
31
+#include "meson_plane.h"
32
+#include "meson_osd_afbcd.h"
5133 #include "meson_registers.h"
34
+#include "meson_venc_cvbs.h"
35
+#include "meson_viu.h"
36
+#include "meson_vpp.h"
37
+#include "meson_rdma.h"
5238
5339 #define DRIVER_NAME "meson"
5440 #define DRIVER_DESC "Amlogic Meson DRM driver"
....@@ -68,15 +54,7 @@
6854 * - Powering Up HDMI controller and PHY
6955 */
7056
71
-static void meson_fb_output_poll_changed(struct drm_device *dev)
72
-{
73
- struct meson_drm *priv = dev->dev_private;
74
-
75
- drm_fbdev_cma_hotplug_event(priv->fbdev);
76
-}
77
-
7857 static const struct drm_mode_config_funcs meson_mode_config_funcs = {
79
- .output_poll_changed = meson_fb_output_poll_changed,
8058 .atomic_check = drm_atomic_helper_check,
8159 .atomic_commit = drm_atomic_helper_commit,
8260 .fb_create = drm_gem_fb_create,
....@@ -98,31 +76,28 @@
9876 return IRQ_HANDLED;
9977 }
10078
79
+static int meson_dumb_create(struct drm_file *file, struct drm_device *dev,
80
+ struct drm_mode_create_dumb *args)
81
+{
82
+ /*
83
+ * We need 64bytes aligned stride, and PAGE aligned size
84
+ */
85
+ args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), SZ_64);
86
+ args->size = PAGE_ALIGN(args->pitch * args->height);
87
+
88
+ return drm_gem_cma_dumb_create_internal(file, dev, args);
89
+}
90
+
10191 DEFINE_DRM_GEM_CMA_FOPS(fops);
10292
10393 static struct drm_driver meson_driver = {
104
- .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
105
- DRIVER_MODESET | DRIVER_PRIME |
106
- DRIVER_ATOMIC,
94
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
10795
10896 /* IRQ */
10997 .irq_handler = meson_irq,
11098
111
- /* PRIME Ops */
112
- .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
113
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
114
- .gem_prime_import = drm_gem_prime_import,
115
- .gem_prime_export = drm_gem_prime_export,
116
- .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
117
- .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
118
- .gem_prime_vmap = drm_gem_cma_prime_vmap,
119
- .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
120
- .gem_prime_mmap = drm_gem_cma_prime_mmap,
121
-
122
- /* GEM Ops */
123
- .dumb_create = drm_gem_cma_dumb_create,
124
- .gem_free_object_unlocked = drm_gem_cma_free_object,
125
- .gem_vm_ops = &drm_gem_cma_vm_ops,
99
+ /* CMA Ops */
100
+ DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create),
126101
127102 /* Misc */
128103 .fops = &fops,
....@@ -141,8 +116,11 @@
141116 for_each_endpoint_of_node(dev->of_node, ep) {
142117 /* If the endpoint node exists, consider it enabled */
143118 remote = of_graph_get_remote_port(ep);
144
- if (remote)
119
+ if (remote) {
120
+ of_node_put(remote);
121
+ of_node_put(ep);
145122 return true;
123
+ }
146124 }
147125
148126 return false;
....@@ -157,26 +135,84 @@
157135
158136 static void meson_vpu_init(struct meson_drm *priv)
159137 {
160
- writel_relaxed(0x210000, priv->io_base + _REG(VPU_RDARB_MODE_L1C1));
161
- writel_relaxed(0x10000, priv->io_base + _REG(VPU_RDARB_MODE_L1C2));
162
- writel_relaxed(0x900000, priv->io_base + _REG(VPU_RDARB_MODE_L2C1));
163
- writel_relaxed(0x20000, priv->io_base + _REG(VPU_WRARB_MODE_L2C1));
138
+ u32 value;
139
+
140
+ /*
141
+ * Slave dc0 and dc5 connected to master port 1.
142
+ * By default other slaves are connected to master port 0.
143
+ */
144
+ value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) |
145
+ VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1);
146
+ writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C1));
147
+
148
+ /* Slave dc0 connected to master port 1 */
149
+ value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1);
150
+ writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C2));
151
+
152
+ /* Slave dc4 and dc7 connected to master port 1 */
153
+ value = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) |
154
+ VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1);
155
+ writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L2C1));
156
+
157
+ /* Slave dc1 connected to master port 1 */
158
+ value = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1);
159
+ writel_relaxed(value, priv->io_base + _REG(VPU_WRARB_MODE_L2C1));
164160 }
161
+
162
+static void meson_remove_framebuffers(void)
163
+{
164
+ struct apertures_struct *ap;
165
+
166
+ ap = alloc_apertures(1);
167
+ if (!ap)
168
+ return;
169
+
170
+ /* The framebuffer can be located anywhere in RAM */
171
+ ap->ranges[0].base = 0;
172
+ ap->ranges[0].size = ~0;
173
+
174
+ drm_fb_helper_remove_conflicting_framebuffers(ap, "meson-drm-fb",
175
+ false);
176
+ kfree(ap);
177
+}
178
+
179
+struct meson_drm_soc_attr {
180
+ struct meson_drm_soc_limits limits;
181
+ const struct soc_device_attribute *attrs;
182
+};
183
+
184
+static const struct meson_drm_soc_attr meson_drm_soc_attrs[] = {
185
+ /* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */
186
+ {
187
+ .limits = {
188
+ .max_hdmi_phy_freq = 1650000,
189
+ },
190
+ .attrs = (const struct soc_device_attribute []) {
191
+ { .soc_id = "GXL (S805*)", },
192
+ { /* sentinel */ },
193
+ }
194
+ },
195
+};
165196
166197 static int meson_drv_bind_master(struct device *dev, bool has_components)
167198 {
168199 struct platform_device *pdev = to_platform_device(dev);
200
+ const struct meson_drm_match_data *match;
169201 struct meson_drm *priv;
170202 struct drm_device *drm;
171203 struct resource *res;
172204 void __iomem *regs;
173
- int ret;
205
+ int ret, i;
174206
175207 /* Checks if an output connector is available */
176208 if (!meson_vpu_has_available_connectors(dev)) {
177209 dev_err(dev, "No output connector available\n");
178210 return -ENODEV;
179211 }
212
+
213
+ match = of_device_get_match_data(dev);
214
+ if (!match)
215
+ return -ENODEV;
180216
181217 drm = drm_dev_alloc(&meson_driver, dev);
182218 if (IS_ERR(drm))
....@@ -190,6 +226,8 @@
190226 drm->dev_private = priv;
191227 priv->drm = drm;
192228 priv->dev = dev;
229
+ priv->compat = match->compat;
230
+ priv->afbcd.ops = match->afbcd_ops;
193231
194232 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu");
195233 regs = devm_ioremap_resource(dev, res);
....@@ -220,23 +258,31 @@
220258 goto free_drm;
221259 }
222260
223
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc");
224
- if (!res) {
225
- ret = -EINVAL;
226
- goto free_drm;
227
- }
228
- /* Simply ioremap since it may be a shared register zone */
229
- regs = devm_ioremap(dev, res->start, resource_size(res));
230
- if (!regs) {
231
- ret = -EADDRNOTAVAIL;
261
+ priv->canvas = meson_canvas_get(dev);
262
+ if (IS_ERR(priv->canvas)) {
263
+ ret = PTR_ERR(priv->canvas);
232264 goto free_drm;
233265 }
234266
235
- priv->dmc = devm_regmap_init_mmio(dev, regs,
236
- &meson_regmap_config);
237
- if (IS_ERR(priv->dmc)) {
238
- dev_err(&pdev->dev, "Couldn't create the DMC regmap\n");
239
- ret = PTR_ERR(priv->dmc);
267
+ ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1);
268
+ if (ret)
269
+ goto free_drm;
270
+ ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0);
271
+ if (ret) {
272
+ meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
273
+ goto free_drm;
274
+ }
275
+ ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1);
276
+ if (ret) {
277
+ meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
278
+ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
279
+ goto free_drm;
280
+ }
281
+ ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2);
282
+ if (ret) {
283
+ meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
284
+ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
285
+ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
240286 goto free_drm;
241287 }
242288
....@@ -246,7 +292,20 @@
246292 if (ret)
247293 goto free_drm;
248294
249
- drm_mode_config_init(drm);
295
+ /* Assign limits per soc revision/package */
296
+ for (i = 0 ; i < ARRAY_SIZE(meson_drm_soc_attrs) ; ++i) {
297
+ if (soc_device_match(meson_drm_soc_attrs[i].attrs)) {
298
+ priv->limits = &meson_drm_soc_attrs[i].limits;
299
+ break;
300
+ }
301
+ }
302
+
303
+ /* Remove early framebuffers (ie. simplefb) */
304
+ meson_remove_framebuffers();
305
+
306
+ ret = drmm_mode_config_init(drm);
307
+ if (ret)
308
+ goto free_drm;
250309 drm->mode_config.max_width = 3840;
251310 drm->mode_config.max_height = 2160;
252311 drm->mode_config.funcs = &meson_mode_config_funcs;
....@@ -258,41 +317,43 @@
258317 meson_venc_init(priv);
259318 meson_vpp_init(priv);
260319 meson_viu_init(priv);
320
+ if (priv->afbcd.ops) {
321
+ ret = priv->afbcd.ops->init(priv);
322
+ if (ret)
323
+ goto free_drm;
324
+ }
261325
262326 /* Encoder Initialization */
263327
264328 ret = meson_venc_cvbs_create(priv);
265329 if (ret)
266
- goto free_drm;
330
+ goto exit_afbcd;
267331
268332 if (has_components) {
269333 ret = component_bind_all(drm->dev, drm);
270334 if (ret) {
271335 dev_err(drm->dev, "Couldn't bind all components\n");
272
- goto free_drm;
336
+ goto exit_afbcd;
273337 }
274338 }
275339
276340 ret = meson_plane_create(priv);
277341 if (ret)
278
- goto free_drm;
342
+ goto unbind_all;
343
+
344
+ ret = meson_overlay_create(priv);
345
+ if (ret)
346
+ goto unbind_all;
279347
280348 ret = meson_crtc_create(priv);
281349 if (ret)
282
- goto free_drm;
350
+ goto unbind_all;
283351
284352 ret = drm_irq_install(drm, priv->vsync_irq);
285353 if (ret)
286
- goto free_drm;
354
+ goto unbind_all;
287355
288356 drm_mode_config_reset(drm);
289
-
290
- priv->fbdev = drm_fbdev_cma_init(drm, 32,
291
- drm->mode_config.num_connector);
292
- if (IS_ERR(priv->fbdev)) {
293
- ret = PTR_ERR(priv->fbdev);
294
- goto free_drm;
295
- }
296357
297358 drm_kms_helper_poll_init(drm);
298359
....@@ -302,10 +363,18 @@
302363 if (ret)
303364 goto uninstall_irq;
304365
366
+ drm_fbdev_generic_setup(drm, 32);
367
+
305368 return 0;
306369
307370 uninstall_irq:
308371 drm_irq_uninstall(drm);
372
+unbind_all:
373
+ if (has_components)
374
+ component_unbind_all(drm->dev, drm);
375
+exit_afbcd:
376
+ if (priv->afbcd.ops)
377
+ priv->afbcd.ops->exit(priv);
309378 free_drm:
310379 drm_dev_put(drm);
311380
....@@ -322,19 +391,55 @@
322391 struct meson_drm *priv = dev_get_drvdata(dev);
323392 struct drm_device *drm = priv->drm;
324393
394
+ if (priv->canvas) {
395
+ meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
396
+ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
397
+ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
398
+ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2);
399
+ }
400
+
325401 drm_dev_unregister(drm);
326
- drm_irq_uninstall(drm);
327402 drm_kms_helper_poll_fini(drm);
328
- drm_fbdev_cma_fini(priv->fbdev);
329
- drm_mode_config_cleanup(drm);
403
+ drm_atomic_helper_shutdown(drm);
404
+ component_unbind_all(dev, drm);
405
+ drm_irq_uninstall(drm);
330406 drm_dev_put(drm);
331407
408
+ if (priv->afbcd.ops)
409
+ priv->afbcd.ops->exit(priv);
332410 }
333411
334412 static const struct component_master_ops meson_drv_master_ops = {
335413 .bind = meson_drv_bind,
336414 .unbind = meson_drv_unbind,
337415 };
416
+
417
+static int __maybe_unused meson_drv_pm_suspend(struct device *dev)
418
+{
419
+ struct meson_drm *priv = dev_get_drvdata(dev);
420
+
421
+ if (!priv)
422
+ return 0;
423
+
424
+ return drm_mode_config_helper_suspend(priv->drm);
425
+}
426
+
427
+static int __maybe_unused meson_drv_pm_resume(struct device *dev)
428
+{
429
+ struct meson_drm *priv = dev_get_drvdata(dev);
430
+
431
+ if (!priv)
432
+ return 0;
433
+
434
+ meson_vpu_init(priv);
435
+ meson_venc_init(priv);
436
+ meson_vpp_init(priv);
437
+ meson_viu_init(priv);
438
+ if (priv->afbcd.ops)
439
+ priv->afbcd.ops->init(priv);
440
+
441
+ return drm_mode_config_helper_resume(priv->drm);
442
+}
338443
339444 static int compare_of(struct device *dev, void *data)
340445 {
....@@ -429,20 +534,56 @@
429534 return 0;
430535 };
431536
537
+static int meson_drv_remove(struct platform_device *pdev)
538
+{
539
+ component_master_del(&pdev->dev, &meson_drv_master_ops);
540
+
541
+ return 0;
542
+}
543
+
544
+static struct meson_drm_match_data meson_drm_gxbb_data = {
545
+ .compat = VPU_COMPATIBLE_GXBB,
546
+};
547
+
548
+static struct meson_drm_match_data meson_drm_gxl_data = {
549
+ .compat = VPU_COMPATIBLE_GXL,
550
+};
551
+
552
+static struct meson_drm_match_data meson_drm_gxm_data = {
553
+ .compat = VPU_COMPATIBLE_GXM,
554
+ .afbcd_ops = &meson_afbcd_gxm_ops,
555
+};
556
+
557
+static struct meson_drm_match_data meson_drm_g12a_data = {
558
+ .compat = VPU_COMPATIBLE_G12A,
559
+ .afbcd_ops = &meson_afbcd_g12a_ops,
560
+};
561
+
432562 static const struct of_device_id dt_match[] = {
433
- { .compatible = "amlogic,meson-gxbb-vpu" },
434
- { .compatible = "amlogic,meson-gxl-vpu" },
435
- { .compatible = "amlogic,meson-gxm-vpu" },
563
+ { .compatible = "amlogic,meson-gxbb-vpu",
564
+ .data = (void *)&meson_drm_gxbb_data },
565
+ { .compatible = "amlogic,meson-gxl-vpu",
566
+ .data = (void *)&meson_drm_gxl_data },
567
+ { .compatible = "amlogic,meson-gxm-vpu",
568
+ .data = (void *)&meson_drm_gxm_data },
569
+ { .compatible = "amlogic,meson-g12a-vpu",
570
+ .data = (void *)&meson_drm_g12a_data },
436571 {}
437572 };
438573 MODULE_DEVICE_TABLE(of, dt_match);
439574
575
+static const struct dev_pm_ops meson_drv_pm_ops = {
576
+ SET_SYSTEM_SLEEP_PM_OPS(meson_drv_pm_suspend, meson_drv_pm_resume)
577
+};
578
+
440579 static struct platform_driver meson_drm_platform_driver = {
441580 .probe = meson_drv_probe,
581
+ .remove = meson_drv_remove,
442582 .shutdown = meson_drv_shutdown,
443583 .driver = {
444584 .name = "meson-drm",
445585 .of_match_table = dt_match,
586
+ .pm = &meson_drv_pm_ops,
446587 },
447588 };
448589