mirror of
https://github.com/torvalds/linux.git
synced 2026-06-07 22:14:04 +02:00
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:
parent
da29ad0019
commit
5b4205b1cb
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -448,6 +448,7 @@ struct notify_info {
|
|||
int pin_assignment_def;
|
||||
bool attention;
|
||||
u32 dp_status;
|
||||
u32 dp_caps;
|
||||
};
|
||||
|
||||
enum tx_state {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user