diff options
Diffstat (limited to 'drivers/gpu/drm/vc4')
-rw-r--r-- | drivers/gpu/drm/vc4/Kconfig | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_bo.c | 100 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_crtc.c | 105 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_debugfs.c | 86 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_dpi.c | 39 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.c | 48 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.h | 91 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_dsi.c | 176 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_gem.c | 60 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_hdmi.c | 170 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_hvs.c | 180 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_irq.c | 29 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_kms.c | 123 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_perfmon.c | 18 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_plane.c | 76 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_regs.h | 51 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_render_cl.c | 23 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_txp.c | 56 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_v3d.c | 312 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_vec.c | 83 |
20 files changed, 1081 insertions, 746 deletions
diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index fdae18aeab4f..7c2317efd5b7 100644 --- a/drivers/gpu/drm/vc4/Kconfig +++ b/drivers/gpu/drm/vc4/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config DRM_VC4 tristate "Broadcom VC4 Graphics" depends on ARCH_BCM || ARCH_BCM2835 || COMPILE_TEST diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 8dcce7182bb7..1434bb829267 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -40,7 +40,7 @@ static bool is_user_label(int label) return label >= VC4_BO_TYPE_COUNT; } -static void vc4_bo_stats_dump(struct vc4_dev *vc4) +static void vc4_bo_stats_print(struct drm_printer *p, struct vc4_dev *vc4) { int i; @@ -48,58 +48,35 @@ static void vc4_bo_stats_dump(struct vc4_dev *vc4) if (!vc4->bo_labels[i].num_allocated) continue; - DRM_INFO("%30s: %6dkb BOs (%d)\n", - vc4->bo_labels[i].name, - vc4->bo_labels[i].size_allocated / 1024, - vc4->bo_labels[i].num_allocated); + drm_printf(p, "%30s: %6dkb BOs (%d)\n", + vc4->bo_labels[i].name, + vc4->bo_labels[i].size_allocated / 1024, + vc4->bo_labels[i].num_allocated); } mutex_lock(&vc4->purgeable.lock); if (vc4->purgeable.num) - DRM_INFO("%30s: %6zdkb BOs (%d)\n", "userspace BO cache", - vc4->purgeable.size / 1024, vc4->purgeable.num); + drm_printf(p, "%30s: %6zdkb BOs (%d)\n", "userspace BO cache", + vc4->purgeable.size / 1024, vc4->purgeable.num); if (vc4->purgeable.purged_num) - DRM_INFO("%30s: %6zdkb BOs (%d)\n", "total purged BO", - vc4->purgeable.purged_size / 1024, - vc4->purgeable.purged_num); + drm_printf(p, "%30s: %6zdkb BOs (%d)\n", "total purged BO", + vc4->purgeable.purged_size / 1024, + vc4->purgeable.purged_num); mutex_unlock(&vc4->purgeable.lock); } -#ifdef CONFIG_DEBUG_FS -int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) +static int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *)m->private; struct drm_device *dev = node->minor->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); - int i; + struct drm_printer p = drm_seq_file_printer(m); - mutex_lock(&vc4->bo_lock); - for (i = 0; i < vc4->num_labels; i++) { - if (!vc4->bo_labels[i].num_allocated) - continue; - - seq_printf(m, "%30s: %6dkb BOs (%d)\n", - vc4->bo_labels[i].name, - vc4->bo_labels[i].size_allocated / 1024, - vc4->bo_labels[i].num_allocated); - } - mutex_unlock(&vc4->bo_lock); - - mutex_lock(&vc4->purgeable.lock); - if (vc4->purgeable.num) - seq_printf(m, "%30s: %6zdkb BOs (%d)\n", "userspace BO cache", - vc4->purgeable.size / 1024, vc4->purgeable.num); - - if (vc4->purgeable.purged_num) - seq_printf(m, "%30s: %6zdkb BOs (%d)\n", "total purged BO", - vc4->purgeable.purged_size / 1024, - vc4->purgeable.purged_num); - mutex_unlock(&vc4->purgeable.lock); + vc4_bo_stats_print(&p, vc4); return 0; } -#endif /* Takes ownership of *name and returns the appropriate slot for it in * the bo_labels[] array, extending it as necessary. @@ -201,8 +178,6 @@ static void vc4_bo_destroy(struct vc4_bo *bo) bo->validated_shader = NULL; } - reservation_object_fini(&bo->_resv); - drm_gem_cma_free_object(obj); } @@ -427,8 +402,6 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) vc4->bo_labels[VC4_BO_TYPE_KERNEL].num_allocated++; vc4->bo_labels[VC4_BO_TYPE_KERNEL].size_allocated += size; mutex_unlock(&vc4->bo_lock); - bo->resv = &bo->_resv; - reservation_object_init(bo->resv); return &bo->base.base; } @@ -479,8 +452,9 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size, } if (IS_ERR(cma_obj)) { + struct drm_printer p = drm_info_printer(vc4->dev->dev); DRM_ERROR("Failed to allocate from CMA:\n"); - vc4_bo_stats_dump(vc4); + vc4_bo_stats_print(&p, vc4); return ERR_PTR(-ENOMEM); } bo = to_vc4_bo(&cma_obj->base); @@ -684,13 +658,6 @@ static void vc4_bo_cache_time_timer(struct timer_list *t) schedule_work(&vc4->bo_cache.time_work); } -struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj) -{ - struct vc4_bo *bo = to_vc4_bo(obj); - - return bo->resv; -} - struct dma_buf * vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) { @@ -822,25 +789,46 @@ vc4_prime_import_sg_table(struct drm_device *dev, struct sg_table *sgt) { struct drm_gem_object *obj; - struct vc4_bo *bo; obj = drm_gem_cma_prime_import_sg_table(dev, attach, sgt); if (IS_ERR(obj)) return obj; - bo = to_vc4_bo(obj); - bo->resv = attach->dmabuf->resv; + obj->resv = attach->dmabuf->resv; return obj; } +static int vc4_grab_bin_bo(struct vc4_dev *vc4, struct vc4_file *vc4file) +{ + int ret; + + if (!vc4->v3d) + return -ENODEV; + + if (vc4file->bin_bo_used) + return 0; + + ret = vc4_v3d_bin_bo_get(vc4, &vc4file->bin_bo_used); + if (ret) + return ret; + + return 0; +} + int vc4_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_vc4_create_bo *args = data; + struct vc4_file *vc4file = file_priv->driver_priv; + struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_bo *bo = NULL; int ret; + ret = vc4_grab_bin_bo(vc4, vc4file); + if (ret) + return ret; + /* * We can't allocate from the BO cache, because the BOs don't * get zeroed, and that might leak data between users. @@ -881,6 +869,8 @@ vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_vc4_create_shader_bo *args = data; + struct vc4_file *vc4file = file_priv->driver_priv; + struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_bo *bo = NULL; int ret; @@ -900,6 +890,10 @@ vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, return -EINVAL; } + ret = vc4_grab_bin_bo(vc4, vc4file); + if (ret) + return ret; + bo = vc4_bo_create(dev, args->size, true, VC4_BO_TYPE_V3D_SHADER); if (IS_ERR(bo)) return PTR_ERR(bo); @@ -929,7 +923,7 @@ vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, */ ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); - fail: +fail: drm_gem_object_put_unlocked(&bo->base.base); return ret; @@ -1038,6 +1032,8 @@ int vc4_bo_cache_init(struct drm_device *dev) mutex_init(&vc4->bo_lock); + vc4_debugfs_add_file(dev, "bo_stats", vc4_bo_stats_debugfs, NULL); + INIT_LIST_HEAD(&vc4->bo_cache.time_list); INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 1baa10e94484..5e09389e1514 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -35,6 +35,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_uapi.h> +#include <drm/drm_print.h> #include <drm/drm_probe_helper.h> #include <linux/clk.h> #include <drm/drm_fb_cma_helper.h> @@ -67,67 +68,22 @@ to_vc4_crtc_state(struct drm_crtc_state *crtc_state) #define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset)) #define CRTC_READ(offset) readl(vc4_crtc->regs + (offset)) -#define CRTC_REG(reg) { reg, #reg } -static const struct { - u32 reg; - const char *name; -} crtc_regs[] = { - CRTC_REG(PV_CONTROL), - CRTC_REG(PV_V_CONTROL), - CRTC_REG(PV_VSYNCD_EVEN), - CRTC_REG(PV_HORZA), - CRTC_REG(PV_HORZB), - CRTC_REG(PV_VERTA), - CRTC_REG(PV_VERTB), - CRTC_REG(PV_VERTA_EVEN), - CRTC_REG(PV_VERTB_EVEN), - CRTC_REG(PV_INTEN), - CRTC_REG(PV_INTSTAT), - CRTC_REG(PV_STAT), - CRTC_REG(PV_HACT_ACT), +static const struct debugfs_reg32 crtc_regs[] = { + VC4_REG32(PV_CONTROL), + VC4_REG32(PV_V_CONTROL), + VC4_REG32(PV_VSYNCD_EVEN), + VC4_REG32(PV_HORZA), + VC4_REG32(PV_HORZB), + VC4_REG32(PV_VERTA), + VC4_REG32(PV_VERTB), + VC4_REG32(PV_VERTA_EVEN), + VC4_REG32(PV_VERTB_EVEN), + VC4_REG32(PV_INTEN), + VC4_REG32(PV_INTSTAT), + VC4_REG32(PV_STAT), + VC4_REG32(PV_HACT_ACT), }; -static void vc4_crtc_dump_regs(struct vc4_crtc *vc4_crtc) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(crtc_regs); i++) { - DRM_INFO("0x%04x (%s): 0x%08x\n", - crtc_regs[i].reg, crtc_regs[i].name, - CRTC_READ(crtc_regs[i].reg)); - } -} - -#ifdef CONFIG_DEBUG_FS -int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused) -{ - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *dev = node->minor->dev; - int crtc_index = (uintptr_t)node->info_ent->data; - struct drm_crtc *crtc; - struct vc4_crtc *vc4_crtc; - int i; - - i = 0; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (i == crtc_index) - break; - i++; - } - if (!crtc) - return 0; - vc4_crtc = to_vc4_crtc(crtc); - - for (i = 0; i < ARRAY_SIZE(crtc_regs); i++) { - seq_printf(m, "%s (0x%04x): 0x%08x\n", - crtc_regs[i].name, crtc_regs[i].reg, - CRTC_READ(crtc_regs[i].reg)); - } - - return 0; -} -#endif - bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, bool in_vblank_irq, int *vpos, int *hpos, ktime_t *stime, ktime_t *etime, @@ -434,8 +390,10 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) bool debug_dump_regs = false; if (debug_dump_regs) { - DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc)); - vc4_crtc_dump_regs(vc4_crtc); + struct drm_printer p = drm_info_printer(&vc4_crtc->pdev->dev); + dev_info(&vc4_crtc->pdev->dev, "CRTC %d regs before:\n", + drm_crtc_index(crtc)); + drm_print_regset32(&p, &vc4_crtc->regset); } if (vc4_crtc->channel == 2) { @@ -476,8 +434,10 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) vc4_crtc_lut_load(crtc); if (debug_dump_regs) { - DRM_INFO("CRTC %d regs after:\n", drm_crtc_index(crtc)); - vc4_crtc_dump_regs(vc4_crtc); + struct drm_printer p = drm_info_printer(&vc4_crtc->pdev->dev); + dev_info(&vc4_crtc->pdev->dev, "CRTC %d regs after:\n", + drm_crtc_index(crtc)); + drm_print_regset32(&p, &vc4_crtc->regset); } } @@ -834,6 +794,14 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc) drm_crtc_send_vblank_event(crtc, vc4_crtc->event); vc4_crtc->event = NULL; drm_crtc_vblank_put(crtc); + + /* Wait for the page flip to unmask the underrun to ensure that + * the display list was updated by the hardware. Before that + * happens, the HVS will be using the previous display list with + * the CRTC and encoder already reconfigured, leading to + * underruns. This can be seen when reconfiguring the CRTC. + */ + vc4_hvs_unmask_underrun(dev, vc4_crtc->channel); } spin_unlock_irqrestore(&dev->event_lock, flags); } @@ -1075,6 +1043,7 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { static const struct vc4_crtc_data pv0_data = { .hvs_channel = 0, + .debugfs_name = "crtc0_regs", .encoder_types = { [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI0, [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_DPI, @@ -1083,6 +1052,7 @@ static const struct vc4_crtc_data pv0_data = { static const struct vc4_crtc_data pv1_data = { .hvs_channel = 2, + .debugfs_name = "crtc1_regs", .encoder_types = { [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI1, [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_SMI, @@ -1091,6 +1061,7 @@ static const struct vc4_crtc_data pv1_data = { static const struct vc4_crtc_data pv2_data = { .hvs_channel = 1, + .debugfs_name = "crtc2_regs", .encoder_types = { [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI, [PV_CONTROL_CLK_SELECT_VEC] = VC4_ENCODER_TYPE_VEC, @@ -1169,11 +1140,16 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) if (!match) return -ENODEV; vc4_crtc->data = match->data; + vc4_crtc->pdev = pdev; vc4_crtc->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(vc4_crtc->regs)) return PTR_ERR(vc4_crtc->regs); + vc4_crtc->regset.base = vc4_crtc->regs; + vc4_crtc->regset.regs = crtc_regs; + vc4_crtc->regset.nregs = ARRAY_SIZE(crtc_regs); + /* For now, we create just the primary and the legacy cursor * planes. We should be able to stack more planes on easily, * but to do that we would need to compute the bandwidth @@ -1247,6 +1223,9 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) platform_set_drvdata(pdev, vc4_crtc); + vc4_debugfs_add_regset32(drm, vc4_crtc->data->debugfs_name, + &vc4_crtc->regset); + return 0; err_destroy_planes: diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c index 7a0003de71ab..f9b46911fa50 100644 --- a/drivers/gpu/drm/vc4/vc4_debugfs.c +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c @@ -15,26 +15,78 @@ #include "vc4_drv.h" #include "vc4_regs.h" -static const struct drm_info_list vc4_debugfs_list[] = { - {"bo_stats", vc4_bo_stats_debugfs, 0}, - {"dpi_regs", vc4_dpi_debugfs_regs, 0}, - {"dsi1_regs", vc4_dsi_debugfs_regs, 0, (void *)(uintptr_t)1}, - {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, - {"vec_regs", vc4_vec_debugfs_regs, 0}, - {"txp_regs", vc4_txp_debugfs_regs, 0}, - {"hvs_regs", vc4_hvs_debugfs_regs, 0}, - {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, - {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1}, - {"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2}, - {"v3d_ident", vc4_v3d_debugfs_ident, 0}, - {"v3d_regs", vc4_v3d_debugfs_regs, 0}, +struct vc4_debugfs_info_entry { + struct list_head link; + struct drm_info_list info; }; -#define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list) - +/** + * Called at drm_dev_register() time on each of the minors registered + * by the DRM device, to attach the debugfs files. + */ int vc4_debugfs_init(struct drm_minor *minor) { - return drm_debugfs_create_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES, - minor->debugfs_root, minor); + struct vc4_dev *vc4 = to_vc4_dev(minor->dev); + struct vc4_debugfs_info_entry *entry; + + debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR, + minor->debugfs_root, &vc4->load_tracker_enabled); + + list_for_each_entry(entry, &vc4->debugfs_list, link) { + int ret = drm_debugfs_create_files(&entry->info, 1, + minor->debugfs_root, minor); + + if (ret) + return ret; + } + + return 0; +} + +static int vc4_debugfs_regset32(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct debugfs_regset32 *regset = node->info_ent->data; + struct drm_printer p = drm_seq_file_printer(m); + + drm_print_regset32(&p, regset); + + return 0; +} + +/** + * Registers a debugfs file with a callback function for a vc4 component. + * + * This is like drm_debugfs_create_files(), but that can only be + * called a given DRM minor, while the various VC4 components want to + * register their debugfs files during the component bind process. We + * track the request and delay it to be called on each minor during + * vc4_debugfs_init(). + */ +void vc4_debugfs_add_file(struct drm_device *dev, + const char *name, + int (*show)(struct seq_file*, void*), + void *data) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + struct vc4_debugfs_info_entry *entry = + devm_kzalloc(dev->dev, sizeof(*entry), GFP_KERNEL); + + if (!entry) + return; + + entry->info.name = name; + entry->info.show = show; + entry->info.data = data; + + list_add(&entry->link, &vc4->debugfs_list); +} + +void vc4_debugfs_add_regset32(struct drm_device *drm, + const char *name, + struct debugfs_regset32 *regset) +{ + vc4_debugfs_add_file(drm, name, vc4_debugfs_regset32, regset); } diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 169521e547ba..34f90ca8f479 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -101,6 +101,8 @@ struct vc4_dpi { struct clk *pixel_clock; struct clk *core_clock; + + struct debugfs_regset32 regset; }; #define DPI_READ(offset) readl(dpi->regs + (offset)) @@ -118,37 +120,11 @@ to_vc4_dpi_encoder(struct drm_encoder *encoder) return container_of(encoder, struct vc4_dpi_encoder, base.base); } -#define DPI_REG(reg) { reg, #reg } -static const struct { - u32 reg; - const char *name; -} dpi_regs[] = { - DPI_REG(DPI_C), - DPI_REG(DPI_ID), +static const struct debugfs_reg32 dpi_regs[] = { + VC4_REG32(DPI_C), + VC4_REG32(DPI_ID), }; -#ifdef CONFIG_DEBUG_FS -int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused) -{ - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *dev = node->minor->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_dpi *dpi = vc4->dpi; - int i; - - if (!dpi) - return 0; - - for (i = 0; i < ARRAY_SIZE(dpi_regs); i++) { - seq_printf(m, "%s (0x%04x): 0x%08x\n", - dpi_regs[i].name, dpi_regs[i].reg, - DPI_READ(dpi_regs[i].reg)); - } - - return 0; -} -#endif - static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = { .destroy = drm_encoder_cleanup, }; @@ -314,6 +290,9 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) dpi->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(dpi->regs)) return PTR_ERR(dpi->regs); + dpi->regset.base = dpi->regs; + dpi->regset.regs = dpi_regs; + dpi->regset.nregs = ARRAY_SIZE(dpi_regs); if (DPI_READ(DPI_ID) != DPI_ID_VALUE) { dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n", @@ -352,6 +331,8 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) vc4->dpi = dpi; + vc4_debugfs_add_regset32(drm, "dpi_regs", &dpi->regset); + return 0; err_destroy_encoder: diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 5fcd2f0da7f7..0f99ad03614e 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -72,30 +72,30 @@ static int vc4_get_param_ioctl(struct drm_device *dev, void *data, if (args->pad != 0) return -EINVAL; + if (!vc4->v3d) + return -ENODEV; + switch (args->param) { case DRM_VC4_PARAM_V3D_IDENT0: - ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); - if (ret < 0) + ret = vc4_v3d_pm_get(vc4); + if (ret) return ret; args->value = V3D_READ(V3D_IDENT0); - pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev); - pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); + vc4_v3d_pm_put(vc4); break; case DRM_VC4_PARAM_V3D_IDENT1: - ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); - if (ret < 0) + ret = vc4_v3d_pm_get(vc4); + if (ret) return ret; args->value = V3D_READ(V3D_IDENT1); - pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev); - pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); + vc4_v3d_pm_put(vc4); break; case DRM_VC4_PARAM_V3D_IDENT2: - ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); - if (ret < 0) + ret = vc4_v3d_pm_get(vc4); + if (ret) return ret; args->value = V3D_READ(V3D_IDENT2); - pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev); - pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); + vc4_v3d_pm_put(vc4); break; case DRM_VC4_PARAM_SUPPORTS_BRANCHES: case DRM_VC4_PARAM_SUPPORTS_ETC1: @@ -128,8 +128,12 @@ static int vc4_open(struct drm_device *dev, struct drm_file *file) static void vc4_close(struct drm_device *dev, struct drm_file *file) { + struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_file *vc4file = file->driver_priv; + if (vc4file->bin_bo_used) + vc4_v3d_bin_bo_put(vc4); + vc4_perfmon_close_file(vc4file); kfree(vc4file); } @@ -200,7 +204,6 @@ static struct drm_driver vc4_drm_driver = { .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = drm_gem_prime_import, .gem_prime_export = vc4_prime_export, - .gem_prime_res_obj = vc4_prime_res_obj, .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, .gem_prime_import_sg_table = vc4_prime_import_sg_table, .gem_prime_vmap = vc4_prime_vmap, @@ -252,6 +255,7 @@ static int vc4_drm_bind(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm; struct vc4_dev *vc4; + struct device_node *node; int ret = 0; dev->coherent_dma_mask = DMA_BIT_MASK(32); @@ -260,12 +264,21 @@ static int vc4_drm_bind(struct device *dev) if (!vc4) return -ENOMEM; + /* If VC4 V3D is missing, don't advertise render nodes. */ + node = of_find_matching_node_and_match(NULL, vc4_v3d_dt_match, NULL); + if (!node || !of_device_is_available(node)) + vc4_drm_driver.driver_features &= ~DRIVER_RENDER; + of_node_put(node); + drm = drm_dev_alloc(&vc4_drm_driver, dev); if (IS_ERR(drm)) return PTR_ERR(drm); platform_set_drvdata(pdev, drm); vc4->dev = drm; drm->dev_private = vc4; + INIT_LIST_HEAD(&vc4->debugfs_list); + + mutex_init(&vc4->bin_bo_lock); ret = vc4_bo_cache_init(drm); if (ret) @@ -281,13 +294,15 @@ static int vc4_drm_bind(struct device *dev) drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false); - ret = drm_dev_register(drm, 0); + ret = vc4_kms_load(drm); if (ret < 0) goto unbind_all; - vc4_kms_load(drm); + ret = drm_dev_register(drm, 0); + if (ret < 0) + goto unbind_all; - drm_fbdev_generic_setup(drm, 32); + drm_fbdev_generic_setup(drm, 16); return 0; @@ -312,6 +327,7 @@ static void vc4_drm_unbind(struct device *dev) drm_mode_config_cleanup(drm); + drm_atomic_private_obj_fini(&vc4->load_tracker); drm_atomic_private_obj_fini(&vc4->ctm_manager); drm_dev_put(drm); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 2c635f001c71..9170a24ec5f5 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -7,7 +7,6 @@ */ #include <linux/mm_types.h> -#include <linux/reservation.h> #include <drm/drmP.h> #include <drm/drm_util.h> #include <drm/drm_encoder.h> @@ -185,10 +184,20 @@ struct vc4_dev { /* Bitmask of the current bin_alloc used for overflow memory. */ uint32_t bin_alloc_overflow; + /* Incremented when an underrun error happened after an atomic commit. + * This is particularly useful to detect when a specific modeset is too + * demanding in term of memory or HVS bandwidth which is hard to guess + * at atomic check time. + */ + atomic_t underrun; + struct work_struct overflow_mem_work; int power_refcount; + /* Set to true when the load tracker is active. */ + bool load_tracker_enabled; + /* Mutex controlling the power refcount. */ struct mutex power_lock; @@ -201,6 +210,17 @@ struct vc4_dev { struct drm_modeset_lock ctm_state_lock; struct drm_private_obj ctm_manager; + struct drm_private_obj load_tracker; + + /* List of vc4_debugfs_info_entry for adding to debugfs once + * the minor is available (after drm_dev_register()). + */ + struct list_head debugfs_list; + + /* Mutex for binner bo allocation. */ + struct mutex bin_bo_lock; + /* Reference count for our binner bo. */ + struct kref bin_bo_kref; }; static inline struct vc4_dev * @@ -240,10 +260,6 @@ struct vc4_bo { */ struct vc4_validated_shader_info *validated_shader; - /* normally (resv == &_resv) except for imported bo's */ - struct reservation_object *resv; - struct reservation_object _resv; - /* One of enum vc4_kernel_bo_type, or VC4_BO_TYPE_COUNT + i * for user-allocated labels. */ @@ -290,6 +306,7 @@ struct vc4_v3d { struct platform_device *pdev; void __iomem *regs; struct clk *clk; + struct debugfs_regset32 regset; }; struct vc4_hvs { @@ -306,6 +323,7 @@ struct vc4_hvs { spinlock_t mm_lock; struct drm_mm_node mitchell_netravali_filter; + struct debugfs_regset32 regset; }; struct vc4_plane { @@ -376,6 +394,16 @@ struct vc4_plane_state { * when async update is not possible. */ bool dlist_initialized; + + /* Load of this plane on the HVS block. The load is expressed in HVS + * cycles/sec. + */ + u64 hvs_load; + + /* Memory bandwidth needed for this plane. This is expressed in + * bytes/sec. + */ + u64 membus_load; }; static inline struct vc4_plane_state * @@ -411,10 +439,12 @@ struct vc4_crtc_data { int hvs_channel; enum vc4_encoder_type encoder_types[4]; + const char *debugfs_name; }; struct vc4_crtc { struct drm_crtc base; + struct platform_device *pdev; const struct vc4_crtc_data *data; void __iomem *regs; @@ -431,6 +461,8 @@ struct vc4_crtc { u32 cob_size; struct drm_pending_vblank_event *event; + + struct debugfs_regset32 regset; }; static inline struct vc4_crtc * @@ -444,6 +476,8 @@ to_vc4_crtc(struct drm_crtc *crtc) #define HVS_READ(offset) readl(vc4->hvs->regs + offset) #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) +#define VC4_REG32(reg) { .name = #reg, .offset = reg } + struct vc4_exec_info { /* Sequence number for this bin/render job. */ uint64_t seqno; @@ -555,6 +589,11 @@ struct vc4_exec_info { * NULL otherwise. */ struct vc4_perfmon *perfmon; + + /* Whether the exec has taken a reference to the binner BO, which should + * happen with a VC4_PACKET_TILE_BINNING_MODE_CONFIG packet. + */ + bool bin_bo_used; }; /* Per-open file private data. Any driver-specific resource that has to be @@ -565,6 +604,8 @@ struct vc4_file { struct idr idr; struct mutex lock; } perfmon; + + bool bin_bo_used; }; static inline struct vc4_exec_info * @@ -685,7 +726,6 @@ int vc4_label_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); vm_fault_t vc4_fault(struct vm_fault *vmf); int vc4_mmap(struct file *filp, struct vm_area_struct *vma); -struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj); int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); struct drm_gem_object *vc4_prime_import_sg_table(struct drm_device *dev, struct dma_buf_attachment *attach, @@ -693,7 +733,6 @@ struct drm_gem_object *vc4_prime_import_sg_table(struct drm_device *dev, void *vc4_prime_vmap(struct drm_gem_object *obj); int vc4_bo_cache_init(struct drm_device *dev); void vc4_bo_cache_destroy(struct drm_device *dev); -int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); int vc4_bo_inc_usecnt(struct vc4_bo *bo); void vc4_bo_dec_usecnt(struct vc4_bo *bo); void vc4_bo_add_to_purgeable_pool(struct vc4_bo *bo); @@ -701,7 +740,6 @@ void vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo); /* vc4_crtc.c */ extern struct platform_driver vc4_crtc_driver; -int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg); bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, bool in_vblank_irq, int *vpos, int *hpos, ktime_t *stime, ktime_t *etime, @@ -714,17 +752,37 @@ void vc4_crtc_get_margins(struct drm_crtc_state *state, /* vc4_debugfs.c */ int vc4_debugfs_init(struct drm_minor *minor); +#ifdef CONFIG_DEBUG_FS +void vc4_debugfs_add_file(struct drm_device *drm, + const char *filename, + int (*show)(struct seq_file*, void*), + void *data); +void vc4_debugfs_add_regset32(struct drm_device *drm, + const char *filename, + struct debugfs_regset32 *regset); +#else +static inline void vc4_debugfs_add_file(struct drm_device *drm, + const char *filename, + int (*show)(struct seq_file*, void*), + void *data) +{ +} + +static inline void vc4_debugfs_add_regset32(struct drm_device *drm, + const char *filename, + struct debugfs_regset32 *regset) +{ +} +#endif /* vc4_drv.c */ void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); /* vc4_dpi.c */ extern struct platform_driver vc4_dpi_driver; -int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused); /* vc4_dsi.c */ extern struct platform_driver vc4_dsi_driver; -int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused); /* vc4_fence.c */ extern const struct dma_fence_ops vc4_fence_ops; @@ -752,15 +810,12 @@ int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data, /* vc4_hdmi.c */ extern struct platform_driver vc4_hdmi_driver; -int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); /* vc4_vec.c */ extern struct platform_driver vc4_vec_driver; -int vc4_vec_debugfs_regs(struct seq_file *m, void *unused); /* vc4_txp.c */ extern struct platform_driver vc4_txp_driver; -int vc4_txp_debugfs_regs(struct seq_file *m, void *unused); /* vc4_irq.c */ irqreturn_t vc4_irq(int irq, void *arg); @@ -772,7 +827,8 @@ void vc4_irq_reset(struct drm_device *dev); /* vc4_hvs.c */ extern struct platform_driver vc4_hvs_driver; void vc4_hvs_dump_state(struct drm_device *dev); -int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused); +void vc4_hvs_unmask_underrun(struct drm_device *dev, int channel); +void vc4_hvs_mask_underrun(struct drm_device *dev, int channel); /* vc4_kms.c */ int vc4_kms_load(struct drm_device *dev); @@ -787,9 +843,12 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, /* vc4_v3d.c */ extern struct platform_driver vc4_v3d_driver; -int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused); -int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused); +extern const struct of_device_id vc4_v3d_dt_match[]; int vc4_v3d_get_bin_slot(struct vc4_dev *vc4); +int vc4_v3d_bin_bo_get(struct vc4_dev *vc4, bool *used); +void vc4_v3d_bin_bo_put(struct vc4_dev *vc4); +int vc4_v3d_pm_get(struct vc4_dev *vc4); +void vc4_v3d_pm_put(struct vc4_dev *vc4); /* vc4_validate.c */ int diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 11702e1d9011..2ea4e20b7b8a 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -41,6 +41,7 @@ #include <linux/component.h> #include <linux/dmaengine.h> #include <linux/i2c.h> +#include <linux/io.h> #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> @@ -545,6 +546,8 @@ struct vc4_dsi { struct completion xfer_completion; int xfer_result; + + struct debugfs_regset32 regset; }; #define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host) @@ -605,113 +608,56 @@ to_vc4_dsi_encoder(struct drm_encoder *encoder) return container_of(encoder, struct vc4_dsi_encoder, base.base); } -#define DSI_REG(reg) { reg, #reg } -static const struct { - u32 reg; - const char *name; -} dsi0_regs[] = { - DSI_REG(DSI0_CTRL), - DSI_REG(DSI0_STAT), - DSI_REG(DSI0_HSTX_TO_CNT), - DSI_REG(DSI0_LPRX_TO_CNT), - DSI_REG(DSI0_TA_TO_CNT), - DSI_REG(DSI0_PR_TO_CNT), - DSI_REG(DSI0_DISP0_CTRL), - DSI_REG(DSI0_DISP1_CTRL), - DSI_REG(DSI0_INT_STAT), - DSI_REG(DSI0_INT_EN), - DSI_REG(DSI0_PHYC), - DSI_REG(DSI0_HS_CLT0), - DSI_REG(DSI0_HS_CLT1), - DSI_REG(DSI0_HS_CLT2), - DSI_REG(DSI0_HS_DLT3), - DSI_REG(DSI0_HS_DLT4), - DSI_REG(DSI0_HS_DLT5), - DSI_REG(DSI0_HS_DLT6), - DSI_REG(DSI0_HS_DLT7), - DSI_REG(DSI0_PHY_AFEC0), - DSI_REG(DSI0_PHY_AFEC1), - DSI_REG(DSI0_ID), +static const struct debugfs_reg32 dsi0_regs[] = { + VC4_REG32(DSI0_CTRL), + VC4_REG32(DSI0_STAT), + VC4_REG32(DSI0_HSTX_TO_CNT), + VC4_REG32(DSI0_LPRX_TO_CNT), + VC4_REG32(DSI0_TA_TO_CNT), + VC4_REG32(DSI0_PR_TO_CNT), + VC4_REG32(DSI0_DISP0_CTRL), + VC4_REG32(DSI0_DISP1_CTRL), + VC4_REG32(DSI0_INT_STAT), + VC4_REG32(DSI0_INT_EN), + VC4_REG32(DSI0_PHYC), + VC4_REG32(DSI0_HS_CLT0), + VC4_REG32(DSI0_HS_CLT1), + VC4_REG32(DSI0_HS_CLT2), + VC4_REG32(DSI0_HS_DLT3), + VC4_REG32(DSI0_HS_DLT4), + VC4_REG32(DSI0_HS_DLT5), + VC4_REG32(DSI0_HS_DLT6), + VC4_REG32(DSI0_HS_DLT7), + VC4_REG32(DSI0_PHY_AFEC0), + VC4_REG32(DSI0_PHY_AFEC1), + VC4_REG32(DSI0_ID), }; -static const struct { - u32 reg; - const char *name; -} dsi1_regs[] = { - DSI_REG(DSI1_CTRL), - DSI_REG(DSI1_STAT), - DSI_REG(DSI1_HSTX_TO_CNT), - DSI_REG(DSI1_LPRX_TO_CNT), - DSI_REG(DSI1_TA_TO_CNT), - DSI_REG(DSI1_PR_TO_CNT), - DSI_REG(DSI1_DISP0_CTRL), - DSI_REG(DSI1_DISP1_CTRL), - DSI_REG(DSI1_INT_STAT), - DSI_REG(DSI1_INT_EN), - DSI_REG(DSI1_PHYC), - DSI_REG(DSI1_HS_CLT0), - DSI_REG(DSI1_HS_CLT1), - DSI_REG(DSI1_HS_CLT2), - DSI_REG(DSI1_HS_DLT3), - DSI_REG(DSI1_HS_DLT4), - DSI_REG(DSI1_HS_DLT5), - DSI_REG(DSI1_HS_DLT6), - DSI_REG(DSI1_HS_DLT7), - DSI_REG(DSI1_PHY_AFEC0), - DSI_REG(DSI1_PHY_AFEC1), - DSI_REG(DSI1_ID), +static const struct debugfs_reg32 dsi1_regs[] = { + VC4_REG32(DSI1_CTRL), + VC4_REG32(DSI1_STAT), + VC4_REG32(DSI1_HSTX_TO_CNT), + VC4_REG32(DSI1_LPRX_TO_CNT), + VC4_REG32(DSI1_TA_TO_CNT), + VC4_REG32(DSI1_PR_TO_CNT), + VC4_REG32(DSI1_DISP0_CTRL), + VC4_REG32(DSI1_DISP1_CTRL), + VC4_REG32(DSI1_INT_STAT), + VC4_REG32(DSI1_INT_EN), + VC4_REG32(DSI1_PHYC), + VC4_REG32(DSI1_HS_CLT0), + VC4_REG32(DSI1_HS_CLT1), + VC4_REG32(DSI1_HS_CLT2), + VC4_REG32(DSI1_HS_DLT3), + VC4_REG32(DSI1_HS_DLT4), + VC4_REG32(DSI1_HS_DLT5), + VC4_REG32(DSI1_HS_DLT6), + VC4_REG32(DSI1_HS_DLT7), + VC4_REG32(DSI1_PHY_AFEC0), + VC4_REG32(DSI1_PHY_AFEC1), + VC4_REG32(DSI1_ID), }; -static void vc4_dsi_dump_regs(struct vc4_dsi *dsi) -{ - int i; - - if (dsi->port == 0) { - for (i = 0; i < ARRAY_SIZE(dsi0_regs); i++) { - DRM_INFO("0x%04x (%s): 0x%08x\n", - dsi0_regs[i].reg, dsi0_regs[i].name, - DSI_READ(dsi0_regs[i].reg)); - } - } else { - for (i = 0; i < ARRAY_SIZE(dsi1_regs); i++) { - DRM_INFO("0x%04x (%s): 0x%08x\n", - dsi1_regs[i].reg, dsi1_regs[i].name, - DSI_READ(dsi1_regs[i].reg)); - } - } -} - -#ifdef CONFIG_DEBUG_FS -int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused) -{ - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *drm = node->minor->dev; - struct vc4_dev *vc4 = to_vc4_dev(drm); - int dsi_index = (uintptr_t)node->info_ent->data; - struct vc4_dsi *dsi = (dsi_index == 1 ? vc4->dsi1 : NULL); - int i; - - if (!dsi) - return 0; - - if (dsi->port == 0) { - for (i = 0; i < ARRAY_SIZE(dsi0_regs); i++) { - seq_printf(m, "0x%04x (%s): 0x%08x\n", - dsi0_regs[i].reg, dsi0_regs[i].name, - DSI_READ(dsi0_regs[i].reg)); - } - } else { - for (i = 0; i < ARRAY_SIZE(dsi1_regs); i++) { - seq_printf(m, "0x%04x (%s): 0x%08x\n", - dsi1_regs[i].reg, dsi1_regs[i].name, - DSI_READ(dsi1_regs[i].reg)); - } - } - - return 0; -} -#endif - static void vc4_dsi_encoder_destroy(struct drm_encoder *encoder) { drm_encoder_cleanup(encoder); @@ -900,8 +846,9 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) } if (debug_dump_regs) { - DRM_INFO("DSI regs before:\n"); - vc4_dsi_dump_regs(dsi); + struct drm_printer p = drm_info_printer(&dsi->pdev->dev); + dev_info(&dsi->pdev->dev, "DSI regs before:\n"); + drm_print_regset32(&p, &dsi->regset); } /* Round up the clk_set_rate() request slightly, since @@ -1135,8 +1082,9 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) drm_bridge_enable(dsi->bridge); if (debug_dump_regs) { - DRM_INFO("DSI regs after:\n"); - vc4_dsi_dump_regs(dsi); + struct drm_printer p = drm_info_printer(&dsi->pdev->dev); + dev_info(&dsi->pdev->dev, "DSI regs after:\n"); + drm_print_regset32(&p, &dsi->regset); } } @@ -1527,6 +1475,15 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) if (IS_ERR(dsi->regs)) return PTR_ERR(dsi->regs); + dsi->regset.base = dsi->regs; + if (dsi->port == 0) { + dsi->regset.regs = dsi0_regs; + dsi->regset.nregs = ARRAY_SIZE(dsi0_regs); + } else { + dsi->regset.regs = dsi1_regs; + dsi->regset.nregs = ARRAY_SIZE(dsi1_regs); + } + if (DSI_PORT_READ(ID) != DSI_ID_VALUE) { dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n", DSI_PORT_READ(ID), DSI_ID_VALUE); @@ -1662,6 +1619,11 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) */ dsi->encoder->bridge = NULL; + if (dsi->port == 0) + vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset); + else + vc4_debugfs_add_regset32(drm, "dsi1_regs", &dsi->regset); + pm_runtime_enable(dev); return 0; diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index aea2b8dfec17..84795d928f20 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -74,6 +74,11 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, u32 i; int ret = 0; + if (!vc4->v3d) { + DRM_DEBUG("VC4_GET_HANG_STATE with no VC4 V3D probed\n"); + return -ENODEV; + } + spin_lock_irqsave(&vc4->job_lock, irqflags); kernel_state = vc4->hang_state; if (!kernel_state) { @@ -536,7 +541,7 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno) bo = to_vc4_bo(&exec->bo[i]->base); bo->seqno = seqno; - reservation_object_add_shared_fence(bo->resv, exec->fence); + reservation_object_add_shared_fence(bo->base.base.resv, exec->fence); } list_for_each_entry(bo, &exec->unref_list, unref_head) { @@ -547,7 +552,7 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno) bo = to_vc4_bo(&exec->rcl_write_bo[i]->base); bo->write_seqno = seqno; - reservation_object_add_excl_fence(bo->resv, exec->fence); + reservation_object_add_excl_fence(bo->base.base.resv, exec->fence); } } @@ -559,7 +564,7 @@ vc4_unlock_bo_reservations(struct drm_device *dev, int i; for (i = 0; i < exec->bo_count; i++) { - struct vc4_bo *bo = to_vc4_bo(&exec->bo[i]->base); + struct drm_gem_object *bo = &exec->bo[i]->base; ww_mutex_unlock(&bo->resv->lock); } @@ -581,13 +586,13 @@ vc4_lock_bo_reservations(struct drm_device *dev, { int contended_lock = -1; int i, ret; - struct vc4_bo *bo; + struct drm_gem_object *bo; ww_acquire_init(acquire_ctx, &reservation_ww_class); retry: if (contended_lock != -1) { - bo = to_vc4_bo(&exec->bo[contended_lock]->base); + bo = &exec->bo[contended_lock]->base; ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock, acquire_ctx); if (ret) { @@ -600,19 +605,19 @@ retry: if (i == contended_lock) continue; - bo = to_vc4_bo(&exec->bo[i]->base); + bo = &exec->bo[i]->base; ret = ww_mutex_lock_interruptible(&bo->resv->lock, acquire_ctx); if (ret) { int j; for (j = 0; j < i; j++) { - bo = to_vc4_bo(&exec->bo[j]->base); + bo = &exec->bo[j]->base; ww_mutex_unlock(&bo->resv->lock); } if (contended_lock != -1 && contended_lock >= i) { - bo = to_vc4_bo(&exec->bo[contended_lock]->base); + bo = &exec->bo[contended_lock]->base; ww_mutex_unlock(&bo->resv->lock); } @@ -633,7 +638,7 @@ retry: * before we commit the CL to the hardware. */ for (i = 0; i < exec->bo_count; i++) { - bo = to_vc4_bo(&exec->bo[i]->base); + bo = &exec->bo[i]->base; ret = reservation_object_reserve_shared(bo->resv, 1); if (ret) { @@ -815,6 +820,7 @@ static int vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec) { struct drm_vc4_submit_cl *args = exec->args; + struct vc4_dev *vc4 = to_vc4_dev(dev); void *temp = NULL; void *bin; int ret = 0; @@ -913,6 +919,12 @@ vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec) if (ret) goto fail; + if (exec->found_tile_binning_mode_config_packet) { + ret = vc4_v3d_bin_bo_get(vc4, &exec->bin_bo_used); + if (ret) + goto fail; + } + /* Block waiting on any previous rendering into the CS's VBO, * IB, or textures, so that pixels are actually written by the * time we try to read them. @@ -961,15 +973,14 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec) vc4->bin_alloc_used &= ~exec->bin_slots; spin_unlock_irqrestore(&vc4->job_lock, irqflags); + /* Release the reference on the binner BO if needed. */ + if (exec->bin_bo_used) + vc4_v3d_bin_bo_put(vc4); + /* Release the reference we had on the perf monitor. */ vc4_perfmon_put(exec->perfmon); - mutex_lock(&vc4->power_lock); - if (--vc4->power_refcount == 0) { - pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev); - pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); - } - mutex_unlock(&vc4->power_lock); + vc4_v3d_pm_put(vc4); kfree(exec); } @@ -1124,6 +1135,11 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct dma_fence *in_fence; int ret = 0; + if (!vc4->v3d) { + DRM_DEBUG("VC4_SUBMIT_CL with no VC4 V3D probed\n"); + return -ENODEV; + } + if ((args->flags & ~(VC4_SUBMIT_CL_USE_CLEAR_COLOR | VC4_SUBMIT_CL_FIXED_RCL_ORDER | VC4_SUBMIT_CL_RCL_ORDER_INCREASING_X | @@ -1143,17 +1159,11 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, return -ENOMEM; } - mutex_lock(&vc4->power_lock); - if (vc4->power_refcount++ == 0) { - ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); - if (ret < 0) { - mutex_unlock(&vc4->power_lock); - vc4->power_refcount--; - kfree(exec); - return ret; - } + ret = vc4_v3d_pm_get(vc4); + if (ret) { + kfree(exec); + return ret; } - mutex_unlock(&vc4->power_lock); exec->args = args; INIT_LIST_HEAD(&exec->unref_list); diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 88fd5df7e7dc..43442c5619a3 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -97,6 +97,9 @@ struct vc4_hdmi { struct clk *pixel_clock; struct clk *hsm_clock; + + struct debugfs_regset32 hdmi_regset; + struct debugfs_regset32 hd_regset; }; #define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset) @@ -134,103 +137,69 @@ to_vc4_hdmi_connector(struct drm_connector *connector) return container_of(connector, struct vc4_hdmi_connector, base); } -#define HDMI_REG(reg) { reg, #reg } -static const struct { - u32 reg; - const char *name; -} hdmi_regs[] = { - HDMI_REG(VC4_HDMI_CORE_REV), - HDMI_REG(VC4_HDMI_SW_RESET_CONTROL), - HDMI_REG(VC4_HDMI_HOTPLUG_INT), - HDMI_REG(VC4_HDMI_HOTPLUG), - HDMI_REG(VC4_HDMI_MAI_CHANNEL_MAP), - HDMI_REG(VC4_HDMI_MAI_CONFIG), - HDMI_REG(VC4_HDMI_MAI_FORMAT), - HDMI_REG(VC4_HDMI_AUDIO_PACKET_CONFIG), - HDMI_REG(VC4_HDMI_RAM_PACKET_CONFIG), - HDMI_REG(VC4_HDMI_HORZA), - HDMI_REG(VC4_HDMI_HORZB), - HDMI_REG(VC4_HDMI_FIFO_CTL), - HDMI_REG(VC4_HDMI_SCHEDULER_CONTROL), - HDMI_REG(VC4_HDMI_VERTA0), - HDMI_REG(VC4_HDMI_VERTA1), - HDMI_REG(VC4_HDMI_VERTB0), - HDMI_REG(VC4_HDMI_VERTB1), - HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL), - HDMI_REG(VC4_HDMI_TX_PHY_CTL0), - - HDMI_REG(VC4_HDMI_CEC_CNTRL_1), - HDMI_REG(VC4_HDMI_CEC_CNTRL_2), - HDMI_REG(VC4_HDMI_CEC_CNTRL_3), - HDMI_REG(VC4_HDMI_CEC_CNTRL_4), - HDMI_REG(VC4_HDMI_CEC_CNTRL_5), - HDMI_REG(VC4_HDMI_CPU_STATUS), - HDMI_REG(VC4_HDMI_CPU_MASK_STATUS), - - HDMI_REG(VC4_HDMI_CEC_RX_DATA_1), - HDMI_REG(VC4_HDMI_CEC_RX_DATA_2), - HDMI_REG(VC4_HDMI_CEC_RX_DATA_3), - HDMI_REG(VC4_HDMI_CEC_RX_DATA_4), - HDMI_REG(VC4_HDMI_CEC_TX_DATA_1), - HDMI_REG(VC4_HDMI_CEC_TX_DATA_2), - HDMI_REG(VC4_HDMI_CEC_TX_DATA_3), - HDMI_REG(VC4_HDMI_CEC_TX_DATA_4), +static const struct debugfs_reg32 hdmi_regs[] = { + VC4_REG32(VC4_HDMI_CORE_REV), + VC4_REG32(VC4_HDMI_SW_RESET_CONTROL), + VC4_REG32(VC4_HDMI_HOTPLUG_INT), + VC4_REG32(VC4_HDMI_HOTPLUG), + VC4_REG32(VC4_HDMI_MAI_CHANNEL_MAP), + VC4_REG32(VC4_HDMI_MAI_CONFIG), + VC4_REG32(VC4_HDMI_MAI_FORMAT), + VC4_REG32(VC4_HDMI_AUDIO_PACKET_CONFIG), + VC4_REG32(VC4_HDMI_RAM_PACKET_CONFIG), + VC4_REG32(VC4_HDMI_HORZA), + VC4_REG32(VC4_HDMI_HORZB), + VC4_REG32(VC4_HDMI_FIFO_CTL), + VC4_REG32(VC4_HDMI_SCHEDULER_CONTROL), + VC4_REG32(VC4_HDMI_VERTA0), + VC4_REG32(VC4_HDMI_VERTA1), + VC4_REG32(VC4_HDMI_VERTB0), + VC4_REG32(VC4_HDMI_VERTB1), + VC4_REG32(VC4_HDMI_TX_PHY_RESET_CTL), + VC4_REG32(VC4_HDMI_TX_PHY_CTL0), + + VC4_REG32(VC4_HDMI_CEC_CNTRL_1), + VC4_REG32(VC4_HDMI_CEC_CNTRL_2), + VC4_REG32(VC4_HDMI_CEC_CNTRL_3), + VC4_REG32(VC4_HDMI_CEC_CNTRL_4), + VC4_REG32(VC4_HDMI_CEC_CNTRL_5), + VC4_REG32(VC4_HDMI_CPU_STATUS), + VC4_REG32(VC4_HDMI_CPU_MASK_STATUS), + + VC4_REG32(VC4_HDMI_CEC_RX_DATA_1), + VC4_REG32(VC4_HDMI_CEC_RX_DATA_2), + VC4_REG32(VC4_HDMI_CEC_RX_DATA_3), + VC4_REG32(VC4_HDMI_CEC_RX_DATA_4), + VC4_REG32(VC4_HDMI_CEC_TX_DATA_1), + VC4_REG32(VC4_HDMI_CEC_TX_DATA_2), + VC4_REG32(VC4_HDMI_CEC_TX_DATA_3), + VC4_REG32(VC4_HDMI_CEC_TX_DATA_4), }; -static const struct { - u32 reg; - const char *name; -} hd_regs[] = { - HDMI_REG(VC4_HD_M_CTL), - HDMI_REG(VC4_HD_MAI_CTL), - HDMI_REG(VC4_HD_MAI_THR), - HDMI_REG(VC4_HD_MAI_FMT), - HDMI_REG(VC4_HD_MAI_SMP), - HDMI_REG(VC4_HD_VID_CTL), - HDMI_REG(VC4_HD_CSC_CTL), - HDMI_REG(VC4_HD_FRAME_COUNT), +static const struct debugfs_reg32 hd_regs[] = { + VC4_REG32(VC4_HD_M_CTL), + VC4_REG32(VC4_HD_MAI_CTL), + VC4_REG32(VC4_HD_MAI_THR), + VC4_REG32(VC4_HD_MAI_FMT), + VC4_REG32(VC4_HD_MAI_SMP), + VC4_REG32(VC4_HD_VID_CTL), + VC4_REG32(VC4_HD_CSC_CTL), + VC4_REG32(VC4_HD_FRAME_COUNT), }; -#ifdef CONFIG_DEBUG_FS -int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) +static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *)m->private; struct drm_device *dev = node->minor->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); - int i; - - for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) { - seq_printf(m, "%s (0x%04x): 0x%08x\n", - hdmi_regs[i].name, hdmi_regs[i].reg, - HDMI_READ(hdmi_regs[i].reg)); - } + struct vc4_hdmi *hdmi = vc4->hdmi; + struct drm_printer p = drm_seq_file_printer(m); - for (i = 0; i < ARRAY_SIZE(hd_regs); i++) { - seq_printf(m, "%s (0x%04x): 0x%08x\n", - hd_regs[i].name, hd_regs[i].reg, - HD_READ(hd_regs[i].reg)); - } + drm_print_regset32(&p, &hdmi->hdmi_regset); + drm_print_regset32(&p, &hdmi->hd_regset); return 0; } -#endif /* CONFIG_DEBUG_FS */ - -static void vc4_hdmi_dump_regs(struct drm_device *dev) -{ - struct vc4_dev *vc4 = to_vc4_dev(dev); - int i; - - for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) { - DRM_INFO("0x%04x (%s): 0x%08x\n", - hdmi_regs[i].reg, hdmi_regs[i].name, - HDMI_READ(hdmi_regs[i].reg)); - } - for (i = 0; i < ARRAY_SIZE(hd_regs); i++) { - DRM_INFO("0x%04x (%s): 0x%08x\n", - hd_regs[i].reg, hd_regs[i].name, - HD_READ(hd_regs[i].reg)); - } -} static enum drm_connector_status vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) @@ -286,11 +255,17 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) return ret; } +static void vc4_hdmi_connector_reset(struct drm_connector *connector) +{ + drm_atomic_helper_connector_reset(connector); + drm_atomic_helper_connector_tv_reset(connector); +} + static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { .detect = vc4_hdmi_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = vc4_hdmi_connector_destroy, - .reset = drm_atomic_helper_connector_reset, + .reset = vc4_hdmi_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; @@ -561,8 +536,11 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0); if (debug_dump_regs) { - DRM_INFO("HDMI regs before:\n"); - vc4_hdmi_dump_regs(dev); + struct drm_printer p = drm_info_printer(&hdmi->pdev->dev); + + dev_info(&hdmi->pdev->dev, "HDMI regs before:\n"); + drm_print_regset32(&p, &hdmi->hdmi_regset); + drm_print_regset32(&p, &hdmi->hd_regset); } HD_WRITE(VC4_HD_VID_CTL, 0); @@ -637,8 +615,11 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); if (debug_dump_regs) { - DRM_INFO("HDMI regs after:\n"); - vc4_hdmi_dump_regs(dev); + struct drm_printer p = drm_info_printer(&hdmi->pdev->dev); + + dev_info(&hdmi->pdev->dev, "HDMI regs after:\n"); + drm_print_regset32(&p, &hdmi->hdmi_regset); + drm_print_regset32(&p, &hdmi->hd_regset); } HD_WRITE(VC4_HD_VID_CTL, @@ -1333,6 +1314,13 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) if (IS_ERR(hdmi->hd_regs)) return PTR_ERR(hdmi->hd_regs); + hdmi->hdmi_regset.base = hdmi->hdmicore_regs; + hdmi->hdmi_regset.regs = hdmi_regs; + hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs); + hdmi->hd_regset.base = hdmi->hd_regs; + hdmi->hd_regset.regs = hd_regs; + hdmi->hd_regset.nregs = ARRAY_SIZE(hd_regs); + hdmi->pixel_clock = devm_clk_get(dev, "pixel"); if (IS_ERR(hdmi->pixel_clock)) { DRM_ERROR("Failed to get pixel clock\n"); @@ -1448,6 +1436,8 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) if (ret) goto err_destroy_encoder; + vc4_debugfs_add_file(drm, "hdmi_regs", vc4_hdmi_debugfs_regs, hdmi); + return 0; #ifdef CONFIG_DRM_VC4_HDMI_CEC diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 5d8c749c9749..f746e9a7a88c 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -22,58 +22,52 @@ * each CRTC. */ +#include <drm/drm_atomic_helper.h> #include <linux/component.h> #include "vc4_drv.h" #include "vc4_regs.h" -#define HVS_REG(reg) { reg, #reg } -static const struct { - u32 reg; - const char *name; -} hvs_regs[] = { - HVS_REG(SCALER_DISPCTRL), - HVS_REG(SCALER_DISPSTAT), - HVS_REG(SCALER_DISPID), - HVS_REG(SCALER_DISPECTRL), - HVS_REG(SCALER_DISPPROF), - HVS_REG(SCALER_DISPDITHER), - HVS_REG(SCALER_DISPEOLN), - HVS_REG(SCALER_DISPLIST0), - HVS_REG(SCALER_DISPLIST1), - HVS_REG(SCALER_DISPLIST2), - HVS_REG(SCALER_DISPLSTAT), - HVS_REG(SCALER_DISPLACT0), - HVS_REG(SCALER_DISPLACT1), - HVS_REG(SCALER_DISPLACT2), - HVS_REG(SCALER_DISPCTRL0), - HVS_REG(SCALER_DISPBKGND0), - HVS_REG(SCALER_DISPSTAT0), - HVS_REG(SCALER_DISPBASE0), - HVS_REG(SCALER_DISPCTRL1), - HVS_REG(SCALER_DISPBKGND1), - HVS_REG(SCALER_DISPSTAT1), - HVS_REG(SCALER_DISPBASE1), - HVS_REG(SCALER_DISPCTRL2), - HVS_REG(SCALER_DISPBKGND2), - HVS_REG(SCALER_DISPSTAT2), - HVS_REG(SCALER_DISPBASE2), - HVS_REG(SCALER_DISPALPHA2), - HVS_REG(SCALER_OLEDOFFS), - HVS_REG(SCALER_OLEDCOEF0), - HVS_REG(SCALER_OLEDCOEF1), - HVS_REG(SCALER_OLEDCOEF2), +static const struct debugfs_reg32 hvs_regs[] = { + VC4_REG32(SCALER_DISPCTRL), + VC4_REG32(SCALER_DISPSTAT), + VC4_REG32(SCALER_DISPID), + VC4_REG32(SCALER_DISPECTRL), + VC4_REG32(SCALER_DISPPROF), + VC4_REG32(SCALER_DISPDITHER), + VC4_REG32(SCALER_DISPEOLN), + VC4_REG32(SCALER_DISPLIST0), + VC4_REG32(SCALER_DISPLIST1), + VC4_REG32(SCALER_DISPLIST2), + VC4_REG32(SCALER_DISPLSTAT), + VC4_REG32(SCALER_DISPLACT0), + VC4_REG32(SCALER_DISPLACT1), + VC4_REG32(SCALER_DISPLACT2), + VC4_REG32(SCALER_DISPCTRL0), + VC4_REG32(SCALER_DISPBKGND0), + VC4_REG32(SCALER_DISPSTAT0), + VC4_REG32(SCALER_DISPBASE0), + VC4_REG32(SCALER_DISPCTRL1), + VC4_REG32(SCALER_DISPBKGND1), + VC4_REG32(SCALER_DISPSTAT1), + VC4_REG32(SCALER_DISPBASE1), + VC4_REG32(SCALER_DISPCTRL2), + VC4_REG32(SCALER_DISPBKGND2), + VC4_REG32(SCALER_DISPSTAT2), + VC4_REG32(SCALER_DISPBASE2), + VC4_REG32(SCALER_DISPALPHA2), + VC4_REG32(SCALER_OLEDOFFS), + VC4_REG32(SCALER_OLEDCOEF0), + VC4_REG32(SCALER_OLEDCOEF1), + VC4_REG32(SCALER_OLEDCOEF2), }; void vc4_hvs_dump_state(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_printer p = drm_info_printer(&vc4->hvs->pdev->dev); int i; - for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) { - DRM_INFO("0x%04x (%s): 0x%08x\n", - hvs_regs[i].reg, hvs_regs[i].name, - HVS_READ(hvs_regs[i].reg)); - } + drm_print_regset32(&p, &vc4->hvs->regset); DRM_INFO("HVS ctx:\n"); for (i = 0; i < 64; i += 4) { @@ -86,23 +80,17 @@ void vc4_hvs_dump_state(struct drm_device *dev) } } -#ifdef CONFIG_DEBUG_FS -int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused) +static int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data) { - struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_info_node *node = m->private; struct drm_device *dev = node->minor->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); - int i; + struct drm_printer p = drm_seq_file_printer(m); - for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) { - seq_printf(m, "%s (0x%04x): 0x%08x\n", - hvs_regs[i].name, hvs_regs[i].reg, - HVS_READ(hvs_regs[i].reg)); - } + drm_printf(&p, "%d\n", atomic_read(&vc4->underrun)); return 0; } -#endif /* The filter kernel is composed of dwords each containing 3 9-bit * signed integers packed next to each other. @@ -166,6 +154,67 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, return 0; } +void vc4_hvs_mask_underrun(struct drm_device *dev, int channel) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + u32 dispctrl = HVS_READ(SCALER_DISPCTRL); + + dispctrl &= ~SCALER_DISPCTRL_DSPEISLUR(channel); + + HVS_WRITE(SCALER_DISPCTRL, dispctrl); +} + +void vc4_hvs_unmask_underrun(struct drm_device *dev, int channel) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + u32 dispctrl = HVS_READ(SCALER_DISPCTRL); + + dispctrl |= SCALER_DISPCTRL_DSPEISLUR(channel); + + HVS_WRITE(SCALER_DISPSTAT, + SCALER_DISPSTAT_EUFLOW(channel)); + HVS_WRITE(SCALER_DISPCTRL, dispctrl); +} + +static void vc4_hvs_report_underrun(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + atomic_inc(&vc4->underrun); + DRM_DEV_ERROR(dev->dev, "HVS underrun\n"); +} + +static irqreturn_t vc4_hvs_irq_handler(int irq, void *data) +{ + struct drm_device *dev = data; + struct vc4_dev *vc4 = to_vc4_dev(dev); + irqreturn_t irqret = IRQ_NONE; + int channel; + u32 control; + u32 status; + + status = HVS_READ(SCALER_DISPSTAT); + control = HVS_READ(SCALER_DISPCTRL); + + for (channel = 0; channel < SCALER_CHANNELS_COUNT; channel++) { + /* Interrupt masking is not always honored, so check it here. */ + if (status & SCALER_DISPSTAT_EUFLOW(channel) && + control & SCALER_DISPCTRL_DSPEISLUR(channel)) { + vc4_hvs_mask_underrun(dev, channel); + vc4_hvs_report_underrun(dev); + + irqret = IRQ_HANDLED; + } + } + + /* Clear every per-channel interrupt flag. */ + HVS_WRITE(SCALER_DISPSTAT, SCALER_DISPSTAT_IRQMASK(0) | + SCALER_DISPSTAT_IRQMASK(1) | + SCALER_DISPSTAT_IRQMASK(2)); + + return irqret; +} + static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); @@ -185,6 +234,10 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) if (IS_ERR(hvs->regs)) return PTR_ERR(hvs->regs); + hvs->regset.base = hvs->regs; + hvs->regset.regs = hvs_regs; + hvs->regset.nregs = ARRAY_SIZE(hvs_regs); + hvs->dlist = hvs->regs + SCALER_DLIST_START; spin_lock_init(&hvs->mm_lock); @@ -219,15 +272,40 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) dispctrl = HVS_READ(SCALER_DISPCTRL); dispctrl |= SCALER_DISPCTRL_ENABLE; + dispctrl |= SCALER_DISPCTRL_DISPEIRQ(0) | + SCALER_DISPCTRL_DISPEIRQ(1) | + SCALER_DISPCTRL_DISPEIRQ(2); /* Set DSP3 (PV1) to use HVS channel 2, which would otherwise * be unused. */ dispctrl &= ~SCALER_DISPCTRL_DSP3_MUX_MASK; + dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ | + SCALER_DISPCTRL_SLVWREIRQ | + SCALER_DISPCTRL_SLVRDEIRQ | + SCALER_DISPCTRL_DSPEIEOF(0) | + SCALER_DISPCTRL_DSPEIEOF(1) | + SCALER_DISPCTRL_DSPEIEOF(2) | + SCALER_DISPCTRL_DSPEIEOLN(0) | + SCALER_DISPCTRL_DSPEIEOLN(1) | + SCALER_DISPCTRL_DSPEIEOLN(2) | + SCALER_DISPCTRL_DSPEISLUR(0) | + SCALER_DISPCTRL_DSPEISLUR(1) | + SCALER_DISPCTRL_DSPEISLUR(2) | + SCALER_DISPCTRL_SCLEIRQ); dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX); HVS_WRITE(SCALER_DISPCTRL, dispctrl); + ret = devm_request_irq(dev, platform_get_irq(pdev, 0), + vc4_hvs_irq_handler, 0, "vc4 hvs", drm); + if (ret) + return ret; + + vc4_debugfs_add_regset32(drm, "hvs_regs", &hvs->regset); + vc4_debugfs_add_file(drm, "hvs_underrun", vc4_hvs_debugfs_underrun, + NULL); + return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index 4cd2ccfe15f4..e226c24e543f 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -59,15 +59,22 @@ vc4_overflow_mem_work(struct work_struct *work) { struct vc4_dev *vc4 = container_of(work, struct vc4_dev, overflow_mem_work); - struct vc4_bo *bo = vc4->bin_bo; + struct vc4_bo *bo; int bin_bo_slot; struct vc4_exec_info *exec; unsigned long irqflags; + mutex_lock(&vc4->bin_bo_lock); + + if (!vc4->bin_bo) + goto complete; + + bo = vc4->bin_bo; + bin_bo_slot = vc4_v3d_get_bin_slot(vc4); if (bin_bo_slot < 0) { DRM_ERROR("Couldn't allocate binner overflow mem\n"); - return; + goto complete; } spin_lock_irqsave(&vc4->job_lock, irqflags); @@ -98,6 +105,9 @@ vc4_overflow_mem_work(struct work_struct *work) V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM); spin_unlock_irqrestore(&vc4->job_lock, irqflags); + +complete: + mutex_unlock(&vc4->bin_bo_lock); } static void @@ -229,6 +239,9 @@ vc4_irq_preinstall(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); + if (!vc4->v3d) + return; + init_waitqueue_head(&vc4->job_wait_queue); INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work); @@ -243,8 +256,13 @@ vc4_irq_postinstall(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); - /* Enable both the render done and out of memory interrupts. */ - V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); + if (!vc4->v3d) + return 0; + + /* Enable the render done interrupts. The out-of-memory interrupt is + * enabled as soon as we have a binner BO allocated. + */ + V3D_WRITE(V3D_INTENA, V3D_INT_FLDONE | V3D_INT_FRDONE); return 0; } @@ -254,6 +272,9 @@ vc4_irq_uninstall(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); + if (!vc4->v3d) + return; + /* Disable sending interrupts for our driver's IRQs. */ V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS); diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 91b8c72ff361..295dacc8bcb9 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -34,6 +34,18 @@ static struct vc4_ctm_state *to_vc4_ctm_state(struct drm_private_state *priv) return container_of(priv, struct vc4_ctm_state, base); } +struct vc4_load_tracker_state { + struct drm_private_state base; + u64 hvs_load; + u64 membus_load; +}; + +static struct vc4_load_tracker_state * +to_vc4_load_tracker_state(struct drm_private_state *priv) +{ + return container_of(priv, struct vc4_load_tracker_state, base); +} + static struct vc4_ctm_state *vc4_get_ctm_state(struct drm_atomic_state *state, struct drm_private_obj *manager) { @@ -138,6 +150,16 @@ vc4_atomic_complete_commit(struct drm_atomic_state *state) { struct drm_device *dev = state->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc; + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + if (!state->crtcs[i].ptr || !state->crtcs[i].commit) + continue; + + vc4_crtc = to_vc4_crtc(state->crtcs[i].ptr); + vc4_hvs_mask_underrun(dev, vc4_crtc->channel); + } drm_atomic_helper_wait_for_fences(dev, state, false); @@ -385,6 +407,85 @@ vc4_ctm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) return 0; } +static int vc4_load_tracker_atomic_check(struct drm_atomic_state *state) +{ + struct drm_plane_state *old_plane_state, *new_plane_state; + struct vc4_dev *vc4 = to_vc4_dev(state->dev); + struct vc4_load_tracker_state *load_state; + struct drm_private_state *priv_state; + struct drm_plane *plane; + int i; + + priv_state = drm_atomic_get_private_obj_state(state, + &vc4->load_tracker); + if (IS_ERR(priv_state)) + return PTR_ERR(priv_state); + + load_state = to_vc4_load_tracker_state(priv_state); + for_each_oldnew_plane_in_state(state, plane, old_plane_state, + new_plane_state, i) { + struct vc4_plane_state *vc4_plane_state; + + if (old_plane_state->fb && old_plane_state->crtc) { + vc4_plane_state = to_vc4_plane_state(old_plane_state); + load_state->membus_load -= vc4_plane_state->membus_load; + load_state->hvs_load -= vc4_plane_state->hvs_load; + } + + if (new_plane_state->fb && new_plane_state->crtc) { + vc4_plane_state = to_vc4_plane_state(new_plane_state); + load_state->membus_load += vc4_plane_state->membus_load; + load_state->hvs_load += vc4_plane_state->hvs_load; + } + } + + /* Don't check the load when the tracker is disabled. */ + if (!vc4->load_tracker_enabled) + return 0; + + /* The absolute limit is 2Gbyte/sec, but let's take a margin to let + * the system work when other blocks are accessing the memory. + */ + if (load_state->membus_load > SZ_1G + SZ_512M) + return -ENOSPC; + + /* HVS clock is supposed to run @ 250Mhz, let's take a margin and + * consider the maximum number of cycles is 240M. + */ + if (load_state->hvs_load > 240000000ULL) + return -ENOSPC; + + return 0; +} + +static struct drm_private_state * +vc4_load_tracker_duplicate_state(struct drm_private_obj *obj) +{ + struct vc4_load_tracker_state *state; + + state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); + + return &state->base; +} + +static void vc4_load_tracker_destroy_state(struct drm_private_obj *obj, + struct drm_private_state *state) +{ + struct vc4_load_tracker_state *load_state; + + load_state = to_vc4_load_tracker_state(state); + kfree(load_state); +} + +static const struct drm_private_state_funcs vc4_load_tracker_state_funcs = { + .atomic_duplicate_state = vc4_load_tracker_duplicate_state, + .atomic_destroy_state = vc4_load_tracker_destroy_state, +}; + static int vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { @@ -394,7 +495,11 @@ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) if (ret < 0) return ret; - return drm_atomic_helper_check(dev, state); + ret = drm_atomic_helper_check(dev, state); + if (ret) + return ret; + + return vc4_load_tracker_atomic_check(state); } static const struct drm_mode_config_funcs vc4_mode_funcs = { @@ -407,13 +512,20 @@ int vc4_kms_load(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_ctm_state *ctm_state; + struct vc4_load_tracker_state *load_state; int ret; + /* Start with the load tracker enabled. Can be disabled through the + * debugfs load_tracker file. + */ + vc4->load_tracker_enabled = true; + sema_init(&vc4->async_modeset, 1); /* Set support for vblank irq fast disable, before drm_vblank_init() */ dev->vblank_disable_immediate = true; + dev->irq_enabled = true; ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret < 0) { dev_err(dev->dev, "failed to initialize vblank\n"); @@ -436,6 +548,15 @@ int vc4_kms_load(struct drm_device *dev) drm_atomic_private_obj_init(dev, &vc4->ctm_manager, &ctm_state->base, &vc4_ctm_state_funcs); + load_state = kzalloc(sizeof(*load_state), GFP_KERNEL); + if (!load_state) { + drm_atomic_private_obj_fini(&vc4->ctm_manager); + return -ENOMEM; + } + + drm_atomic_private_obj_init(dev, &vc4->load_tracker, &load_state->base, + &vc4_load_tracker_state_funcs); + drm_mode_config_reset(dev); drm_kms_helper_poll_init(dev); diff --git a/drivers/gpu/drm/vc4/vc4_perfmon.c b/drivers/gpu/drm/vc4/vc4_perfmon.c index 495150415020..f4aa75efd16b 100644 --- a/drivers/gpu/drm/vc4/vc4_perfmon.c +++ b/drivers/gpu/drm/vc4/vc4_perfmon.c @@ -100,12 +100,18 @@ void vc4_perfmon_close_file(struct vc4_file *vc4file) int vc4_perfmon_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_file *vc4file = file_priv->driver_priv; struct drm_vc4_perfmon_create *req = data; struct vc4_perfmon *perfmon; unsigned int i; int ret; + if (!vc4->v3d) { + DRM_DEBUG("Creating perfmon no VC4 V3D probed\n"); + return -ENODEV; + } + /* Number of monitored counters cannot exceed HW limits. */ if (req->ncounters > DRM_VC4_MAX_PERF_COUNTERS || !req->ncounters) @@ -146,10 +152,16 @@ int vc4_perfmon_create_ioctl(struct drm_device *dev, void *data, int vc4_perfmon_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_file *vc4file = file_priv->driver_priv; struct drm_vc4_perfmon_destroy *req = data; struct vc4_perfmon *perfmon; + if (!vc4->v3d) { + DRM_DEBUG("Destroying perfmon no VC4 V3D probed\n"); + return -ENODEV; + } + mutex_lock(&vc4file->perfmon.lock); perfmon = idr_remove(&vc4file->perfmon.idr, req->id); mutex_unlock(&vc4file->perfmon.lock); @@ -164,11 +176,17 @@ int vc4_perfmon_destroy_ioctl(struct drm_device *dev, void *data, int vc4_perfmon_get_values_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_file *vc4file = file_priv->driver_priv; struct drm_vc4_perfmon_get_values *req = data; struct vc4_perfmon *perfmon; int ret; + if (!vc4->v3d) { + DRM_DEBUG("Getting perfmon no VC4 V3D probed\n"); + return -ENODEV; + } + mutex_lock(&vc4file->perfmon.lock); perfmon = idr_find(&vc4file->perfmon.idr, req->id); vc4_perfmon_get(perfmon); diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index d098337c10e9..441e06d45c89 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -310,10 +310,10 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) struct drm_framebuffer *fb = state->fb; struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); u32 subpixel_src_mask = (1 << 16) - 1; - u32 format = fb->format->format; int num_planes = fb->format->num_planes; struct drm_crtc_state *crtc_state; - u32 h_subsample, v_subsample; + u32 h_subsample = fb->format->hsub; + u32 v_subsample = fb->format->vsub; int i, ret; crtc_state = drm_atomic_get_existing_crtc_state(state->state, @@ -328,9 +328,6 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) if (ret) return ret; - h_subsample = drm_format_horz_chroma_subsampling(format); - v_subsample = drm_format_vert_chroma_subsampling(format); - for (i = 0; i < num_planes; i++) vc4_state->offsets[i] = bo->paddr + fb->offsets[i]; @@ -488,6 +485,61 @@ static void vc4_write_scaling_parameters(struct drm_plane_state *state, } } +static void vc4_plane_calc_load(struct drm_plane_state *state) +{ + unsigned int hvs_load_shift, vrefresh, i; + struct drm_framebuffer *fb = state->fb; + struct vc4_plane_state *vc4_state; + struct drm_crtc_state *crtc_state; + unsigned int vscale_factor; + + vc4_state = to_vc4_plane_state(state); + crtc_state = drm_atomic_get_existing_crtc_state(state->state, + state->crtc); + vrefresh = drm_mode_vrefresh(&crtc_state->adjusted_mode); + + /* The HVS is able to process 2 pixels/cycle when scaling the source, + * 4 pixels/cycle otherwise. + * Alpha blending step seems to be pipelined and it's always operating + * at 4 pixels/cycle, so the limiting aspect here seems to be the + * scaler block. + * HVS load is expressed in clk-cycles/sec (AKA Hz). + */ + if (vc4_state->x_scaling[0] != VC4_SCALING_NONE || + vc4_state->x_scaling[1] != VC4_SCALING_NONE || + vc4_state->y_scaling[0] != VC4_SCALING_NONE || + vc4_state->y_scaling[1] != VC4_SCALING_NONE) + hvs_load_shift = 1; + else + hvs_load_shift = 2; + + vc4_state->membus_load = 0; + vc4_state->hvs_load = 0; + for (i = 0; i < fb->format->num_planes; i++) { + /* Even if the bandwidth/plane required for a single frame is + * + * vc4_state->src_w[i] * vc4_state->src_h[i] * cpp * vrefresh + * + * when downscaling, we have to read more pixels per line in + * the time frame reserved for a single line, so the bandwidth + * demand can be punctually higher. To account for that, we + * calculate the down-scaling factor and multiply the plane + * load by this number. We're likely over-estimating the read + * demand, but that's better than under-estimating it. + */ + vscale_factor = DIV_ROUND_UP(vc4_state->src_h[i], + vc4_state->crtc_h); + vc4_state->membus_load += vc4_state->src_w[i] * + vc4_state->src_h[i] * vscale_factor * + fb->format->cpp[i]; + vc4_state->hvs_load += vc4_state->crtc_h * vc4_state->crtc_w; + } + + vc4_state->hvs_load *= vrefresh; + vc4_state->hvs_load >>= hvs_load_shift; + vc4_state->membus_load *= vrefresh; +} + static int vc4_plane_allocate_lbm(struct drm_plane_state *state) { struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev); @@ -537,8 +589,9 @@ static int vc4_plane_mode_set(struct drm_plane *plane, u32 ctl0_offset = vc4_state->dlist_count; const struct hvs_format *format = vc4_get_hvs_format(fb->format->format); u64 base_format_mod = fourcc_mod_broadcom_mod(fb->modifier); - int num_planes = drm_format_num_planes(format->drm); - u32 h_subsample, v_subsample; + int num_planes = fb->format->num_planes; + u32 h_subsample = fb->format->hsub; + u32 v_subsample = fb->format->vsub; bool mix_plane_alpha; bool covers_screen; u32 scl0, scl1, pitch0; @@ -568,9 +621,6 @@ static int vc4_plane_mode_set(struct drm_plane *plane, scl1 = vc4_get_scl_field(state, 0); } - h_subsample = drm_format_horz_chroma_subsampling(format->drm); - v_subsample = drm_format_vert_chroma_subsampling(format->drm); - rotation = drm_rotation_simplify(state->rotation, DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X | @@ -875,6 +925,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, */ vc4_state->dlist_initialized = 1; + vc4_plane_calc_load(state); + return 0; } @@ -968,7 +1020,7 @@ static void vc4_plane_atomic_async_update(struct drm_plane *plane, { struct vc4_plane_state *vc4_state, *new_vc4_state; - drm_atomic_set_fb_for_plane(plane->state, state->fb); + swap(plane->state->fb, state->fb); plane->state->crtc_x = state->crtc_x; plane->state->crtc_y = state->crtc_y; plane->state->crtc_w = state->crtc_w; @@ -1082,7 +1134,7 @@ static int vc4_prepare_fb(struct drm_plane *plane, bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); - fence = reservation_object_get_excl_rcu(bo->resv); + fence = reservation_object_get_excl_rcu(bo->base.base.resv); drm_atomic_set_fence_for_plane(state, fence); if (plane->state->fb == state->fb) diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 931088014272..c0c5fadaf7e3 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -212,11 +212,11 @@ #define PV_HACT_ACT 0x30 +#define SCALER_CHANNELS_COUNT 3 + #define SCALER_DISPCTRL 0x00000000 /* Global register for clock gating the HVS */ # define SCALER_DISPCTRL_ENABLE BIT(31) -# define SCALER_DISPCTRL_DSP2EISLUR BIT(15) -# define SCALER_DISPCTRL_DSP1EISLUR BIT(14) # define SCALER_DISPCTRL_DSP3_MUX_MASK VC4_MASK(19, 18) # define SCALER_DISPCTRL_DSP3_MUX_SHIFT 18 @@ -224,45 +224,25 @@ * SCALER_DISPSTAT_IRQDISP0. Note that short frame contributions are * always enabled. */ -# define SCALER_DISPCTRL_DSP0EISLUR BIT(13) -# define SCALER_DISPCTRL_DSP2EIEOLN BIT(12) -# define SCALER_DISPCTRL_DSP2EIEOF BIT(11) -# define SCALER_DISPCTRL_DSP1EIEOLN BIT(10) -# define SCALER_DISPCTRL_DSP1EIEOF BIT(9) +# define SCALER_DISPCTRL_DSPEISLUR(x) BIT(13 + (x)) /* Enables Display 0 end-of-line-N contribution to * SCALER_DISPSTAT_IRQDISP0 */ -# define SCALER_DISPCTRL_DSP0EIEOLN BIT(8) +# define SCALER_DISPCTRL_DSPEIEOLN(x) BIT(8 + ((x) * 2)) /* Enables Display 0 EOF contribution to SCALER_DISPSTAT_IRQDISP0 */ -# define SCALER_DISPCTRL_DSP0EIEOF BIT(7) +# define SCALER_DISPCTRL_DSPEIEOF(x) BIT(7 + ((x) * 2)) # define SCALER_DISPCTRL_SLVRDEIRQ BIT(6) # define SCALER_DISPCTRL_SLVWREIRQ BIT(5) # define SCALER_DISPCTRL_DMAEIRQ BIT(4) -# define SCALER_DISPCTRL_DISP2EIRQ BIT(3) -# define SCALER_DISPCTRL_DISP1EIRQ BIT(2) /* Enables interrupt generation on the enabled EOF/EOLN/EISLUR * bits and short frames.. */ -# define SCALER_DISPCTRL_DISP0EIRQ BIT(1) +# define SCALER_DISPCTRL_DISPEIRQ(x) BIT(1 + (x)) /* Enables interrupt generation on scaler profiler interrupt. */ # define SCALER_DISPCTRL_SCLEIRQ BIT(0) #define SCALER_DISPSTAT 0x00000004 -# define SCALER_DISPSTAT_COBLOW2 BIT(29) -# define SCALER_DISPSTAT_EOLN2 BIT(28) -# define SCALER_DISPSTAT_ESFRAME2 BIT(27) -# define SCALER_DISPSTAT_ESLINE2 BIT(26) -# define SCALER_DISPSTAT_EUFLOW2 BIT(25) -# define SCALER_DISPSTAT_EOF2 BIT(24) - -# define SCALER_DISPSTAT_COBLOW1 BIT(21) -# define SCALER_DISPSTAT_EOLN1 BIT(20) -# define SCALER_DISPSTAT_ESFRAME1 BIT(19) -# define SCALER_DISPSTAT_ESLINE1 BIT(18) -# define SCALER_DISPSTAT_EUFLOW1 BIT(17) -# define SCALER_DISPSTAT_EOF1 BIT(16) - # define SCALER_DISPSTAT_RESP_MASK VC4_MASK(15, 14) # define SCALER_DISPSTAT_RESP_SHIFT 14 # define SCALER_DISPSTAT_RESP_OKAY 0 @@ -270,23 +250,26 @@ # define SCALER_DISPSTAT_RESP_SLVERR 2 # define SCALER_DISPSTAT_RESP_DECERR 3 -# define SCALER_DISPSTAT_COBLOW0 BIT(13) +# define SCALER_DISPSTAT_COBLOW(x) BIT(13 + ((x) * 8)) /* Set when the DISPEOLN line is done compositing. */ -# define SCALER_DISPSTAT_EOLN0 BIT(12) +# define SCALER_DISPSTAT_EOLN(x) BIT(12 + ((x) * 8)) /* Set when VSTART is seen but there are still pixels in the current * output line. */ -# define SCALER_DISPSTAT_ESFRAME0 BIT(11) +# define SCALER_DISPSTAT_ESFRAME(x) BIT(11 + ((x) * 8)) /* Set when HSTART is seen but there are still pixels in the current * output line. */ -# define SCALER_DISPSTAT_ESLINE0 BIT(10) +# define SCALER_DISPSTAT_ESLINE(x) BIT(10 + ((x) * 8)) /* Set when the the downstream tries to read from the display FIFO * while it's empty. */ -# define SCALER_DISPSTAT_EUFLOW0 BIT(9) +# define SCALER_DISPSTAT_EUFLOW(x) BIT(9 + ((x) * 8)) /* Set when the display mode changes from RUN to EOF */ -# define SCALER_DISPSTAT_EOF0 BIT(8) +# define SCALER_DISPSTAT_EOF(x) BIT(8 + ((x) * 8)) + +# define SCALER_DISPSTAT_IRQMASK(x) VC4_MASK(13 + ((x) * 8), \ + 8 + ((x) * 8)) /* Set on AXI invalid DMA ID error. */ # define SCALER_DISPSTAT_DMA_ERROR BIT(7) @@ -298,12 +281,10 @@ * SCALER_DISPSTAT_RESP_ERROR is not SCALER_DISPSTAT_RESP_OKAY. */ # define SCALER_DISPSTAT_IRQDMA BIT(4) -# define SCALER_DISPSTAT_IRQDISP2 BIT(3) -# define SCALER_DISPSTAT_IRQDISP1 BIT(2) /* Set when any of the EOF/EOLN/ESFRAME/ESLINE bits are set and their * corresponding interrupt bit is enabled in DISPCTRL. */ -# define SCALER_DISPSTAT_IRQDISP0 BIT(1) +# define SCALER_DISPSTAT_IRQDISP(x) BIT(1 + (x)) /* On read, the profiler interrupt. On write, clear *all* interrupt bits. */ # define SCALER_DISPSTAT_IRQSCL BIT(0) diff --git a/drivers/gpu/drm/vc4/vc4_render_cl.c b/drivers/gpu/drm/vc4/vc4_render_cl.c index 273984f71ae2..3c918eeaf56e 100644 --- a/drivers/gpu/drm/vc4/vc4_render_cl.c +++ b/drivers/gpu/drm/vc4/vc4_render_cl.c @@ -148,6 +148,12 @@ static void emit_tile(struct vc4_exec_info *exec, } if (setup->zs_read) { + if (setup->color_read) { + /* Exec previous load. */ + vc4_tile_coordinates(setup, x, y); + vc4_store_before_load(setup); + } + if (args->zs_read.flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { rcl_u8(setup, VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER); @@ -156,12 +162,6 @@ static void emit_tile(struct vc4_exec_info *exec, &args->zs_read, x, y) | VC4_LOADSTORE_FULL_RES_DISABLE_COLOR); } else { - if (setup->color_read) { - /* Exec previous load. */ - vc4_tile_coordinates(setup, x, y); - vc4_store_before_load(setup); - } - rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); rcl_u16(setup, args->zs_read.bits); rcl_u32(setup, setup->zs_read->paddr + @@ -291,16 +291,15 @@ static int vc4_create_rcl_bo(struct drm_device *dev, struct vc4_exec_info *exec, } } if (setup->zs_read) { + if (setup->color_read) { + loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; + loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; + } + if (args->zs_read.flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE; } else { - if (setup->color_read && - !(args->color_read.flags & - VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES)) { - loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; - loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; - } loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; } } diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index aa279b5b0de7..96f91c1b4b6e 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -148,6 +148,7 @@ struct vc4_txp { struct drm_writeback_connector connector; void __iomem *regs; + struct debugfs_regset32 regset; }; static inline struct vc4_txp *encoder_to_vc4_txp(struct drm_encoder *encoder) @@ -160,40 +161,14 @@ static inline struct vc4_txp *connector_to_vc4_txp(struct drm_connector *conn) return container_of(conn, struct vc4_txp, connector.base); } -#define TXP_REG(reg) { reg, #reg } -static const struct { - u32 reg; - const char *name; -} txp_regs[] = { - TXP_REG(TXP_DST_PTR), - TXP_REG(TXP_DST_PITCH), - TXP_REG(TXP_DIM), - TXP_REG(TXP_DST_CTRL), - TXP_REG(TXP_PROGRESS), +static const struct debugfs_reg32 txp_regs[] = { + VC4_REG32(TXP_DST_PTR), + VC4_REG32(TXP_DST_PITCH), + VC4_REG32(TXP_DIM), + VC4_REG32(TXP_DST_CTRL), + VC4_REG32(TXP_PROGRESS), }; -#ifdef CONFIG_DEBUG_FS -int vc4_txp_debugfs_regs(struct seq_file *m, void *unused) -{ - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *dev = node->minor->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_txp *txp = vc4->txp; - int i; - - if (!txp) - return 0; - - for (i = 0; i < ARRAY_SIZE(txp_regs); i++) { - seq_printf(m, "%s (0x%04x): 0x%08x\n", - txp_regs[i].name, txp_regs[i].reg, - TXP_READ(txp_regs[i].reg)); - } - - return 0; -} -#endif - static int vc4_txp_connector_get_modes(struct drm_connector *connector) { struct drm_device *dev = connector->dev; @@ -246,18 +221,18 @@ static const u32 txp_fmts[] = { }; static int vc4_txp_connector_atomic_check(struct drm_connector *conn, - struct drm_connector_state *conn_state) + struct drm_atomic_state *state) { + struct drm_connector_state *conn_state; struct drm_crtc_state *crtc_state; - struct drm_gem_cma_object *gem; struct drm_framebuffer *fb; int i; + conn_state = drm_atomic_get_new_connector_state(state, conn); if (!conn_state->writeback_job || !conn_state->writeback_job->fb) return 0; - crtc_state = drm_atomic_get_new_crtc_state(conn_state->state, - conn_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); fb = conn_state->writeback_job->fb; if (fb->width != crtc_state->mode.hdisplay || @@ -275,8 +250,6 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn, if (i == ARRAY_SIZE(drm_fmts)) return -EINVAL; - gem = drm_fb_cma_get_gem_obj(fb, 0); - /* Pitch must be aligned on 16 bytes. */ if (fb->pitches[0] & GENMASK(3, 0)) return -EINVAL; @@ -327,7 +300,7 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, TXP_WRITE(TXP_DST_CTRL, ctrl); - drm_writeback_queue_job(&txp->connector, conn_state->writeback_job); + drm_writeback_queue_job(&txp->connector, conn_state); } static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = { @@ -413,6 +386,9 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) txp->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(txp->regs)) return PTR_ERR(txp->regs); + txp->regset.base = txp->regs; + txp->regset.regs = txp_regs; + txp->regset.nregs = ARRAY_SIZE(txp_regs); drm_connector_helper_add(&txp->connector.base, &vc4_txp_connector_helper_funcs); @@ -431,6 +407,8 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(dev, txp); vc4->txp = txp; + vc4_debugfs_add_regset32(drm, "txp_regs", &txp->regset); + return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index e47e29426078..0533646a4d13 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -22,129 +22,145 @@ #include "vc4_drv.h" #include "vc4_regs.h" -#ifdef CONFIG_DEBUG_FS -#define REGDEF(reg) { reg, #reg } -static const struct { - uint32_t reg; - const char *name; -} vc4_reg_defs[] = { - REGDEF(V3D_IDENT0), - REGDEF(V3D_IDENT1), - REGDEF(V3D_IDENT2), - REGDEF(V3D_SCRATCH), - REGDEF(V3D_L2CACTL), - REGDEF(V3D_SLCACTL), - REGDEF(V3D_INTCTL), - REGDEF(V3D_INTENA), - REGDEF(V3D_INTDIS), - REGDEF(V3D_CT0CS), - REGDEF(V3D_CT1CS), - REGDEF(V3D_CT0EA), - REGDEF(V3D_CT1EA), - REGDEF(V3D_CT0CA), - REGDEF(V3D_CT1CA), - REGDEF(V3D_CT00RA0), - REGDEF(V3D_CT01RA0), - REGDEF(V3D_CT0LC), - REGDEF(V3D_CT1LC), - REGDEF(V3D_CT0PC), - REGDEF(V3D_CT1PC), - REGDEF(V3D_PCS), - REGDEF(V3D_BFC), - REGDEF(V3D_RFC), - REGDEF(V3D_BPCA), - REGDEF(V3D_BPCS), - REGDEF(V3D_BPOA), - REGDEF(V3D_BPOS), - REGDEF(V3D_BXCF), - REGDEF(V3D_SQRSV0), - REGDEF(V3D_SQRSV1), - REGDEF(V3D_SQCNTL), - REGDEF(V3D_SRQPC), - REGDEF(V3D_SRQUA), - REGDEF(V3D_SRQUL), - REGDEF(V3D_SRQCS), - REGDEF(V3D_VPACNTL), - REGDEF(V3D_VPMBASE), - REGDEF(V3D_PCTRC), - REGDEF(V3D_PCTRE), - REGDEF(V3D_PCTR(0)), - REGDEF(V3D_PCTRS(0)), - REGDEF(V3D_PCTR(1)), - REGDEF(V3D_PCTRS(1)), - REGDEF(V3D_PCTR(2)), - REGDEF(V3D_PCTRS(2)), - REGDEF(V3D_PCTR(3)), - REGDEF(V3D_PCTRS(3)), - REGDEF(V3D_PCTR(4)), - REGDEF(V3D_PCTRS(4)), - REGDEF(V3D_PCTR(5)), - REGDEF(V3D_PCTRS(5)), - REGDEF(V3D_PCTR(6)), - REGDEF(V3D_PCTRS(6)), - REGDEF(V3D_PCTR(7)), - REGDEF(V3D_PCTRS(7)), - REGDEF(V3D_PCTR(8)), - REGDEF(V3D_PCTRS(8)), - REGDEF(V3D_PCTR(9)), - REGDEF(V3D_PCTRS(9)), - REGDEF(V3D_PCTR(10)), - REGDEF(V3D_PCTRS(10)), - REGDEF(V3D_PCTR(11)), - REGDEF(V3D_PCTRS(11)), - REGDEF(V3D_PCTR(12)), - REGDEF(V3D_PCTRS(12)), - REGDEF(V3D_PCTR(13)), - REGDEF(V3D_PCTRS(13)), - REGDEF(V3D_PCTR(14)), - REGDEF(V3D_PCTRS(14)), - REGDEF(V3D_PCTR(15)), - REGDEF(V3D_PCTRS(15)), - REGDEF(V3D_DBGE), - REGDEF(V3D_FDBGO), - REGDEF(V3D_FDBGB), - REGDEF(V3D_FDBGR), - REGDEF(V3D_FDBGS), - REGDEF(V3D_ERRSTAT), +static const struct debugfs_reg32 v3d_regs[] = { + VC4_REG32(V3D_IDENT0), + VC4_REG32(V3D_IDENT1), + VC4_REG32(V3D_IDENT2), + VC4_REG32(V3D_SCRATCH), + VC4_REG32(V3D_L2CACTL), + VC4_REG32(V3D_SLCACTL), + VC4_REG32(V3D_INTCTL), + VC4_REG32(V3D_INTENA), + VC4_REG32(V3D_INTDIS), + VC4_REG32(V3D_CT0CS), + VC4_REG32(V3D_CT1CS), + VC4_REG32(V3D_CT0EA), + VC4_REG32(V3D_CT1EA), + VC4_REG32(V3D_CT0CA), + VC4_REG32(V3D_CT1CA), + VC4_REG32(V3D_CT00RA0), + VC4_REG32(V3D_CT01RA0), + VC4_REG32(V3D_CT0LC), + VC4_REG32(V3D_CT1LC), + VC4_REG32(V3D_CT0PC), + VC4_REG32(V3D_CT1PC), + VC4_REG32(V3D_PCS), + VC4_REG32(V3D_BFC), + VC4_REG32(V3D_RFC), + VC4_REG32(V3D_BPCA), + VC4_REG32(V3D_BPCS), + VC4_REG32(V3D_BPOA), + VC4_REG32(V3D_BPOS), + VC4_REG32(V3D_BXCF), + VC4_REG32(V3D_SQRSV0), + VC4_REG32(V3D_SQRSV1), + VC4_REG32(V3D_SQCNTL), + VC4_REG32(V3D_SRQPC), + VC4_REG32(V3D_SRQUA), + VC4_REG32(V3D_SRQUL), + VC4_REG32(V3D_SRQCS), + VC4_REG32(V3D_VPACNTL), + VC4_REG32(V3D_VPMBASE), + VC4_REG32(V3D_PCTRC), + VC4_REG32(V3D_PCTRE), + VC4_REG32(V3D_PCTR(0)), + VC4_REG32(V3D_PCTRS(0)), + VC4_REG32(V3D_PCTR(1)), + VC4_REG32(V3D_PCTRS(1)), + VC4_REG32(V3D_PCTR(2)), + VC4_REG32(V3D_PCTRS(2)), + VC4_REG32(V3D_PCTR(3)), + VC4_REG32(V3D_PCTRS(3)), + VC4_REG32(V3D_PCTR(4)), + VC4_REG32(V3D_PCTRS(4)), + VC4_REG32(V3D_PCTR(5)), + VC4_REG32(V3D_PCTRS(5)), + VC4_REG32(V3D_PCTR(6)), + VC4_REG32(V3D_PCTRS(6)), + VC4_REG32(V3D_PCTR(7)), + VC4_REG32(V3D_PCTRS(7)), + VC4_REG32(V3D_PCTR(8)), + VC4_REG32(V3D_PCTRS(8)), + VC4_REG32(V3D_PCTR(9)), + VC4_REG32(V3D_PCTRS(9)), + VC4_REG32(V3D_PCTR(10)), + VC4_REG32(V3D_PCTRS(10)), + VC4_REG32(V3D_PCTR(11)), + VC4_REG32(V3D_PCTRS(11)), + VC4_REG32(V3D_PCTR(12)), + VC4_REG32(V3D_PCTRS(12)), + VC4_REG32(V3D_PCTR(13)), + VC4_REG32(V3D_PCTRS(13)), + VC4_REG32(V3D_PCTR(14)), + VC4_REG32(V3D_PCTRS(14)), + VC4_REG32(V3D_PCTR(15)), + VC4_REG32(V3D_PCTRS(15)), + VC4_REG32(V3D_DBGE), + VC4_REG32(V3D_FDBGO), + VC4_REG32(V3D_FDBGB), + VC4_REG32(V3D_FDBGR), + VC4_REG32(V3D_FDBGS), + VC4_REG32(V3D_ERRSTAT), }; -int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused) +static int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *)m->private; struct drm_device *dev = node->minor->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); - int i; - - for (i = 0; i < ARRAY_SIZE(vc4_reg_defs); i++) { - seq_printf(m, "%s (0x%04x): 0x%08x\n", - vc4_reg_defs[i].name, vc4_reg_defs[i].reg, - V3D_READ(vc4_reg_defs[i].reg)); + int ret = vc4_v3d_pm_get(vc4); + + if (ret == 0) { + uint32_t ident1 = V3D_READ(V3D_IDENT1); + uint32_t nslc = VC4_GET_FIELD(ident1, V3D_IDENT1_NSLC); + uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS); + uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS); + + seq_printf(m, "Revision: %d\n", + VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); + seq_printf(m, "Slices: %d\n", nslc); + seq_printf(m, "TMUs: %d\n", nslc * tups); + seq_printf(m, "QPUs: %d\n", nslc * qups); + seq_printf(m, "Semaphores: %d\n", + VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); + vc4_v3d_pm_put(vc4); } return 0; } -int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) +/** + * Wraps pm_runtime_get_sync() in a refcount, so that we can reliably + * get the pm_runtime refcount to 0 in vc4_reset(). + */ +int +vc4_v3d_pm_get(struct vc4_dev *vc4) { - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *dev = node->minor->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - uint32_t ident1 = V3D_READ(V3D_IDENT1); - uint32_t nslc = VC4_GET_FIELD(ident1, V3D_IDENT1_NSLC); - uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS); - uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS); - - seq_printf(m, "Revision: %d\n", - VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); - seq_printf(m, "Slices: %d\n", nslc); - seq_printf(m, "TMUs: %d\n", nslc * tups); - seq_printf(m, "QPUs: %d\n", nslc * qups); - seq_printf(m, "Semaphores: %d\n", - VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); + mutex_lock(&vc4->power_lock); + if (vc4->power_refcount++ == 0) { + int ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); + + if (ret < 0) { + vc4->power_refcount--; + mutex_unlock(&vc4->power_lock); + return ret; + } + } + mutex_unlock(&vc4->power_lock); return 0; } -#endif /* CONFIG_DEBUG_FS */ + +void +vc4_v3d_pm_put(struct vc4_dev *vc4) +{ + mutex_lock(&vc4->power_lock); + if (--vc4->power_refcount == 0) { + pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev); + pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); + } + mutex_unlock(&vc4->power_lock); +} static void vc4_v3d_init_hw(struct drm_device *dev) { @@ -197,7 +213,7 @@ try_again: } /** - * vc4_allocate_bin_bo() - allocates the memory that will be used for + * bin_bo_alloc() - allocates the memory that will be used for * tile binning. * * The binner has a limitation that the addresses in the tile state @@ -218,14 +234,16 @@ try_again: * overall CMA pool before they make scenes complicated enough to run * out of bin space. */ -static int vc4_allocate_bin_bo(struct drm_device *drm) +static int bin_bo_alloc(struct vc4_dev *vc4) { - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_v3d *v3d = vc4->v3d; uint32_t size = 16 * 1024 * 1024; int ret = 0; struct list_head list; + if (!v3d) + return -ENODEV; + /* We may need to try allocating more than once to get a BO * that doesn't cross 256MB. Track the ones we've allocated * that failed so far, so that we can free them when we've got @@ -235,7 +253,7 @@ static int vc4_allocate_bin_bo(struct drm_device *drm) INIT_LIST_HEAD(&list); while (true) { - struct vc4_bo *bo = vc4_bo_create(drm, size, true, + struct vc4_bo *bo = vc4_bo_create(vc4->dev, size, true, VC4_BO_TYPE_BIN); if (IS_ERR(bo)) { @@ -276,6 +294,14 @@ static int vc4_allocate_bin_bo(struct drm_device *drm) WARN_ON_ONCE(sizeof(vc4->bin_alloc_used) * 8 != bo->base.base.size / vc4->bin_alloc_size); + kref_init(&vc4->bin_bo_kref); + + /* Enable the out-of-memory interrupt to set our + * newly-allocated binner BO, potentially from an + * already-pending-but-masked interrupt. + */ + V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM); + break; } @@ -295,6 +321,47 @@ static int vc4_allocate_bin_bo(struct drm_device *drm) return ret; } +int vc4_v3d_bin_bo_get(struct vc4_dev *vc4, bool *used) +{ + int ret = 0; + + mutex_lock(&vc4->bin_bo_lock); + + if (used && *used) + goto complete; + + if (vc4->bin_bo) + kref_get(&vc4->bin_bo_kref); + else + ret = bin_bo_alloc(vc4); + + if (ret == 0 && used) + *used = true; + +complete: + mutex_unlock(&vc4->bin_bo_lock); + + return ret; +} + +static void bin_bo_release(struct kref *ref) +{ + struct vc4_dev *vc4 = container_of(ref, struct vc4_dev, bin_bo_kref); + + if (WARN_ON_ONCE(!vc4->bin_bo)) + return; + + drm_gem_object_put_unlocked(&vc4->bin_bo->base.base); + vc4->bin_bo = NULL; +} + +void vc4_v3d_bin_bo_put(struct vc4_dev *vc4) +{ + mutex_lock(&vc4->bin_bo_lock); + kref_put(&vc4->bin_bo_kref, bin_bo_release); + mutex_unlock(&vc4->bin_bo_lock); +} + #ifdef CONFIG_PM static int vc4_v3d_runtime_suspend(struct device *dev) { @@ -303,9 +370,6 @@ static int vc4_v3d_runtime_suspend(struct device *dev) vc4_irq_uninstall(vc4->dev); - drm_gem_object_put_unlocked(&vc4->bin_bo->base.base); - vc4->bin_bo = NULL; - clk_disable_unprepare(v3d->clk); return 0; @@ -317,10 +381,6 @@ static int vc4_v3d_runtime_resume(struct device *dev) struct vc4_dev *vc4 = v3d->vc4; int ret; - ret = vc4_allocate_bin_bo(vc4->dev); - if (ret) - return ret; - ret = clk_prepare_enable(v3d->clk); if (ret != 0) return ret; @@ -354,6 +414,9 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) v3d->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(v3d->regs)) return PTR_ERR(v3d->regs); + v3d->regset.base = v3d->regs; + v3d->regset.regs = v3d_regs; + v3d->regset.nregs = ARRAY_SIZE(v3d_regs); vc4->v3d = v3d; v3d->vc4 = vc4; @@ -384,12 +447,6 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) if (ret != 0) return ret; - ret = vc4_allocate_bin_bo(drm); - if (ret) { - clk_disable_unprepare(v3d->clk); - return ret; - } - /* Reset the binner overflow address/size at setup, to be sure * we don't reuse an old one. */ @@ -409,6 +466,9 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) pm_runtime_set_autosuspend_delay(dev, 40); /* a little over 2 frames. */ pm_runtime_enable(dev); + vc4_debugfs_add_file(drm, "v3d_ident", vc4_v3d_debugfs_ident, NULL); + vc4_debugfs_add_regset32(drm, "v3d_regs", &v3d->regset); + return 0; } @@ -452,7 +512,7 @@ static int vc4_v3d_dev_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id vc4_v3d_dt_match[] = { +const struct of_device_id vc4_v3d_dt_match[] = { { .compatible = "brcm,bcm2835-v3d" }, { .compatible = "brcm,cygnus-v3d" }, { .compatible = "brcm,vc4-v3d" }, diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index 858c3a483229..0a27e48fab31 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -176,6 +176,8 @@ struct vc4_vec { struct clk *clock; const struct vc4_vec_tv_mode *tv_mode; + + struct debugfs_regset32 regset; }; #define VEC_READ(offset) readl(vec->regs + (offset)) @@ -223,59 +225,33 @@ struct vc4_vec_tv_mode { void (*mode_set)(struct vc4_vec *vec); }; -#define VEC_REG(reg) { reg, #reg } -static const struct { - u32 reg; - const char *name; -} vec_regs[] = { - VEC_REG(VEC_WSE_CONTROL), - VEC_REG(VEC_WSE_WSS_DATA), - VEC_REG(VEC_WSE_VPS_DATA1), - VEC_REG(VEC_WSE_VPS_CONTROL), - VEC_REG(VEC_REVID), - VEC_REG(VEC_CONFIG0), - VEC_REG(VEC_SCHPH), - VEC_REG(VEC_CLMP0_START), - VEC_REG(VEC_CLMP0_END), - VEC_REG(VEC_FREQ3_2), - VEC_REG(VEC_FREQ1_0), - VEC_REG(VEC_CONFIG1), - VEC_REG(VEC_CONFIG2), - VEC_REG(VEC_INTERRUPT_CONTROL), - VEC_REG(VEC_INTERRUPT_STATUS), - VEC_REG(VEC_FCW_SECAM_B), - VEC_REG(VEC_SECAM_GAIN_VAL), - VEC_REG(VEC_CONFIG3), - VEC_REG(VEC_STATUS0), - VEC_REG(VEC_MASK0), - VEC_REG(VEC_CFG), - VEC_REG(VEC_DAC_TEST), - VEC_REG(VEC_DAC_CONFIG), - VEC_REG(VEC_DAC_MISC), +static const struct debugfs_reg32 vec_regs[] = { + VC4_REG32(VEC_WSE_CONTROL), + VC4_REG32(VEC_WSE_WSS_DATA), + VC4_REG32(VEC_WSE_VPS_DATA1), + VC4_REG32(VEC_WSE_VPS_CONTROL), + VC4_REG32(VEC_REVID), + VC4_REG32(VEC_CONFIG0), + VC4_REG32(VEC_SCHPH), + VC4_REG32(VEC_CLMP0_START), + VC4_REG32(VEC_CLMP0_END), + VC4_REG32(VEC_FREQ3_2), + VC4_REG32(VEC_FREQ1_0), + VC4_REG32(VEC_CONFIG1), + VC4_REG32(VEC_CONFIG2), + VC4_REG32(VEC_INTERRUPT_CONTROL), + VC4_REG32(VEC_INTERRUPT_STATUS), + VC4_REG32(VEC_FCW_SECAM_B), + VC4_REG32(VEC_SECAM_GAIN_VAL), + VC4_REG32(VEC_CONFIG3), + VC4_REG32(VEC_STATUS0), + VC4_REG32(VEC_MASK0), + VC4_REG32(VEC_CFG), + VC4_REG32(VEC_DAC_TEST), + VC4_REG32(VEC_DAC_CONFIG), + VC4_REG32(VEC_DAC_MISC), }; -#ifdef CONFIG_DEBUG_FS -int vc4_vec_debugfs_regs(struct seq_file *m, void *unused) -{ - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *dev = node->minor->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_vec *vec = vc4->vec; - int i; - - if (!vec) - return 0; - - for (i = 0; i < ARRAY_SIZE(vec_regs); i++) { - seq_printf(m, "%s (0x%04x): 0x%08x\n", - vec_regs[i].name, vec_regs[i].reg, - VEC_READ(vec_regs[i].reg)); - } - - return 0; -} -#endif - static void vc4_vec_ntsc_mode_set(struct vc4_vec *vec) { VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN); @@ -587,6 +563,9 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) vec->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(vec->regs)) return PTR_ERR(vec->regs); + vec->regset.base = vec->regs; + vec->regset.regs = vec_regs; + vec->regset.nregs = ARRAY_SIZE(vec_regs); vec->clock = devm_clk_get(dev, NULL); if (IS_ERR(vec->clock)) { @@ -612,6 +591,8 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) vc4->vec = vec; + vc4_debugfs_add_regset32(drm, "vec_regs", &vec->regset); + return 0; err_destroy_encoder: |