mb/google/octopus: add audio codec into SSFC support for Bobba

BUG=b:174118027
BRANCH=octopus
TEST=adjust SSFC value of CBI to select RT5682 or DA7219 then check
whether device tree is updated correspondingly by disabling unselected
one.

Signed-off-by: Marco Chen <marcochen@google.com>
Change-Id: Id37c4c5716ade0851cfcb24e12b390841e633ac9
Reviewed-on: https://review.coreboot.org/c/coreboot/+/48795
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Zhuohao Lee <zhuohao@chromium.org>
diff --git a/src/mainboard/google/octopus/Kconfig.name b/src/mainboard/google/octopus/Kconfig.name
index 6e3dbf4..76715a5 100644
--- a/src/mainboard/google/octopus/Kconfig.name
+++ b/src/mainboard/google/octopus/Kconfig.name
@@ -34,6 +34,7 @@
 	select BASEBOARD_OCTOPUS_LAPTOP
 	select BOARD_GOOGLE_BASEBOARD_OCTOPUS
 	select NHLT_DA7219 if INCLUDE_NHLT_BLOBS
+	select NHLT_RT5682 if INCLUDE_NHLT_BLOBS
 
 config BOARD_GOOGLE_MEEP
 	bool "->  Meep"
diff --git a/src/mainboard/google/octopus/mainboard.c b/src/mainboard/google/octopus/mainboard.c
index e6dbd03..91d29cf 100644
--- a/src/mainboard/google/octopus/mainboard.c
+++ b/src/mainboard/google/octopus/mainboard.c
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 
 #include <acpi/acpi.h>
+#include <baseboard/cbi_ssfc.h>
 #include <baseboard/variants.h>
 #include <boardid.h>
 #include <bootstate.h>
@@ -8,6 +9,7 @@
 #include <device/device.h>
 #include <device/pci_def.h>
 #include <device/pci_ops.h>
+#include <drivers/i2c/generic/chip.h>
 #include <ec/google/chromeec/ec.h>
 #include <ec/ec.h>
 #include <intelblocks/xhci.h>
@@ -22,6 +24,9 @@
 #include <variant/ec.h>
 #include <variant/gpio.h>
 
+extern struct chip_operations drivers_i2c_generic_ops;
+extern struct chip_operations drivers_i2c_da7219_ops;
+
 static bool is_cnvi_held_in_reset(void)
 {
 	struct device *dev = pcidev_path_on_root(PCH_DEVFN_CNVI);
@@ -46,6 +51,41 @@
 	gpio_configure_pads(wifi_wake_gpio, ARRAY_SIZE(wifi_wake_gpio));
 }
 
+/*
+ * GPIO_137 for two audio codecs right now has the different configuration so
+ * if SSFC indicates that codec is different than default one then GPIO_137
+ * needs to be overridden for the corresponding second source.
+ */
+static void gpio_modification_by_ssfc(struct pad_config *table, size_t num)
+{
+	/* For RT5682, GPIO 137 should be set as EDGE_BOTH. */
+	const struct pad_config rt5682_gpio_137 = PAD_CFG_GPI_APIC_IOS(GPIO_137,
+			NONE, DEEP, EDGE_BOTH, INVERT, HIZCRx1, DISPUPD);
+
+	if (table == NULL || num == 0)
+		return;
+
+	/*
+	 * Currently we only have the case of RT5682 as the second source. And
+	 * in case of Ampton which used RT5682 as the default source, it didn't
+	 * provide override_table right now so it will be returned ealier since
+	 * table above is NULL.
+	 */
+	if (ssfc_get_audio_codec() != SSFC_AUDIO_CODEC_RT5682)
+		return;
+
+	while (num--) {
+		if (table->pad == GPIO_137) {
+			*table = rt5682_gpio_137;
+			printk(BIOS_INFO,
+				"Configure GPIO 137 based on SSFC.\n");
+			return;
+		}
+
+		table++;
+	}
+}
+
 static void mainboard_init(void *chip_info)
 {
 	int boardid;
@@ -58,6 +98,8 @@
 
 	base_pads = variant_base_gpio_table(&base_num);
 	override_pads = variant_override_gpio_table(&override_num);
+	gpio_modification_by_ssfc((struct pad_config *)override_pads,
+			override_num);
 
 	gpio_configure_pads_with_override(base_pads, base_num,
 			override_pads, override_num);
@@ -128,10 +170,47 @@
 		dev->enabled = 0;
 }
 
