162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2014-2015 Broadcom
462306a36Sopenharmony_ci * Copyright (C) 2013 Red Hat
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/**
862306a36Sopenharmony_ci * DOC: Broadcom VC4 Graphics Driver
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * The Broadcom VideoCore 4 (present in the Raspberry Pi) contains a
1162306a36Sopenharmony_ci * OpenGL ES 2.0-compatible 3D engine called V3D, and a highly
1262306a36Sopenharmony_ci * configurable display output pipeline that supports HDMI, DSI, DPI,
1362306a36Sopenharmony_ci * and Composite TV output.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * The 3D engine also has an interface for submitting arbitrary
1662306a36Sopenharmony_ci * compute shader-style jobs using the same shader processor as is
1762306a36Sopenharmony_ci * used for vertex and fragment shaders in GLES 2.0.  However, given
1862306a36Sopenharmony_ci * that the hardware isn't able to expose any standard interfaces like
1962306a36Sopenharmony_ci * OpenGL compute shaders or OpenCL, it isn't supported by this
2062306a36Sopenharmony_ci * driver.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/clk.h>
2462306a36Sopenharmony_ci#include <linux/component.h>
2562306a36Sopenharmony_ci#include <linux/device.h>
2662306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2762306a36Sopenharmony_ci#include <linux/io.h>
2862306a36Sopenharmony_ci#include <linux/module.h>
2962306a36Sopenharmony_ci#include <linux/of_device.h>
3062306a36Sopenharmony_ci#include <linux/platform_device.h>
3162306a36Sopenharmony_ci#include <linux/pm_runtime.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <drm/drm_aperture.h>
3462306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
3562306a36Sopenharmony_ci#include <drm/drm_drv.h>
3662306a36Sopenharmony_ci#include <drm/drm_fbdev_dma.h>
3762306a36Sopenharmony_ci#include <drm/drm_vblank.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#include <soc/bcm2835/raspberrypi-firmware.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include "uapi/drm/vc4_drm.h"
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include "vc4_drv.h"
4462306a36Sopenharmony_ci#include "vc4_regs.h"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define DRIVER_NAME "vc4"
4762306a36Sopenharmony_ci#define DRIVER_DESC "Broadcom VC4 graphics"
4862306a36Sopenharmony_ci#define DRIVER_DATE "20140616"
4962306a36Sopenharmony_ci#define DRIVER_MAJOR 0
5062306a36Sopenharmony_ci#define DRIVER_MINOR 0
5162306a36Sopenharmony_ci#define DRIVER_PATCHLEVEL 0
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* Helper function for mapping the regs on a platform device. */
5462306a36Sopenharmony_civoid __iomem *vc4_ioremap_regs(struct platform_device *pdev, int index)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	void __iomem *map;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	map = devm_platform_ioremap_resource(pdev, index);
5962306a36Sopenharmony_ci	if (IS_ERR(map))
6062306a36Sopenharmony_ci		return map;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return map;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciint vc4_dumb_fixup_args(struct drm_mode_create_dumb *args)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (args->pitch < min_pitch)
7062306a36Sopenharmony_ci		args->pitch = min_pitch;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (args->size < args->pitch * args->height)
7362306a36Sopenharmony_ci		args->size = args->pitch * args->height;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int vc5_dumb_create(struct drm_file *file_priv,
7962306a36Sopenharmony_ci			   struct drm_device *dev,
8062306a36Sopenharmony_ci			   struct drm_mode_create_dumb *args)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	int ret;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	ret = vc4_dumb_fixup_args(args);
8562306a36Sopenharmony_ci	if (ret)
8662306a36Sopenharmony_ci		return ret;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return drm_gem_dma_dumb_create_internal(file_priv, dev, args);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int vc4_get_param_ioctl(struct drm_device *dev, void *data,
9262306a36Sopenharmony_ci			       struct drm_file *file_priv)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
9562306a36Sopenharmony_ci	struct drm_vc4_get_param *args = data;
9662306a36Sopenharmony_ci	int ret;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (args->pad != 0)
9962306a36Sopenharmony_ci		return -EINVAL;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
10262306a36Sopenharmony_ci		return -ENODEV;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (!vc4->v3d)
10562306a36Sopenharmony_ci		return -ENODEV;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	switch (args->param) {
10862306a36Sopenharmony_ci	case DRM_VC4_PARAM_V3D_IDENT0:
10962306a36Sopenharmony_ci		ret = vc4_v3d_pm_get(vc4);
11062306a36Sopenharmony_ci		if (ret)
11162306a36Sopenharmony_ci			return ret;
11262306a36Sopenharmony_ci		args->value = V3D_READ(V3D_IDENT0);
11362306a36Sopenharmony_ci		vc4_v3d_pm_put(vc4);
11462306a36Sopenharmony_ci		break;
11562306a36Sopenharmony_ci	case DRM_VC4_PARAM_V3D_IDENT1:
11662306a36Sopenharmony_ci		ret = vc4_v3d_pm_get(vc4);
11762306a36Sopenharmony_ci		if (ret)
11862306a36Sopenharmony_ci			return ret;
11962306a36Sopenharmony_ci		args->value = V3D_READ(V3D_IDENT1);
12062306a36Sopenharmony_ci		vc4_v3d_pm_put(vc4);
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci	case DRM_VC4_PARAM_V3D_IDENT2:
12362306a36Sopenharmony_ci		ret = vc4_v3d_pm_get(vc4);
12462306a36Sopenharmony_ci		if (ret)
12562306a36Sopenharmony_ci			return ret;
12662306a36Sopenharmony_ci		args->value = V3D_READ(V3D_IDENT2);
12762306a36Sopenharmony_ci		vc4_v3d_pm_put(vc4);
12862306a36Sopenharmony_ci		break;
12962306a36Sopenharmony_ci	case DRM_VC4_PARAM_SUPPORTS_BRANCHES:
13062306a36Sopenharmony_ci	case DRM_VC4_PARAM_SUPPORTS_ETC1:
13162306a36Sopenharmony_ci	case DRM_VC4_PARAM_SUPPORTS_THREADED_FS:
13262306a36Sopenharmony_ci	case DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER:
13362306a36Sopenharmony_ci	case DRM_VC4_PARAM_SUPPORTS_MADVISE:
13462306a36Sopenharmony_ci	case DRM_VC4_PARAM_SUPPORTS_PERFMON:
13562306a36Sopenharmony_ci		args->value = true;
13662306a36Sopenharmony_ci		break;
13762306a36Sopenharmony_ci	default:
13862306a36Sopenharmony_ci		DRM_DEBUG("Unknown parameter %d\n", args->param);
13962306a36Sopenharmony_ci		return -EINVAL;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return 0;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int vc4_open(struct drm_device *dev, struct drm_file *file)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
14862306a36Sopenharmony_ci	struct vc4_file *vc4file;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
15162306a36Sopenharmony_ci		return -ENODEV;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	vc4file = kzalloc(sizeof(*vc4file), GFP_KERNEL);
15462306a36Sopenharmony_ci	if (!vc4file)
15562306a36Sopenharmony_ci		return -ENOMEM;
15662306a36Sopenharmony_ci	vc4file->dev = vc4;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	vc4_perfmon_open_file(vc4file);
15962306a36Sopenharmony_ci	file->driver_priv = vc4file;
16062306a36Sopenharmony_ci	return 0;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic void vc4_close(struct drm_device *dev, struct drm_file *file)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
16662306a36Sopenharmony_ci	struct vc4_file *vc4file = file->driver_priv;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
16962306a36Sopenharmony_ci		return;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (vc4file->bin_bo_used)
17262306a36Sopenharmony_ci		vc4_v3d_bin_bo_put(vc4);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	vc4_perfmon_close_file(vc4file);
17562306a36Sopenharmony_ci	kfree(vc4file);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ciDEFINE_DRM_GEM_FOPS(vc4_drm_fops);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic const struct drm_ioctl_desc vc4_drm_ioctls[] = {
18162306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_SUBMIT_CL, vc4_submit_cl_ioctl, DRM_RENDER_ALLOW),
18262306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_WAIT_SEQNO, vc4_wait_seqno_ioctl, DRM_RENDER_ALLOW),
18362306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_WAIT_BO, vc4_wait_bo_ioctl, DRM_RENDER_ALLOW),
18462306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, DRM_RENDER_ALLOW),
18562306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, DRM_RENDER_ALLOW),
18662306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, DRM_RENDER_ALLOW),
18762306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl,
18862306a36Sopenharmony_ci			  DRM_ROOT_ONLY),
18962306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_GET_PARAM, vc4_get_param_ioctl, DRM_RENDER_ALLOW),
19062306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_SET_TILING, vc4_set_tiling_ioctl, DRM_RENDER_ALLOW),
19162306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_GET_TILING, vc4_get_tiling_ioctl, DRM_RENDER_ALLOW),
19262306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_LABEL_BO, vc4_label_bo_ioctl, DRM_RENDER_ALLOW),
19362306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_GEM_MADVISE, vc4_gem_madvise_ioctl, DRM_RENDER_ALLOW),
19462306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_PERFMON_CREATE, vc4_perfmon_create_ioctl, DRM_RENDER_ALLOW),
19562306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_PERFMON_DESTROY, vc4_perfmon_destroy_ioctl, DRM_RENDER_ALLOW),
19662306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(VC4_PERFMON_GET_VALUES, vc4_perfmon_get_values_ioctl, DRM_RENDER_ALLOW),
19762306a36Sopenharmony_ci};
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ciconst struct drm_driver vc4_drm_driver = {
20062306a36Sopenharmony_ci	.driver_features = (DRIVER_MODESET |
20162306a36Sopenharmony_ci			    DRIVER_ATOMIC |
20262306a36Sopenharmony_ci			    DRIVER_GEM |
20362306a36Sopenharmony_ci			    DRIVER_RENDER |
20462306a36Sopenharmony_ci			    DRIVER_SYNCOBJ),
20562306a36Sopenharmony_ci	.open = vc4_open,
20662306a36Sopenharmony_ci	.postclose = vc4_close,
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_FS)
20962306a36Sopenharmony_ci	.debugfs_init = vc4_debugfs_init,
21062306a36Sopenharmony_ci#endif
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	.gem_create_object = vc4_create_object,
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vc4_bo_dumb_create),
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	.ioctls = vc4_drm_ioctls,
21762306a36Sopenharmony_ci	.num_ioctls = ARRAY_SIZE(vc4_drm_ioctls),
21862306a36Sopenharmony_ci	.fops = &vc4_drm_fops,
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	.name = DRIVER_NAME,
22162306a36Sopenharmony_ci	.desc = DRIVER_DESC,
22262306a36Sopenharmony_ci	.date = DRIVER_DATE,
22362306a36Sopenharmony_ci	.major = DRIVER_MAJOR,
22462306a36Sopenharmony_ci	.minor = DRIVER_MINOR,
22562306a36Sopenharmony_ci	.patchlevel = DRIVER_PATCHLEVEL,
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ciconst struct drm_driver vc5_drm_driver = {
22962306a36Sopenharmony_ci	.driver_features = (DRIVER_MODESET |
23062306a36Sopenharmony_ci			    DRIVER_ATOMIC |
23162306a36Sopenharmony_ci			    DRIVER_GEM),
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_FS)
23462306a36Sopenharmony_ci	.debugfs_init = vc4_debugfs_init,
23562306a36Sopenharmony_ci#endif
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vc5_dumb_create),
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	.fops = &vc4_drm_fops,
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	.name = DRIVER_NAME,
24262306a36Sopenharmony_ci	.desc = DRIVER_DESC,
24362306a36Sopenharmony_ci	.date = DRIVER_DATE,
24462306a36Sopenharmony_ci	.major = DRIVER_MAJOR,
24562306a36Sopenharmony_ci	.minor = DRIVER_MINOR,
24662306a36Sopenharmony_ci	.patchlevel = DRIVER_PATCHLEVEL,
24762306a36Sopenharmony_ci};
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void vc4_match_add_drivers(struct device *dev,
25062306a36Sopenharmony_ci				  struct component_match **match,
25162306a36Sopenharmony_ci				  struct platform_driver *const *drivers,
25262306a36Sopenharmony_ci				  int count)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	int i;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
25762306a36Sopenharmony_ci		struct device_driver *drv = &drivers[i]->driver;
25862306a36Sopenharmony_ci		struct device *p = NULL, *d;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		while ((d = platform_find_device_by_driver(p, drv))) {
26162306a36Sopenharmony_ci			put_device(p);
26262306a36Sopenharmony_ci			component_match_add(dev, match, component_compare_dev, d);
26362306a36Sopenharmony_ci			p = d;
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci		put_device(p);
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic void vc4_component_unbind_all(void *ptr)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct vc4_dev *vc4 = ptr;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	component_unbind_all(vc4->dev, &vc4->base);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic const struct of_device_id vc4_dma_range_matches[] = {
27762306a36Sopenharmony_ci	{ .compatible = "brcm,bcm2711-hvs" },
27862306a36Sopenharmony_ci	{ .compatible = "brcm,bcm2835-hvs" },
27962306a36Sopenharmony_ci	{ .compatible = "brcm,bcm2835-v3d" },
28062306a36Sopenharmony_ci	{ .compatible = "brcm,cygnus-v3d" },
28162306a36Sopenharmony_ci	{ .compatible = "brcm,vc4-v3d" },
28262306a36Sopenharmony_ci	{}
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int vc4_drm_bind(struct device *dev)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
28862306a36Sopenharmony_ci	const struct drm_driver *driver;
28962306a36Sopenharmony_ci	struct rpi_firmware *firmware = NULL;
29062306a36Sopenharmony_ci	struct drm_device *drm;
29162306a36Sopenharmony_ci	struct vc4_dev *vc4;
29262306a36Sopenharmony_ci	struct device_node *node;
29362306a36Sopenharmony_ci	struct drm_crtc *crtc;
29462306a36Sopenharmony_ci	bool is_vc5;
29562306a36Sopenharmony_ci	int ret = 0;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	dev->coherent_dma_mask = DMA_BIT_MASK(32);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	is_vc5 = of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5");
30062306a36Sopenharmony_ci	if (is_vc5)
30162306a36Sopenharmony_ci		driver = &vc5_drm_driver;
30262306a36Sopenharmony_ci	else
30362306a36Sopenharmony_ci		driver = &vc4_drm_driver;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	node = of_find_matching_node_and_match(NULL, vc4_dma_range_matches,
30662306a36Sopenharmony_ci					       NULL);
30762306a36Sopenharmony_ci	if (node) {
30862306a36Sopenharmony_ci		ret = of_dma_configure(dev, node, true);
30962306a36Sopenharmony_ci		of_node_put(node);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		if (ret)
31262306a36Sopenharmony_ci			return ret;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	vc4 = devm_drm_dev_alloc(dev, driver, struct vc4_dev, base);
31662306a36Sopenharmony_ci	if (IS_ERR(vc4))
31762306a36Sopenharmony_ci		return PTR_ERR(vc4);
31862306a36Sopenharmony_ci	vc4->is_vc5 = is_vc5;
31962306a36Sopenharmony_ci	vc4->dev = dev;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	drm = &vc4->base;
32262306a36Sopenharmony_ci	platform_set_drvdata(pdev, drm);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (!is_vc5) {
32562306a36Sopenharmony_ci		ret = drmm_mutex_init(drm, &vc4->bin_bo_lock);
32662306a36Sopenharmony_ci		if (ret)
32762306a36Sopenharmony_ci			return ret;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		ret = vc4_bo_cache_init(drm);
33062306a36Sopenharmony_ci		if (ret)
33162306a36Sopenharmony_ci			return ret;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	ret = drmm_mode_config_init(drm);
33562306a36Sopenharmony_ci	if (ret)
33662306a36Sopenharmony_ci		return ret;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (!is_vc5) {
33962306a36Sopenharmony_ci		ret = vc4_gem_init(drm);
34062306a36Sopenharmony_ci		if (ret)
34162306a36Sopenharmony_ci			return ret;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	node = of_find_compatible_node(NULL, NULL, "raspberrypi,bcm2835-firmware");
34562306a36Sopenharmony_ci	if (node) {
34662306a36Sopenharmony_ci		firmware = rpi_firmware_get(node);
34762306a36Sopenharmony_ci		of_node_put(node);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		if (!firmware)
35062306a36Sopenharmony_ci			return -EPROBE_DEFER;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	ret = drm_aperture_remove_framebuffers(driver);
35462306a36Sopenharmony_ci	if (ret)
35562306a36Sopenharmony_ci		return ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (firmware) {
35862306a36Sopenharmony_ci		ret = rpi_firmware_property(firmware,
35962306a36Sopenharmony_ci					    RPI_FIRMWARE_NOTIFY_DISPLAY_DONE,
36062306a36Sopenharmony_ci					    NULL, 0);
36162306a36Sopenharmony_ci		if (ret)
36262306a36Sopenharmony_ci			drm_warn(drm, "Couldn't stop firmware display driver: %d\n", ret);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci		rpi_firmware_put(firmware);
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	ret = component_bind_all(dev, drm);
36862306a36Sopenharmony_ci	if (ret)
36962306a36Sopenharmony_ci		return ret;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	ret = devm_add_action_or_reset(dev, vc4_component_unbind_all, vc4);
37262306a36Sopenharmony_ci	if (ret)
37362306a36Sopenharmony_ci		return ret;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	ret = vc4_plane_create_additional_planes(drm);
37662306a36Sopenharmony_ci	if (ret)
37762306a36Sopenharmony_ci		goto unbind_all;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	ret = vc4_kms_load(drm);
38062306a36Sopenharmony_ci	if (ret < 0)
38162306a36Sopenharmony_ci		goto unbind_all;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	drm_for_each_crtc(crtc, drm)
38462306a36Sopenharmony_ci		vc4_crtc_disable_at_boot(crtc);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	ret = drm_dev_register(drm, 0);
38762306a36Sopenharmony_ci	if (ret < 0)
38862306a36Sopenharmony_ci		goto unbind_all;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	drm_fbdev_dma_setup(drm, 16);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return 0;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ciunbind_all:
39562306a36Sopenharmony_ci	return ret;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic void vc4_drm_unbind(struct device *dev)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(dev);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	drm_dev_unplug(drm);
40362306a36Sopenharmony_ci	drm_atomic_helper_shutdown(drm);
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic const struct component_master_ops vc4_drm_ops = {
40762306a36Sopenharmony_ci	.bind = vc4_drm_bind,
40862306a36Sopenharmony_ci	.unbind = vc4_drm_unbind,
40962306a36Sopenharmony_ci};
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/*
41262306a36Sopenharmony_ci * This list determines the binding order of our components, and we have
41362306a36Sopenharmony_ci * a few constraints:
41462306a36Sopenharmony_ci *   - The TXP driver needs to be bound before the PixelValves (CRTC)
41562306a36Sopenharmony_ci *     but after the HVS to set the possible_crtc field properly
41662306a36Sopenharmony_ci *   - The HDMI driver needs to be bound after the HVS so that we can
41762306a36Sopenharmony_ci *     lookup the HVS maximum core clock rate and figure out if we
41862306a36Sopenharmony_ci *     support 4kp60 or not.
41962306a36Sopenharmony_ci */
42062306a36Sopenharmony_cistatic struct platform_driver *const component_drivers[] = {
42162306a36Sopenharmony_ci	&vc4_hvs_driver,
42262306a36Sopenharmony_ci	&vc4_hdmi_driver,
42362306a36Sopenharmony_ci	&vc4_vec_driver,
42462306a36Sopenharmony_ci	&vc4_dpi_driver,
42562306a36Sopenharmony_ci	&vc4_dsi_driver,
42662306a36Sopenharmony_ci	&vc4_txp_driver,
42762306a36Sopenharmony_ci	&vc4_crtc_driver,
42862306a36Sopenharmony_ci	&vc4_v3d_driver,
42962306a36Sopenharmony_ci};
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int vc4_platform_drm_probe(struct platform_device *pdev)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct component_match *match = NULL;
43462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	vc4_match_add_drivers(dev, &match,
43762306a36Sopenharmony_ci			      component_drivers, ARRAY_SIZE(component_drivers));
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	return component_master_add_with_match(dev, &vc4_drm_ops, match);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic void vc4_platform_drm_remove(struct platform_device *pdev)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	component_master_del(&pdev->dev, &vc4_drm_ops);
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic const struct of_device_id vc4_of_match[] = {
44862306a36Sopenharmony_ci	{ .compatible = "brcm,bcm2711-vc5", },
44962306a36Sopenharmony_ci	{ .compatible = "brcm,bcm2835-vc4", },
45062306a36Sopenharmony_ci	{ .compatible = "brcm,cygnus-vc4", },
45162306a36Sopenharmony_ci	{},
45262306a36Sopenharmony_ci};
45362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, vc4_of_match);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic struct platform_driver vc4_platform_driver = {
45662306a36Sopenharmony_ci	.probe		= vc4_platform_drm_probe,
45762306a36Sopenharmony_ci	.remove_new	= vc4_platform_drm_remove,
45862306a36Sopenharmony_ci	.driver		= {
45962306a36Sopenharmony_ci		.name	= "vc4-drm",
46062306a36Sopenharmony_ci		.of_match_table = vc4_of_match,
46162306a36Sopenharmony_ci	},
46262306a36Sopenharmony_ci};
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic int __init vc4_drm_register(void)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	int ret;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (drm_firmware_drivers_only())
46962306a36Sopenharmony_ci		return -ENODEV;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	ret = platform_register_drivers(component_drivers,
47262306a36Sopenharmony_ci					ARRAY_SIZE(component_drivers));
47362306a36Sopenharmony_ci	if (ret)
47462306a36Sopenharmony_ci		return ret;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	ret = platform_driver_register(&vc4_platform_driver);
47762306a36Sopenharmony_ci	if (ret)
47862306a36Sopenharmony_ci		platform_unregister_drivers(component_drivers,
47962306a36Sopenharmony_ci					    ARRAY_SIZE(component_drivers));
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return ret;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic void __exit vc4_drm_unregister(void)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	platform_unregister_drivers(component_drivers,
48762306a36Sopenharmony_ci				    ARRAY_SIZE(component_drivers));
48862306a36Sopenharmony_ci	platform_driver_unregister(&vc4_platform_driver);
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cimodule_init(vc4_drm_register);
49262306a36Sopenharmony_cimodule_exit(vc4_drm_unregister);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ciMODULE_ALIAS("platform:vc4-drm");
49562306a36Sopenharmony_ciMODULE_SOFTDEP("pre: snd-soc-hdmi-codec");
49662306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom VC4 DRM Driver");
49762306a36Sopenharmony_ciMODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
49862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
499