* [PATCH 0/8] usb: typec: ucsi: yoga-c630: enable DisplayPort support
@ 2025-06-21 18:12 Dmitry Baryshkov
2025-06-21 18:12 ` [PATCH 1/8] usb: typec: ucsi: yoga-c630: fix error and remove paths Dmitry Baryshkov
` (7 more replies)
0 siblings, 8 replies; 17+ messages in thread
From: Dmitry Baryshkov @ 2025-06-21 18:12 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman, Bryan O'Donoghue,
Ilpo Järvinen, Bjorn Andersson, Konrad Dybcio
Cc: linux-usb, linux-kernel, linux-arm-msm
On Lenovo Yoga C630 laptop the EC implements UCSI support. However in
order to properly handle DisplayPort AltMode events it requires
additional support. Implement several workarounds, fixing issues with
AltMode UCSI commands and also handle out-of-bound DisplayPort hotplug
events.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
Dmitry Baryshkov (8):
usb: typec: ucsi: yoga-c630: fix error and remove paths
usb: typec: ucsi: add recipient arg to update_altmodes() callback
usb: typec: ucsi: yoga-c630: remove duplicate AltModes
usb: typec: ucsi: yoga-c630: remove extra AltModes for port 1
usb: typec: ucsi: yoga-c630: fake AltModes for port 0
usb: typec: ucsi: yoga-c630: correct response for GET_CURRENT_CAM
usb: typec: ucsi: yoga-c630: handle USB / HPD messages to set port orientation
usb: typec: ucsi: yoga-c630: register DRM HPD bridge
drivers/usb/typec/ucsi/Kconfig | 1 +
drivers/usb/typec/ucsi/ucsi.c | 5 +-
drivers/usb/typec/ucsi/ucsi.h | 4 +-
drivers/usb/typec/ucsi/ucsi_ccg.c | 4 +
drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 176 +++++++++++++++++++++++++++++++-
5 files changed, 182 insertions(+), 8 deletions(-)
---
base-commit: 5d4809e25903ab8e74034c1f23c787fd26d52934
change-id: 20250621-c630-ucsi-f1550bac5c17
Best regards,
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 1/8] usb: typec: ucsi: yoga-c630: fix error and remove paths
2025-06-21 18:12 [PATCH 0/8] usb: typec: ucsi: yoga-c630: enable DisplayPort support Dmitry Baryshkov
@ 2025-06-21 18:12 ` Dmitry Baryshkov
2025-06-24 9:05 ` Heikki Krogerus
2025-06-21 18:12 ` [PATCH 2/8] usb: typec: ucsi: add recipient arg to update_altmodes() callback Dmitry Baryshkov
` (6 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Dmitry Baryshkov @ 2025-06-21 18:12 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman, Bryan O'Donoghue,
Ilpo Järvinen, Bjorn Andersson, Konrad Dybcio
Cc: linux-usb, linux-kernel, linux-arm-msm
Fix memory leak and call ucsi_destroy() from the driver's remove
function and probe's error path in order to remove debugfs files and
free the memory. Also call yoga_c630_ec_unregister_notify() in the
probe's error path.
Fixes: 2ea6d07efe53 ("usb: typec: ucsi: add Lenovo Yoga C630 glue driver")
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
index d33e3f2dd1d80f7b49d9c5e7355fb14a97c74dd5..47e8dd5b255b2b306dd9c3d6ac8c3b896954981a 100644
--- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -133,17 +133,30 @@ static int yoga_c630_ucsi_probe(struct auxiliary_device *adev,
ret = yoga_c630_ec_register_notify(ec, &uec->nb);
if (ret)
- return ret;
+ goto err_destroy;
+
+ ret = ucsi_register(uec->ucsi);
+ if (ret)
+ goto err_unregister;
+
+ return 0;
- return ucsi_register(uec->ucsi);
+err_unregister:
+ yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
+
+err_destroy:
+ ucsi_destroy(uec->ucsi);
+
+ return ret;
}
static void yoga_c630_ucsi_remove(struct auxiliary_device *adev)
{
struct yoga_c630_ucsi *uec = auxiliary_get_drvdata(adev);
- yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
ucsi_unregister(uec->ucsi);
+ yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
+ ucsi_destroy(uec->ucsi);
}
static const struct auxiliary_device_id yoga_c630_ucsi_id_table[] = {
--
2.39.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 2/8] usb: typec: ucsi: add recipient arg to update_altmodes() callback
2025-06-21 18:12 [PATCH 0/8] usb: typec: ucsi: yoga-c630: enable DisplayPort support Dmitry Baryshkov
2025-06-21 18:12 ` [PATCH 1/8] usb: typec: ucsi: yoga-c630: fix error and remove paths Dmitry Baryshkov
@ 2025-06-21 18:12 ` Dmitry Baryshkov
2025-06-24 9:07 ` Heikki Krogerus
2025-06-21 18:12 ` [PATCH 3/8] usb: typec: ucsi: yoga-c630: remove duplicate AltModes Dmitry Baryshkov
` (5 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Dmitry Baryshkov @ 2025-06-21 18:12 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman, Bryan O'Donoghue,
Ilpo Järvinen, Bjorn Andersson, Konrad Dybcio
Cc: linux-usb, linux-kernel, linux-arm-msm
The update_altmodes() is executed only for connetor's AltModes, because
that's what required on the CCG platforms. Other platforms (like Lenovo
Yoga C630) requires similar fix for partner's AltModes. Extend
update_altmodes() callback to accept the recipient as an argument and
move corresponding check to the CCG UCSI driver.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/usb/typec/ucsi/ucsi.c | 5 ++---
drivers/usb/typec/ucsi/ucsi.h | 3 ++-
drivers/usb/typec/ucsi/ucsi_ccg.c | 4 ++++
3 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 01ce858a1a2b3466155db340e213c767d1e79479..e913d099f934c7728cb678fc8e21e75ab0a00cce 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -531,13 +531,12 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
* Update the original altmode table as some ppms may report
* multiple DP altmodes.
*/
- if (recipient == UCSI_RECIPIENT_CON)
- multi_dp = ucsi->ops->update_altmodes(ucsi, orig, updated);
+ multi_dp = ucsi->ops->update_altmodes(ucsi, recipient, orig, updated);
/* now register altmodes */
for (i = 0; i < max_altmodes; i++) {
memset(&desc, 0, sizeof(desc));
- if (multi_dp && recipient == UCSI_RECIPIENT_CON) {
+ if (multi_dp) {
desc.svid = updated[i].svid;
desc.vdo = updated[i].mid;
} else {
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 5a8f947fcece29f98c0458f9eb05c0c5ede2d244..d02f6a3e2f50a4044eb3f22276931017cc624532 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -82,7 +82,8 @@ struct ucsi_operations {
int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci,
void *data, size_t size);
int (*async_control)(struct ucsi *ucsi, u64 command);
- bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig,
+ bool (*update_altmodes)(struct ucsi *ucsi, u8 recipient,
+ struct ucsi_altmode *orig,
struct ucsi_altmode *updated);
void (*update_connector)(struct ucsi_connector *con);
void (*connector_status)(struct ucsi_connector *con);
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index e9a9df1431af3a52c00cc456f5c920b9077a1ade..d83a0051c737336af2543fc2f6d53a131ffe9583 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -394,6 +394,7 @@ static void ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data)
}
static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi,
+ u8 recipient,
struct ucsi_altmode *orig,
struct ucsi_altmode *updated)
{
@@ -402,6 +403,9 @@ static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi,
int i, j, k = 0;
bool found = false;
+ if (recipient != UCSI_RECIPIENT_CON)
+ return false;
+
alt = uc->orig;
new_alt = uc->updated;
memset(uc->updated, 0, sizeof(uc->updated));
--
2.39.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 3/8] usb: typec: ucsi: yoga-c630: remove duplicate AltModes
2025-06-21 18:12 [PATCH 0/8] usb: typec: ucsi: yoga-c630: enable DisplayPort support Dmitry Baryshkov
2025-06-21 18:12 ` [PATCH 1/8] usb: typec: ucsi: yoga-c630: fix error and remove paths Dmitry Baryshkov
2025-06-21 18:12 ` [PATCH 2/8] usb: typec: ucsi: add recipient arg to update_altmodes() callback Dmitry Baryshkov
@ 2025-06-21 18:12 ` Dmitry Baryshkov
2025-06-24 9:08 ` Heikki Krogerus
2025-06-21 18:12 ` [PATCH 4/8] usb: typec: ucsi: yoga-c630: remove extra AltModes for port 1 Dmitry Baryshkov
` (4 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Dmitry Baryshkov @ 2025-06-21 18:12 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman, Bryan O'Donoghue,
Ilpo Järvinen, Bjorn Andersson, Konrad Dybcio
Cc: linux-usb, linux-kernel, linux-arm-msm
On Lenovo Yoga C630 the EC firmware is buggy and it returns duplicate
AltModes over and over again instead of returning an empty one, as
demanded by the spec. Ignore extra altmodes by zeroing them in the
update_altmodes() callback. It is not possible to shortcut that in the
sync_control() callback since we need to know if the AltMode matches the
first reported AltMode or not.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
index 47e8dd5b255b2b306dd9c3d6ac8c3b896954981a..7cc1342d6e2f1d2c6f5b95caa48a711a280b91d3 100644
--- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -71,6 +71,28 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
return yoga_c630_ec_ucsi_write(uec->ec, (u8*)&command);
}
+static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi,
+ u8 recipient,
+ struct ucsi_altmode *orig,
+ struct ucsi_altmode *updated)
+{
+ int i;
+
+ if (orig[0].svid == 0 || recipient != UCSI_RECIPIENT_SOP)
+ return false;
+
+ /* EC is nice and repeats altmodes again and again. Ignore copies. */
+ for (i = 1; i < UCSI_MAX_ALTMODES; i++) {
+ if (orig[i].svid == orig[0].svid) {
+ dev_dbg(ucsi->dev, "Found duplicate altmodes, starting from %d\n", i);
+ memset(&orig[i], 0, (UCSI_MAX_ALTMODES - i) * sizeof(*orig));
+ break;
+ }
+ }
+
+ return false;
+}
+
static const struct ucsi_operations yoga_c630_ucsi_ops = {
.read_version = yoga_c630_ucsi_read_version,
.read_cci = yoga_c630_ucsi_read_cci,
@@ -78,6 +100,7 @@ static const struct ucsi_operations yoga_c630_ucsi_ops = {
.read_message_in = yoga_c630_ucsi_read_message_in,
.sync_control = ucsi_sync_control_common,
.async_control = yoga_c630_ucsi_async_control,
+ .update_altmodes = yoga_c630_ucsi_update_altmodes,
};
static int yoga_c630_ucsi_notify(struct notifier_block *nb,
--
2.39.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 4/8] usb: typec: ucsi: yoga-c630: remove extra AltModes for port 1
2025-06-21 18:12 [PATCH 0/8] usb: typec: ucsi: yoga-c630: enable DisplayPort support Dmitry Baryshkov
` (2 preceding siblings ...)
2025-06-21 18:12 ` [PATCH 3/8] usb: typec: ucsi: yoga-c630: remove duplicate AltModes Dmitry Baryshkov
@ 2025-06-21 18:12 ` Dmitry Baryshkov
2025-06-24 9:12 ` Heikki Krogerus
2025-06-21 18:13 ` [PATCH 5/8] usb: typec: ucsi: yoga-c630: fake AltModes for port 0 Dmitry Baryshkov
` (3 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Dmitry Baryshkov @ 2025-06-21 18:12 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman, Bryan O'Donoghue,
Ilpo Järvinen, Bjorn Andersson, Konrad Dybcio
Cc: linux-usb, linux-kernel, linux-arm-msm
On Lenovo Yoga C630 the EC firmware is buggy and it cat return altmodes
for a device pushed into the port 0 (right) when the driver asks for
altmodes for port 1 (left). Since the left Type-C port doesn't support
DP anyway, ignore all UCSI_GET_ALTERNATE_MODES commands destined to the
port 1.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
index 7cc1342d6e2f1d2c6f5b95caa48a711a280b91d3..2005f64ebfe43ca2bcada2231ff99c578fdce877 100644
--- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -71,6 +71,27 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
return yoga_c630_ec_ucsi_write(uec->ec, (u8*)&command);
}
+static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
+ u64 command,
+ u32 *cci,
+ void *data, size_t size)
+{
+ /*
+ * EC can return AltModes present on CON1 (port0, right) for CON2
+ * (port1, left) too. Ignore all requests going to CON2 (it doesn't
+ * support DP anyway).
+ */
+ if (UCSI_COMMAND(command) == UCSI_GET_ALTERNATE_MODES &&
+ UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(command) == 2) {
+ dev_dbg(ucsi->dev, "ignoring altmodes for con2\n");
+ memset(data, 0, size);
+ *cci = UCSI_CCI_COMMAND_COMPLETE;
+ return 0;
+ }
+
+ return ucsi_sync_control_common(ucsi, command, cci, data, size);
+}
+
static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi,
u8 recipient,
struct ucsi_altmode *orig,
@@ -98,7 +119,7 @@ static const struct ucsi_operations yoga_c630_ucsi_ops = {
.read_cci = yoga_c630_ucsi_read_cci,
.poll_cci = yoga_c630_ucsi_read_cci,
.read_message_in = yoga_c630_ucsi_read_message_in,
- .sync_control = ucsi_sync_control_common,
+ .sync_control = yoga_c630_ucsi_sync_control,
.async_control = yoga_c630_ucsi_async_control,
.update_altmodes = yoga_c630_ucsi_update_altmodes,
};
--
2.39.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 5/8] usb: typec: ucsi: yoga-c630: fake AltModes for port 0
2025-06-21 18:12 [PATCH 0/8] usb: typec: ucsi: yoga-c630: enable DisplayPort support Dmitry Baryshkov
` (3 preceding siblings ...)
2025-06-21 18:12 ` [PATCH 4/8] usb: typec: ucsi: yoga-c630: remove extra AltModes for port 1 Dmitry Baryshkov
@ 2025-06-21 18:13 ` Dmitry Baryshkov
2025-06-24 9:13 ` Heikki Krogerus
2025-06-21 18:13 ` [PATCH 6/8] usb: typec: ucsi: yoga-c630: correct response for GET_CURRENT_CAM Dmitry Baryshkov
` (2 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Dmitry Baryshkov @ 2025-06-21 18:13 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman, Bryan O'Donoghue,
Ilpo Järvinen, Bjorn Andersson, Konrad Dybcio
Cc: linux-usb, linux-kernel, linux-arm-msm
EC firmware provides information about partner AltModes and handles the
DisplayPort AltMode internally, however it doesn't report AltModes of
the port to the host. Fake the DP AltMode for port0 in order to let
Linux bind displayport AltMode driver.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/usb/typec/ucsi/ucsi.h | 1 +
drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 22 ++++++++++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index d02f6a3e2f50a4044eb3f22276931017cc624532..b711e1ecc3785eeb34e407e58df939f2d29bdb38 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -50,6 +50,7 @@ struct dentry;
/* Command Status and Connector Change Indication (CCI) bits */
#define UCSI_CCI_CONNECTOR(_c_) (((_c_) & GENMASK(7, 1)) >> 1)
#define UCSI_CCI_LENGTH(_c_) (((_c_) & GENMASK(15, 8)) >> 8)
+#define UCSI_SET_CCI_LENGTH(_c_) ((_c_) << 8)
#define UCSI_CCI_NOT_SUPPORTED BIT(25)
#define UCSI_CCI_CANCEL_COMPLETE BIT(26)
#define UCSI_CCI_RESET_COMPLETE BIT(27)
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
index 2005f64ebfe43ca2bcada2231ff99c578fdce877..506faf31b6e3a056e067f2bb69f5e9e5ea40e514 100644
--- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -76,6 +76,28 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
u32 *cci,
void *data, size_t size)
{
+ int ret;
+
+ /*
+ * EC doesn't return connector's DP mode even though it is supported.
+ * Fake it.
+ */
+ if (UCSI_COMMAND(command) == UCSI_GET_ALTERNATE_MODES &&
+ UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(command) == 1 &&
+ UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_CON &&
+ UCSI_ALTMODE_OFFSET(command) == 0) {
+ static const struct ucsi_altmode alt = {
+ .svid = USB_TYPEC_DP_SID,
+ .mid = USB_TYPEC_DP_MODE,
+ };
+
+ dev_dbg(ucsi->dev, "faking DP altmode for con1\n");
+ memset(data, 0, size);
+ memcpy(data, &alt, min(sizeof(alt), size));
+ *cci = UCSI_CCI_COMMAND_COMPLETE | UCSI_SET_CCI_LENGTH(sizeof(alt));
+ return 0;
+ }
+
/*
* EC can return AltModes present on CON1 (port0, right) for CON2
* (port1, left) too. Ignore all requests going to CON2 (it doesn't
--
2.39.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 6/8] usb: typec: ucsi: yoga-c630: correct response for GET_CURRENT_CAM
2025-06-21 18:12 [PATCH 0/8] usb: typec: ucsi: yoga-c630: enable DisplayPort support Dmitry Baryshkov
` (4 preceding siblings ...)
2025-06-21 18:13 ` [PATCH 5/8] usb: typec: ucsi: yoga-c630: fake AltModes for port 0 Dmitry Baryshkov
@ 2025-06-21 18:13 ` Dmitry Baryshkov
2025-06-24 9:16 ` Heikki Krogerus
2025-06-21 18:13 ` [PATCH 7/8] usb: typec: ucsi: yoga-c630: handle USB / HPD messages to set port orientation Dmitry Baryshkov
2025-06-21 18:13 ` [PATCH 8/8] usb: typec: ucsi: yoga-c630: register DRM HPD bridge Dmitry Baryshkov
7 siblings, 1 reply; 17+ messages in thread
From: Dmitry Baryshkov @ 2025-06-21 18:13 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman, Bryan O'Donoghue,
Ilpo Järvinen, Bjorn Andersson, Konrad Dybcio
Cc: linux-usb, linux-kernel, linux-arm-msm
On Lenovo Yoga C630 the EC handles GET_CURRENT_CAM command, but it
returns 1 if DisplayPort is active and 0 otherwise. However in order to
let UCSI drivers handle AltModes correctly, it should return 0 / 0xff.
Correct returned value.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
index 506faf31b6e3a056e067f2bb69f5e9e5ea40e514..76afd128d42a2573ff55433f815c2773462a6426 100644
--- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -111,7 +111,15 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
return 0;
}
- return ucsi_sync_control_common(ucsi, command, cci, data, size);
+ ret = ucsi_sync_control_common(ucsi, command, cci, data, size);
+ if (ret < 0)
+ return ret;
+
+ /* UCSI_GET_CURRENT_CAM is off-by-one on all ports */
+ if (UCSI_COMMAND(command) == UCSI_GET_CURRENT_CAM && data)
+ ((u8 *)data)[0]--;
+
+ return ret;
}
static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi,
--
2.39.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 7/8] usb: typec: ucsi: yoga-c630: handle USB / HPD messages to set port orientation
2025-06-21 18:12 [PATCH 0/8] usb: typec: ucsi: yoga-c630: enable DisplayPort support Dmitry Baryshkov
` (5 preceding siblings ...)
2025-06-21 18:13 ` [PATCH 6/8] usb: typec: ucsi: yoga-c630: correct response for GET_CURRENT_CAM Dmitry Baryshkov
@ 2025-06-21 18:13 ` Dmitry Baryshkov
2025-06-24 9:20 ` Heikki Krogerus
2025-06-21 18:13 ` [PATCH 8/8] usb: typec: ucsi: yoga-c630: register DRM HPD bridge Dmitry Baryshkov
7 siblings, 1 reply; 17+ messages in thread
From: Dmitry Baryshkov @ 2025-06-21 18:13 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman, Bryan O'Donoghue,
Ilpo Järvinen, Bjorn Andersson, Konrad Dybcio
Cc: linux-usb, linux-kernel, linux-arm-msm
Parse USB / HPD messages from EC in order to set the orientation of the
corresponding Type-C port, letting connected USB 3 devices to enjoy
SuperSpeed connection rather than beign limited to HighSpeed only (if
the orientation didn't match).
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 42 +++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
index 76afd128d42a2573ff55433f815c2773462a6426..f85170417d19cdc5ae39a15e2f97010259ef12f6 100644
--- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -7,15 +7,25 @@
*/
#include <linux/auxiliary_bus.h>
#include <linux/bitops.h>
+#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/container_of.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/string.h>
#include <linux/platform_data/lenovo-yoga-c630.h>
+#include <linux/usb/typec_dp.h>
#include "ucsi.h"
+#define LENOVO_EC_USB_MUX 0x08
+
+#define USB_MUX_MUXC GENMASK(1, 0)
+#define USB_MUX_CCST GENMASK(3, 2)
+#define USB_MUX_DPPN GENMASK(7, 4)
+#define USB_MUX_HPDS BIT(8)
+#define USB_MUX_HSFL GENMASK(11, 9)
+
struct yoga_c630_ucsi {
struct yoga_c630_ec *ec;
struct ucsi *ucsi;
@@ -144,6 +154,12 @@ static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi,
return false;
}
+static void yoga_c630_ucsi_update_connector(struct ucsi_connector *con)
+{
+ if (con->num == 1)
+ con->typec_cap.orientation_aware = true;
+}
+
static const struct ucsi_operations yoga_c630_ucsi_ops = {
.read_version = yoga_c630_ucsi_read_version,
.read_cci = yoga_c630_ucsi_read_cci,
@@ -152,8 +168,33 @@ static const struct ucsi_operations yoga_c630_ucsi_ops = {
.sync_control = yoga_c630_ucsi_sync_control,
.async_control = yoga_c630_ucsi_async_control,
.update_altmodes = yoga_c630_ucsi_update_altmodes,
+ .update_connector = yoga_c630_ucsi_update_connector,
};
+static void yoga_c630_ucsi_read_port0_status(struct yoga_c630_ucsi *uec)
+{
+ int val;
+ unsigned int muxc, ccst, dppn, hpds, hsfl;
+
+ val = yoga_c630_ec_read16(uec->ec, LENOVO_EC_USB_MUX);
+
+ muxc = FIELD_GET(USB_MUX_MUXC, val);
+ ccst = FIELD_GET(USB_MUX_CCST, val);
+ dppn = FIELD_GET(USB_MUX_DPPN, val);
+ hpds = FIELD_GET(USB_MUX_HPDS, val);
+ hsfl = FIELD_GET(USB_MUX_HSFL, val);
+
+ dev_dbg(uec->ucsi->dev, " mux %04x (muxc %d ccst %d dppn %d hpds %d hsfl %d)\n",
+ val,
+ muxc, ccst, dppn, hpds, hsfl);
+
+ if (uec->ucsi->connector && uec->ucsi->connector[0].port)
+ typec_set_orientation(uec->ucsi->connector[0].port,
+ ccst == 1 ?
+ TYPEC_ORIENTATION_REVERSE :
+ TYPEC_ORIENTATION_NORMAL);
+}
+
static int yoga_c630_ucsi_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
@@ -164,6 +205,7 @@ static int yoga_c630_ucsi_notify(struct notifier_block *nb,
switch (action) {
case LENOVO_EC_EVENT_USB:
case LENOVO_EC_EVENT_HPD:
+ yoga_c630_ucsi_read_port0_status(uec);
ucsi_connector_change(uec->ucsi, 1);
return NOTIFY_OK;
--
2.39.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 8/8] usb: typec: ucsi: yoga-c630: register DRM HPD bridge
2025-06-21 18:12 [PATCH 0/8] usb: typec: ucsi: yoga-c630: enable DisplayPort support Dmitry Baryshkov
` (6 preceding siblings ...)
2025-06-21 18:13 ` [PATCH 7/8] usb: typec: ucsi: yoga-c630: handle USB / HPD messages to set port orientation Dmitry Baryshkov
@ 2025-06-21 18:13 ` Dmitry Baryshkov
2025-06-24 9:21 ` Heikki Krogerus
7 siblings, 1 reply; 17+ messages in thread
From: Dmitry Baryshkov @ 2025-06-21 18:13 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman, Bryan O'Donoghue,
Ilpo Järvinen, Bjorn Andersson, Konrad Dybcio
Cc: linux-usb, linux-kernel, linux-arm-msm
On Qualcomm platforms DisplayPort driver expects to have a drm bridge in
the final device on the USB-C chain. Register the DRM HPD bridge in
order to fulfill this requirement and to send HPD events to the DRM
driver.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/usb/typec/ucsi/Kconfig | 1 +
drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 39 +++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index 8bf8fefb4f07bccc4be90a4b7f771d91294386b0..52b53bb6dfed28c4272f8ccc5e31601aede29911 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -85,6 +85,7 @@ config CROS_EC_UCSI
config UCSI_LENOVO_YOGA_C630
tristate "UCSI Interface Driver for Lenovo Yoga C630"
depends on EC_LENOVO_YOGA_C630
+ select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
help
This driver enables UCSI support on the Lenovo Yoga C630 laptop.
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
index f85170417d19cdc5ae39a15e2f97010259ef12f6..0187c1c4b21abc7b5429526ebb4538c28b2e2e77 100644
--- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -12,10 +12,14 @@
#include <linux/container_of.h>
#include <linux/module.h>
#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/property.h>
#include <linux/string.h>
#include <linux/platform_data/lenovo-yoga-c630.h>
#include <linux/usb/typec_dp.h>
+#include <drm/bridge/aux-bridge.h>
+
#include "ucsi.h"
#define LENOVO_EC_USB_MUX 0x08
@@ -29,6 +33,7 @@
struct yoga_c630_ucsi {
struct yoga_c630_ec *ec;
struct ucsi *ucsi;
+ struct auxiliary_device *bridge;
struct notifier_block nb;
u16 version;
};
@@ -193,6 +198,13 @@ static void yoga_c630_ucsi_read_port0_status(struct yoga_c630_ucsi *uec)
ccst == 1 ?
TYPEC_ORIENTATION_REVERSE :
TYPEC_ORIENTATION_NORMAL);
+
+ if (uec->bridge)
+ drm_aux_hpd_bridge_notify(&uec->bridge->dev,
+ dppn != 0 ?
+ connector_status_connected :
+ connector_status_disconnected);
+
}
static int yoga_c630_ucsi_notify(struct notifier_block *nb,
@@ -237,6 +249,24 @@ static int yoga_c630_ucsi_probe(struct auxiliary_device *adev,
uec->ec = ec;
uec->nb.notifier_call = yoga_c630_ucsi_notify;
+ device_for_each_child_node_scoped(&adev->dev, fwnode) {
+ u32 port;
+
+ ret = fwnode_property_read_u32(fwnode, "reg", &port);
+ if (ret < 0) {
+ dev_err(&adev->dev, "missing reg property of %pfwP\n", fwnode);
+ return ret;
+ }
+
+ /* DP is only on port0 */
+ if (port != 0)
+ continue;
+
+ uec->bridge = devm_drm_dp_hpd_bridge_alloc(&adev->dev, to_of_node(fwnode));
+ if (IS_ERR(uec->bridge))
+ return PTR_ERR(uec->bridge);
+ }
+
uec->ucsi = ucsi_create(&adev->dev, &yoga_c630_ucsi_ops);
if (IS_ERR(uec->ucsi))
return PTR_ERR(uec->ucsi);
@@ -255,8 +285,17 @@ static int yoga_c630_ucsi_probe(struct auxiliary_device *adev,
if (ret)
goto err_unregister;
+ if (uec->bridge) {
+ ret = devm_drm_dp_hpd_bridge_add(&adev->dev, uec->bridge);
+ if (ret)
+ goto err_ucsi_unregister;
+ }
+
return 0;
+err_ucsi_unregister:
+ ucsi_unregister(uec->ucsi);
+
err_unregister:
yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
--
2.39.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH 1/8] usb: typec: ucsi: yoga-c630: fix error and remove paths
2025-06-21 18:12 ` [PATCH 1/8] usb: typec: ucsi: yoga-c630: fix error and remove paths Dmitry Baryshkov
@ 2025-06-24 9:05 ` Heikki Krogerus
0 siblings, 0 replies; 17+ messages in thread
From: Heikki Krogerus @ 2025-06-24 9:05 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Greg Kroah-Hartman, Bryan O'Donoghue, Ilpo Järvinen,
Bjorn Andersson, Konrad Dybcio, linux-usb, linux-kernel,
linux-arm-msm
On Sat, Jun 21, 2025 at 09:12:56PM +0300, Dmitry Baryshkov wrote:
> Fix memory leak and call ucsi_destroy() from the driver's remove
> function and probe's error path in order to remove debugfs files and
> free the memory. Also call yoga_c630_ec_unregister_notify() in the
> probe's error path.
>
> Fixes: 2ea6d07efe53 ("usb: typec: ucsi: add Lenovo Yoga C630 glue driver")
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> ---
> drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 19 ++++++++++++++++---
> 1 file changed, 16 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> index d33e3f2dd1d80f7b49d9c5e7355fb14a97c74dd5..47e8dd5b255b2b306dd9c3d6ac8c3b896954981a 100644
> --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> @@ -133,17 +133,30 @@ static int yoga_c630_ucsi_probe(struct auxiliary_device *adev,
>
> ret = yoga_c630_ec_register_notify(ec, &uec->nb);
> if (ret)
> - return ret;
> + goto err_destroy;
> +
> + ret = ucsi_register(uec->ucsi);
> + if (ret)
> + goto err_unregister;
> +
> + return 0;
>
> - return ucsi_register(uec->ucsi);
> +err_unregister:
> + yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
> +
> +err_destroy:
> + ucsi_destroy(uec->ucsi);
> +
> + return ret;
> }
>
> static void yoga_c630_ucsi_remove(struct auxiliary_device *adev)
> {
> struct yoga_c630_ucsi *uec = auxiliary_get_drvdata(adev);
>
> - yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
> ucsi_unregister(uec->ucsi);
> + yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
> + ucsi_destroy(uec->ucsi);
> }
>
> static const struct auxiliary_device_id yoga_c630_ucsi_id_table[] = {
>
> --
> 2.39.5
--
heikki
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 2/8] usb: typec: ucsi: add recipient arg to update_altmodes() callback
2025-06-21 18:12 ` [PATCH 2/8] usb: typec: ucsi: add recipient arg to update_altmodes() callback Dmitry Baryshkov
@ 2025-06-24 9:07 ` Heikki Krogerus
0 siblings, 0 replies; 17+ messages in thread
From: Heikki Krogerus @ 2025-06-24 9:07 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Greg Kroah-Hartman, Bryan O'Donoghue, Ilpo Järvinen,
Bjorn Andersson, Konrad Dybcio, linux-usb, linux-kernel,
linux-arm-msm
On Sat, Jun 21, 2025 at 09:12:57PM +0300, Dmitry Baryshkov wrote:
> The update_altmodes() is executed only for connetor's AltModes, because
> that's what required on the CCG platforms. Other platforms (like Lenovo
> Yoga C630) requires similar fix for partner's AltModes. Extend
> update_altmodes() callback to accept the recipient as an argument and
> move corresponding check to the CCG UCSI driver.
>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> ---
> drivers/usb/typec/ucsi/ucsi.c | 5 ++---
> drivers/usb/typec/ucsi/ucsi.h | 3 ++-
> drivers/usb/typec/ucsi/ucsi_ccg.c | 4 ++++
> 3 files changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
> index 01ce858a1a2b3466155db340e213c767d1e79479..e913d099f934c7728cb678fc8e21e75ab0a00cce 100644
> --- a/drivers/usb/typec/ucsi/ucsi.c
> +++ b/drivers/usb/typec/ucsi/ucsi.c
> @@ -531,13 +531,12 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
> * Update the original altmode table as some ppms may report
> * multiple DP altmodes.
> */
> - if (recipient == UCSI_RECIPIENT_CON)
> - multi_dp = ucsi->ops->update_altmodes(ucsi, orig, updated);
> + multi_dp = ucsi->ops->update_altmodes(ucsi, recipient, orig, updated);
>
> /* now register altmodes */
> for (i = 0; i < max_altmodes; i++) {
> memset(&desc, 0, sizeof(desc));
> - if (multi_dp && recipient == UCSI_RECIPIENT_CON) {
> + if (multi_dp) {
> desc.svid = updated[i].svid;
> desc.vdo = updated[i].mid;
> } else {
> diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
> index 5a8f947fcece29f98c0458f9eb05c0c5ede2d244..d02f6a3e2f50a4044eb3f22276931017cc624532 100644
> --- a/drivers/usb/typec/ucsi/ucsi.h
> +++ b/drivers/usb/typec/ucsi/ucsi.h
> @@ -82,7 +82,8 @@ struct ucsi_operations {
> int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci,
> void *data, size_t size);
> int (*async_control)(struct ucsi *ucsi, u64 command);
> - bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig,
> + bool (*update_altmodes)(struct ucsi *ucsi, u8 recipient,
> + struct ucsi_altmode *orig,
> struct ucsi_altmode *updated);
> void (*update_connector)(struct ucsi_connector *con);
> void (*connector_status)(struct ucsi_connector *con);
> diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
> index e9a9df1431af3a52c00cc456f5c920b9077a1ade..d83a0051c737336af2543fc2f6d53a131ffe9583 100644
> --- a/drivers/usb/typec/ucsi/ucsi_ccg.c
> +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
> @@ -394,6 +394,7 @@ static void ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data)
> }
>
> static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi,
> + u8 recipient,
> struct ucsi_altmode *orig,
> struct ucsi_altmode *updated)
> {
> @@ -402,6 +403,9 @@ static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi,
> int i, j, k = 0;
> bool found = false;
>
> + if (recipient != UCSI_RECIPIENT_CON)
> + return false;
> +
> alt = uc->orig;
> new_alt = uc->updated;
> memset(uc->updated, 0, sizeof(uc->updated));
>
> --
> 2.39.5
--
heikki
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 3/8] usb: typec: ucsi: yoga-c630: remove duplicate AltModes
2025-06-21 18:12 ` [PATCH 3/8] usb: typec: ucsi: yoga-c630: remove duplicate AltModes Dmitry Baryshkov
@ 2025-06-24 9:08 ` Heikki Krogerus
0 siblings, 0 replies; 17+ messages in thread
From: Heikki Krogerus @ 2025-06-24 9:08 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Greg Kroah-Hartman, Bryan O'Donoghue, Ilpo Järvinen,
Bjorn Andersson, Konrad Dybcio, linux-usb, linux-kernel,
linux-arm-msm
On Sat, Jun 21, 2025 at 09:12:58PM +0300, Dmitry Baryshkov wrote:
> On Lenovo Yoga C630 the EC firmware is buggy and it returns duplicate
> AltModes over and over again instead of returning an empty one, as
> demanded by the spec. Ignore extra altmodes by zeroing them in the
> update_altmodes() callback. It is not possible to shortcut that in the
> sync_control() callback since we need to know if the AltMode matches the
> first reported AltMode or not.
>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> ---
> drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 23 +++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
>
> diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> index 47e8dd5b255b2b306dd9c3d6ac8c3b896954981a..7cc1342d6e2f1d2c6f5b95caa48a711a280b91d3 100644
> --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> @@ -71,6 +71,28 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
> return yoga_c630_ec_ucsi_write(uec->ec, (u8*)&command);
> }
>
> +static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi,
> + u8 recipient,
> + struct ucsi_altmode *orig,
> + struct ucsi_altmode *updated)
> +{
> + int i;
> +
> + if (orig[0].svid == 0 || recipient != UCSI_RECIPIENT_SOP)
> + return false;
> +
> + /* EC is nice and repeats altmodes again and again. Ignore copies. */
> + for (i = 1; i < UCSI_MAX_ALTMODES; i++) {
> + if (orig[i].svid == orig[0].svid) {
> + dev_dbg(ucsi->dev, "Found duplicate altmodes, starting from %d\n", i);
> + memset(&orig[i], 0, (UCSI_MAX_ALTMODES - i) * sizeof(*orig));
> + break;
> + }
> + }
> +
> + return false;
> +}
> +
> static const struct ucsi_operations yoga_c630_ucsi_ops = {
> .read_version = yoga_c630_ucsi_read_version,
> .read_cci = yoga_c630_ucsi_read_cci,
> @@ -78,6 +100,7 @@ static const struct ucsi_operations yoga_c630_ucsi_ops = {
> .read_message_in = yoga_c630_ucsi_read_message_in,
> .sync_control = ucsi_sync_control_common,
> .async_control = yoga_c630_ucsi_async_control,
> + .update_altmodes = yoga_c630_ucsi_update_altmodes,
> };
>
> static int yoga_c630_ucsi_notify(struct notifier_block *nb,
>
> --
> 2.39.5
--
heikki
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 4/8] usb: typec: ucsi: yoga-c630: remove extra AltModes for port 1
2025-06-21 18:12 ` [PATCH 4/8] usb: typec: ucsi: yoga-c630: remove extra AltModes for port 1 Dmitry Baryshkov
@ 2025-06-24 9:12 ` Heikki Krogerus
0 siblings, 0 replies; 17+ messages in thread
From: Heikki Krogerus @ 2025-06-24 9:12 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Greg Kroah-Hartman, Bryan O'Donoghue, Ilpo Järvinen,
Bjorn Andersson, Konrad Dybcio, linux-usb, linux-kernel,
linux-arm-msm
On Sat, Jun 21, 2025 at 09:12:59PM +0300, Dmitry Baryshkov wrote:
> On Lenovo Yoga C630 the EC firmware is buggy and it cat return altmodes
> for a device pushed into the port 0 (right) when the driver asks for
> altmodes for port 1 (left). Since the left Type-C port doesn't support
> DP anyway, ignore all UCSI_GET_ALTERNATE_MODES commands destined to the
> port 1.
>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> ---
> drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 23 ++++++++++++++++++++++-
> 1 file changed, 22 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> index 7cc1342d6e2f1d2c6f5b95caa48a711a280b91d3..2005f64ebfe43ca2bcada2231ff99c578fdce877 100644
> --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> @@ -71,6 +71,27 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
> return yoga_c630_ec_ucsi_write(uec->ec, (u8*)&command);
> }
>
> +static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
> + u64 command,
> + u32 *cci,
> + void *data, size_t size)
> +{
> + /*
> + * EC can return AltModes present on CON1 (port0, right) for CON2
> + * (port1, left) too. Ignore all requests going to CON2 (it doesn't
> + * support DP anyway).
> + */
> + if (UCSI_COMMAND(command) == UCSI_GET_ALTERNATE_MODES &&
> + UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(command) == 2) {
> + dev_dbg(ucsi->dev, "ignoring altmodes for con2\n");
> + memset(data, 0, size);
> + *cci = UCSI_CCI_COMMAND_COMPLETE;
> + return 0;
> + }
> +
> + return ucsi_sync_control_common(ucsi, command, cci, data, size);
> +}
> +
> static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi,
> u8 recipient,
> struct ucsi_altmode *orig,
> @@ -98,7 +119,7 @@ static const struct ucsi_operations yoga_c630_ucsi_ops = {
> .read_cci = yoga_c630_ucsi_read_cci,
> .poll_cci = yoga_c630_ucsi_read_cci,
> .read_message_in = yoga_c630_ucsi_read_message_in,
> - .sync_control = ucsi_sync_control_common,
> + .sync_control = yoga_c630_ucsi_sync_control,
> .async_control = yoga_c630_ucsi_async_control,
> .update_altmodes = yoga_c630_ucsi_update_altmodes,
> };
>
> --
> 2.39.5
--
heikki
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 5/8] usb: typec: ucsi: yoga-c630: fake AltModes for port 0
2025-06-21 18:13 ` [PATCH 5/8] usb: typec: ucsi: yoga-c630: fake AltModes for port 0 Dmitry Baryshkov
@ 2025-06-24 9:13 ` Heikki Krogerus
0 siblings, 0 replies; 17+ messages in thread
From: Heikki Krogerus @ 2025-06-24 9:13 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Greg Kroah-Hartman, Bryan O'Donoghue, Ilpo Järvinen,
Bjorn Andersson, Konrad Dybcio, linux-usb, linux-kernel,
linux-arm-msm
On Sat, Jun 21, 2025 at 09:13:00PM +0300, Dmitry Baryshkov wrote:
> EC firmware provides information about partner AltModes and handles the
> DisplayPort AltMode internally, however it doesn't report AltModes of
> the port to the host. Fake the DP AltMode for port0 in order to let
> Linux bind displayport AltMode driver.
>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> ---
> drivers/usb/typec/ucsi/ucsi.h | 1 +
> drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 22 ++++++++++++++++++++++
> 2 files changed, 23 insertions(+)
>
> diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
> index d02f6a3e2f50a4044eb3f22276931017cc624532..b711e1ecc3785eeb34e407e58df939f2d29bdb38 100644
> --- a/drivers/usb/typec/ucsi/ucsi.h
> +++ b/drivers/usb/typec/ucsi/ucsi.h
> @@ -50,6 +50,7 @@ struct dentry;
> /* Command Status and Connector Change Indication (CCI) bits */
> #define UCSI_CCI_CONNECTOR(_c_) (((_c_) & GENMASK(7, 1)) >> 1)
> #define UCSI_CCI_LENGTH(_c_) (((_c_) & GENMASK(15, 8)) >> 8)
> +#define UCSI_SET_CCI_LENGTH(_c_) ((_c_) << 8)
> #define UCSI_CCI_NOT_SUPPORTED BIT(25)
> #define UCSI_CCI_CANCEL_COMPLETE BIT(26)
> #define UCSI_CCI_RESET_COMPLETE BIT(27)
> diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> index 2005f64ebfe43ca2bcada2231ff99c578fdce877..506faf31b6e3a056e067f2bb69f5e9e5ea40e514 100644
> --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> @@ -76,6 +76,28 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
> u32 *cci,
> void *data, size_t size)
> {
> + int ret;
> +
> + /*
> + * EC doesn't return connector's DP mode even though it is supported.
> + * Fake it.
> + */
> + if (UCSI_COMMAND(command) == UCSI_GET_ALTERNATE_MODES &&
> + UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(command) == 1 &&
> + UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_CON &&
> + UCSI_ALTMODE_OFFSET(command) == 0) {
> + static const struct ucsi_altmode alt = {
> + .svid = USB_TYPEC_DP_SID,
> + .mid = USB_TYPEC_DP_MODE,
> + };
> +
> + dev_dbg(ucsi->dev, "faking DP altmode for con1\n");
> + memset(data, 0, size);
> + memcpy(data, &alt, min(sizeof(alt), size));
> + *cci = UCSI_CCI_COMMAND_COMPLETE | UCSI_SET_CCI_LENGTH(sizeof(alt));
> + return 0;
> + }
> +
> /*
> * EC can return AltModes present on CON1 (port0, right) for CON2
> * (port1, left) too. Ignore all requests going to CON2 (it doesn't
>
> --
> 2.39.5
--
heikki
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 6/8] usb: typec: ucsi: yoga-c630: correct response for GET_CURRENT_CAM
2025-06-21 18:13 ` [PATCH 6/8] usb: typec: ucsi: yoga-c630: correct response for GET_CURRENT_CAM Dmitry Baryshkov
@ 2025-06-24 9:16 ` Heikki Krogerus
0 siblings, 0 replies; 17+ messages in thread
From: Heikki Krogerus @ 2025-06-24 9:16 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Greg Kroah-Hartman, Bryan O'Donoghue, Ilpo Järvinen,
Bjorn Andersson, Konrad Dybcio, linux-usb, linux-kernel,
linux-arm-msm
On Sat, Jun 21, 2025 at 09:13:01PM +0300, Dmitry Baryshkov wrote:
> On Lenovo Yoga C630 the EC handles GET_CURRENT_CAM command, but it
> returns 1 if DisplayPort is active and 0 otherwise. However in order to
> let UCSI drivers handle AltModes correctly, it should return 0 / 0xff.
> Correct returned value.
>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> ---
> drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 10 +++++++++-
> 1 file changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> index 506faf31b6e3a056e067f2bb69f5e9e5ea40e514..76afd128d42a2573ff55433f815c2773462a6426 100644
> --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> @@ -111,7 +111,15 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
> return 0;
> }
>
> - return ucsi_sync_control_common(ucsi, command, cci, data, size);
> + ret = ucsi_sync_control_common(ucsi, command, cci, data, size);
> + if (ret < 0)
> + return ret;
> +
> + /* UCSI_GET_CURRENT_CAM is off-by-one on all ports */
> + if (UCSI_COMMAND(command) == UCSI_GET_CURRENT_CAM && data)
> + ((u8 *)data)[0]--;
> +
> + return ret;
> }
>
> static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi,
>
> --
> 2.39.5
--
heikki
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 7/8] usb: typec: ucsi: yoga-c630: handle USB / HPD messages to set port orientation
2025-06-21 18:13 ` [PATCH 7/8] usb: typec: ucsi: yoga-c630: handle USB / HPD messages to set port orientation Dmitry Baryshkov
@ 2025-06-24 9:20 ` Heikki Krogerus
0 siblings, 0 replies; 17+ messages in thread
From: Heikki Krogerus @ 2025-06-24 9:20 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Greg Kroah-Hartman, Bryan O'Donoghue, Ilpo Järvinen,
Bjorn Andersson, Konrad Dybcio, linux-usb, linux-kernel,
linux-arm-msm
On Sat, Jun 21, 2025 at 09:13:02PM +0300, Dmitry Baryshkov wrote:
> Parse USB / HPD messages from EC in order to set the orientation of the
> corresponding Type-C port, letting connected USB 3 devices to enjoy
> SuperSpeed connection rather than beign limited to HighSpeed only (if
> the orientation didn't match).
>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> ---
> drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 42 +++++++++++++++++++++++++++++++++
> 1 file changed, 42 insertions(+)
>
> diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> index 76afd128d42a2573ff55433f815c2773462a6426..f85170417d19cdc5ae39a15e2f97010259ef12f6 100644
> --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> @@ -7,15 +7,25 @@
> */
> #include <linux/auxiliary_bus.h>
> #include <linux/bitops.h>
> +#include <linux/bitfield.h>
> #include <linux/completion.h>
> #include <linux/container_of.h>
> #include <linux/module.h>
> #include <linux/notifier.h>
> #include <linux/string.h>
> #include <linux/platform_data/lenovo-yoga-c630.h>
> +#include <linux/usb/typec_dp.h>
>
> #include "ucsi.h"
>
> +#define LENOVO_EC_USB_MUX 0x08
> +
> +#define USB_MUX_MUXC GENMASK(1, 0)
> +#define USB_MUX_CCST GENMASK(3, 2)
> +#define USB_MUX_DPPN GENMASK(7, 4)
> +#define USB_MUX_HPDS BIT(8)
> +#define USB_MUX_HSFL GENMASK(11, 9)
> +
> struct yoga_c630_ucsi {
> struct yoga_c630_ec *ec;
> struct ucsi *ucsi;
> @@ -144,6 +154,12 @@ static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi,
> return false;
> }
>
> +static void yoga_c630_ucsi_update_connector(struct ucsi_connector *con)
> +{
> + if (con->num == 1)
> + con->typec_cap.orientation_aware = true;
> +}
> +
> static const struct ucsi_operations yoga_c630_ucsi_ops = {
> .read_version = yoga_c630_ucsi_read_version,
> .read_cci = yoga_c630_ucsi_read_cci,
> @@ -152,8 +168,33 @@ static const struct ucsi_operations yoga_c630_ucsi_ops = {
> .sync_control = yoga_c630_ucsi_sync_control,
> .async_control = yoga_c630_ucsi_async_control,
> .update_altmodes = yoga_c630_ucsi_update_altmodes,
> + .update_connector = yoga_c630_ucsi_update_connector,
> };
>
> +static void yoga_c630_ucsi_read_port0_status(struct yoga_c630_ucsi *uec)
> +{
> + int val;
> + unsigned int muxc, ccst, dppn, hpds, hsfl;
> +
> + val = yoga_c630_ec_read16(uec->ec, LENOVO_EC_USB_MUX);
> +
> + muxc = FIELD_GET(USB_MUX_MUXC, val);
> + ccst = FIELD_GET(USB_MUX_CCST, val);
> + dppn = FIELD_GET(USB_MUX_DPPN, val);
> + hpds = FIELD_GET(USB_MUX_HPDS, val);
> + hsfl = FIELD_GET(USB_MUX_HSFL, val);
> +
> + dev_dbg(uec->ucsi->dev, " mux %04x (muxc %d ccst %d dppn %d hpds %d hsfl %d)\n",
> + val,
> + muxc, ccst, dppn, hpds, hsfl);
> +
> + if (uec->ucsi->connector && uec->ucsi->connector[0].port)
> + typec_set_orientation(uec->ucsi->connector[0].port,
> + ccst == 1 ?
> + TYPEC_ORIENTATION_REVERSE :
> + TYPEC_ORIENTATION_NORMAL);
> +}
> +
> static int yoga_c630_ucsi_notify(struct notifier_block *nb,
> unsigned long action, void *data)
> {
> @@ -164,6 +205,7 @@ static int yoga_c630_ucsi_notify(struct notifier_block *nb,
> switch (action) {
> case LENOVO_EC_EVENT_USB:
> case LENOVO_EC_EVENT_HPD:
> + yoga_c630_ucsi_read_port0_status(uec);
> ucsi_connector_change(uec->ucsi, 1);
> return NOTIFY_OK;
>
>
> --
> 2.39.5
--
heikki
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 8/8] usb: typec: ucsi: yoga-c630: register DRM HPD bridge
2025-06-21 18:13 ` [PATCH 8/8] usb: typec: ucsi: yoga-c630: register DRM HPD bridge Dmitry Baryshkov
@ 2025-06-24 9:21 ` Heikki Krogerus
0 siblings, 0 replies; 17+ messages in thread
From: Heikki Krogerus @ 2025-06-24 9:21 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Greg Kroah-Hartman, Bryan O'Donoghue, Ilpo Järvinen,
Bjorn Andersson, Konrad Dybcio, linux-usb, linux-kernel,
linux-arm-msm
On Sat, Jun 21, 2025 at 09:13:03PM +0300, Dmitry Baryshkov wrote:
> On Qualcomm platforms DisplayPort driver expects to have a drm bridge in
> the final device on the USB-C chain. Register the DRM HPD bridge in
> order to fulfill this requirement and to send HPD events to the DRM
> driver.
>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> ---
> drivers/usb/typec/ucsi/Kconfig | 1 +
> drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 39 +++++++++++++++++++++++++++++++++
> 2 files changed, 40 insertions(+)
>
> diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
> index 8bf8fefb4f07bccc4be90a4b7f771d91294386b0..52b53bb6dfed28c4272f8ccc5e31601aede29911 100644
> --- a/drivers/usb/typec/ucsi/Kconfig
> +++ b/drivers/usb/typec/ucsi/Kconfig
> @@ -85,6 +85,7 @@ config CROS_EC_UCSI
> config UCSI_LENOVO_YOGA_C630
> tristate "UCSI Interface Driver for Lenovo Yoga C630"
> depends on EC_LENOVO_YOGA_C630
> + select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
> help
> This driver enables UCSI support on the Lenovo Yoga C630 laptop.
>
> diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> index f85170417d19cdc5ae39a15e2f97010259ef12f6..0187c1c4b21abc7b5429526ebb4538c28b2e2e77 100644
> --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
> @@ -12,10 +12,14 @@
> #include <linux/container_of.h>
> #include <linux/module.h>
> #include <linux/notifier.h>
> +#include <linux/of.h>
> +#include <linux/property.h>
> #include <linux/string.h>
> #include <linux/platform_data/lenovo-yoga-c630.h>
> #include <linux/usb/typec_dp.h>
>
> +#include <drm/bridge/aux-bridge.h>
> +
> #include "ucsi.h"
>
> #define LENOVO_EC_USB_MUX 0x08
> @@ -29,6 +33,7 @@
> struct yoga_c630_ucsi {
> struct yoga_c630_ec *ec;
> struct ucsi *ucsi;
> + struct auxiliary_device *bridge;
> struct notifier_block nb;
> u16 version;
> };
> @@ -193,6 +198,13 @@ static void yoga_c630_ucsi_read_port0_status(struct yoga_c630_ucsi *uec)
> ccst == 1 ?
> TYPEC_ORIENTATION_REVERSE :
> TYPEC_ORIENTATION_NORMAL);
> +
> + if (uec->bridge)
> + drm_aux_hpd_bridge_notify(&uec->bridge->dev,
> + dppn != 0 ?
> + connector_status_connected :
> + connector_status_disconnected);
> +
> }
>
> static int yoga_c630_ucsi_notify(struct notifier_block *nb,
> @@ -237,6 +249,24 @@ static int yoga_c630_ucsi_probe(struct auxiliary_device *adev,
> uec->ec = ec;
> uec->nb.notifier_call = yoga_c630_ucsi_notify;
>
> + device_for_each_child_node_scoped(&adev->dev, fwnode) {
> + u32 port;
> +
> + ret = fwnode_property_read_u32(fwnode, "reg", &port);
> + if (ret < 0) {
> + dev_err(&adev->dev, "missing reg property of %pfwP\n", fwnode);
> + return ret;
> + }
> +
> + /* DP is only on port0 */
> + if (port != 0)
> + continue;
> +
> + uec->bridge = devm_drm_dp_hpd_bridge_alloc(&adev->dev, to_of_node(fwnode));
> + if (IS_ERR(uec->bridge))
> + return PTR_ERR(uec->bridge);
> + }
> +
> uec->ucsi = ucsi_create(&adev->dev, &yoga_c630_ucsi_ops);
> if (IS_ERR(uec->ucsi))
> return PTR_ERR(uec->ucsi);
> @@ -255,8 +285,17 @@ static int yoga_c630_ucsi_probe(struct auxiliary_device *adev,
> if (ret)
> goto err_unregister;
>
> + if (uec->bridge) {
> + ret = devm_drm_dp_hpd_bridge_add(&adev->dev, uec->bridge);
> + if (ret)
> + goto err_ucsi_unregister;
> + }
> +
> return 0;
>
> +err_ucsi_unregister:
> + ucsi_unregister(uec->ucsi);
> +
> err_unregister:
> yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
>
>
> --
> 2.39.5
--
heikki
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2025-06-24 9:21 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-21 18:12 [PATCH 0/8] usb: typec: ucsi: yoga-c630: enable DisplayPort support Dmitry Baryshkov
2025-06-21 18:12 ` [PATCH 1/8] usb: typec: ucsi: yoga-c630: fix error and remove paths Dmitry Baryshkov
2025-06-24 9:05 ` Heikki Krogerus
2025-06-21 18:12 ` [PATCH 2/8] usb: typec: ucsi: add recipient arg to update_altmodes() callback Dmitry Baryshkov
2025-06-24 9:07 ` Heikki Krogerus
2025-06-21 18:12 ` [PATCH 3/8] usb: typec: ucsi: yoga-c630: remove duplicate AltModes Dmitry Baryshkov
2025-06-24 9:08 ` Heikki Krogerus
2025-06-21 18:12 ` [PATCH 4/8] usb: typec: ucsi: yoga-c630: remove extra AltModes for port 1 Dmitry Baryshkov
2025-06-24 9:12 ` Heikki Krogerus
2025-06-21 18:13 ` [PATCH 5/8] usb: typec: ucsi: yoga-c630: fake AltModes for port 0 Dmitry Baryshkov
2025-06-24 9:13 ` Heikki Krogerus
2025-06-21 18:13 ` [PATCH 6/8] usb: typec: ucsi: yoga-c630: correct response for GET_CURRENT_CAM Dmitry Baryshkov
2025-06-24 9:16 ` Heikki Krogerus
2025-06-21 18:13 ` [PATCH 7/8] usb: typec: ucsi: yoga-c630: handle USB / HPD messages to set port orientation Dmitry Baryshkov
2025-06-24 9:20 ` Heikki Krogerus
2025-06-21 18:13 ` [PATCH 8/8] usb: typec: ucsi: yoga-c630: register DRM HPD bridge Dmitry Baryshkov
2025-06-24 9:21 ` Heikki Krogerus
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).