Files
android_kernel_samsung_sm86…/msm/dp/dp_bridge_hpd.c
Xiaowen Wu bdf97a004b 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>
2021-02-10 22:34:33 -05:00

209 lines
4.6 KiB
C

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