Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / usb / typec / altmodes / displayport.c
1 // SPDX-License-Identifier: GPL-2.0
2 /**
3  * USB Typec-C DisplayPort Alternate Mode driver
4  *
5  * Copyright (C) 2018 Intel Corporation
6  * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
7  *
8  * DisplayPort is trademark of VESA (www.vesa.org)
9  */
10
11 #include <linux/delay.h>
12 #include <linux/mutex.h>
13 #include <linux/module.h>
14 #include <linux/usb/pd_vdo.h>
15 #include <linux/usb/typec_dp.h>
16
17 #define DP_HEADER(_dp, cmd)             (VDO((_dp)->alt->svid, 1, cmd) | \
18                                          VDO_OPOS(USB_TYPEC_DP_MODE))
19
20 enum {
21         DP_CONF_USB,
22         DP_CONF_DFP_D,
23         DP_CONF_UFP_D,
24         DP_CONF_DUAL_D,
25 };
26
27 /* Pin assignments that use USB3.1 Gen2 signaling to carry DP protocol */
28 #define DP_PIN_ASSIGN_GEN2_BR_MASK      (BIT(DP_PIN_ASSIGN_A) | \
29                                          BIT(DP_PIN_ASSIGN_B))
30
31 /* Pin assignments that use DP v1.3 signaling to carry DP protocol */
32 #define DP_PIN_ASSIGN_DP_BR_MASK        (BIT(DP_PIN_ASSIGN_C) | \
33                                          BIT(DP_PIN_ASSIGN_D) | \
34                                          BIT(DP_PIN_ASSIGN_E) | \
35                                          BIT(DP_PIN_ASSIGN_F))
36
37 /* DP only pin assignments */
38 #define DP_PIN_ASSIGN_DP_ONLY_MASK      (BIT(DP_PIN_ASSIGN_A) | \
39                                          BIT(DP_PIN_ASSIGN_C) | \
40                                          BIT(DP_PIN_ASSIGN_E))
41
42 /* Pin assignments where one channel is for USB */
43 #define DP_PIN_ASSIGN_MULTI_FUNC_MASK   (BIT(DP_PIN_ASSIGN_B) | \
44                                          BIT(DP_PIN_ASSIGN_D) | \
45                                          BIT(DP_PIN_ASSIGN_F))
46
47 enum dp_state {
48         DP_STATE_IDLE,
49         DP_STATE_ENTER,
50         DP_STATE_UPDATE,
51         DP_STATE_CONFIGURE,
52         DP_STATE_EXIT,
53 };
54
55 struct dp_altmode {
56         struct typec_displayport_data data;
57
58         enum dp_state state;
59
60         struct mutex lock; /* device lock */
61         struct work_struct work;
62         struct typec_altmode *alt;
63         const struct typec_altmode *port;
64 };
65
66 static int dp_altmode_notify(struct dp_altmode *dp)
67 {
68         u8 state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
69
70         return typec_altmode_notify(dp->alt, TYPEC_MODAL_STATE(state),
71                                    &dp->data);
72 }
73
74 static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
75 {
76         u32 conf = DP_CONF_SIGNALING_DP; /* Only DP signaling supported */
77         u8 pin_assign = 0;
78
79         switch (con) {
80         case DP_STATUS_CON_DISABLED:
81                 return 0;
82         case DP_STATUS_CON_DFP_D:
83                 conf |= DP_CONF_UFP_U_AS_DFP_D;
84                 pin_assign = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) &
85                              DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo);
86                 break;
87         case DP_STATUS_CON_UFP_D:
88         case DP_STATUS_CON_BOTH: /* NOTE: First acting as DP source */
89                 conf |= DP_CONF_UFP_U_AS_UFP_D;
90                 pin_assign = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo) &
91                              DP_CAP_UFP_D_PIN_ASSIGN(dp->port->vdo);
92                 break;
93         default:
94                 break;
95         }
96
97         /* Determining the initial pin assignment. */
98         if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) {
99                 /* Is USB together with DP preferred */
100                 if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC &&
101                     pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK)
102                         pin_assign &= DP_PIN_ASSIGN_MULTI_FUNC_MASK;
103                 else if (pin_assign & DP_PIN_ASSIGN_DP_ONLY_MASK)
104                         pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK;
105
106                 if (!pin_assign)
107                         return -EINVAL;
108
109                 conf |= DP_CONF_SET_PIN_ASSIGN(pin_assign);
110         }
111
112         dp->data.conf = conf;
113
114         return 0;
115 }
116
117 static int dp_altmode_status_update(struct dp_altmode *dp)
118 {
119         bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
120         u8 con = DP_STATUS_CONNECTION(dp->data.status);
121         int ret = 0;
122
123         if (configured && (dp->data.status & DP_STATUS_SWITCH_TO_USB)) {
124                 dp->data.conf = 0;
125                 dp->state = DP_STATE_CONFIGURE;
126         } else if (dp->data.status & DP_STATUS_EXIT_DP_MODE) {
127                 dp->state = DP_STATE_EXIT;
128         } else if (!(con & DP_CONF_CURRENTLY(dp->data.conf))) {
129                 ret = dp_altmode_configure(dp, con);
130                 if (!ret)
131                         dp->state = DP_STATE_CONFIGURE;
132         }
133
134         return ret;
135 }
136
137 static int dp_altmode_configured(struct dp_altmode *dp)
138 {
139         int ret;
140
141         sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration");
142
143         if (!dp->data.conf)
144                 return typec_altmode_notify(dp->alt, TYPEC_STATE_USB,
145                                             &dp->data);
146
147         ret = dp_altmode_notify(dp);
148         if (ret)
149                 return ret;
150
151         sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment");
152
153         return 0;
154 }
155
156 static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf)
157 {
158         u32 header = DP_HEADER(dp, DP_CMD_CONFIGURE);
159         int ret;
160
161         ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data);
162         if (ret) {
163                 dev_err(&dp->alt->dev,
164                         "unable to put to connector to safe mode\n");
165                 return ret;
166         }
167
168         ret = typec_altmode_vdm(dp->alt, header, &conf, 2);
169         if (ret) {
170                 if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf))
171                         dp_altmode_notify(dp);
172                 else
173                         typec_altmode_notify(dp->alt, TYPEC_STATE_USB,
174                                              &dp->data);
175         }
176
177         return ret;
178 }
179
180 static void dp_altmode_work(struct work_struct *work)
181 {
182         struct dp_altmode *dp = container_of(work, struct dp_altmode, work);
183         u32 header;
184         u32 vdo;
185         int ret;
186
187         mutex_lock(&dp->lock);
188
189         switch (dp->state) {
190         case DP_STATE_ENTER:
191                 ret = typec_altmode_enter(dp->alt);
192                 if (ret)
193                         dev_err(&dp->alt->dev, "failed to enter mode\n");
194                 break;
195         case DP_STATE_UPDATE:
196                 header = DP_HEADER(dp, DP_CMD_STATUS_UPDATE);
197                 vdo = 1;
198                 ret = typec_altmode_vdm(dp->alt, header, &vdo, 2);
199                 if (ret)
200                         dev_err(&dp->alt->dev,
201                                 "unable to send Status Update command (%d)\n",
202                                 ret);
203                 break;
204         case DP_STATE_CONFIGURE:
205                 ret = dp_altmode_configure_vdm(dp, dp->data.conf);
206                 if (ret)
207                         dev_err(&dp->alt->dev,
208                                 "unable to send Configure command (%d)\n", ret);
209                 break;
210         case DP_STATE_EXIT:
211                 if (typec_altmode_exit(dp->alt))
212                         dev_err(&dp->alt->dev, "Exit Mode Failed!\n");
213                 break;
214         default:
215                 break;
216         }
217
218         dp->state = DP_STATE_IDLE;
219
220         mutex_unlock(&dp->lock);
221 }
222
223 static void dp_altmode_attention(struct typec_altmode *alt, const u32 vdo)
224 {
225         struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
226         u8 old_state;
227
228         mutex_lock(&dp->lock);
229
230         old_state = dp->state;
231         dp->data.status = vdo;
232
233         if (old_state != DP_STATE_IDLE)
234                 dev_warn(&alt->dev, "ATTENTION while processing state %d\n",
235                          old_state);
236
237         if (dp_altmode_status_update(dp))
238                 dev_warn(&alt->dev, "%s: status update failed\n", __func__);
239
240         if (dp_altmode_notify(dp))
241                 dev_err(&alt->dev, "%s: notification failed\n", __func__);
242
243         if (old_state == DP_STATE_IDLE && dp->state != DP_STATE_IDLE)
244                 schedule_work(&dp->work);
245
246         mutex_unlock(&dp->lock);
247 }
248
249 static int dp_altmode_vdm(struct typec_altmode *alt,
250                           const u32 hdr, const u32 *vdo, int count)
251 {
252         struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
253         int cmd_type = PD_VDO_CMDT(hdr);
254         int cmd = PD_VDO_CMD(hdr);
255         int ret = 0;
256
257         mutex_lock(&dp->lock);
258
259         if (dp->state != DP_STATE_IDLE) {
260                 ret = -EBUSY;
261                 goto err_unlock;
262         }
263
264         switch (cmd_type) {
265         case CMDT_RSP_ACK:
266                 switch (cmd) {
267                 case CMD_ENTER_MODE:
268                         dp->state = DP_STATE_UPDATE;
269                         break;
270                 case CMD_EXIT_MODE:
271                         dp->data.status = 0;
272                         dp->data.conf = 0;
273                         break;
274                 case DP_CMD_STATUS_UPDATE:
275                         dp->data.status = *vdo;
276                         ret = dp_altmode_status_update(dp);
277                         break;
278                 case DP_CMD_CONFIGURE:
279                         ret = dp_altmode_configured(dp);
280                         break;
281                 default:
282                         break;
283                 }
284                 break;
285         case CMDT_RSP_NAK:
286                 switch (cmd) {
287                 case DP_CMD_CONFIGURE:
288                         dp->data.conf = 0;
289                         ret = dp_altmode_configured(dp);
290                         break;
291                 default:
292                         break;
293                 }
294                 break;
295         default:
296                 break;
297         }
298
299         if (dp->state != DP_STATE_IDLE)
300                 schedule_work(&dp->work);
301
302 err_unlock:
303         mutex_unlock(&dp->lock);
304         return ret;
305 }
306
307 static int dp_altmode_activate(struct typec_altmode *alt, int activate)
308 {
309         return activate ? typec_altmode_enter(alt) : typec_altmode_exit(alt);
310 }
311
312 static const struct typec_altmode_ops dp_altmode_ops = {
313         .attention = dp_altmode_attention,
314         .vdm = dp_altmode_vdm,
315         .activate = dp_altmode_activate,
316 };
317
318 static const char * const configurations[] = {
319         [DP_CONF_USB]   = "USB",
320         [DP_CONF_DFP_D] = "source",
321         [DP_CONF_UFP_D] = "sink",
322 };
323
324 static ssize_t
325 configuration_store(struct device *dev, struct device_attribute *attr,
326                     const char *buf, size_t size)
327 {
328         struct dp_altmode *dp = dev_get_drvdata(dev);
329         u32 conf;
330         u32 cap;
331         int con;
332         int ret = 0;
333
334         con = sysfs_match_string(configurations, buf);
335         if (con < 0)
336                 return con;
337
338         mutex_lock(&dp->lock);
339
340         if (dp->state != DP_STATE_IDLE) {
341                 ret = -EBUSY;
342                 goto err_unlock;
343         }
344
345         cap = DP_CAP_CAPABILITY(dp->alt->vdo);
346
347         if ((con == DP_CONF_DFP_D && !(cap & DP_CAP_DFP_D)) ||
348             (con == DP_CONF_UFP_D && !(cap & DP_CAP_UFP_D))) {
349                 ret = -EINVAL;
350                 goto err_unlock;
351         }
352
353         conf = dp->data.conf & ~DP_CONF_DUAL_D;
354         conf |= con;
355
356         if (dp->alt->active) {
357                 ret = dp_altmode_configure_vdm(dp, conf);
358                 if (ret)
359                         goto err_unlock;
360         }
361
362         dp->data.conf = conf;
363
364 err_unlock:
365         mutex_unlock(&dp->lock);
366
367         return ret ? ret : size;
368 }
369
370 static ssize_t configuration_show(struct device *dev,
371                                   struct device_attribute *attr, char *buf)
372 {
373         struct dp_altmode *dp = dev_get_drvdata(dev);
374         int len;
375         u8 cap;
376         u8 cur;
377         int i;
378
379         mutex_lock(&dp->lock);
380
381         cap = DP_CAP_CAPABILITY(dp->alt->vdo);
382         cur = DP_CONF_CURRENTLY(dp->data.conf);
383
384         len = sprintf(buf, "%s ", cur ? "USB" : "[USB]");
385
386         for (i = 1; i < ARRAY_SIZE(configurations); i++) {
387                 if (i == cur)
388                         len += sprintf(buf + len, "[%s] ", configurations[i]);
389                 else if ((i == DP_CONF_DFP_D && cap & DP_CAP_DFP_D) ||
390                          (i == DP_CONF_UFP_D && cap & DP_CAP_UFP_D))
391                         len += sprintf(buf + len, "%s ", configurations[i]);
392         }
393
394         mutex_unlock(&dp->lock);
395
396         buf[len - 1] = '\n';
397         return len;
398 }
399 static DEVICE_ATTR_RW(configuration);
400
401 static const char * const pin_assignments[] = {
402         [DP_PIN_ASSIGN_A] = "A",
403         [DP_PIN_ASSIGN_B] = "B",
404         [DP_PIN_ASSIGN_C] = "C",
405         [DP_PIN_ASSIGN_D] = "D",
406         [DP_PIN_ASSIGN_E] = "E",
407         [DP_PIN_ASSIGN_F] = "F",
408 };
409
410 static ssize_t
411 pin_assignment_store(struct device *dev, struct device_attribute *attr,
412                      const char *buf, size_t size)
413 {
414         struct dp_altmode *dp = dev_get_drvdata(dev);
415         u8 assignments;
416         u32 conf;
417         int ret;
418
419         ret = sysfs_match_string(pin_assignments, buf);
420         if (ret < 0)
421                 return ret;
422
423         conf = DP_CONF_SET_PIN_ASSIGN(BIT(ret));
424         ret = 0;
425
426         mutex_lock(&dp->lock);
427
428         if (conf & dp->data.conf)
429                 goto out_unlock;
430
431         if (dp->state != DP_STATE_IDLE) {
432                 ret = -EBUSY;
433                 goto out_unlock;
434         }
435
436         if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D)
437                 assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo);
438         else
439                 assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo);
440
441         if (!(DP_CONF_GET_PIN_ASSIGN(conf) & assignments)) {
442                 ret = -EINVAL;
443                 goto out_unlock;
444         }
445
446         conf |= dp->data.conf & ~DP_CONF_PIN_ASSIGNEMENT_MASK;
447
448         /* Only send Configure command if a configuration has been set */
449         if (dp->alt->active && DP_CONF_CURRENTLY(dp->data.conf)) {
450                 ret = dp_altmode_configure_vdm(dp, conf);
451                 if (ret)
452                         goto out_unlock;
453         }
454
455         dp->data.conf = conf;
456
457 out_unlock:
458         mutex_unlock(&dp->lock);
459
460         return ret ? ret : size;
461 }
462
463 static ssize_t pin_assignment_show(struct device *dev,
464                                    struct device_attribute *attr, char *buf)
465 {
466         struct dp_altmode *dp = dev_get_drvdata(dev);
467         u8 assignments;
468         int len = 0;
469         u8 cur;
470         int i;
471
472         mutex_lock(&dp->lock);
473
474         cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
475
476         if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D)
477                 assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo);
478         else
479                 assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo);
480
481         for (i = 0; assignments; assignments >>= 1, i++) {
482                 if (assignments & 1) {
483                         if (i == cur)
484                                 len += sprintf(buf + len, "[%s] ",
485                                                pin_assignments[i]);
486                         else
487                                 len += sprintf(buf + len, "%s ",
488                                                pin_assignments[i]);
489                 }
490         }
491
492         mutex_unlock(&dp->lock);
493
494         buf[len - 1] = '\n';
495         return len;
496 }
497 static DEVICE_ATTR_RW(pin_assignment);
498
499 static struct attribute *dp_altmode_attrs[] = {
500         &dev_attr_configuration.attr,
501         &dev_attr_pin_assignment.attr,
502         NULL
503 };
504
505 static const struct attribute_group dp_altmode_group = {
506         .name = "displayport",
507         .attrs = dp_altmode_attrs,
508 };
509
510 int dp_altmode_probe(struct typec_altmode *alt)
511 {
512         const struct typec_altmode *port = typec_altmode_get_partner(alt);
513         struct dp_altmode *dp;
514         int ret;
515
516         /* FIXME: Port can only be DFP_U. */
517
518         /* Make sure we have compatiple pin configurations */
519         if (!(DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) &
520               DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo)) &&
521             !(DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) &
522               DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo)))
523                 return -ENODEV;
524
525         ret = sysfs_create_group(&alt->dev.kobj, &dp_altmode_group);
526         if (ret)
527                 return ret;
528
529         dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
530         if (!dp)
531                 return -ENOMEM;
532
533         INIT_WORK(&dp->work, dp_altmode_work);
534         mutex_init(&dp->lock);
535         dp->port = port;
536         dp->alt = alt;
537
538         alt->desc = "DisplayPort";
539         alt->ops = &dp_altmode_ops;
540
541         typec_altmode_set_drvdata(alt, dp);
542
543         dp->state = DP_STATE_ENTER;
544         schedule_work(&dp->work);
545
546         return 0;
547 }
548 EXPORT_SYMBOL_GPL(dp_altmode_probe);
549
550 void dp_altmode_remove(struct typec_altmode *alt)
551 {
552         struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
553
554         sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group);
555         cancel_work_sync(&dp->work);
556 }
557 EXPORT_SYMBOL_GPL(dp_altmode_remove);
558
559 static const struct typec_device_id dp_typec_id[] = {
560         { USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE },
561         { },
562 };
563 MODULE_DEVICE_TABLE(typec, dp_typec_id);
564
565 static struct typec_altmode_driver dp_altmode_driver = {
566         .id_table = dp_typec_id,
567         .probe = dp_altmode_probe,
568         .remove = dp_altmode_remove,
569         .driver = {
570                 .name = "typec_displayport",
571                 .owner = THIS_MODULE,
572         },
573 };
574 module_typec_altmode_driver(dp_altmode_driver);
575
576 MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
577 MODULE_LICENSE("GPL v2");
578 MODULE_DESCRIPTION("DisplayPort Alternate Mode");