rockchip_drm_drv.c 12.3 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
M
Mark Yao 已提交
2 3 4 5 6 7 8 9 10
/*
 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
 * Author:Mark Yao <mark.yao@rock-chips.com>
 *
 * based on exynos_drm_drv.c
 */

#include <drm/drmP.h>
#include <drm/drm_fb_helper.h>
D
Daniel Vetter 已提交
11
#include <drm/drm_gem_cma_helper.h>
12
#include <drm/drm_of.h>
13
#include <drm/drm_probe_helper.h>
M
Mark Yao 已提交
14
#include <linux/dma-mapping.h>
15
#include <linux/dma-iommu.h>
M
Mark Yao 已提交
16
#include <linux/pm_runtime.h>
17
#include <linux/module.h>
M
Mark Yao 已提交
18
#include <linux/of_graph.h>
19
#include <linux/of_platform.h>
M
Mark Yao 已提交
20
#include <linux/component.h>
21
#include <linux/console.h>
22
#include <linux/iommu.h>
M
Mark Yao 已提交
23 24 25 26 27 28 29 30 31 32 33 34

#include "rockchip_drm_drv.h"
#include "rockchip_drm_fb.h"
#include "rockchip_drm_fbdev.h"
#include "rockchip_drm_gem.h"

#define DRIVER_NAME	"rockchip"
#define DRIVER_DESC	"RockChip Soc DRM"
#define DRIVER_DATE	"20140818"
#define DRIVER_MAJOR	1
#define DRIVER_MINOR	0

35
static bool is_support_iommu = true;
36
static struct drm_driver rockchip_drm_driver;
37

M
Mark Yao 已提交
38 39 40 41 42 43 44 45
/*
 * Attach a (component) device to the shared drm dma mapping from master drm
 * device.  This is used by the VOPs to map GEM buffers to a common DMA
 * mapping.
 */
int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
				   struct device *dev)
{
46
	struct rockchip_drm_private *private = drm_dev->dev_private;
M
Mark Yao 已提交
47 48
	int ret;

49 50 51
	if (!is_support_iommu)
		return 0;

52 53
	ret = iommu_attach_device(private->domain, dev);
	if (ret) {
54
		DRM_DEV_ERROR(dev, "Failed to attach iommu device\n");
M
Mark Yao 已提交
55
		return ret;
56
	}
M
Mark Yao 已提交
57

58
	return 0;
M
Mark Yao 已提交
59 60 61 62 63
}

void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
				    struct device *dev)
{
64 65 66
	struct rockchip_drm_private *private = drm_dev->dev_private;
	struct iommu_domain *domain = private->domain;

67 68 69
	if (!is_support_iommu)
		return;

70
	iommu_detach_device(domain, dev);
M
Mark Yao 已提交
71 72
}

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
{
	struct rockchip_drm_private *private = drm_dev->dev_private;
	struct iommu_domain_geometry *geometry;
	u64 start, end;

	if (!is_support_iommu)
		return 0;

	private->domain = iommu_domain_alloc(&platform_bus_type);
	if (!private->domain)
		return -ENOMEM;

	geometry = &private->domain->geometry;
	start = geometry->aperture_start;
	end = geometry->aperture_end;

	DRM_DEBUG("IOMMU context initialized (aperture: %#llx-%#llx)\n",
		  start, end);
	drm_mm_init(&private->mm, start, end - start + 1);
	mutex_init(&private->mm_lock);

	return 0;
}

static void rockchip_iommu_cleanup(struct drm_device *drm_dev)
{
	struct rockchip_drm_private *private = drm_dev->dev_private;

	if (!is_support_iommu)
		return;

	drm_mm_takedown(&private->mm);
	iommu_domain_free(private->domain);
M
Mark Yao 已提交
107 108
}

