This patch adds super device support to bind sub drivers using device tree. For this, we should add a super device node to each machine dt files like below example, exynos-drm { crtcs = <&fimd>; connectors = <&dsi>; }; crtcs propery can declare crtc device nodes, fimd and mixer. And connectors propery can declare connector device nodes, MIPI-DSI, eDP, and HDMI. With this patch, we can resolve the probing order issue without some lists. So this patch also removes unnecessary lists and stuff related to these lists. Signed-off-by: Inki Dae <inki.dae@xxxxxxxxxxx> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> --- drivers/gpu/drm/exynos/exynos_dp_core.c | 45 ++++--- drivers/gpu/drm/exynos/exynos_drm_core.c | 204 +---------------------------- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 17 +++ drivers/gpu/drm/exynos/exynos_drm_crtc.h | 4 + drivers/gpu/drm/exynos/exynos_drm_drv.c | 209 ++++++++++++++++-------------- drivers/gpu/drm/exynos/exynos_drm_drv.h | 57 +++----- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 48 ++++--- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 48 +++++-- drivers/gpu/drm/exynos/exynos_drm_vidi.c | 61 +++++++-- drivers/gpu/drm/exynos/exynos_hdmi.c | 46 ++++--- drivers/gpu/drm/exynos/exynos_mixer.c | 54 ++++++-- 11 files changed, 374 insertions(+), 419 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index a59bca9..cb9aa58 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> @@ -969,16 +970,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; @@ -1106,7 +1097,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, }; @@ -1230,8 +1220,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; @@ -1293,21 +1285,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 component_add(&pdev->dev, &exynos_dp_ops); +} + +static int exynos_dp_remove(struct platform_device *pdev) +{ + 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 0e9e06c..a0a467f 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,196 +55,6 @@ 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) -{ - 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; - } - } - - manager->drm_dev = dev; - manager->pipe = pipe++; - - 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); -} - -int exynos_drm_initialize_displays(struct drm_device *dev) -{ - 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; - } - } - - initialized++; - - 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); -} - -int exynos_drm_device_register(struct drm_device *dev) -{ - struct exynos_drm_subdrv *subdrv, *n; - int err; - - if (!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; - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(exynos_drm_device_register); - -int exynos_drm_device_unregister(struct drm_device *dev) -{ - struct exynos_drm_subdrv *subdrv; - - if (!dev) { - WARN(1, "Unexpected drm device unregister!\n"); - return -EINVAL; - } - - list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) { - exynos_drm_subdrv_remove(dev, subdrv); - } - - 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) diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 100a561..ce1054e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -367,6 +367,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; @@ -490,3 +491,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 c27b66c..9f74b10 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_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index de010b1..9156a73 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,6 @@ #define VBLANK_OFF_DELAY 50000 -/* platform device pointer for eynos drm device. */ -static struct platform_device *exynos_drm_pdev; - static int exynos_drm_load(struct drm_device *dev, unsigned long flags) { struct exynos_drm_private *private; @@ -76,35 +74,18 @@ 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; - 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,14 +94,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; + +#ifdef CONFIG_DRM_EXYNOS_VIDI + ret = exynos_drm_probe_vidi(dev); + if (ret < 0) + goto err_unbind_all; + +#endif return 0; -err_vblank: +#ifdef CONFIG_DRM_EXYNOS_VIDI +err_unbind_all: + component_unbind_all(dev->dev, dev); +#endif +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); @@ -133,16 +125,14 @@ err_free_private: static int exynos_drm_unload(struct drm_device *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; @@ -348,27 +338,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) { @@ -423,20 +392,82 @@ 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, - }, +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int exynos_drm_add_components(struct device *dev, struct master *m) +{ + struct device_node *np = dev->of_node; + unsigned int i; + + for (i = 0;; i++) { + struct device_node *node; + int ret; + + node = of_parse_phandle(np, "crtcs", i); + if (!node) + break; + + ret = component_master_add_child(m, compare_of, node); + of_node_put(node); + + if (ret) + return ret; + } + + for (i = 0;; i++) { + struct device_node *node; + int ret; + + node = of_parse_phandle(np, "connectors", i); + if (!node) + break; + + ret = component_master_add_child(m, compare_of, node); + of_node_put(node); + + if (ret) + return ret; + } + + + return 0; +} + +static int exynos_drm_bind(struct device *dev) +{ + int ret; + + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + 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_DP ret = platform_driver_register(&dp_driver); if (ret < 0) @@ -464,12 +495,6 @@ static int __init exynos_drm_init(void) goto out_mixer; #endif -#ifdef CONFIG_DRM_EXYNOS_VIDI - ret = platform_driver_register(&vidi_driver); - if (ret < 0) - goto out_vidi; -#endif - #ifdef CONFIG_DRM_EXYNOS_G2D ret = platform_driver_register(&g2d_driver); if (ret < 0) @@ -504,23 +529,14 @@ static int __init exynos_drm_init(void) goto out_ipp_dev; #endif - ret = platform_driver_register(&exynos_drm_platform_driver); + ret = component_master_add(&pdev->dev, &exynos_drm_ops); if (ret < 0) - goto out_drm; + goto out_master; - 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 out; - } return 0; -out: - platform_driver_unregister(&exynos_drm_platform_driver); - -out_drm: +out_master: #ifdef CONFIG_DRM_EXYNOS_IPP exynos_platform_device_ipp_unregister(); out_ipp_dev: @@ -548,11 +564,6 @@ out_fimc: out_g2d: #endif -#ifdef CONFIG_DRM_EXYNOS_VIDI - platform_driver_unregister(&vidi_driver); -out_vidi: -#endif - #ifdef CONFIG_DRM_EXYNOS_HDMI platform_driver_unregister(&mixer_driver); out_mixer: @@ -575,12 +586,8 @@ out_dp: 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); @@ -607,10 +614,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 @@ -622,10 +625,28 @@ 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; } -module_init(exynos_drm_init); -module_exit(exynos_drm_exit); +static const struct of_device_id exynos_drm_dt_match[] = { + { .compatible = "samsung,exynos-drm", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, exynos_drm_dt_match); + +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, + .of_match_table = exynos_drm_dt_match, + }, +}; +module_platform_driver(exynos_drm_platform_driver); MODULE_AUTHOR("Inki Dae <inki.dae@xxxxxxxxxxx>"); MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@xxxxxxxxxxx>"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 6135135..c0b98f6 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; @@ -255,6 +249,7 @@ struct drm_exynos_file_private { * @da_space_size: size of device address space. * if 0 then default value is used for it. * @da_space_order: order to device address space. + * @pipe: the pipe number for this crtc/manager. */ struct exynos_drm_private { struct drm_fb_helper *fb_helper; @@ -273,6 +268,8 @@ struct exynos_drm_private { unsigned long da_start; unsigned long da_space_size; unsigned long da_space_order; + + unsigned int pipe; }; /* @@ -283,11 +280,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. */ @@ -304,34 +301,7 @@ 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 */ @@ -361,6 +331,15 @@ int exynos_platform_device_ipp_register(void); */ void exynos_platform_device_ipp_unregister(void); +/* + * this function registers exynos drm vidi platform device. + */ +int exynos_drm_probe_vidi(struct drm_device *dev); + +/* 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); + extern struct platform_driver dp_driver; extern struct platform_driver dsi_driver; extern struct platform_driver fimd_driver; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 6d3d994..89e8585 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> @@ -1090,16 +1091,6 @@ static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = { .best_encoder = exynos_dsi_best_encoder, }; -static int exynos_dsi_initialize(struct exynos_drm_display *display, - struct drm_device *drm_dev) -{ - struct exynos_dsi *dsi = display->ctx; - - dsi->drm_dev = drm_dev; - - return 0; -} - static int exynos_dsi_create_connector(struct exynos_drm_display *display, struct drm_encoder *encoder) { @@ -1221,7 +1212,6 @@ static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode) } static struct exynos_drm_display_ops exynos_dsi_display_ops = { - .initialize = exynos_dsi_initialize, .create_connector = exynos_dsi_create_connector, .mode_set = exynos_dsi_mode_set, .dpms = exynos_dsi_dpms @@ -1258,8 +1248,11 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi) return 0; } -static int exynos_dsi_probe(struct platform_device *pdev) +static int exynos_dsi_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_dsi *dsi; int ret; @@ -1334,22 +1327,47 @@ static int exynos_dsi_probe(struct platform_device *pdev) } exynos_dsi_display.ctx = dsi; + dsi->drm_dev = drm_dev; + + 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; + } platform_set_drvdata(pdev, &exynos_dsi_display); - exynos_drm_display_register(&exynos_dsi_display); return mipi_dsi_host_register(&dsi->dsi_host); } -static int exynos_dsi_remove(struct platform_device *pdev) +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); - exynos_drm_display_unregister(&exynos_dsi_display); mipi_dsi_host_unregister(&dsi->dsi_host); + encoder->funcs->destroy(encoder); + drm_connector_cleanup(&dsi->connector); +} + +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) +{ + return component_add(&pdev->dev, &exynos_dsi_component_ops); +} + +static int exynos_dsi_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &exynos_dsi_component_ops); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index f78fbf4..8d41288 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -19,6 +19,7 @@ #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> @@ -145,12 +146,14 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data( } 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. @@ -813,8 +816,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, @@ -859,9 +860,10 @@ 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 resource *res; int win; @@ -920,7 +922,9 @@ 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_drm_crtc_create(&fimd_manager); pm_runtime_enable(dev); @@ -930,16 +934,34 @@ 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); - - exynos_drm_manager_unregister(&fimd_manager); + struct exynos_drm_manager *mgr = dev_get_drvdata(dev); + struct drm_crtc *crtc = mgr->crtc; fimd_dpms(mgr, DRM_MODE_DPMS_OFF); - pm_runtime_disable(&pdev->dev); + fimd_mgr_remove(mgr); + pm_runtime_disable(dev); + + 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) +{ + return component_add(&pdev->dev, &fimd_component_ops); +} + +static int fimd_remove(struct platform_device *pdev) +{ + 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 7afead9..22076e5 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -65,6 +65,8 @@ struct vidi_context { int pipe; }; +static struct drm_device *drm_dev; + static const char fake_edid_info[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, @@ -294,14 +296,15 @@ 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); + DRM_DEBUG_KMS("vidi initialize ctx=%p dev=%p\n", ctx, drm_dev); - 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 +327,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, @@ -588,11 +590,14 @@ static struct exynos_drm_display vidi_display = { static int vidi_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; struct vidi_context *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + /* drm_dev shouldn't be NULL. */ + if (!drm_dev) + return -EFAULT; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -603,32 +608,40 @@ static int vidi_probe(struct platform_device *pdev) vidi_manager.ctx = ctx; vidi_display.ctx = ctx; + vidi_mgr_initialize(&vidi_manager, drm_dev); + mutex_init(&ctx->lock); platform_set_drvdata(pdev, &vidi_manager); - ret = device_create_file(dev, &dev_attr_connection); + ret = device_create_file(&pdev->dev, &dev_attr_connection); if (ret < 0) DRM_INFO("failed to create connection sysfs.\n"); - exynos_drm_manager_register(&vidi_manager); - exynos_drm_display_register(&vidi_display); + exynos_drm_crtc_create(&vidi_manager); + exynos_drm_create_enc_conn(drm_dev, &vidi_display); 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; } @@ -640,3 +653,23 @@ struct platform_driver vidi_driver = { .owner = THIS_MODULE, }, }; + +int exynos_drm_probe_vidi(struct drm_device *dev) +{ + struct platform_device *pdev; + int ret; + + drm_dev = dev; + + 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; +} diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index e6ce363..712ce2d 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> @@ -851,16 +853,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, @@ -1834,7 +1826,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, @@ -1968,9 +1959,10 @@ static struct of_device_id hdmi_match_types[] = { } }; -static int hdmi_probe(struct platform_device *pdev) +static int hdmi_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 hdmi_context *hdata; struct s5p_hdmi_platform_data *pdata; struct resource *res; @@ -2064,10 +2056,10 @@ static int hdmi_probe(struct platform_device *pdev) pm_runtime_enable(dev); + hdata->drm_dev = drm_dev; hdmi_display.ctx = hdata; - exynos_drm_display_register(&hdmi_display); - return 0; + return exynos_drm_create_enc_conn(drm_dev, &hdmi_display); err_hdmiphy: put_device(&hdata->hdmiphy_port->dev); @@ -2076,16 +2068,34 @@ err_ddc: return ret; } -static int hdmi_remove(struct platform_device *pdev) +static void hdmi_unbind(struct device *dev, struct device *master, void *data) { - struct device *dev = &pdev->dev; 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); + put_device(&hdata->hdmiphy_port->dev); put_device(&hdata->ddc_port->dev); - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); +} + +static const struct component_ops hdmi_component_ops = { + .bind = hdmi_bind, + .unbind = hdmi_unbind, +}; + +static int hdmi_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &hdmi_component_ops); +} + +static int hdmi_remove(struct platform_device *pdev) +{ + 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 ce28881..d46a262 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 component_add(&pdev->dev, &mixer_component_ops); +} + +static int mixer_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &mixer_component_ops); return 0; } -- 1.7.9.5 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel