Skip to content

Commit 170a672

Browse files
Ajay Guptagregkh
authored andcommitted
usb: typec: ucsi: add support for separate DP altmode devices
CCGx controller used on NVIDIA GPU card has two separate display altmode for two DP pin assignments. UCSI specification doesn't prohibits using separate display altmode. Current UCSI Type-C framework expects only one display altmode for all DP pin assignment. This patch squashes two separate display altmode into single altmode to support controllers with separate display altmode. We first read all the alternate modes of connector and then run through it to know if there are separate display altmodes. If so, it prepares a new port altmode set after squashing two or more separate altmodes into one. Signed-off-by: Ajay Gupta <[email protected]> Signed-off-by: Heikki Krogerus <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 71a1fa0 commit 170a672

File tree

3 files changed

+279
-2
lines changed

3 files changed

+279
-2
lines changed

drivers/usb/typec/ucsi/ucsi.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,82 @@ static int ucsi_register_altmode(struct ucsi_connector *con,
318318
return ret;
319319
}
320320

321+
static int
322+
ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
323+
{
324+
int max_altmodes = UCSI_MAX_ALTMODES;
325+
struct typec_altmode_desc desc;
326+
struct ucsi_altmode alt;
327+
struct ucsi_altmode orig[UCSI_MAX_ALTMODES];
328+
struct ucsi_altmode updated[UCSI_MAX_ALTMODES];
329+
struct ucsi *ucsi = con->ucsi;
330+
bool multi_dp = false;
331+
u64 command;
332+
int ret;
333+
int len;
334+
int i;
335+
int k = 0;
336+
337+
if (recipient == UCSI_RECIPIENT_CON)
338+
max_altmodes = con->ucsi->cap.num_alt_modes;
339+
340+
memset(orig, 0, sizeof(orig));
341+
memset(updated, 0, sizeof(updated));
342+
343+
/* First get all the alternate modes */
344+
for (i = 0; i < max_altmodes; i++) {
345+
memset(&alt, 0, sizeof(alt));
346+
command = UCSI_GET_ALTERNATE_MODES;
347+
command |= UCSI_GET_ALTMODE_RECIPIENT(recipient);
348+
command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num);
349+
command |= UCSI_GET_ALTMODE_OFFSET(i);
350+
len = ucsi_run_command(con->ucsi, command, &alt, sizeof(alt));
351+
/*
352+
* We are collecting all altmodes first and then registering.
353+
* Some type-C device will return zero length data beyond last
354+
* alternate modes. We should not return if length is zero.
355+
*/
356+
if (len < 0)
357+
return len;
358+
359+
/* We got all altmodes, now break out and register them */
360+
if (!len || !alt.svid)
361+
break;
362+
363+
orig[k].mid = alt.mid;
364+
orig[k].svid = alt.svid;
365+
k++;
366+
}
367+
/*
368+
* Update the original altmode table as some ppms may report
369+
* multiple DP altmodes.
370+
*/
371+
if (recipient == UCSI_RECIPIENT_CON)
372+
multi_dp = ucsi->ops->update_altmodes(ucsi, orig, updated);
373+
374+
/* now register altmodes */
375+
for (i = 0; i < max_altmodes; i++) {
376+
memset(&desc, 0, sizeof(desc));
377+
if (multi_dp && recipient == UCSI_RECIPIENT_CON) {
378+
desc.svid = updated[i].svid;
379+
desc.vdo = updated[i].mid;
380+
} else {
381+
desc.svid = orig[i].svid;
382+
desc.vdo = orig[i].mid;
383+
}
384+
desc.roles = TYPEC_PORT_DRD;
385+
386+
if (!desc.svid)
387+
return 0;
388+
389+
ret = ucsi_register_altmode(con, &desc, recipient);
390+
if (ret)
391+
return ret;
392+
}
393+
394+
return 0;
395+
}
396+
321397
static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
322398
{
323399
int max_altmodes = UCSI_MAX_ALTMODES;
@@ -336,6 +412,9 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
336412
if (recipient == UCSI_RECIPIENT_SOP && con->partner_altmode[0])
337413
return 0;
338414

415+
if (con->ucsi->ops->update_altmodes)
416+
return ucsi_register_altmodes_nvidia(con, recipient);
417+
339418
if (recipient == UCSI_RECIPIENT_CON)
340419
max_altmodes = con->ucsi->cap.num_alt_modes;
341420

drivers/usb/typec/ucsi/ucsi.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
/* -------------------------------------------------------------------------- */
1212

1313
struct ucsi;
14+
struct ucsi_altmode;
1415

1516
/* UCSI offsets (Bytes) */
1617
#define UCSI_VERSION 0
@@ -35,6 +36,7 @@ struct ucsi;
3536
* @read: Read operation
3637
* @sync_write: Blocking write operation
3738
* @async_write: Non-blocking write operation
39+
* @update_altmodes: Squashes duplicate DP altmodes
3840
*
3941
* Read and write routines for UCSI interface. @sync_write must wait for the
4042
* Command Completion Event from the PPM before returning, and @async_write must
@@ -47,6 +49,8 @@ struct ucsi_operations {
4749
const void *val, size_t val_len);
4850
int (*async_write)(struct ucsi *ucsi, unsigned int offset,
4951
const void *val, size_t val_len);
52+
bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig,
53+
struct ucsi_altmode *updated);
5054
};
5155

