|
@@ -17,10 +17,13 @@
|
|
|
|
|
|
/* LCDC DRM driver, based on da8xx-fb */
|
|
|
|
|
|
+#include <linux/component.h>
|
|
|
+
|
|
|
#include "tilcdc_drv.h"
|
|
|
#include "tilcdc_regs.h"
|
|
|
#include "tilcdc_tfp410.h"
|
|
|
#include "tilcdc_panel.h"
|
|
|
+#include "tilcdc_external.h"
|
|
|
|
|
|
#include "drm_fb_helper.h"
|
|
|
|
|
@@ -73,13 +76,6 @@ static int modeset_init(struct drm_device *dev)
|
|
|
mod->funcs->modeset_init(mod, dev);
|
|
|
}
|
|
|
|
|
|
- if ((priv->num_encoders == 0) || (priv->num_connectors == 0)) {
|
|
|
- /* oh nos! */
|
|
|
- dev_err(dev->dev, "no encoders/connectors found\n");
|
|
|
- drm_mode_config_cleanup(dev);
|
|
|
- return -ENXIO;
|
|
|
- }
|
|
|
-
|
|
|
dev->mode_config.min_width = 0;
|
|
|
dev->mode_config.min_height = 0;
|
|
|
dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc);
|
|
@@ -114,6 +110,8 @@ static int tilcdc_unload(struct drm_device *dev)
|
|
|
{
|
|
|
struct tilcdc_drm_private *priv = dev->dev_private;
|
|
|
|
|
|
+ tilcdc_remove_external_encoders(dev);
|
|
|
+
|
|
|
drm_fbdev_cma_fini(priv->fbdev);
|
|
|
drm_kms_helper_poll_fini(dev);
|
|
|
drm_mode_config_cleanup(dev);
|
|
@@ -164,6 +162,9 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
|
|
|
|
|
|
dev->dev_private = priv;
|
|
|
|
|
|
+ priv->is_componentized =
|
|
|
+ tilcdc_get_external_components(dev->dev, NULL) > 0;
|
|
|
+
|
|
|
priv->wq = alloc_ordered_workqueue("tilcdc", 0);
|
|
|
if (!priv->wq) {
|
|
|
ret = -ENOMEM;
|
|
@@ -253,10 +254,28 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
|
|
|
goto fail_cpufreq_unregister;
|
|
|
}
|
|
|
|
|
|
+ platform_set_drvdata(pdev, dev);
|
|
|
+
|
|
|
+ if (priv->is_componentized) {
|
|
|
+ ret = component_bind_all(dev->dev, dev);
|
|
|
+ if (ret < 0)
|
|
|
+ goto fail_mode_config_cleanup;
|
|
|
+
|
|
|
+ ret = tilcdc_add_external_encoders(dev, &bpp);
|
|
|
+ if (ret < 0)
|
|
|
+ goto fail_component_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((priv->num_encoders == 0) || (priv->num_connectors == 0)) {
|
|
|
+ dev_err(dev->dev, "no encoders/connectors found\n");
|
|
|
+ ret = -ENXIO;
|
|
|
+ goto fail_external_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
ret = drm_vblank_init(dev, 1);
|
|
|
if (ret < 0) {
|
|
|
dev_err(dev->dev, "failed to initialize vblank\n");
|
|
|
- goto fail_mode_config_cleanup;
|
|
|
+ goto fail_external_cleanup;
|
|
|
}
|
|
|
|
|
|
pm_runtime_get_sync(dev->dev);
|
|
@@ -267,9 +286,6 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
|
|
|
goto fail_vblank_cleanup;
|
|
|
}
|
|
|
|
|
|
- platform_set_drvdata(pdev, dev);
|
|
|
-
|
|
|
-
|
|
|
list_for_each_entry(mod, &module_list, list) {
|
|
|
DBG("%s: preferred_bpp: %d", mod->name, mod->preferred_bpp);
|
|
|
bpp = mod->preferred_bpp;
|
|
@@ -300,6 +316,13 @@ fail_vblank_cleanup:
|
|
|
fail_mode_config_cleanup:
|
|
|
drm_mode_config_cleanup(dev);
|
|
|
|
|
|
+fail_component_cleanup:
|
|
|
+ if (priv->is_componentized)
|
|
|
+ component_unbind_all(dev->dev, dev);
|
|
|
+
|
|
|
+fail_external_cleanup:
|
|
|
+ tilcdc_remove_external_encoders(dev);
|
|
|
+
|
|
|
fail_cpufreq_unregister:
|
|
|
pm_runtime_disable(dev->dev);
|
|
|
#ifdef CONFIG_CPU_FREQ
|
|
@@ -605,20 +628,56 @@ static const struct dev_pm_ops tilcdc_pm_ops = {
|
|
|
* Platform driver:
|
|
|
*/
|
|
|
|
|
|
+static int tilcdc_bind(struct device *dev)
|
|
|
+{
|
|
|
+ return drm_platform_init(&tilcdc_driver, to_platform_device(dev));
|
|
|
+}
|
|
|
+
|
|
|
+static void tilcdc_unbind(struct device *dev)
|
|
|
+{
|
|
|
+ drm_put_dev(dev_get_drvdata(dev));
|
|
|
+}
|
|
|
+
|
|
|
+static const struct component_master_ops tilcdc_comp_ops = {
|
|
|
+ .bind = tilcdc_bind,
|
|
|
+ .unbind = tilcdc_unbind,
|
|
|
+};
|
|
|
+
|
|
|
static int tilcdc_pdev_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
+ struct component_match *match = NULL;
|
|
|
+ int ret;
|
|
|
+
|
|
|
/* bail out early if no DT data: */
|
|
|
if (!pdev->dev.of_node) {
|
|
|
dev_err(&pdev->dev, "device-tree data is missing\n");
|
|
|
return -ENXIO;
|
|
|
}
|
|
|
|
|
|
- return drm_platform_init(&tilcdc_driver, pdev);
|
|
|
+ ret = tilcdc_get_external_components(&pdev->dev, &match);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ else if (ret == 0)
|
|
|
+ return drm_platform_init(&tilcdc_driver, pdev);
|
|
|
+ else
|
|
|
+ return component_master_add_with_match(&pdev->dev,
|
|
|
+ &tilcdc_comp_ops,
|
|
|
+ match);
|
|
|
}
|
|
|
|
|
|
static int tilcdc_pdev_remove(struct platform_device *pdev)
|
|
|
{
|
|
|
- drm_put_dev(platform_get_drvdata(pdev));
|
|
|
+ struct drm_device *ddev = dev_get_drvdata(&pdev->dev);
|
|
|
+ struct tilcdc_drm_private *priv = ddev->dev_private;
|
|
|
+
|
|
|
+ /* Check if a subcomponent has already triggered the unloading. */
|
|
|
+ if (!priv)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (priv->is_componentized)
|
|
|
+ component_master_del(&pdev->dev, &tilcdc_comp_ops);
|
|
|
+ else
|
|
|
+ drm_put_dev(platform_get_drvdata(pdev));
|
|
|
|
|
|
return 0;
|
|
|
}
|