[audio][mt8167s] Convert mt8167s drivers to composite

mt8167-tdm-input and mt8167-tdm-output now use composite protocol
to access I2C and GPIO.

TEST: "audio record" and "audio play" on cleo
Change-Id: Idf0a7a74b52b127eb796a61e7be68d6defe3c192
diff --git a/zircon/system/dev/audio/mt8167-tdm-input/BUILD.gn b/zircon/system/dev/audio/mt8167-tdm-input/BUILD.gn
index 1e02347..944167e 100644
--- a/zircon/system/dev/audio/mt8167-tdm-input/BUILD.gn
+++ b/zircon/system/dev/audio/mt8167-tdm-input/BUILD.gn
@@ -8,7 +8,7 @@
     "tlv320adc.cpp",
   ]
   deps = [
-    "$zx/system/banjo/ddk-protocol-clock",
+    "$zx/system/banjo/ddk-protocol-composite",
     "$zx/system/banjo/ddk-protocol-gpio",
     "$zx/system/banjo/ddk-protocol-i2c",
     "$zx/system/banjo/ddk-protocol-platform-device",
diff --git a/zircon/system/dev/audio/mt8167-tdm-input/audio-stream-in.cpp b/zircon/system/dev/audio/mt8167-tdm-input/audio-stream-in.cpp
index 6e8012b..b037f45 100644
--- a/zircon/system/dev/audio/mt8167-tdm-input/audio-stream-in.cpp
+++ b/zircon/system/dev/audio/mt8167-tdm-input/audio-stream-in.cpp
@@ -11,11 +11,19 @@
 #include <ddk/debug.h>
 #include <ddk/driver.h>
 #include <ddk/platform-defs.h>
+#include <ddk/protocol/composite.h>
 #include <soc/mt8167/mt8167-clk-regs.h>
 
 namespace audio {
 namespace mt8167 {
 
+enum {
+    COMPONENT_PDEV,
+    COMPONENT_I2C,
+    COMPONENT_GPIO,
+    COMPONENT_COUNT,
+};
+
 // Expects 2 mics.
 constexpr size_t kNumberOfChannels = 2;
 constexpr size_t kMinSampleRate = 8000;
@@ -25,7 +33,7 @@
     kMaxSampleRate * 2 * kNumberOfChannels, PAGE_SIZE);
 
 Mt8167AudioStreamIn::Mt8167AudioStreamIn(zx_device_t* parent)
-    : SimpleAudioStream(parent, true /* is input */), pdev_(parent) {
+    : SimpleAudioStream(parent, true /* is input */) {
 }
 
 zx_status_t Mt8167AudioStreamIn::Init() {
@@ -60,31 +68,40 @@
 }
 
 zx_status_t Mt8167AudioStreamIn::InitPdev() {
+    composite_protocol_t composite;
+
+    auto status = device_get_protocol(parent(), ZX_PROTOCOL_COMPOSITE, &composite);
+    if (status != ZX_OK) {
+        zxlogf(ERROR, "Could not get composite protocol\n");
+        return status;
+    }
+
+    zx_device_t* components[COMPONENT_COUNT];
+    size_t actual;
+    composite_get_components(&composite, components, countof(components), &actual);
+    if (actual != countof(components)) {
+        zxlogf(ERROR, "could not get components\n");
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+
+    pdev_ = components[COMPONENT_PDEV];
     if (!pdev_.is_valid()) {
         return ZX_ERR_NO_RESOURCES;
     }
 
-    for (unsigned i = 0; i < kClockCount; i++) {
-        clks_[i] = pdev_.GetClk(i);
-        if (!clks_[i].is_valid()) {
-            zxlogf(ERROR, "%s failed to allocate clk\n", __FUNCTION__);
-            return ZX_ERR_NO_RESOURCES;
-        }
-    }
-
-    codec_reset_ = pdev_.GetGpio(0);
+    codec_reset_ = components[COMPONENT_GPIO];
     if (!codec_reset_.is_valid()) {
         zxlogf(ERROR, "%s failed to allocate gpio\n", __FUNCTION__);
         return ZX_ERR_NO_RESOURCES;
     }
 
-    codec_ = Tlv320adc::Create(pdev_, 0); // ADC for TDM in.
+    codec_ = Tlv320adc::Create(components[COMPONENT_I2C], 0); // ADC for TDM in.
     if (!codec_) {
         zxlogf(ERROR, "%s could not get Tlv320adc\n", __func__);
         return ZX_ERR_NO_RESOURCES;
     }
 
-    zx_status_t status = pdev_.GetBti(0, &bti_);
+    status = pdev_.GetBti(0, &bti_);
     if (status != ZX_OK) {
         zxlogf(ERROR, "%s could not obtain bti %d\n", __func__, status);
         return status;
@@ -125,10 +142,6 @@
     mt_audio_->SetBuffer(pinned_ring_buffer_.region(0).phys_addr,
                          pinned_ring_buffer_.region(0).size);
 
-    // Disables aud1 clk gating: 0 is the index, board_mt8167::kClkRgAud1.
-    clks_[kClkRgAud1].Enable();
-    // Disables aud2 clk gating: 1 is the index, board_mt8167::kClkRgAud2.
-    clks_[kClkRgAud2].Enable();
     return ZX_OK;
 }
 
@@ -280,7 +293,8 @@
 };
 
 // clang-format off
-ZIRCON_DRIVER_BEGIN(mt8167_audio_in, mt_audio_in_driver_ops, "zircon", "0.1", 3)
+ZIRCON_DRIVER_BEGIN(mt8167_audio_in, mt_audio_in_driver_ops, "zircon", "0.1", 4)
+    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_COMPOSITE),
     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_MEDIATEK),
     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_MEDIATEK_8167S_REF),
     BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_MEDIATEK_AUDIO_IN),
