| /* | |
| * 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; | |
| } |