diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index eb776a6a9899..e87a6f2f5916 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -11,6 +11,7 @@ vc4-y := \ vc4_hdmi.o \ vc4_hvs.o \ vc4_plane.o \ + vc4_v3d.o \ vc4_validate_shaders.o vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c index 6bcf96e9a482..d76ad10b07fd 100644 --- a/drivers/gpu/drm/vc4/vc4_debugfs.c +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c @@ -22,6 +22,8 @@ static const struct drm_info_list vc4_debugfs_list[] = { {"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}, }; #define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list) diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index da4be9c8f70d..db58d74efe95 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -236,6 +236,7 @@ static struct platform_driver *const component_drivers[] = { &vc4_hdmi_driver, &vc4_crtc_driver, &vc4_hvs_driver, + &vc4_v3d_driver, }; static int vc4_platform_drm_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index bd77d5574e30..8945463e70b6 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -15,6 +15,7 @@ struct vc4_dev { struct vc4_hdmi *hdmi; struct vc4_hvs *hvs; struct vc4_crtc *crtc[3]; + struct vc4_v3d *v3d; struct drm_fbdev_cma *fbdev; @@ -82,6 +83,11 @@ to_vc4_bo(struct drm_gem_object *bo) return (struct vc4_bo *)bo; } +struct vc4_v3d { + struct platform_device *pdev; + void __iomem *regs; +}; + struct vc4_hvs { struct platform_device *pdev; void __iomem *regs; @@ -119,6 +125,8 @@ to_vc4_encoder(struct drm_encoder *encoder) return container_of(encoder, struct vc4_encoder, base); } +#define V3D_READ(offset) readl(vc4->v3d->regs + offset) +#define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset) #define HVS_READ(offset) readl(vc4->hvs->regs + offset) #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) @@ -241,6 +249,11 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); u32 vc4_plane_dlist_size(struct drm_plane_state *state); +/* 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); + /* vc4_validate_shader.c */ struct vc4_validated_shader_info * vc4_validate_shader(struct drm_gem_cma_object *shader_obj); diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c new file mode 100644 index 000000000000..040ad0d8b8a1 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2014 The Linux Foundation. All rights reserved. + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "linux/component.h" +#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_PCTR0), + REGDEF(V3D_PCTRS0), + REGDEF(V3D_PCTR1), + REGDEF(V3D_PCTRS1), + REGDEF(V3D_PCTR2), + REGDEF(V3D_PCTRS2), + REGDEF(V3D_PCTR3), + REGDEF(V3D_PCTRS3), + REGDEF(V3D_PCTR4), + REGDEF(V3D_PCTRS4), + REGDEF(V3D_PCTR5), + REGDEF(V3D_PCTRS5), + REGDEF(V3D_PCTR6), + REGDEF(V3D_PCTRS6), + REGDEF(V3D_PCTR7), + REGDEF(V3D_PCTRS7), + REGDEF(V3D_PCTR8), + REGDEF(V3D_PCTRS8), + REGDEF(V3D_PCTR9), + REGDEF(V3D_PCTRS9), + REGDEF(V3D_PCTR10), + REGDEF(V3D_PCTRS10), + REGDEF(V3D_PCTR11), + REGDEF(V3D_PCTRS11), + REGDEF(V3D_PCTR12), + REGDEF(V3D_PCTRS12), + REGDEF(V3D_PCTR13), + REGDEF(V3D_PCTRS13), + REGDEF(V3D_PCTR14), + REGDEF(V3D_PCTRS14), + REGDEF(V3D_PCTR15), + REGDEF(V3D_PCTRS15), + REGDEF(V3D_DBGE), + REGDEF(V3D_FDBGO), + REGDEF(V3D_FDBGB), + REGDEF(V3D_FDBGR), + REGDEF(V3D_FDBGS), + REGDEF(V3D_ERRSTAT), +}; + +int vc4_v3d_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(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)); + } + + return 0; +} + +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); + 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)); + + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static void vc4_v3d_init_hw(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + /* Take all the memory that would have been reserved for user + * QPU programs, since we don't have an interface for running + * them, anyway. + */ + V3D_WRITE(V3D_VPMBASE, 0); +} + +static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_v3d *v3d = NULL; + + v3d = devm_kzalloc(&pdev->dev, sizeof(*v3d), GFP_KERNEL); + if (!v3d) + return -ENOMEM; + + v3d->pdev = pdev; + + v3d->regs = vc4_ioremap_regs(pdev, 0); + if (IS_ERR(v3d->regs)) + return PTR_ERR(v3d->regs); + + vc4->v3d = v3d; + + if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) { + DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n", + V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0); + return -EINVAL; + } + + vc4_v3d_init_hw(drm); + + return 0; +} + +static void vc4_v3d_unbind(struct device *dev, struct device *master, + void *data) +{ + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + + vc4->v3d = NULL; +} + +static const struct component_ops vc4_v3d_ops = { + .bind = vc4_v3d_bind, + .unbind = vc4_v3d_unbind, +}; + +static int vc4_v3d_dev_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vc4_v3d_ops); +} + +static int vc4_v3d_dev_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vc4_v3d_ops); + return 0; +} + +static const struct of_device_id vc4_v3d_dt_match[] = { + { .compatible = "brcm,vc4-v3d" }, + {} +}; + +struct platform_driver vc4_v3d_driver = { + .probe = vc4_v3d_dev_probe, + .remove = vc4_v3d_dev_remove, + .driver = { + .name = "vc4_v3d", + .of_match_table = vc4_v3d_dt_match, + }, +};