diff --git a/msm/Kbuild b/msm/Kbuild index 1fbc6b5d57..3d36b4f160 100644 --- a/msm/Kbuild +++ b/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 \ diff --git a/msm/dp/dp_aux.c b/msm/dp/dp_aux.c index ae00856bb3..61b61e3f9f 100644 --- a/msm/dp/dp_aux.c +++ b/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 @@ -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; diff --git a/msm/dp/dp_aux.h b/msm/dp/dp_aux.h index 4c8f7230ad..e4f0c6a53e 100644 --- a/msm/dp/dp_aux.h +++ b/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_*/ diff --git a/msm/dp/dp_aux_bridge.c b/msm/dp/dp_aux_bridge.c new file mode 100644 index 0000000000..593df9c590 --- /dev/null +++ b/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 + diff --git a/msm/dp/dp_aux_bridge.h b/msm/dp/dp_aux_bridge.h new file mode 100644 index 0000000000..150e66e5e7 --- /dev/null +++ b/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 +#include + +/** + * 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_ */ + diff --git a/msm/dp/dp_bridge_hpd.c b/msm/dp/dp_bridge_hpd.c new file mode 100644 index 0000000000..53f4e10ba0 --- /dev/null +++ b/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 +#include +#include +#include +#include +#include +#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); +} diff --git a/msm/dp/dp_bridge_hpd.h b/msm/dp/dp_bridge_hpd.h new file mode 100644 index 0000000000..6f640c832c --- /dev/null +++ b/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_ */ diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index d87cd89b2c..b8767201b7 100644 --- a/msm/dp/dp_display.c +++ b/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"); diff --git a/msm/dp/dp_hpd.c b/msm/dp/dp_hpd.c index 72af24fd56..ab20a3b375 100644 --- a/msm/dp/dp_hpd.c +++ b/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 @@ -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; diff --git a/msm/dp/dp_hpd.h b/msm/dp/dp_hpd.h index 8f4be5f558..2ea903be32 100644 --- a/msm/dp/dp_hpd.h +++ b/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 #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()