5256
struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops);
@@ -82,6 +86,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
8286
#define UCSI_GET_ERROR_STATUS 0x13
8387

8488
#define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16)
89+
#define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff)
8590

8691
/* CONNECTOR_RESET command bits */
8792
#define UCSI_CONNECTOR_RESET_HARD BIT(23) /* Deprecated in v1.1 */
@@ -140,6 +145,12 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
140145
#define UCSI_ERROR_PPM_POLICY_CONFLICT BIT(11)
141146
#define UCSI_ERROR_SWAP_REJECTED BIT(12)
142147

148+
#define UCSI_SET_NEW_CAM_ENTER(x) (((x) >> 23) & 0x1)
149+
#define UCSI_SET_NEW_CAM_GET_AM(x) (((x) >> 24) & 0xff)
150+
#define UCSI_SET_NEW_CAM_AM_MASK (0xff << 24)
151+
#define UCSI_SET_NEW_CAM_SET_AM(x) (((x) & 0xff) << 24)
152+
#define UCSI_CMD_CONNECTOR_MASK (0x7)
153+
143154
/* Data structure filled by PPM in response to GET_CAPABILITY command. */
144155
struct ucsi_capability {
145156
u32 attributes;

drivers/usb/typec/ucsi/ucsi_ccg.c

Lines changed: 189 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/platform_device.h>
1717
#include <linux/pm.h>
1818
#include <linux/pm_runtime.h>
19+
#include <linux/usb/typec_dp.h>
1920

2021
#include <asm/unaligned.h>
2122
#include "ucsi.h"
@@ -173,6 +174,15 @@ struct ccg_resp {
173174
u8 length;
174175
};
175176

177+
struct ucsi_ccg_altmode {
178+
u16 svid;
179+
u32 mid;
180+
u8 linked_idx;
181+
u8 active_idx;
182+
#define UCSI_MULTI_DP_INDEX (0xff)
183+
bool checked;
184+
} __packed;
185+
176186
struct ucsi_ccg {
177187
struct device *dev;
178188
struct ucsi *ucsi;
@@ -198,6 +208,11 @@ struct ucsi_ccg {
198208
struct work_struct pm_work;
199209

200210
struct completion complete;
211+
212+
u64 last_cmd_sent;
213+
bool has_multiple_dp;
214+
struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
215+
struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES];
201216
};
202217

203218
static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
@@ -319,12 +334,169 @@ static int ucsi_ccg_init(struct ucsi_ccg *uc)
319334
return -ETIMEDOUT;
320335
}
321336