diff --git a/zircon/system/dev/audio/mt8167-tdm-input/audio-stream-in.h b/zircon/system/dev/audio/mt8167-tdm-input/audio-stream-in.h
index dbe13de..a33186d 100644
--- a/zircon/system/dev/audio/mt8167-tdm-input/audio-stream-in.h
+++ b/zircon/system/dev/audio/mt8167-tdm-input/audio-stream-in.h
@@ -35,12 +35,6 @@
     zx_status_t InitPost() override;
 
 private:
-    enum {
-        kClkRgAud1,
-        kClkRgAud2,
-        kClockCount,
-    };
-
     friend class SimpleAudioStream;
     friend class fbl::RefPtr<Mt8167AudioStreamIn>;
 
@@ -55,7 +49,6 @@
     uint32_t us_per_notification_ = 0;
     fbl::RefPtr<dispatcher::Timer> notify_timer_;
     ddk::PDev pdev_ TA_GUARDED(domain_->token());
-    ddk::ClockProtocolClient clks_[kClockCount];
     std::unique_ptr<Tlv320adc> codec_;
     zx::vmo ring_buffer_vmo_ TA_GUARDED(domain_->token());
     fzl::PinnedVmo pinned_ring_buffer_ TA_GUARDED(domain_->token());
diff --git a/zircon/system/dev/audio/mt8167-tdm-input/tlv320adc.cpp b/zircon/system/dev/audio/mt8167-tdm-input/tlv320adc.cpp
index d3c2ced..a0ac035 100644
--- a/zircon/system/dev/audio/mt8167-tdm-input/tlv320adc.cpp
+++ b/zircon/system/dev/audio/mt8167-tdm-input/tlv320adc.cpp
@@ -17,13 +17,7 @@
 constexpr float Tlv320adc::kMaxGain;
 constexpr float Tlv320adc::kMinGain;
 