109
static int rockchip_drm_bind(struct device *dev)
M
Mark Yao 已提交
110
{
111
	struct drm_device *drm_dev;
M
Mark Yao 已提交
112 113 114
	struct rockchip_drm_private *private;
	int ret;

115
	drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
116 117
	if (IS_ERR(drm_dev))
		return PTR_ERR(drm_dev);
M
Mark Yao 已提交
118

119 120 121 122 123
	dev_set_drvdata(dev, drm_dev);

	private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
	if (!private) {
		ret = -ENOMEM;
124
		goto err_free;
125 126
	}

M
Mark Yao 已提交
127 128
	drm_dev->dev_private = private;

129
	INIT_LIST_HEAD(&private->psr_list);
130
	mutex_init(&private->psr_list_lock);
131

132 133 134 135
	ret = rockchip_drm_init_iommu(drm_dev);
	if (ret)
		goto err_free;

M
Mark Yao 已提交
136 137 138 139 140 141 142
	drm_mode_config_init(drm_dev);

	rockchip_drm_mode_config_init(drm_dev);

	/* Try to bind all sub drivers. */
	ret = component_bind_all(dev, drm_dev);
	if (ret)
143
		goto err_mode_config_cleanup;
M
Mark Yao 已提交
144

145 146 147 148 149
	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
	if (ret)
		goto err_unbind_all;

	drm_mode_config_reset(drm_dev);
M
Mark Yao 已提交
150 151 152 153 154 155 156 157 158

	/*
	 * enable drm irq mode.
	 * - with irq_enabled = true, we can use the vblank feature.
	 */
	drm_dev->irq_enabled = true;

	ret = rockchip_drm_fbdev_init(drm_dev);
	if (ret)
159 160 161 162
		goto err_unbind_all;

	/* init kms poll for handling hpd */
	drm_kms_helper_poll_init(drm_dev);
M
Mark Yao 已提交
163

164 165
	ret = drm_dev_register(drm_dev, 0);
	if (ret)
166
		goto err_kms_helper_poll_fini;
167

M
Mark Yao 已提交
168 169 170
	return 0;
err_kms_helper_poll_fini:
	drm_kms_helper_poll_fini(drm_dev);
171
	rockchip_drm_fbdev_fini(drm_dev);
172
err_unbind_all:
M
Mark Yao 已提交
173
	component_unbind_all(dev, drm_dev);
174
err_mode_config_cleanup:
M
Mark Yao 已提交
175
	drm_mode_config_cleanup(drm_dev);
176
	rockchip_iommu_cleanup(drm_dev);
177
err_free:
178 179
	drm_dev->dev_private = NULL;
	dev_set_drvdata(dev, NULL);
180
	drm_dev_put(drm_dev);
M
Mark Yao 已提交
181 182 183
	return ret;
}

184
static void rockchip_drm_unbind(struct device *dev)
M
Mark Yao 已提交
185
{
186
	struct drm_device *drm_dev = dev_get_drvdata(dev);
M
Mark Yao 已提交
187

188 189
	drm_dev_unregister(drm_dev);

M
Mark Yao 已提交
190 191
	rockchip_drm_fbdev_fini(drm_dev);
	drm_kms_helper_poll_fini(drm_dev);
192

193
	drm_atomic_helper_shutdown(drm_dev);
M
Mark Yao 已提交
194 195
	component_unbind_all(dev, drm_dev);
	drm_mode_config_cleanup(drm_dev);
196 197
	rockchip_iommu_cleanup(drm_dev);

M
Mark Yao 已提交
198
	drm_dev->dev_private = NULL;
199
	dev_set_drvdata(dev, NULL);
200
	drm_dev_put(drm_dev);
M
Mark Yao 已提交
201 202 203 204 205 206 207 208 209 210 211 212 213 214
}

static const struct file_operations rockchip_drm_driver_fops = {
	.owner = THIS_MODULE,
	.open = drm_open,
	.mmap = rockchip_gem_mmap,
	.poll = drm_poll,
	.read = drm_read,
	.unlocked_ioctl = drm_ioctl,
	.compat_ioctl = drm_compat_ioctl,
	.release = drm_release,
};

static struct drm_driver rockchip_drm_driver = {
215 216
	.driver_features	= DRIVER_MODESET | DRIVER_GEM |
				  DRIVER_PRIME | DRIVER_ATOMIC,
217
	.lastclose		= drm_fb_helper_lastclose,
D
Daniel Vetter 已提交
218
	.gem_vm_ops		= &drm_gem_cma_vm_ops,
219
	.gem_free_object_unlocked = rockchip_gem_free_object,
M
Mark Yao 已提交
220 221 222 223 224 225
	.dumb_create		= rockchip_gem_dumb_create,
	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
	.gem_prime_import	= drm_gem_prime_import,
	.gem_prime_export	= drm_gem_prime_export,
	.gem_prime_get_sg_table	= rockchip_gem_prime_get_sg_table,
226
	.gem_prime_import_sg_table	= rockchip_gem_prime_import_sg_table,
M
Mark Yao 已提交
227 228 229 230 231 232 233 234 235 236 237 238
	.gem_prime_vmap		= rockchip_gem_prime_vmap,
	.gem_prime_vunmap	= rockchip_gem_prime_vunmap,
	.gem_prime_mmap		= rockchip_gem_mmap_buf,
	.fops			= &rockchip_drm_driver_fops,
	.name	= DRIVER_NAME,
	.desc	= DRIVER_DESC,
	.date	= DRIVER_DATE,
	.major	= DRIVER_MAJOR,
	.minor	= DRIVER_MINOR,
};

