drivers/intel/soundwire: Add Intel SoundWire controller driver

This driver provides support for Intel SoundWire controllers.  It is
intended to be used by multiple Intel SoCs and relies on retrieving
controller/master information from the SoC itself.  As such it
provides a function that must be implemented by the SoC to fill out
this structure.

The Intel SoundWire driver in the Linux kernel expects firmware to
inform it which master links are unused by adding a custom property
to the link descriptor.  This is done by looking for any children
attached to the device that use each link and disabling the ones
that are unused.

Mainboards will enable this driver and define the controller in
devicetree.cb in order provide the required ACPI tables, but the
mainboard should not need to provide any configuration itself as that
should all come from the SoC directly.

This was tested with the volteer board by adding this controller and a
codec to devicetree.cb and ensuring that the properties are all present,
including the custom properties for the device clock and quirk mask for
disabled links.

Device (SNDW)
{
    Name (_ADR, 0x40000003)
    Name (_CID, Package ()  { "PRP0001", "PNP0A05" })
    Name (_DDN, "Intel SoundWire Controller")
    Name (_DSD, Package ()
    {
        ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
        Package () {
            Package () { "mipi-sdw-sw-interface-revision", 0x00010000 },
            Package () { "mipi-sdw-master-count", 0x04 }
        },
        ToUUID ("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
        Package () {
            Package () { "mipi-sdw-link-0-subproperties", "LNK0" },
            Package () { "mipi-sdw-link-1-subproperties", "LNK1" },
            Package () { "mipi-sdw-link-2-subproperties", "LNK2" },
            Package () { "mipi-sdw-link-3-subproperties", "LNK3" },
        }
    }
    Name (LNK0, Package ()
    {
        ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
        Package () {
            Package () { "mipi-sdw-clock-stop-mode0-supported", One },
            [...]
            Package () { "intel-sdw-ip-clock", 0x0249F000 },
            Package () { "intel-quirk-mask", Zero },
        }
    }
    [...]
}

BUG=b:146482091

Signed-off-by: Duncan Laurie <dlaurie@google.com>
Change-Id: I4b4f843a7e5ea170b070a1697c8eedc7c103e127
Reviewed-on: https://review.coreboot.org/c/coreboot/+/40888
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
diff --git a/src/drivers/intel/soundwire/Kconfig b/src/drivers/intel/soundwire/Kconfig
new file mode 100644
index 0000000..fd6e399
--- /dev/null
+++ b/src/drivers/intel/soundwire/Kconfig
@@ -0,0 +1,2 @@
+config DRIVERS_INTEL_SOUNDWIRE
+	bool
diff --git a/src/drivers/intel/soundwire/Makefile.inc b/src/drivers/intel/soundwire/Makefile.inc
new file mode 100644
index 0000000..1f6773e
--- /dev/null
+++ b/src/drivers/intel/soundwire/Makefile.inc
@@ -0,0 +1 @@
+ramstage-$(CONFIG_DRIVERS_INTEL_SOUNDWIRE) += soundwire.c
diff --git a/src/drivers/intel/soundwire/chip.h b/src/drivers/intel/soundwire/chip.h
new file mode 100644
index 0000000..1ec3540
--- /dev/null
+++ b/src/drivers/intel/soundwire/chip.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __DRIVERS_INTEL_SOUNDWIRE_CHIP_H__
+#define __DRIVERS_INTEL_SOUNDWIRE_CHIP_H__
+
+struct drivers_intel_soundwire_config {
+};
+
+#endif /* __DRIVERS_INTEL_SOUNDWIRE_CHIP_H__ */
diff --git a/src/drivers/intel/soundwire/soundwire.c b/src/drivers/intel/soundwire/soundwire.c
new file mode 100644
index 0000000..ab09ff4
--- /dev/null
+++ b/src/drivers/intel/soundwire/soundwire.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
+#include <acpi/acpi_soundwire.h>
+#include <commonlib/helpers.h>
+#include <device/device.h>
+#include <device/path.h>
+#include <device/soundwire.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "soundwire.h"
+#include "chip.h"
+
+__weak int soc_fill_soundwire_controller(struct intel_soundwire_controller **controller)
+{
+	return -1;
+}
+
+static bool link_enabled(const struct device *dev, unsigned int link)
+{
+	struct device *child;
+
+	for (child = dev->link_list->children; child; child = child->sibling) {
+		if (child->enabled && child->path.type == DEVICE_PATH_GENERIC &&
+		    child->path.generic.id == link)
+			return true;
+	}
+	return false;
+}
+
+static void intel_soundwire_link_prop_cb(struct acpi_dp *dsd, unsigned int id,
+					 const struct soundwire_controller *controller)
+{
+	struct intel_soundwire_controller *intel_controller =
+		container_of(controller, struct intel_soundwire_controller, sdw);
+	unsigned int quirk_mask = intel_controller->quirk_mask;
+
+	/* Disable link if no are children enabled on this link device. */
+	if (!link_enabled(intel_controller->dev, id))
+		quirk_mask |= INTEL_SOUNDWIRE_QUIRK_BUS_DISABLE;
+
+	acpi_dp_add_integer(dsd, "intel-sdw-ip-clock", intel_controller->ip_clock);
+	acpi_dp_add_integer(dsd, "intel-quirk-mask", quirk_mask);
+}
+
+static void intel_soundwire_fill_ssdt(const struct device *dev)
+{
+	struct acpi_dp *dsd;
+	struct intel_soundwire_controller *controller;
+	const char *scope = acpi_device_scope(dev);
+
+	if (!dev->enabled || !scope)
+		return;
+
+	if (soc_fill_soundwire_controller(&controller) < 0 || !controller)
+		return;
+
+	/* Provide device pointer for link property callback function. */
+	controller->dev = dev;
+
+	acpigen_write_scope(scope);
+	acpigen_write_device(acpi_device_name(dev));
+	acpigen_write_name_string("_DDN", dev->chip_ops->name);
+	acpigen_write_name_integer("_ADR", controller->acpi_address);
+	acpigen_write_name_string("_CID", ACPI_HID_CONTAINER);
+
+	acpigen_write_STA(acpi_device_status(dev));
+
+	dsd = acpi_dp_new_table("_DSD");
+	soundwire_gen_controller(dsd, &controller->sdw, &intel_soundwire_link_prop_cb);
+	acpi_dp_write(dsd);
+
+	acpigen_pop_len(); /* Device */
+	acpigen_pop_len(); /* Scope */
+}
+
+static const char *intel_soundwire_acpi_name(const struct device *dev)
+{
+	return "SNDW";
+}
+
+static struct device_operations intel_soundwire_ops = {
+	.read_resources		= noop_read_resources,
+	.set_resources		= noop_set_resources,
+	.acpi_name		= intel_soundwire_acpi_name,
+	.acpi_fill_ssdt		= intel_soundwire_fill_ssdt,
+	.scan_bus		= scan_static_bus,
+};
+
+static void intel_soundwire_enable(struct device *dev)
+{
+	dev->ops = &intel_soundwire_ops;
+}
+
+struct chip_operations drivers_intel_soundwire_ops = {
+	CHIP_NAME("Intel SoundWire Controller")
+	.enable_dev = intel_soundwire_enable
+};
diff --git a/src/drivers/intel/soundwire/soundwire.h b/src/drivers/intel/soundwire/soundwire.h
new file mode 100644
index 0000000..3e2addf
--- /dev/null
+++ b/src/drivers/intel/soundwire/soundwire.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __DRIVERS_INTEL_SOUNDWIRE_H__
+#define __DRIVERS_INTEL_SOUNDWIRE_H__
+
+#include <device/soundwire.h>
+#include <stdint.h>
+
+/**
+ * enum intel_soundwire_quirk - Quirks for controller master links.
+ * @INTEL_SOUNDWIRE_QUIRK_STATIC_CLOCK: Link clock is fixed.
+ * @INTEL_SOUNDWIRE_QUIRK_BUS_DISABLE: This link should be disabled.
+ */
+enum intel_soundwire_quirk {
+	INTEL_SOUNDWIRE_QUIRK_STATIC_CLOCK = BIT(0),
+	INTEL_SOUNDWIRE_QUIRK_BUS_DISABLE = BIT(1),
+};
+
+/**
+ * struct intel_soundwire_controller - SoundWire controller configuration for Intel SoC.
+ * @dev: Device handle for this controller.
+ * @acpi_address: ACPI address for this controller.  This is a custom address that is not
+ *                compatible with either PCI or SoundWire.
+ * @ip_clock: Frequency of the source clock connected to the controller.
+ * @quirk_mask: Quirks that can be passed to the kernel drivers.
+ * @sdw: SoundWire controller properties defined in MIPI SoundWire DisCo Specification.
+ */
+struct intel_soundwire_controller {
+	const struct device *dev;
+	uint64_t acpi_address;
+	unsigned int ip_clock;
+	unsigned int quirk_mask;
+	struct soundwire_controller sdw;
+};
+
+/**
+ * soc_fill_soundwire_controller() - Get SoundWire controller properties from the SoC.
+ * @controller: Properties to be filled by the SoC.
+ * Return zero for success, -1 if there was any error filling the properties.
+ */
+int soc_fill_soundwire_controller(struct intel_soundwire_controller **controller);
+
+#endif /* __DRIVERS_INTEL_SOUNDWIRE_H__ */