diff options
Diffstat (limited to 'drivers/gpu/drm/v3d/v3d_drv.c')
| -rw-r--r-- | drivers/gpu/drm/v3d/v3d_drv.c | 383 |
1 files changed, 251 insertions, 132 deletions
diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index f0afcec72c34..e8a46c8bad8a 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -7,74 +7,49 @@ * This driver supports the Broadcom V3D 3.3 and 4.1 OpenGL ES GPUs. * For V3D 2.x support, see the VC4 driver. * - * Currently only single-core rendering using the binner and renderer - * is supported. The TFU (texture formatting unit) and V3D 4.x's CSD - * (compute shader dispatch) are not yet supported. + * The V3D GPU includes a tiled render (composed of a bin and render + * pipelines), the TFU (texture formatting unit), and the CSD (compute + * shader dispatch). */ #include <linux/clk.h> #include <linux/device.h> +#include <linux/dma-mapping.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_fb_helper.h> +#include <linux/sched/clock.h> +#include <linux/reset.h> + +#include <drm/drm_drv.h> +#include <drm/drm_managed.h> +#include <drm/drm_print.h> +#include <uapi/drm/v3d_drm.h> -#include "uapi/drm/v3d_drm.h" #include "v3d_drv.h" #include "v3d_regs.h" #define DRIVER_NAME "v3d" #define DRIVER_DESC "Broadcom V3D graphics" -#define DRIVER_DATE "20180419" #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 #define DRIVER_PATCHLEVEL 0 -#ifdef CONFIG_PM -static int v3d_runtime_suspend(struct device *dev) -{ - struct drm_device *drm = dev_get_drvdata(dev); - struct v3d_dev *v3d = to_v3d_dev(drm); - - v3d_irq_disable(v3d); - - clk_disable_unprepare(v3d->clk); - - return 0; -} - -static int v3d_runtime_resume(struct device *dev) -{ - struct drm_device *drm = dev_get_drvdata(dev); - struct v3d_dev *v3d = to_v3d_dev(drm); - int ret; - - ret = clk_prepare_enable(v3d->clk); - if (ret != 0) - return ret; - - /* XXX: VPM base */ - - v3d_mmu_set_page_table(v3d); - v3d_irq_enable(v3d); - - return 0; -} +/* Only expose the `super_pages` modparam if THP is enabled. */ +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +bool super_pages = true; +module_param_named(super_pages, super_pages, bool, 0400); +MODULE_PARM_DESC(super_pages, "Enable/Disable Super Pages support."); #endif -static const struct dev_pm_ops v3d_v3d_pm_ops = { - SET_RUNTIME_PM_OPS(v3d_runtime_suspend, v3d_runtime_resume, NULL) -}; - static int v3d_get_param_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct v3d_file_priv *v3d_priv = file_priv->driver_priv; struct v3d_dev *v3d = to_v3d_dev(dev); struct drm_v3d_get_param *args = data; - int ret; static const u32 reg_map[] = { [DRM_V3D_PARAM_V3D_UIFCFG] = V3D_HUB_UIFCFG, [DRM_V3D_PARAM_V3D_HUB_IDENT1] = V3D_HUB_IDENT1, @@ -100,23 +75,50 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data, if (args->value != 0) return -EINVAL; - ret = pm_runtime_get_sync(v3d->dev); if (args->param >= DRM_V3D_PARAM_V3D_CORE0_IDENT0 && args->param <= DRM_V3D_PARAM_V3D_CORE0_IDENT2) { args->value = V3D_CORE_READ(0, offset); } else { args->value = V3D_READ(offset); } - pm_runtime_mark_last_busy(v3d->dev); - pm_runtime_put_autosuspend(v3d->dev); return 0; } - switch (args->param) { case DRM_V3D_PARAM_SUPPORTS_TFU: args->value = 1; return 0; + case DRM_V3D_PARAM_SUPPORTS_CSD: + args->value = v3d_has_csd(v3d); + return 0; + case DRM_V3D_PARAM_SUPPORTS_CACHE_FLUSH: + args->value = 1; + return 0; + case DRM_V3D_PARAM_SUPPORTS_PERFMON: + args->value = (v3d->ver >= V3D_GEN_41); + return 0; + case DRM_V3D_PARAM_SUPPORTS_MULTISYNC_EXT: + args->value = 1; + return 0; + case DRM_V3D_PARAM_SUPPORTS_CPU_QUEUE: + args->value = 1; + return 0; + case DRM_V3D_PARAM_MAX_PERF_COUNTERS: + args->value = v3d->perfmon_info.max_counters; + return 0; + case DRM_V3D_PARAM_SUPPORTS_SUPER_PAGES: + args->value = !!v3d->gemfs; + return 0; + case DRM_V3D_PARAM_GLOBAL_RESET_COUNTER: + mutex_lock(&v3d->reset_lock); + args->value = v3d->reset_counter; + mutex_unlock(&v3d->reset_lock); + return 0; + case DRM_V3D_PARAM_CONTEXT_RESET_COUNTER: + mutex_lock(&v3d->reset_lock); + args->value = v3d_priv->reset_counter; + mutex_unlock(&v3d->reset_lock); + return 0; default: DRM_DEBUG("Unknown parameter %d\n", args->param); return -EINVAL; @@ -128,7 +130,7 @@ v3d_open(struct drm_device *dev, struct drm_file *file) { struct v3d_dev *v3d = to_v3d_dev(dev); struct v3d_file_priv *v3d_priv; - struct drm_sched_rq *rq; + struct drm_gpu_scheduler *sched; int i; v3d_priv = kzalloc(sizeof(*v3d_priv), GFP_KERNEL); @@ -138,10 +140,16 @@ v3d_open(struct drm_device *dev, struct drm_file *file) v3d_priv->v3d = v3d; for (i = 0; i < V3D_MAX_QUEUES; i++) { - rq = &v3d->queue[i].sched.sched_rq[DRM_SCHED_PRIORITY_NORMAL]; - drm_sched_entity_init(&v3d_priv->sched_entity[i], &rq, 1, NULL); + sched = &v3d->queue[i].sched; + drm_sched_entity_init(&v3d_priv->sched_entity[i], + DRM_SCHED_PRIORITY_NORMAL, &sched, + 1, NULL); + + memset(&v3d_priv->stats[i], 0, sizeof(v3d_priv->stats[i])); + seqcount_init(&v3d_priv->stats[i].lock); } + v3d_perfmon_open_file(v3d_priv); file->driver_priv = v3d_priv; return 0; @@ -150,30 +158,78 @@ v3d_open(struct drm_device *dev, struct drm_file *file) static void v3d_postclose(struct drm_device *dev, struct drm_file *file) { + struct v3d_dev *v3d = to_v3d_dev(dev); struct v3d_file_priv *v3d_priv = file->driver_priv; + unsigned long irqflags; enum v3d_queue q; for (q = 0; q < V3D_MAX_QUEUES; q++) { + struct v3d_queue_state *queue = &v3d->queue[q]; + struct v3d_job *job = queue->active_job; + drm_sched_entity_destroy(&v3d_priv->sched_entity[q]); + + if (job && job->base.entity == &v3d_priv->sched_entity[q]) { + spin_lock_irqsave(&queue->queue_lock, irqflags); + job->file_priv = NULL; + spin_unlock_irqrestore(&queue->queue_lock, irqflags); + } } + v3d_perfmon_close_file(v3d_priv); kfree(v3d_priv); } +void v3d_get_stats(const struct v3d_stats *stats, u64 timestamp, + u64 *active_runtime, u64 *jobs_completed) +{ + unsigned int seq; + + do { + seq = read_seqcount_begin(&stats->lock); + *active_runtime = stats->enabled_ns; + if (stats->start_ns) + *active_runtime += timestamp - stats->start_ns; + *jobs_completed = stats->jobs_completed; + } while (read_seqcount_retry(&stats->lock, seq)); +} + +static void v3d_show_fdinfo(struct drm_printer *p, struct drm_file *file) +{ + struct v3d_file_priv *file_priv = file->driver_priv; + u64 timestamp = local_clock(); + enum v3d_queue queue; + + for (queue = 0; queue < V3D_MAX_QUEUES; queue++) { + struct v3d_stats *stats = &file_priv->stats[queue]; + u64 active_runtime, jobs_completed; + + v3d_get_stats(stats, timestamp, &active_runtime, &jobs_completed); + + /* Note that, in case of a GPU reset, the time spent during an + * attempt of executing the job is not computed in the runtime. + */ + drm_printf(p, "drm-engine-%s: \t%llu ns\n", + v3d_queue_to_string(queue), active_runtime); + + /* Note that we only count jobs that completed. Therefore, jobs + * that were resubmitted due to a GPU reset are not computed. + */ + drm_printf(p, "v3d-jobs-%s: \t%llu jobs\n", + v3d_queue_to_string(queue), jobs_completed); + } + + drm_show_memory_stats(p, file); +} + static const struct file_operations v3d_drm_fops = { .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .mmap = v3d_mmap, - .poll = drm_poll, - .read = drm_read, - .compat_ioctl = drm_compat_ioctl, - .llseek = noop_llseek, + DRM_GEM_FOPS, + .show_fdinfo = drm_show_fdinfo, }; /* DRM_AUTH is required on SUBMIT_CL for now, while we don't have GMP - * protection between clients. Note that render nodes would be be + * protection between clients. Note that render nodes would be * able to submit CLs that could access BOs from clients authenticated * with the master node. The TFU doesn't use the GMP, so it would * need to stay DRM_AUTH until we do buffer size/offset validation. @@ -186,18 +242,18 @@ static const struct drm_ioctl_desc v3d_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(V3D_GET_PARAM, v3d_get_param_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(V3D_GET_BO_OFFSET, v3d_get_bo_offset_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(V3D_SUBMIT_TFU, v3d_submit_tfu_ioctl, DRM_RENDER_ALLOW | DRM_AUTH), + DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CSD, v3d_submit_csd_ioctl, DRM_RENDER_ALLOW | DRM_AUTH), + DRM_IOCTL_DEF_DRV(V3D_PERFMON_CREATE, v3d_perfmon_create_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(V3D_PERFMON_DESTROY, v3d_perfmon_destroy_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(V3D_PERFMON_GET_VALUES, v3d_perfmon_get_values_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CPU, v3d_submit_cpu_ioctl, DRM_RENDER_ALLOW | DRM_AUTH), + DRM_IOCTL_DEF_DRV(V3D_PERFMON_GET_COUNTER, v3d_perfmon_get_counter_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(V3D_PERFMON_SET_GLOBAL, v3d_perfmon_set_global_ioctl, DRM_RENDER_ALLOW), }; -static const struct vm_operations_struct v3d_vm_ops = { - .fault = v3d_gem_fault, - .open = drm_gem_vm_open, - .close = drm_gem_vm_close, -}; - -static struct drm_driver v3d_drm_driver = { +static const struct drm_driver v3d_drm_driver = { .driver_features = (DRIVER_GEM | DRIVER_RENDER | - DRIVER_PRIME | DRIVER_SYNCOBJ), .open = v3d_open, @@ -207,44 +263,64 @@ static struct drm_driver v3d_drm_driver = { .debugfs_init = v3d_debugfs_init, #endif - .gem_free_object_unlocked = v3d_free_object, - .gem_vm_ops = &v3d_vm_ops, - - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_import = drm_gem_prime_import, - .gem_prime_export = drm_gem_prime_export, - .gem_prime_res_obj = v3d_prime_res_obj, - .gem_prime_get_sg_table = v3d_prime_get_sg_table, + .gem_create_object = v3d_create_object, .gem_prime_import_sg_table = v3d_prime_import_sg_table, - .gem_prime_mmap = v3d_prime_mmap, .ioctls = v3d_drm_ioctls, .num_ioctls = ARRAY_SIZE(v3d_drm_ioctls), .fops = &v3d_drm_fops, + .show_fdinfo = v3d_show_fdinfo, .name = DRIVER_NAME, .desc = DRIVER_DESC, - .date = DRIVER_DATE, .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL, }; static const struct of_device_id v3d_of_match[] = { - { .compatible = "brcm,7268-v3d" }, - { .compatible = "brcm,7278-v3d" }, + { .compatible = "brcm,2711-v3d", .data = (void *)V3D_GEN_42 }, + { .compatible = "brcm,2712-v3d", .data = (void *)V3D_GEN_71 }, + { .compatible = "brcm,7268-v3d", .data = (void *)V3D_GEN_33 }, + { .compatible = "brcm,7278-v3d", .data = (void *)V3D_GEN_41 }, {}, }; MODULE_DEVICE_TABLE(of, v3d_of_match); +static void +v3d_idle_sms(struct v3d_dev *v3d) +{ + if (v3d->ver < V3D_GEN_71) + return; + + V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_CLEAR_POWER_OFF); + + if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS), + V3D_SMS_STATE) == V3D_SMS_IDLE), 100)) { + DRM_ERROR("Failed to power up SMS\n"); + } + + v3d_reset_sms(v3d); +} + +static void +v3d_power_off_sms(struct v3d_dev *v3d) +{ + if (v3d->ver < V3D_GEN_71) + return; + + V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_POWER_OFF); + + if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS), + V3D_SMS_STATE) == V3D_SMS_POWER_OFF_STATE), 100)) { + DRM_ERROR("Failed to power off SMS\n"); + } +} + static int map_regs(struct v3d_dev *v3d, void __iomem **regs, const char *name) { - struct resource *res = - platform_get_resource_byname(v3d->pdev, IORESOURCE_MEM, name); - - *regs = devm_ioremap_resource(v3d->dev, res); + *regs = devm_platform_ioremap_resource_byname(v3d_to_pdev(v3d), name); return PTR_ERR_OR_ZERO(*regs); } @@ -253,40 +329,93 @@ static int v3d_platform_drm_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct drm_device *drm; struct v3d_dev *v3d; + enum v3d_gen gen; int ret; - u32 ident1; + u32 mmu_debug; + u32 ident1, ident3; + u64 mask; - dev->coherent_dma_mask = DMA_BIT_MASK(36); + v3d = devm_drm_dev_alloc(dev, &v3d_drm_driver, struct v3d_dev, drm); + if (IS_ERR(v3d)) + return PTR_ERR(v3d); - v3d = kzalloc(sizeof(*v3d), GFP_KERNEL); - if (!v3d) - return -ENOMEM; - v3d->dev = dev; - v3d->pdev = pdev; drm = &v3d->drm; - ret = map_regs(v3d, &v3d->bridge_regs, "bridge"); - if (ret) - goto dev_free; + platform_set_drvdata(pdev, drm); + + gen = (uintptr_t)of_device_get_match_data(dev); + v3d->ver = gen; ret = map_regs(v3d, &v3d->hub_regs, "hub"); if (ret) - goto dev_free; + return ret; ret = map_regs(v3d, &v3d->core_regs[0], "core0"); if (ret) - goto dev_free; + return ret; + + if (v3d->ver >= V3D_GEN_71) { + ret = map_regs(v3d, &v3d->sms_regs, "sms"); + if (ret) + return ret; + } + + v3d->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(v3d->clk)) + return dev_err_probe(dev, PTR_ERR(v3d->clk), "Failed to get V3D clock\n"); + + ret = clk_prepare_enable(v3d->clk); + if (ret) { + dev_err(&pdev->dev, "Couldn't enable the V3D clock\n"); + return ret; + } + + v3d_idle_sms(v3d); + + mmu_debug = V3D_READ(V3D_MMU_DEBUG_INFO); + mask = DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_PA_WIDTH)); + ret = dma_set_mask_and_coherent(dev, mask); + if (ret) + goto clk_disable; + + v3d->va_width = 30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_VA_WIDTH); ident1 = V3D_READ(V3D_HUB_IDENT1); v3d->ver = (V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_TVER) * 10 + V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_REV)); + /* Make sure that the V3D tech version retrieved from the HW is equal + * to the one advertised by the device tree. + */ + WARN_ON(v3d->ver != gen); + v3d->cores = V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_NCORES); WARN_ON(v3d->cores > 1); /* multicore not yet implemented */ - if (v3d->ver < 41) { + ident3 = V3D_READ(V3D_HUB_IDENT3); + v3d->rev = V3D_GET_FIELD(ident3, V3D_HUB_IDENT3_IPREV); + + v3d_perfmon_init(v3d); + + v3d->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(v3d->reset)) { + ret = PTR_ERR(v3d->reset); + + if (ret == -EPROBE_DEFER) + goto clk_disable; + + v3d->reset = NULL; + ret = map_regs(v3d, &v3d->bridge_regs, "bridge"); + if (ret) { + dev_err(dev, + "Failed to get reset control or bridge regs\n"); + goto clk_disable; + } + } + + if (v3d->ver < V3D_GEN_41) { ret = map_regs(v3d, &v3d->gca_regs, "gca"); if (ret) - goto dev_free; + goto clk_disable; } v3d->mmu_scratch = dma_alloc_wc(dev, 4096, &v3d->mmu_scratch_paddr, @@ -294,57 +423,58 @@ static int v3d_platform_drm_probe(struct platform_device *pdev) if (!v3d->mmu_scratch) { dev_err(dev, "Failed to allocate MMU scratch page\n"); ret = -ENOMEM; - goto dev_free; + goto clk_disable; } - pm_runtime_use_autosuspend(dev); - pm_runtime_set_autosuspend_delay(dev, 50); - pm_runtime_enable(dev); - - ret = drm_dev_init(&v3d->drm, &v3d_drm_driver, dev); + ret = v3d_gem_init(drm); if (ret) goto dma_free; - platform_set_drvdata(pdev, drm); - drm->dev_private = v3d; - - ret = v3d_gem_init(drm); + ret = v3d_irq_init(v3d); if (ret) - goto dev_destroy; - - v3d_irq_init(v3d); + goto gem_destroy; ret = drm_dev_register(drm, 0); if (ret) - goto gem_destroy; + goto irq_disable; + + ret = v3d_sysfs_init(dev); + if (ret) + goto drm_unregister; return 0; +drm_unregister: + drm_dev_unregister(drm); +irq_disable: + v3d_irq_disable(v3d); gem_destroy: v3d_gem_destroy(drm); -dev_destroy: - drm_dev_put(drm); dma_free: dma_free_wc(dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr); -dev_free: - kfree(v3d); +clk_disable: + clk_disable_unprepare(v3d->clk); return ret; } -static int v3d_platform_drm_remove(struct platform_device *pdev) +static void v3d_platform_drm_remove(struct platform_device *pdev) { struct drm_device *drm = platform_get_drvdata(pdev); struct v3d_dev *v3d = to_v3d_dev(drm); + struct device *dev = &pdev->dev; + + v3d_sysfs_destroy(dev); drm_dev_unregister(drm); v3d_gem_destroy(drm); - drm_dev_put(drm); + dma_free_wc(v3d->drm.dev, 4096, v3d->mmu_scratch, + v3d->mmu_scratch_paddr); - dma_free_wc(v3d->dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr); + v3d_power_off_sms(v3d); - return 0; + clk_disable_unprepare(v3d->clk); } static struct platform_driver v3d_platform_driver = { @@ -356,18 +486,7 @@ static struct platform_driver v3d_platform_driver = { }, }; -static int __init v3d_drm_register(void) -{ - return platform_driver_register(&v3d_platform_driver); -} - -static void __exit v3d_drm_unregister(void) -{ - platform_driver_unregister(&v3d_platform_driver); -} - -module_init(v3d_drm_register); -module_exit(v3d_drm_unregister); +module_platform_driver(v3d_platform_driver); MODULE_ALIAS("platform:v3d-drm"); MODULE_DESCRIPTION("Broadcom V3D DRM Driver"); |