337+
static void ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data)
338+
{
339+
u8 cam, new_cam;
340+
341+
cam = data[0];
342+
new_cam = uc->orig[cam].linked_idx;
343+
uc->updated[new_cam].active_idx = cam;
344+
data[0] = new_cam;
345+
}
346+
347+
static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi,
348+
struct ucsi_altmode *orig,
349+
struct ucsi_altmode *updated)
350+
{
351+
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
352+
struct ucsi_ccg_altmode *alt, *new_alt;
353+
int i, j, k = 0;
354+
bool found = false;
355+
356+
alt = uc->orig;
357+
new_alt = uc->updated;
358+
memset(uc->updated, 0, sizeof(uc->updated));
359+
360+
/*
361+
* Copy original connector altmodes to new structure.
362+
* We need this before second loop since second loop
363+
* checks for duplicate altmodes.
364+
*/
365+
for (i = 0; i < UCSI_MAX_ALTMODES; i++) {
366+
alt[i].svid = orig[i].svid;
367+
alt[i].mid = orig[i].mid;
368+
if (!alt[i].svid)
369+
break;
370+
}
371+
372+
for (i = 0; i < UCSI_MAX_ALTMODES; i++) {
373+
if (!alt[i].svid)
374+
break;
375+
376+
/* already checked and considered */
377+
if (alt[i].checked)
378+
continue;
379+
380+
if (!DP_CONF_GET_PIN_ASSIGN(alt[i].mid)) {
381+
/* Found Non DP altmode */
382+
new_alt[k].svid = alt[i].svid;
383+
new_alt[k].mid |= alt[i].mid;
384+
new_alt[k].linked_idx = i;
385+
alt[i].linked_idx = k;
386+
updated[k].svid = new_alt[k].svid;
387+
updated[k].mid = new_alt[k].mid;
388+
k++;
389+
continue;
390+
}
391+
392+
for (j = i + 1; j < UCSI_MAX_ALTMODES; j++) {
393+
if (alt[i].svid != alt[j].svid ||
394+
!DP_CONF_GET_PIN_ASSIGN(alt[j].mid)) {
395+
continue;
396+
} else {
397+
/* Found duplicate DP mode */
398+
new_alt[k].svid = alt[i].svid;
399+
new_alt[k].mid |= alt[i].mid | alt[j].mid;
400+
new_alt[k].linked_idx = UCSI_MULTI_DP_INDEX;
401+
alt[i].linked_idx = k;
402+
alt[j].linked_idx = k;
403+
alt[j].checked = true;
404+
found = true;
405+
}
406+
}
407+
if (found) {
408+
uc->has_multiple_dp = true;
409+
} else {
410+
/* Didn't find any duplicate DP altmode */
411+
new_alt[k].svid = alt[i].svid;
412+
new_alt[k].mid |= alt[i].mid;
413+
new_alt[k].linked_idx = i;
414+
alt[i].linked_idx = k;
415+
}
416+
updated[k].svid = new_alt[k].svid;
417+
updated[k].mid = new_alt[k].mid;
418+
k++;
419+
}
420+
return found;
421+
}
422+
423+
static void ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg *uc,
424+
struct ucsi_connector *con,
425+
u64 *cmd)
426+
{
427+
struct ucsi_ccg_altmode *new_port, *port;
428+
struct typec_altmode *alt = NULL;
429+
u8 new_cam, cam, pin;
430+
bool enter_new_mode;
431+
int i, j, k = 0xff;
432+
433+
port = uc->orig;
434+
new_cam = UCSI_SET_NEW_CAM_GET_AM(*cmd);
435+
new_port = &uc->updated[new_cam];
436+
cam = new_port->linked_idx;
437+
enter_new_mode = UCSI_SET_NEW_CAM_ENTER(*cmd);
438+
439+
/*
440+
* If CAM is UCSI_MULTI_DP_INDEX then this is DP altmode
441+
* with multiple DP mode. Find out CAM for best pin assignment
442+
* among all DP mode. Priorite pin E->D->C after making sure
443+
* the partner supports that pin.
444+
*/
445+
if (cam == UCSI_MULTI_DP_INDEX) {
446+
if (enter_new_mode) {
447+
for (i = 0; con->partner_altmode[i]; i++) {
448+
alt = con->partner_altmode[i];
449+
if (alt->svid == new_port->svid)
450+
break;
451+
}
452+
/*
453+
* alt will always be non NULL since this is
454+
* UCSI_SET_NEW_CAM command and so there will be
455+
* at least one con->partner_altmode[i] with svid
456+
* matching with new_port->svid.
457+
*/
458+
for (j = 0; port[j].svid; j++) {
459+
pin = DP_CONF_GET_PIN_ASSIGN(port[j].mid);
460+
if (alt && port[j].svid == alt->svid &&
461+
(pin & DP_CONF_GET_PIN_ASSIGN(alt->vdo))) {
462+
/* prioritize pin E->D->C */
463+
if (k == 0xff || (k != 0xff && pin >
464+
DP_CONF_GET_PIN_ASSIGN(port[k].mid))
465+
) {
466+
k = j;
467+
}
468+
}
469+
}
470+
cam = k;
471+
new_port->active_idx = cam;
472+
} else {
473+
cam = new_port->active_idx;
474+
}
475+
}
476+
*cmd &= ~UCSI_SET_NEW_CAM_AM_MASK;
477+
*cmd |= UCSI_SET_NEW_CAM_SET_AM(cam);
478+
}
479+
322480
static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
323481
void *val, size_t val_len)
324482
{
483+
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
484+
int ret;
325485
u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
326486

327-
return ccg_read(ucsi_get_drvdata(ucsi), reg, val, val_len);
487+
ret = ccg_read(uc, reg, val, val_len);
488+
if (ret)
489+
return ret;
490+
491+
if (offset == UCSI_MESSAGE_IN) {
492+
if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_GET_CURRENT_CAM &&
493+
uc->has_multiple_dp) {
494+
ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)val);
495+
}
496+
uc->last_cmd_sent = 0;
497+
}
498+
499+
return ret;
328500
}
329501

