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 <bivvy.bi@rock-chips.com>
This commit is contained in:
Wyon Bi 2018-07-19 14:15:27 +08:00 committed by Tao Huang
parent da29ad0019
commit 5b4205b1cb
2 changed files with 111 additions and 30 deletions

View File

@ -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;

View File

@ -448,6 +448,7 @@ struct notify_info {
int pin_assignment_def;
bool attention;
u32 dp_status;
u32 dp_caps;
};
enum tx_state {