diff mbox series

[5/6] ASoC: Intel: bytcr_wm5102: Add BYT_WM5102_OUT_MAP quirk

Message ID 20231021211534.114991-5-hdegoede@redhat.com
State Superseded
Headers show
Series [1/6] ASoC: Intel: soc-acpi-cht: Add Lenovo Yoga Tab 3 Pro YT3-X90 quirk | expand

Commit Message

Hans de Goede Oct. 21, 2023, 9:15 p.m. UTC
Some x86 WM5102 designs don't use the SPK pins for speaker output
instead they use the HPOUT2L + HPOUT2R for the speakers.

Add an BYT_WM5102_OUT_MAP quirk mechanism to allow selecting
between 2 output maps, one for the speakers on the SPK output pins
and one for the speakers on the HPOUT2 pins.

The new HPOUT2 map is enabled by default on CHT because this is used on
the Lenovo Yoga Tab 3 YT3-X90 model which is the only Cherry Trail design
currently supported. If different CHT designs turn up which need different
output maps we can add DMI quirks to select a different map later.

The userspace UCM profile also needs to know about this so
setup a components string with this info too.

While at it also drop the unused "Line Out" route.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 sound/soc/intel/boards/bytcr_wm5102.c | 72 +++++++++++++++++++++++----
 1 file changed, 63 insertions(+), 9 deletions(-)
diff mbox series

Patch

diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c
index 622ee3079f26..d1664d7e6443 100644
--- a/sound/soc/intel/boards/bytcr_wm5102.c
+++ b/sound/soc/intel/boards/bytcr_wm5102.c
@@ -10,6 +10,7 @@ 
  */
 
 #include <linux/acpi.h>
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/init.h>
@@ -37,10 +38,17 @@  struct byt_wm5102_private {
 	int mclk_freq;
 };
 
-/* Bits 0-15 are reserved for things like an input-map */
+/* Bits 0-3 are reserved for the input-map */
+#define BYT_WM5102_OUT_MAP		GENMASK(7, 4)
 #define BYT_WM5102_SSP2			BIT(16)
 #define BYT_WM5102_MCLK_19_2MHZ		BIT(17)
 
+/* Note these values are pre-shifted for easy use of setting quirks */
+enum {
+	BYT_WM5102_SPK_SPK_MAP		= FIELD_PREP_CONST(BYT_WM5102_OUT_MAP, 0),
+	BYT_WM5102_SPK_HPOUT2_MAP	= FIELD_PREP_CONST(BYT_WM5102_OUT_MAP, 1),
+};
+
 static unsigned long quirk;
 
 static int quirk_override = -1;
