diff options
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_dp_core.c | 45 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_core.c | 216 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.c | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_dpi.c | 16 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.c | 310 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.h | 89 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_dsi.c | 110 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 83 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_vidi.c | 101 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_hdmi.c | 59 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_mixer.c | 54 |
12 files changed, 641 insertions, 463 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index c31129652807..1f8914b44714 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/of.h> +#include <linux/component.h> #include <linux/phy/phy.h> #include <video/of_display_timing.h> #include <video/of_videomode.h> @@ -962,16 +963,6 @@ static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = { .best_encoder = exynos_dp_best_encoder, }; -static int exynos_dp_initialize(struct exynos_drm_display *display, - struct drm_device *drm_dev) -{ - struct exynos_dp_device *dp = display->ctx; - - dp->drm_dev = drm_dev; - - return 0; -} - static bool find_bridge(const char *compat, struct bridge_init *bridge) { bridge->client = NULL; @@ -1099,7 +1090,6 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode) } static struct exynos_drm_display_ops exynos_dp_display_ops = { - .initialize = exynos_dp_initialize, .create_connector = exynos_dp_create_connector, .dpms = exynos_dp_dpms, }; @@ -1221,8 +1211,10 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp) return 0; } -static int exynos_dp_probe(struct platform_device *pdev) +static int exynos_dp_bind(struct device *dev, struct device *master, void *data) { + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm_dev = data; struct resource *res; struct exynos_dp_device *dp; @@ -1282,21 +1274,40 @@ static int exynos_dp_probe(struct platform_device *pdev) } disable_irq(dp->irq); + dp->drm_dev = drm_dev; exynos_dp_display.ctx = dp; platform_set_drvdata(pdev, &exynos_dp_display); - exynos_drm_display_register(&exynos_dp_display); - return 0; + return exynos_drm_create_enc_conn(drm_dev, &exynos_dp_display); } -static int exynos_dp_remove(struct platform_device *pdev) +static void exynos_dp_unbind(struct device *dev, struct device *master, + void *data) { - struct exynos_drm_display *display = platform_get_drvdata(pdev); + struct exynos_drm_display *display = dev_get_drvdata(dev); + struct exynos_dp_device *dp = display->ctx; + struct drm_encoder *encoder = dp->encoder; exynos_dp_dpms(display, DRM_MODE_DPMS_OFF); - exynos_drm_display_unregister(&exynos_dp_display); + encoder->funcs->destroy(encoder); + drm_connector_cleanup(&dp->connector); +} + +static const struct component_ops exynos_dp_ops = { + .bind = exynos_dp_bind, + .unbind = exynos_dp_unbind, +}; + +static int exynos_dp_probe(struct platform_device *pdev) +{ + return exynos_drm_component_add(&pdev->dev, &exynos_dp_ops); +} + +static int exynos_dp_remove(struct platform_device *pdev) +{ + exynos_drm_component_del(&pdev->dev, &exynos_dp_ops); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index 0e9e06ce36b8..4c9f972eaa07 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -19,21 +19,19 @@ #include "exynos_drm_fbdev.h" static LIST_HEAD(exynos_drm_subdrv_list); -static LIST_HEAD(exynos_drm_manager_list); -static LIST_HEAD(exynos_drm_display_list); -static int exynos_drm_create_enc_conn(struct drm_device *dev, +int exynos_drm_create_enc_conn(struct drm_device *dev, struct exynos_drm_display *display) { struct drm_encoder *encoder; - struct exynos_drm_manager *manager; int ret; unsigned long possible_crtcs = 0; - /* Find possible crtcs for this display */ - list_for_each_entry(manager, &exynos_drm_manager_list, list) - if (manager->type == display->type) - possible_crtcs |= 1 << manager->pipe; + ret = exynos_drm_crtc_get_pipe_from_type(dev, display->type); + if (ret < 0) + return ret; + + possible_crtcs |= 1 << ret; /* create and initialize a encoder for this sub driver. */ encoder = exynos_drm_encoder_create(dev, display, possible_crtcs); @@ -57,127 +55,29 @@ err_destroy_encoder: return ret; } -static int exynos_drm_subdrv_probe(struct drm_device *dev, - struct exynos_drm_subdrv *subdrv) -{ - if (subdrv->probe) { - int ret; - - subdrv->drm_dev = dev; - - /* - * this probe callback would be called by sub driver - * after setting of all resources to this sub driver, - * such as clock, irq and register map are done or by load() - * of exynos drm driver. - * - * P.S. note that this driver is considered for modularization. - */ - ret = subdrv->probe(dev, subdrv->dev); - if (ret) - return ret; - } - - return 0; -} - -static void exynos_drm_subdrv_remove(struct drm_device *dev, - struct exynos_drm_subdrv *subdrv) -{ - if (subdrv->remove) - subdrv->remove(dev, subdrv->dev); -} - -int exynos_drm_initialize_managers(struct drm_device *dev) +int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv) { - struct exynos_drm_manager *manager, *n; - int ret, pipe = 0; - - list_for_each_entry(manager, &exynos_drm_manager_list, list) { - if (manager->ops->initialize) { - ret = manager->ops->initialize(manager, dev, pipe); - if (ret) { - DRM_ERROR("Mgr init [%d] failed with %d\n", - manager->type, ret); - goto err; - } - } + if (!subdrv) + return -EINVAL; - manager->drm_dev = dev; - manager->pipe = pipe++; + list_add_tail(&subdrv->list, &exynos_drm_subdrv_list); - ret = exynos_drm_crtc_create(manager); - if (ret) { - DRM_ERROR("CRTC create [%d] failed with %d\n", - manager->type, ret); - goto err; - } - } return 0; - -err: - list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) { - if (pipe-- > 0) - exynos_drm_manager_unregister(manager); - else - list_del(&manager->list); - } - return ret; -} - -void exynos_drm_remove_managers(struct drm_device *dev) -{ - struct exynos_drm_manager *manager, *n; - - list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) - exynos_drm_manager_unregister(manager); } +EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register); -int exynos_drm_initialize_displays(struct drm_device *dev) +int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv) { - struct exynos_drm_display *display, *n; - int ret, initialized = 0; - - list_for_each_entry(display, &exynos_drm_display_list, list) { - if (display->ops->initialize) { - ret = display->ops->initialize(display, dev); - if (ret) { - DRM_ERROR("Display init [%d] failed with %d\n", - display->type, ret); - goto err; - } - } + if (!subdrv) + return -EINVAL; - initialized++; + list_del(&subdrv->list); - ret = exynos_drm_create_enc_conn(dev, display); - if (ret) { - DRM_ERROR("Encoder create [%d] failed with %d\n", - display->type, ret); - goto err; - } - } return 0; - -err: - list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) { - if (initialized-- > 0) - exynos_drm_display_unregister(display); - else - list_del(&display->list); - } - return ret; -} - -void exynos_drm_remove_displays(struct drm_device *dev) -{ - struct exynos_drm_display *display, *n; - - list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) - exynos_drm_display_unregister(display); } +EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister); -int exynos_drm_device_register(struct drm_device *dev) +int exynos_drm_device_subdrv_probe(struct drm_device *dev) { struct exynos_drm_subdrv *subdrv, *n; int err; @@ -186,19 +86,28 @@ int exynos_drm_device_register(struct drm_device *dev) return -EINVAL; list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) { - err = exynos_drm_subdrv_probe(dev, subdrv); - if (err) { - DRM_DEBUG("exynos drm subdrv probe failed.\n"); - list_del(&subdrv->list); - continue; + if (subdrv->probe) { + subdrv->drm_dev = dev; + + /* + * this probe callback would be called by sub driver + * after setting of all resources to this sub driver, + * such as clock, irq and register map are done. + */ + err = subdrv->probe(dev, subdrv->dev); + if (err) { + DRM_DEBUG("exynos drm subdrv probe failed.\n"); + list_del(&subdrv->list); + continue; + } } } return 0; } -EXPORT_SYMBOL_GPL(exynos_drm_device_register); +EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_probe); -int exynos_drm_device_unregister(struct drm_device *dev) +int exynos_drm_device_subdrv_remove(struct drm_device *dev) { struct exynos_drm_subdrv *subdrv; @@ -208,66 +117,13 @@ int exynos_drm_device_unregister(struct drm_device *dev) } list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) { - exynos_drm_subdrv_remove(dev, subdrv); + if (subdrv->remove) + subdrv->remove(dev, subdrv->dev); } return 0; } -EXPORT_SYMBOL_GPL(exynos_drm_device_unregister); - -int exynos_drm_manager_register(struct exynos_drm_manager *manager) -{ - BUG_ON(!manager->ops); - list_add_tail(&manager->list, &exynos_drm_manager_list); - return 0; -} - -int exynos_drm_manager_unregister(struct exynos_drm_manager *manager) -{ - if (manager->ops->remove) - manager->ops->remove(manager); - - list_del(&manager->list); - return 0; -} - -int exynos_drm_display_register(struct exynos_drm_display *display) -{ - BUG_ON(!display->ops); - list_add_tail(&display->list, &exynos_drm_display_list); - return 0; -} - -int exynos_drm_display_unregister(struct exynos_drm_display *display) -{ - if (display->ops->remove) - display->ops->remove(display); - - list_del(&display->list); - return 0; -} - -int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv) -{ - if (!subdrv) - return -EINVAL; - - list_add_tail(&subdrv->list, &exynos_drm_subdrv_list); - - return 0; -} -EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register); - -int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv) -{ - if (!subdrv) - return -EINVAL; - - list_del(&subdrv->list); - - return 0; -} -EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister); +EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_remove); int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 1ef5ab9c9d51..95c9435d0266 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -368,6 +368,7 @@ int exynos_drm_crtc_create(struct exynos_drm_manager *manager) return -ENOMEM; } + manager->crtc = &exynos_crtc->drm_crtc; crtc = &exynos_crtc->drm_crtc; private->crtc[manager->pipe] = crtc; @@ -491,3 +492,19 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb) manager->ops->wait_for_vblank(manager); } } + +int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, + unsigned int out_type) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) { + struct exynos_drm_crtc *exynos_crtc; + + exynos_crtc = to_exynos_crtc(crtc); + if (exynos_crtc->manager->type == out_type) + return exynos_crtc->manager->pipe; + } + + return -EPERM; +} diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index c27b66cc5d24..9f74b10a8a01 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -32,4 +32,8 @@ void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos); void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos); void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos); +/* This function gets pipe value to crtc device matched with out_type. */ +int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, + unsigned int out_type); + #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 82e52c71bccc..f5503af52148 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -248,7 +248,7 @@ enum { FIMD_PORT_WRB, }; -static struct device_node *exynos_dpi_of_find_panel_node(struct device *dev) +struct device_node *exynos_dpi_of_find_panel_node(struct device *dev) { struct device_node *np, *ep; @@ -301,7 +301,7 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx) return 0; } -int exynos_dpi_probe(struct device *dev) +int exynos_dpi_probe(struct drm_device *drm_dev, struct device *dev) { struct exynos_dpi *ctx; int ret; @@ -318,15 +318,17 @@ int exynos_dpi_probe(struct device *dev) if (ret < 0) return ret; - exynos_drm_display_register(&exynos_dpi_display); - - return 0; + return exynos_drm_create_enc_conn(drm_dev, &exynos_dpi_display); } -int exynos_dpi_remove(struct device *dev) +int exynos_dpi_remove(struct drm_device *drm_dev, struct device *dev) { + struct drm_encoder *encoder = exynos_dpi_display.encoder; + struct exynos_dpi *ctx = exynos_dpi_display.ctx; + exynos_dpi_dpms(&exynos_dpi_display, DRM_MODE_DPMS_OFF); - exynos_drm_display_unregister(&exynos_dpi_display); + encoder->funcs->destroy(encoder); + drm_connector_cleanup(&ctx->connector); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index a00a0b6b8e2c..5cdb2dc833de 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -16,6 +16,7 @@ #include <drm/drm_crtc_helper.h> #include <linux/anon_inodes.h> +#include <linux/component.h> #include <drm/exynos_drm.h> @@ -40,9 +41,16 @@ #define VBLANK_OFF_DELAY 50000 -/* platform device pointer for eynos drm device. */ static struct platform_device *exynos_drm_pdev; +static DEFINE_MUTEX(drm_component_lock); +static LIST_HEAD(drm_component_list); + +struct component_dev { + struct list_head list; + struct device *dev; +}; + static int exynos_drm_load(struct drm_device *dev, unsigned long flags) { struct exynos_drm_private *private; @@ -73,38 +81,21 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) exynos_drm_mode_config_init(dev); - ret = exynos_drm_initialize_managers(dev); - if (ret) - goto err_mode_config_cleanup; - for (nr = 0; nr < MAX_PLANE; nr++) { struct drm_plane *plane; unsigned long possible_crtcs = (1 << MAX_CRTC) - 1; plane = exynos_plane_init(dev, possible_crtcs, false); if (!plane) - goto err_manager_cleanup; + goto err_mode_config_cleanup; } - ret = exynos_drm_initialize_displays(dev); - if (ret) - goto err_manager_cleanup; - /* init kms poll for handling hpd */ drm_kms_helper_poll_init(dev); ret = drm_vblank_init(dev, MAX_CRTC); if (ret) - goto err_display_cleanup; - - /* - * probe sub drivers such as display controller and hdmi driver, - * that were registered at probe() of platform driver - * to the sub driver and create encoder and connector for them. - */ - ret = exynos_drm_device_register(dev); - if (ret) - goto err_vblank; + goto err_mode_config_cleanup; /* setup possible_clones. */ exynos_drm_encoder_setup(dev); @@ -113,17 +104,25 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) platform_set_drvdata(dev->platformdev, dev); + /* Try to bind all sub drivers. */ + ret = component_bind_all(dev->dev, dev); + if (ret) + goto err_cleanup_vblank; + + /* Probe non kms sub drivers. */ + ret = exynos_drm_device_subdrv_probe(dev); + if (ret) + goto err_unbind_all; + /* force connectors detection */ drm_helper_hpd_irq_event(dev); return 0; -err_vblank: +err_unbind_all: + component_unbind_all(dev->dev, dev); +err_cleanup_vblank: drm_vblank_cleanup(dev); -err_display_cleanup: - exynos_drm_remove_displays(dev); -err_manager_cleanup: - exynos_drm_remove_managers(dev); err_mode_config_cleanup: drm_mode_config_cleanup(dev); drm_release_iommu_mapping(dev); @@ -135,17 +134,17 @@ err_free_private: static int exynos_drm_unload(struct drm_device *dev) { + exynos_drm_device_subdrv_remove(dev); + exynos_drm_fbdev_fini(dev); - exynos_drm_device_unregister(dev); drm_vblank_cleanup(dev); drm_kms_helper_poll_fini(dev); - exynos_drm_remove_displays(dev); - exynos_drm_remove_managers(dev); drm_mode_config_cleanup(dev); drm_release_iommu_mapping(dev); kfree(dev->dev_private); + component_unbind_all(dev->dev, dev); dev->dev_private = NULL; return 0; @@ -354,27 +353,6 @@ static struct drm_driver exynos_drm_driver = { .minor = DRIVER_MINOR, }; -static int exynos_drm_platform_probe(struct platform_device *pdev) -{ - int ret; - - ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); - - return drm_platform_init(&exynos_drm_driver, pdev); -} - -static int exynos_drm_platform_remove(struct platform_device *pdev) -{ - drm_put_dev(platform_get_drvdata(pdev)); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int exynos_drm_sys_suspend(struct device *dev) { @@ -429,24 +407,120 @@ static const struct dev_pm_ops exynos_drm_pm_ops = { exynos_drm_runtime_resume, NULL) }; -static struct platform_driver exynos_drm_platform_driver = { - .probe = exynos_drm_platform_probe, - .remove = exynos_drm_platform_remove, - .driver = { - .owner = THIS_MODULE, - .name = "exynos-drm", - .pm = &exynos_drm_pm_ops, - }, +int exynos_drm_component_add(struct device *dev, + const struct component_ops *ops) +{ + struct component_dev *cdev; + int ret; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + ret = component_add(dev, ops); + if (ret) { + kfree(cdev); + return ret; + } + + cdev->dev = dev; + + mutex_lock(&drm_component_lock); + list_add_tail(&cdev->list, &drm_component_list); + mutex_unlock(&drm_component_lock); + + return 0; +} + +void exynos_drm_component_del(struct device *dev, + const struct component_ops *ops) +{ + struct component_dev *cdev, *next; + + mutex_lock(&drm_component_lock); + + list_for_each_entry_safe(cdev, next, &drm_component_list, list) { + if (dev == cdev->dev) { + list_del(&cdev->list); + kfree(cdev); + mutex_unlock(&drm_component_lock); + break; + } + } + + mutex_unlock(&drm_component_lock); + + component_del(dev, ops); +} + +static int compare_of(struct device *dev, void *data) +{ + return dev == (struct device *)data; +} + +static int exynos_drm_add_components(struct device *dev, struct master *m) +{ + unsigned int attached_cnt = 0; + struct component_dev *cdev; + + mutex_lock(&drm_component_lock); + + list_for_each_entry(cdev, &drm_component_list, list) { + int ret; + + mutex_unlock(&drm_component_lock); + + ret = component_master_add_child(m, compare_of, cdev->dev); + if (!ret) + attached_cnt++; + + mutex_lock(&drm_component_lock); + } + + mutex_unlock(&drm_component_lock); + + if (!attached_cnt) + return -ENXIO; + + return 0; +} + +static int exynos_drm_bind(struct device *dev) +{ + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + return drm_platform_init(&exynos_drm_driver, to_platform_device(dev)); +} + +static void exynos_drm_unbind(struct device *dev) +{ + drm_put_dev(dev_get_drvdata(dev)); +} + +static const struct component_master_ops exynos_drm_ops = { + .add_components = exynos_drm_add_components, + .bind = exynos_drm_bind, + .unbind = exynos_drm_unbind, }; -static int __init exynos_drm_init(void) +static int exynos_drm_platform_probe(struct platform_device *pdev) { int ret; + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls); + +#ifdef CONFIG_DRM_EXYNOS_FIMD + ret = platform_driver_register(&fimd_driver); + if (ret < 0) + return ret; +#endif + #ifdef CONFIG_DRM_EXYNOS_DP ret = platform_driver_register(&dp_driver); if (ret < 0) - return ret; + goto err_unregister_fimd_drv; #endif #ifdef CONFIG_DRM_EXYNOS_DSI @@ -455,23 +529,11 @@ static int __init exynos_drm_init(void) goto err_unregister_dp_drv; #endif -#ifdef CONFIG_DRM_EXYNOS_FIMD - ret = platform_driver_register(&fimd_driver); - if (ret < 0) - goto err_unregister_dsi_drv; -#endif - #ifdef CONFIG_DRM_EXYNOS_HDMI - ret = platform_driver_register(&hdmi_driver); - if (ret < 0) - goto err_unregister_fimd_drv; ret = platform_driver_register(&mixer_driver); if (ret < 0) - goto err_unregister_hdmi_drv; -#endif - -#ifdef CONFIG_DRM_EXYNOS_VIDI - ret = platform_driver_register(&vidi_driver); + goto err_unregister_dsi_drv; + ret = platform_driver_register(&hdmi_driver); if (ret < 0) goto err_unregister_mixer_drv; #endif @@ -479,7 +541,7 @@ static int __init exynos_drm_init(void) #ifdef CONFIG_DRM_EXYNOS_G2D ret = platform_driver_register(&g2d_driver); if (ret < 0) - goto err_unregister_vidi_drv; + goto err_unregister_hdmi_drv; #endif #ifdef CONFIG_DRM_EXYNOS_FIMC @@ -510,25 +572,13 @@ static int __init exynos_drm_init(void) goto err_unregister_ipp_drv; #endif - ret = platform_driver_register(&exynos_drm_platform_driver); + ret = component_master_add(&pdev->dev, &exynos_drm_ops); if (ret < 0) - goto err_unregister_ipp_dev; - - exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1, - NULL, 0); - if (IS_ERR(exynos_drm_pdev)) { - ret = PTR_ERR(exynos_drm_pdev); - goto err_unregister_drm_drv; - } + DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n"); return 0; -err_unregister_drm_drv: - platform_driver_unregister(&exynos_drm_platform_driver); - -err_unregister_ipp_dev: #ifdef CONFIG_DRM_EXYNOS_IPP - exynos_platform_device_ipp_unregister(); err_unregister_ipp_drv: platform_driver_unregister(&ipp_driver); err_unregister_gsc_drv: @@ -551,23 +601,13 @@ err_unregister_g2d_drv: #ifdef CONFIG_DRM_EXYNOS_G2D platform_driver_unregister(&g2d_driver); -err_unregister_vidi_drv: -#endif - -#ifdef CONFIG_DRM_EXYNOS_VIDI - platform_driver_unregister(&vidi_driver); -err_unregister_mixer_drv: +err_unregister_hdmi_drv: #endif #ifdef CONFIG_DRM_EXYNOS_HDMI - platform_driver_unregister(&mixer_driver); -err_unregister_hdmi_drv: platform_driver_unregister(&hdmi_driver); -err_unregister_fimd_drv: -#endif - -#ifdef CONFIG_DRM_EXYNOS_FIMD - platform_driver_unregister(&fimd_driver); +err_unregister_mixer_drv: + platform_driver_unregister(&mixer_driver); err_unregister_dsi_drv: #endif @@ -578,16 +618,17 @@ err_unregister_dp_drv: #ifdef CONFIG_DRM_EXYNOS_DP platform_driver_unregister(&dp_driver); +err_unregister_fimd_drv: +#endif + +#ifdef CONFIG_DRM_EXYNOS_FIMD + platform_driver_unregister(&fimd_driver); #endif return ret; } -static void __exit exynos_drm_exit(void) +static int exynos_drm_platform_remove(struct platform_device *pdev) { - platform_device_unregister(exynos_drm_pdev); - - platform_driver_unregister(&exynos_drm_platform_driver); - #ifdef CONFIG_DRM_EXYNOS_IPP exynos_platform_device_ipp_unregister(); platform_driver_unregister(&ipp_driver); @@ -614,10 +655,6 @@ static void __exit exynos_drm_exit(void) platform_driver_unregister(&hdmi_driver); #endif -#ifdef CONFIG_DRM_EXYNOS_VIDI - platform_driver_unregister(&vidi_driver); -#endif - #ifdef CONFIG_DRM_EXYNOS_FIMD platform_driver_unregister(&fimd_driver); #endif @@ -629,6 +666,59 @@ static void __exit exynos_drm_exit(void) #ifdef CONFIG_DRM_EXYNOS_DP platform_driver_unregister(&dp_driver); #endif + component_master_del(&pdev->dev, &exynos_drm_ops); + return 0; +} + +static struct platform_driver exynos_drm_platform_driver = { + .probe = exynos_drm_platform_probe, + .remove = exynos_drm_platform_remove, + .driver = { + .owner = THIS_MODULE, + .name = "exynos-drm", + .pm = &exynos_drm_pm_ops, + }, +}; + +static int exynos_drm_init(void) +{ + int ret; + + exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1, + NULL, 0); + if (IS_ERR(exynos_drm_pdev)) + return PTR_ERR(exynos_drm_pdev); + +#ifdef CONFIG_DRM_EXYNOS_VIDI + ret = exynos_drm_probe_vidi(); + if (ret < 0) + goto err_unregister_pd; +#endif + + ret = platform_driver_register(&exynos_drm_platform_driver); + if (ret) + goto err_remove_vidi; + + return 0; + +err_unregister_pd: + platform_device_unregister(exynos_drm_pdev); + +err_remove_vidi: +#ifdef CONFIG_DRM_EXYNOS_VIDI + exynos_drm_remove_vidi(); +#endif + + return ret; +} + +static void exynos_drm_exit(void) +{ +#ifdef CONFIG_DRM_EXYNOS_VIDI + exynos_drm_remove_vidi(); +#endif + platform_device_unregister(exynos_drm_pdev); + platform_driver_unregister(&exynos_drm_platform_driver); } module_init(exynos_drm_init); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index ce3e6a30deaa..f4414c17ddd9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -122,7 +122,6 @@ struct exynos_drm_overlay { * Exynos DRM Display Structure. * - this structure is common to analog tv, digital tv and lcd panel. * - * @initialize: initializes the display with drm_dev * @remove: cleans up the display for removal * @mode_fixup: fix mode data comparing to hw specific display mode. * @mode_set: convert drm_display_mode to hw specific display mode and @@ -133,8 +132,6 @@ struct exynos_drm_overlay { */ struct exynos_drm_display; struct exynos_drm_display_ops { - int (*initialize)(struct exynos_drm_display *display, - struct drm_device *drm_dev); int (*create_connector)(struct exynos_drm_display *display, struct drm_encoder *encoder); void (*remove)(struct exynos_drm_display *display); @@ -172,8 +169,6 @@ struct exynos_drm_display { /* * Exynos drm manager ops * - * @initialize: initializes the manager with drm_dev - * @remove: cleans up the manager for removal * @dpms: control device power. * @mode_fixup: fix mode data before applying it * @mode_set: set the given mode to the manager @@ -189,9 +184,6 @@ struct exynos_drm_display { */ struct exynos_drm_manager; struct exynos_drm_manager_ops { - int (*initialize)(struct exynos_drm_manager *mgr, - struct drm_device *drm_dev, int pipe); - void (*remove)(struct exynos_drm_manager *mgr); void (*dpms)(struct exynos_drm_manager *mgr, int mode); bool (*mode_fixup)(struct exynos_drm_manager *mgr, const struct drm_display_mode *mode, @@ -215,6 +207,7 @@ struct exynos_drm_manager_ops { * @list: the list entry for this manager * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI. * @drm_dev: pointer to the drm device + * @crtc: crtc object. * @pipe: the pipe number for this crtc/manager * @ops: pointer to callbacks for exynos drm specific functionality * @ctx: A pointer to the manager's implementation specific context @@ -223,6 +216,7 @@ struct exynos_drm_manager { struct list_head list; enum exynos_drm_output_type type; struct drm_device *drm_dev; + struct drm_crtc *crtc; int pipe; struct exynos_drm_manager_ops *ops; void *ctx; @@ -254,6 +248,7 @@ struct drm_exynos_file_private { * otherwise default one. * @da_space_size: size of device address space. * if 0 then default value is used for it. + * @pipe: the pipe number for this crtc/manager. */ struct exynos_drm_private { struct drm_fb_helper *fb_helper; @@ -271,6 +266,8 @@ struct exynos_drm_private { unsigned long da_start; unsigned long da_space_size; + + unsigned int pipe; }; /* @@ -281,11 +278,11 @@ struct exynos_drm_private { * @drm_dev: pointer to drm_device and this pointer would be set * when sub driver calls exynos_drm_subdrv_register(). * @manager: subdrv has its own manager to control a hardware appropriately - * and we can access a hardware drawing on this manager. + * and we can access a hardware drawing on this manager. * @probe: this callback would be called by exynos drm driver after - * subdrv is registered to it. + * subdrv is registered to it. * @remove: this callback is used to release resources created - * by probe callback. + * by probe callback. * @open: this would be called with drm device file open. * @close: this would be called with drm device file close. */ @@ -302,39 +299,14 @@ struct exynos_drm_subdrv { struct drm_file *file); }; -/* - * this function calls a probe callback registered to sub driver list and - * create its own encoder and connector and then set drm_device object - * to global one. - */ -int exynos_drm_device_register(struct drm_device *dev); -/* - * this function calls a remove callback registered to sub driver list and - * destroy its own encoder and connetor. - */ -int exynos_drm_device_unregister(struct drm_device *dev); - -int exynos_drm_initialize_managers(struct drm_device *dev); -void exynos_drm_remove_managers(struct drm_device *dev); -int exynos_drm_initialize_displays(struct drm_device *dev); -void exynos_drm_remove_displays(struct drm_device *dev); - -int exynos_drm_manager_register(struct exynos_drm_manager *manager); -int exynos_drm_manager_unregister(struct exynos_drm_manager *manager); -int exynos_drm_display_register(struct exynos_drm_display *display); -int exynos_drm_display_unregister(struct exynos_drm_display *display); - -/* - * this function would be called by sub drivers such as display controller - * or hdmi driver to register this sub driver object to exynos drm driver - * and when a sub driver is registered to exynos drm driver a probe callback - * of the sub driver is called and creates its own encoder and connector. - */ + /* This function would be called by non kms drivers such as g2d and ipp. */ int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv); /* this function removes subdrv list from exynos drm driver */ int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv); +int exynos_drm_device_subdrv_probe(struct drm_device *dev); +int exynos_drm_device_subdrv_remove(struct drm_device *dev); int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file); void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file); @@ -360,18 +332,45 @@ int exynos_platform_device_ipp_register(void); void exynos_platform_device_ipp_unregister(void); #ifdef CONFIG_DRM_EXYNOS_DPI -int exynos_dpi_probe(struct device *dev); -int exynos_dpi_remove(struct device *dev); +int exynos_dpi_probe(struct drm_device *drm_dev, struct device *dev); +int exynos_dpi_remove(struct drm_device *drm_dev, struct device *dev); +struct device_node *exynos_dpi_of_find_panel_node(struct device *dev); #else -static inline int exynos_dpi_probe(struct device *dev) { return 0; } -static inline int exynos_dpi_remove(struct device *dev) { return 0; } +static inline int exynos_dpi_probe(struct drm_device *drm_dev, + struct device *dev) { return 0; } +static inline int exynos_dpi_remove(struct drm_device *drm_dev, + struct device *dev) { return 0; } +static inline struct device_node + *exynos_dpi_of_find_panel_node(struct device *dev) +{ return NULL; } #endif +/* + * this function registers exynos drm vidi platform device/driver. + */ +int exynos_drm_probe_vidi(void); + +/* + * this function unregister exynos drm vidi platform device/driver. + */ +void exynos_drm_remove_vidi(void); + +/* This function creates a encoder and a connector, and initializes them. */ +int exynos_drm_create_enc_conn(struct drm_device *dev, + struct exynos_drm_display *display); + +struct component_ops; +int exynos_drm_component_add(struct device *dev, + const struct component_ops *ops); + +void exynos_drm_component_del(struct device *dev, + const struct component_ops *ops); + +extern struct platform_driver fimd_driver; extern struct platform_driver dp_driver; extern struct platform_driver dsi_driver; -extern struct platform_driver fimd_driver; -extern struct platform_driver hdmi_driver; extern struct platform_driver mixer_driver; +extern struct platform_driver hdmi_driver; extern struct platform_driver exynos_drm_common_hdmi_driver; extern struct platform_driver vidi_driver; extern struct platform_driver g2d_driver; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 4ac438187568..03bc3dae002b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -19,6 +19,7 @@ #include <linux/irq.h> #include <linux/phy/phy.h> #include <linux/regulator/consumer.h> +#include <linux/component.h> #include <video/mipi_display.h> #include <video/videomode.h> @@ -1378,6 +1379,74 @@ end: return ret; } +static int exynos_dsi_bind(struct device *dev, struct device *master, + void *data) +{ + struct drm_device *drm_dev = data; + struct exynos_dsi *dsi; + int ret; + + ret = exynos_drm_create_enc_conn(drm_dev, &exynos_dsi_display); + if (ret) { + DRM_ERROR("Encoder create [%d] failed with %d\n", + exynos_dsi_display.type, ret); + return ret; + } + + dsi = exynos_dsi_display.ctx; + + return mipi_dsi_host_register(&dsi->dsi_host); +} + +static void exynos_dsi_unbind(struct device *dev, struct device *master, + void *data) +{ + struct exynos_dsi *dsi = exynos_dsi_display.ctx; + struct drm_encoder *encoder = dsi->encoder; + + exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF); + + mipi_dsi_host_unregister(&dsi->dsi_host); + + encoder->funcs->destroy(encoder); + drm_connector_cleanup(&dsi->connector); +} + +#if CONFIG_PM_SLEEP +static int exynos_dsi_resume(struct device *dev) +{ + struct exynos_dsi *dsi = exynos_dsi_display.ctx; + + if (dsi->state & DSIM_STATE_ENABLED) { + dsi->state &= ~DSIM_STATE_ENABLED; + exynos_dsi_enable(dsi); + } + + return 0; +} + +static int exynos_dsi_suspend(struct device *dev) +{ + struct exynos_dsi *dsi = exynos_dsi_display.ctx; + + if (dsi->state & DSIM_STATE_ENABLED) { + exynos_dsi_disable(dsi); + dsi->state |= DSIM_STATE_ENABLED; + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_dsi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume) +}; + +static const struct component_ops exynos_dsi_component_ops = { + .bind = exynos_dsi_bind, + .unbind = exynos_dsi_unbind, +}; + static int exynos_dsi_probe(struct platform_device *pdev) { struct resource *res; @@ -1455,53 +1524,16 @@ static int exynos_dsi_probe(struct platform_device *pdev) exynos_dsi_display.ctx = dsi; platform_set_drvdata(pdev, &exynos_dsi_display); - exynos_drm_display_register(&exynos_dsi_display); - return mipi_dsi_host_register(&dsi->dsi_host); + return exynos_drm_component_add(&pdev->dev, &exynos_dsi_component_ops); } static int exynos_dsi_remove(struct platform_device *pdev) { - struct exynos_dsi *dsi = exynos_dsi_display.ctx; - - exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF); - - exynos_drm_display_unregister(&exynos_dsi_display); - mipi_dsi_host_unregister(&dsi->dsi_host); - + exynos_drm_component_del(&pdev->dev, &exynos_dsi_component_ops); return 0; } -#if CONFIG_PM_SLEEP -static int exynos_dsi_resume(struct device *dev) -{ - struct exynos_dsi *dsi = exynos_dsi_display.ctx; - - if (dsi->state & DSIM_STATE_ENABLED) { - dsi->state &= ~DSIM_STATE_ENABLED; - exynos_dsi_enable(dsi); - } - - return 0; -} - -static int exynos_dsi_suspend(struct device *dev) -{ - struct exynos_dsi *dsi = exynos_dsi_display.ctx; - - if (dsi->state & DSIM_STATE_ENABLED) { - exynos_dsi_disable(dsi); - dsi->state |= DSIM_STATE_ENABLED; - } - - return 0; -} -#endif - -static const struct dev_pm_ops exynos_dsi_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume) -}; - static struct of_device_id exynos_dsi_of_match[] = { { .compatible = "samsung,exynos4210-mipi-dsi" }, { } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 99ae79e09e82..d1a282c3b062 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -19,10 +19,12 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/pm_runtime.h> +#include <linux/component.h> #include <video/of_display_timing.h> #include <video/of_videomode.h> #include <video/samsung_fimd.h> +#include <drm/drm_panel.h> #include <drm/exynos_drm.h> #include "exynos_drm_drv.h" @@ -186,12 +188,14 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr) } static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, - struct drm_device *drm_dev, int pipe) + struct drm_device *drm_dev) { struct fimd_context *ctx = mgr->ctx; + struct exynos_drm_private *priv; + priv = drm_dev->dev_private; - ctx->drm_dev = drm_dev; - ctx->pipe = pipe; + mgr->drm_dev = ctx->drm_dev = drm_dev; + mgr->pipe = ctx->pipe = priv->pipe++; /* * enable drm irq mode. @@ -832,8 +836,6 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) } static struct exynos_drm_manager_ops fimd_manager_ops = { - .initialize = fimd_mgr_initialize, - .remove = fimd_mgr_remove, .dpms = fimd_dpms, .mode_fixup = fimd_mode_fixup, .mode_set = fimd_mode_set, @@ -878,10 +880,12 @@ out: return IRQ_HANDLED; } -static int fimd_probe(struct platform_device *pdev) +static int fimd_bind(struct device *dev, struct device *master, void *data) { - struct device *dev = &pdev->dev; + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm_dev = data; struct fimd_context *ctx; + struct device_node *dn; struct resource *res; int win; int ret = -EINVAL; @@ -939,11 +943,19 @@ static int fimd_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &fimd_manager); fimd_manager.ctx = ctx; - exynos_drm_manager_register(&fimd_manager); + fimd_mgr_initialize(&fimd_manager, drm_dev); - exynos_dpi_probe(ctx->dev); + exynos_drm_crtc_create(&fimd_manager); - pm_runtime_enable(dev); + dn = exynos_dpi_of_find_panel_node(&pdev->dev); + if (dn) { + /* + * It should be called after exynos_drm_crtc_create call + * because exynos_dpi_probe call will try to find same lcd + * type of manager to setup possible_crtcs. + */ + exynos_dpi_probe(drm_dev, dev); + } for (win = 0; win < WINDOWS_NR; win++) fimd_clear_win(ctx, win); @@ -951,18 +963,59 @@ static int fimd_probe(struct platform_device *pdev) return 0; } -static int fimd_remove(struct platform_device *pdev) +static void fimd_unbind(struct device *dev, struct device *master, + void *data) { - struct exynos_drm_manager *mgr = platform_get_drvdata(pdev); + struct exynos_drm_manager *mgr = dev_get_drvdata(dev); + struct drm_crtc *crtc = mgr->crtc; + struct device_node *dn; + + fimd_dpms(mgr, DRM_MODE_DPMS_OFF); - exynos_dpi_remove(&pdev->dev); + dn = exynos_dpi_of_find_panel_node(dev); + if (dn) + exynos_dpi_remove(mgr->drm_dev, dev); - exynos_drm_manager_unregister(&fimd_manager); + fimd_mgr_remove(mgr); - fimd_dpms(mgr, DRM_MODE_DPMS_OFF); + crtc->funcs->destroy(crtc); +} + +static const struct component_ops fimd_component_ops = { + .bind = fimd_bind, + .unbind = fimd_unbind, +}; + +static int fimd_probe(struct platform_device *pdev) +{ + struct device_node *dn; + + /* Check if fimd node has port node. */ + dn = exynos_dpi_of_find_panel_node(&pdev->dev); + if (dn) { + struct drm_panel *panel; + + /* + * Do not bind if there is the port node but a drm_panel + * isn't added to panel_list yet. + * In this case, fimd_probe will be called by defered probe + * again after the drm_panel is added to panel_list. + */ + panel = of_drm_find_panel(dn); + if (!panel) + return -EPROBE_DEFER; + } + + pm_runtime_enable(&pdev->dev); + + return exynos_drm_component_add(&pdev->dev, &fimd_component_ops); +} +static int fimd_remove(struct platform_device *pdev) +{ pm_runtime_disable(&pdev->dev); + exynos_drm_component_del(&pdev->dev, &fimd_component_ops); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 3fa987df906a..2fb8705d6461 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -51,6 +51,7 @@ struct vidi_context { struct drm_crtc *crtc; struct drm_encoder *encoder; struct drm_connector connector; + struct exynos_drm_subdrv subdrv; struct vidi_win_data win_data[WINDOWS_NR]; struct edid *raw_edid; unsigned int clkdiv; @@ -294,14 +295,13 @@ static void vidi_dpms(struct exynos_drm_manager *mgr, int mode) } static int vidi_mgr_initialize(struct exynos_drm_manager *mgr, - struct drm_device *drm_dev, int pipe) + struct drm_device *drm_dev) { struct vidi_context *ctx = mgr->ctx; + struct exynos_drm_private *priv = drm_dev->dev_private; - DRM_ERROR("vidi initialize ct=%p dev=%p pipe=%d\n", ctx, drm_dev, pipe); - - ctx->drm_dev = drm_dev; - ctx->pipe = pipe; + mgr->drm_dev = ctx->drm_dev = drm_dev; + mgr->pipe = ctx->pipe = priv->pipe++; /* * enable drm irq mode. @@ -324,7 +324,6 @@ static int vidi_mgr_initialize(struct exynos_drm_manager *mgr, } static struct exynos_drm_manager_ops vidi_manager_ops = { - .initialize = vidi_mgr_initialize, .dpms = vidi_dpms, .commit = vidi_commit, .enable_vblank = vidi_enable_vblank, @@ -579,13 +578,38 @@ static struct exynos_drm_display vidi_display = { .ops = &vidi_display_ops, }; +static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev) +{ + struct exynos_drm_manager *mgr = get_vidi_mgr(dev); + struct vidi_context *ctx = mgr->ctx; + struct drm_crtc *crtc = ctx->crtc; + int ret; + + vidi_mgr_initialize(mgr, drm_dev); + + ret = exynos_drm_crtc_create(&vidi_manager); + if (ret) { + DRM_ERROR("failed to create crtc.\n"); + return ret; + } + + ret = exynos_drm_create_enc_conn(drm_dev, &vidi_display); + if (ret) { + crtc->funcs->destroy(crtc); + DRM_ERROR("failed to create encoder and connector.\n"); + return ret; + } + + return 0; +} + static int vidi_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; + struct exynos_drm_subdrv *subdrv; struct vidi_context *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -600,28 +624,43 @@ static int vidi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &vidi_manager); - ret = device_create_file(dev, &dev_attr_connection); - if (ret < 0) - DRM_INFO("failed to create connection sysfs.\n"); + subdrv = &ctx->subdrv; + subdrv->dev = &pdev->dev; + subdrv->probe = vidi_subdrv_probe; - exynos_drm_manager_register(&vidi_manager); - exynos_drm_display_register(&vidi_display); + ret = exynos_drm_subdrv_register(subdrv); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register drm vidi device\n"); + return ret; + } + + ret = device_create_file(&pdev->dev, &dev_attr_connection); + if (ret < 0) { + exynos_drm_subdrv_unregister(subdrv); + DRM_INFO("failed to create connection sysfs.\n"); + } return 0; } static int vidi_remove(struct platform_device *pdev) { - struct vidi_context *ctx = platform_get_drvdata(pdev); - - exynos_drm_display_unregister(&vidi_display); - exynos_drm_manager_unregister(&vidi_manager); + struct exynos_drm_manager *mgr = platform_get_drvdata(pdev); + struct vidi_context *ctx = mgr->ctx; + struct drm_encoder *encoder = ctx->encoder; + struct drm_crtc *crtc = mgr->crtc; if (ctx->raw_edid != (struct edid *)fake_edid_info) { kfree(ctx->raw_edid); ctx->raw_edid = NULL; + + return -EINVAL; } + crtc->funcs->destroy(crtc); + encoder->funcs->destroy(encoder); + drm_connector_cleanup(&ctx->connector); + return 0; } @@ -633,3 +672,31 @@ struct platform_driver vidi_driver = { .owner = THIS_MODULE, }, }; + +int exynos_drm_probe_vidi(void) +{ + struct platform_device *pdev; + int ret; + + pdev = platform_device_register_simple("exynos-drm-vidi", -1, NULL, 0); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + ret = platform_driver_register(&vidi_driver); + if (ret) { + platform_device_unregister(pdev); + return ret; + } + + return ret; +} + +void exynos_drm_remove_vidi(void) +{ + struct vidi_context *ctx = vidi_manager.ctx; + struct exynos_drm_subdrv *subdrv = &ctx->subdrv; + struct platform_device *pdev = to_platform_device(subdrv->dev); + + platform_driver_unregister(&vidi_driver); + platform_device_unregister(pdev); +} diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index d939dc560525..bffe24ee561d 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -36,10 +36,12 @@ #include <linux/i2c.h> #include <linux/of_gpio.h> #include <linux/hdmi.h> +#include <linux/component.h> #include <drm/exynos_drm.h> #include "exynos_drm_drv.h" +#include "exynos_drm_crtc.h" #include "exynos_mixer.h" #include <linux/gpio.h> @@ -928,16 +930,6 @@ static int hdmi_create_connector(struct exynos_drm_display *display, return 0; } -static int hdmi_initialize(struct exynos_drm_display *display, - struct drm_device *drm_dev) -{ - struct hdmi_context *hdata = display->ctx; - - hdata->drm_dev = drm_dev; - - return 0; -} - static void hdmi_mode_fixup(struct exynos_drm_display *display, struct drm_connector *connector, const struct drm_display_mode *mode, @@ -1913,7 +1905,6 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode) } static struct exynos_drm_display_ops hdmi_display_ops = { - .initialize = hdmi_initialize, .create_connector = hdmi_create_connector, .mode_fixup = hdmi_mode_fixup, .mode_set = hdmi_mode_set, @@ -2047,18 +2038,44 @@ static struct of_device_id hdmi_match_types[] = { } }; +static int hdmi_bind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *drm_dev = data; + struct hdmi_context *hdata; + + hdata = hdmi_display.ctx; + hdata->drm_dev = drm_dev; + + return exynos_drm_create_enc_conn(drm_dev, &hdmi_display); +} + +static void hdmi_unbind(struct device *dev, struct device *master, void *data) +{ + struct exynos_drm_display *display = get_hdmi_display(dev); + struct drm_encoder *encoder = display->encoder; + struct hdmi_context *hdata = display->ctx; + + encoder->funcs->destroy(encoder); + drm_connector_cleanup(&hdata->connector); +} + +static const struct component_ops hdmi_component_ops = { + .bind = hdmi_bind, + .unbind = hdmi_unbind, +}; + static int hdmi_probe(struct platform_device *pdev) { + struct device_node *ddc_node, *phy_node; + struct s5p_hdmi_platform_data *pdata; + struct hdmi_driver_data *drv_data; + const struct of_device_id *match; struct device *dev = &pdev->dev; struct hdmi_context *hdata; - struct s5p_hdmi_platform_data *pdata; struct resource *res; - const struct of_device_id *match; - struct device_node *ddc_node, *phy_node; - struct hdmi_driver_data *drv_data; int ret; - if (!dev->of_node) + if (!dev->of_node) return -ENODEV; pdata = drm_hdmi_dt_parse_pdata(dev); @@ -2149,11 +2166,9 @@ static int hdmi_probe(struct platform_device *pdev) } pm_runtime_enable(dev); - hdmi_display.ctx = hdata; - exynos_drm_display_register(&hdmi_display); - return 0; + return exynos_drm_component_add(&pdev->dev, &hdmi_component_ops); err_hdmiphy: put_device(&hdata->hdmiphy_port->dev); @@ -2164,14 +2179,14 @@ err_ddc: static int hdmi_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct exynos_drm_display *display = get_hdmi_display(dev); - struct hdmi_context *hdata = display->ctx; + struct hdmi_context *hdata = hdmi_display.ctx; put_device(&hdata->hdmiphy_port->dev); put_device(&hdata->ddc_adpt->dev); + pm_runtime_disable(&pdev->dev); + exynos_drm_component_del(&pdev->dev, &hdmi_component_ops); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index ce288818d2c0..483d7c08384a 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -31,6 +31,7 @@ #include <linux/clk.h> #include <linux/regulator/consumer.h> #include <linux/of.h> +#include <linux/component.h> #include <drm/exynos_drm.h> @@ -830,13 +831,15 @@ static int vp_resources_init(struct mixer_context *mixer_ctx) } static int mixer_initialize(struct exynos_drm_manager *mgr, - struct drm_device *drm_dev, int pipe) + struct drm_device *drm_dev) { int ret; struct mixer_context *mixer_ctx = mgr->ctx; + struct exynos_drm_private *priv; + priv = drm_dev->dev_private; - mixer_ctx->drm_dev = drm_dev; - mixer_ctx->pipe = pipe; + mgr->drm_dev = mixer_ctx->drm_dev = drm_dev; + mgr->pipe = mixer_ctx->pipe = priv->pipe++; /* acquire resources: regs, irqs, clocks */ ret = mixer_resources_init(mixer_ctx); @@ -1142,8 +1145,6 @@ int mixer_check_mode(struct drm_display_mode *mode) } static struct exynos_drm_manager_ops mixer_manager_ops = { - .initialize = mixer_initialize, - .remove = mixer_mgr_remove, .dpms = mixer_dpms, .enable_vblank = mixer_enable_vblank, .disable_vblank = mixer_disable_vblank, @@ -1200,11 +1201,13 @@ static struct of_device_id mixer_match_types[] = { } }; -static int mixer_probe(struct platform_device *pdev) +static int mixer_bind(struct device *dev, struct device *manager, void *data) { - struct device *dev = &pdev->dev; + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm_dev = data; struct mixer_context *ctx; struct mixer_drv_data *drv; + int ret; dev_info(dev, "probe start\n"); @@ -1233,20 +1236,49 @@ static int mixer_probe(struct platform_device *pdev) atomic_set(&ctx->wait_vsync_event, 0); mixer_manager.ctx = ctx; + ret = mixer_initialize(&mixer_manager, drm_dev); + if (ret) + return ret; + platform_set_drvdata(pdev, &mixer_manager); - exynos_drm_manager_register(&mixer_manager); + ret = exynos_drm_crtc_create(&mixer_manager); + if (ret) { + mixer_mgr_remove(&mixer_manager); + return ret; + } pm_runtime_enable(dev); return 0; } -static int mixer_remove(struct platform_device *pdev) +static void mixer_unbind(struct device *dev, struct device *master, void *data) { - dev_info(&pdev->dev, "remove successful\n"); + struct exynos_drm_manager *mgr = dev_get_drvdata(dev); + struct drm_crtc *crtc = mgr->crtc; + + dev_info(dev, "remove successful\n"); + + mixer_mgr_remove(mgr); - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); + crtc->funcs->destroy(crtc); +} + +static const struct component_ops mixer_component_ops = { + .bind = mixer_bind, + .unbind = mixer_unbind, +}; + +static int mixer_probe(struct platform_device *pdev) +{ + return exynos_drm_component_add(&pdev->dev, &mixer_component_ops); +} + +static int mixer_remove(struct platform_device *pdev) +{ + exynos_drm_component_del(&pdev->dev, &mixer_component_ops); return 0; } |