#ifdef CONFIG_PM_SLEEP
239 240 241 242
static int rockchip_drm_sys_suspend(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);

243
	return drm_mode_config_helper_suspend(drm);
M
Mark Yao 已提交
244 245 246 247 248
}

static int rockchip_drm_sys_resume(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);
249

250
	return drm_mode_config_helper_resume(drm);
M
Mark Yao 已提交
251 252 253 254 255 256 257 258
}
#endif

static const struct dev_pm_ops rockchip_drm_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
				rockchip_drm_sys_resume)
};

259 260 261
#define MAX_ROCKCHIP_SUB_DRIVERS 16
static struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS];
static int num_rockchip_sub_drivers;
M
Mark Yao 已提交
262

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
/*
 * Check if a vop endpoint is leading to a rockchip subdriver or bridge.
 * Should be called from the component bind stage of the drivers
 * to ensure that all subdrivers are probed.
 *
 * @ep: endpoint of a rockchip vop
 *
 * returns true if subdriver, false if external bridge and -ENODEV
 * if remote port does not contain a device.
 */
int rockchip_drm_endpoint_is_subdriver(struct device_node *ep)
{
	struct device_node *node = of_graph_get_remote_port_parent(ep);
	struct platform_device *pdev;
	struct device_driver *drv;
	int i;

	if (!node)
		return -ENODEV;

	/* status disabled will prevent creation of platform-devices */
	pdev = of_find_device_by_node(node);
	of_node_put(node);
	if (!pdev)
		return -ENODEV;

	/*
	 * All rockchip subdrivers have probed at this point, so
	 * any device not having a driver now is an external bridge.
	 */
	drv = pdev->dev.driver;
	if (!drv) {
		platform_device_put(pdev);
		return false;
	}

	for (i = 0; i < num_rockchip_sub_drivers; i++) {
		if (rockchip_sub_drivers[i] == to_platform_driver(drv)) {
			platform_device_put(pdev);
			return true;
		}
	}

	platform_device_put(pdev);
	return false;
}

310 311 312
static int compare_dev(struct device *dev, void *data)
{
	return dev == (struct device *)data;
M
Mark Yao 已提交
313 314
}

315 316 317 318 319 320 321 322
static void rockchip_drm_match_remove(struct device *dev)
{
	struct device_link *link;

	list_for_each_entry(link, &dev->links.consumers, s_node)
		device_link_del(link);
}

323
static struct component_match *rockchip_drm_match_add(struct device *dev)
324
{
325 326
	struct component_match *match = NULL;
	int i;
327

328 329 330 331 332 333 334 335 336
	for (i = 0; i < num_rockchip_sub_drivers; i++) {
		struct platform_driver *drv = rockchip_sub_drivers[i];
		struct device *p = NULL, *d;

		do {
			d = bus_find_device(&platform_bus_type, p, &drv->driver,
					    (void *)platform_bus_type.match);
			put_device(p);
			p = d;
337

338 339
			if (!d)
				break;
340 341

			device_link_add(dev, d, DL_FLAG_STATELESS);
342 343
			component_match_add(dev, &match, compare_dev, d);
		} while (true);
344
	}
345

346 347 348
	if (IS_ERR(match))
		rockchip_drm_match_remove(dev);

349
	return match ?: ERR_PTR(-ENODEV);
350 351
}

M
Mark Yao 已提交
352 353 354 355 356
static const struct component_master_ops rockchip_drm_ops = {
	.bind = rockchip_drm_bind,
	.unbind = rockchip_drm_unbind,
};

