Received: by 2002:a05:6a10:16a7:0:0:0:0 with SMTP id gp39csp133477pxb; Wed, 11 Nov 2020 22:46:42 -0800 (PST) X-Google-Smtp-Source: ABdhPJz08UpdByWXJawFdY8KBPWxzRDJSSiUT1/v3ocBQaaRAVhV7o31ot7XYCUzwiFCkeNUWwqg X-Received: by 2002:a50:fa92:: with SMTP id w18mr3524823edr.44.1605163602006; Wed, 11 Nov 2020 22:46:42 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1605163602; cv=none; d=google.com; s=arc-20160816; b=PcHwEQgSTnHn7eb4wJ7SNsidhm9bGhafzfKc6JRQbqtX1YmhYpH/63mqw7UkLxjt+3 7a68uZemiAf0HNgHtyP3pM74E3lwfC2L/FAkE44Mt/m5hYJ6VF6vzHbMb//DTKC954Cd HKZ5DJ7+JxwyC9jB2HZL5N3EnGcqYch76084JVLs0tayt1b9pNwdKW2+ElH/tI4hqCoa J0UsFKFGKW705fhe/V0hHobpKKCes7qclLjfBLox1dWvNMs3OZGRmUZpETb88IQ65XV2 ClrTvSBz4U3NO2BCYsavgGscuDwsoCMy2H0jP/5cdehJCGM3cp9fLtKoYM5htcis+d6/ 2+wQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:to:content-transfer-encoding:mime-version :message-id:date:subject:cc:from:dkim-signature; bh=cRBmLI6FMajTvMm2Htv9opYdYie4XEp5GifCJuoIgiA=; b=e1CUcpLIt5tvf1bGbx3u29c9qxXe+wxc9JFYa7HAHddIEtRzQP7QV9H/vTHWkyPfSu X1UXJddnubrYNkCo4fnPtWi3Bq2UGjYa12ZyqJVdRo3UAvm1joa24TWVf5hQL5NDNYTH IUeV89lThd78VyITi5A1wIp7zyv+vVdWETcg51jJmNVX5uT4+A85128fcfmkN3Js+Q0N KMlIlnP/WnQy8+37nx37F8F5WAgKx0yoA1VUjwkptOFxp0ErADv+MU6QGCxnEIKmcmxP gy5CHR1QIQr7sDklBo+hfZ9iqbJxEXLdmF6bKKnw4IGdLXQhi3ovpZzs0Pv2lrXpvrAs U+YA== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@chromium.org header.s=google header.b=LKDBIu4s; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=chromium.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id 5si3492449edh.141.2020.11.11.22.46.19; Wed, 11 Nov 2020 22:46:41 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=fail header.i=@chromium.org header.s=google header.b=LKDBIu4s; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726731AbgKLGlp (ORCPT + 99 others); Thu, 12 Nov 2020 01:41:45 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43062 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725981AbgKLGlo (ORCPT ); Thu, 12 Nov 2020 01:41:44 -0500 Received: from mail-pl1-x641.google.com (mail-pl1-x641.google.com [IPv6:2607:f8b0:4864:20::641]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AD14FC0613D1 for ; Wed, 11 Nov 2020 22:41:43 -0800 (PST) Received: by mail-pl1-x641.google.com with SMTP id b3so2260800pls.11 for ; Wed, 11 Nov 2020 22:41:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=cRBmLI6FMajTvMm2Htv9opYdYie4XEp5GifCJuoIgiA=; b=LKDBIu4sC3ynBG9hmtGI3W7nmsadRABYWGNKgoQzXJJAGqhadvqK6gGdQgecCfLh40 1x//8mmpzNNimzTjno9DfWOOdpWW+l1/zlbiTVlWc42rIKMmqY9/4KmpkJbARf3wpIYp U5yU9J3IEHoRe4WMnWAIQcOaTCi3NqV+0Xn3I= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=cRBmLI6FMajTvMm2Htv9opYdYie4XEp5GifCJuoIgiA=; b=MsYbqx93csYLWRxXo2qD4UTWmQUR6JF1COdQGA2ZEJ1lyon51n4c29ZVKx/1MDxpj3 M/rkIq/fQxoyVmd9/dAhlt32VVEjfBIDeLTEgxqluN2uTeWw0nr8CMQIRJ5u6eLBO6+J dO+XSambc+J365MPog+16ZvuWv1SgLUCKMJsX0I7opM17pG3DWeRBrzPP7Qy0nTvnvpB GIBgbGQGiwiEvp+qKTW2oAqXMet6eObm/MMMeaoitbuMMU/4Dll4iAYkLna2zAQoxWpx QSOP8uPyYdABIlgmdSU+M5Cl1uPBwtopU/iCtmiOnwSroY3lmnk0sqW16H5GUhJ9BJkN nKhA== X-Gm-Message-State: AOAM530+DXNIHdvdn9Z4SJT5V+J/qngYv2xVHH9+2IyvSYvaMtHNLFtA UeWjMKfhbGRvc3Q7e75r9Hgrng== X-Received: by 2002:a17:902:7c86:b029:d5:f680:f756 with SMTP id y6-20020a1709027c86b02900d5f680f756mr14450696pll.39.1605163303208; Wed, 11 Nov 2020 22:41:43 -0800 (PST) Received: from kafuu-chino.c.googlers.com.com (105.219.229.35.bc.googleusercontent.com. [35.229.219.105]) by smtp.googlemail.com with ESMTPSA id w22sm4832251pge.25.2020.11.11.22.41.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 11 Nov 2020 22:41:42 -0800 (PST) From: Pi-Hsun Shih Cc: Pi-Hsun Shih , Nicolas Boichat , Prashant Malani , Andrzej Hajda , Neil Armstrong , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , David Airlie , Daniel Vetter , Xin Ji , Sam Ravnborg , dri-devel@lists.freedesktop.org (open list:DRM DRIVERS), linux-kernel@vger.kernel.org (open list) Subject: [PATCH] drm/bridge: anx7625: Add anx7625 port switching. Date: Thu, 12 Nov 2020 14:40:40 +0800 Message-Id: <20201112064051.3716968-1-pihsun@chromium.org> X-Mailer: git-send-email 2.29.2.299.gdc1121823c-goog MIME-Version: 1.0 Content-Transfer-Encoding: 8bit To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org When output 2 lanes DP data, anx7625 can output to either TX1/RX1 or TX2/RX2. In typical usage, these two TX/RX pairs corresponds to two orientations of typec. On some board one anx7625 is used as DPI to DP converter for two typec ports. In this case, the TX1/RX1 and TX2/RX2 are connected to two usb muxes, which mux the DP data with the rest of the USB3 data, and connects to the two typec ports. This patch adds option for anx7625 to acts as a usb typec switch and switch output lanes based on the typec orientation, or acts as two usb typec mux and switch output lanes depending on whether the two ports currently has DP enabled. Signed-off-by: Pi-Hsun Shih ==================================================================== This is an attempt to use typec framework with how we're using anx7625 on Chrome OS asurada board. An example of the dts for the two ports case can be found at https://crrev.com/c/2507199/6 Sending this as a RFC patch since I'm not sure about the best approach here. Should the logic of switching output lanes depends on ports be coupled inside anx7625 driver, or in another driver, or is there any existing way to accomplish this? --- drivers/gpu/drm/bridge/analogix/anx7625.c | 135 ++++++++++++++++++++++ drivers/gpu/drm/bridge/analogix/anx7625.h | 24 ++++ 2 files changed, 159 insertions(+) diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 65cc05982f82..75f35a197196 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include #include #include @@ -1224,6 +1227,122 @@ static irqreturn_t anx7625_intr_hpd_isr(int irq, void *data) return IRQ_HANDLED; } +static void anx7625_set_crosspoint_switch(struct anx7625_data *ctx, + enum typec_orientation orientation) +{ + if (orientation == TYPEC_ORIENTATION_NORMAL) { + anx7625_reg_write(ctx, ctx->i2c.tcpc_client, TCPC_SWITCH_0, + SW_SEL1_SSRX_B10_B11 | SW_SEL1_ML0_A10_A11); + anx7625_reg_write(ctx, ctx->i2c.tcpc_client, TCPC_SWITCH_1, + SW_SEL2_SSTX_A2_A3 | SW_SEL2_ML1_B2_B3); + } else if (orientation == TYPEC_ORIENTATION_REVERSE) { + anx7625_reg_write(ctx, ctx->i2c.tcpc_client, TCPC_SWITCH_0, + SW_SEL1_SSRX_A10_A11 | SW_SEL1_ML0_B10_B11); + anx7625_reg_write(ctx, ctx->i2c.tcpc_client, TCPC_SWITCH_1, + SW_SEL2_SSTX_B2_B3 | SW_SEL2_ML1_A2_A3); + } +} + +static int anx7625_usb_set_orientation(struct typec_switch *sw, + enum typec_orientation orientation) +{ + struct anx7625_data *ctx = typec_switch_get_drvdata(sw); + + anx7625_set_crosspoint_switch(ctx, orientation); + return 0; +} + +static int anx7625_register_usb(struct device *device, + struct anx7625_data *ctx) +{ + struct typec_switch_desc sw_desc = { }; + struct fwnode_handle *fwnode = of_fwnode_handle(device->of_node); + + sw_desc.fwnode = fwnode; + sw_desc.drvdata = ctx; + sw_desc.name = fwnode_get_name(fwnode); + sw_desc.set = anx7625_usb_set_orientation; + + ctx->typec_sw = typec_switch_register(device, &sw_desc); + if (IS_ERR(ctx->typec_sw)) + return PTR_ERR(ctx->typec_sw); + + return 0; +} + +static void anx7625_usb_two_ports_update(struct anx7625_data *ctx) +{ + if (ctx->typec_ports[0].has_dp && ctx->typec_ports[1].has_dp) + // Both ports available, do nothing to retain the current one. + return; + else if (ctx->typec_ports[0].has_dp) + anx7625_set_crosspoint_switch(ctx, TYPEC_ORIENTATION_NORMAL); + else if (ctx->typec_ports[1].has_dp) + anx7625_set_crosspoint_switch(ctx, TYPEC_ORIENTATION_REVERSE); +} + +static int anx7625_usb_mux_set(struct typec_mux *mux, + struct typec_mux_state *state) +{ + struct anx7625_port_data *data = typec_mux_get_drvdata(mux); + + if (state->alt && state->alt->svid == USB_TYPEC_DP_SID && + state->alt->mode == USB_TYPEC_DP_MODE) + data->has_dp = true; + else + data->has_dp = false; + + anx7625_usb_two_ports_update(data->ctx); + return 0; +} + +static int anx7625_register_usb_two_ports(struct device *device, + struct anx7625_data *ctx) +{ + struct typec_mux_desc mux_desc = { }; + struct fwnode_handle *fwnode; + struct anx7625_port_data *port_data; + u32 port_num; + int ret; + + device_for_each_child_node(device, fwnode) { + if (fwnode_property_read_u32(fwnode, "reg", &port_num)) + continue; + + if (port_num >= 2) { + DRM_DEV_ERROR(device, "reg too large for ports."); + continue; + } + + port_data = &ctx->typec_ports[port_num]; + + port_data->ctx = ctx; + mux_desc.fwnode = fwnode; + mux_desc.drvdata = port_data; + mux_desc.name = fwnode_get_name(fwnode); + mux_desc.set = anx7625_usb_mux_set; + + port_data->typec_mux = + typec_mux_register(device, &mux_desc); + if (IS_ERR(port_data->typec_mux)) { + ret = PTR_ERR(port_data->typec_mux); + DRM_DEV_ERROR(device, + "mux register for port %d failed: %d", + port_num, ret); + goto err; + } + } + + return 0; + +err: + for (port_num = 0; port_num < 2; port_num++) { + typec_mux_unregister(ctx->typec_ports[port_num].typec_mux); + ctx->typec_ports[port_num].typec_mux = NULL; + } + return ret; +} + static int anx7625_parse_dt(struct device *dev, struct anx7625_platform_data *pdata) { @@ -1239,6 +1358,9 @@ static int anx7625_parse_dt(struct device *dev, DRM_DEV_DEBUG_DRIVER(dev, "found dsi host node.\n"); + pdata->tx_rx_to_two_ports = + of_property_read_bool(dev->of_node, "anx,tx-rx-to-two-ports"); + ret = drm_of_find_panel_or_bridge(np, 1, 0, &panel, NULL); if (ret < 0) { if (ret == -ENODEV) @@ -1784,6 +1906,11 @@ static int anx7625_i2c_probe(struct i2c_client *client, if (platform->pdata.intp_irq) queue_work(platform->workqueue, &platform->work); + if (platform->pdata.tx_rx_to_two_ports) + anx7625_register_usb_two_ports(dev, platform); + else + anx7625_register_usb(dev, platform); + platform->bridge.funcs = &anx7625_bridge_funcs; platform->bridge.of_node = client->dev.of_node; platform->bridge.ops = DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; @@ -1807,9 +1934,17 @@ static int anx7625_i2c_probe(struct i2c_client *client, static int anx7625_i2c_remove(struct i2c_client *client) { struct anx7625_data *platform = i2c_get_clientdata(client); + int i; drm_bridge_remove(&platform->bridge); + if (platform->pdata.tx_rx_to_two_ports) + for (i = 0; i < 2; i++) + typec_mux_unregister( + platform->typec_ports[i].typec_mux); + else + typec_switch_unregister(platform->typec_sw); + if (platform->pdata.intp_irq) destroy_workqueue(platform->workqueue); diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h index 193ad86c5450..bf546f3a4c06 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.h +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h @@ -55,6 +55,18 @@ #define HPD_STATUS_CHANGE 0x80 #define HPD_STATUS 0x80 +#define TCPC_SWITCH_0 0xB4 +#define SW_SEL1_ML0_A10_A11 BIT(0) +#define SW_SEL1_ML0_B10_B11 BIT(1) +#define SW_SEL1_SSRX_A10_A11 BIT(4) +#define SW_SEL1_SSRX_B10_B11 BIT(5) + +#define TCPC_SWITCH_1 0xB5 +#define SW_SEL2_ML1_B2_B3 BIT(0) +#define SW_SEL2_ML1_A2_A3 BIT(1) +#define SW_SEL2_SSTX_B2_B3 BIT(4) +#define SW_SEL2_SSTX_A2_A3 BIT(5) + /******** END of I2C Address 0x58 ********/ /***************************************************************/ @@ -354,6 +366,7 @@ struct anx7625_platform_data { int intp_irq; u32 low_power_mode; struct device_node *mipi_host_node; + bool tx_rx_to_two_ports; }; struct anx7625_i2c_client { @@ -366,6 +379,14 @@ struct anx7625_i2c_client { struct i2c_client *tcpc_client; }; +struct anx7625_data; + +struct anx7625_port_data { + bool has_dp; + struct typec_mux *typec_mux; + struct anx7625_data *ctx; +}; + struct anx7625_data { struct anx7625_platform_data pdata; atomic_t power_status; @@ -385,6 +406,9 @@ struct anx7625_data { struct drm_bridge bridge; u8 bridge_attached; struct mipi_dsi_device *dsi; + + struct typec_switch *typec_sw; + struct anx7625_port_data typec_ports[2]; }; #endif /* __ANX7625_H__ */ base-commit: 3e14f70c05cda4794901ed8f976de3a88deebcc0 -- 2.29.2.299.gdc1121823c-goog