18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd. 48c2ecf20Sopenharmony_ci * Authors: 58c2ecf20Sopenharmony_ci * Inki Dae <inki.dae@samsung.com> 68c2ecf20Sopenharmony_ci * Joonyoung Shim <jy0922.shim@samsung.com> 78c2ecf20Sopenharmony_ci * Seung-Woo Kim <sw0312.kim@samsung.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/component.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 148c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_file.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_ioctl.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 258c2ecf20Sopenharmony_ci#include <drm/exynos_drm.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "exynos_drm_drv.h" 288c2ecf20Sopenharmony_ci#include "exynos_drm_fb.h" 298c2ecf20Sopenharmony_ci#include "exynos_drm_fbdev.h" 308c2ecf20Sopenharmony_ci#include "exynos_drm_g2d.h" 318c2ecf20Sopenharmony_ci#include "exynos_drm_gem.h" 328c2ecf20Sopenharmony_ci#include "exynos_drm_ipp.h" 338c2ecf20Sopenharmony_ci#include "exynos_drm_plane.h" 348c2ecf20Sopenharmony_ci#include "exynos_drm_vidi.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define DRIVER_NAME "exynos" 378c2ecf20Sopenharmony_ci#define DRIVER_DESC "Samsung SoC DRM" 388c2ecf20Sopenharmony_ci#define DRIVER_DATE "20180330" 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * Interface history: 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * 1.0 - Original version 448c2ecf20Sopenharmony_ci * 1.1 - Upgrade IPP driver to version 2.0 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci#define DRIVER_MAJOR 1 478c2ecf20Sopenharmony_ci#define DRIVER_MINOR 1 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int exynos_drm_open(struct drm_device *dev, struct drm_file *file) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct drm_exynos_file_private *file_priv; 528c2ecf20Sopenharmony_ci int ret; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); 558c2ecf20Sopenharmony_ci if (!file_priv) 568c2ecf20Sopenharmony_ci return -ENOMEM; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci file->driver_priv = file_priv; 598c2ecf20Sopenharmony_ci ret = g2d_open(dev, file); 608c2ecf20Sopenharmony_ci if (ret) 618c2ecf20Sopenharmony_ci goto err_file_priv_free; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return ret; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cierr_file_priv_free: 668c2ecf20Sopenharmony_ci kfree(file_priv); 678c2ecf20Sopenharmony_ci file->driver_priv = NULL; 688c2ecf20Sopenharmony_ci return ret; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci g2d_close(dev, file); 748c2ecf20Sopenharmony_ci kfree(file->driver_priv); 758c2ecf20Sopenharmony_ci file->driver_priv = NULL; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic const struct vm_operations_struct exynos_drm_gem_vm_ops = { 798c2ecf20Sopenharmony_ci .open = drm_gem_vm_open, 808c2ecf20Sopenharmony_ci .close = drm_gem_vm_close, 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const struct drm_ioctl_desc exynos_ioctls[] = { 848c2ecf20Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl, 858c2ecf20Sopenharmony_ci DRM_RENDER_ALLOW), 868c2ecf20Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP, exynos_drm_gem_map_ioctl, 878c2ecf20Sopenharmony_ci DRM_RENDER_ALLOW), 888c2ecf20Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl, 898c2ecf20Sopenharmony_ci DRM_RENDER_ALLOW), 908c2ecf20Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl, 918c2ecf20Sopenharmony_ci DRM_AUTH), 928c2ecf20Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, exynos_g2d_get_ver_ioctl, 938c2ecf20Sopenharmony_ci DRM_RENDER_ALLOW), 948c2ecf20Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, exynos_g2d_set_cmdlist_ioctl, 958c2ecf20Sopenharmony_ci DRM_RENDER_ALLOW), 968c2ecf20Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl, 978c2ecf20Sopenharmony_ci DRM_RENDER_ALLOW), 988c2ecf20Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_RESOURCES, 998c2ecf20Sopenharmony_ci exynos_drm_ipp_get_res_ioctl, 1008c2ecf20Sopenharmony_ci DRM_RENDER_ALLOW), 1018c2ecf20Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_CAPS, exynos_drm_ipp_get_caps_ioctl, 1028c2ecf20Sopenharmony_ci DRM_RENDER_ALLOW), 1038c2ecf20Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_LIMITS, 1048c2ecf20Sopenharmony_ci exynos_drm_ipp_get_limits_ioctl, 1058c2ecf20Sopenharmony_ci DRM_RENDER_ALLOW), 1068c2ecf20Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_IPP_COMMIT, exynos_drm_ipp_commit_ioctl, 1078c2ecf20Sopenharmony_ci DRM_RENDER_ALLOW), 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic const struct file_operations exynos_drm_driver_fops = { 1118c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1128c2ecf20Sopenharmony_ci .open = drm_open, 1138c2ecf20Sopenharmony_ci .mmap = exynos_drm_gem_mmap, 1148c2ecf20Sopenharmony_ci .poll = drm_poll, 1158c2ecf20Sopenharmony_ci .read = drm_read, 1168c2ecf20Sopenharmony_ci .unlocked_ioctl = drm_ioctl, 1178c2ecf20Sopenharmony_ci .compat_ioctl = drm_compat_ioctl, 1188c2ecf20Sopenharmony_ci .release = drm_release, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic struct drm_driver exynos_drm_driver = { 1228c2ecf20Sopenharmony_ci .driver_features = DRIVER_MODESET | DRIVER_GEM 1238c2ecf20Sopenharmony_ci | DRIVER_ATOMIC | DRIVER_RENDER, 1248c2ecf20Sopenharmony_ci .open = exynos_drm_open, 1258c2ecf20Sopenharmony_ci .lastclose = drm_fb_helper_lastclose, 1268c2ecf20Sopenharmony_ci .postclose = exynos_drm_postclose, 1278c2ecf20Sopenharmony_ci .gem_free_object_unlocked = exynos_drm_gem_free_object, 1288c2ecf20Sopenharmony_ci .gem_vm_ops = &exynos_drm_gem_vm_ops, 1298c2ecf20Sopenharmony_ci .dumb_create = exynos_drm_gem_dumb_create, 1308c2ecf20Sopenharmony_ci .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 1318c2ecf20Sopenharmony_ci .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 1328c2ecf20Sopenharmony_ci .gem_prime_import = exynos_drm_gem_prime_import, 1338c2ecf20Sopenharmony_ci .gem_prime_get_sg_table = exynos_drm_gem_prime_get_sg_table, 1348c2ecf20Sopenharmony_ci .gem_prime_import_sg_table = exynos_drm_gem_prime_import_sg_table, 1358c2ecf20Sopenharmony_ci .gem_prime_vmap = exynos_drm_gem_prime_vmap, 1368c2ecf20Sopenharmony_ci .gem_prime_vunmap = exynos_drm_gem_prime_vunmap, 1378c2ecf20Sopenharmony_ci .gem_prime_mmap = exynos_drm_gem_prime_mmap, 1388c2ecf20Sopenharmony_ci .ioctls = exynos_ioctls, 1398c2ecf20Sopenharmony_ci .num_ioctls = ARRAY_SIZE(exynos_ioctls), 1408c2ecf20Sopenharmony_ci .fops = &exynos_drm_driver_fops, 1418c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 1428c2ecf20Sopenharmony_ci .desc = DRIVER_DESC, 1438c2ecf20Sopenharmony_ci .date = DRIVER_DATE, 1448c2ecf20Sopenharmony_ci .major = DRIVER_MAJOR, 1458c2ecf20Sopenharmony_ci .minor = DRIVER_MINOR, 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int exynos_drm_suspend(struct device *dev) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(dev); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return drm_mode_config_helper_suspend(drm_dev); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic void exynos_drm_resume(struct device *dev) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(dev); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci drm_mode_config_helper_resume(drm_dev); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic const struct dev_pm_ops exynos_drm_pm_ops = { 1638c2ecf20Sopenharmony_ci .prepare = exynos_drm_suspend, 1648c2ecf20Sopenharmony_ci .complete = exynos_drm_resume, 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* forward declaration */ 1688c2ecf20Sopenharmony_cistatic struct platform_driver exynos_drm_platform_driver; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistruct exynos_drm_driver_info { 1718c2ecf20Sopenharmony_ci struct platform_driver *driver; 1728c2ecf20Sopenharmony_ci unsigned int flags; 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci#define DRM_COMPONENT_DRIVER BIT(0) /* supports component framework */ 1768c2ecf20Sopenharmony_ci#define DRM_VIRTUAL_DEVICE BIT(1) /* create virtual platform device */ 1778c2ecf20Sopenharmony_ci#define DRM_FIMC_DEVICE BIT(2) /* devices shared with V4L2 subsystem */ 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#define DRV_PTR(drv, cond) (IS_ENABLED(cond) ? &drv : NULL) 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * Connector drivers should not be placed before associated crtc drivers, 1838c2ecf20Sopenharmony_ci * because connector requires pipe number of its crtc during initialization. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic struct exynos_drm_driver_info exynos_drm_drivers[] = { 1868c2ecf20Sopenharmony_ci { 1878c2ecf20Sopenharmony_ci DRV_PTR(fimd_driver, CONFIG_DRM_EXYNOS_FIMD), 1888c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 1898c2ecf20Sopenharmony_ci }, { 1908c2ecf20Sopenharmony_ci DRV_PTR(exynos5433_decon_driver, CONFIG_DRM_EXYNOS5433_DECON), 1918c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 1928c2ecf20Sopenharmony_ci }, { 1938c2ecf20Sopenharmony_ci DRV_PTR(decon_driver, CONFIG_DRM_EXYNOS7_DECON), 1948c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 1958c2ecf20Sopenharmony_ci }, { 1968c2ecf20Sopenharmony_ci DRV_PTR(mixer_driver, CONFIG_DRM_EXYNOS_MIXER), 1978c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 1988c2ecf20Sopenharmony_ci }, { 1998c2ecf20Sopenharmony_ci DRV_PTR(mic_driver, CONFIG_DRM_EXYNOS_MIC), 2008c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 2018c2ecf20Sopenharmony_ci }, { 2028c2ecf20Sopenharmony_ci DRV_PTR(dp_driver, CONFIG_DRM_EXYNOS_DP), 2038c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 2048c2ecf20Sopenharmony_ci }, { 2058c2ecf20Sopenharmony_ci DRV_PTR(dsi_driver, CONFIG_DRM_EXYNOS_DSI), 2068c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 2078c2ecf20Sopenharmony_ci }, { 2088c2ecf20Sopenharmony_ci DRV_PTR(hdmi_driver, CONFIG_DRM_EXYNOS_HDMI), 2098c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 2108c2ecf20Sopenharmony_ci }, { 2118c2ecf20Sopenharmony_ci DRV_PTR(vidi_driver, CONFIG_DRM_EXYNOS_VIDI), 2128c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER | DRM_VIRTUAL_DEVICE 2138c2ecf20Sopenharmony_ci }, { 2148c2ecf20Sopenharmony_ci DRV_PTR(g2d_driver, CONFIG_DRM_EXYNOS_G2D), 2158c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 2168c2ecf20Sopenharmony_ci }, { 2178c2ecf20Sopenharmony_ci DRV_PTR(fimc_driver, CONFIG_DRM_EXYNOS_FIMC), 2188c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER | DRM_FIMC_DEVICE, 2198c2ecf20Sopenharmony_ci }, { 2208c2ecf20Sopenharmony_ci DRV_PTR(rotator_driver, CONFIG_DRM_EXYNOS_ROTATOR), 2218c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 2228c2ecf20Sopenharmony_ci }, { 2238c2ecf20Sopenharmony_ci DRV_PTR(scaler_driver, CONFIG_DRM_EXYNOS_SCALER), 2248c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 2258c2ecf20Sopenharmony_ci }, { 2268c2ecf20Sopenharmony_ci DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC), 2278c2ecf20Sopenharmony_ci DRM_COMPONENT_DRIVER 2288c2ecf20Sopenharmony_ci }, { 2298c2ecf20Sopenharmony_ci &exynos_drm_platform_driver, 2308c2ecf20Sopenharmony_ci DRM_VIRTUAL_DEVICE 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci}; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int compare_dev(struct device *dev, void *data) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci return dev == (struct device *)data; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic struct component_match *exynos_drm_match_add(struct device *dev) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct component_match *match = NULL; 2428c2ecf20Sopenharmony_ci int i; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { 2458c2ecf20Sopenharmony_ci struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; 2468c2ecf20Sopenharmony_ci struct device *p = NULL, *d; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (!info->driver || !(info->flags & DRM_COMPONENT_DRIVER)) 2498c2ecf20Sopenharmony_ci continue; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci while ((d = platform_find_device_by_driver(p, &info->driver->driver))) { 2528c2ecf20Sopenharmony_ci put_device(p); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!(info->flags & DRM_FIMC_DEVICE) || 2558c2ecf20Sopenharmony_ci exynos_drm_check_fimc_device(d) == 0) 2568c2ecf20Sopenharmony_ci component_match_add(dev, &match, 2578c2ecf20Sopenharmony_ci compare_dev, d); 2588c2ecf20Sopenharmony_ci p = d; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci put_device(p); 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return match ?: ERR_PTR(-ENODEV); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int exynos_drm_bind(struct device *dev) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct exynos_drm_private *private; 2698c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 2708c2ecf20Sopenharmony_ci struct drm_device *drm; 2718c2ecf20Sopenharmony_ci unsigned int clone_mask; 2728c2ecf20Sopenharmony_ci int ret; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci drm = drm_dev_alloc(&exynos_drm_driver, dev); 2758c2ecf20Sopenharmony_ci if (IS_ERR(drm)) 2768c2ecf20Sopenharmony_ci return PTR_ERR(drm); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL); 2798c2ecf20Sopenharmony_ci if (!private) { 2808c2ecf20Sopenharmony_ci ret = -ENOMEM; 2818c2ecf20Sopenharmony_ci goto err_free_drm; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci init_waitqueue_head(&private->wait); 2858c2ecf20Sopenharmony_ci spin_lock_init(&private->lock); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci dev_set_drvdata(dev, drm); 2888c2ecf20Sopenharmony_ci drm->dev_private = (void *)private; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci drm_mode_config_init(drm); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci exynos_drm_mode_config_init(drm); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* setup possible_clones. */ 2958c2ecf20Sopenharmony_ci clone_mask = 0; 2968c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) 2978c2ecf20Sopenharmony_ci clone_mask |= drm_encoder_mask(encoder); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) 3008c2ecf20Sopenharmony_ci encoder->possible_clones = clone_mask; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* Try to bind all sub drivers. */ 3038c2ecf20Sopenharmony_ci ret = component_bind_all(drm->dev, drm); 3048c2ecf20Sopenharmony_ci if (ret) 3058c2ecf20Sopenharmony_ci goto err_mode_config_cleanup; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ret = drm_vblank_init(drm, drm->mode_config.num_crtc); 3088c2ecf20Sopenharmony_ci if (ret) 3098c2ecf20Sopenharmony_ci goto err_unbind_all; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci drm_mode_config_reset(drm); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* 3148c2ecf20Sopenharmony_ci * enable drm irq mode. 3158c2ecf20Sopenharmony_ci * - with irq_enabled = true, we can use the vblank feature. 3168c2ecf20Sopenharmony_ci * 3178c2ecf20Sopenharmony_ci * P.S. note that we wouldn't use drm irq handler but 3188c2ecf20Sopenharmony_ci * just specific driver own one instead because 3198c2ecf20Sopenharmony_ci * drm framework supports only one irq handler. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci drm->irq_enabled = true; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* init kms poll for handling hpd */ 3248c2ecf20Sopenharmony_ci drm_kms_helper_poll_init(drm); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci ret = exynos_drm_fbdev_init(drm); 3278c2ecf20Sopenharmony_ci if (ret) 3288c2ecf20Sopenharmony_ci goto err_cleanup_poll; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* register the DRM device */ 3318c2ecf20Sopenharmony_ci ret = drm_dev_register(drm, 0); 3328c2ecf20Sopenharmony_ci if (ret < 0) 3338c2ecf20Sopenharmony_ci goto err_cleanup_fbdev; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cierr_cleanup_fbdev: 3388c2ecf20Sopenharmony_ci exynos_drm_fbdev_fini(drm); 3398c2ecf20Sopenharmony_cierr_cleanup_poll: 3408c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(drm); 3418c2ecf20Sopenharmony_cierr_unbind_all: 3428c2ecf20Sopenharmony_ci component_unbind_all(drm->dev, drm); 3438c2ecf20Sopenharmony_cierr_mode_config_cleanup: 3448c2ecf20Sopenharmony_ci drm_mode_config_cleanup(drm); 3458c2ecf20Sopenharmony_ci exynos_drm_cleanup_dma(drm); 3468c2ecf20Sopenharmony_ci kfree(private); 3478c2ecf20Sopenharmony_ci dev_set_drvdata(dev, NULL); 3488c2ecf20Sopenharmony_cierr_free_drm: 3498c2ecf20Sopenharmony_ci drm_dev_put(drm); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic void exynos_drm_unbind(struct device *dev) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci drm_dev_unregister(drm); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci exynos_drm_fbdev_fini(drm); 3618c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(drm); 3628c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(drm); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci component_unbind_all(drm->dev, drm); 3658c2ecf20Sopenharmony_ci drm_mode_config_cleanup(drm); 3668c2ecf20Sopenharmony_ci exynos_drm_cleanup_dma(drm); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci kfree(drm->dev_private); 3698c2ecf20Sopenharmony_ci drm->dev_private = NULL; 3708c2ecf20Sopenharmony_ci dev_set_drvdata(dev, NULL); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci drm_dev_put(drm); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic const struct component_master_ops exynos_drm_ops = { 3768c2ecf20Sopenharmony_ci .bind = exynos_drm_bind, 3778c2ecf20Sopenharmony_ci .unbind = exynos_drm_unbind, 3788c2ecf20Sopenharmony_ci}; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic int exynos_drm_platform_probe(struct platform_device *pdev) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct component_match *match; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci match = exynos_drm_match_add(&pdev->dev); 3878c2ecf20Sopenharmony_ci if (IS_ERR(match)) 3888c2ecf20Sopenharmony_ci return PTR_ERR(match); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return component_master_add_with_match(&pdev->dev, &exynos_drm_ops, 3918c2ecf20Sopenharmony_ci match); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int exynos_drm_platform_remove(struct platform_device *pdev) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci component_master_del(&pdev->dev, &exynos_drm_ops); 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic void exynos_drm_platform_shutdown(struct platform_device *pdev) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct drm_device *drm = platform_get_drvdata(pdev); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (drm) 4058c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(drm); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic struct platform_driver exynos_drm_platform_driver = { 4098c2ecf20Sopenharmony_ci .probe = exynos_drm_platform_probe, 4108c2ecf20Sopenharmony_ci .remove = exynos_drm_platform_remove, 4118c2ecf20Sopenharmony_ci .shutdown = exynos_drm_platform_shutdown, 4128c2ecf20Sopenharmony_ci .driver = { 4138c2ecf20Sopenharmony_ci .name = "exynos-drm", 4148c2ecf20Sopenharmony_ci .pm = &exynos_drm_pm_ops, 4158c2ecf20Sopenharmony_ci }, 4168c2ecf20Sopenharmony_ci}; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic void exynos_drm_unregister_devices(void) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci int i; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) { 4238c2ecf20Sopenharmony_ci struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; 4248c2ecf20Sopenharmony_ci struct device *dev; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE)) 4278c2ecf20Sopenharmony_ci continue; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci while ((dev = platform_find_device_by_driver(NULL, 4308c2ecf20Sopenharmony_ci &info->driver->driver))) { 4318c2ecf20Sopenharmony_ci put_device(dev); 4328c2ecf20Sopenharmony_ci platform_device_unregister(to_platform_device(dev)); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int exynos_drm_register_devices(void) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct platform_device *pdev; 4408c2ecf20Sopenharmony_ci int i; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { 4438c2ecf20Sopenharmony_ci struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE)) 4468c2ecf20Sopenharmony_ci continue; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci pdev = platform_device_register_simple( 4498c2ecf20Sopenharmony_ci info->driver->driver.name, -1, NULL, 0); 4508c2ecf20Sopenharmony_ci if (IS_ERR(pdev)) 4518c2ecf20Sopenharmony_ci goto fail; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_cifail: 4568c2ecf20Sopenharmony_ci exynos_drm_unregister_devices(); 4578c2ecf20Sopenharmony_ci return PTR_ERR(pdev); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic void exynos_drm_unregister_drivers(void) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci int i; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) { 4658c2ecf20Sopenharmony_ci struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (!info->driver) 4688c2ecf20Sopenharmony_ci continue; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci platform_driver_unregister(info->driver); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int exynos_drm_register_drivers(void) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci int i, ret; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { 4798c2ecf20Sopenharmony_ci struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (!info->driver) 4828c2ecf20Sopenharmony_ci continue; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ret = platform_driver_register(info->driver); 4858c2ecf20Sopenharmony_ci if (ret) 4868c2ecf20Sopenharmony_ci goto fail; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_cifail: 4908c2ecf20Sopenharmony_ci exynos_drm_unregister_drivers(); 4918c2ecf20Sopenharmony_ci return ret; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic int exynos_drm_init(void) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci int ret; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci ret = exynos_drm_register_devices(); 4998c2ecf20Sopenharmony_ci if (ret) 5008c2ecf20Sopenharmony_ci return ret; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci ret = exynos_drm_register_drivers(); 5038c2ecf20Sopenharmony_ci if (ret) 5048c2ecf20Sopenharmony_ci goto err_unregister_pdevs; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cierr_unregister_pdevs: 5098c2ecf20Sopenharmony_ci exynos_drm_unregister_devices(); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return ret; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic void exynos_drm_exit(void) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci exynos_drm_unregister_drivers(); 5178c2ecf20Sopenharmony_ci exynos_drm_unregister_devices(); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cimodule_init(exynos_drm_init); 5218c2ecf20Sopenharmony_cimodule_exit(exynos_drm_exit); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); 5248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); 5258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>"); 5268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung SoC DRM Driver"); 5278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 528