diff --git a/msm/Makefile b/msm/Makefile index 6c6acffdea96..6de95ab53ed4 100644 --- a/msm/Makefile +++ b/msm/Makefile @@ -4,7 +4,7 @@ ccflags-y += -I$(srctree)/techpack/display/msm/sde ccflags-y += -I$(srctree)/techpack/display/rotator ccflags-y += -I$(srctree)/drivers/clk/qcom/ -msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_usbpd.o \ +msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_altmode.o \ dp/dp_parser.o \ dp/dp_power.o \ dp/dp_catalog.o \ @@ -28,6 +28,7 @@ msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_usbpd.o \ dp/dp_pll_5nm.o \ msm_drm-$(CONFIG_DRM_MSM_DP_MST) += dp/dp_mst_drm.o \ +msm_drm-$(CONFIG_DRM_MSM_DP_USBPD_LEGACY) += dp/dp_usbpd.o \ msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \ sde/sde_encoder.o \ diff --git a/msm/dp/dp_altmode.c b/msm/dp/dp_altmode.c new file mode 100644 index 000000000000..2104a4956dac --- /dev/null +++ b/msm/dp/dp_altmode.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dp_altmode.h" +#include "dp_debug.h" +#include "sde_dbg.h" + + +#define ALTMODE_CONFIGURE_MASK (0x3f) +#define ALTMODE_HPD_STATE_MASK (0x40) +#define ALTMODE_HPD_IRQ_MASK (0x80) + +struct dp_altmode_private { + bool forced_disconnect; + struct device *dev; + struct dp_hpd_cb *dp_cb; + struct dp_altmode dp_altmode; + struct altmode_client *amclient; + struct altmode_client_data altmode; + const char *parent_name; + bool connected; + struct notifier_block nb; +}; + +enum dp_altmode_pin_assignment { + DPAM_HPD_OUT, + DPAM_HPD_A, + DPAM_HPD_B, + DPAM_HPD_C, + DPAM_HPD_D, + DPAM_HPD_E, + DPAM_HPD_F, +}; + +static int dp_altmode_release_ss_lanes(struct dp_altmode_private *altmode) +{ + int rc; + struct device_node *np; + struct device_node *usb_node; + struct platform_device *usb_pdev; + int timeout = 250; + + if (!altmode || !altmode->dev) { + DP_ERR("invalid args\n"); + return -EINVAL; + } + + np = altmode->dev->of_node; + + usb_node = of_parse_phandle(np, "usb-controller", 0); + if (!usb_node) { + DP_ERR("unable to get usb node\n"); + return -EINVAL; + } + + usb_pdev = of_find_device_by_node(usb_node); + if (!usb_pdev) { + of_node_put(usb_node); + DP_ERR("unable to get usb pdev\n"); + return -EINVAL; + } + + while (timeout) { + rc = dwc3_msm_release_ss_lane(&usb_pdev->dev); + if (rc != -EBUSY) + break; + + DP_WARN("USB busy, retry\n"); + + /* wait for hw recommended delay for usb */ + msleep(20); + timeout--; + } + of_node_put(usb_node); + platform_device_put(usb_pdev); + + if (rc) + DP_ERR("Error releasing SS lanes: %d\n", rc); + + return rc; +} + +static void dp_altmode_send_pan_ack(struct altmode_client *amclient, + u8 port_index) +{ + int rc; + struct altmode_pan_ack_msg ack; + + ack.cmd_type = ALTMODE_PAN_ACK; + ack.port_index = port_index; + + rc = altmode_send_data(amclient, &ack, sizeof(ack)); + if (rc < 0) { + DP_ERR("failed: %d\n", rc); + return; + } + + DP_DEBUG("port=%d\n", port_index); +} + +static int dp_altmode_notify(void *priv, void *data, size_t len) +{ + int rc = 0; + struct dp_altmode_private *altmode = + (struct dp_altmode_private *) priv; + u8 port_index, dp_data, orientation; + u8 *payload = (u8 *) data; + u8 pin, hpd_state, hpd_irq; + bool force_multi_func = altmode->dp_altmode.base.force_multi_func; + + port_index = payload[0]; + orientation = payload[1]; + dp_data = payload[8]; + + pin = dp_data & ALTMODE_CONFIGURE_MASK; + hpd_state = (dp_data & ALTMODE_HPD_STATE_MASK) >> 6; + hpd_irq = (dp_data & ALTMODE_HPD_IRQ_MASK) >> 7; + + altmode->dp_altmode.base.hpd_high = !!hpd_state; + altmode->dp_altmode.base.hpd_irq = !!hpd_irq; + altmode->dp_altmode.base.multi_func = force_multi_func ? true : + !(pin == DPAM_HPD_C || pin == DPAM_HPD_E); + + DP_DEBUG("payload=0x%x\n", dp_data); + DP_DEBUG("port_index=%d, orientation=%d, pin=%d, hpd_state=%d\n", + port_index, orientation, pin, hpd_state); + DP_DEBUG("multi_func=%d, hpd_high=%d, hpd_irq=%d\n", + altmode->dp_altmode.base.multi_func, + altmode->dp_altmode.base.hpd_high, + altmode->dp_altmode.base.hpd_irq); + DP_DEBUG("connected=%d\n", altmode->connected); + SDE_EVT32_VERBOSE(dp_data, port_index, orientation, pin, hpd_state, + altmode->dp_altmode.base.multi_func, + altmode->dp_altmode.base.hpd_high, + altmode->dp_altmode.base.hpd_irq, altmode->connected); + + if (!pin) { + /* Cable detach */ + if (altmode->connected) { + altmode->connected = false; + altmode->dp_altmode.base.alt_mode_cfg_done = false; + altmode->dp_altmode.base.orientation = ORIENTATION_NONE; + if (altmode->dp_cb && altmode->dp_cb->disconnect) + altmode->dp_cb->disconnect(altmode->dev); + } + goto ack; + } + + /* Configure */ + if (!altmode->connected) { + altmode->connected = true; + altmode->dp_altmode.base.alt_mode_cfg_done = true; + + switch (orientation) { + case 0: + orientation = ORIENTATION_CC1; + break; + case 1: + orientation = ORIENTATION_CC2; + break; + case 2: + orientation = ORIENTATION_NONE; + break; + default: + orientation = ORIENTATION_NONE; + break; + } + + altmode->dp_altmode.base.orientation = orientation; + + if (!altmode->dp_altmode.base.multi_func) { + rc = dp_altmode_release_ss_lanes(altmode); + if (rc) + goto ack; + } + + if (altmode->dp_cb && altmode->dp_cb->configure) + altmode->dp_cb->configure(altmode->dev); + goto ack; + } + + /* Attention */ + if (altmode->forced_disconnect) + goto ack; + + if (altmode->dp_cb && altmode->dp_cb->attention) + altmode->dp_cb->attention(altmode->dev); +ack: + dp_altmode_send_pan_ack(altmode->amclient, port_index); + return rc; +} + +static int dp_altmode_register(struct notifier_block *nb, unsigned long ebt, + void *pdev) +{ + int rc; + struct platform_device *altmode_pdev = pdev; + struct dp_altmode_private *altmode = container_of(nb, + struct dp_altmode_private, nb); + + altmode->amclient = altmode_register_client(&altmode_pdev->dev, + &altmode->altmode); + if (IS_ERR_OR_NULL(altmode->amclient)) { + rc = PTR_ERR(altmode->amclient); + DP_ERR("failed to register dp altmode client: %d\n", rc); + return -EINVAL; + } + + DP_DEBUG("success\n"); + + return 0; +} + +static int dp_altmode_simulate_connect(struct dp_hpd *dp_hpd, bool hpd) +{ + struct dp_altmode *dp_altmode; + struct dp_altmode_private *altmode; + + dp_altmode = container_of(dp_hpd, struct dp_altmode, base); + altmode = container_of(dp_altmode, struct dp_altmode_private, + dp_altmode); + + dp_altmode->base.hpd_high = hpd; + altmode->forced_disconnect = !hpd; + altmode->dp_altmode.base.alt_mode_cfg_done = hpd; + + if (hpd) + altmode->dp_cb->configure(altmode->dev); + else + altmode->dp_cb->disconnect(altmode->dev); + + return 0; +} + +static int dp_altmode_simulate_attention(struct dp_hpd *dp_hpd, int vdo) +{ + struct dp_altmode *dp_altmode; + struct dp_altmode_private *altmode; + struct dp_altmode *status; + + dp_altmode = container_of(dp_hpd, struct dp_altmode, base); + altmode = container_of(dp_altmode, struct dp_altmode_private, + dp_altmode); + + status = &altmode->dp_altmode; + + status->base.hpd_high = (vdo & BIT(7)) ? true : false; + status->base.hpd_irq = (vdo & BIT(8)) ? true : false; + + if (altmode->dp_cb && altmode->dp_cb->attention) + altmode->dp_cb->attention(altmode->dev); + + return 0; +} + +struct dp_hpd *dp_altmode_get(struct device *dev, struct dp_hpd_cb *cb) +{ + int rc = 0; + struct dp_altmode_private *altmode; + struct dp_altmode *dp_altmode; + const char *altmode_str = "qcom,altmode-parent"; + struct altmode_client_data client_data = { + .callback = &dp_altmode_notify, + }; + + if (!cb) { + DP_ERR("invalid cb data\n"); + return ERR_PTR(-EINVAL); + } + + altmode = kzalloc(sizeof(*altmode), GFP_KERNEL); + if (!altmode) + return ERR_PTR(-ENOMEM); + + altmode->dev = dev; + altmode->dp_cb = cb; + + client_data.name = "displayport"; + client_data.svid = USB_SID_DISPLAYPORT; + client_data.priv = altmode; + + altmode->altmode = client_data; + + dp_altmode = &altmode->dp_altmode; + dp_altmode->base.register_hpd = NULL; + dp_altmode->base.simulate_connect = dp_altmode_simulate_connect; + dp_altmode->base.simulate_attention = dp_altmode_simulate_attention; + + rc = of_property_read_string(altmode->dev->of_node, altmode_str, + &altmode->parent_name); + if (rc < 0) { + DP_ERR("No altmode parent device specified\n"); + goto error; + } + + DP_DEBUG("parent_name=%s\n", altmode->parent_name); + + altmode->nb.notifier_call = dp_altmode_register; + rc = altmode_register_notifier(altmode->parent_name, &altmode->nb); + if (rc < 0) { + DP_ERR("altmode probe notifier registration failed: %d\n", rc); + goto error; + } + + DP_DEBUG("success\n"); + + return &dp_altmode->base; +error: + kfree(altmode); + return ERR_PTR(rc); +} + +void dp_altmode_put(struct dp_hpd *dp_hpd) +{ + struct dp_altmode *dp_altmode; + struct dp_altmode_private *altmode; + + dp_altmode = container_of(dp_hpd, struct dp_altmode, base); + if (!dp_altmode) + return; + + altmode = container_of(dp_altmode, struct dp_altmode_private, + dp_altmode); + + altmode_deregister_client(altmode->amclient); + + kfree(altmode); +} diff --git a/msm/dp/dp_altmode.h b/msm/dp/dp_altmode.h new file mode 100644 index 000000000000..b8e403ed1ffc --- /dev/null +++ b/msm/dp/dp_altmode.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _DP_ALTMODE_H_ +#define _DP_ALTMODE_H_ + +#include +#include "dp_hpd.h" + +struct device; + +struct dp_altmode { + struct dp_hpd base; +}; + +struct dp_hpd *dp_altmode_get(struct device *dev, struct dp_hpd_cb *cb); + +void dp_altmode_put(struct dp_hpd *pd); +#endif /* _DP_ALTMODE_H_ */ + diff --git a/msm/dp/dp_aux.c b/msm/dp/dp_aux.c index 12eddb50ae9a..907ccdb0b087 100644 --- a/msm/dp/dp_aux.c +++ b/msm/dp/dp_aux.c @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ #include -#include #include #include "dp_aux.h" +#include "dp_hpd.h" #include "dp_debug.h" #define DP_AUX_ENUM_STR(x) #x diff --git a/msm/dp/dp_debug.c b/msm/dp/dp_debug.c index 90472b5dc35f..3ddb4d9f6989 100644 --- a/msm/dp/dp_debug.c +++ b/msm/dp/dp_debug.c @@ -14,6 +14,7 @@ #include "sde_connector.h" #include "dp_display.h" #include "dp_pll.h" +#include "dp_hpd.h" #define DEBUG_NAME "drm_dp" @@ -2151,6 +2152,15 @@ static int dp_debug_init_sim(struct dp_debug_private *debug, struct dentry *dir) return rc; } + file = debugfs_create_bool("force_multi_func", 0644, dir, + &debug->hpd->force_multi_func); + if (IS_ERR_OR_NULL(file)) { + rc = PTR_ERR(file); + DP_ERR("[%s] debugfs force_multi_func failed, rc=%d\n", + DEBUG_NAME, rc); + return rc; + } + return rc; } diff --git a/msm/dp/dp_debug.h b/msm/dp/dp_debug.h index e49e018aebd9..c5d09699a0df 100644 --- a/msm/dp/dp_debug.h +++ b/msm/dp/dp_debug.h @@ -9,7 +9,6 @@ #include "dp_panel.h" #include "dp_ctrl.h" #include "dp_link.h" -#include "dp_usbpd.h" #include "dp_aux.h" #include "dp_display.h" #include "dp_pll.h" diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 17ed7d394806..92334bde6a8e 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -11,7 +11,6 @@ #include #include #include -#include #include "sde_connector.h" @@ -1539,10 +1538,12 @@ static int dp_init_sub_modules(struct dp_display_private *dp) dp_display_get_usb_extcon(dp); - rc = dp->hpd->register_hpd(dp->hpd); - if (rc) { - DP_ERR("failed register hpd\n"); - goto error_hpd_reg; + if (dp->hpd->register_hpd) { + rc = dp->hpd->register_hpd(dp->hpd); + if (rc) { + DP_ERR("failed register hpd\n"); + goto error_hpd_reg; + } } return rc; diff --git a/msm/dp/dp_hpd.c b/msm/dp/dp_hpd.c index 3c96350ccb3e..efeba7072a6c 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-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ #include @@ -9,6 +9,7 @@ #include #include "dp_hpd.h" +#include "dp_altmode.h" #include "dp_usbpd.h" #include "dp_gpio_hpd.h" #include "dp_lphw_hpd.h" @@ -58,6 +59,14 @@ struct dp_hpd *dp_hpd_get(struct device *dev, struct dp_parser *parser, } dp_hpd->type = DP_HPD_GPIO; } else { + dp_hpd = dp_altmode_get(dev, cb); + if (!IS_ERR(dp_hpd)) { + dp_hpd->type = DP_HPD_ALTMODE; + goto config; + } + DP_WARN("dp_altmode failed (%ld), falling back to dp_usbpd\n", + PTR_ERR(dp_hpd)); + dp_hpd = dp_usbpd_get(dev, cb); if (IS_ERR(dp_hpd)) { DP_ERR("failed to get usbpd\n"); @@ -66,6 +75,7 @@ struct dp_hpd *dp_hpd_get(struct device *dev, struct dp_parser *parser, dp_hpd->type = DP_HPD_USBPD; } +config: if (!dp_hpd->host_init) dp_hpd->host_init = dp_hpd_host_init; if (!dp_hpd->host_deinit) @@ -85,6 +95,9 @@ void dp_hpd_put(struct dp_hpd *dp_hpd) case DP_HPD_USBPD: dp_usbpd_put(dp_hpd); break; + case DP_HPD_ALTMODE: + dp_altmode_put(dp_hpd); + break; case DP_HPD_GPIO: dp_gpio_hpd_put(dp_hpd); break; diff --git a/msm/dp/dp_hpd.h b/msm/dp/dp_hpd.h index 86806fbdf773..8f4be5f5581f 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-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ #ifndef _DP_HPD_H_ @@ -12,14 +12,28 @@ struct device; +/** + * enum dp_hpd_plug_orientation - plug orientation + * @ORIENTATION_NONE: Undefined or unspecified + * @ORIENTATION_CC1: CC1 + * @ORIENTATION_CC2: CC2 + */ +enum dp_hpd_plug_orientation { + ORIENTATION_NONE, + ORIENTATION_CC1, + ORIENTATION_CC2, +}; + /** * enum dp_hpd_type - dp hpd type + * @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 */ enum dp_hpd_type { + DP_HPD_ALTMODE, DP_HPD_USBPD, DP_HPD_GPIO, DP_HPD_LPHW, @@ -48,6 +62,8 @@ struct dp_hpd_cb { * @hpd_irq: Change in the status since last message * @alt_mode_cfg_done: bool to specify alt mode status * @multi_func: multi-function preferred, USBPD type only + * @peer_usb_com: downstream supports usb data communication + * @force_multi_func: force multi-function preferred * @isr: event interrupt, BUILTIN and LPHW type only * @register_hpd: register hardware callback * @host_init: source or host side setup for hpd @@ -64,6 +80,7 @@ struct dp_hpd { bool alt_mode_cfg_done; bool multi_func; bool peer_usb_comm; + bool force_multi_func; void (*isr)(struct dp_hpd *dp_hpd); int (*register_hpd)(struct dp_hpd *dp_hpd); diff --git a/msm/dp/dp_panel.h b/msm/dp/dp_panel.h index fc10758c303b..69bfcc3ab2a3 100644 --- a/msm/dp/dp_panel.h +++ b/msm/dp/dp_panel.h @@ -10,7 +10,6 @@ #include "dp_aux.h" #include "dp_link.h" -#include "dp_usbpd.h" #include "sde_edid_parser.h" #include "sde_connector.h" #include "msm_drv.h" diff --git a/msm/dp/dp_reg.h b/msm/dp/dp_reg.h index 6291bac4fea2..b367a8a070c0 100644 --- a/msm/dp/dp_reg.h +++ b/msm/dp/dp_reg.h @@ -390,8 +390,6 @@ #define QSERDES_COM_BIAS_EN_CLKBUFLR_EN (0x044) /* DP MMSS_CC registers */ -#define MMSS_DP_LINK_CMD_RCGR (0x0138) -#define MMSS_DP_LINK_CFG_RCGR (0x013C) #define MMSS_DP_PIXEL_M (0x01B4) #define MMSS_DP_PIXEL_N (0x01B8) #define MMSS_DP_PIXEL1_M (0x01CC) @@ -400,10 +398,10 @@ #define MMSS_DP_PIXEL_N_V200 (0x0134) #define MMSS_DP_PIXEL1_M_V200 (0x0148) #define MMSS_DP_PIXEL1_N_V200 (0x014C) -#define MMSS_DP_PIXEL_M_V420 (0x01B4) -#define MMSS_DP_PIXEL_N_V420 (0x01B8) -#define MMSS_DP_PIXEL1_M_V420 (0x01CC) -#define MMSS_DP_PIXEL1_N_V420 (0x01D0) +#define MMSS_DP_PIXEL_M_V420 (0x01B0) +#define MMSS_DP_PIXEL_N_V420 (0x01B4) +#define MMSS_DP_PIXEL1_M_V420 (0x01C8) +#define MMSS_DP_PIXEL1_N_V420 (0x01CC) /* DP HDCP 1.3 registers */ #define DP_HDCP_CTRL (0x0A0) @@ -431,13 +429,11 @@ #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12 (0x020) /* USB3 DP COM registers */ -#define USB3_DP_COM_RESET_OVRD_CTRL (0x1C) #define USB3_DP_COM_PHY_MODE_CTRL (0x00) #define USB3_DP_COM_SW_RESET (0x04) -#define USB3_DP_COM_TYPEC_CTRL (0x10) -#define USB3_DP_COM_SWI_CTRL (0x0c) #define USB3_DP_COM_POWER_DOWN_CTRL (0x08) - - +#define USB3_DP_COM_SWI_CTRL (0x0C) +#define USB3_DP_COM_TYPEC_CTRL (0x10) +#define USB3_DP_COM_RESET_OVRD_CTRL (0x1C) #endif /* _DP_REG_H_ */ diff --git a/msm/dp/dp_usbpd.h b/msm/dp/dp_usbpd.h index 899ac4c5960c..bd7a639e11d1 100644 --- a/msm/dp/dp_usbpd.h +++ b/msm/dp/dp_usbpd.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ #ifndef _DP_USBPD_H_ @@ -46,6 +46,7 @@ struct dp_usbpd { bool debug_en; }; +#ifdef CONFIG_DRM_MSM_DP_USBPD_LEGACY /** * dp_usbpd_get() - setup usbpd module * @@ -59,6 +60,16 @@ struct dp_usbpd { * the callback functions about the connection and status. */ struct dp_hpd *dp_usbpd_get(struct device *dev, struct dp_hpd_cb *cb); - void dp_usbpd_put(struct dp_hpd *pd); +#else +static inline struct dp_hpd *dp_usbpd_get(struct device *dev, + struct dp_hpd_cb *cb) +{ + return NULL; +} + +static inline void dp_usbpd_put(struct dp_hpd *pd) +{ +} +#endif /* CONFIG_DRM_MSM_DP_USBPD_LEGACY */ #endif /* _DP_USBPD_H_ */