123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
- */
- #include <linux/slab.h>
- #include <linux/device.h>
- #include <linux/delay.h>
- #include <linux/module.h>
- #include <linux/kthread.h>
- #include <linux/soc/qcom/altmode-glink.h>
- #include <linux/usb/dwc3-msm.h>
- #include <linux/usb/pd_vdo.h>
- #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;
- bool connected;
- u32 lanes;
- };
- 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_set_usb_dp_mode(struct dp_altmode_private *altmode)
- {
- int rc = 0;
- 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_set_dp_mode(&usb_pdev->dev, altmode->connected, altmode->lanes);
- if (rc != -EBUSY && rc != -EAGAIN)
- 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 || pin == DPAM_HPD_OUT);
- 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_EXTERNAL(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);
- rc = dp_altmode_set_usb_dp_mode(altmode);
- if (rc)
- DP_ERR("failed to clear usb dp mode, rc: %d\n", rc);
- }
- goto ack;
- }
- /* Configure */
- if (!altmode->connected) {
- altmode->connected = true;
- altmode->dp_altmode.base.alt_mode_cfg_done = true;
- altmode->forced_disconnect = false;
- altmode->lanes = 4;
- if (altmode->dp_altmode.base.multi_func)
- altmode->lanes = 2;
- DP_DEBUG("Connected=%d, lanes=%d\n",altmode->connected,altmode->lanes);
- 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;
- rc = dp_altmode_set_usb_dp_mode(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 void dp_altmode_register(void *priv)
- {
- struct dp_altmode_private *altmode = priv;
- struct altmode_client_data cd = {
- .callback = &dp_altmode_notify,
- };
- cd.name = "displayport";
- cd.svid = USB_SID_DISPLAYPORT;
- cd.priv = altmode;
- altmode->amclient = altmode_register_client(altmode->dev, &cd);
- if (IS_ERR_OR_NULL(altmode->amclient))
- DP_ERR("failed to register as client: %ld\n",
- PTR_ERR(altmode->amclient));
- else
- DP_DEBUG("success\n");
- }
- 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;
- 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;
- 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 = altmode_register_notifier(dev, dp_altmode_register, altmode);
- 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);
- altmode_deregister_notifier(altmode->dev, altmode);
- kfree(altmode);
- }
|