123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- /*
- * Copyright (c) 2019-2021, 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.
- *
- */
- #define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
- #include <linux/interrupt.h>
- #include <linux/delay.h>
- #include <linux/kernel.h>
- #include <linux/kthread.h>
- #include <linux/slab.h>
- #include <linux/device.h>
- #include "dp_bridge_hpd.h"
- struct dp_bridge_hpd_private {
- struct device *dev;
- struct dp_hpd base;
- struct dp_aux_bridge *bridge;
- struct delayed_work work;
- struct dp_hpd_cb *cb;
- bool hpd;
- bool hpd_irq;
- struct mutex hpd_lock;
- };
- static int dp_bridge_hpd_connect(struct dp_bridge_hpd_private *bridge_hpd,
- bool hpd)
- {
- int rc = 0;
- if (!bridge_hpd) {
- pr_err("invalid input\n");
- rc = -EINVAL;
- goto error;
- }
- bridge_hpd->base.hpd_high = hpd;
- bridge_hpd->base.alt_mode_cfg_done = hpd;
- bridge_hpd->base.hpd_irq = false;
- if (!bridge_hpd->cb ||
- !bridge_hpd->cb->configure ||
- !bridge_hpd->cb->disconnect) {
- pr_err("invalid cb\n");
- rc = -EINVAL;
- goto error;
- }
- if (hpd)
- rc = bridge_hpd->cb->configure(bridge_hpd->dev);
- else
- rc = bridge_hpd->cb->disconnect(bridge_hpd->dev);
- error:
- return rc;
- }
- static int dp_bridge_hpd_attention(struct dp_bridge_hpd_private *bridge_hpd)
- {
- int rc = 0;
- if (!bridge_hpd) {
- pr_err("invalid input\n");
- rc = -EINVAL;
- goto error;
- }
- bridge_hpd->base.hpd_irq = true;
- if (bridge_hpd->cb && bridge_hpd->cb->attention)
- rc = bridge_hpd->cb->attention(bridge_hpd->dev);
- error:
- return rc;
- }
- static void dp_bridge_hpd_work(struct work_struct *work)
- {
- struct delayed_work *dw = to_delayed_work(work);
- struct dp_bridge_hpd_private *bridge_hpd = container_of(dw,
- struct dp_bridge_hpd_private, work);
- mutex_lock(&bridge_hpd->hpd_lock);
- if (bridge_hpd->hpd_irq)
- dp_bridge_hpd_attention(bridge_hpd);
- else
- dp_bridge_hpd_connect(bridge_hpd, bridge_hpd->hpd);
- mutex_unlock(&bridge_hpd->hpd_lock);
- }
- static int dp_bridge_hpd_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
- {
- int rc = 0;
- struct dp_bridge_hpd_private *bridge_hpd;
- if (!dp_hpd) {
- pr_err("invalid input\n");
- rc = -EINVAL;
- goto error;
- }
- bridge_hpd = container_of(dp_hpd, struct dp_bridge_hpd_private, base);
- dp_bridge_hpd_connect(bridge_hpd, hpd);
- error:
- return rc;
- }
- static int dp_bridge_hpd_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
- {
- int rc = 0;
- struct dp_bridge_hpd_private *bridge_hpd;
- if (!dp_hpd) {
- pr_err("invalid input\n");
- rc = -EINVAL;
- goto error;
- }
- bridge_hpd = container_of(dp_hpd, struct dp_bridge_hpd_private, base);
- dp_bridge_hpd_attention(bridge_hpd);
- error:
- return rc;
- }
- static int dp_bridge_hpd_cb(void *dp_hpd, bool hpd, bool hpd_irq)
- {
- struct dp_bridge_hpd_private *bridge_hpd = dp_hpd;
- mutex_lock(&bridge_hpd->hpd_lock);
- bridge_hpd->hpd = hpd;
- bridge_hpd->hpd_irq = hpd_irq;
- queue_delayed_work(system_wq, &bridge_hpd->work, 0);
- mutex_unlock(&bridge_hpd->hpd_lock);
- return 0;
- }
- static int dp_bridge_hpd_register(struct dp_hpd *dp_hpd)
- {
- struct dp_bridge_hpd_private *bridge_hpd;
- if (!dp_hpd)
- return -EINVAL;
- bridge_hpd = container_of(dp_hpd, struct dp_bridge_hpd_private, base);
- return bridge_hpd->bridge->register_hpd(bridge_hpd->bridge,
- dp_bridge_hpd_cb, bridge_hpd);
- }
- struct dp_hpd *dp_bridge_hpd_get(struct device *dev,
- struct dp_hpd_cb *cb, struct dp_aux_bridge *aux_bridge)
- {
- int rc = 0;
- struct dp_bridge_hpd_private *bridge_hpd;
- if (!dev || !cb) {
- pr_err("invalid device\n");
- rc = -EINVAL;
- goto error;
- }
- bridge_hpd = devm_kzalloc(dev, sizeof(*bridge_hpd), GFP_KERNEL);
- if (!bridge_hpd) {
- rc = -ENOMEM;
- goto error;
- }
- bridge_hpd->dev = dev;
- bridge_hpd->cb = cb;
- bridge_hpd->bridge = aux_bridge;
- mutex_init(&bridge_hpd->hpd_lock);
- INIT_DELAYED_WORK(&bridge_hpd->work, dp_bridge_hpd_work);
- bridge_hpd->base.simulate_connect = dp_bridge_hpd_simulate_connect;
- bridge_hpd->base.simulate_attention = dp_bridge_hpd_simulate_attention;
- bridge_hpd->base.register_hpd = dp_bridge_hpd_register;
- return &bridge_hpd->base;
- error:
- return ERR_PTR(rc);
- }
- void dp_bridge_hpd_put(struct dp_hpd *dp_hpd)
- {
- struct dp_bridge_hpd_private *bridge_hpd;
- if (!dp_hpd)
- return;
- bridge_hpd = container_of(dp_hpd, struct dp_bridge_hpd_private, base);
- devm_kfree(bridge_hpd->dev, bridge_hpd);
- }
|