+/*
+ * Base on SSFC value in the CBI from EC to enable one of audio codec sources in
+ * the device tree.
+ */
+static void audio_codec_device_update(void)
+{
+	struct device *audio_dev = NULL;
+	struct bus *audio_i2c_bus =
+		pcidev_path_on_root(PCH_DEVFN_I2C5)->link_list;
+	enum ssfc_audio_codec codec = ssfc_get_audio_codec();
+
+	while ((audio_dev = dev_bus_each_child(audio_i2c_bus, audio_dev))) {
+		if (audio_dev->chip_info == NULL)
+			continue;
+
+		if ((audio_dev->chip_ops == &drivers_i2c_da7219_ops) &&
+			(codec == SSFC_AUDIO_CODEC_DA7219)) {
+			printk(BIOS_INFO, "enable DA7219.\n");
+			continue;
+		}
+
+		if ((audio_dev->chip_ops == &drivers_i2c_generic_ops) &&
+			(codec == SSFC_AUDIO_CODEC_RT5682)) {
+			struct drivers_i2c_generic_config *cfg =
+				audio_dev->chip_info;
+
+			if (cfg != NULL && !strcmp(cfg->hid, "10EC5682")) {
+				printk(BIOS_INFO, "enable RT5682.\n");
+				continue;
+			}
+		}
+
+		audio_dev->enabled = 0;
+	}
+}
+
 void mainboard_devtree_update(struct device *dev)
 {
 	/* Apply common devtree updates. */
 	wifi_device_update();
+	audio_codec_device_update();
 
 	/* Defer to variant for board-specific updates. */
 	variant_update_devtree(dev);
diff --git a/src/mainboard/google/octopus/variants/baseboard/Makefile.inc b/src/mainboard/google/octopus/variants/baseboard/Makefile.inc
index 63b03a6..f28c3d6 100644
--- a/src/mainboard/google/octopus/variants/baseboard/Makefile.inc
+++ b/src/mainboard/google/octopus/variants/baseboard/Makefile.inc
@@ -4,5 +4,6 @@
 
 ramstage-y += gpio.c
 ramstage-y += nhlt.c
+ramstage-y += cbi_ssfc.c
 
 smm-y += gpio.c
diff --git a/src/mainboard/google/octopus/variants/baseboard/cbi_ssfc.c b/src/mainboard/google/octopus/variants/baseboard/cbi_ssfc.c
new file mode 100644
index 0000000..589f92e
--- /dev/null
+++ b/src/mainboard/google/octopus/variants/baseboard/cbi_ssfc.c
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <baseboard/cbi_ssfc.h>
+#include <console/console.h>
+#include <ec/google/chromeec/ec.h>
+
+static int get_ssfc(uint32_t *val)
+{
+	static uint32_t known_value;
+	static enum {
+		SSFC_NOT_READ,
+		SSFC_AVAILABLE,
+	} ssfc_state = SSFC_NOT_READ;
+
+	if (ssfc_state == SSFC_AVAILABLE) {
+		*val = known_value;
+		return 0;
+	}
+
+	/*
+	 * If SSFC field is not in the CBI then the value of SSFC will be 0 for
+	 * further processing later since 0 of each bits group means default
+	 * component in a variant. For more detail, please refer to cbi_ssfc.h.
+	 */
+	if (google_chromeec_cbi_get_ssfc(&known_value) != 0) {
+		printk(BIOS_DEBUG, "SSFC not set in CBI\n");
+		return -1;
+	}
+
+	ssfc_state = SSFC_AVAILABLE;
+	*val = known_value;
+	printk(BIOS_INFO, "SSFC 0x%x.\n", known_value);
+
+	return 0;
+}
+
+static unsigned int extract_field(uint32_t mask, int shift)
+{
+	uint32_t ssfc;
+
+	/* On errors nothing is assumed to be set. */
+	if (get_ssfc(&ssfc))
+		return 0;
+
+	return (ssfc >> shift) & mask;
+}
+
+static enum ssfc_audio_codec ssfc_get_default_audio_codec(void)
+{
+	/*
+	 * Octopus has two reference boards; yorp is with DA7219 and bip is with
+	 * RT5682. Currently only AMPTON derived from bip so only it uses
+	 * RT5682 as the default source in the first MP devices.
+	 */
+	if (CONFIG(BOARD_GOOGLE_AMPTON))
+		return SSFC_AUDIO_CODEC_RT5682;
+
+	return SSFC_AUDIO_CODEC_DA7219;
+}
+
+enum ssfc_audio_codec ssfc_get_audio_codec(void)
+{
+	uint32_t codec = extract_field(
+			SSFC_AUDIO_CODEC_MASK, SSFC_AUDIO_CODEC_OFFSET);
+
+	if (codec != SSFC_AUDIO_CODEC_DEFAULT)
+		return codec;
+
+	return ssfc_get_default_audio_codec();
+}
diff --git a/src/mainboard/google/octopus/variants/baseboard/include/baseboard/cbi_ssfc.h b/src/mainboard/google/octopus/variants/baseboard/include/baseboard/cbi_ssfc.h
new file mode 100644
index 0000000..84020d7
--- /dev/null
+++ b/src/mainboard/google/octopus/variants/baseboard/include/baseboard/cbi_ssfc.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _OCTOPUS_CBI_SSFC__H_
+#define _OCTOPUS_CBI_SSFC__H_
+
+#include <inttypes.h>
+
+/****************************************************************************
+ * Octopus CBI Second Source Factory Cache
+ *
+ * SSFC was introduced after variants were MPed already so we can expect there
+ * can be devices in the field without SSFC field in the CBI. For devices
+ * without SSFC field in the CBI, the value of SSFC will be 0 set by get_ssfc()
+ * in the cbi_ssfc.c.
+ *
+ * On the other hand, taking audio codec as the example there are two sources -
+ * DA7219 and RT5682 used in the MPed devices before introducing SSFC. As a
+ * result, the value 0 of each bits group for a specific component is defined as
+ * DEFAULT and different variants should transform this DEFAULT to one of
+ * sources they used as the first sources. In the example here, either DA7219 or
+ * RT5682 should be transformed.
+ */
+
+/*
+ * Audio Codec (Bits 9-11)
+ *
+ */
+enum ssfc_audio_codec {
+	SSFC_AUDIO_CODEC_DEFAULT,
+	SSFC_AUDIO_CODEC_DA7219,
+	SSFC_AUDIO_CODEC_RT5682,
+};
+#define SSFC_AUDIO_CODEC_OFFSET		9
+#define SSFC_AUDIO_CODEC_MASK		0x7
+
+enum ssfc_audio_codec ssfc_get_audio_codec(void);
+
+#endif /* _OCTOPUS_CBI_SSFC__H_ */
diff --git a/src/mainboard/google/octopus/variants/baseboard/nhlt.c b/src/mainboard/google/octopus/variants/baseboard/nhlt.c
index f1304f4..9c9316c 100644
--- a/src/mainboard/google/octopus/variants/baseboard/nhlt.c
+++ b/src/mainboard/google/octopus/variants/baseboard/nhlt.c
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 
+#include <baseboard/cbi_ssfc.h>
 #include <baseboard/variants.h>
 #include <console/console.h>
 #include <nhlt.h>
@@ -7,6 +8,8 @@
 
 void __weak variant_nhlt_init(struct nhlt *nhlt)
 {
+	enum ssfc_audio_codec codec = ssfc_get_audio_codec();
+
 	/* 2 Channel DMIC array. */
 	if (!nhlt_soc_add_dmic_array(nhlt, 2))
 		printk(BIOS_ERR, "Added 2CH DMIC array.\n");
@@ -19,13 +22,13 @@
 	 * Headset codec is bi-directional but uses the same configuration
 	 * settings for render and capture endpoints.
 	 */
-	if (CONFIG(NHLT_DA7219)) {
+	if (CONFIG(NHLT_DA7219) && codec == SSFC_AUDIO_CODEC_DA7219) {
 		/* Dialog for Headset codec */
 		if (!nhlt_soc_add_da7219(nhlt, AUDIO_LINK_SSP2))
 			printk(BIOS_ERR, "Added Dialog_7219 codec.\n");
 	}
 
-	if (CONFIG(NHLT_RT5682)) {
+	if (CONFIG(NHLT_RT5682) && codec == SSFC_AUDIO_CODEC_RT5682) {
 		/* Realtek for Headset codec */
 		if (!nhlt_soc_add_rt5682(nhlt, AUDIO_LINK_SSP2))
 			printk(BIOS_ERR, "Added ALC5682 codec.\n");
diff --git a/src/mainboard/google/octopus/variants/bobba/gpio.c b/src/mainboard/google/octopus/variants/bobba/gpio.c
index fd94377..11fe9b5 100644
--- a/src/mainboard/google/octopus/variants/bobba/gpio.c
+++ b/src/mainboard/google/octopus/variants/bobba/gpio.c
@@ -17,6 +17,10 @@
 static const struct pad_config default_override_table[] = {
 	PAD_NC(GPIO_104, UP_20K),
 
+	/* GPIO_137 -- HP_INT_ODL and would be amend by SSFC. */
+	PAD_CFG_GPI_APIC_IOS(GPIO_137, NONE, DEEP, LEVEL, INVERT, HIZCRx1,
+				DISPUPD),
+
 	/* EN_PP3300_TOUCHSCREEN */
 	PAD_CFG_GPO_IOSSTATE_IOSTERM(GPIO_146, 0, DEEP, NONE, Tx0RxDCRx0,
 				     DISPUPD),
@@ -28,6 +32,10 @@
 	/* Default override table. */
 	PAD_NC(GPIO_104, UP_20K),
 
+	/* GPIO_137 -- HP_INT_ODL and would be amend by SSFC. */
+	PAD_CFG_GPI_APIC_IOS(GPIO_137, NONE, DEEP, LEVEL, INVERT, HIZCRx1,
+				DISPUPD),
+
 	/* EN_PP3300_TOUCHSCREEN */
 	PAD_CFG_GPO_IOSSTATE_IOSTERM(GPIO_146, 0, DEEP, NONE, Tx0RxDCRx0,
 				     DISPUPD),
diff --git a/src/mainboard/google/octopus/variants/bobba/overridetree.cb b/src/mainboard/google/octopus/variants/bobba/overridetree.cb
index 3c7187e..73adfef 100644
--- a/src/mainboard/google/octopus/variants/bobba/overridetree.cb
+++ b/src/mainboard/google/octopus/variants/bobba/overridetree.cb
@@ -165,6 +165,19 @@
 				register "mic_amp_in_sel" = ""diff""
 				device i2c 1a on end
 			end
+			chip drivers/i2c/generic
+				register "hid" = ""10EC5682""
+				register "name" = ""RT58""
+				register "desc" = ""Realtek RT5682""
+				register "irq" = "ACPI_IRQ_LEVEL_LOW(GPIO_137_IRQ)"
+				register "probed" = "1"
+				register "property_count" = "1"
+				# Set the jd_src to RT5668_JD1 for jack detection
+				register "property_list[0].type" = "ACPI_DP_TYPE_INTEGER"
+				register "property_list[0].name" = ""realtek,jd-src""
+				register "property_list[0].integer" = "1"
+				device i2c 1a on end
+			end
 		end	# - I2C 5
 		device pci 17.2 on
 			chip drivers/i2c/generic