| 
									
										
										
										
											2021-12-07 22:27:13 +08:00
										 |  |  | // SPDX-License-Identifier: GPL-2.0
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (C) 2020 Unisoc Inc. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/component.h>
 | 
					
						
							|  |  |  | #include <linux/dma-mapping.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/mutex.h>
 | 
					
						
							|  |  |  | #include <linux/of_graph.h>
 | 
					
						
							|  |  |  | #include <linux/of_platform.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <drm/drm_atomic_helper.h>
 | 
					
						
							|  |  |  | #include <drm/drm_crtc_helper.h>
 | 
					
						
							|  |  |  | #include <drm/drm_drv.h>
 | 
					
						
							| 
									
										
										
										
											2022-08-02 02:04:03 +02:00
										 |  |  | #include <drm/drm_gem_dma_helper.h>
 | 
					
						
							| 
									
										
										
										
											2021-12-07 22:27:13 +08:00
										 |  |  | #include <drm/drm_gem_framebuffer_helper.h>
 | 
					
						
							|  |  |  | #include <drm/drm_of.h>
 | 
					
						
							|  |  |  | #include <drm/drm_probe_helper.h>
 | 
					
						
							|  |  |  | #include <drm/drm_vblank.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "sprd_drm.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DRIVER_NAME	"sprd"
 | 
					
						
							|  |  |  | #define DRIVER_DESC	"Spreadtrum SoCs' DRM Driver"
 | 
					
						
							|  |  |  | #define DRIVER_DATE	"20200201"
 | 
					
						
							|  |  |  | #define DRIVER_MAJOR	1
 | 
					
						
							|  |  |  | #define DRIVER_MINOR	0
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = { | 
					
						
							|  |  |  | 	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = { | 
					
						
							|  |  |  | 	.fb_create = drm_gem_fb_create, | 
					
						
							|  |  |  | 	.atomic_check = drm_atomic_helper_check, | 
					
						
							|  |  |  | 	.atomic_commit = drm_atomic_helper_commit, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void sprd_drm_mode_config_init(struct drm_device *drm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	drm->mode_config.min_width = 0; | 
					
						
							|  |  |  | 	drm->mode_config.min_height = 0; | 
					
						
							|  |  |  | 	drm->mode_config.max_width = 8192; | 
					
						
							|  |  |  | 	drm->mode_config.max_height = 8192; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	drm->mode_config.funcs = &sprd_drm_mode_config_funcs; | 
					
						
							|  |  |  | 	drm->mode_config.helper_private = &sprd_drm_mode_config_helper; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 02:04:03 +02:00
										 |  |  | DEFINE_DRM_GEM_DMA_FOPS(sprd_drm_fops); | 
					
						
							| 
									
										
										
										
											2021-12-07 22:27:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | static struct drm_driver sprd_drm_drv = { | 
					
						
							|  |  |  | 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, | 
					
						
							|  |  |  | 	.fops			= &sprd_drm_fops, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* GEM Operations */ | 
					
						
							| 
									
										
										
										
											2022-08-02 02:04:03 +02:00
										 |  |  | 	DRM_GEM_DMA_DRIVER_OPS, | 
					
						
							| 
									
										
										
										
											2021-12-07 22:27:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	.name			= DRIVER_NAME, | 
					
						
							|  |  |  | 	.desc			= DRIVER_DESC, | 
					
						
							|  |  |  | 	.date			= DRIVER_DATE, | 
					
						
							|  |  |  | 	.major			= DRIVER_MAJOR, | 
					
						
							|  |  |  | 	.minor			= DRIVER_MINOR, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sprd_drm_bind(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct platform_device *pdev = to_platform_device(dev); | 
					
						
							|  |  |  | 	struct drm_device *drm; | 
					
						
							|  |  |  | 	struct sprd_drm *sprd; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sprd = devm_drm_dev_alloc(dev, &sprd_drm_drv, struct sprd_drm, drm); | 
					
						
							|  |  |  | 	if (IS_ERR(sprd)) | 
					
						
							|  |  |  | 		return PTR_ERR(sprd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	drm = &sprd->drm; | 
					
						
							|  |  |  | 	platform_set_drvdata(pdev, drm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = drmm_mode_config_init(drm); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sprd_drm_mode_config_init(drm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* bind and init sub drivers */ | 
					
						
							|  |  |  | 	ret = component_bind_all(drm->dev, drm); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		drm_err(drm, "failed to bind all component.\n"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* vblank init */ | 
					
						
							|  |  |  | 	ret = drm_vblank_init(drm, drm->mode_config.num_crtc); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		drm_err(drm, "failed to initialize vblank.\n"); | 
					
						
							|  |  |  | 		goto err_unbind_all; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* reset all the states of crtc/plane/encoder/connector */ | 
					
						
							|  |  |  | 	drm_mode_config_reset(drm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* init kms poll for handling hpd */ | 
					
						
							|  |  |  | 	drm_kms_helper_poll_init(drm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = drm_dev_register(drm, 0); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		goto err_kms_helper_poll_fini; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | err_kms_helper_poll_fini: | 
					
						
							|  |  |  | 	drm_kms_helper_poll_fini(drm); | 
					
						
							|  |  |  | err_unbind_all: | 
					
						
							|  |  |  | 	component_unbind_all(drm->dev, drm); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void sprd_drm_unbind(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_device *drm = dev_get_drvdata(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	drm_dev_unregister(drm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	drm_kms_helper_poll_fini(drm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	component_unbind_all(drm->dev, drm); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct component_master_ops drm_component_ops = { | 
					
						
							|  |  |  | 	.bind = sprd_drm_bind, | 
					
						
							|  |  |  | 	.unbind = sprd_drm_unbind, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sprd_drm_probe(struct platform_device *pdev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-02-14 14:08:06 +08:00
										 |  |  | 	return drm_of_component_probe(&pdev->dev, component_compare_of, &drm_component_ops); | 
					
						
							| 
									
										
										
										
											2021-12-07 22:27:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sprd_drm_remove(struct platform_device *pdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	component_master_del(&pdev->dev, &drm_component_ops); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void sprd_drm_shutdown(struct platform_device *pdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_device *drm = platform_get_drvdata(pdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!drm) { | 
					
						
							| 
									
										
										
										
											2022-01-16 23:55:47 +08:00
										 |  |  | 		dev_warn(&pdev->dev, "drm device is not available, no shutdown\n"); | 
					
						
							| 
									
										
										
										
											2021-12-07 22:27:13 +08:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	drm_atomic_helper_shutdown(drm); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct of_device_id drm_match_table[] = { | 
					
						
							|  |  |  | 	{ .compatible = "sprd,display-subsystem", }, | 
					
						
							|  |  |  | 	{ /* sentinel */ }, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | MODULE_DEVICE_TABLE(of, drm_match_table); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct platform_driver sprd_drm_driver = { | 
					
						
							|  |  |  | 	.probe = sprd_drm_probe, | 
					
						
							|  |  |  | 	.remove = sprd_drm_remove, | 
					
						
							|  |  |  | 	.shutdown = sprd_drm_shutdown, | 
					
						
							|  |  |  | 	.driver = { | 
					
						
							|  |  |  | 		.name = "sprd-drm-drv", | 
					
						
							|  |  |  | 		.of_match_table = drm_match_table, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct platform_driver *sprd_drm_drivers[]  = { | 
					
						
							|  |  |  | 	&sprd_drm_driver, | 
					
						
							| 
									
										
										
										
											2021-12-07 22:27:15 +08:00
										 |  |  | 	&sprd_dpu_driver, | 
					
						
							| 
									
										
										
										
											2021-12-07 22:27:17 +08:00
										 |  |  | 	&sprd_dsi_driver, | 
					
						
							| 
									
										
										
										
											2021-12-07 22:27:13 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init sprd_drm_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-12-17 01:37:48 +01:00
										 |  |  | 	if (drm_firmware_drivers_only()) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 22:27:13 +08:00
										 |  |  | 	return platform_register_drivers(sprd_drm_drivers, | 
					
						
							|  |  |  | 					ARRAY_SIZE(sprd_drm_drivers)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __exit sprd_drm_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	platform_unregister_drivers(sprd_drm_drivers, | 
					
						
							|  |  |  | 				    ARRAY_SIZE(sprd_drm_drivers)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_init(sprd_drm_init); | 
					
						
							|  |  |  | module_exit(sprd_drm_exit); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR("Leon He <leon.he@unisoc.com>"); | 
					
						
							|  |  |  | MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL v2"); |