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:
Xiaowen Wu
2019-10-29 17:00:49 -04:00
committed by Sudarsan Ramesh
parent b1120f3470
commit bdf97a004b
10 changed files with 567 additions and 13 deletions

View File

@@ -95,6 +95,8 @@ msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_altmode.o \
dp/dp_audio.o \ dp/dp_audio.o \
dp/dp_debug.o \ dp/dp_debug.o \
dp/dp_hpd.o \ dp/dp_hpd.o \
dp/dp_aux_bridge.o \
dp/dp_bridge_hpd.o \
dp/dp_gpio_hpd.o \ dp/dp_gpio_hpd.o \
dp/dp_lphw_hpd.o \ dp/dp_lphw_hpd.o \
dp/dp_display.o \ dp/dp_display.o \

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only // 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> #include <linux/soc/qcom/fsa4480-i2c.h>
@@ -26,6 +26,9 @@ struct dp_aux_private {
struct completion comp; struct completion comp;
struct drm_dp_aux drm_aux; struct drm_dp_aux drm_aux;
struct dp_aux_bridge *aux_bridge;
bool bridge_in_transfer;
bool cmd_busy; bool cmd_busy;
bool native; bool native;
bool read; bool read;
@@ -623,6 +626,25 @@ unlock_exit:
return ret; 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) static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg)
{ {
int i = 0; int i = 0;
@@ -696,6 +718,10 @@ static int dp_aux_register(struct dp_aux *dp_aux)
goto exit; goto exit;
} }
dp_aux->drm_aux = &aux->drm_aux; 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: exit:
return ret; return ret;
} }
@@ -749,6 +775,8 @@ static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en,
if (en) { if (en) {
atomic_set(&aux->aborted, 0); atomic_set(&aux->aborted, 0);
aux->drm_aux.transfer = dp_aux_transfer_debug; 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 { } else {
aux->drm_aux.transfer = dp_aux_transfer; 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_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; int rc = 0;
struct dp_aux_private *aux; 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->catalog = catalog;
aux->cfg = parser->aux_cfg; aux->cfg = parser->aux_cfg;
aux->aux_switch_node = aux_switch; aux->aux_switch_node = aux_switch;
aux->aux_bridge = aux_bridge;
dp_aux = &aux->dp_aux; dp_aux = &aux->dp_aux;
aux->retry_cnt = 0; aux->retry_cnt = 0;
aux->dp_aux.reg = 0xFFFF; aux->dp_aux.reg = 0xFFFF;

View File

@@ -8,6 +8,7 @@
#include "dp_catalog.h" #include "dp_catalog.h"
#include "drm/drm_dp_helper.h" #include "drm/drm_dp_helper.h"
#include "dp_aux_bridge.h"
#define DP_STATE_NOTIFICATION_SENT BIT(0) #define DP_STATE_NOTIFICATION_SENT BIT(0)
#define DP_STATE_TRAIN_1_STARTED BIT(1) #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_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); void dp_aux_put(struct dp_aux *aux);
#endif /*__DP_AUX_H_*/ #endif /*__DP_AUX_H_*/

70
msm/dp/dp_aux_bridge.c Normal file
View 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
View 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
View 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
View 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_ */

View File

@@ -163,6 +163,7 @@ struct dp_display_private {
struct platform_device *pdev; struct platform_device *pdev;
struct device_node *aux_switch_node; struct device_node *aux_switch_node;
struct dp_aux_bridge *aux_bridge;
struct dentry *root; struct dentry *root;
struct completion notification_comp; struct completion notification_comp;
struct completion attention_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 = 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)) { if (IS_ERR(dp->aux)) {
rc = PTR_ERR(dp->aux); rc = PTR_ERR(dp->aux);
DP_ERR("failed to initialize aux, rc = %d\n", rc); 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->disconnect = dp_display_usbpd_disconnect_cb;
cb->attention = dp_display_usbpd_attention_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)) { if (IS_ERR(dp->hpd)) {
rc = PTR_ERR(dp->hpd); rc = PTR_ERR(dp->hpd);
DP_ERR("failed to initialize hpd, rc = %d\n", rc); DP_ERR("failed to initialize hpd, rc = %d\n", rc);
@@ -3180,6 +3182,52 @@ end:
return rc; 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, static int dp_display_mst_install(struct dp_display *dp_display,
struct dp_mst_drm_install_info *mst_install_info) struct dp_mst_drm_install_info *mst_install_info)
{ {
@@ -3637,6 +3685,10 @@ static int dp_display_probe(struct platform_device *pdev)
goto error; goto error;
} }
rc = dp_display_init_aux_bridge(dp);
if (rc)
goto error;
rc = dp_display_create_workqueue(dp); rc = dp_display_create_workqueue(dp);
if (rc) { if (rc) {
DP_ERR("Failed to create workqueue\n"); DP_ERR("Failed to create workqueue\n");

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only // 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> #include <linux/slab.h>
@@ -14,6 +14,7 @@
#include "dp_gpio_hpd.h" #include "dp_gpio_hpd.h"
#include "dp_lphw_hpd.h" #include "dp_lphw_hpd.h"
#include "dp_debug.h" #include "dp_debug.h"
#include "dp_bridge_hpd.h"
static void dp_hpd_host_init(struct dp_hpd *dp_hpd, static void dp_hpd_host_init(struct dp_hpd *dp_hpd,
struct dp_catalog_hpd *catalog) 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_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; 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); dp_hpd = dp_lphw_hpd_get(dev, parser, catalog, cb);
if (IS_ERR_OR_NULL(dp_hpd)) { if (IS_ERR_OR_NULL(dp_hpd)) {
DP_ERR("failed to get lphw hpd\n"); 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: case DP_HPD_LPHW:
dp_lphw_hpd_put(dp_hpd); dp_lphw_hpd_put(dp_hpd);
break; break;
case DP_HPD_BRIDGE:
dp_bridge_hpd_put(dp_hpd);
break;
default: default:
DP_ERR("unknown hpd type %d\n", dp_hpd->type); DP_ERR("unknown hpd type %d\n", dp_hpd->type);
break; break;

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */ /* 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_ #ifndef _DP_HPD_H_
@@ -9,6 +9,7 @@
#include <linux/types.h> #include <linux/types.h>
#include "dp_parser.h" #include "dp_parser.h"
#include "dp_catalog.h" #include "dp_catalog.h"
#include "dp_aux_bridge.h"
struct device; struct device;
@@ -29,7 +30,8 @@ enum dp_hpd_plug_orientation {
* @DP_HPD_ALTMODE: AltMode over G-Link based HPD * @DP_HPD_ALTMODE: AltMode over G-Link based HPD
* @DP_HPD_USBPD: USB type-c based HPD * @DP_HPD_USBPD: USB type-c based HPD
* @DP_HPD_GPIO: GPIO 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 { enum dp_hpd_type {
@@ -37,7 +39,7 @@ enum dp_hpd_type {
DP_HPD_USBPD, DP_HPD_USBPD,
DP_HPD_GPIO, DP_HPD_GPIO,
DP_HPD_LPHW, 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 * dp_hpd_get() - configure and get the DisplayPlot HPD module data
* *
* @dev: device instance of the caller * @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 * @cb: callback function for HPD response
* return: pointer to allocated hpd module data * return: pointer to allocated hpd module data
* *
* This function sets up the hpd module * This function sets up the hpd module
*/ */
struct dp_hpd *dp_hpd_get(struct device *dev, struct dp_parser *parser, 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() * dp_hpd_put()