plugin/synaptics-mst: add support for cayenne/spyder

This commit is contained in:
Apollo Ling 2021-08-19 12:50:27 +08:00 committed by Mario Limonciello
parent 7ae233b7cf
commit 98f78f4b15
4 changed files with 224 additions and 35 deletions

View File

@ -1,6 +1,7 @@
/*
* Copyright (C) 2016 Mario Limonciello <mario.limonciello@dell.com>
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
* Copyright (C) 2021 Apollo Ling <apollo.ling@synaptics.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
@ -28,12 +29,20 @@ fu_synaptics_mst_family_to_string (FuSynapticsMstFamily family)
return "leaf";
if (family == FU_SYNAPTICS_MST_FAMILY_PANAMERA)
return "panamera";
if (family == FU_SYNAPTICS_MST_FAMILY_CAYENNE)
return "cayenne";
if (family == FU_SYNAPTICS_MST_FAMILY_SPYDER)
return "spyder";
return NULL;
}
FuSynapticsMstFamily
fu_synaptics_mst_family_from_chip_id (guint16 chip_id)
{
if (chip_id >= 0x7000 && chip_id < 0x8000)
return FU_SYNAPTICS_MST_FAMILY_SPYDER;
if ((chip_id >= 0x6000 && chip_id < 0x7000) || (chip_id >= 0x8000 && chip_id < 0x9000))
return FU_SYNAPTICS_MST_FAMILY_CAYENNE;
if (chip_id >= 0x5000 && chip_id < 0x6000)
return FU_SYNAPTICS_MST_FAMILY_PANAMERA;
if (chip_id >= 0x3000 && chip_id < 0x4000)

View File

@ -2,6 +2,7 @@
* Copyright (C) 2016 Mario Limonciello <mario.limonciello@dell.com>
* Copyright (C) 2017 Peichen Huang <peichenhuang@tw.synaptics.com>
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
* Copyright (C) 2021 Apollo Ling <apollo.ling@synaptics.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
@ -33,6 +34,8 @@ typedef enum {
FU_SYNAPTICS_MST_FAMILY_TESLA,
FU_SYNAPTICS_MST_FAMILY_LEAF,
FU_SYNAPTICS_MST_FAMILY_PANAMERA,
FU_SYNAPTICS_MST_FAMILY_CAYENNE,
FU_SYNAPTICS_MST_FAMILY_SPYDER,
/*<private >*/
FU_SYNAPTICS_MST_FAMILY_LAST
} FuSynapticsMstFamily;

View File