-std::unique_ptr<Tlv320adc> Tlv320adc::Create(ddk::PDev pdev, uint32_t i2c_index) {
-    auto i2c = pdev.GetI2c(i2c_index);
-    if (!i2c.is_valid()) {
-        zxlogf(ERROR, "%s failed to allocate i2c\n", __func__);
-        return nullptr;
-    }
-
+std::unique_ptr<Tlv320adc> Tlv320adc::Create(const ddk::I2cChannel& i2c, uint32_t i2c_index) {
     fbl::AllocChecker ac;
     auto ptr = std::unique_ptr<Tlv320adc>(new (&ac) Tlv320adc(i2c));
     if (!ac.check()) {
diff --git a/zircon/system/dev/audio/mt8167-tdm-input/tlv320adc.h b/zircon/system/dev/audio/mt8167-tdm-input/tlv320adc.h
index f1188d6..d084c60 100644
--- a/zircon/system/dev/audio/mt8167-tdm-input/tlv320adc.h
+++ b/zircon/system/dev/audio/mt8167-tdm-input/tlv320adc.h
@@ -8,14 +8,14 @@
 
 #include <ddk/debug.h>
 #include <ddk/protocol/i2c.h>
-#include <ddktl/pdev.h>
+#include <ddktl/i2c-channel.h>
 
 namespace audio {
 namespace mt8167 {
 
 class Tlv320adc {
 public:
-    static std::unique_ptr<Tlv320adc> Create(ddk::PDev pdev, uint32_t i2c_index);
+    static std::unique_ptr<Tlv320adc> Create(const ddk::I2cChannel& i2c, uint32_t i2c_index);
 
     explicit Tlv320adc(const ddk::I2cChannel& i2c)
         : i2c_(i2c) {}
diff --git a/zircon/system/dev/audio/mt8167-tdm-output/BUILD.gn b/zircon/system/dev/audio/mt8167-tdm-output/BUILD.gn
index e593b4f..fbcf042 100644
--- a/zircon/system/dev/audio/mt8167-tdm-output/BUILD.gn
+++ b/zircon/system/dev/audio/mt8167-tdm-output/BUILD.gn
@@ -9,7 +9,7 @@
     "tas5805.cpp",
   ]
   deps = [
-    "$zx/system/banjo/ddk-protocol-clock",
+    "$zx/system/banjo/ddk-protocol-composite",
     "$zx/system/banjo/ddk-protocol-gpio",
     "$zx/system/banjo/ddk-protocol-i2c",
     "$zx/system/banjo/ddk-protocol-platform-device",
diff --git a/zircon/system/dev/audio/mt8167-tdm-output/audio-stream-out.cpp b/zircon/system/dev/audio/mt8167-tdm-output/audio-stream-out.cpp
index a4b2932..94a8383 100644
--- a/zircon/system/dev/audio/mt8167-tdm-output/audio-stream-out.cpp
+++ b/zircon/system/dev/audio/mt8167-tdm-output/audio-stream-out.cpp
@@ -13,6 +13,7 @@
 #include <ddk/metadata.h>
 #include <ddk/platform-defs.h>
 #include <ddktl/metadata/audio.h>
+#include <ddk/protocol/composite.h>
 #include <soc/mt8167/mt8167-clk-regs.h>
 
 #include "tas5782.h"
@@ -21,6 +22,15 @@
 namespace audio {
 namespace mt8167 {
 
+enum {
+    COMPONENT_PDEV,
+    COMPONENT_I2C,
+    COMPONENT_RESET_GPIO, // This is optional
+    COMPONENT_MUTE_GPIO, // This is optional
+    COMPONENT_COUNT,
+};
+
+
 // Expects L+R.
 constexpr size_t kNumberOfChannels = 2;
 // Calculate ring buffer size for 1 second of 16-bit, 48kHz.
@@ -32,37 +42,51 @@
 }
 
 zx_status_t Mt8167AudioStreamOut::InitPdev() {
+    composite_protocol_t composite;
+
+    auto status = device_get_protocol(parent(), ZX_PROTOCOL_COMPOSITE, &composite);
+    if (status != ZX_OK) {
+        zxlogf(ERROR, "Could not get composite protocol\n");
+        return status;
+    }
+
+    zx_device_t* components[COMPONENT_COUNT] = {};
+    size_t actual;
+    composite_get_components(&composite, components, countof(components), &actual);
+    // Only PDEV and I2C components are required.
+    if (actual < 2) {
+        zxlogf(ERROR, "could not get components\n");
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+
+    pdev_ = components[COMPONENT_PDEV];
     if (!pdev_.is_valid()) {
         return ZX_ERR_NO_RESOURCES;
     }
 
-    size_t actual = 0;
     metadata::Codec codec;
-    zx_status_t status = device_get_metadata(parent(), DEVICE_METADATA_PRIVATE, &codec,
-                                             sizeof(metadata::Codec), &actual);
+    status = device_get_metadata(parent(), DEVICE_METADATA_PRIVATE, &codec, sizeof(metadata::Codec),
+                                 &actual);
     if (status != ZX_OK || sizeof(metadata::Codec) != actual) {
         zxlogf(ERROR, "%s device_get_metadata failed %d\n", __FILE__, status);
         return status;
     }
 
-    clk_ = pdev_.GetClk(0);
-    if (!clk_.is_valid()) {
-        zxlogf(ERROR, "%s failed to allocate clk\n", __FUNCTION__);
-        return ZX_ERR_NO_RESOURCES;
-    }
-    clk_.Enable(); // board_mt8167::kClkRgAud1, disables clk gating.
-
     // TODO(andresoportus): Move GPIO control to codecs?
     // Not all codecs have these GPIOs.
-    codec_reset_ = pdev_.GetGpio(0);
-    codec_mute_ = pdev_.GetGpio(1);
+    if (components[COMPONENT_RESET_GPIO]) {
+        codec_reset_ = components[COMPONENT_RESET_GPIO];
+    }
+    if (components[COMPONENT_MUTE_GPIO]) {
+        codec_mute_ = components[COMPONENT_MUTE_GPIO];
+    }
 
     if (codec == metadata::Codec::Tas5782) {
         zxlogf(INFO, "audio: using TAS5782 codec\n");
-        codec_ = Tas5782::Create(pdev_, 0);
+        codec_ = Tas5782::Create(components[COMPONENT_I2C], 0);
     } else if (codec == metadata::Codec::Tas5805) {
         zxlogf(INFO, "audio: using TAS5805 codec\n");
-        codec_ = Tas5805::Create(pdev_, 0);
+        codec_ = Tas5805::Create(components[COMPONENT_I2C], 0);
     } else {
         zxlogf(ERROR, "%s could not get codec\n", __FUNCTION__);
         return ZX_ERR_NO_RESOURCES;
@@ -328,7 +352,8 @@
 };
 
 // clang-format off
-ZIRCON_DRIVER_BEGIN(mt8167_audio_out, mt_audio_out_driver_ops, "zircon", "0.1", 3)
+ZIRCON_DRIVER_BEGIN(mt8167_audio_out, mt_audio_out_driver_ops, "zircon", "0.1", 4)
+    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_COMPOSITE),
     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_MEDIATEK),
     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_MEDIATEK_8167S_REF),
     BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_MEDIATEK_AUDIO_OUT),
diff --git a/zircon/system/dev/audio/mt8167-tdm-output/audio-stream-out.h b/zircon/system/dev/audio/mt8167-tdm-output/audio-stream-out.h
index d879c1f..1ed52df 100644
--- a/zircon/system/dev/audio/mt8167-tdm-output/audio-stream-out.h
+++ b/zircon/system/dev/audio/mt8167-tdm-output/audio-stream-out.h
@@ -60,7 +60,6 @@
     uint32_t us_per_notification_ = 0;
     fbl::RefPtr<dispatcher::Timer> notify_timer_;
     ddk::PDev pdev_ TA_GUARDED(domain_->token());
-    ddk::ClockProtocolClient clk_;
     std::unique_ptr<Codec> codec_;
     zx::vmo ring_buffer_vmo_ TA_GUARDED(domain_->token());
     fzl::PinnedVmo pinned_ring_buffer_ TA_GUARDED(domain_->token());
diff --git a/zircon/system/dev/audio/mt8167-tdm-output/tas5782.cpp b/zircon/system/dev/audio/mt8167-tdm-output/tas5782.cpp
index b292a4e..865a3ae 100644
--- a/zircon/system/dev/audio/mt8167-tdm-output/tas5782.cpp
+++ b/zircon/system/dev/audio/mt8167-tdm-output/tas5782.cpp
@@ -19,13 +19,7 @@
 constexpr float Tas5782::kMaxGain;
 constexpr float Tas5782::kMinGain;
 
-std::unique_ptr<Tas5782> Tas5782::Create(ddk::PDev pdev, uint32_t i2c_index) {
-    auto i2c = pdev.GetI2c(i2c_index);
-    if (!i2c.is_valid()) {
-        zxlogf(ERROR, "%s failed to allocate i2c\n", __func__);
-        return nullptr;
-    }
-
+std::unique_ptr<Tas5782> Tas5782::Create(ddk::I2cChannel i2c, uint32_t i2c_index) {
     fbl::AllocChecker ac;
     auto ptr = std::unique_ptr<Tas5782>(new (&ac) Tas5782(i2c));
     if (!ac.check()) {
diff --git a/zircon/system/dev/audio/mt8167-tdm-output/tas5782.h b/zircon/system/dev/audio/mt8167-tdm-output/tas5782.h
index e107025..f70dbf0 100644
--- a/zircon/system/dev/audio/mt8167-tdm-output/tas5782.h
+++ b/zircon/system/dev/audio/mt8167-tdm-output/tas5782.h
@@ -8,7 +8,7 @@
 
 #include <ddk/debug.h>
 #include <ddk/protocol/i2c.h>
-#include <ddktl/pdev.h>
+#include <ddktl/i2c-channel.h>
 
 #include "codec.h"
 
@@ -17,7 +17,7 @@
 
 class Tas5782 final : public Codec {
 public:
-    static std::unique_ptr<Tas5782> Create(ddk::PDev pdev, uint32_t i2c_index);
+    static std::unique_ptr<Tas5782> Create(ddk::I2cChannel i2c, uint32_t i2c_index);
 
     explicit Tas5782(const ddk::I2cChannel& i2c)
         : i2c_(i2c) {}
diff --git a/zircon/system/dev/audio/mt8167-tdm-output/tas5805.cpp b/zircon/system/dev/audio/mt8167-tdm-output/tas5805.cpp
index 83235cf..b748606 100644
--- a/zircon/system/dev/audio/mt8167-tdm-output/tas5805.cpp
+++ b/zircon/system/dev/audio/mt8167-tdm-output/tas5805.cpp
@@ -34,13 +34,7 @@
 namespace audio {
 namespace mt8167 {
 
-std::unique_ptr<Tas5805> Tas5805::Create(ddk::PDev pdev, uint32_t i2c_index) {
-    auto i2c = pdev.GetI2c(i2c_index);
-    if (!i2c.is_valid()) {
-        zxlogf(ERROR, "%s failed to allocate i2c\n", __func__);
-        return nullptr;
-    }
-
+std::unique_ptr<Tas5805> Tas5805::Create(ddk::I2cChannel i2c, uint32_t i2c_index) {
     fbl::AllocChecker ac;
     auto ptr = std::unique_ptr<Tas5805>(new (&ac) Tas5805(i2c));
     if (!ac.check()) {
diff --git a/zircon/system/dev/audio/mt8167-tdm-output/tas5805.h b/zircon/system/dev/audio/mt8167-tdm-output/tas5805.h
index d8ada96..eecad1a 100644
--- a/zircon/system/dev/audio/mt8167-tdm-output/tas5805.h
+++ b/zircon/system/dev/audio/mt8167-tdm-output/tas5805.h
@@ -8,7 +8,7 @@
 
 #include <ddk/debug.h>
 #include <ddk/protocol/i2c.h>
-#include <ddktl/pdev.h>
+#include <ddktl/i2c-channel.h>
 
 #include "codec.h"
 
@@ -17,7 +17,7 @@
 
 class Tas5805 final : public Codec {
 public:
-    static std::unique_ptr<Tas5805> Create(ddk::PDev pdev, uint32_t i2c_index);
+    static std::unique_ptr<Tas5805> Create(ddk::I2cChannel i2c, uint32_t i2c_index);
 
     explicit Tas5805(const ddk::I2cChannel& i2c)
         : i2c_(i2c) {}
diff --git a/zircon/system/dev/board/mt8167s_ref/BUILD.gn b/zircon/system/dev/board/mt8167s_ref/BUILD.gn
index 7678c6ab..a218b5e 100644
--- a/zircon/system/dev/board/mt8167s_ref/BUILD.gn
+++ b/zircon/system/dev/board/mt8167s_ref/BUILD.gn
@@ -24,6 +24,7 @@
     "mt8167.cpp",
   ]
   deps = [
+    "$zx/system/banjo/ddk-protocol-clockimpl",
     "$zx/system/banjo/ddk-protocol-gpio",
     "$zx/system/banjo/ddk-protocol-gpioimpl",
     "$zx/system/banjo/ddk-protocol-platform-bus",
diff --git a/zircon/system/dev/board/mt8167s_ref/mt8167-audio.cpp b/zircon/system/dev/board/mt8167s_ref/mt8167-audio.cpp
index 45eca05..fb9f50e 100644
--- a/zircon/system/dev/board/mt8167s_ref/mt8167-audio.cpp
+++ b/zircon/system/dev/board/mt8167s_ref/mt8167-audio.cpp
@@ -4,12 +4,14 @@
 
 #include <limits.h>
 
+#include <ddk/binding.h>
 #include <ddk/debug.h>
 #include <ddk/device.h>
 #include <ddk/metadata.h>
 #include <ddk/platform-defs.h>
 #include <ddk/protocol/gpio.h>
 #include <ddktl/metadata/audio.h>
+#include <ddktl/protocol/clockimpl.h>
 #include <fbl/algorithm.h>
 #include <hwreg/bitfields.h>
 #include <soc/mt8167/mt8167-clk.h>
@@ -38,6 +40,75 @@
     DEF_FIELD(18, 16, status);
 };
 
+constexpr zx_bind_inst_t root_match[] = {
+    BI_MATCH(),
+};
+static const zx_bind_inst_t in_i2c_match[] = {
+    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_I2C),
+    BI_ABORT_IF(NE, BIND_I2C_BUS_ID, 1),
+    BI_MATCH_IF(EQ, BIND_I2C_ADDRESS, 0x1B),
+};
+static const zx_bind_inst_t mt8167s_out_i2c_match[] = {
+    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_I2C),
+    BI_ABORT_IF(NE, BIND_I2C_BUS_ID, 2),
+    BI_MATCH_IF(EQ, BIND_I2C_ADDRESS, 0x48),
+};
+static const zx_bind_inst_t cleo_out_i2c_match[] = {
+    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_I2C),
+    BI_ABORT_IF(NE, BIND_I2C_BUS_ID, 2),
+    BI_MATCH_IF(EQ, BIND_I2C_ADDRESS, 0x2C),
+};
+static const device_component_part_t in_i2c_component[] = {
+    { fbl::count_of(root_match), root_match },
+    { fbl::count_of(in_i2c_match), in_i2c_match },
+};
+static const device_component_part_t mt8167s_out_i2c_component[] = {
+    { fbl::count_of(root_match), root_match },
+    { fbl::count_of(mt8167s_out_i2c_match), mt8167s_out_i2c_match },
+};
+static const device_component_part_t cleo_out_i2c_component[] = {
+    { fbl::count_of(root_match), root_match },
+    { fbl::count_of(cleo_out_i2c_match), cleo_out_i2c_match },
+};
+
+static const zx_bind_inst_t in_gpio_match[] = {
+    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO),
+    BI_MATCH_IF(EQ, BIND_GPIO_PIN, MT8167_GPIO24_EINT24),
+};
+static const zx_bind_inst_t mt8167s_out_reset_gpio_match[] = {
+    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO),
+    BI_MATCH_IF(EQ, BIND_GPIO_PIN, MT8167_GPIO107_MSDC1_DAT1),
+};
+static const zx_bind_inst_t mt8167s_out_mute_gpio_match[] = {
+    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO),
+    BI_MATCH_IF(EQ, BIND_GPIO_PIN, MT8167_GPIO108_MSDC1_DAT2),
+};
+static const device_component_part_t in_gpio_component[] = {
+    { countof(root_match), root_match },
+    { countof(in_gpio_match), in_gpio_match },
+};
+static const device_component_part_t mt8167s_out_reset_gpio_component[] = {
+    { countof(root_match), root_match },
+    { countof(mt8167s_out_reset_gpio_match), mt8167s_out_reset_gpio_match },
+};
+static const device_component_part_t mt8167s_out_mute_gpio_component[] = {
+    { countof(root_match), root_match },
+    { countof(mt8167s_out_mute_gpio_match), mt8167s_out_mute_gpio_match },
+};
+
+static const device_component_t in_components[] = {
+    { countof(in_i2c_component), in_i2c_component },
+    { countof(in_gpio_component), in_gpio_component },
+};
+static const device_component_t mt8167s_ref_out_components[] = {
+    { countof(mt8167s_out_i2c_component), mt8167s_out_i2c_component },
+    { countof(mt8167s_out_reset_gpio_component), mt8167s_out_reset_gpio_component },
+    { countof(mt8167s_out_mute_gpio_component), mt8167s_out_mute_gpio_component },
+};
+static const device_component_t cleo_out_components[] = {
+    { countof(cleo_out_i2c_component), cleo_out_i2c_component },
+};
+
 zx_status_t Mt8167::AudioInit() {
     if (board_info_.pid != PDEV_PID_MEDIATEK_8167S_REF &&
         board_info_.pid != PDEV_PID_CLEO) {
@@ -60,10 +131,6 @@
             .length = MT8167_PLL_SIZE,
         },
     };
-    static constexpr pbus_clk_t clks[] = {
-        {.clk = board_mt8167::kClkRgAud1},
-        {.clk = board_mt8167::kClkRgAud2},
-    };
 
     static constexpr pbus_bti_t btis_out[] = {
         {
@@ -72,23 +139,12 @@
         },
     };
 
-    constexpr pbus_gpio_t gpios_in[] = {
-        {
-            .gpio = MT8167_GPIO24_EINT24, // ~ADC_RESET.
-        },
-    };
     static constexpr pbus_bti_t btis_in[] = {
         {
             .iommu_index = 0,
             .bti_id = BTI_AUDIO_IN,
         },
     };
-    static constexpr pbus_i2c_channel_t i2cs_in[] = {
-        {
-            .bus_id = 1,
-            .address = 0x1B,
-        },
-    };
 
     metadata::Codec out_codec = metadata::Codec::Tas5782; // Default to PDEV_PID_MEDIATEK_8167S_REF.
     if (board_info_.pid == PDEV_PID_CLEO) {
@@ -109,50 +165,11 @@
     dev_out.did = PDEV_DID_MEDIATEK_AUDIO_OUT;
     dev_out.mmio_list = mmios;
     dev_out.mmio_count = countof(mmios);
-    dev_out.clk_list = clks;
-    dev_out.clk_count = countof(clks);
     dev_out.bti_list = btis_out;
     dev_out.bti_count = countof(btis_out);
     dev_out.metadata_list = out_metadata;
     dev_out.metadata_count = countof(out_metadata);
 
-    pbus_gpio_t mt8167s_ref_gpios_out[] = {
-        {
-            .gpio = MT8167_GPIO107_MSDC1_DAT1, // ~AMP_RESET.
-        },
-        {
-            .gpio = MT8167_GPIO108_MSDC1_DAT2, // ~AMP_MUTE.
-        },
-    };
-    // No reset/mute on Cleo.
-    if (board_info_.pid == PDEV_PID_MEDIATEK_8167S_REF) {
-        dev_out.gpio_list = mt8167s_ref_gpios_out;
-        dev_out.gpio_count = countof(mt8167s_ref_gpios_out);
-    } else {
-        dev_out.gpio_list = nullptr;
-        dev_out.gpio_count = 0;
-    }
-
-    pbus_i2c_channel_t mt8167s_ref_i2cs_out[] = {
-        {
-            .bus_id = 2,
-            .address = 0x48,
-        },
-    };
-    pbus_i2c_channel_t cleo_i2cs_out[] = {
-        {
-            .bus_id = 2,
-            .address = 0x2C,
-        },
-    };
-    if (board_info_.pid == PDEV_PID_MEDIATEK_8167S_REF) {
-        dev_out.i2c_channel_list = mt8167s_ref_i2cs_out;
-        dev_out.i2c_channel_count = countof(mt8167s_ref_i2cs_out);
-    } else {
-        dev_out.i2c_channel_list = cleo_i2cs_out;
-        dev_out.i2c_channel_count = countof(cleo_i2cs_out);
-    }
-
     pbus_dev_t dev_in = {};
     dev_in.name = "mt8167-audio-in";
     dev_in.vid = PDEV_VID_MEDIATEK;
@@ -160,14 +177,8 @@
     dev_in.did = PDEV_DID_MEDIATEK_AUDIO_IN;
     dev_in.mmio_list = mmios;
     dev_in.mmio_count = countof(mmios);
-    dev_in.clk_list = clks;
-    dev_in.clk_count = countof(clks);
-    dev_in.gpio_list = gpios_in;
-    dev_in.gpio_count = countof(gpios_in);
     dev_in.bti_list = btis_in;
     dev_in.bti_count = countof(btis_in);
-    dev_in.i2c_channel_list = i2cs_in;
-    dev_in.i2c_channel_count = countof(i2cs_in);
 
     // Output pin assignments.
     // Datasheet has 2 numberings for I2S engines: I2S[0-3] (used in GPIOs) and I2S[1-4] (other
@@ -227,14 +238,30 @@
     pmic.set_WACS2_WRITE(1).set_WACS2_ADR(kDigLdoCon11 >> 1).set_WACS2_WDATA(kVcn18Enable);
     pmic.WriteTo(&(*pmic_mmio));
 
-    status = pbus_.DeviceAdd(&dev_out);
+    // Enable clocks. These are needed by both the input and output drivers, so enable them here
+    // instead of in those drivers.
+    ddk::ClockImplProtocolClient clock = parent();
+    if (!clock.is_valid()) {
+        zxlogf(ERROR, "%s: could not get CLOCK_IMPL protocol\n", __func__);
+        return ZX_ERR_INTERNAL;
+    }
+    clock.Enable(kClkRgAud1);
+    clock.Enable(kClkRgAud2);
+    
+    if (board_info_.pid == PDEV_PID_MEDIATEK_8167S_REF) {
+        status = pbus_.CompositeDeviceAdd(&dev_out, mt8167s_ref_out_components,
+                                          countof(mt8167s_ref_out_components), UINT32_MAX);
+    } else {
+        status = pbus_.CompositeDeviceAdd(&dev_out, cleo_out_components,
+                                          countof(cleo_out_components), UINT32_MAX);
+    }
     if (status != ZX_OK) {
-        zxlogf(ERROR, "%s: pbus_.DeviceAdd failed %d\n", __FUNCTION__, status);
+        zxlogf(ERROR, "%s: pbus_.CompositeDeviceAdd failed %d\n", __FUNCTION__, status);
         return status;
     }
-    status = pbus_.DeviceAdd(&dev_in);
+    status = pbus_.CompositeDeviceAdd(&dev_in, in_components, countof(in_components), UINT32_MAX);
     if (status != ZX_OK) {
-        zxlogf(ERROR, "%s: pbus_.DeviceAdd failed %d\n", __FUNCTION__, status);
+        zxlogf(ERROR, "%s: pbus_.CompositeDeviceAdd failed %d\n", __FUNCTION__, status);
         return status;
     }
     return ZX_OK;
diff --git a/zircon/system/dev/board/mt8167s_ref/mt8167-gpio.cpp b/zircon/system/dev/board/mt8167s_ref/mt8167-gpio.cpp
index 01dc639..e6181045 100644
--- a/zircon/system/dev/board/mt8167s_ref/mt8167-gpio.cpp
+++ b/zircon/system/dev/board/mt8167s_ref/mt8167-gpio.cpp
@@ -8,6 +8,7 @@
 #include <ddk/metadata/gpio.h>
 #include <ddk/platform-defs.h>
 #include <ddk/protocol/platform/bus.h>
+#include <soc/mt8167/mt8167-gpio.h>
 #include <soc/mt8167/mt8167-hw.h>
 
 #include "mt8167.h"
@@ -46,6 +47,11 @@
         // For touch screen driver
         { MT8167_GPIO_TOUCH_INT },
         { MT8167_GPIO_TOUCH_RST },
+        // For mt8167s audio out
+        { MT8167_GPIO107_MSDC1_DAT1 },
+        { MT8167_GPIO108_MSDC1_DAT2 },
+        // For audio in
+        { MT8167_GPIO24_EINT24 },
     };
 
     const pbus_metadata_t cleo_gpio_metadata[] = {
diff --git a/zircon/system/dev/board/mt8167s_ref/mt8167-i2c.cpp b/zircon/system/dev/board/mt8167s_ref/mt8167-i2c.cpp
index 2e734de..a3a81fb 100644
--- a/zircon/system/dev/board/mt8167s_ref/mt8167-i2c.cpp
+++ b/zircon/system/dev/board/mt8167s_ref/mt8167-i2c.cpp
@@ -104,6 +104,30 @@
             .pid = 0,
             .did = 0,
         },
+        // For mt8167s_ref audio out
+        {
+            .bus_id = 2,
+            .address = 0x48,
+            .vid = 0,
+            .pid = 0,
+            .did = 0,
+        },
+        // For cleo audio out
+        {
+            .bus_id = 2,
+            .address = 0x2C,
+            .vid = 0,
+            .pid = 0,
+            .did = 0,
+        },
+        // For audio in
+        {
+            .bus_id = 1,
+            .address = 0x1B,
+            .vid = 0,
+            .pid = 0,
+            .did = 0,
+        },
     };
 
     const pbus_metadata_t cleo_i2c_metadata[] = {