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

#include <linux/dma-mapping.h>
10
#include <linux/dma-iommu.h>
M
Mark Yao 已提交
11
#include <linux/pm_runtime.h>
12
#include <linux/module.h>
M
Mark Yao 已提交
13
#include <linux/of_graph.h>
14
#include <linux/of_platform.h>
M
Mark Yao 已提交
15
#include <linux/component.h>
16
#include <linux/console.h>
17
#include <linux/iommu.h>
M
Mark Yao 已提交
18

19
#include <drm/drm_aperture.h>
S
Sam Ravnborg 已提交
20 21 22 23 24 25 26
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>

M
Mark Yao 已提交
27 28 29 30 31 32 33 34 35 36 37
#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

38
static bool is_support_iommu = true;
39
static const struct drm_driver rockchip_drm_driver;
40

M
Mark Yao 已提交
41 42 43 44 45 46 47 48
/*
 * 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)
{
49
	struct rockchip_drm_private *private = drm_dev->dev_private;
M
Mark Yao 已提交
50 51
	int ret;

52 53 54
	if (!is_support_iommu)
		return 0;

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

61
	return 0;
M
Mark Yao 已提交
62 63 64 65 66
}

void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
				    struct device *dev)
{
67 68 69
	struct rockchip_drm_private *private = drm_dev->dev_private;
	struct iommu_domain *domain = private->domain;

70 71 72
	if (!is_support_iommu)
		return;

73
	iommu_detach_device(domain, dev);
M
Mark Yao 已提交
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 107 108 109
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 已提交
110 111
}

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

118 119 120 121 122 123 124 125 126
	/* Remove existing drivers that may own the framebuffer memory. */
	ret = drm_aperture_remove_framebuffers(false, "rockchip-drm-fb");
	if (ret) {
		DRM_DEV_ERROR(dev,
			      "Failed to remove existing framebuffers - %d.\n",
			      ret);
		return ret;
	}

127
	drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
128 129
	if (IS_ERR(drm_dev))
		return PTR_ERR(drm_dev);
M
Mark Yao 已提交
130

131 132 133 134 135
	dev_set_drvdata(dev, drm_dev);

	private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
	if (!private) {
		ret = -ENOMEM;
136
		goto err_free;
137 138
	}

M
Mark Yao 已提交
139 140
	drm_dev->dev_private = private;

141
	INIT_LIST_HEAD(&private->psr_list);
142
	mutex_init(&private->psr_list_lock);
143

144 145 146 147
	ret = rockchip_drm_init_iommu(drm_dev);
	if (ret)
		goto err_free;

148 149 150
	ret = drmm_mode_config_init(drm_dev);
	if (ret)
		goto err_iommu_cleanup;
M
Mark Yao 已提交
151 152 153 154 155 156

	rockchip_drm_mode_config_init(drm_dev);

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

159 160 161 162 163
	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 已提交
164 165 166

	ret = rockchip_drm_fbdev_init(drm_dev);
	if (ret)
167 168 169 170
		goto err_unbind_all;

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

172 173
	ret = drm_dev_register(drm_dev, 0);
	if (ret)
174
		goto err_kms_helper_poll_fini;
175

M
Mark Yao 已提交
176 177 178
	return 0;
err_kms_helper_poll_fini:
	drm_kms_helper_poll_fini(drm_dev);
179
	rockchip_drm_fbdev_fini(drm_dev);
180
err_unbind_all:
M
Mark Yao 已提交
181
	component_unbind_all(dev, drm_dev);
182
err_iommu_cleanup:
183
	rockchip_iommu_cleanup(drm_dev);
184
err_free:
185
	drm_dev_put(drm_dev);
M
Mark Yao 已提交
186 187 188
	return ret;
}

189
static void rockchip_drm_unbind(struct device *dev)
M
Mark Yao 已提交
190
{
191
	struct drm_device *drm_dev = dev_get_drvdata(dev);
M
Mark Yao 已提交
192

193 194
	drm_dev_unregister(drm_dev);

M
Mark Yao 已提交
195 196
	rockchip_drm_fbdev_fini(drm_dev);
	drm_kms_helper_poll_fini(drm_dev);
197

198
	drm_atomic_helper_shutdown(drm_dev);
M
Mark Yao 已提交
199
	component_unbind_all(dev, drm_dev);
200 201
	rockchip_iommu_cleanup(drm_dev);

202
	drm_dev_put(drm_dev);
M
Mark Yao 已提交
203 204 205 206 207 208 209 210 211 212 213 214 215
}

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,
};