@@ -49,6 +57,20 @@  MODULE_PARM_DESC(quirk, "Board-specific quirk override");
 
 static void log_quirks(struct device *dev)
 {
+	switch (quirk & BYT_WM5102_OUT_MAP) {
+	case BYT_WM5102_SPK_SPK_MAP:
+		dev_info_once(dev, "quirk SPK_SPK_MAP enabled\n");
+		break;
+	case BYT_WM5102_SPK_HPOUT2_MAP:
+		dev_info_once(dev, "quirk SPK_HPOUT2_MAP enabled\n");
+		break;
+	default:
+		dev_warn_once(dev, "quirk sets invalid output map: 0x%lx, defaulting to SPK_SPK_MAP\n",
+			      quirk & BYT_WM5102_OUT_MAP);
+		quirk &= ~BYT_WM5102_OUT_MAP;
+		quirk |= BYT_WM5102_SPK_SPK_MAP;
+		break;
+	}
 	if (quirk & BYT_WM5102_SSP2)
 		dev_info_once(dev, "quirk SSP2 enabled");
 	if (quirk & BYT_WM5102_MCLK_19_2MHZ)
@@ -164,12 +186,6 @@  static const struct snd_soc_dapm_route byt_wm5102_audio_map[] = {
 	{"Headset Mic", NULL, "Platform Clock"},
 	{"Internal Mic", NULL, "Platform Clock"},
 	{"Speaker", NULL, "Platform Clock"},
-	{"Line Out", NULL, "Platform Clock"},
-
-	{"Speaker", NULL, "SPKOUTLP"},
-	{"Speaker", NULL, "SPKOUTLN"},
-	{"Speaker", NULL, "SPKOUTRP"},
-	{"Speaker", NULL, "SPKOUTRN"},
 	{"Speaker", NULL, "Speaker VDD"},
 
 	{"Headphone", NULL, "HPOUT1L"},
@@ -203,6 +219,18 @@  static const struct snd_soc_dapm_route bytcr_wm5102_ssp2_map[] = {
 	{"ssp2 Rx", NULL, "AIF1 Capture"},
 };
 
+static const struct snd_soc_dapm_route byt_wm5102_spk_spk_map[] = {
+	{"Speaker", NULL, "SPKOUTLP"},
+	{"Speaker", NULL, "SPKOUTLN"},
+	{"Speaker", NULL, "SPKOUTRP"},
+	{"Speaker", NULL, "SPKOUTRN"},
+};
+
+static const struct snd_soc_dapm_route byt_wm5102_spk_hpout2_map[] = {
+	{"Speaker", NULL, "HPOUT2L"},
+	{"Speaker", NULL, "HPOUT2R"},
+};
+
 static const struct snd_kcontrol_new byt_wm5102_controls[] = {
 	SOC_DAPM_PIN_SWITCH("Headphone"),
 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -243,6 +271,20 @@  static int byt_wm5102_init(struct snd_soc_pcm_runtime *runtime)
 		return ret;
 	}
 
+	switch (quirk & BYT_WM5102_OUT_MAP) {
+	case BYT_WM5102_SPK_SPK_MAP:
+		custom_map = byt_wm5102_spk_spk_map;
+		num_routes = ARRAY_SIZE(byt_wm5102_spk_spk_map);
+		break;
+	case BYT_WM5102_SPK_HPOUT2_MAP:
+		custom_map = byt_wm5102_spk_hpout2_map;
+		num_routes = ARRAY_SIZE(byt_wm5102_spk_hpout2_map);
+		break;
+	}
+	ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
+	if (ret)
+		return ret;
+
 	if (quirk & BYT_WM5102_SSP2) {
 		custom_map = bytcr_wm5102_ssp2_map;
 		num_routes = ARRAY_SIZE(bytcr_wm5102_ssp2_map);
@@ -434,8 +476,11 @@  static struct snd_soc_card byt_wm5102_card = {
 	.fully_routed = true,
 };
 
+static char byt_wm5102_components[64]; /* = "cfg-spk:* cfg-int-mic:* cfg-hs-mic:* ..." */
+
 static int snd_byt_wm5102_mc_probe(struct platform_device *pdev)
 {
+	static const char * const out_map_name[] = { "spk", "hpout2" };
 	char codec_name[SND_ACPI_I2C_ID_LEN];
 	struct device *dev = &pdev->dev;
 	struct byt_wm5102_private *priv;
@@ -493,8 +538,13 @@  static int snd_byt_wm5102_mc_probe(struct platform_device *pdev)
 	}
 
 	if (soc_intel_is_cht()) {
-		/* On CHT default to SSP2 */
-		quirk = BYT_WM5102_SSP2 | BYT_WM5102_MCLK_19_2MHZ;
+		/*
+		 * CHT always uses SSP2 and 19.2 MHz; and
+		 * the one currently supported CHT design uses HPOUT2 as
+		 * speaker output.
+		 */
+		quirk = BYT_WM5102_SSP2 | BYT_WM5102_MCLK_19_2MHZ |
+			BYT_WM5102_SPK_HPOUT2_MAP;
 	}
 	if (quirk_override != -1) {
 		dev_info_once(dev, "Overriding quirk 0x%lx => 0x%x\n",
@@ -503,6 +553,10 @@  static int snd_byt_wm5102_mc_probe(struct platform_device *pdev)
 	}
 	log_quirks(dev);
 
+	snprintf(byt_wm5102_components, sizeof(byt_wm5102_components),
+		 "cfg-spk:%s", out_map_name[FIELD_GET(BYT_WM5102_OUT_MAP, quirk)]);
+	byt_wm5102_card.components = byt_wm5102_components;
+
 	/* find index of codec dai */
 	for (i = 0; i < ARRAY_SIZE(byt_wm5102_dais); i++) {
 		if (!strcmp(byt_wm5102_dais[i].codecs->name,