honeybuns: Update VDM information for DisplayPort

Updated the VDM information to handle properly the DisplayPort alternate
mode.
Switch to 2+2 (DP+USB3.0) if mode D is entered else use 4 lanes of DP
for mode C.
Set the Multi-Function Preferred bit, so laptops select the mode D.

BUG=none
BRANCH=none
TEST=Tested with samus. Verified we get 36W of power + USB2.0 key +
USB3.0 key + external DP display.

Change-Id: I95e3b3640fd5952faeb24312e387468aed6266c7
Signed-off-by: Scott Collyer <scollyer@chromium.org>
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/267688
Reviewed-by: Todd Broch <tbroch@chromium.org>
This commit is contained in:
Scott
2015-04-28 10:07:56 -07:00
committed by ChromeOS Commit Bot
parent 0ec956ea9d
commit 590c76d910
6 changed files with 328 additions and 100 deletions

View File

@@ -16,10 +16,12 @@
#include "task.h"
#include "timer.h"
#include "usb.h"
#include "usb_bb.h"
#include "usb_pd.h"
#include "util.h"
static volatile uint64_t hpd_prev_ts;
static volatile int hpd_prev_level;
void vbus_event(enum gpio_signal signal)
{
@@ -29,6 +31,71 @@ void vbus_event(enum gpio_signal signal)
#include "gpio_list.h"
/**
* Hotplug detect deferred task
*
* Called after level change on hpd GPIO to evaluate (and debounce) what event
* has occurred. There are 3 events that occur on HPD:
* 1. low : downstream display sink is deattached
* 2. high : downstream display sink is attached
* 3. irq : downstream display sink signalling an interrupt.
*
* The debounce times for these various events are:
* HPD_USTREAM_DEBOUNCE_LVL : min pulse width of level value.
* HPD_USTREAM_DEBOUNCE_IRQ : min pulse width of IRQ low pulse.
*
* lvl(n-2) lvl(n-1) lvl prev_delta now_delta event
* ----------------------------------------------------
* 1 0 1 <IRQ n/a low glitch (ignore)
* 1 0 1 >IRQ <LVL irq
* x 0 1 n/a >LVL high
* 0 1 0 <LVL n/a high glitch (ignore)
* x 1 0 n/a >LVL low
*/
void hpd_irq_deferred(void)
{
pd_send_hpd(0, hpd_irq);
}
DECLARE_DEFERRED(hpd_irq_deferred);
void hpd_lvl_deferred(void)
{
int level = gpio_get_level(GPIO_DP_HPD);
if (level != hpd_prev_level)
/* It's a glitch while in deferred or canceled action */
return;
pd_send_hpd(0, (level) ? hpd_high : hpd_low);
}
DECLARE_DEFERRED(hpd_lvl_deferred);
void hpd_event(enum gpio_signal signal)
{
timestamp_t now = get_time();
int level = gpio_get_level(signal);
uint64_t cur_delta = now.val - hpd_prev_ts;
/* store current time */
hpd_prev_ts = now.val;
/* All previous hpd level events need to be re-triggered */
hook_call_deferred(hpd_lvl_deferred, -1);
/* It's a glitch. Previous time moves but level is the same. */
if (cur_delta < HPD_USTREAM_DEBOUNCE_IRQ)
return;
if ((!hpd_prev_level && level) &&
(cur_delta < HPD_USTREAM_DEBOUNCE_LVL))
/* It's an irq */
hook_call_deferred(hpd_irq_deferred, 0);
else if (cur_delta >= HPD_USTREAM_DEBOUNCE_LVL)
hook_call_deferred(hpd_lvl_deferred, HPD_USTREAM_DEBOUNCE_LVL);
hpd_prev_level = level;
}
static void honeybuns_test_led_update(void)
{
@@ -58,6 +125,18 @@ void board_config_pre_init(void)
STM32_SYSCFG_CFGR1 |= (1 << 9) | (1 << 10);
}
/* Initialize board. */
static void board_init(void)
{
timestamp_t now;
now = get_time();
hpd_prev_level = gpio_get_level(GPIO_DP_HPD);
hpd_prev_ts = now.val;
gpio_enable_interrupt(GPIO_DP_HPD);
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
/* ADC channels */
const struct adc_t adc_channels[] = {
@@ -88,7 +167,6 @@ BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT);
void board_set_usb_mux(int port, enum typec_mux mux,
enum usb_switch usb, int polarity)
{
if (mux == TYPEC_MUX_NONE) {
/* put the mux in the high impedance state */
gpio_set_level(GPIO_SS_MUX_OE_L, 1);
@@ -141,3 +219,64 @@ int board_get_usb_mux(int port, const char **dp_str, const char **usb_str)
}
return 1;
}
/**
* USB configuration
* Any type-C device with alternate mode capabilities must have the following
* set of descriptors.
*
* 1. Standard Device
* 2. BOS
* 2a. Container ID
* 2b. Billboard Caps
*/
struct my_bos {
struct usb_bos_hdr_descriptor bos;
struct usb_contid_caps_descriptor contid_caps;
struct usb_bb_caps_base_descriptor bb_caps;
struct usb_bb_caps_svid_descriptor bb_caps_svids[1];
};
static struct my_bos bos_desc = {
.bos = {
.bLength = USB_DT_BOS_SIZE,
.bDescriptorType = USB_DT_BOS,
.wTotalLength = (USB_DT_BOS_SIZE + USB_DT_CONTID_SIZE +
USB_BB_CAPS_BASE_SIZE +
USB_BB_CAPS_SVID_SIZE * 1),
.bNumDeviceCaps = 2, /* contid + bb_caps */
},
.contid_caps = {
.bLength = USB_DT_CONTID_SIZE,
.bDescriptorType = USB_DT_DEVICE_CAPABILITY,
.bDevCapabilityType = USB_DC_DTYPE_CONTID,
.bReserved = 0,
.ContainerID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
},
.bb_caps = {
.bLength = (USB_BB_CAPS_BASE_SIZE + USB_BB_CAPS_SVID_SIZE * 1),
.bDescriptorType = USB_DT_DEVICE_CAPABILITY,
.bDevCapabilityType = USB_DC_DTYPE_BILLBOARD,
.iAdditionalInfoURL = USB_STR_BB_URL,
.bNumberOfAlternateModes = 1,
.bPreferredAlternateMode = 1,
.VconnPower = 0,
.bmConfigured = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
},
.bReserved = 0,
},
.bb_caps_svids = {
{
.wSVID = USB_SID_DISPLAYPORT,
.bAlternateMode = 1,
.iAlternateModeString = USB_STR_BB_URL,
},
},
};
const struct bos_context bos_ctx = {
.descp = (void *)&bos_desc,
.size = sizeof(struct my_bos),
};

View File

@@ -20,6 +20,7 @@
#define CONFIG_HW_CRC
#define CONFIG_I2C
#undef CONFIG_LID_SWITCH
#define CONFIG_SHA256
#define CONFIG_STM_HWTIMER32
#undef CONFIG_TASK_PROFILING
#define CONFIG_USB
@@ -28,6 +29,10 @@
#undef CONFIG_USB_PD_ALT_MODE_DFP
#define CONFIG_USB_PD_CUSTOM_VDM
#undef CONFIG_USB_PD_DUAL_ROLE
#define CONFIG_USB_PD_HW_DEV_ID_BOARD_MAJOR USB_PD_HW_DEV_ID_HONEYBUNS
#define CONFIG_USB_PD_HW_DEV_ID_BOARD_MINOR 0
#define CONFIG_USB_PD_IDENTITY_HW_VERS 1
#define CONFIG_USB_PD_IDENTITY_SW_VERS 1
#define CONFIG_USB_PD_INTERNAL_COMP
#define CONFIG_USB_PD_PORT_COUNT 1
#define CONFIG_USB_PD_TCPC
@@ -36,13 +41,12 @@
#define CONFIG_USBC_VCONN
#undef CONFIG_WATCHDOG_HELP
/* I2C ports configuration */
#define I2C_PORT_MASTER 0
/* USB configuration */
#define CONFIG_USB_PID 0x5015
#define CONFIG_USB_BCD_DEV 0x0001 /* v 0.01 */
/* By default, enable all console messages excepted USB */
#define CC_DEFAULT (CC_ALL & ~CC_MASK(CC_USB))
@@ -103,6 +107,9 @@ enum usb_strings {
/* Enable/disable USB Hub */
void hx3_enable(int enable);
/* DisplayPort hotplug detection interrupt */
void hpd_event(enum gpio_signal signal);
#endif /* !__ASSEMBLER__ */
/* USB interface indexes (use define rather than enum to expand them) */

View File

@@ -20,4 +20,4 @@
TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(USBCFG, hx3_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \
TASK_ALWAYS(PD, pd_task, NULL, TASK_STACK_SIZE)
TASK_ALWAYS(PD, pd_task, NULL, LARGER_TASK_STACK_SIZE)

View File

@@ -5,8 +5,7 @@
* found in the LICENSE file.
*/
/*GPIO_INT(DP_HPD, PIN(A, 0), GPIO_INT_BOTH, hpd_event)*/
GPIO(DP_HPD, PIN(A, 0), GPIO_INPUT)
GPIO_INT(DP_HPD, PIN(A, 0), GPIO_INT_BOTH, hpd_event)
/* PD RX/TX */
GPIO(USB_CC1_PD, PIN(A, 1), GPIO_ANALOG)

View File

@@ -6,6 +6,7 @@
#include "charger.h"
#include "common.h"
#include "console.h"
#include "ec_commands.h"
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
@@ -13,6 +14,7 @@
#include "task.h"
#include "timer.h"
#include "util.h"
#include "usb.h"
#include "usb_pd.h"
@@ -162,111 +164,184 @@ int pd_alt_mode(int port, uint16_t svid)
return 0;
}
/* ----------------- Vendor Defined Messages ------------------ */
/* TODO () The VDM section needs to be updated for honeybuns */
const uint32_t vdo_idh = VDO_IDH(0, /* data caps as USB host */
1, /* data caps as USB device */
IDH_PTYPE_AMA, /* Alternate mode */
1, /* supports alt modes */
USB_VID_GOOGLE);
const uint32_t vdo_product = VDO_PRODUCT(CONFIG_USB_PID, CONFIG_USB_BCD_DEV);
const uint32_t vdo_ama = VDO_AMA(CONFIG_USB_PD_IDENTITY_HW_VERS,
CONFIG_USB_PD_IDENTITY_SW_VERS,
0, 0, 0, 0, /* SS[TR][12] */
0, /* Vconn power */
0, /* Vconn power required */
0, /* Vbus power required */
AMA_USBSS_U31_GEN1 /* USB SS support */);
static int svdm_response_identity(int port, uint32_t *payload)
{
payload[VDO_I(IDH)] = vdo_idh;
payload[VDO_I(CSTAT)] = VDO_CSTAT(0);
payload[VDO_I(PRODUCT)] = vdo_product;
payload[VDO_I(AMA)] = vdo_ama;
return VDO_I(AMA) + 1;
}
static int svdm_response_svids(int port, uint32_t *payload)
{
payload[1] = VDO_SVID(USB_SID_DISPLAYPORT, USB_VID_GOOGLE);
payload[2] = 0;
return 3;
}
#define OPOS_DP 1
#define OPOS_GFU 1
const uint32_t vdo_dp_modes[1] = {
VDO_MODE_DP(0, /* UFP pin cfg supported : none */
MODE_DP_PIN_C | MODE_DP_PIN_D, /* DFP pin cfg supported */
0, /* usb2.0 signalling even in AMode */
CABLE_PLUG, /* its a plug */
MODE_DP_V13, /* DPv1.3 Support, no Gen2 */
MODE_DP_SNK) /* Its a sink only */
};
const uint32_t vdo_goog_modes[1] = {
VDO_MODE_GOOGLE(MODE_GOOGLE_FU)
};
static int svdm_response_modes(int port, uint32_t *payload)
{
if (PD_VDO_VID(payload[0]) == USB_SID_DISPLAYPORT) {
memcpy(payload + 1, vdo_dp_modes, sizeof(vdo_dp_modes));
return ARRAY_SIZE(vdo_dp_modes) + 1;
} else if (PD_VDO_VID(payload[0]) == USB_VID_GOOGLE) {
memcpy(payload + 1, vdo_goog_modes, sizeof(vdo_goog_modes));
return ARRAY_SIZE(vdo_goog_modes) + 1;
} else {
return 0; /* nak */
}
}
static int dp_status(int port, uint32_t *payload)
{
int opos = PD_VDO_OPOS(payload[0]);
int hpd = gpio_get_level(GPIO_DP_HPD);
if (opos != OPOS_DP)
return 0; /* nak */
payload[1] = VDO_DP_STATUS(0, /* IRQ_HPD */
(hpd == 1), /* HPD_HI|LOW */
0, /* request exit DP */
0, /* request exit USB */
1, /* MF pref */
gpio_get_level(GPIO_PD_SBU_ENABLE),
0, /* power low */
0x2);
return 2;
}
static int dp_config(int port, uint32_t *payload)
{
/* is it a 2+2 or 4 DP lanes mode ? */
enum typec_mux mux = PD_DP_CFG_PIN(payload[1]) & MODE_DP_PIN_MF_MASK ?
TYPEC_MUX_DOCK : TYPEC_MUX_DP;
if (PD_DP_CFG_DPON(payload[1]))
gpio_set_level(GPIO_PD_SBU_ENABLE, 1);
/* Get the DP lanes (or DP+USB SS depending on the mode) */
board_set_usb_mux(port, mux, USB_SWITCH_CONNECT, pd_get_polarity(port));
return 1;
}
static int svdm_enter_mode(int port, uint32_t *payload)
{
int rv = 0; /* will generate a NAK */
/* SID & mode request is valid */
if ((PD_VDO_VID(payload[0]) == USB_SID_DISPLAYPORT) &&
(PD_VDO_OPOS(payload[0]) == OPOS_DP)) {
alt_mode[PD_AMODE_DISPLAYPORT] = OPOS_DP;
rv = 1;
pd_log_event(PD_EVENT_VIDEO_DP_MODE, 0, 1, NULL);
} else if ((PD_VDO_VID(payload[0]) == USB_VID_GOOGLE) &&
(PD_VDO_OPOS(payload[0]) == OPOS_GFU)) {
alt_mode[PD_AMODE_GOOGLE] = OPOS_GFU;
rv = 1;
}
if (rv)
/*
* If we failed initial mode entry we'll have enumerated the USB
* Billboard class. If so we should disconnect.
*/
usb_disconnect();
return rv;
}
static int svdm_exit_mode(int port, uint32_t *payload)
{
if (PD_VDO_VID(payload[0]) == USB_SID_DISPLAYPORT) {
gpio_set_level(GPIO_PD_SBU_ENABLE, 0);
alt_mode[PD_AMODE_DISPLAYPORT] = 0;
pd_log_event(PD_EVENT_VIDEO_DP_MODE, 0, 0, NULL);
} else if (PD_VDO_VID(payload[0]) == USB_VID_GOOGLE) {
alt_mode[PD_AMODE_GOOGLE] = 0;
} else {
CPRINTF("Unknown exit mode req:0x%08x\n", payload[0]);
}
return 1; /* Must return ACK */
}
static struct amode_fx dp_fx = {
.status = &dp_status,
.config = &dp_config,
};
const struct svdm_response svdm_rsp = {
.identity = NULL,
.svids = NULL,
.modes = NULL,
.identity = &svdm_response_identity,
.svids = &svdm_response_svids,
.modes = &svdm_response_modes,
.enter_mode = &svdm_enter_mode,
.amode = &dp_fx,
.exit_mode = &svdm_exit_mode,
};
int pd_custom_vdm(int port, int cnt, uint32_t *payload,
uint32_t **rpayload)
{
int cmd = PD_VDO_CMD(payload[0]);
uint16_t dev_id = 0;
int rsize;
/* make sure we have some payload */
if (cnt == 0)
if (PD_VDO_VID(payload[0]) != USB_VID_GOOGLE ||
!alt_mode[PD_AMODE_GOOGLE])
return 0;
switch (cmd) {
case VDO_CMD_VERSION:
/* guarantee last byte of payload is null character */
*(payload + cnt - 1) = 0;
CPRINTF("version: %s\n", (char *)(payload+1));
break;
case VDO_CMD_READ_INFO:
case VDO_CMD_SEND_INFO:
/* if last word is present, it contains lots of info */
if (cnt == 7) {
dev_id = VDO_INFO_HW_DEV_ID(payload[6]);
CPRINTF("DevId:%d.%d SW:%d RW:%d\n",
HW_DEV_ID_MAJ(dev_id),
HW_DEV_ID_MIN(dev_id),
VDO_INFO_SW_DBG_VER(payload[6]),
VDO_INFO_IS_RW(payload[6]));
*rpayload = payload;
rsize = pd_custom_flash_vdm(port, cnt, payload);
if (!rsize) {
int cmd = PD_VDO_CMD(payload[0]);
switch (cmd) {
#ifdef CONFIG_USB_PD_LOGGING
case VDO_CMD_GET_LOG:
rsize = pd_vdm_get_log_entry(payload);
break;
#endif
default:
/* Unknown : do not answer */
return 0;
}
/* copy hash */
if (cnt >= 6)
pd_dev_store_rw_hash(port, dev_id, payload + 1,
SYSTEM_IMAGE_UNKNOWN);
break;
}
return 0;
/* respond (positively) to the request */
payload[0] |= VDO_SRC_RESPONDER;
return rsize;
}
static int svdm_enter_dp_mode(int port, uint32_t mode_caps)
{
/* Only enter mode if device is DFP_D capable */
if (mode_caps & MODE_DP_SNK) {
CPRINTF("Entering mode w/ vdo = %08x\n", mode_caps);
return 0;
}
return -1;
}
static int dp_on;
static int svdm_dp_status(int port, uint32_t *payload)
{
payload[0] = VDO(USB_SID_DISPLAYPORT, 1, CMD_DP_STATUS);
payload[1] = VDO_DP_STATUS(0, /* HPD IRQ ... not applicable */
0, /* HPD level ... not applicable */
0, /* exit DP? ... no */
0, /* usb mode? ... no */
0, /* multi-function ... no */
dp_on,
0, /* power low? ... no */
dp_on);
return 2;
};
static int svdm_dp_config(int port, uint32_t *payload)
{
board_set_usb_mux(port, TYPEC_MUX_DP, USB_SWITCH_CONNECT,
pd_get_polarity(port));
dp_on = 1;
payload[0] = VDO(USB_SID_DISPLAYPORT, 1, CMD_DP_CONFIG);
payload[1] = VDO_DP_CFG(MODE_DP_PIN_E, /* pin mode */
1, /* DPv1.3 signaling */
2); /* UFP connected */
return 2;
};
static int svdm_dp_attention(int port, uint32_t *payload)
{
return 1; /* ack */
}
static void svdm_exit_dp_mode(int port)
{
CPRINTF("Exiting mode\n");
/* return to safe config */
}
const struct svdm_amode_fx supported_modes[] = {
{
.svid = USB_SID_DISPLAYPORT,
.enter = &svdm_enter_dp_mode,
.status = &svdm_dp_status,
.config = &svdm_dp_config,
.attention = &svdm_dp_attention,
.exit = &svdm_exit_dp_mode,
},
};
const int supported_modes_cnt = ARRAY_SIZE(supported_modes);

View File

@@ -575,6 +575,13 @@ struct pd_policy {
(((pin) & 0xff) << 8 | ((sig) & 0xf) << 2 | ((cfg) & 0x3))
#define PD_DP_CFG_DPON(x) (((x & 0x3) == 1) || ((x & 0x3) == 2))
/*
* Get the pin assignment mask
* for backward compatibility, if it is null,
* get the former sink pin assignment we used to be in <23:16>.
*/
#define PD_DP_CFG_PIN(x) ((((x) >> 8) & 0xff) ? (((x) >> 8) & 0xff) \
: (((x) >> 16) & 0xff))
/*
* ChromeOS specific PD device Hardware IDs. Used to identify unique
* products and used in VDO_INFO. Note this field is 10 bits.
@@ -584,6 +591,7 @@ struct pd_policy {
#define USB_PD_HW_DEV_ID_MINIMUFFIN 2
#define USB_PD_HW_DEV_ID_DINGDONG 3
#define USB_PD_HW_DEV_ID_HOHO 4
#define USB_PD_HW_DEV_ID_HONEYBUNS 5
/*
* ChromeOS specific VDO_CMD_READ_INFO responds with device info including: