162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd. 462306a36Sopenharmony_ci * Authors: 562306a36Sopenharmony_ci * Inki Dae <inki.dae@samsung.com> 662306a36Sopenharmony_ci * Joonyoung Shim <jy0922.shim@samsung.com> 762306a36Sopenharmony_ci * Seung-Woo Kim <sw0312.kim@samsung.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/component.h> 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1462306a36Sopenharmony_ci#include <linux/uaccess.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 1862306a36Sopenharmony_ci#include <drm/drm_drv.h> 1962306a36Sopenharmony_ci#include <drm/drm_file.h> 2062306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2162306a36Sopenharmony_ci#include <drm/drm_ioctl.h> 2262306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2362306a36Sopenharmony_ci#include <drm/drm_vblank.h> 2462306a36Sopenharmony_ci#include <drm/exynos_drm.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "exynos_drm_drv.h" 2762306a36Sopenharmony_ci#include "exynos_drm_fb.h" 2862306a36Sopenharmony_ci#include "exynos_drm_fbdev.h" 2962306a36Sopenharmony_ci#include "exynos_drm_g2d.h" 3062306a36Sopenharmony_ci#include "exynos_drm_gem.h" 3162306a36Sopenharmony_ci#include "exynos_drm_ipp.h" 3262306a36Sopenharmony_ci#include "exynos_drm_plane.h" 3362306a36Sopenharmony_ci#include "exynos_drm_vidi.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define DRIVER_NAME "exynos" 3662306a36Sopenharmony_ci#define DRIVER_DESC "Samsung SoC DRM" 3762306a36Sopenharmony_ci#define DRIVER_DATE "20180330" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * Interface history: 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * 1.0 - Original version 4362306a36Sopenharmony_ci * 1.1 - Upgrade IPP driver to version 2.0 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci#define DRIVER_MAJOR 1 4662306a36Sopenharmony_ci#define DRIVER_MINOR 1 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int exynos_drm_open(struct drm_device *dev, struct drm_file *file) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct drm_exynos_file_private *file_priv; 5162306a36Sopenharmony_ci int ret; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); 5462306a36Sopenharmony_ci if (!file_priv) 5562306a36Sopenharmony_ci return -ENOMEM; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci file->driver_priv = file_priv; 5862306a36Sopenharmony_ci ret = g2d_open(dev, file); 5962306a36Sopenharmony_ci if (ret) 6062306a36Sopenharmony_ci goto err_file_priv_free; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return ret; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cierr_file_priv_free: 6562306a36Sopenharmony_ci kfree(file_priv); 6662306a36Sopenharmony_ci file->driver_priv = NULL; 6762306a36Sopenharmony_ci return ret; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci g2d_close(dev, file); 7362306a36Sopenharmony_ci kfree(file->driver_priv); 7462306a36Sopenharmony_ci file->driver_priv = NULL; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic const struct drm_ioctl_desc exynos_ioctls[] = { 7862306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl, 7962306a36Sopenharmony_ci DRM_RENDER_ALLOW), 8062306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP, exynos_drm_gem_map_ioctl, 8162306a36Sopenharmony_ci DRM_RENDER_ALLOW), 8262306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl, 8362306a36Sopenharmony_ci DRM_RENDER_ALLOW), 8462306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl, 8562306a36Sopenharmony_ci DRM_AUTH), 8662306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, exynos_g2d_get_ver_ioctl, 8762306a36Sopenharmony_ci DRM_RENDER_ALLOW), 8862306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, exynos_g2d_set_cmdlist_ioctl, 8962306a36Sopenharmony_ci DRM_RENDER_ALLOW), 9062306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl, 9162306a36Sopenharmony_ci DRM_RENDER_ALLOW), 9262306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_RESOURCES, 9362306a36Sopenharmony_ci exynos_drm_ipp_get_res_ioctl, 9462306a36Sopenharmony_ci DRM_RENDER_ALLOW), 9562306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_CAPS, exynos_drm_ipp_get_caps_ioctl, 9662306a36Sopenharmony_ci DRM_RENDER_ALLOW), 9762306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_LIMITS, 9862306a36Sopenharmony_ci exynos_drm_ipp_get_limits_ioctl, 9962306a36Sopenharmony_ci DRM_RENDER_ALLOW), 10062306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(EXYNOS_IPP_COMMIT, exynos_drm_ipp_commit_ioctl, 10162306a36Sopenharmony_ci DRM_RENDER_ALLOW), 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ciDEFINE_DRM_GEM_FOPS(exynos_drm_driver_fops); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic const struct drm_driver exynos_drm_driver = { 10762306a36Sopenharmony_ci .driver_features = DRIVER_MODESET | DRIVER_GEM 10862306a36Sopenharmony_ci | DRIVER_ATOMIC | DRIVER_RENDER, 10962306a36Sopenharmony_ci .open = exynos_drm_open, 11062306a36Sopenharmony_ci .postclose = exynos_drm_postclose, 11162306a36Sopenharmony_ci .dumb_create = exynos_drm_gem_dumb_create, 11262306a36Sopenharmony_ci .gem_prime_import = exynos_drm_gem_prime_import, 11362306a36Sopenharmony_ci .gem_prime_import_sg_table = exynos_drm_gem_prime_import_sg_table, 11462306a36Sopenharmony_ci .ioctls = exynos_ioctls, 11562306a36Sopenharmony_ci .num_ioctls = ARRAY_SIZE(exynos_ioctls), 11662306a36Sopenharmony_ci .fops = &exynos_drm_driver_fops, 11762306a36Sopenharmony_ci .name = DRIVER_NAME, 11862306a36Sopenharmony_ci .desc = DRIVER_DESC, 11962306a36Sopenharmony_ci .date = DRIVER_DATE, 12062306a36Sopenharmony_ci .major = DRIVER_MAJOR, 12162306a36Sopenharmony_ci .minor = DRIVER_MINOR, 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int exynos_drm_suspend(struct device *dev) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(dev); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return drm_mode_config_helper_suspend(drm_dev); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void exynos_drm_resume(struct device *dev) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(dev); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci drm_mode_config_helper_resume(drm_dev); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic const struct dev_pm_ops exynos_drm_pm_ops = { 13962306a36Sopenharmony_ci .prepare = exynos_drm_suspend, 14062306a36Sopenharmony_ci .complete = exynos_drm_resume, 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* forward declaration */ 14462306a36Sopenharmony_cistatic struct platform_driver exynos_drm_platform_driver; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistruct exynos_drm_driver_info { 14762306a36Sopenharmony_ci struct platform_driver *driver; 14862306a36Sopenharmony_ci unsigned int flags; 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#define DRM_COMPONENT_DRIVER BIT(0) /* supports component framework */ 15262306a36Sopenharmony_ci#define DRM_VIRTUAL_DEVICE BIT(1) /* create virtual platform device */ 15362306a36Sopenharmony_ci#define DRM_FIMC_DEVICE BIT(2) /* devices shared with V4L2 subsystem */ 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci#define DRV_PTR(drv, cond) (IS_ENABLED(cond) ? &drv : NULL) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* 15862306a36Sopenharmony_ci * Connector drivers should not be placed before associated crtc drivers, 15962306a36Sopenharmony_ci * because connector requires pipe number of its crtc during initialization. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_cistatic struct exynos_drm_driver_info exynos_drm_drivers[] = { 16262306a36Sopenharmony_ci { 16362306a36Sopenharmony_ci DRV_PTR(fimd_driver, CONFIG_DRM_EXYNOS_FIMD), 16462306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 16562306a36Sopenharmony_ci }, { 16662306a36Sopenharmony_ci DRV_PTR(exynos5433_decon_driver, CONFIG_DRM_EXYNOS5433_DECON), 16762306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 16862306a36Sopenharmony_ci }, { 16962306a36Sopenharmony_ci DRV_PTR(decon_driver, CONFIG_DRM_EXYNOS7_DECON), 17062306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 17162306a36Sopenharmony_ci }, { 17262306a36Sopenharmony_ci DRV_PTR(mixer_driver, CONFIG_DRM_EXYNOS_MIXER), 17362306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 17462306a36Sopenharmony_ci }, { 17562306a36Sopenharmony_ci DRV_PTR(dp_driver, CONFIG_DRM_EXYNOS_DP), 17662306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 17762306a36Sopenharmony_ci }, { 17862306a36Sopenharmony_ci DRV_PTR(dsi_driver, CONFIG_DRM_EXYNOS_DSI), 17962306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 18062306a36Sopenharmony_ci }, { 18162306a36Sopenharmony_ci DRV_PTR(mic_driver, CONFIG_DRM_EXYNOS_MIC), 18262306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 18362306a36Sopenharmony_ci }, { 18462306a36Sopenharmony_ci DRV_PTR(hdmi_driver, CONFIG_DRM_EXYNOS_HDMI), 18562306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 18662306a36Sopenharmony_ci }, { 18762306a36Sopenharmony_ci DRV_PTR(vidi_driver, CONFIG_DRM_EXYNOS_VIDI), 18862306a36Sopenharmony_ci DRM_COMPONENT_DRIVER | DRM_VIRTUAL_DEVICE 18962306a36Sopenharmony_ci }, { 19062306a36Sopenharmony_ci DRV_PTR(g2d_driver, CONFIG_DRM_EXYNOS_G2D), 19162306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 19262306a36Sopenharmony_ci }, { 19362306a36Sopenharmony_ci DRV_PTR(fimc_driver, CONFIG_DRM_EXYNOS_FIMC), 19462306a36Sopenharmony_ci DRM_COMPONENT_DRIVER | DRM_FIMC_DEVICE, 19562306a36Sopenharmony_ci }, { 19662306a36Sopenharmony_ci DRV_PTR(rotator_driver, CONFIG_DRM_EXYNOS_ROTATOR), 19762306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 19862306a36Sopenharmony_ci }, { 19962306a36Sopenharmony_ci DRV_PTR(scaler_driver, CONFIG_DRM_EXYNOS_SCALER), 20062306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 20162306a36Sopenharmony_ci }, { 20262306a36Sopenharmony_ci DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC), 20362306a36Sopenharmony_ci DRM_COMPONENT_DRIVER 20462306a36Sopenharmony_ci }, { 20562306a36Sopenharmony_ci &exynos_drm_platform_driver, 20662306a36Sopenharmony_ci DRM_VIRTUAL_DEVICE 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic struct component_match *exynos_drm_match_add(struct device *dev) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct component_match *match = NULL; 21362306a36Sopenharmony_ci int i; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { 21662306a36Sopenharmony_ci struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; 21762306a36Sopenharmony_ci struct device *p = NULL, *d; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (!info->driver || !(info->flags & DRM_COMPONENT_DRIVER)) 22062306a36Sopenharmony_ci continue; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci while ((d = platform_find_device_by_driver(p, &info->driver->driver))) { 22362306a36Sopenharmony_ci put_device(p); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (!(info->flags & DRM_FIMC_DEVICE) || 22662306a36Sopenharmony_ci exynos_drm_check_fimc_device(d) == 0) 22762306a36Sopenharmony_ci component_match_add(dev, &match, component_compare_dev, d); 22862306a36Sopenharmony_ci p = d; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci put_device(p); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return match ?: ERR_PTR(-ENODEV); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int exynos_drm_bind(struct device *dev) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct exynos_drm_private *private; 23962306a36Sopenharmony_ci struct drm_encoder *encoder; 24062306a36Sopenharmony_ci struct drm_device *drm; 24162306a36Sopenharmony_ci unsigned int clone_mask; 24262306a36Sopenharmony_ci int ret; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci drm = drm_dev_alloc(&exynos_drm_driver, dev); 24562306a36Sopenharmony_ci if (IS_ERR(drm)) 24662306a36Sopenharmony_ci return PTR_ERR(drm); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL); 24962306a36Sopenharmony_ci if (!private) { 25062306a36Sopenharmony_ci ret = -ENOMEM; 25162306a36Sopenharmony_ci goto err_free_drm; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci init_waitqueue_head(&private->wait); 25562306a36Sopenharmony_ci spin_lock_init(&private->lock); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci dev_set_drvdata(dev, drm); 25862306a36Sopenharmony_ci drm->dev_private = (void *)private; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci drm_mode_config_init(drm); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci exynos_drm_mode_config_init(drm); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* setup possible_clones. */ 26562306a36Sopenharmony_ci clone_mask = 0; 26662306a36Sopenharmony_ci list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) 26762306a36Sopenharmony_ci clone_mask |= drm_encoder_mask(encoder); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) 27062306a36Sopenharmony_ci encoder->possible_clones = clone_mask; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Try to bind all sub drivers. */ 27362306a36Sopenharmony_ci ret = component_bind_all(drm->dev, drm); 27462306a36Sopenharmony_ci if (ret) 27562306a36Sopenharmony_ci goto err_mode_config_cleanup; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ret = drm_vblank_init(drm, drm->mode_config.num_crtc); 27862306a36Sopenharmony_ci if (ret) 27962306a36Sopenharmony_ci goto err_unbind_all; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci drm_mode_config_reset(drm); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* init kms poll for handling hpd */ 28462306a36Sopenharmony_ci drm_kms_helper_poll_init(drm); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* register the DRM device */ 28762306a36Sopenharmony_ci ret = drm_dev_register(drm, 0); 28862306a36Sopenharmony_ci if (ret < 0) 28962306a36Sopenharmony_ci goto err_cleanup_poll; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci exynos_drm_fbdev_setup(drm); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cierr_cleanup_poll: 29662306a36Sopenharmony_ci drm_kms_helper_poll_fini(drm); 29762306a36Sopenharmony_cierr_unbind_all: 29862306a36Sopenharmony_ci component_unbind_all(drm->dev, drm); 29962306a36Sopenharmony_cierr_mode_config_cleanup: 30062306a36Sopenharmony_ci drm_mode_config_cleanup(drm); 30162306a36Sopenharmony_ci exynos_drm_cleanup_dma(drm); 30262306a36Sopenharmony_ci kfree(private); 30362306a36Sopenharmony_ci dev_set_drvdata(dev, NULL); 30462306a36Sopenharmony_cierr_free_drm: 30562306a36Sopenharmony_ci drm_dev_put(drm); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic void exynos_drm_unbind(struct device *dev) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci drm_dev_unregister(drm); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci drm_kms_helper_poll_fini(drm); 31762306a36Sopenharmony_ci drm_atomic_helper_shutdown(drm); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci component_unbind_all(drm->dev, drm); 32062306a36Sopenharmony_ci drm_mode_config_cleanup(drm); 32162306a36Sopenharmony_ci exynos_drm_cleanup_dma(drm); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci kfree(drm->dev_private); 32462306a36Sopenharmony_ci drm->dev_private = NULL; 32562306a36Sopenharmony_ci dev_set_drvdata(dev, NULL); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci drm_dev_put(drm); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic const struct component_master_ops exynos_drm_ops = { 33162306a36Sopenharmony_ci .bind = exynos_drm_bind, 33262306a36Sopenharmony_ci .unbind = exynos_drm_unbind, 33362306a36Sopenharmony_ci}; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int exynos_drm_platform_probe(struct platform_device *pdev) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct component_match *match; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci match = exynos_drm_match_add(&pdev->dev); 34262306a36Sopenharmony_ci if (IS_ERR(match)) 34362306a36Sopenharmony_ci return PTR_ERR(match); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return component_master_add_with_match(&pdev->dev, &exynos_drm_ops, 34662306a36Sopenharmony_ci match); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int exynos_drm_platform_remove(struct platform_device *pdev) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci component_master_del(&pdev->dev, &exynos_drm_ops); 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void exynos_drm_platform_shutdown(struct platform_device *pdev) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct drm_device *drm = platform_get_drvdata(pdev); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (drm) 36062306a36Sopenharmony_ci drm_atomic_helper_shutdown(drm); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic struct platform_driver exynos_drm_platform_driver = { 36462306a36Sopenharmony_ci .probe = exynos_drm_platform_probe, 36562306a36Sopenharmony_ci .remove = exynos_drm_platform_remove, 36662306a36Sopenharmony_ci .shutdown = exynos_drm_platform_shutdown, 36762306a36Sopenharmony_ci .driver = { 36862306a36Sopenharmony_ci .name = "exynos-drm", 36962306a36Sopenharmony_ci .pm = &exynos_drm_pm_ops, 37062306a36Sopenharmony_ci }, 37162306a36Sopenharmony_ci}; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void exynos_drm_unregister_devices(void) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci int i; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) { 37862306a36Sopenharmony_ci struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; 37962306a36Sopenharmony_ci struct device *dev; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE)) 38262306a36Sopenharmony_ci continue; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci while ((dev = platform_find_device_by_driver(NULL, 38562306a36Sopenharmony_ci &info->driver->driver))) { 38662306a36Sopenharmony_ci put_device(dev); 38762306a36Sopenharmony_ci platform_device_unregister(to_platform_device(dev)); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int exynos_drm_register_devices(void) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct platform_device *pdev; 39562306a36Sopenharmony_ci int i; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { 39862306a36Sopenharmony_ci struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE)) 40162306a36Sopenharmony_ci continue; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci pdev = platform_device_register_simple( 40462306a36Sopenharmony_ci info->driver->driver.name, -1, NULL, 0); 40562306a36Sopenharmony_ci if (IS_ERR(pdev)) 40662306a36Sopenharmony_ci goto fail; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_cifail: 41162306a36Sopenharmony_ci exynos_drm_unregister_devices(); 41262306a36Sopenharmony_ci return PTR_ERR(pdev); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void exynos_drm_unregister_drivers(void) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci int i; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) { 42062306a36Sopenharmony_ci struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (!info->driver) 42362306a36Sopenharmony_ci continue; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci platform_driver_unregister(info->driver); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int exynos_drm_register_drivers(void) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci int i, ret; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { 43462306a36Sopenharmony_ci struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (!info->driver) 43762306a36Sopenharmony_ci continue; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci ret = platform_driver_register(info->driver); 44062306a36Sopenharmony_ci if (ret) 44162306a36Sopenharmony_ci goto fail; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_cifail: 44562306a36Sopenharmony_ci exynos_drm_unregister_drivers(); 44662306a36Sopenharmony_ci return ret; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int exynos_drm_init(void) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci int ret; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (drm_firmware_drivers_only()) 45462306a36Sopenharmony_ci return -ENODEV; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci ret = exynos_drm_register_devices(); 45762306a36Sopenharmony_ci if (ret) 45862306a36Sopenharmony_ci return ret; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ret = exynos_drm_register_drivers(); 46162306a36Sopenharmony_ci if (ret) 46262306a36Sopenharmony_ci goto err_unregister_pdevs; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cierr_unregister_pdevs: 46762306a36Sopenharmony_ci exynos_drm_unregister_devices(); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return ret; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic void exynos_drm_exit(void) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci exynos_drm_unregister_drivers(); 47562306a36Sopenharmony_ci exynos_drm_unregister_devices(); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cimodule_init(exynos_drm_init); 47962306a36Sopenharmony_cimodule_exit(exynos_drm_exit); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ciMODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); 48262306a36Sopenharmony_ciMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); 48362306a36Sopenharmony_ciMODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>"); 48462306a36Sopenharmony_ciMODULE_DESCRIPTION("Samsung SoC DRM Driver"); 48562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 486