From 5b4205b1cb92c610a3a69ecb77a3fd18e5703c6c Mon Sep 17 00:00:00 2001 From: Wyon Bi Date: Thu, 19 Jul 2018 14:15:27 +0800 Subject: [PATCH] mfd: fusb302: fix pin assignment selection algorithm This algorithm defaults to choosing higher pin config over lower ones in order to prefer multi-function if desired. NAME | SIGNALING | OUTPUT TYPE | MULTI-FUNCTION | PIN CONFIG ------------------------------------------------------------- A | USB G2 | ? | no | 00_0001 B | USB G2 | ? | yes | 00_0010 C | DP | CONVERTED | no | 00_0100 D | PD | CONVERTED | yes | 00_1000 E | DP | DP | no | 01_0000 F | PD | DP | yes | 10_0000 if UFP has NOT asserted multi-function preferred code masks away B/D/F leaving only A/C/E. For single-output dongles that should leave only one possible pin config depending on whether its a converter DP->(VGA|HDMI) or DP output. If UFP is a USB-C receptacle it may assert C/D/E/F. The DFP USB-C receptacle must always choose C/D in those cases. Change-Id: I5594d39a302f27e2d72259f6a18308488d4fa47c Signed-off-by: Wyon Bi --- drivers/mfd/fusb302.c | 140 +++++++++++++++++++++++++++++++++--------- drivers/mfd/fusb302.h | 1 + 2 files changed, 111 insertions(+), 30 deletions(-) diff --git a/drivers/mfd/fusb302.c b/drivers/mfd/fusb302.c index f60375e159a4..240cecac65b5 100644 --- a/drivers/mfd/fusb302.c +++ b/drivers/mfd/fusb302.c @@ -39,15 +39,6 @@ #define FLAG_EVENT (EVENT_RX | EVENT_TIMER_MUX | \ EVENT_TIMER_STATE) -#define PIN_MAP_A BIT(0) -#define PIN_MAP_B BIT(1) -#define PIN_MAP_C BIT(2) -#define PIN_MAP_D BIT(3) -#define PIN_MAP_E BIT(4) -#define PIN_MAP_F BIT(5) - -#define PIN_MAP_MF_MASK 0x2a - #define PACKET_IS_CONTROL_MSG(header, type) \ (PD_HEADER_CNT(header) == 0 && \ PD_HEADER_TYPE(header) == type) @@ -56,8 +47,54 @@ (PD_HEADER_CNT(header) != 0 && \ PD_HEADER_TYPE(header) == type) -#define VDM_DP_GET_RECEPTACLE(mode) ((mode >> 6) & 0x1) -#define VDM_DP_MULTIFUCTION(status) ((status >> 4) & 0x1) +/* + * DisplayPort modes capabilities + * ------------------------------- + * <31:24> : Reserved (always 0). + * <23:16> : UFP_D pin assignment supported + * <15:8> : DFP_D pin assignment supported + * <7> : USB 2.0 signaling (0b=yes, 1b=no) + * <6> : Plug | Receptacle (0b == plug, 1b == receptacle) + * <5:2> : xxx1: Supports DPv1.3, xx1x Supports USB Gen 2 signaling + * Other bits are reserved. + * <1:0> : signal direction ( 00b=rsv, 01b=sink, 10b=src 11b=both ) + */ +#define PD_DP_PIN_CAPS(x) ((((x) >> 6) & 0x1) ? (((x) >> 16) & 0x3f) \ + : (((x) >> 8) & 0x3f)) +#define PD_DP_SIGNAL_GEN2(x) (((x) >> 3) & 0x1) + +#define MODE_DP_PIN_A BIT(0) +#define MODE_DP_PIN_B BIT(1) +#define MODE_DP_PIN_C BIT(2) +#define MODE_DP_PIN_D BIT(3) +#define MODE_DP_PIN_E BIT(4) +#define MODE_DP_PIN_F BIT(5) + +/* Pin configs B/D/F support multi-function */ +#define MODE_DP_PIN_MF_MASK (MODE_DP_PIN_B | MODE_DP_PIN_D | MODE_DP_PIN_F) +/* Pin configs A/B support BR2 signaling levels */ +#define MODE_DP_PIN_BR2_MASK (MODE_DP_PIN_A | MODE_DP_PIN_B) +/* Pin configs C/D/E/F support DP signaling levels */ +#define MODE_DP_PIN_DP_MASK (MODE_DP_PIN_C | MODE_DP_PIN_D | \ + MODE_DP_PIN_E | MODE_DP_PIN_F) + +/* + * DisplayPort Status VDO + * ---------------------- + * <31:9> : Reserved (always 0). + * <8> : IRQ_HPD : 1 == irq arrived since last message otherwise 0. + * <7> : HPD state : 0 = HPD_LOW, 1 == HPD_HIGH + * <6> : Exit DP Alt mode: 0 == maintain, 1 == exit + * <5> : USB config : 0 == maintain current, 1 == switch to USB from DP + * <4> : Multi-function preference : 0 == no pref, 1 == MF preferred. + * <3> : enabled : is DPout on/off. + * <2> : power low : 0 == normal or LPM disabled, 1 == DP disabled for LPM + * <1:0> : connect status : 00b == no (DFP|UFP)_D is connected or disabled. + * 01b == DFP_D connected, 10b == UFP_D connected, 11b == both. + */ +#define PD_VDO_DPSTS_HPD_IRQ(x) (((x) >> 8) & 0x1) +#define PD_VDO_DPSTS_HPD_LVL(x) (((x) >> 7) & 0x1) +#define PD_VDO_DPSTS_MF_PREF(x) (((x) >> 4) & 0x1) static u8 fusb30x_port_used; static struct fusb30x_chip *fusb30x_port_info[256]; @@ -251,8 +288,7 @@ static void platform_fusb_notify(struct fusb30x_chip *chip) if (dp) { dfp = true; usb_ss = (chip->notify.pin_assignment_def & - (PIN_MAP_B | PIN_MAP_D | PIN_MAP_F)) ? - true : false; + MODE_DP_PIN_MF_MASK) ? true : false; hpd = GET_DP_STATUS_HPD(chip->notify.dp_status); } else if (chip->notify.data_role) { dfp = true; @@ -918,10 +954,57 @@ static void set_mesg(struct fusb30x_chip *chip, int cmd, int is_DMT) } } +/* + * This algorithm defaults to choosing higher pin config over lower ones in + * order to prefer multi-function if desired. + * + * NAME | SIGNALING | OUTPUT TYPE | MULTI-FUNCTION | PIN CONFIG + * ------------------------------------------------------------- + * A | USB G2 | ? | no | 00_0001 + * B | USB G2 | ? | yes | 00_0010 + * C | DP | CONVERTED | no | 00_0100 + * D | PD | CONVERTED | yes | 00_1000 + * E | DP | DP | no | 01_0000 + * F | PD | DP | yes | 10_0000 + * + * if UFP has NOT asserted multi-function preferred code masks away B/D/F + * leaving only A/C/E. For single-output dongles that should leave only one + * possible pin config depending on whether its a converter DP->(VGA|HDMI) or DP + * output. If UFP is a USB-C receptacle it may assert C/D/E/F. The DFP USB-C + * receptacle must always choose C/D in those cases. + */ +static int pd_dfp_dp_get_pin_assignment(struct fusb30x_chip *chip, + uint32_t caps, uint32_t status) +{ + uint32_t pin_caps; + + /* revisit with DFP that can be a sink */ + pin_caps = PD_DP_PIN_CAPS(caps); + + /* if don't want multi-function then ignore those pin configs */ + if (!PD_VDO_DPSTS_MF_PREF(status)) + pin_caps &= ~MODE_DP_PIN_MF_MASK; + + /* revisit if DFP drives USB Gen 2 signals */ + if (PD_DP_SIGNAL_GEN2(caps)) + pin_caps &= ~MODE_DP_PIN_DP_MASK; + else + pin_caps &= ~MODE_DP_PIN_BR2_MASK; + + /* if C/D present they have precedence over E/F for USB-C->USB-C */ + if (pin_caps & (MODE_DP_PIN_C | MODE_DP_PIN_D)) + pin_caps &= ~(MODE_DP_PIN_E | MODE_DP_PIN_F); + + /* returns undefined for zero */ + if (!pin_caps) + return 0; + + /* choosing higher pin config over lower ones */ + return 1 << (31 - __builtin_clz(pin_caps)); +} + static void set_vdm_mesg(struct fusb30x_chip *chip, int cmd, int type, int mode) { - u32 tmp, i; - chip->send_head = (chip->msg_id & 0x7) << 9; chip->send_head |= (chip->notify.power_role & 0x1) << 8; @@ -965,20 +1048,14 @@ static void set_vdm_mesg(struct fusb30x_chip *chip, int cmd, int type, int mode) chip->send_head |= (2 << 12); chip->send_load[0] |= (1 << 8) | (0xff01 << 16); - tmp = chip->notify.pin_assignment_support; - if (!VDM_DP_MULTIFUCTION(chip->notify.dp_status)) - tmp &= ~PIN_MAP_MF_MASK; - - for (i = 0; i < 6; i++) { - if (!(tmp & 0x20)) - tmp = tmp << 1; - else - break; - } - chip->notify.pin_assignment_def = 0x20 >> i; + chip->notify.pin_assignment_def = + pd_dfp_dp_get_pin_assignment(chip, chip->notify.dp_caps, + chip->notify.dp_status); chip->send_load[1] = (chip->notify.pin_assignment_def << 8) | (1 << 2) | 2; + dev_dbg(chip->dev, "DisplayPort Configurations: 0x%08x\n", + chip->send_load[1]); break; default: break; @@ -1104,17 +1181,20 @@ static void process_vdm_msg(struct fusb30x_chip *chip) * enter first mode default */ tmp = chip->rec_load[1]; + if ((!((tmp >> 8) & 0x3f)) && (!((tmp >> 16) & 0x3f))) { chip->val_tmp |= 1; break; } + chip->notify.dp_caps = chip->rec_load[1]; chip->notify.pin_assignment_def = 0; chip->notify.pin_assignment_support = - VDM_DP_GET_RECEPTACLE(tmp) ? - ((chip->rec_load[1] >> 16) & 0x3f) : - ((chip->rec_load[1] >> 8) & 0x3f); + PD_DP_PIN_CAPS(tmp); chip->val_tmp |= 1; + dev_dbg(chip->dev, + "DisplayPort Capabilities: 0x%08x\n", + chip->rec_load[1]); } break; case VDM_ENTER_MODE: @@ -1122,7 +1202,7 @@ static void process_vdm_msg(struct fusb30x_chip *chip) break; case VDM_DP_STATUS_UPDATE: chip->notify.dp_status = GET_DP_STATUS(chip->rec_load[1]); - dev_dbg(chip->dev, "dp_status 0x%x\n", + dev_dbg(chip->dev, "DisplayPort Status: 0x%08x\n", chip->rec_load[1]); chip->val_tmp = 1; break; diff --git a/drivers/mfd/fusb302.h b/drivers/mfd/fusb302.h index 1ae63788d0b8..4f5ca64f7226 100644 --- a/drivers/mfd/fusb302.h +++ b/drivers/mfd/fusb302.h @@ -448,6 +448,7 @@ struct notify_info { int pin_assignment_def; bool attention; u32 dp_status; + u32 dp_caps; }; enum tx_state {