216
static const struct drm_driver rockchip_drm_driver = {
217
	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
218
	.lastclose		= drm_fb_helper_lastclose,
M
Mark Yao 已提交
219 220 221
	.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,
222
	.gem_prime_import_sg_table	= rockchip_gem_prime_import_sg_table,
M
Mark Yao 已提交
223 224 225 226 227 228 229 230 231 232
	.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
233 234 235 236
static int rockchip_drm_sys_suspend(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);

237
	return drm_mode_config_helper_suspend(drm);
M
Mark Yao 已提交
238 239 240 241 242
}

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

244
	return drm_mode_config_helper_resume(drm);
M
Mark Yao 已提交
245 246 247 248 249 250 251 252
}
#endif

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

253 254 255
#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 已提交
256

257 258 259 260 261 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
/*
 * 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;
}

304 305 306
static int compare_dev(struct device *dev, void *data)
{
	return dev == (struct device *)data;
M
Mark Yao 已提交
307 308
}

309 310 311 312 313 314 315 316
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);
}

317
static struct component_match *rockchip_drm_match_add(struct device *dev)
318
{
319 320
	struct component_match *match = NULL;
	int i;
321

322 323 324 325 326
	for (i = 0; i < num_rockchip_sub_drivers; i++) {
		struct platform_driver *drv = rockchip_sub_drivers[i];
		struct device *p = NULL, *d;

		do {
327
			d = platform_find_device_by_driver(p, &drv->driver);
328 329
			put_device(p);
			p = d;
330

331 332
			if (!d)
				break;
333 334

			device_link_add(dev, d, DL_FLAG_STATELESS);
335 336
			component_match_add(dev, &match, compare_dev, d);
		} while (true);
337
	}
338

339 340 341
	if (IS_ERR(match))
		rockchip_drm_match_remove(dev);

342
	return match ?: ERR_PTR(-ENODEV);
343 344
}

M
Mark Yao 已提交
345 346 347 348 349
static const struct component_master_ops rockchip_drm_ops = {
	.bind = rockchip_drm_bind,
	.unbind = rockchip_drm_unbind,
};

350
static int rockchip_drm_platform_of_probe(struct device *dev)
M
Mark Yao 已提交
351
{
352 353
	struct device_node *np = dev->of_node;
	struct device_node *port;
354
	bool found = false;
355
	int i;
M
Mark Yao 已提交
356

357
	if (!np)
M
Mark Yao 已提交
358
		return -ENODEV;
359

360
	for (i = 0;; i++) {
361 362
		struct device_node *iommu;

363 364 365 366 367 368 369 370
		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 已提交
371

372 373
		iommu = of_parse_phandle(port->parent, "iommus", 0);
		if (!iommu || !of_device_is_available(iommu->parent)) {
374 375 376
			DRM_DEV_DEBUG(dev,
				      "no iommu attached for %pOF, using non-iommu buffers\n",
				      port->parent);
377 378 379 380 381 382 383
			/*
			 * if there is a crtc not support iommu, force set all
			 * crtc use non-iommu buffer.
			 */
			is_support_iommu = false;
		}

384 385
		found = true;

386
		of_node_put(iommu);
387 388 389 390
		of_node_put(port);
	}

	if (i == 0) {
391
		DRM_DEV_ERROR(dev, "missing 'ports' property\n");
392 393 394
		return -ENODEV;
	}

395
	if (!found) {
396 397
		DRM_DEV_ERROR(dev,
			      "No available vop found for display-subsystem.\n");
398 399 400
		return -ENODEV;
	}

401 402
	return 0;
}
403

404 405 406 407 408 409 410 411 412 413 414 415 416
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);
417

418 419 420 421 422 423 424
	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 已提交
425 426 427 428 429 430
}

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

431 432
	rockchip_drm_match_remove(&pdev->dev);

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

436 437 438 439 440 441 442 443
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 已提交
444 445 446 447 448 449 450 451 452
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,
453
	.shutdown = rockchip_drm_platform_shutdown,
M
Mark Yao 已提交
454 455 456 457 458 459 460
	.driver = {
		.name = "rockchip-drm",
		.of_match_table = rockchip_drm_dt_ids,
		.pm = &rockchip_drm_pm_ops,
	},
};

461 462 463 464 465 466 467 468 469 470 471 472
#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);
473 474
	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
				CONFIG_ROCKCHIP_LVDS);
475 476 477 478 479
	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);
480
	ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
481 482
				CONFIG_ROCKCHIP_DW_MIPI_DSI);
	ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
483 484
	ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver,
				CONFIG_ROCKCHIP_RK3066_HDMI);
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512

	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 已提交
513 514 515 516

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