357
static int rockchip_drm_platform_of_probe(struct device *dev)
M
Mark Yao 已提交
358
{
359 360
	struct device_node *np = dev->of_node;
	struct device_node *port;
361
	bool found = false;
362
	int i;
M
Mark Yao 已提交
363

364
	if (!np)
M
Mark Yao 已提交
365
		return -ENODEV;
366

367
	for (i = 0;; i++) {
368 369
		struct device_node *iommu;

370 371 372 373 374 375 376 377
		port = of_parse_phandle(np, "ports", i);
		if (!port)
			break;

		if (!of_device_is_available(port->parent)) {
			of_node_put(port);
			continue;
		}
M
Mark Yao 已提交
378

379 380
		iommu = of_parse_phandle(port->parent, "iommus", 0);
		if (!iommu || !of_device_is_available(iommu->parent)) {
381 382 383
			DRM_DEV_DEBUG(dev,
				      "no iommu attached for %pOF, using non-iommu buffers\n",
				      port->parent);
384 385 386 387 388 389 390
			/*
			 * if there is a crtc not support iommu, force set all
			 * crtc use non-iommu buffer.
			 */
			is_support_iommu = false;
		}

391 392
		found = true;

393
		of_node_put(iommu);
394 395 396 397
		of_node_put(port);
	}

	if (i == 0) {
398
		DRM_DEV_ERROR(dev, "missing 'ports' property\n");
399 400 401
		return -ENODEV;
	}

402
	if (!found) {
403 404
		DRM_DEV_ERROR(dev,
			      "No available vop found for display-subsystem.\n");
405 406 407
		return -ENODEV;
	}

408 409
	return 0;
}
410

411 412 413 414 415 416 417 418 419 420 421 422 423
static int rockchip_drm_platform_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct component_match *match = NULL;
	int ret;

	ret = rockchip_drm_platform_of_probe(dev);
	if (ret)
		return ret;

	match = rockchip_drm_match_add(dev);
	if (IS_ERR(match))
		return PTR_ERR(match);
424

425 426 427 428 429 430 431
	ret = component_master_add_with_match(dev, &rockchip_drm_ops, match);
	if (ret < 0) {
		rockchip_drm_match_remove(dev);
		return ret;
	}

	return 0;
M
Mark Yao 已提交
432 433 434 435 436 437
}

static int rockchip_drm_platform_remove(struct platform_device *pdev)
{
	component_master_del(&pdev->dev, &rockchip_drm_ops);

438 439
	rockchip_drm_match_remove(&pdev->dev);

M
Mark Yao 已提交
440 441 442
	return 0;
}

443 444 445 446 447 448 449 450
static void rockchip_drm_platform_shutdown(struct platform_device *pdev)
{
	struct drm_device *drm = platform_get_drvdata(pdev);

	if (drm)
		drm_atomic_helper_shutdown(drm);
}

M
Mark Yao 已提交
451 452 453 454 455 456 457 458 459
static const struct of_device_id rockchip_drm_dt_ids[] = {
	{ .compatible = "rockchip,display-subsystem", },
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);

static struct platform_driver rockchip_drm_platform_driver = {
	.probe = rockchip_drm_platform_probe,
	.remove = rockchip_drm_platform_remove,
460
	.shutdown = rockchip_drm_platform_shutdown,
M
Mark Yao 已提交
461 462 463 464 465 466 467
	.driver = {
		.name = "rockchip-drm",
		.of_match_table = rockchip_drm_dt_ids,
		.pm = &rockchip_drm_pm_ops,
	},
};

468 469 470 471 472 473 474 475 476 477 478 479
#define ADD_ROCKCHIP_SUB_DRIVER(drv, cond) { \
	if (IS_ENABLED(cond) && \
	    !WARN_ON(num_rockchip_sub_drivers >= MAX_ROCKCHIP_SUB_DRIVERS)) \
		rockchip_sub_drivers[num_rockchip_sub_drivers++] = &drv; \
}

static int __init rockchip_drm_init(void)
{
	int ret;

	num_rockchip_sub_drivers = 0;
	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP);
480 481
	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
				CONFIG_ROCKCHIP_LVDS);
482 483 484 485 486
	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
				CONFIG_ROCKCHIP_ANALOGIX_DP);
	ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
	ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
				CONFIG_ROCKCHIP_DW_HDMI);
487
	ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
488 489
				CONFIG_ROCKCHIP_DW_MIPI_DSI);
	ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
490 491
	ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver,
				CONFIG_ROCKCHIP_RK3066_HDMI);
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519

	ret = platform_register_drivers(rockchip_sub_drivers,
					num_rockchip_sub_drivers);
	if (ret)
		return ret;

	ret = platform_driver_register(&rockchip_drm_platform_driver);
	if (ret)
		goto err_unreg_drivers;

	return 0;

err_unreg_drivers:
	platform_unregister_drivers(rockchip_sub_drivers,
				    num_rockchip_sub_drivers);
	return ret;
}

static void __exit rockchip_drm_fini(void)
{
	platform_driver_unregister(&rockchip_drm_platform_driver);

	platform_unregister_drivers(rockchip_sub_drivers,
				    num_rockchip_sub_drivers);
}

module_init(rockchip_drm_init);
module_exit(rockchip_drm_fini);
M
Mark Yao 已提交
520 521 522 523

MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
MODULE_LICENSE("GPL v2");