| 
									
										
										
										
											2014-07-31 09:39:11 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (C) STMicroelectronics SA 2014 | 
					
						
							|  |  |  |  * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. | 
					
						
							|  |  |  |  * License terms:  GNU General Public License (GPL), version 2 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <drm/drmP.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/component.h>
 | 
					
						
							|  |  |  | #include <linux/debugfs.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/of_platform.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <drm/drm_crtc_helper.h>
 | 
					
						
							|  |  |  | #include <drm/drm_gem_cma_helper.h>
 | 
					
						
							|  |  |  | #include <drm/drm_fb_cma_helper.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "sti_drm_drv.h"
 | 
					
						
							|  |  |  | #include "sti_drm_crtc.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DRIVER_NAME	"sti"
 | 
					
						
							|  |  |  | #define DRIVER_DESC	"STMicroelectronics SoC DRM"
 | 
					
						
							|  |  |  | #define DRIVER_DATE	"20140601"
 | 
					
						
							|  |  |  | #define DRIVER_MAJOR	1
 | 
					
						
							|  |  |  | #define DRIVER_MINOR	0
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define STI_MAX_FB_HEIGHT	4096
 | 
					
						
							|  |  |  | #define STI_MAX_FB_WIDTH	4096
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct drm_mode_config_funcs sti_drm_mode_config_funcs = { | 
					
						
							|  |  |  | 	.fb_create = drm_fb_cma_create, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void sti_drm_mode_config_init(struct drm_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	dev->mode_config.min_width = 0; | 
					
						
							|  |  |  | 	dev->mode_config.min_height = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * set max width and height as default value. | 
					
						
							|  |  |  | 	 * this value would be used to check framebuffer size limitation | 
					
						
							|  |  |  | 	 * at drm_mode_addfb(). | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	dev->mode_config.max_width = STI_MAX_FB_HEIGHT; | 
					
						
							|  |  |  | 	dev->mode_config.max_height = STI_MAX_FB_WIDTH; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->mode_config.funcs = &sti_drm_mode_config_funcs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sti_drm_load(struct drm_device *dev, unsigned long flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct sti_drm_private *private; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	private = kzalloc(sizeof(struct sti_drm_private), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!private) { | 
					
						
							|  |  |  | 		DRM_ERROR("Failed to allocate private\n"); | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dev->dev_private = (void *)private; | 
					
						
							|  |  |  | 	private->drm_dev = dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	drm_mode_config_init(dev); | 
					
						
							|  |  |  | 	drm_kms_helper_poll_init(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sti_drm_mode_config_init(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = component_bind_all(dev->dev, dev); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	drm_helper_disable_unused_functions(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_DRM_STI_FBDEV
 | 
					
						
							|  |  |  | 	drm_fbdev_cma_init(dev, 32, | 
					
						
							|  |  |  | 		   dev->mode_config.num_crtc, | 
					
						
							|  |  |  | 		   dev->mode_config.num_connector); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct file_operations sti_drm_driver_fops = { | 
					
						
							|  |  |  | 	.owner = THIS_MODULE, | 
					
						
							|  |  |  | 	.open = drm_open, | 
					
						
							|  |  |  | 	.mmap = drm_gem_cma_mmap, | 
					
						
							|  |  |  | 	.poll = drm_poll, | 
					
						
							|  |  |  | 	.read = drm_read, | 
					
						
							|  |  |  | 	.unlocked_ioctl = drm_ioctl, | 
					
						
							|  |  |  | #ifdef CONFIG_COMPAT
 | 
					
						
							|  |  |  | 	.compat_ioctl = drm_compat_ioctl, | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	.release = drm_release, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct dma_buf *sti_drm_gem_prime_export(struct drm_device *dev, | 
					
						
							|  |  |  | 						struct drm_gem_object *obj, | 
					
						
							|  |  |  | 						int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* we want to be able to write in mmapped buffer */ | 
					
						
							|  |  |  | 	flags |= O_RDWR; | 
					
						
							|  |  |  | 	return drm_gem_prime_export(dev, obj, flags); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct drm_driver sti_drm_driver = { | 
					
						
							|  |  |  | 	.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | | 
					
						
							|  |  |  | 	    DRIVER_GEM | DRIVER_PRIME, | 
					
						
							|  |  |  | 	.load = sti_drm_load, | 
					
						
							|  |  |  | 	.gem_free_object = drm_gem_cma_free_object, | 
					
						
							|  |  |  | 	.gem_vm_ops = &drm_gem_cma_vm_ops, | 
					
						
							|  |  |  | 	.dumb_create = drm_gem_cma_dumb_create, | 
					
						
							|  |  |  | 	.dumb_map_offset = drm_gem_cma_dumb_map_offset, | 
					
						
							|  |  |  | 	.dumb_destroy = drm_gem_dumb_destroy, | 
					
						
							|  |  |  | 	.fops = &sti_drm_driver_fops, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.get_vblank_counter = drm_vblank_count, | 
					
						
							|  |  |  | 	.enable_vblank = sti_drm_crtc_enable_vblank, | 
					
						
							|  |  |  | 	.disable_vblank = sti_drm_crtc_disable_vblank, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.prime_handle_to_fd = drm_gem_prime_handle_to_fd, | 
					
						
							|  |  |  | 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle, | 
					
						
							|  |  |  | 	.gem_prime_export = sti_drm_gem_prime_export, | 
					
						
							|  |  |  | 	.gem_prime_import = drm_gem_prime_import, | 
					
						
							|  |  |  | 	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | 
					
						
							|  |  |  | 	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | 
					
						
							|  |  |  | 	.gem_prime_vmap = drm_gem_cma_prime_vmap, | 
					
						
							|  |  |  | 	.gem_prime_vunmap = drm_gem_cma_prime_vunmap, | 
					
						
							|  |  |  | 	.gem_prime_mmap = drm_gem_cma_prime_mmap, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.name = DRIVER_NAME, | 
					
						
							|  |  |  | 	.desc = DRIVER_DESC, | 
					
						
							|  |  |  | 	.date = DRIVER_DATE, | 
					
						
							|  |  |  | 	.major = DRIVER_MAJOR, | 
					
						
							|  |  |  | 	.minor = DRIVER_MINOR, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int compare_of(struct device *dev, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return dev->of_node == data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sti_drm_bind(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return drm_platform_init(&sti_drm_driver, to_platform_device(dev)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void sti_drm_unbind(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	drm_put_dev(dev_get_drvdata(dev)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct component_master_ops sti_drm_ops = { | 
					
						
							|  |  |  | 	.bind = sti_drm_bind, | 
					
						
							|  |  |  | 	.unbind = sti_drm_unbind, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sti_drm_master_probe(struct platform_device *pdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device *dev = &pdev->dev; | 
					
						
							|  |  |  | 	struct device_node *node = dev->parent->of_node; | 
					
						
							|  |  |  | 	struct device_node *child_np; | 
					
						
							|  |  |  | 	struct component_match *match = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	child_np = of_get_next_available_child(node, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (child_np) { | 
					
						
							|  |  |  | 		component_match_add(dev, &match, compare_of, child_np); | 
					
						
							|  |  |  | 		of_node_put(child_np); | 
					
						
							|  |  |  | 		child_np = of_get_next_available_child(node, child_np); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return component_master_add_with_match(dev, &sti_drm_ops, match); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sti_drm_master_remove(struct platform_device *pdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	component_master_del(&pdev->dev, &sti_drm_ops); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct platform_driver sti_drm_master_driver = { | 
					
						
							|  |  |  | 	.probe = sti_drm_master_probe, | 
					
						
							|  |  |  | 	.remove = sti_drm_master_remove, | 
					
						
							|  |  |  | 	.driver = { | 
					
						
							|  |  |  | 		.owner = THIS_MODULE, | 
					
						
							|  |  |  | 		.name = DRIVER_NAME "__master", | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sti_drm_platform_probe(struct platform_device *pdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device *dev = &pdev->dev; | 
					
						
							|  |  |  | 	struct device_node *node = dev->of_node; | 
					
						
							|  |  |  | 	struct platform_device *master; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	of_platform_populate(node, NULL, NULL, dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	platform_driver_register(&sti_drm_master_driver); | 
					
						
							|  |  |  | 	master = platform_device_register_resndata(dev, | 
					
						
							|  |  |  | 			DRIVER_NAME "__master", -1, | 
					
						
							|  |  |  | 			NULL, 0, NULL, 0); | 
					
						
							| 
									
										
										
										
											2014-08-26 12:24:24 +02:00
										 |  |  | 	if (IS_ERR(master)) | 
					
						
							|  |  |  |                return PTR_ERR(master); | 
					
						
							| 
									
										
										
										
											2014-07-31 09:39:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	platform_set_drvdata(pdev, master); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int sti_drm_platform_remove(struct platform_device *pdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct platform_device *master = platform_get_drvdata(pdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	of_platform_depopulate(&pdev->dev); | 
					
						
							|  |  |  | 	platform_device_unregister(master); | 
					
						
							|  |  |  | 	platform_driver_unregister(&sti_drm_master_driver); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct of_device_id sti_drm_dt_ids[] = { | 
					
						
							|  |  |  | 	{ .compatible = "st,sti-display-subsystem", }, | 
					
						
							|  |  |  | 	{ /* end node */ }, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | MODULE_DEVICE_TABLE(of, sti_drm_dt_ids); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct platform_driver sti_drm_platform_driver = { | 
					
						
							|  |  |  | 	.probe = sti_drm_platform_probe, | 
					
						
							|  |  |  | 	.remove = sti_drm_platform_remove, | 
					
						
							|  |  |  | 	.driver = { | 
					
						
							|  |  |  | 		.owner = THIS_MODULE, | 
					
						
							|  |  |  | 		.name = DRIVER_NAME, | 
					
						
							|  |  |  | 		.of_match_table = sti_drm_dt_ids, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_platform_driver(sti_drm_platform_driver); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); |