| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2014-2015 Broadcom |
|---|
| 3 | 4 | * Copyright (C) 2013 Red Hat |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 6 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 7 | | - * published by the Free Software Foundation. |
|---|
| 8 | 5 | */ |
|---|
| 9 | 6 | |
|---|
| 10 | 7 | /** |
|---|
| .. | .. |
|---|
| 26 | 23 | #include <linux/clk.h> |
|---|
| 27 | 24 | #include <linux/component.h> |
|---|
| 28 | 25 | #include <linux/device.h> |
|---|
| 26 | +#include <linux/dma-mapping.h> |
|---|
| 29 | 27 | #include <linux/io.h> |
|---|
| 30 | 28 | #include <linux/module.h> |
|---|
| 31 | 29 | #include <linux/of_platform.h> |
|---|
| 32 | 30 | #include <linux/platform_device.h> |
|---|
| 33 | 31 | #include <linux/pm_runtime.h> |
|---|
| 32 | + |
|---|
| 33 | +#include <drm/drm_atomic_helper.h> |
|---|
| 34 | +#include <drm/drm_drv.h> |
|---|
| 34 | 35 | #include <drm/drm_fb_cma_helper.h> |
|---|
| 35 | 36 | #include <drm/drm_fb_helper.h> |
|---|
| 37 | +#include <drm/drm_vblank.h> |
|---|
| 36 | 38 | |
|---|
| 37 | 39 | #include "uapi/drm/vc4_drm.h" |
|---|
| 40 | + |
|---|
| 38 | 41 | #include "vc4_drv.h" |
|---|
| 39 | 42 | #include "vc4_regs.h" |
|---|
| 40 | 43 | |
|---|
| .. | .. |
|---|
| 71 | 74 | if (args->pad != 0) |
|---|
| 72 | 75 | return -EINVAL; |
|---|
| 73 | 76 | |
|---|
| 77 | + if (!vc4->v3d) |
|---|
| 78 | + return -ENODEV; |
|---|
| 79 | + |
|---|
| 74 | 80 | switch (args->param) { |
|---|
| 75 | 81 | case DRM_VC4_PARAM_V3D_IDENT0: |
|---|
| 76 | | - ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); |
|---|
| 77 | | - if (ret < 0) |
|---|
| 82 | + ret = vc4_v3d_pm_get(vc4); |
|---|
| 83 | + if (ret) |
|---|
| 78 | 84 | return ret; |
|---|
| 79 | 85 | args->value = V3D_READ(V3D_IDENT0); |
|---|
| 80 | | - pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev); |
|---|
| 81 | | - pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); |
|---|
| 86 | + vc4_v3d_pm_put(vc4); |
|---|
| 82 | 87 | break; |
|---|
| 83 | 88 | case DRM_VC4_PARAM_V3D_IDENT1: |
|---|
| 84 | | - ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); |
|---|
| 85 | | - if (ret < 0) |
|---|
| 89 | + ret = vc4_v3d_pm_get(vc4); |
|---|
| 90 | + if (ret) |
|---|
| 86 | 91 | return ret; |
|---|
| 87 | 92 | args->value = V3D_READ(V3D_IDENT1); |
|---|
| 88 | | - pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev); |
|---|
| 89 | | - pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); |
|---|
| 93 | + vc4_v3d_pm_put(vc4); |
|---|
| 90 | 94 | break; |
|---|
| 91 | 95 | case DRM_VC4_PARAM_V3D_IDENT2: |
|---|
| 92 | | - ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); |
|---|
| 93 | | - if (ret < 0) |
|---|
| 96 | + ret = vc4_v3d_pm_get(vc4); |
|---|
| 97 | + if (ret) |
|---|
| 94 | 98 | return ret; |
|---|
| 95 | 99 | args->value = V3D_READ(V3D_IDENT2); |
|---|
| 96 | | - pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev); |
|---|
| 97 | | - pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); |
|---|
| 100 | + vc4_v3d_pm_put(vc4); |
|---|
| 98 | 101 | break; |
|---|
| 99 | 102 | case DRM_VC4_PARAM_SUPPORTS_BRANCHES: |
|---|
| 100 | 103 | case DRM_VC4_PARAM_SUPPORTS_ETC1: |
|---|
| .. | .. |
|---|
| 127 | 130 | |
|---|
| 128 | 131 | static void vc4_close(struct drm_device *dev, struct drm_file *file) |
|---|
| 129 | 132 | { |
|---|
| 133 | + struct vc4_dev *vc4 = to_vc4_dev(dev); |
|---|
| 130 | 134 | struct vc4_file *vc4file = file->driver_priv; |
|---|
| 135 | + |
|---|
| 136 | + if (vc4file->bin_bo_used) |
|---|
| 137 | + vc4_v3d_bin_bo_put(vc4); |
|---|
| 131 | 138 | |
|---|
| 132 | 139 | vc4_perfmon_close_file(vc4file); |
|---|
| 133 | 140 | kfree(vc4file); |
|---|
| .. | .. |
|---|
| 174 | 181 | .driver_features = (DRIVER_MODESET | |
|---|
| 175 | 182 | DRIVER_ATOMIC | |
|---|
| 176 | 183 | DRIVER_GEM | |
|---|
| 177 | | - DRIVER_HAVE_IRQ | |
|---|
| 178 | 184 | DRIVER_RENDER | |
|---|
| 179 | | - DRIVER_PRIME | |
|---|
| 180 | 185 | DRIVER_SYNCOBJ), |
|---|
| 181 | | - .lastclose = drm_fb_helper_lastclose, |
|---|
| 182 | 186 | .open = vc4_open, |
|---|
| 183 | 187 | .postclose = vc4_close, |
|---|
| 184 | 188 | .irq_handler = vc4_irq, |
|---|
| 185 | 189 | .irq_preinstall = vc4_irq_preinstall, |
|---|
| 186 | 190 | .irq_postinstall = vc4_irq_postinstall, |
|---|
| 187 | 191 | .irq_uninstall = vc4_irq_uninstall, |
|---|
| 188 | | - |
|---|
| 189 | | - .get_scanout_position = vc4_crtc_get_scanoutpos, |
|---|
| 190 | | - .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos, |
|---|
| 191 | 192 | |
|---|
| 192 | 193 | #if defined(CONFIG_DEBUG_FS) |
|---|
| 193 | 194 | .debugfs_init = vc4_debugfs_init, |
|---|
| .. | .. |
|---|
| 199 | 200 | |
|---|
| 200 | 201 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
|---|
| 201 | 202 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, |
|---|
| 202 | | - .gem_prime_import = drm_gem_prime_import, |
|---|
| 203 | 203 | .gem_prime_export = vc4_prime_export, |
|---|
| 204 | | - .gem_prime_res_obj = vc4_prime_res_obj, |
|---|
| 205 | 204 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, |
|---|
| 206 | 205 | .gem_prime_import_sg_table = vc4_prime_import_sg_table, |
|---|
| 207 | 206 | .gem_prime_vmap = vc4_prime_vmap, |
|---|
| .. | .. |
|---|
| 238 | 237 | struct device_driver *drv = &drivers[i]->driver; |
|---|
| 239 | 238 | struct device *p = NULL, *d; |
|---|
| 240 | 239 | |
|---|
| 241 | | - while ((d = bus_find_device(&platform_bus_type, p, drv, |
|---|
| 242 | | - (void *)platform_bus_type.match))) { |
|---|
| 240 | + while ((d = platform_find_device_by_driver(p, drv))) { |
|---|
| 243 | 241 | put_device(p); |
|---|
| 244 | 242 | component_match_add(dev, match, compare_dev, d); |
|---|
| 245 | 243 | p = d; |
|---|
| .. | .. |
|---|
| 248 | 246 | } |
|---|
| 249 | 247 | } |
|---|
| 250 | 248 | |
|---|
| 251 | | -static void vc4_kick_out_firmware_fb(void) |
|---|
| 252 | | -{ |
|---|
| 253 | | - struct apertures_struct *ap; |
|---|
| 254 | | - |
|---|
| 255 | | - ap = alloc_apertures(1); |
|---|
| 256 | | - if (!ap) |
|---|
| 257 | | - return; |
|---|
| 258 | | - |
|---|
| 259 | | - /* Since VC4 is a UMA device, the simplefb node may have been |
|---|
| 260 | | - * located anywhere in memory. |
|---|
| 261 | | - */ |
|---|
| 262 | | - ap->ranges[0].base = 0; |
|---|
| 263 | | - ap->ranges[0].size = ~0; |
|---|
| 264 | | - |
|---|
| 265 | | - drm_fb_helper_remove_conflicting_framebuffers(ap, "vc4drmfb", false); |
|---|
| 266 | | - kfree(ap); |
|---|
| 267 | | -} |
|---|
| 249 | +static const struct of_device_id vc4_dma_range_matches[] = { |
|---|
| 250 | + { .compatible = "brcm,bcm2711-hvs" }, |
|---|
| 251 | + { .compatible = "brcm,bcm2835-hvs" }, |
|---|
| 252 | + { .compatible = "brcm,bcm2835-v3d" }, |
|---|
| 253 | + { .compatible = "brcm,cygnus-v3d" }, |
|---|
| 254 | + { .compatible = "brcm,vc4-v3d" }, |
|---|
| 255 | + {} |
|---|
| 256 | +}; |
|---|
| 268 | 257 | |
|---|
| 269 | 258 | static int vc4_drm_bind(struct device *dev) |
|---|
| 270 | 259 | { |
|---|
| 271 | 260 | struct platform_device *pdev = to_platform_device(dev); |
|---|
| 272 | 261 | struct drm_device *drm; |
|---|
| 273 | 262 | struct vc4_dev *vc4; |
|---|
| 263 | + struct device_node *node; |
|---|
| 264 | + struct drm_crtc *crtc; |
|---|
| 274 | 265 | int ret = 0; |
|---|
| 275 | 266 | |
|---|
| 276 | 267 | dev->coherent_dma_mask = DMA_BIT_MASK(32); |
|---|
| 277 | 268 | |
|---|
| 278 | | - vc4 = devm_kzalloc(dev, sizeof(*vc4), GFP_KERNEL); |
|---|
| 279 | | - if (!vc4) |
|---|
| 280 | | - return -ENOMEM; |
|---|
| 269 | + /* If VC4 V3D is missing, don't advertise render nodes. */ |
|---|
| 270 | + node = of_find_matching_node_and_match(NULL, vc4_v3d_dt_match, NULL); |
|---|
| 271 | + if (!node || !of_device_is_available(node)) |
|---|
| 272 | + vc4_drm_driver.driver_features &= ~DRIVER_RENDER; |
|---|
| 273 | + of_node_put(node); |
|---|
| 281 | 274 | |
|---|
| 282 | | - drm = drm_dev_alloc(&vc4_drm_driver, dev); |
|---|
| 283 | | - if (IS_ERR(drm)) |
|---|
| 284 | | - return PTR_ERR(drm); |
|---|
| 275 | + node = of_find_matching_node_and_match(NULL, vc4_dma_range_matches, |
|---|
| 276 | + NULL); |
|---|
| 277 | + if (node) { |
|---|
| 278 | + ret = of_dma_configure(dev, node, true); |
|---|
| 279 | + of_node_put(node); |
|---|
| 280 | + |
|---|
| 281 | + if (ret) |
|---|
| 282 | + return ret; |
|---|
| 283 | + } |
|---|
| 284 | + |
|---|
| 285 | + vc4 = devm_drm_dev_alloc(dev, &vc4_drm_driver, struct vc4_dev, base); |
|---|
| 286 | + if (IS_ERR(vc4)) |
|---|
| 287 | + return PTR_ERR(vc4); |
|---|
| 288 | + |
|---|
| 289 | + drm = &vc4->base; |
|---|
| 285 | 290 | platform_set_drvdata(pdev, drm); |
|---|
| 286 | | - vc4->dev = drm; |
|---|
| 287 | | - drm->dev_private = vc4; |
|---|
| 291 | + INIT_LIST_HEAD(&vc4->debugfs_list); |
|---|
| 292 | + |
|---|
| 293 | + mutex_init(&vc4->bin_bo_lock); |
|---|
| 288 | 294 | |
|---|
| 289 | 295 | ret = vc4_bo_cache_init(drm); |
|---|
| 290 | 296 | if (ret) |
|---|
| 291 | | - goto dev_put; |
|---|
| 297 | + return ret; |
|---|
| 292 | 298 | |
|---|
| 293 | | - drm_mode_config_init(drm); |
|---|
| 299 | + ret = drmm_mode_config_init(drm); |
|---|
| 300 | + if (ret) |
|---|
| 301 | + return ret; |
|---|
| 294 | 302 | |
|---|
| 295 | | - vc4_gem_init(drm); |
|---|
| 303 | + ret = vc4_gem_init(drm); |
|---|
| 304 | + if (ret) |
|---|
| 305 | + return ret; |
|---|
| 296 | 306 | |
|---|
| 297 | 307 | ret = component_bind_all(dev, drm); |
|---|
| 298 | 308 | if (ret) |
|---|
| 299 | | - goto gem_destroy; |
|---|
| 309 | + return ret; |
|---|
| 300 | 310 | |
|---|
| 301 | | - vc4_kick_out_firmware_fb(); |
|---|
| 311 | + ret = vc4_plane_create_additional_planes(drm); |
|---|
| 312 | + if (ret) |
|---|
| 313 | + goto unbind_all; |
|---|
| 314 | + |
|---|
| 315 | + drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false); |
|---|
| 316 | + |
|---|
| 317 | + ret = vc4_kms_load(drm); |
|---|
| 318 | + if (ret < 0) |
|---|
| 319 | + goto unbind_all; |
|---|
| 320 | + |
|---|
| 321 | + drm_for_each_crtc(crtc, drm) |
|---|
| 322 | + vc4_crtc_disable_at_boot(crtc); |
|---|
| 302 | 323 | |
|---|
| 303 | 324 | ret = drm_dev_register(drm, 0); |
|---|
| 304 | 325 | if (ret < 0) |
|---|
| 305 | 326 | goto unbind_all; |
|---|
| 306 | 327 | |
|---|
| 307 | | - vc4_kms_load(drm); |
|---|
| 328 | + drm_fbdev_generic_setup(drm, 16); |
|---|
| 308 | 329 | |
|---|
| 309 | 330 | return 0; |
|---|
| 310 | 331 | |
|---|
| 311 | 332 | unbind_all: |
|---|
| 312 | 333 | component_unbind_all(dev, drm); |
|---|
| 313 | | -gem_destroy: |
|---|
| 314 | | - vc4_gem_destroy(drm); |
|---|
| 315 | | - drm_mode_config_cleanup(drm); |
|---|
| 316 | | - vc4_bo_cache_destroy(drm); |
|---|
| 317 | | -dev_put: |
|---|
| 318 | | - drm_dev_put(drm); |
|---|
| 334 | + |
|---|
| 319 | 335 | return ret; |
|---|
| 320 | 336 | } |
|---|
| 321 | 337 | |
|---|
| 322 | 338 | static void vc4_drm_unbind(struct device *dev) |
|---|
| 323 | 339 | { |
|---|
| 324 | 340 | struct drm_device *drm = dev_get_drvdata(dev); |
|---|
| 325 | | - struct vc4_dev *vc4 = to_vc4_dev(drm); |
|---|
| 326 | 341 | |
|---|
| 327 | 342 | drm_dev_unregister(drm); |
|---|
| 328 | 343 | |
|---|
| 329 | | - drm_fb_cma_fbdev_fini(drm); |
|---|
| 330 | | - |
|---|
| 331 | | - drm_mode_config_cleanup(drm); |
|---|
| 332 | | - |
|---|
| 333 | | - drm_atomic_private_obj_fini(&vc4->ctm_manager); |
|---|
| 334 | | - |
|---|
| 335 | | - drm_dev_put(drm); |
|---|
| 344 | + drm_atomic_helper_shutdown(drm); |
|---|
| 336 | 345 | } |
|---|
| 337 | 346 | |
|---|
| 338 | 347 | static const struct component_master_ops vc4_drm_ops = { |
|---|
| .. | .. |
|---|
| 345 | 354 | &vc4_vec_driver, |
|---|
| 346 | 355 | &vc4_dpi_driver, |
|---|
| 347 | 356 | &vc4_dsi_driver, |
|---|
| 348 | | - &vc4_txp_driver, |
|---|
| 349 | 357 | &vc4_hvs_driver, |
|---|
| 358 | + &vc4_txp_driver, |
|---|
| 350 | 359 | &vc4_crtc_driver, |
|---|
| 351 | 360 | &vc4_v3d_driver, |
|---|
| 352 | 361 | }; |
|---|
| .. | .. |
|---|
| 370 | 379 | } |
|---|
| 371 | 380 | |
|---|
| 372 | 381 | static const struct of_device_id vc4_of_match[] = { |
|---|
| 382 | + { .compatible = "brcm,bcm2711-vc5", }, |
|---|
| 373 | 383 | { .compatible = "brcm,bcm2835-vc4", }, |
|---|
| 374 | 384 | { .compatible = "brcm,cygnus-vc4", }, |
|---|
| 375 | 385 | {}, |
|---|
| .. | .. |
|---|
| 394 | 404 | if (ret) |
|---|
| 395 | 405 | return ret; |
|---|
| 396 | 406 | |
|---|
| 397 | | - return platform_driver_register(&vc4_platform_driver); |
|---|
| 407 | + ret = platform_driver_register(&vc4_platform_driver); |
|---|
| 408 | + if (ret) |
|---|
| 409 | + platform_unregister_drivers(component_drivers, |
|---|
| 410 | + ARRAY_SIZE(component_drivers)); |
|---|
| 411 | + |
|---|
| 412 | + return ret; |
|---|
| 398 | 413 | } |
|---|
| 399 | 414 | |
|---|
| 400 | 415 | static void __exit vc4_drm_unregister(void) |
|---|