@@ -50,13 +50,17 @@ enum {
50
50
enum dp_state {
51
51
DP_STATE_IDLE ,
52
52
DP_STATE_ENTER ,
53
+ DP_STATE_ENTER_PRIME ,
53
54
DP_STATE_UPDATE ,
54
55
DP_STATE_CONFIGURE ,
56
+ DP_STATE_CONFIGURE_PRIME ,
55
57
DP_STATE_EXIT ,
58
+ DP_STATE_EXIT_PRIME ,
56
59
};
57
60
58
61
struct dp_altmode {
59
62
struct typec_displayport_data data ;
63
+ struct typec_displayport_data data_prime ;
60
64
61
65
enum dp_state state ;
62
66
bool hpd ;
@@ -67,6 +71,7 @@ struct dp_altmode {
67
71
struct typec_altmode * alt ;
68
72
const struct typec_altmode * port ;
69
73
struct fwnode_handle * connector_fwnode ;
74
+ struct typec_altmode * plug_prime ;
70
75
};
71
76
72
77
static int dp_altmode_notify (struct dp_altmode * dp )
@@ -99,12 +104,18 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
99
104
conf |= DP_CONF_UFP_U_AS_DFP_D ;
100
105
pin_assign = DP_CAP_UFP_D_PIN_ASSIGN (dp -> alt -> vdo ) &
101
106
DP_CAP_DFP_D_PIN_ASSIGN (dp -> port -> vdo );
107
+ /* Account for active cable capabilities */
108
+ if (dp -> plug_prime )
109
+ pin_assign &= DP_CAP_DFP_D_PIN_ASSIGN (dp -> plug_prime -> vdo );
102
110
break ;
103
111
case DP_STATUS_CON_UFP_D :
104
112
case DP_STATUS_CON_BOTH : /* NOTE: First acting as DP source */
105
113
conf |= DP_CONF_UFP_U_AS_UFP_D ;
106
114
pin_assign = DP_CAP_PIN_ASSIGN_UFP_D (dp -> alt -> vdo ) &
107
115
DP_CAP_PIN_ASSIGN_DFP_D (dp -> port -> vdo );
116
+ /* Account for active cable capabilities */
117
+ if (dp -> plug_prime )
118
+ pin_assign &= DP_CAP_UFP_D_PIN_ASSIGN (dp -> plug_prime -> vdo );
108
119
break ;
109
120
default :
110
121
break ;
@@ -130,6 +141,8 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
130
141
}
131
142
132
143
dp -> data .conf = conf ;
144
+ if (dp -> plug_prime )
145
+ dp -> data_prime .conf = conf ;
133
146
134
147
return 0 ;
135
148
}
@@ -143,7 +156,9 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
143
156
144
157
if (configured && (dp -> data .status & DP_STATUS_SWITCH_TO_USB )) {
145
158
dp -> data .conf = 0 ;
146
- dp -> state = DP_STATE_CONFIGURE ;
159
+ dp -> data_prime .conf = 0 ;
160
+ dp -> state = dp -> plug_prime ? DP_STATE_CONFIGURE_PRIME :
161
+ DP_STATE_CONFIGURE ;
147
162
} else if (dp -> data .status & DP_STATUS_EXIT_DP_MODE ) {
148
163
dp -> state = DP_STATE_EXIT ;
149
164
} else if (!(con & DP_CONF_CURRENTLY (dp -> data .conf ))) {
@@ -209,6 +224,19 @@ static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf)
209
224
return ret ;
210
225
}
211
226
227
+ static int dp_altmode_configure_vdm_cable (struct dp_altmode * dp , u32 conf )
228
+ {
229
+ int svdm_version = typec_altmode_get_cable_svdm_version (dp -> plug_prime );
230
+ u32 header ;
231
+
232
+ if (svdm_version < 0 )
233
+ return svdm_version ;
234
+
235
+ header = DP_HEADER (dp , svdm_version , DP_CMD_CONFIGURE );
236
+
237
+ return typec_cable_altmode_vdm (dp -> plug_prime , TYPEC_PLUG_SOP_P , header , & conf , 2 );
238
+ }
239
+
212
240
static void dp_altmode_work (struct work_struct * work )
213
241
{
214
242
struct dp_altmode * dp = container_of (work , struct dp_altmode , work );
@@ -225,6 +253,19 @@ static void dp_altmode_work(struct work_struct *work)
225
253
if (ret && ret != - EBUSY )
226
254
dev_err (& dp -> alt -> dev , "failed to enter mode\n" );
227
255
break ;
256
+ case DP_STATE_ENTER_PRIME :
257
+ ret = typec_cable_altmode_enter (dp -> alt , TYPEC_PLUG_SOP_P , NULL );
258
+ /*
259
+ * If we fail to enter Alt Mode on SOP', then we should drop the
260
+ * plug from the driver and attempt to run the driver without
261
+ * it.
262
+ */
263
+ if (ret && ret != - EBUSY ) {
264
+ dev_err (& dp -> alt -> dev , "plug failed to enter mode\n" );
265
+ dp -> state = DP_STATE_ENTER ;
266
+ goto disable_prime ;
267
+ }
268
+ break ;
228
269
case DP_STATE_UPDATE :
229
270
svdm_version = typec_altmode_get_svdm_version (dp -> alt );
230
271
if (svdm_version < 0 )
@@ -243,17 +284,38 @@ static void dp_altmode_work(struct work_struct *work)
243
284
dev_err (& dp -> alt -> dev ,
244
285
"unable to send Configure command (%d)\n" , ret );
245
286
break ;
287
+ case DP_STATE_CONFIGURE_PRIME :
288
+ ret = dp_altmode_configure_vdm_cable (dp , dp -> data_prime .conf );
289
+ if (ret ) {
290
+ dev_err (& dp -> plug_prime -> dev ,
291
+ "unable to send Configure command (%d)\n" ,
292
+ ret );
293
+ dp -> state = DP_STATE_CONFIGURE ;
294
+ goto disable_prime ;
295
+ }
296
+ break ;
246
297
case DP_STATE_EXIT :
247
298
if (typec_altmode_exit (dp -> alt ))
248
299
dev_err (& dp -> alt -> dev , "Exit Mode Failed!\n" );
249
300
break ;
301
+ case DP_STATE_EXIT_PRIME :
302
+ if (typec_cable_altmode_exit (dp -> plug_prime , TYPEC_PLUG_SOP_P ))
303
+ dev_err (& dp -> plug_prime -> dev , "Exit Mode Failed!\n" );
304
+ break ;
250
305
default :
251
306
break ;
252
307
}
253
308
254
309
dp -> state = DP_STATE_IDLE ;
255
310
256
311
mutex_unlock (& dp -> lock );
312
+ return ;
313
+
314
+ disable_prime :
315
+ typec_altmode_put_plug (dp -> plug_prime );
316
+ dp -> plug_prime = NULL ;
317
+ schedule_work (& dp -> work );
318
+ mutex_unlock (& dp -> lock );
257
319
}
258
320
259
321
static void dp_altmode_attention (struct typec_altmode * alt , const u32 vdo )
@@ -314,6 +376,8 @@ static int dp_altmode_vdm(struct typec_altmode *alt,
314
376
dp -> hpd = false;
315
377
sysfs_notify (& dp -> alt -> dev .kobj , "displayport" , "hpd" );
316
378
}
379
+ if (dp -> plug_prime )
380
+ dp -> state = DP_STATE_EXIT_PRIME ;
317
381
break ;
318
382
case DP_CMD_STATUS_UPDATE :
319
383
dp -> data .status = * vdo ;
@@ -348,10 +412,84 @@ static int dp_altmode_vdm(struct typec_altmode *alt,
348
412
return ret ;
349
413
}
350
414
415
+ static int dp_cable_altmode_vdm (struct typec_altmode * alt , enum typec_plug_index sop ,
416
+ const u32 hdr , const u32 * vdo , int count )
417
+ {
418
+ struct dp_altmode * dp = typec_altmode_get_drvdata (alt );
419
+ int cmd_type = PD_VDO_CMDT (hdr );
420
+ int cmd = PD_VDO_CMD (hdr );
421
+ int ret = 0 ;
422
+
423
+ mutex_lock (& dp -> lock );
424
+
425
+ if (dp -> state != DP_STATE_IDLE ) {
426
+ ret = - EBUSY ;
427
+ goto err_unlock ;
428
+ }
429
+
430
+ switch (cmd_type ) {
431
+ case CMDT_RSP_ACK :
432
+ switch (cmd ) {
433
+ case CMD_ENTER_MODE :
434
+ typec_altmode_update_active (dp -> plug_prime , true);
435
+ dp -> state = DP_STATE_ENTER ;
436
+ break ;
437
+ case CMD_EXIT_MODE :
438
+ dp -> data_prime .status = 0 ;
439
+ dp -> data_prime .conf = 0 ;
440
+ typec_altmode_update_active (dp -> plug_prime , false);
441
+ break ;
442
+ case DP_CMD_CONFIGURE :
443
+ dp -> state = DP_STATE_CONFIGURE ;
444
+ break ;
445
+ default :
446
+ break ;
447
+ }
448
+ break ;
449
+ case CMDT_RSP_NAK :
450
+ switch (cmd ) {
451
+ case DP_CMD_CONFIGURE :
452
+ dp -> data_prime .conf = 0 ;
453
+ /* Attempt to configure on SOP, drop plug */
454
+ typec_altmode_put_plug (dp -> plug_prime );
455
+ dp -> plug_prime = NULL ;
456
+ dp -> state = DP_STATE_CONFIGURE ;
457
+ break ;
458
+ default :
459
+ break ;
460
+ }
461
+ break ;
462
+ default :
463
+ break ;
464
+ }
465
+
466
+ if (dp -> state != DP_STATE_IDLE )
467
+ schedule_work (& dp -> work );
468
+
469
+ err_unlock :
470
+ mutex_unlock (& dp -> lock );
471
+ return ret ;
472
+ }
473
+
351
474
static int dp_altmode_activate (struct typec_altmode * alt , int activate )
352
475
{
353
- return activate ? typec_altmode_enter (alt , NULL ) :
354
- typec_altmode_exit (alt );
476
+ struct dp_altmode * dp = typec_altmode_get_drvdata (alt );
477
+ int ret ;
478
+
479
+ if (activate ) {
480
+ if (dp -> plug_prime ) {
481
+ ret = typec_cable_altmode_enter (alt , TYPEC_PLUG_SOP_P , NULL );
482
+ if (ret < 0 ) {
483
+ typec_altmode_put_plug (dp -> plug_prime );
484
+ dp -> plug_prime = NULL ;
485
+ } else {
486
+ return ret ;
487
+ }
488
+ }
489
+ return typec_altmode_enter (alt , NULL );
490
+ } else {
491
+ return typec_altmode_exit (alt );
492
+ }
355
493
}
356
494
357
495
static const struct typec_altmode_ops dp_altmode_ops = {
@@ -360,6 +498,10 @@ static const struct typec_altmode_ops dp_altmode_ops = {
360
498
.activate = dp_altmode_activate ,
361
499
};
362
500
501
+ static const struct typec_cable_ops dp_cable_ops = {
502
+ .vdm = dp_cable_altmode_vdm ,
503
+ };
504
+
363
505
static const char * const configurations [] = {
364
506
[DP_CONF_USB ] = "USB" ,
365
507
[DP_CONF_DFP_D ] = "source" ,
@@ -501,6 +643,7 @@ pin_assignment_store(struct device *dev, struct device_attribute *attr,
501
643
502
644
/* Only send Configure command if a configuration has been set */
503
645
if (dp -> alt -> active && DP_CONF_CURRENTLY (dp -> data .conf )) {
646
+ /* todo: send manual configure over SOP'*/
504
647
ret = dp_altmode_configure_vdm (dp , conf );
505
648
if (ret )
506
649
goto out_unlock ;
@@ -574,6 +717,7 @@ static const struct attribute_group dp_altmode_group = {
574
717
int dp_altmode_probe (struct typec_altmode * alt )
575
718
{
576
719
const struct typec_altmode * port = typec_altmode_get_partner (alt );
720
+ struct typec_altmode * plug = typec_altmode_get_plug (alt , TYPEC_PLUG_SOP_P );
577
721
struct fwnode_handle * fwnode ;
578
722
struct dp_altmode * dp ;
579
723
int ret ;
@@ -603,6 +747,13 @@ int dp_altmode_probe(struct typec_altmode *alt)
603
747
alt -> desc = "DisplayPort" ;
604
748
alt -> ops = & dp_altmode_ops ;
605
749
750
+ if (plug ) {
751
+ plug -> desc = "Displayport" ;
752
+ plug -> cable_ops = & dp_cable_ops ;
753
+ }
754
+
755
+ dp -> plug_prime = plug ;
756
+
606
757
fwnode = dev_fwnode (alt -> dev .parent -> parent ); /* typec_port fwnode */
607
758
if (fwnode_property_present (fwnode , "displayport" ))
608
759
dp -> connector_fwnode = fwnode_find_reference (fwnode , "displayport" , 0 );
@@ -612,8 +763,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
612
763
dp -> connector_fwnode = NULL ;
613
764
614
765
typec_altmode_set_drvdata (alt , dp );
766
+ if (plug )
767
+ typec_altmode_set_drvdata (plug , dp );
615
768
616
- dp -> state = DP_STATE_ENTER ;
769
+ dp -> state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER ;
617
770
schedule_work (& dp -> work );
618
771
619
772
return 0 ;
@@ -626,6 +779,7 @@ void dp_altmode_remove(struct typec_altmode *alt)
626
779
627
780
sysfs_remove_group (& alt -> dev .kobj , & dp_altmode_group );
628
781
cancel_work_sync (& dp -> work );
782
+ typec_altmode_put_plug (dp -> plug_prime );
629
783
630
784
if (dp -> connector_fwnode ) {
631
785
drm_connector_oob_hotplug_event (dp -> connector_fwnode ,
0 commit comments