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