Просмотр исходного кода

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 <[email protected]>
Signed-off-by: Karim Henain <[email protected]>
Signed-off-by: Sudarsan Ramesh <[email protected]>
Xiaowen Wu 5 лет назад
Родитель
Сommit
bdf97a004b
10 измененных файлов с 567 добавлено и 13 удалено
  1. 2 0
      msm/Kbuild
  2. 32 2
      msm/dp/dp_aux.c
  3. 3 1
      msm/dp/dp_aux.h
  4. 70 0
      msm/dp/dp_aux_bridge.c
  5. 129 0
      msm/dp/dp_aux_bridge.h
  6. 208 0
      msm/dp/dp_bridge_hpd.c
  7. 42 0
      msm/dp/dp_bridge_hpd.h
  8. 54 2
      msm/dp/dp_display.c
  9. 16 3
      msm/dp/dp_hpd.c
  10. 11 5
      msm/dp/dp_hpd.h

+ 2 - 0
msm/Kbuild

@@ -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 \

+ 32 - 2
msm/dp/dp_aux.c

@@ -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;

+ 3 - 1
msm/dp/dp_aux.h

@@ -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 - 0
msm/dp/dp_aux_bridge.c

@@ -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 - 0
msm/dp/dp_aux_bridge.h

@@ -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 - 0
msm/dp/dp_bridge_hpd.c

@@ -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 - 0
msm/dp/dp_bridge_hpd.h

@@ -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_ */

+ 54 - 2
msm/dp/dp_display.c

@@ -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");

+ 16 - 3
msm/dp/dp_hpd.c

@@ -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;

+ 11 - 5
msm/dp/dp_hpd.h

@@ -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()