Re: [RFC] usb: typec: ucsi: add support for separate DP altmode devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Heikki,

> On Jun 28, 2019, at 3:47 AM, Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> wrote:
> 
>> On Thu, Jun 27, 2019 at 10:45:47PM -0700, Ajay Gupta wrote:
>> 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.
> 
> I don't have any better ideas how to solve this at them moment.
> Therefore, I don't have a problem with going forward with this.

Thanks, please review the change and provide any comment. I will anyways repost this as a patch.

Thanks 
> nvpublic 
> But
> please note, that we are already at -rc6, so nothing is going to
> happen for a while. I'll away for the next four weeks.
> 
>> Signed-off-by: Ajay Gupta <ajayg@xxxxxxxxxx>
>> ---
>> drivers/usb/typec/ucsi/ucsi.c | 191 +++++++++++++++++++++++++++++++++-
>> drivers/usb/typec/ucsi/ucsi.h |  12 +++
>> 2 files changed, 199 insertions(+), 4 deletions(-)
>> 
>> diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
>> index ba288b964dc8..491b5c925a90 100644
>> --- a/drivers/usb/typec/ucsi/ucsi.c
>> +++ b/drivers/usb/typec/ucsi/ucsi.c
>> @@ -39,8 +39,27 @@
>>  */
>> #define UCSI_SWAP_TIMEOUT_MS    5000
>> 
>> +static void ucsi_update_get_current_cam_cmd(struct ucsi_connector *con,
>> +                        struct ucsi_data *data)
>> +{
>> +    u8 cam, new_cam;
>> +
>> +    if (data->cci.data_length == 0x1) {
>> +        cam = data->message_in[0];
>> +        new_cam = con->port_alt[cam].linked_idx;
>> +        data->message_in[0] = new_cam;
>> +        con->new_port_alt[new_cam].active_idx = cam;
>> +    }
>> +}
>> +
>> static inline int ucsi_sync(struct ucsi *ucsi)
>> {
>> +    struct ucsi_connector *con = ucsi->connector;
>> +    struct ucsi_data *data = ucsi->ppm->data;
>> +
>> +    if (data->ctrl.alt.cmd == UCSI_GET_CURRENT_CAM && con->has_multiple_dp)
>> +        ucsi_update_get_current_cam_cmd(con, data);
>> +
>>    if (ucsi->ppm && ucsi->ppm->sync)
>>        return ucsi->ppm->sync(ucsi->ppm);
>>    return 0;
>> @@ -101,14 +120,57 @@ static int ucsi_ack(struct ucsi *ucsi, u8 ack)
>>    return ret;
>> }
>> 
>> +static void ucsi_update_set_new_cam_cmd(struct ucsi_connector *con,
>> +                    struct ucsi_control *ctrl)
>> +{
>> +    struct new_ucsi_altmode *new_port, *port;
>> +    struct typec_altmode *partner = NULL;
>> +    u64 cmd;
>> +    u8 new_cam, cam;
>> +    bool enter_new_mode;
>> +    int i, j;
>> +
>> +    cmd = ctrl->raw_cmd;
>> +    new_cam = (cmd >> 24) & 0xff;
>> +    new_port = &con->new_port_alt[new_cam];
>> +    cam = new_port->linked_idx;
>> +    enter_new_mode = (cmd >> 23) & 1;
>> +
>> +    if (cam == UCSI_MULTI_LINKED_INDEX) {
>> +        if (enter_new_mode) {
>> +            port = con->port_alt;
>> +            for (i = 0; con->partner_altmode[i]; i++) {
>> +                partner = con->partner_altmode[i];
>> +                if (partner->svid == new_port->svid)
>> +                    break;
>> +            }
>> +            for (j = 0; port[j].svid; j++)
>> +                if (partner && port[j].svid == partner->svid &&
>> +                    port[j].mid & partner->vdo)
>> +                    break;
>> +            cam = j;
>> +            new_port->active_idx = cam;
>> +        } else {
>> +            cam = new_port->active_idx;
>> +        }
>> +    }
>> +    cmd &= ~(0xff << 24);
>> +    cmd |= (cam << 24);
>> +    ctrl->raw_cmd = cmd;
>> +}
>> +
>> static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
>>                void *data, size_t size)
>> {
>>    struct ucsi_control _ctrl;
>> +    struct ucsi_connector *con = ucsi->connector;
>>    u8 data_length;
>>    u16 error;
>>    int ret;
>> 
>> +    if (ctrl->alt.cmd == UCSI_SET_NEW_CAM && con->has_multiple_dp)
>> +        ucsi_update_set_new_cam_cmd(con, ctrl);
>> +
>>    ret = ucsi_command(ucsi, ctrl);
>>    if (ret)
>>        goto err;
>> @@ -364,10 +426,24 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
>> 
>>    for (i = 0; i < max_altmodes;) {
>>        memset(alt, 0, sizeof(alt));
>> -        UCSI_CMD_GET_ALTERNATE_MODES(ctrl, recipient, con->num, i, 1);
>> -        len = ucsi_run_command(con->ucsi, &ctrl, alt, sizeof(alt));
>> -        if (len <= 0)
>> -            return len;
>> +
>> +        if (recipient == UCSI_RECIPIENT_CON) {
>> +            if (con->has_multiple_dp) {
>> +                alt[0].svid = con->new_port_alt[i].svid;
>> +                alt[0].mid = con->new_port_alt[i].mid;
>> +            } else {
>> +                alt[0].svid = con->port_alt[i].svid;
>> +                alt[0].mid = con->port_alt[i].mid;
>> +            }
>> +            len = sizeof(alt[0]);
>> +        } else {
>> +            UCSI_CMD_GET_ALTERNATE_MODES(ctrl, recipient,
>> +                             con->num, i, 1);
>> +            len = ucsi_run_command(con->ucsi, &ctrl, alt,
>> +                           sizeof(alt));
>> +            if (len <= 0)
>> +                return len;
>> +        }
>> 
>>        /*
>>         * This code is requesting one alt mode at a time, but some PPMs
>> @@ -521,6 +597,103 @@ static void ucsi_partner_change(struct ucsi_connector *con)
>>        ucsi_altmode_update_active(con);
>> }
>> 
>> +static void ucsi_update_con_altmodes(struct ucsi_connector *con)
>> +{
>> +    int max_altmodes = con->ucsi->cap.num_alt_modes;
>> +    struct new_ucsi_altmode *alt, *new_alt;
>> +    int i, j, k = 0;
>> +    bool found = false;
>> +
>> +    alt = con->port_alt;
>> +    new_alt = con->new_port_alt;
>> +    memset(con->new_port_alt, 0, sizeof(con->new_port_alt));
>> +
>> +    for (i = 0; i < max_altmodes; i++) {
>> +        if (!alt[i].svid)
>> +            break;
>> +
>> +        /* already checked and considered */
>> +        if (alt[i].checked)
>> +            continue;
>> +
>> +        if (!DP_CONF_GET_PIN_ASSIGN(alt[i].mid)) {
>> +            /* Found Non DP altmode */
>> +            new_alt[k].svid = alt[i].svid;
>> +            new_alt[k].mid |= alt[i].mid;
>> +            new_alt[k].linked_idx = i;
>> +            alt[i].linked_idx = k;
>> +            k++;
>> +            continue;
>> +        }
>> +
>> +        for (j = i + 1; j < max_altmodes; j++) {
>> +            if (alt[i].svid != alt[j].svid ||
>> +                !DP_CONF_GET_PIN_ASSIGN(alt[j].mid)) {
>> +                continue;
>> +            } else {
>> +                /* Found duplicate DP mode */
>> +                new_alt[k].svid = alt[i].svid;
>> +                new_alt[k].mid |= alt[i].mid | alt[j].mid;
>> +                new_alt[k].linked_idx = UCSI_MULTI_LINKED_INDEX;
>> +                alt[i].linked_idx = k;
>> +                alt[j].linked_idx = k;
>> +                alt[j].checked = true;
>> +                found = true;
>> +            }
>> +        }
>> +        if (found) {
>> +            con->has_multiple_dp = true;
>> +        } else {
>> +            /* Didn't find any duplicate DP altmode */
>> +            new_alt[k].svid = alt[i].svid;
>> +            new_alt[k].mid |= alt[i].mid;
>> +            new_alt[k].linked_idx = i;
>> +            alt[i].linked_idx = k;
>> +        }
>> +        k++;
>> +    }
>> +}
>> +
>> +static int ucsi_get_altmodes(struct ucsi_connector *con)
>> +{
>> +    int max_altmodes = con->ucsi->cap.num_alt_modes;
>> +    struct ucsi_altmode alt[2];
>> +    struct ucsi_control ctrl;
>> +    int num = 1;
>> +    int len;
>> +    int j;
>> +    int i;
>> +    int k = 0;
>> +
>> +    memset(con->port_alt, 0, sizeof(con->port_alt));
>> +
>> +    for (i = 0; i < max_altmodes;) {
>> +        memset(alt, 0, sizeof(alt));
>> +        UCSI_CMD_GET_ALTERNATE_MODES(ctrl, UCSI_RECIPIENT_CON,
>> +                         con->num, i, 1);
>> +        len = ucsi_run_command(con->ucsi, &ctrl, alt, sizeof(alt));
>> +        if (len <= 0)
>> +            return len;
>> +
>> +        /*
>> +         * This code is requesting one alt mode at a time, but some PPMs
>> +         * may still return two.
>> +         */
>> +        num = len / sizeof(alt[0]);
>> +        i += num;
>> +
>> +        for (j = 0; j < num; j++) {
>> +            if (!alt[j].svid)
>> +                return 0;
>> +
>> +            con->port_alt[k].mid = alt[j].mid;
>> +            con->port_alt[k].svid = alt[j].svid;
>> +            k++;
>> +        }
>> +    }
>> +    return 0;
>> +}
>> +
>> static void ucsi_connector_change(struct work_struct *work)
>> {
>>    struct ucsi_connector *con = container_of(work, struct ucsi_connector,
>> @@ -851,6 +1024,16 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
>>    if (IS_ERR(con->port))
>>        return PTR_ERR(con->port);
>> 
>> +    /* Get Alternate modes */
>> +    ret = ucsi_get_altmodes(con);
>> +    if (ret) {
>> +        dev_err(ucsi->dev,
>> +            "%s: con%d failed (%d)to get port alternate modes\n",
>> +            __func__, con->num, ret);
>> +        return 0;
>> +    }
>> +    ucsi_update_con_altmodes(con);
>> +
>>    /* Alternate modes */
>>    ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON);
>>    if (ret)
>> diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
>> index de87d0b8319d..7bbdf83c8d4a 100644
>> --- a/drivers/usb/typec/ucsi/ucsi.h
>> +++ b/drivers/usb/typec/ucsi/ucsi.h
>> @@ -274,6 +274,15 @@ struct ucsi_connector_capability {
>>    u8:6; /* reserved */
>> } __packed;
>> 
>> +struct new_ucsi_altmode {
>> +    u16 svid;
>> +    u32 mid;
>> +    u8 linked_idx;
>> +    u8 active_idx;
>> +#define UCSI_MULTI_LINKED_INDEX    (0xff)
>> +    bool checked;
>> +} __packed;
>> +
>> struct ucsi_altmode {
>>    u16 svid;
>>    u32 mid;
>> @@ -408,6 +417,7 @@ struct ucsi {
>> 
>> struct ucsi_connector {
>>    int num;
>> +    bool has_multiple_dp;
>> 
>>    struct ucsi *ucsi;
>>    struct mutex lock; /* port lock */
>> @@ -424,6 +434,8 @@ struct ucsi_connector {
>> 
>>    struct ucsi_connector_status status;
>>    struct ucsi_connector_capability cap;
>> +    struct new_ucsi_altmode port_alt[UCSI_MAX_ALTMODES];
>> +    struct new_ucsi_altmode new_port_alt[UCSI_MAX_ALTMODES];
>> };
>> 
>> int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
>> -- 
>> 2.17.1
> 
> thanks,
> 
> -- 
> heikki




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux