disp: msm: dp: add dp aux bridge framework support
Create the framework to support external dp aux bridge device which can handle DPCD/I2C/HPD from external. Change-Id: Iabd0998efc8bf7134d186b1751d219c00217385c Signed-off-by: Xiaowen Wu <wxiaowen@codeaurora.org> Signed-off-by: Karim Henain <khenain@codeaurora.org> Signed-off-by: Sudarsan Ramesh <sudarame@codeaurora.org>
This commit is contained in:

gecommit door
Sudarsan Ramesh

bovenliggende
b1120f3470
commit
bdf97a004b
208
msm/dp/dp_bridge_hpd.c
Normal file
208
msm/dp/dp_bridge_hpd.c
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
Verwijs in nieuw issue
Block a user