330502
static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
@@ -339,12 +511,26 @@ static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
339511
const void *val, size_t val_len)
340512
{
341513
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
514+
struct ucsi_connector *con;
515+
int con_index;
342516
int ret;
343517

344518
mutex_lock(&uc->lock);
345519
pm_runtime_get_sync(uc->dev);
346520
set_bit(DEV_CMD_PENDING, &uc->flags);
347521

522+
if (offset == UCSI_CONTROL && val_len == sizeof(uc->last_cmd_sent)) {
523+
uc->last_cmd_sent = *(u64 *)val;
524+
525+
if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM &&
526+
uc->has_multiple_dp) {
527+
con_index = (uc->last_cmd_sent >> 16) &
528+
UCSI_CMD_CONNECTOR_MASK;
529+
con = &uc->ucsi->connector[con_index - 1];
530+
ucsi_ccg_update_set_new_cam_cmd(uc, con, (u64 *)val);
531+
}
532+
}
533+
348534
ret = ucsi_ccg_async_write(ucsi, offset, val, val_len);
349535
if (ret)
350536
goto err_clear_bit;
@@ -363,7 +549,8 @@ static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
363549
static const struct ucsi_operations ucsi_ccg_ops = {
364550
.read = ucsi_ccg_read,
365551
.sync_write = ucsi_ccg_sync_write,
366-
.async_write = ucsi_ccg_async_write
552+
.async_write = ucsi_ccg_async_write,
553+
.update_altmodes = ucsi_ccg_update_altmodes
367554
};
368555

369556
static irqreturn_t ccg_irq_handler(int irq, void *data)

0 commit comments

Comments
 (0)