diff options
Diffstat (limited to 'drivers/soc/qcom/pmic_glink_altmode.c')
| -rw-r--r-- | drivers/soc/qcom/pmic_glink_altmode.c | 141 |
1 files changed, 82 insertions, 59 deletions
diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index d05e0d6edf49..7f11acd33323 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -5,13 +5,14 @@ */ #include <linux/auxiliary_bus.h> #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/mutex.h> #include <linux/property.h> #include <linux/soc/qcom/pdr.h> -#include <drm/drm_bridge.h> +#include <drm/bridge/aux-bridge.h> #include <linux/usb/typec_altmode.h> #include <linux/usb/typec_dp.h> @@ -20,7 +21,7 @@ #include <linux/soc/qcom/pmic_glink.h> -#define PMIC_GLINK_MAX_PORTS 2 +#define PMIC_GLINK_MAX_PORTS 3 #define USBC_SC8180X_NOTIFY_IND 0x13 #define USBC_CMD_WRITE_REQ 0x15 @@ -76,7 +77,7 @@ struct pmic_glink_altmode_port { struct work_struct work; - struct drm_bridge bridge; + struct auxiliary_device *bridge; enum typec_orientation orientation; u16 svid; @@ -114,7 +115,7 @@ static int pmic_glink_altmode_request(struct pmic_glink_altmode *altmode, u32 cm * The USBC_CMD_WRITE_REQ ack doesn't identify the request, so wait for * one ack at a time. */ - mutex_lock(&altmode->lock); + guard(mutex)(&altmode->lock); req.hdr.owner = cpu_to_le32(altmode->owner_id); req.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP); @@ -125,18 +126,16 @@ static int pmic_glink_altmode_request(struct pmic_glink_altmode *altmode, u32 cm ret = pmic_glink_send(altmode->client, &req, sizeof(req)); if (ret) { dev_err(altmode->dev, "failed to send altmode request: %#x (%d)\n", cmd, ret); - goto out_unlock; + return ret; } left = wait_for_completion_timeout(&altmode->pan_ack, 5 * HZ); if (!left) { dev_err(altmode->dev, "timeout waiting for altmode request ack for: %#x\n", cmd); - ret = -ETIMEDOUT; + return -ETIMEDOUT; } -out_unlock: - mutex_unlock(&altmode->lock); - return ret; + return 0; } static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode, @@ -160,7 +159,7 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode, ret = typec_mux_set(port->typec_mux, &port->state); if (ret) - dev_err(altmode->dev, "failed to switch mux to DP\n"); + dev_err(altmode->dev, "failed to switch mux to DP: %d\n", ret); port->retimer_state.alt = &port->dp_alt; port->retimer_state.data = &dp_data; @@ -168,7 +167,7 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode, ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); if (ret) - dev_err(altmode->dev, "failed to setup retimer to DP\n"); + dev_err(altmode->dev, "failed to setup retimer to DP: %d\n", ret); } static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode, @@ -182,7 +181,7 @@ static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode, ret = typec_mux_set(port->typec_mux, &port->state); if (ret) - dev_err(altmode->dev, "failed to switch mux to USB\n"); + dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret); port->retimer_state.alt = NULL; port->retimer_state.data = NULL; @@ -190,7 +189,7 @@ static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode, ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); if (ret) - dev_err(altmode->dev, "failed to setup retimer to USB\n"); + dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret); } static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode, @@ -204,7 +203,7 @@ static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode, ret = typec_mux_set(port->typec_mux, &port->state); if (ret) - dev_err(altmode->dev, "failed to switch mux to safe mode\n"); + dev_err(altmode->dev, "failed to switch mux to safe mode: %d\n", ret); port->retimer_state.alt = NULL; port->retimer_state.data = NULL; @@ -212,31 +211,39 @@ static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode, ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); if (ret) - dev_err(altmode->dev, "failed to setup retimer to USB\n"); + dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret); } static void pmic_glink_altmode_worker(struct work_struct *work) { struct pmic_glink_altmode_port *alt_port = work_to_altmode_port(work); struct pmic_glink_altmode *altmode = alt_port->altmode; + enum drm_connector_status conn_status; typec_switch_set(alt_port->typec_switch, alt_port->orientation); - if (alt_port->svid == USB_TYPEC_DP_SID && alt_port->mode == 0xff) - pmic_glink_altmode_safe(altmode, alt_port); - else if (alt_port->svid == USB_TYPEC_DP_SID) - pmic_glink_altmode_enable_dp(altmode, alt_port, alt_port->mode, - alt_port->hpd_state, alt_port->hpd_irq); - else - pmic_glink_altmode_enable_usb(altmode, alt_port); + if (alt_port->svid == USB_TYPEC_DP_SID) { + if (alt_port->mode == 0xff) { + pmic_glink_altmode_safe(altmode, alt_port); + } else { + pmic_glink_altmode_enable_dp(altmode, alt_port, + alt_port->mode, + alt_port->hpd_state, + alt_port->hpd_irq); + } - if (alt_port->hpd_state) - drm_bridge_hpd_notify(&alt_port->bridge, connector_status_connected); - else - drm_bridge_hpd_notify(&alt_port->bridge, connector_status_disconnected); + if (alt_port->hpd_state) + conn_status = connector_status_connected; + else + conn_status = connector_status_disconnected; + + drm_aux_hpd_bridge_notify(&alt_port->bridge->dev, conn_status); + } else { + pmic_glink_altmode_enable_usb(altmode, alt_port); + } pmic_glink_altmode_request(altmode, ALTMODE_PAN_ACK, alt_port->index); -}; +} static enum typec_orientation pmic_glink_altmode_orientation(unsigned int orientation) { @@ -285,7 +292,7 @@ static void pmic_glink_altmode_sc8180xp_notify(struct pmic_glink_altmode *altmod svid = mux == 2 ? USB_TYPEC_DP_SID : 0; - if (!altmode->ports[port].altmode) { + if (port >= ARRAY_SIZE(altmode->ports) || !altmode->ports[port].altmode) { dev_dbg(altmode->dev, "notification on undefined port %d\n", port); return; } @@ -328,7 +335,7 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, notify->payload[8]); hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, notify->payload[8]); - if (!altmode->ports[port].altmode) { + if (port >= ARRAY_SIZE(altmode->ports) || !altmode->ports[port].altmode) { dev_dbg(altmode->dev, "notification on undefined port %d\n", port); return; } @@ -365,16 +372,6 @@ static void pmic_glink_altmode_callback(const void *data, size_t len, void *priv } } -static int pmic_glink_altmode_attach(struct drm_bridge *bridge, - enum drm_bridge_attach_flags flags) -{ - return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL; -} - -static const struct drm_bridge_funcs pmic_glink_altmode_bridge_funcs = { - .attach = pmic_glink_altmode_attach, -}; - static void pmic_glink_altmode_put_retimer(void *data) { typec_retimer_put(data); @@ -397,7 +394,7 @@ static void pmic_glink_altmode_enable_worker(struct work_struct *work) ret = pmic_glink_altmode_request(altmode, ALTMODE_PAN_EN, 0); if (ret) - dev_err(altmode->dev, "failed to request altmode notifications\n"); + dev_err(altmode->dev, "failed to request altmode notifications: %d\n", ret); } static void pmic_glink_altmode_pdr_notify(void *priv, int state) @@ -444,6 +441,7 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, ret = fwnode_property_read_u32(fwnode, "reg", &port); if (ret < 0) { dev_err(dev, "missing reg property of %pOFn\n", fwnode); + fwnode_handle_put(fwnode); return ret; } @@ -454,6 +452,7 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, if (altmode->ports[port].altmode) { dev_err(dev, "multiple connector definition for port %u\n", port); + fwnode_handle_put(fwnode); return -EINVAL; } @@ -462,59 +461,83 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, alt_port->index = port; INIT_WORK(&alt_port->work, pmic_glink_altmode_worker); - alt_port->bridge.funcs = &pmic_glink_altmode_bridge_funcs; - alt_port->bridge.of_node = to_of_node(fwnode); - alt_port->bridge.ops = DRM_BRIDGE_OP_HPD; - alt_port->bridge.type = DRM_MODE_CONNECTOR_USB; - - ret = devm_drm_bridge_add(dev, &alt_port->bridge); - if (ret) - return ret; + alt_port->bridge = devm_drm_dp_hpd_bridge_alloc(dev, to_of_node(fwnode)); + if (IS_ERR(alt_port->bridge)) { + fwnode_handle_put(fwnode); + return PTR_ERR(alt_port->bridge); + } alt_port->dp_alt.svid = USB_TYPEC_DP_SID; alt_port->dp_alt.mode = USB_TYPEC_DP_MODE; alt_port->dp_alt.active = 1; alt_port->typec_mux = fwnode_typec_mux_get(fwnode); - if (IS_ERR(alt_port->typec_mux)) + if (IS_ERR(alt_port->typec_mux)) { + fwnode_handle_put(fwnode); return dev_err_probe(dev, PTR_ERR(alt_port->typec_mux), "failed to acquire mode-switch for port: %d\n", port); + } ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_mux, alt_port->typec_mux); - if (ret) + if (ret) { + fwnode_handle_put(fwnode); return ret; + } alt_port->typec_retimer = fwnode_typec_retimer_get(fwnode); - if (IS_ERR(alt_port->typec_retimer)) + if (IS_ERR(alt_port->typec_retimer)) { + fwnode_handle_put(fwnode); return dev_err_probe(dev, PTR_ERR(alt_port->typec_retimer), "failed to acquire retimer-switch for port: %d\n", port); + } ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_retimer, alt_port->typec_retimer); - if (ret) + if (ret) { + fwnode_handle_put(fwnode); return ret; + } alt_port->typec_switch = fwnode_typec_switch_get(fwnode); - if (IS_ERR(alt_port->typec_switch)) + if (IS_ERR(alt_port->typec_switch)) { + fwnode_handle_put(fwnode); return dev_err_probe(dev, PTR_ERR(alt_port->typec_switch), "failed to acquire orientation-switch for port: %d\n", port); + } ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_switch, alt_port->typec_switch); + if (ret) { + fwnode_handle_put(fwnode); + return ret; + } + } + + for (port = 0; port < ARRAY_SIZE(altmode->ports); port++) { + alt_port = &altmode->ports[port]; + if (!alt_port->bridge) + continue; + + ret = devm_drm_dp_hpd_bridge_add(dev, alt_port->bridge); if (ret) return ret; } - altmode->client = devm_pmic_glink_register_client(dev, - altmode->owner_id, - pmic_glink_altmode_callback, - pmic_glink_altmode_pdr_notify, - altmode); - return PTR_ERR_OR_ZERO(altmode->client); + altmode->client = devm_pmic_glink_client_alloc(dev, + altmode->owner_id, + pmic_glink_altmode_callback, + pmic_glink_altmode_pdr_notify, + altmode); + if (IS_ERR(altmode->client)) + return PTR_ERR(altmode->client); + + pmic_glink_client_register(altmode->client); + + return 0; } static const struct auxiliary_device_id pmic_glink_altmode_id_table[] = { |
