diff options
Diffstat (limited to 'drivers/gpu/drm/msm/edp/edp.c')
-rw-r--r-- | drivers/gpu/drm/msm/edp/edp.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/edp/edp.c b/drivers/gpu/drm/msm/edp/edp.c new file mode 100644 index 000000000000..0940e84b2821 --- /dev/null +++ b/drivers/gpu/drm/msm/edp/edp.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/of_irq.h> +#include "edp.h" + +static irqreturn_t edp_irq(int irq, void *dev_id) +{ + struct msm_edp *edp = dev_id; + + /* Process eDP irq */ + return msm_edp_ctrl_irq(edp->ctrl); +} + +static void edp_destroy(struct platform_device *pdev) +{ + struct msm_edp *edp = platform_get_drvdata(pdev); + + if (!edp) + return; + + if (edp->ctrl) { + msm_edp_ctrl_destroy(edp->ctrl); + edp->ctrl = NULL; + } + + platform_set_drvdata(pdev, NULL); +} + +/* construct eDP at bind/probe time, grab all the resources. */ +static struct msm_edp *edp_init(struct platform_device *pdev) +{ + struct msm_edp *edp = NULL; + int ret; + + if (!pdev) { + pr_err("no eDP device\n"); + ret = -ENXIO; + goto fail; + } + + edp = devm_kzalloc(&pdev->dev, sizeof(*edp), GFP_KERNEL); + if (!edp) { + ret = -ENOMEM; + goto fail; + } + DBG("eDP probed=%p", edp); + + edp->pdev = pdev; + platform_set_drvdata(pdev, edp); + + ret = msm_edp_ctrl_init(edp); + if (ret) + goto fail; + + return edp; + +fail: + if (edp) + edp_destroy(pdev); + + return ERR_PTR(ret); +} + +static int edp_bind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *drm = dev_get_drvdata(master); + struct msm_drm_private *priv = drm->dev_private; + struct msm_edp *edp; + + DBG(""); + edp = edp_init(to_platform_device(dev)); + if (IS_ERR(edp)) + return PTR_ERR(edp); + priv->edp = edp; + + return 0; +} + +static void edp_unbind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *drm = dev_get_drvdata(master); + struct msm_drm_private *priv = drm->dev_private; + + DBG(""); + if (priv->edp) { + edp_destroy(to_platform_device(dev)); + priv->edp = NULL; + } +} + +static const struct component_ops edp_ops = { + .bind = edp_bind, + .unbind = edp_unbind, +}; + +static int edp_dev_probe(struct platform_device *pdev) +{ + DBG(""); + return component_add(&pdev->dev, &edp_ops); +} + +static int edp_dev_remove(struct platform_device *pdev) +{ + DBG(""); + component_del(&pdev->dev, &edp_ops); + return 0; +} + +static const struct of_device_id dt_match[] = { + { .compatible = "qcom,mdss-edp" }, + {} +}; + +static struct platform_driver edp_driver = { + .probe = edp_dev_probe, + .remove = edp_dev_remove, + .driver = { + .name = "msm_edp", + .of_match_table = dt_match, + }, +}; + +void __init msm_edp_register(void) +{ + DBG(""); + platform_driver_register(&edp_driver); +} + +void __exit msm_edp_unregister(void) +{ + DBG(""); + platform_driver_unregister(&edp_driver); +} + +/* Second part of initialization, the drm/kms level modeset_init */ +int msm_edp_modeset_init(struct msm_edp *edp, struct drm_device *dev, + struct drm_encoder *encoder) +{ + struct platform_device *pdev = edp->pdev; + struct msm_drm_private *priv = dev->dev_private; + int ret; + + edp->encoder = encoder; + edp->dev = dev; + + edp->bridge = msm_edp_bridge_init(edp); + if (IS_ERR(edp->bridge)) { + ret = PTR_ERR(edp->bridge); + dev_err(dev->dev, "failed to create eDP bridge: %d\n", ret); + edp->bridge = NULL; + goto fail; + } + + edp->connector = msm_edp_connector_init(edp); + if (IS_ERR(edp->connector)) { + ret = PTR_ERR(edp->connector); + dev_err(dev->dev, "failed to create eDP connector: %d\n", ret); + edp->connector = NULL; + goto fail; + } + + edp->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (edp->irq < 0) { + ret = edp->irq; + dev_err(dev->dev, "failed to get IRQ: %d\n", ret); + goto fail; + } + + ret = devm_request_irq(&pdev->dev, edp->irq, + edp_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "edp_isr", edp); + if (ret < 0) { + dev_err(dev->dev, "failed to request IRQ%u: %d\n", + edp->irq, ret); + goto fail; + } + + encoder->bridge = edp->bridge; + + priv->bridges[priv->num_bridges++] = edp->bridge; + priv->connectors[priv->num_connectors++] = edp->connector; + + return 0; + +fail: + /* bridge/connector are normally destroyed by drm */ + if (edp->bridge) { + edp_bridge_destroy(edp->bridge); + edp->bridge = NULL; + } + if (edp->connector) { + edp->connector->funcs->destroy(edp->connector); + edp->connector = NULL; + } + + return ret; +} |