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:

committed by
Sudarsan Ramesh

szülő
b1120f3470
commit
bdf97a004b
@@ -95,6 +95,8 @@ msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_altmode.o \
|
||||
dp/dp_audio.o \
|
||||
dp/dp_debug.o \
|
||||
dp/dp_hpd.o \
|
||||
dp/dp_aux_bridge.o \
|
||||
dp/dp_bridge_hpd.o \
|
||||
dp/dp_gpio_hpd.o \
|
||||
dp/dp_lphw_hpd.o \
|
||||
dp/dp_display.o \
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/soc/qcom/fsa4480-i2c.h>
|
||||
@@ -26,6 +26,9 @@ struct dp_aux_private {
|
||||
struct completion comp;
|
||||
struct drm_dp_aux drm_aux;
|
||||
|
||||
struct dp_aux_bridge *aux_bridge;
|
||||
bool bridge_in_transfer;
|
||||
|
||||
bool cmd_busy;
|
||||
bool native;
|
||||
bool read;
|
||||
@@ -623,6 +626,25 @@ unlock_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t dp_aux_bridge_transfer(struct drm_dp_aux *drm_aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
struct dp_aux_private *aux = container_of(drm_aux,
|
||||
struct dp_aux_private, drm_aux);
|
||||
ssize_t size;
|
||||
|
||||
if (aux->bridge_in_transfer) {
|
||||
size = dp_aux_transfer(drm_aux, msg);
|
||||
} else {
|
||||
aux->bridge_in_transfer = true;
|
||||
size = aux->aux_bridge->transfer(aux->aux_bridge,
|
||||
drm_aux, msg);
|
||||
aux->bridge_in_transfer = false;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg)
|
||||
{
|
||||
int i = 0;
|
||||
@@ -696,6 +718,10 @@ static int dp_aux_register(struct dp_aux *dp_aux)
|
||||
goto exit;
|
||||
}
|
||||
dp_aux->drm_aux = &aux->drm_aux;
|
||||
|
||||
/* if bridge is defined, override transfer function */
|
||||
if (aux->aux_bridge && aux->aux_bridge->transfer)
|
||||
aux->drm_aux.transfer = dp_aux_bridge_transfer;
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
@@ -749,6 +775,8 @@ static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en,
|
||||
if (en) {
|
||||
atomic_set(&aux->aborted, 0);
|
||||
aux->drm_aux.transfer = dp_aux_transfer_debug;
|
||||
} else if (aux->aux_bridge && aux->aux_bridge->transfer) {
|
||||
aux->drm_aux.transfer = dp_aux_bridge_transfer;
|
||||
} else {
|
||||
aux->drm_aux.transfer = dp_aux_transfer;
|
||||
}
|
||||
@@ -803,7 +831,8 @@ end:
|
||||
}
|
||||
|
||||
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
|
||||
struct dp_parser *parser, struct device_node *aux_switch)
|
||||
struct dp_parser *parser, struct device_node *aux_switch,
|
||||
struct dp_aux_bridge *aux_bridge)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_aux_private *aux;
|
||||
@@ -832,6 +861,7 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
|
||||
aux->catalog = catalog;
|
||||
aux->cfg = parser->aux_cfg;
|
||||
aux->aux_switch_node = aux_switch;
|
||||
aux->aux_bridge = aux_bridge;
|
||||
dp_aux = &aux->dp_aux;
|
||||
aux->retry_cnt = 0;
|
||||
aux->dp_aux.reg = 0xFFFF;
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "dp_catalog.h"
|
||||
#include "drm/drm_dp_helper.h"
|
||||
#include "dp_aux_bridge.h"
|
||||
|
||||
#define DP_STATE_NOTIFICATION_SENT BIT(0)
|
||||
#define DP_STATE_TRAIN_1_STARTED BIT(1)
|
||||
@@ -57,7 +58,8 @@ struct dp_aux {
|
||||
};
|
||||
|
||||
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
|
||||
struct dp_parser *parser, struct device_node *aux_switch);
|
||||
struct dp_parser *parser, struct device_node *aux_switch,
|
||||
struct dp_aux_bridge *aux_bridge);
|
||||
void dp_aux_put(struct dp_aux *aux);
|
||||
|
||||
#endif /*__DP_AUX_H_*/
|
||||
|
70
msm/dp/dp_aux_bridge.c
Normal file
70
msm/dp/dp_aux_bridge.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014 Samsung Electronics Co., Ltd
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sub license,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dp_aux_bridge.h"
|
||||
|
||||
static DEFINE_MUTEX(dp_aux_bridge_lock);
|
||||
static LIST_HEAD(du_aux_bridge_list);
|
||||
|
||||
int dp_aux_add_bridge(struct dp_aux_bridge *bridge)
|
||||
{
|
||||
mutex_lock(&dp_aux_bridge_lock);
|
||||
list_add_tail(&bridge->head, &du_aux_bridge_list);
|
||||
mutex_unlock(&dp_aux_bridge_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
struct dp_aux_bridge *of_dp_aux_find_bridge(struct device_node *np)
|
||||
{
|
||||
struct dp_aux_bridge *bridge;
|
||||
|
||||
mutex_lock(&dp_aux_bridge_lock);
|
||||
|
||||
list_for_each_entry(bridge, &du_aux_bridge_list, head) {
|
||||
if (bridge->of_node == np) {
|
||||
mutex_unlock(&dp_aux_bridge_lock);
|
||||
return bridge;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dp_aux_bridge_lock);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
129
msm/dp/dp_aux_bridge.h
Normal file
129
msm/dp/dp_aux_bridge.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that copyright
|
||||
* notice and this permission notice appear in supporting documentation, and
|
||||
* that the name of the copyright holders not be used in advertising or
|
||||
* publicity pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no representations
|
||||
* about the suitability of this software for any purpose. It is provided "as
|
||||
* is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _DP_AUX_BRIDGE_H_
|
||||
#define _DP_AUX_BRIDGE_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
|
||||
/**
|
||||
* enum dp_aux_bridge_flag - DP aux bridge capability flag
|
||||
* DP_AUX_BRIDGE_HPD: HPD will be generated by DP aux bridge
|
||||
* DP_AUX_BRIDGE_MST: MST simulator is used by DP aux bridge
|
||||
*/
|
||||
enum dp_aux_bridge_flag {
|
||||
DP_AUX_BRIDGE_HPD = (1 << 0),
|
||||
DP_AUX_BRIDGE_MST = (1 << 1),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dp_aux_bridge - DP aux bridge control structure
|
||||
* @of_node: device node pointer to the bridge
|
||||
* @dev_priv: pointer to the bridge driver's internal context
|
||||
* @flag: flag for capability
|
||||
* @mst_ctx: pointer to mst context when DP_AUX_BRIDGE_MST is set
|
||||
* @head: to keep track of all added bridges
|
||||
*/
|
||||
struct dp_aux_bridge {
|
||||
#ifdef CONFIG_OF
|
||||
struct device_node *of_node;
|
||||
#endif
|
||||
void *dev_priv;
|
||||
u32 flag;
|
||||
void *mst_ctx;
|
||||
struct list_head head;
|
||||
|
||||
/**
|
||||
* @register_hpd:
|
||||
*
|
||||
* This callback is invoked whenever bridge is registered
|
||||
* for HPD handling
|
||||
*
|
||||
* The attach callback is optional.
|
||||
*
|
||||
* Host will pass HPD callback handle to bridge, with
|
||||
* arguments @hpd_cb(void* dev, bool hpd, bool hpd_irq):
|
||||
*
|
||||
* @dev: private handle passed in register_hpd
|
||||
* @hpd: true if HPD is high, false if HPD is low
|
||||
* @hpd_irq: true if this is a HPD irq. @hpd will be
|
||||
* ignored when hpd_irq is true.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
* Zero on success, error code on failure.
|
||||
*/
|
||||
int (*register_hpd)(struct dp_aux_bridge *bridge,
|
||||
int (*hpd_cb)(void *, bool, bool), void *dev);
|
||||
|
||||
/**
|
||||
* @transfer:
|
||||
*
|
||||
* This callback is invoked whenever dp_aux transfer
|
||||
* is called from host. Inside @transfer bridge can still
|
||||
* call @drm_aux->transfer to trigger the actual
|
||||
* DPCD/I2C transfer at host side.
|
||||
*
|
||||
* The attach callback is optional.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
* Size of the bytes transferred, error code on failure.
|
||||
*/
|
||||
ssize_t (*transfer)(struct dp_aux_bridge *bridge,
|
||||
struct drm_dp_aux *drm_aux,
|
||||
struct drm_dp_aux_msg *msg);
|
||||
};
|
||||
|
||||
/**
|
||||
* dp_aux_add_bridge - Register DP aux bridge
|
||||
* @bridge: bridge pointer
|
||||
* return: 0 if successful
|
||||
*/
|
||||
int dp_aux_add_bridge(struct dp_aux_bridge *bridge);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/**
|
||||
* of_dp_aux_find_bridge - Find registered DP aux bridge
|
||||
* @np: device node pointer to the bridge
|
||||
* return: DP aux bridge pointer, NULL if not found
|
||||
*/
|
||||
struct dp_aux_bridge *of_dp_aux_find_bridge(struct device_node *np);
|
||||
#endif
|
||||
|
||||
#endif /* _DP_AUX_BRIDGE_H_ */
|
||||
|
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);
|
||||
}
|
42
msm/dp/dp_bridge_hpd.h
Normal file
42
msm/dp/dp_bridge_hpd.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _DP_BRIDGE_HPD_H_
|
||||
#define _DP_BRIDGE_HPD_H_
|
||||
|
||||
#include "dp_hpd.h"
|
||||
|
||||
/**
|
||||
* dp_bridge_hpd_get() - configure and get the DisplayPlot HPD module data
|
||||
*
|
||||
* @dev: device instance of the caller
|
||||
* @cb: callback function for HPD response
|
||||
* @aux_bridge: handle for aux_bridge driver data
|
||||
* return: pointer to allocated gpio hpd module data
|
||||
*
|
||||
* This function sets up the gpio hpd module
|
||||
*/
|
||||
struct dp_hpd *dp_bridge_hpd_get(struct device *dev,
|
||||
struct dp_hpd_cb *cb, struct dp_aux_bridge *aux_bridge);
|
||||
|
||||
/**
|
||||
* dp_bridge_hpd_put()
|
||||
*
|
||||
* Cleans up dp_hpd instance
|
||||
*
|
||||
* @hpd: instance of gpio_hpd
|
||||
*/
|
||||
void dp_bridge_hpd_put(struct dp_hpd *hpd);
|
||||
|
||||
#endif /* _DP_BRIDGE_HPD_H_ */
|
@@ -163,6 +163,7 @@ struct dp_display_private {
|
||||
|
||||
struct platform_device *pdev;
|
||||
struct device_node *aux_switch_node;
|
||||
struct dp_aux_bridge *aux_bridge;
|
||||
struct dentry *root;
|
||||
struct completion notification_comp;
|
||||
struct completion attention_comp;
|
||||
@@ -1922,7 +1923,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
|
||||
}
|
||||
|
||||
dp->aux = dp_aux_get(dev, &dp->catalog->aux, dp->parser,
|
||||
dp->aux_switch_node);
|
||||
dp->aux_switch_node, dp->aux_bridge);
|
||||
if (IS_ERR(dp->aux)) {
|
||||
rc = PTR_ERR(dp->aux);
|
||||
DP_ERR("failed to initialize aux, rc = %d\n", rc);
|
||||
@@ -2024,7 +2025,8 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
|
||||
cb->disconnect = dp_display_usbpd_disconnect_cb;
|
||||
cb->attention = dp_display_usbpd_attention_cb;
|
||||
|
||||
dp->hpd = dp_hpd_get(dev, dp->parser, &dp->catalog->hpd, cb);
|
||||
dp->hpd = dp_hpd_get(dev, dp->parser, &dp->catalog->hpd,
|
||||
dp->aux_bridge, cb);
|
||||
if (IS_ERR(dp->hpd)) {
|
||||
rc = PTR_ERR(dp->hpd);
|
||||
DP_ERR("failed to initialize hpd, rc = %d\n", rc);
|
||||
@@ -3180,6 +3182,52 @@ end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_display_bridge_mst_attention(void *dev, bool hpd, bool hpd_irq)
|
||||
{
|
||||
struct dp_display_private *dp = dev;
|
||||
|
||||
if (!hpd_irq)
|
||||
return -EINVAL;
|
||||
|
||||
dp_display_mst_attention(dp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_init_aux_bridge(struct dp_display_private *dp)
|
||||
{
|
||||
int rc = 0;
|
||||
const char *phandle = "qcom,dp-aux-bridge";
|
||||
struct device_node *bridge_node;
|
||||
|
||||
if (!dp->pdev->dev.of_node) {
|
||||
pr_err("cannot find dev.of_node\n");
|
||||
rc = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
||||
bridge_node = of_parse_phandle(dp->pdev->dev.of_node,
|
||||
phandle, 0);
|
||||
if (!bridge_node)
|
||||
goto end;
|
||||
|
||||
dp->aux_bridge = of_dp_aux_find_bridge(bridge_node);
|
||||
if (!dp->aux_bridge) {
|
||||
pr_err("failed to find dp aux bridge\n");
|
||||
rc = -EPROBE_DEFER;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (dp->aux_bridge->register_hpd &&
|
||||
(dp->aux_bridge->flag & DP_AUX_BRIDGE_MST) &&
|
||||
!(dp->aux_bridge->flag & DP_AUX_BRIDGE_HPD))
|
||||
dp->aux_bridge->register_hpd(dp->aux_bridge,
|
||||
dp_display_bridge_mst_attention, dp);
|
||||
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_display_mst_install(struct dp_display *dp_display,
|
||||
struct dp_mst_drm_install_info *mst_install_info)
|
||||
{
|
||||
@@ -3637,6 +3685,10 @@ static int dp_display_probe(struct platform_device *pdev)
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = dp_display_init_aux_bridge(dp);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
rc = dp_display_create_workqueue(dp);
|
||||
if (rc) {
|
||||
DP_ERR("Failed to create workqueue\n");
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "dp_gpio_hpd.h"
|
||||
#include "dp_lphw_hpd.h"
|
||||
#include "dp_debug.h"
|
||||
#include "dp_bridge_hpd.h"
|
||||
|
||||
static void dp_hpd_host_init(struct dp_hpd *dp_hpd,
|
||||
struct dp_catalog_hpd *catalog)
|
||||
@@ -40,11 +41,20 @@ static void dp_hpd_isr(struct dp_hpd *dp_hpd)
|
||||
}
|
||||
|
||||
struct dp_hpd *dp_hpd_get(struct device *dev, struct dp_parser *parser,
|
||||
struct dp_catalog_hpd *catalog, struct dp_hpd_cb *cb)
|
||||
struct dp_catalog_hpd *catalog,
|
||||
struct dp_aux_bridge *aux_bridge,
|
||||
struct dp_hpd_cb *cb)
|
||||
{
|
||||
struct dp_hpd *dp_hpd;
|
||||
|
||||
if (parser->no_aux_switch && parser->lphw_hpd) {
|
||||
if (aux_bridge && (aux_bridge->flag & DP_AUX_BRIDGE_HPD)) {
|
||||
dp_hpd = dp_bridge_hpd_get(dev, cb, aux_bridge);
|
||||
if (IS_ERR(dp_hpd)) {
|
||||
pr_err("failed to get bridge hpd\n");
|
||||
return dp_hpd;
|
||||
}
|
||||
dp_hpd->type = DP_HPD_BRIDGE;
|
||||
} else if (parser->no_aux_switch && parser->lphw_hpd) {
|
||||
dp_hpd = dp_lphw_hpd_get(dev, parser, catalog, cb);
|
||||
if (IS_ERR_OR_NULL(dp_hpd)) {
|
||||
DP_ERR("failed to get lphw hpd\n");
|
||||
@@ -104,6 +114,9 @@ void dp_hpd_put(struct dp_hpd *dp_hpd)
|
||||
case DP_HPD_LPHW:
|
||||
dp_lphw_hpd_put(dp_hpd);
|
||||
break;
|
||||
case DP_HPD_BRIDGE:
|
||||
dp_bridge_hpd_put(dp_hpd);
|
||||
break;
|
||||
default:
|
||||
DP_ERR("unknown hpd type %d\n", dp_hpd->type);
|
||||
break;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_HPD_H_
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/types.h>
|
||||
#include "dp_parser.h"
|
||||
#include "dp_catalog.h"
|
||||
#include "dp_aux_bridge.h"
|
||||
|
||||
struct device;
|
||||
|
||||
@@ -29,7 +30,8 @@ enum dp_hpd_plug_orientation {
|
||||
* @DP_HPD_ALTMODE: AltMode over G-Link based HPD
|
||||
* @DP_HPD_USBPD: USB type-c based HPD
|
||||
* @DP_HPD_GPIO: GPIO based HPD
|
||||
* @DP_HPD_BUILTIN: Controller built-in HPD
|
||||
* @DP_HPD_LPHW: LPHW based HPD
|
||||
* @DP_HPD_BRIDGE: External bridge HPD
|
||||
*/
|
||||
|
||||
enum dp_hpd_type {
|
||||
@@ -37,7 +39,7 @@ enum dp_hpd_type {
|
||||
DP_HPD_USBPD,
|
||||
DP_HPD_GPIO,
|
||||
DP_HPD_LPHW,
|
||||
DP_HPD_BUILTIN,
|
||||
DP_HPD_BRIDGE,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -95,14 +97,18 @@ struct dp_hpd {
|
||||
* dp_hpd_get() - configure and get the DisplayPlot HPD module data
|
||||
*
|
||||
* @dev: device instance of the caller
|
||||
* @parser: DP parser
|
||||
* @parser: pointer to DP parser module
|
||||
* @catalog: pointer to DP catalog module
|
||||
* @aux_bridge: handle for aux_bridge driver data
|
||||
* @cb: callback function for HPD response
|
||||
* return: pointer to allocated hpd module data
|
||||
*
|
||||
* This function sets up the hpd module
|
||||
*/
|
||||
struct dp_hpd *dp_hpd_get(struct device *dev, struct dp_parser *parser,
|
||||
struct dp_catalog_hpd *catalog, struct dp_hpd_cb *cb);
|
||||
struct dp_catalog_hpd *catalog,
|
||||
struct dp_aux_bridge *aux_bridge,
|
||||
struct dp_hpd_cb *cb);
|
||||
|
||||
/**
|
||||
* dp_hpd_put()
|
||||
|
Reference in New Issue
Block a user