usb: typec: altmodes/displayport: add SOP' support
[linux-block.git] / drivers / usb / typec / altmodes / displayport.c
CommitLineData
0e3bb7d6 1// SPDX-License-Identifier: GPL-2.0
50c9bd05 2/*
0e3bb7d6
HK
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>
7f811394 14#include <linux/property.h>
0e3bb7d6
HK
15#include <linux/usb/pd_vdo.h>
16#include <linux/usb/typec_dp.h>
7f811394 17#include <drm/drm_connector.h>
318ebed2 18#include "displayport.h"
0e3bb7d6 19
4c93cad8
KT
20#define DP_HEADER(_dp, ver, cmd) (VDO((_dp)->alt->svid, 1, ver, cmd) \
21 | VDO_OPOS(USB_TYPEC_DP_MODE))
0e3bb7d6
HK
22
23enum {
24 DP_CONF_USB,
25 DP_CONF_DFP_D,
26 DP_CONF_UFP_D,
27 DP_CONF_DUAL_D,
28};
29
0e3bb7d6
HK
30/* Pin assignments that use USB3.1 Gen2 signaling to carry DP protocol */
31#define DP_PIN_ASSIGN_GEN2_BR_MASK (BIT(DP_PIN_ASSIGN_A) | \
32 BIT(DP_PIN_ASSIGN_B))
33
34/* Pin assignments that use DP v1.3 signaling to carry DP protocol */
35#define DP_PIN_ASSIGN_DP_BR_MASK (BIT(DP_PIN_ASSIGN_C) | \
36 BIT(DP_PIN_ASSIGN_D) | \
37 BIT(DP_PIN_ASSIGN_E) | \
38 BIT(DP_PIN_ASSIGN_F))
39
40/* DP only pin assignments */
41#define DP_PIN_ASSIGN_DP_ONLY_MASK (BIT(DP_PIN_ASSIGN_A) | \
42 BIT(DP_PIN_ASSIGN_C) | \
43 BIT(DP_PIN_ASSIGN_E))
44
45/* Pin assignments where one channel is for USB */
46#define DP_PIN_ASSIGN_MULTI_FUNC_MASK (BIT(DP_PIN_ASSIGN_B) | \
47 BIT(DP_PIN_ASSIGN_D) | \
48 BIT(DP_PIN_ASSIGN_F))
49
50enum dp_state {
51 DP_STATE_IDLE,
52 DP_STATE_ENTER,
71ba4fe5 53 DP_STATE_ENTER_PRIME,
0e3bb7d6
HK
54 DP_STATE_UPDATE,
55 DP_STATE_CONFIGURE,
71ba4fe5 56 DP_STATE_CONFIGURE_PRIME,
0e3bb7d6 57 DP_STATE_EXIT,
71ba4fe5 58 DP_STATE_EXIT_PRIME,
0e3bb7d6
HK
59};
60
61struct dp_altmode {
62 struct typec_displayport_data data;
71ba4fe5 63 struct typec_displayport_data data_prime;
0e3bb7d6
HK
64
65 enum dp_state state;
7f811394 66 bool hpd;
5a5ccd61 67 bool pending_hpd;
0e3bb7d6
HK
68
69 struct mutex lock; /* device lock */
70 struct work_struct work;
71 struct typec_altmode *alt;
72 const struct typec_altmode *port;
7f811394 73 struct fwnode_handle *connector_fwnode;
71ba4fe5 74 struct typec_altmode *plug_prime;
0e3bb7d6
HK
75};
76
77static int dp_altmode_notify(struct dp_altmode *dp)
78{
fc27e046
HG
79 unsigned long conf;
80 u8 state;
81
82 if (dp->data.conf) {
83 state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
84 conf = TYPEC_MODAL_STATE(state);
85 } else {
86 conf = TYPEC_STATE_USB;
87 }
0e3bb7d6 88
fc27e046 89 return typec_altmode_notify(dp->alt, conf, &dp->data);
0e3bb7d6
HK
90}
91
92static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
93{
0e3bb7d6 94 u8 pin_assign = 0;
a17fae8f
UP
95 u32 conf;
96
97 /* DP Signalling */
98 conf = (dp->data.conf & DP_CONF_SIGNALLING_MASK) >> DP_CONF_SIGNALLING_SHIFT;
0e3bb7d6
HK
99
100 switch (con) {
101 case DP_STATUS_CON_DISABLED:
102 return 0;
103 case DP_STATUS_CON_DFP_D:
104 conf |= DP_CONF_UFP_U_AS_DFP_D;
105 pin_assign = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) &
106 DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo);
71ba4fe5
RB
107 /* Account for active cable capabilities */
108 if (dp->plug_prime)
109 pin_assign &= DP_CAP_DFP_D_PIN_ASSIGN(dp->plug_prime->vdo);
0e3bb7d6
HK
110 break;
111 case DP_STATUS_CON_UFP_D:
112 case DP_STATUS_CON_BOTH: /* NOTE: First acting as DP source */
113 conf |= DP_CONF_UFP_U_AS_UFP_D;
c1e5c2f0
PS
114 pin_assign = DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo) &
115 DP_CAP_PIN_ASSIGN_DFP_D(dp->port->vdo);
71ba4fe5
RB
116 /* Account for active cable capabilities */
117 if (dp->plug_prime)
118 pin_assign &= DP_CAP_UFP_D_PIN_ASSIGN(dp->plug_prime->vdo);
0e3bb7d6
HK
119 break;
120 default:
121 break;
122 }
123
124 /* Determining the initial pin assignment. */
125 if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) {
126 /* Is USB together with DP preferred */
127 if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC &&
128 pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK)
129 pin_assign &= DP_PIN_ASSIGN_MULTI_FUNC_MASK;
eddebe39 130 else if (pin_assign & DP_PIN_ASSIGN_DP_ONLY_MASK) {
0e3bb7d6 131 pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK;
eddebe39
RB
132 /* Default to pin assign C if available */
133 if (pin_assign & BIT(DP_PIN_ASSIGN_C))
134 pin_assign = BIT(DP_PIN_ASSIGN_C);
135 }
0e3bb7d6
HK
136
137 if (!pin_assign)
138 return -EINVAL;
139
140 conf |= DP_CONF_SET_PIN_ASSIGN(pin_assign);
141 }
142
143 dp->data.conf = conf;
71ba4fe5
RB
144 if (dp->plug_prime)
145 dp->data_prime.conf = conf;
0e3bb7d6
HK
146
147 return 0;
148}
149
150static int dp_altmode_status_update(struct dp_altmode *dp)
151{
152 bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
7f811394 153 bool hpd = !!(dp->data.status & DP_STATUS_HPD_STATE);
0e3bb7d6
HK
154 u8 con = DP_STATUS_CONNECTION(dp->data.status);
155 int ret = 0;
156
157 if (configured && (dp->data.status & DP_STATUS_SWITCH_TO_USB)) {
158 dp->data.conf = 0;
71ba4fe5
RB
159 dp->data_prime.conf = 0;
160 dp->state = dp->plug_prime ? DP_STATE_CONFIGURE_PRIME :
161 DP_STATE_CONFIGURE;
0e3bb7d6
HK
162 } else if (dp->data.status & DP_STATUS_EXIT_DP_MODE) {
163 dp->state = DP_STATE_EXIT;
164 } else if (!(con & DP_CONF_CURRENTLY(dp->data.conf))) {
165 ret = dp_altmode_configure(dp, con);
5a5ccd61 166 if (!ret) {
0e3bb7d6 167 dp->state = DP_STATE_CONFIGURE;
5a5ccd61
RB
168 if (dp->hpd != hpd) {
169 dp->hpd = hpd;
170 dp->pending_hpd = true;
171 }
172 }
7f811394 173 } else {
fc93835b
BA
174 drm_connector_oob_hotplug_event(dp->connector_fwnode,
175 hpd ? connector_status_connected :
176 connector_status_disconnected);
177 dp->hpd = hpd;
178 sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
0e3bb7d6
HK
179 }
180
181 return ret;
182}
183
184static int dp_altmode_configured(struct dp_altmode *dp)
185{
0e3bb7d6 186 sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration");
0e3bb7d6 187 sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment");
5a5ccd61
RB
188 /*
189 * If the DFP_D/UFP_D sends a change in HPD when first notifying the
190 * DisplayPort driver that it is connected, then we wait until
191 * configuration is complete to signal HPD.
192 */
193 if (dp->pending_hpd) {
fc93835b
BA
194 drm_connector_oob_hotplug_event(dp->connector_fwnode,
195 connector_status_connected);
5a5ccd61
RB
196 sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
197 dp->pending_hpd = false;
198 }
0e3bb7d6 199
fc27e046 200 return dp_altmode_notify(dp);
0e3bb7d6
HK
201}
202
203static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf)
204{
4c93cad8
KT
205 int svdm_version = typec_altmode_get_svdm_version(dp->alt);
206 u32 header;
0e3bb7d6
HK
207 int ret;
208
4c93cad8
KT
209 if (svdm_version < 0)
210 return svdm_version;
211
212 header = DP_HEADER(dp, svdm_version, DP_CMD_CONFIGURE);
0e3bb7d6
HK
213 ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data);
214 if (ret) {
215 dev_err(&dp->alt->dev,
216 "unable to put to connector to safe mode\n");
217 return ret;
218 }
219
220 ret = typec_altmode_vdm(dp->alt, header, &conf, 2);
fc27e046
HG
221 if (ret)
222 dp_altmode_notify(dp);
0e3bb7d6
HK
223
224 return ret;
225}
226
71ba4fe5
RB
227static 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
0e3bb7d6
HK
240static void dp_altmode_work(struct work_struct *work)
241{
242 struct dp_altmode *dp = container_of(work, struct dp_altmode, work);
4c93cad8 243 int svdm_version;
0e3bb7d6
HK
244 u32 header;
245 u32 vdo;
246 int ret;
247
248 mutex_lock(&dp->lock);
249
250 switch (dp->state) {
251 case DP_STATE_ENTER:
8face9aa 252 ret = typec_altmode_enter(dp->alt, NULL);
5789051f 253 if (ret && ret != -EBUSY)
0e3bb7d6
HK
254 dev_err(&dp->alt->dev, "failed to enter mode\n");
255 break;
71ba4fe5
RB
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;
0e3bb7d6 269 case DP_STATE_UPDATE:
4c93cad8
KT
270 svdm_version = typec_altmode_get_svdm_version(dp->alt);
271 if (svdm_version < 0)
272 break;
273 header = DP_HEADER(dp, svdm_version, DP_CMD_STATUS_UPDATE);
0e3bb7d6
HK
274 vdo = 1;
275 ret = typec_altmode_vdm(dp->alt, header, &vdo, 2);
276 if (ret)
277 dev_err(&dp->alt->dev,
278 "unable to send Status Update command (%d)\n",
279 ret);
280 break;
281 case DP_STATE_CONFIGURE:
282 ret = dp_altmode_configure_vdm(dp, dp->data.conf);
283 if (ret)
284 dev_err(&dp->alt->dev,
285 "unable to send Configure command (%d)\n", ret);
286 break;
71ba4fe5
RB
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;
0e3bb7d6
HK
297 case DP_STATE_EXIT:
298 if (typec_altmode_exit(dp->alt))
299 dev_err(&dp->alt->dev, "Exit Mode Failed!\n");
300 break;
71ba4fe5
RB
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;
0e3bb7d6
HK
305 default:
306 break;
307 }
308
309 dp->state = DP_STATE_IDLE;
310
311 mutex_unlock(&dp->lock);
71ba4fe5
RB
312 return;
313
314disable_prime:
315 typec_altmode_put_plug(dp->plug_prime);
316 dp->plug_prime = NULL;
317 schedule_work(&dp->work);
318 mutex_unlock(&dp->lock);
0e3bb7d6
HK
319}
320
321static void dp_altmode_attention(struct typec_altmode *alt, const u32 vdo)
322{
323 struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
324 u8 old_state;
325
326 mutex_lock(&dp->lock);
327
328 old_state = dp->state;
329 dp->data.status = vdo;
330
331 if (old_state != DP_STATE_IDLE)
332 dev_warn(&alt->dev, "ATTENTION while processing state %d\n",
333 old_state);
334
335 if (dp_altmode_status_update(dp))
336 dev_warn(&alt->dev, "%s: status update failed\n", __func__);
337
338 if (dp_altmode_notify(dp))
339 dev_err(&alt->dev, "%s: notification failed\n", __func__);
340
341 if (old_state == DP_STATE_IDLE && dp->state != DP_STATE_IDLE)
342 schedule_work(&dp->work);
343
344 mutex_unlock(&dp->lock);
345}
346
347static int dp_altmode_vdm(struct typec_altmode *alt,
348 const u32 hdr, const u32 *vdo, int count)
349{
350 struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
351 int cmd_type = PD_VDO_CMDT(hdr);
352 int cmd = PD_VDO_CMD(hdr);
353 int ret = 0;
354
355 mutex_lock(&dp->lock);
356
357 if (dp->state != DP_STATE_IDLE) {
358 ret = -EBUSY;
359 goto err_unlock;
360 }
361
362 switch (cmd_type) {
363 case CMDT_RSP_ACK:
364 switch (cmd) {
365 case CMD_ENTER_MODE:
25d6d1bf 366 typec_altmode_update_active(alt, true);
0e3bb7d6
HK
367 dp->state = DP_STATE_UPDATE;
368 break;
369 case CMD_EXIT_MODE:
25d6d1bf 370 typec_altmode_update_active(alt, false);
0e3bb7d6
HK
371 dp->data.status = 0;
372 dp->data.conf = 0;
89434b06 373 if (dp->hpd) {
5258dfd4
DA
374 drm_connector_oob_hotplug_event(dp->connector_fwnode,
375 connector_status_disconnected);
89434b06
RB
376 dp->hpd = false;
377 sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
378 }
71ba4fe5
RB
379 if (dp->plug_prime)
380 dp->state = DP_STATE_EXIT_PRIME;
0e3bb7d6
HK
381 break;
382 case DP_CMD_STATUS_UPDATE:
383 dp->data.status = *vdo;
384 ret = dp_altmode_status_update(dp);
385 break;
386 case DP_CMD_CONFIGURE:
387 ret = dp_altmode_configured(dp);
388 break;
389 default:
390 break;
391 }
392 break;
393 case CMDT_RSP_NAK:
394 switch (cmd) {
395 case DP_CMD_CONFIGURE:
396 dp->data.conf = 0;
397 ret = dp_altmode_configured(dp);
398 break;
399 default:
400 break;
401 }
402 break;
403 default:
404 break;
405 }
406
407 if (dp->state != DP_STATE_IDLE)
408 schedule_work(&dp->work);
409
410err_unlock:
411 mutex_unlock(&dp->lock);
412 return ret;
413}
414
71ba4fe5
RB
415static 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
469err_unlock:
470 mutex_unlock(&dp->lock);
471 return ret;
472}
473
0e3bb7d6
HK
474static int dp_altmode_activate(struct typec_altmode *alt, int activate)
475{
71ba4fe5
RB
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 }
0e3bb7d6
HK
493}
494
495static const struct typec_altmode_ops dp_altmode_ops = {
496 .attention = dp_altmode_attention,
497 .vdm = dp_altmode_vdm,
498 .activate = dp_altmode_activate,
499};
500
71ba4fe5
RB
501static const struct typec_cable_ops dp_cable_ops = {
502 .vdm = dp_cable_altmode_vdm,
503};
504
0e3bb7d6
HK
505static const char * const configurations[] = {
506 [DP_CONF_USB] = "USB",
507 [DP_CONF_DFP_D] = "source",
508 [DP_CONF_UFP_D] = "sink",
509};
510
511static ssize_t
512configuration_store(struct device *dev, struct device_attribute *attr,
513 const char *buf, size_t size)
514{
515 struct dp_altmode *dp = dev_get_drvdata(dev);
516 u32 conf;
517 u32 cap;
518 int con;
98a1a0c7 519 int ret = 0;
0e3bb7d6
HK
520
521 con = sysfs_match_string(configurations, buf);
522 if (con < 0)
523 return con;
524
525 mutex_lock(&dp->lock);
526
527 if (dp->state != DP_STATE_IDLE) {
528 ret = -EBUSY;
529 goto err_unlock;
530 }
531
532 cap = DP_CAP_CAPABILITY(dp->alt->vdo);
533
534 if ((con == DP_CONF_DFP_D && !(cap & DP_CAP_DFP_D)) ||
98a1a0c7
CIK
535 (con == DP_CONF_UFP_D && !(cap & DP_CAP_UFP_D))) {
536 ret = -EINVAL;
537 goto err_unlock;
538 }
0e3bb7d6
HK
539
540 conf = dp->data.conf & ~DP_CONF_DUAL_D;
541 conf |= con;
542
543 if (dp->alt->active) {
544 ret = dp_altmode_configure_vdm(dp, conf);
545 if (ret)
546 goto err_unlock;
547 }
548
549 dp->data.conf = conf;
550
551err_unlock:
552 mutex_unlock(&dp->lock);
553
554 return ret ? ret : size;
555}
556
557static ssize_t configuration_show(struct device *dev,
558 struct device_attribute *attr, char *buf)
559{
560 struct dp_altmode *dp = dev_get_drvdata(dev);
561 int len;
562 u8 cap;
563 u8 cur;
564 int i;
565
566 mutex_lock(&dp->lock);
567
568 cap = DP_CAP_CAPABILITY(dp->alt->vdo);
569 cur = DP_CONF_CURRENTLY(dp->data.conf);
570
571 len = sprintf(buf, "%s ", cur ? "USB" : "[USB]");
572
573 for (i = 1; i < ARRAY_SIZE(configurations); i++) {
574 if (i == cur)
575 len += sprintf(buf + len, "[%s] ", configurations[i]);
576 else if ((i == DP_CONF_DFP_D && cap & DP_CAP_DFP_D) ||
577 (i == DP_CONF_UFP_D && cap & DP_CAP_UFP_D))
578 len += sprintf(buf + len, "%s ", configurations[i]);
579 }
580
581 mutex_unlock(&dp->lock);
582
583 buf[len - 1] = '\n';
584 return len;
585}
586static DEVICE_ATTR_RW(configuration);
587
588static const char * const pin_assignments[] = {
589 [DP_PIN_ASSIGN_A] = "A",
590 [DP_PIN_ASSIGN_B] = "B",
591 [DP_PIN_ASSIGN_C] = "C",
592 [DP_PIN_ASSIGN_D] = "D",
593 [DP_PIN_ASSIGN_E] = "E",
594 [DP_PIN_ASSIGN_F] = "F",
595};
596
582836e3
PM
597/*
598 * Helper function to extract a peripheral's currently supported
599 * Pin Assignments from its DisplayPort alternate mode state.
600 */
601static u8 get_current_pin_assignments(struct dp_altmode *dp)
602{
85af23df 603 if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_UFP_U_AS_DFP_D)
9682b41e 604 return DP_CAP_PIN_ASSIGN_DFP_D(dp->alt->vdo);
582836e3 605 else
9682b41e 606 return DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo);
582836e3
PM
607}
608
0e3bb7d6
HK
609static ssize_t
610pin_assignment_store(struct device *dev, struct device_attribute *attr,
611 const char *buf, size_t size)
612{
613 struct dp_altmode *dp = dev_get_drvdata(dev);
614 u8 assignments;
615 u32 conf;
616 int ret;
617
618 ret = sysfs_match_string(pin_assignments, buf);
619 if (ret < 0)
620 return ret;
621
622 conf = DP_CONF_SET_PIN_ASSIGN(BIT(ret));
623 ret = 0;
624
625 mutex_lock(&dp->lock);
626
627 if (conf & dp->data.conf)
628 goto out_unlock;
629
630 if (dp->state != DP_STATE_IDLE) {
631 ret = -EBUSY;
632 goto out_unlock;
633 }
634
582836e3 635 assignments = get_current_pin_assignments(dp);
0e3bb7d6
HK
636
637 if (!(DP_CONF_GET_PIN_ASSIGN(conf) & assignments)) {
638 ret = -EINVAL;
639 goto out_unlock;
640 }
641
642 conf |= dp->data.conf & ~DP_CONF_PIN_ASSIGNEMENT_MASK;
643
644 /* Only send Configure command if a configuration has been set */
645 if (dp->alt->active && DP_CONF_CURRENTLY(dp->data.conf)) {
71ba4fe5 646 /* todo: send manual configure over SOP'*/
0e3bb7d6
HK
647 ret = dp_altmode_configure_vdm(dp, conf);
648 if (ret)
649 goto out_unlock;
650 }
651
652 dp->data.conf = conf;
653
654out_unlock:
655 mutex_unlock(&dp->lock);
656
657 return ret ? ret : size;
658}
659
660static ssize_t pin_assignment_show(struct device *dev,
661 struct device_attribute *attr, char *buf)
662{
663 struct dp_altmode *dp = dev_get_drvdata(dev);
664 u8 assignments;
665 int len = 0;
666 u8 cur;
667 int i;
668
669 mutex_lock(&dp->lock);
670
671 cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
672
582836e3 673 assignments = get_current_pin_assignments(dp);
0e3bb7d6
HK
674
675 for (i = 0; assignments; assignments >>= 1, i++) {
676 if (assignments & 1) {
677 if (i == cur)
678 len += sprintf(buf + len, "[%s] ",
679 pin_assignments[i]);
680 else
681 len += sprintf(buf + len, "%s ",
682 pin_assignments[i]);
683 }
684 }
685
686 mutex_unlock(&dp->lock);
687
d8f28269
BJS
688 /* get_current_pin_assignments can return 0 when no matching pin assignments are found */
689 if (len == 0)
690 len++;
691
0e3bb7d6
HK
692 buf[len - 1] = '\n';
693 return len;
694}
695static DEVICE_ATTR_RW(pin_assignment);
696
001b0c78
BJS
697static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char *buf)
698{
699 struct dp_altmode *dp = dev_get_drvdata(dev);
700
701 return sysfs_emit(buf, "%d\n", dp->hpd);
702}
703static DEVICE_ATTR_RO(hpd);
704
0e3bb7d6
HK
705static struct attribute *dp_altmode_attrs[] = {
706 &dev_attr_configuration.attr,
707 &dev_attr_pin_assignment.attr,
001b0c78 708 &dev_attr_hpd.attr,
0e3bb7d6
HK
709 NULL
710};
711
712static const struct attribute_group dp_altmode_group = {
713 .name = "displayport",
714 .attrs = dp_altmode_attrs,
715};
716
d266e968 717int dp_altmode_probe(struct typec_altmode *alt)
0e3bb7d6
HK
718{
719 const struct typec_altmode *port = typec_altmode_get_partner(alt);
71ba4fe5 720 struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P);
7f811394 721 struct fwnode_handle *fwnode;
0e3bb7d6
HK
722 struct dp_altmode *dp;
723 int ret;
724
725 /* FIXME: Port can only be DFP_U. */
726
727 /* Make sure we have compatiple pin configurations */
54e5c00a
PM
728 if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) &
729 DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) &&
730 !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) &
731 DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)))
0e3bb7d6
HK
732 return -ENODEV;
733
734 ret = sysfs_create_group(&alt->dev.kobj, &dp_altmode_group);
735 if (ret)
736 return ret;
737
738 dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
739 if (!dp)
740 return -ENOMEM;
741
742 INIT_WORK(&dp->work, dp_altmode_work);
743 mutex_init(&dp->lock);
744 dp->port = port;
745 dp->alt = alt;
746
747 alt->desc = "DisplayPort";
748 alt->ops = &dp_altmode_ops;
749
71ba4fe5
RB
750 if (plug) {
751 plug->desc = "Displayport";
752 plug->cable_ops = &dp_cable_ops;
753 }
754
755 dp->plug_prime = plug;
756
7f811394 757 fwnode = dev_fwnode(alt->dev.parent->parent); /* typec_port fwnode */
0bd9e021
DB
758 if (fwnode_property_present(fwnode, "displayport"))
759 dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0);
760 else
761 dp->connector_fwnode = fwnode_handle_get(fwnode); /* embedded DP */
7f811394
HG
762 if (IS_ERR(dp->connector_fwnode))
763 dp->connector_fwnode = NULL;
764
0e3bb7d6 765 typec_altmode_set_drvdata(alt, dp);
71ba4fe5
RB
766 if (plug)
767 typec_altmode_set_drvdata(plug, dp);
0e3bb7d6 768
71ba4fe5 769 dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
0e3bb7d6
HK
770 schedule_work(&dp->work);
771
772 return 0;
773}
d266e968 774EXPORT_SYMBOL_GPL(dp_altmode_probe);
0e3bb7d6 775
d266e968 776void dp_altmode_remove(struct typec_altmode *alt)
0e3bb7d6
HK
777{
778 struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
779
780 sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group);
781 cancel_work_sync(&dp->work);
71ba4fe5 782 typec_altmode_put_plug(dp->plug_prime);
7f811394
HG
783
784 if (dp->connector_fwnode) {
fc93835b
BA
785 drm_connector_oob_hotplug_event(dp->connector_fwnode,
786 connector_status_disconnected);
7f811394
HG
787
788 fwnode_handle_put(dp->connector_fwnode);
789 }
0e3bb7d6 790}
d266e968 791EXPORT_SYMBOL_GPL(dp_altmode_remove);
0e3bb7d6
HK
792
793static const struct typec_device_id dp_typec_id[] = {
794 { USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE },
795 { },
796};
797MODULE_DEVICE_TABLE(typec, dp_typec_id);
798
799static struct typec_altmode_driver dp_altmode_driver = {
800 .id_table = dp_typec_id,
801 .probe = dp_altmode_probe,
802 .remove = dp_altmode_remove,
803 .driver = {
804 .name = "typec_displayport",
805 .owner = THIS_MODULE,
806 },
807};
808module_typec_altmode_driver(dp_altmode_driver);
809
810MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
811MODULE_LICENSE("GPL v2");
812MODULE_DESCRIPTION("DisplayPort Alternate Mode");