@ -2,6 +2,7 @@
* Copyright (C) 2016 Mario Limonciello <mario.limonciello@dell.com>
* Copyright (C) 2017 Peichen Huang <peichenhuang@tw.synaptics.com>
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
* Copyright (C) 2021 Apollo Ling <apollo.ling@synaptics.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
@ -17,6 +18,8 @@ G_DECLARE_FINAL_TYPE (FuSynapticsMstConnection, fu_synaptics_mst_connection, FU,
#define ADDR_CUSTOMER_ID 0X10E
#define ADDR_BOARD_ID 0x10F
#define ADDR_MEMORY_CUSTOMER_ID_CAYENNE 0x9000024E
#define ADDR_MEMORY_BOARD_ID_CAYENNE 0x9000024F
#define ADDR_MEMORY_CUSTOMER_ID 0x170E
#define ADDR_MEMORY_BOARD_ID 0x170F
@ -41,21 +44,23 @@ typedef enum {
} SynapticsMstUpdcRc;
typedef enum {
UPDC_ENABLE_RC = 0x01,
UPDC_DISABLE_RC = 0x02,
UPDC_GET_ID = 0x03,
UPDC_GET_VERSION = 0x04,
UPDC_ENABLE_FLASH_CHIP_ERASE = 0x08,
UPDC_CAL_EEPROM_CHECKSUM = 0x11,
UPDC_FLASH_ERASE = 0x14,
UPDC_CAL_EEPROM_CHECK_CRC8 = 0x16,
UPDC_CAL_EEPROM_CHECK_CRC16 = 0x17,
UPDC_WRITE_TO_EEPROM = 0X20,
UPDC_WRITE_TO_MEMORY = 0x21,
UPDC_WRITE_TO_TX_DPCD = 0x22,
UPDC_READ_FROM_EEPROM = 0x30,
UPDC_READ_FROM_MEMORY = 0x31,
UPDC_READ_FROM_TX_DPCD = 0x32,
UPDC_ENABLE_RC = 0x01,
UPDC_DISABLE_RC = 0x02,
UPDC_GET_ID = 0x03,
UPDC_GET_VERSION = 0x04,
UPDC_FLASH_MAPPING = 0x07,
UPDC_ENABLE_FLASH_CHIP_ERASE = 0x08,
UPDC_CAL_EEPROM_CHECKSUM = 0x11,
UPDC_FLASH_ERASE = 0x14,
UPDC_CAL_EEPROM_CHECK_CRC8 = 0x16,
UPDC_CAL_EEPROM_CHECK_CRC16 = 0x17,
UPDC_ACTIVATE_FIRMWARE = 0X18,
UPDC_WRITE_TO_EEPROM = 0X20,
UPDC_WRITE_TO_MEMORY = 0x21,
UPDC_WRITE_TO_TX_DPCD = 0x22,
UPDC_READ_FROM_EEPROM = 0x30,
UPDC_READ_FROM_MEMORY = 0x31,
UPDC_READ_FROM_TX_DPCD = 0x32,
} SynapticsMstUpdcCmd;
FuSynapticsMstConnection *fu_synaptics_mst_connection_new (gint fd,

View File

@ -3,6 +3,7 @@
* Copyright (C) 2016 Mario Limonciello <mario.limonciello@dell.com>
* Copyright (C) 2017 Peichen Huang <peichenhuang@tw.synaptics.com>
* Copyright (C) 2018 Ryan Chang <ryan.chang@synaptics.com>
* Copyright (C) 2021 Apollo Ling <apollo.ling@synaptics.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
@ -771,20 +772,150 @@ fu_synaptics_mst_device_panamera_prepare_write (FuSynapticsMstDevice *self, GErr
return TRUE;
}
static gboolean
fu_synaptics_mst_device_update_cayenne_firmware(FuSynapticsMstDevice *self,
guint32 payload_len,
const guint8 *payload_data,
GError **error)
{
g_autoptr(FuSynapticsMstConnection) connection = NULL;
guint32 data_to_write = 0;
guint32 offset = 0;
guint32 write_loops = 0;
payload_len = 0x50000;
write_loops = (payload_len / BLOCK_UNIT);
data_to_write = payload_len;
if (payload_len % BLOCK_UNIT)
write_loops++;
connection = fu_synaptics_mst_connection_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(self)),
self->layer,
self->rad);
for (guint32 retries_cnt = 0;; retries_cnt++) {
guint32 checksum = 0;
guint32 flash_checksum = 0;
if (!fu_synaptics_mst_device_set_flash_sector_erase(self, 0xffff, 0, error))
return FALSE;
g_debug("Waiting for flash clear to settle");
g_usleep(FLASH_SETTLE_TIME);
for (guint32 i = 0; i < write_loops; i++) {
g_autoptr(GError) error_local = NULL;
guint8 length = BLOCK_UNIT;
if (data_to_write < BLOCK_UNIT)
length = data_to_write;
if (!fu_synaptics_mst_connection_rc_set_command(connection,
UPDC_WRITE_TO_EEPROM,
length,
offset,
payload_data + offset,
&error_local)) {
g_warning("Failed to write flash offset 0x%04x: %s, retrying",
offset,
error_local->message);
/* repeat once */
if (!fu_synaptics_mst_connection_rc_set_command(
connection,
UPDC_WRITE_TO_EEPROM,
length,
offset,
payload_data + offset,
error)) {
g_prefix_error(error,
"can't write flash offset 0x%04x: ",
offset);
return FALSE;
}
}
offset += length;
data_to_write -= length;
fu_device_set_progress_full(FU_DEVICE(self),
(goffset)i * 100,
(goffset)(write_loops - 1) * 100);
}
/* verify CRC */
checksum = fu_synaptics_mst_device_get_crc(0, 16, payload_len, payload_data);
if (!fu_synaptics_mst_connection_rc_special_get_command(connection,
UPDC_CAL_EEPROM_CHECK_CRC16,
payload_len,
0,
NULL,
4,
(guint8 *)(&flash_checksum),
error)) {
g_prefix_error(error, "Failed to get flash checksum: ");
return FALSE;
}
if (checksum == flash_checksum)
break;
g_debug("attempt %u: checksum %x didn't match %x",
retries_cnt,
flash_checksum,
checksum);
if (retries_cnt > MAX_RETRY_COUNTS) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"checksum %x mismatched %x",
flash_checksum,
checksum);
return FALSE;
}
}
if (!fu_synaptics_mst_connection_rc_set_command(connection,
UPDC_ACTIVATE_FIRMWARE,
0,
0,
NULL,
error)) {
g_prefix_error(error, "active firmware failed: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_synaptics_mst_device_restart (FuSynapticsMstDevice *self, GError **error)
{
g_autoptr(FuSynapticsMstConnection) connection = NULL;
guint8 buf[4] = {0xF5, 0, 0 ,0};
gint offset;
g_autoptr(GError) error_local = NULL;
switch (self->family) {
case FU_SYNAPTICS_MST_FAMILY_TESLA:
case FU_SYNAPTICS_MST_FAMILY_LEAF:
case FU_SYNAPTICS_MST_FAMILY_PANAMERA:
offset = 0x2000FC;
break;
case FU_SYNAPTICS_MST_FAMILY_CAYENNE:
case FU_SYNAPTICS_MST_FAMILY_SPYDER:
offset = 0x2020021C;
break;
default:
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Unsupported chip family");
return FALSE;
}
/* issue the reboot command, ignore return code (triggers before returning) */
connection = fu_synaptics_mst_connection_new (fu_udev_device_get_fd (FU_UDEV_DEVICE (self)),
self->layer, self->rad);
if (!fu_synaptics_mst_connection_rc_set_command (connection,
UPDC_WRITE_TO_MEMORY,
4, (gint) 0x2000FC, (guint8*) &buf,
&error_local))
if (!fu_synaptics_mst_connection_rc_set_command(connection,
UPDC_WRITE_TO_MEMORY,
4,
offset,
(guint8 *)&buf,
&error_local))
g_debug ("failed to restart: %s", error_local->message);
return TRUE;
@ -853,7 +984,18 @@ fu_synaptics_mst_device_write_firmware (FuDevice *device,
return FALSE;
/* update firmware */
if (self->family == FU_SYNAPTICS_MST_FAMILY_PANAMERA) {
switch (self->family) {
case FU_SYNAPTICS_MST_FAMILY_TESLA:
case FU_SYNAPTICS_MST_FAMILY_LEAF:
if (!fu_synaptics_mst_device_update_tesla_leaf_firmware(self,
payload_len,
payload_data,
error)) {
g_prefix_error(error, "Firmware update failed: ");
return FALSE;
}
break;
case FU_SYNAPTICS_MST_FAMILY_PANAMERA:
if (!fu_synaptics_mst_device_panamera_prepare_write (self, error)) {
g_prefix_error (error, "Failed to prepare for write: ");
return FALSE;
@ -871,14 +1013,23 @@ fu_synaptics_mst_device_write_firmware (FuDevice *device,
g_prefix_error (error, "Firmware update failed: ");
return FALSE;
}
} else {
if (!fu_synaptics_mst_device_update_tesla_leaf_firmware (self,
payload_len,
payload_data,
error)) {
break;
case FU_SYNAPTICS_MST_FAMILY_CAYENNE:
case FU_SYNAPTICS_MST_FAMILY_SPYDER:
if (!fu_synaptics_mst_device_update_cayenne_firmware(self,
payload_len,
payload_data,
error)) {
g_prefix_error (error, "Firmware update failed: ");
return FALSE;
}
break;
default:
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Unsupported chip family");
return FALSE;
}
/* wait for flash clear to settle */
@ -901,6 +1052,8 @@ fu_synaptics_mst_device_read_board_id (FuSynapticsMstDevice *self,
guint8 *byte,
GError **error)
{
gint offset;
/* in test mode we need to open a different file node instead */
if (fu_udev_device_get_dev (FU_UDEV_DEVICE (self)) == NULL) {
g_autofree gchar *filename = NULL;
@ -940,12 +1093,31 @@ fu_synaptics_mst_device_read_board_id (FuSynapticsMstDevice *self,
return TRUE;
}
switch (self->family) {
case FU_SYNAPTICS_MST_FAMILY_TESLA:
case FU_SYNAPTICS_MST_FAMILY_LEAF:
case FU_SYNAPTICS_MST_FAMILY_PANAMERA:
offset = (gint)ADDR_MEMORY_CUSTOMER_ID;
break;
case FU_SYNAPTICS_MST_FAMILY_CAYENNE:
case FU_SYNAPTICS_MST_FAMILY_SPYDER:
offset = (gint)ADDR_MEMORY_CUSTOMER_ID_CAYENNE;
break;
default:
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Unsupported chip family");
return FALSE;
}
/* get board ID via MCU address 0x170E instead of flash access due to HDCP2.2 running */
if (!fu_synaptics_mst_connection_rc_get_command (connection,
UPDC_READ_FROM_MEMORY,
2,
(gint)ADDR_MEMORY_CUSTOMER_ID, byte,
error)) {
if (!fu_synaptics_mst_connection_rc_get_command(connection,
UPDC_READ_FROM_MEMORY,
2,
offset,
byte,
error)) {
g_prefix_error (error, "Memory query failed: ");
return FALSE;
}
@ -1067,11 +1239,6 @@ fu_synaptics_mst_device_rescan (FuDevice *device, GError **error)
version = g_strdup_printf ("%1d.%02d.%02d", buf_ver[0], buf_ver[1], buf_ver[2]);
fu_device_set_version (FU_DEVICE (self), version);
/* read board ID */
if (!fu_synaptics_mst_device_read_board_id (self, connection, buf_ver, error))
return FALSE;
self->board_id = fu_common_read_uint16 (buf_ver, G_BIG_ENDIAN);
/* read board chip_id */
if (!fu_synaptics_mst_connection_read (connection, REG_CHIP_ID,
buf_ver, 2, error)) {
@ -1094,6 +1261,11 @@ fu_synaptics_mst_device_rescan (FuDevice *device, GError **error)
return FALSE;
}
/* read board ID */
if (!fu_synaptics_mst_device_read_board_id(self, connection, buf_ver, error))
return FALSE;
self->board_id = fu_common_read_uint16(buf_ver, G_BIG_ENDIAN);
/* recursively look for cascade devices */
if (!fu_device_locker_close (locker, error)) {
g_prefix_error (error, "failed to close parent: ");