162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci// Copyright (c) 2018-2020, Intel Corporation
362306a36Sopenharmony_ci//
462306a36Sopenharmony_ci// sof-wm8804.c - ASoC machine driver for Up and Up2 board
562306a36Sopenharmony_ci// based on WM8804/Hifiberry Digi+
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/acpi.h>
962306a36Sopenharmony_ci#include <linux/dmi.h>
1062306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1162306a36Sopenharmony_ci#include <linux/gpio/machine.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <sound/pcm.h>
1662306a36Sopenharmony_ci#include <sound/pcm_params.h>
1762306a36Sopenharmony_ci#include <sound/soc.h>
1862306a36Sopenharmony_ci#include <sound/soc-acpi.h>
1962306a36Sopenharmony_ci#include "../../codecs/wm8804.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistruct sof_card_private {
2262306a36Sopenharmony_ci	struct gpio_desc *gpio_44;
2362306a36Sopenharmony_ci	struct gpio_desc *gpio_48;
2462306a36Sopenharmony_ci	int sample_rate;
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define SOF_WM8804_UP2_QUIRK			BIT(0)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic unsigned long sof_wm8804_quirk;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int sof_wm8804_quirk_cb(const struct dmi_system_id *id)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	sof_wm8804_quirk = (unsigned long)id->driver_data;
3462306a36Sopenharmony_ci	return 1;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic const struct dmi_system_id sof_wm8804_quirk_table[] = {
3862306a36Sopenharmony_ci	{
3962306a36Sopenharmony_ci		.callback = sof_wm8804_quirk_cb,
4062306a36Sopenharmony_ci		.matches = {
4162306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
4262306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "UP-APL01"),
4362306a36Sopenharmony_ci		},
4462306a36Sopenharmony_ci		.driver_data = (void *)SOF_WM8804_UP2_QUIRK,
4562306a36Sopenharmony_ci	},
4662306a36Sopenharmony_ci	{}
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int sof_wm8804_hw_params(struct snd_pcm_substream *substream,
5062306a36Sopenharmony_ci				struct snd_pcm_hw_params *params)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
5362306a36Sopenharmony_ci	struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
5462306a36Sopenharmony_ci	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
5562306a36Sopenharmony_ci	struct snd_soc_component *codec = codec_dai->component;
5662306a36Sopenharmony_ci	const int sysclk = 27000000; /* This is fixed on this board */
5762306a36Sopenharmony_ci	int samplerate;
5862306a36Sopenharmony_ci	long mclk_freq;
5962306a36Sopenharmony_ci	int mclk_div;
6062306a36Sopenharmony_ci	int sampling_freq;
6162306a36Sopenharmony_ci	bool clk_44;
6262306a36Sopenharmony_ci	int ret;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	samplerate = params_rate(params);
6562306a36Sopenharmony_ci	if (samplerate == ctx->sample_rate)
6662306a36Sopenharmony_ci		return 0;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	ctx->sample_rate = 0;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (samplerate <= 96000) {
7162306a36Sopenharmony_ci		mclk_freq = samplerate * 256;
7262306a36Sopenharmony_ci		mclk_div = WM8804_MCLKDIV_256FS;
7362306a36Sopenharmony_ci	} else {
7462306a36Sopenharmony_ci		mclk_freq = samplerate * 128;
7562306a36Sopenharmony_ci		mclk_div = WM8804_MCLKDIV_128FS;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	switch (samplerate) {
7962306a36Sopenharmony_ci	case 32000:
8062306a36Sopenharmony_ci		sampling_freq = 0x03;
8162306a36Sopenharmony_ci		break;
8262306a36Sopenharmony_ci	case 44100:
8362306a36Sopenharmony_ci		sampling_freq = 0x00;
8462306a36Sopenharmony_ci		break;
8562306a36Sopenharmony_ci	case 48000:
8662306a36Sopenharmony_ci		sampling_freq = 0x02;
8762306a36Sopenharmony_ci		break;
8862306a36Sopenharmony_ci	case 88200:
8962306a36Sopenharmony_ci		sampling_freq = 0x08;
9062306a36Sopenharmony_ci		break;
9162306a36Sopenharmony_ci	case 96000:
9262306a36Sopenharmony_ci		sampling_freq = 0x0a;
9362306a36Sopenharmony_ci		break;
9462306a36Sopenharmony_ci	case 176400:
9562306a36Sopenharmony_ci		sampling_freq = 0x0c;
9662306a36Sopenharmony_ci		break;
9762306a36Sopenharmony_ci	case 192000:
9862306a36Sopenharmony_ci		sampling_freq = 0x0e;
9962306a36Sopenharmony_ci		break;
10062306a36Sopenharmony_ci	default:
10162306a36Sopenharmony_ci		dev_err(rtd->card->dev,
10262306a36Sopenharmony_ci			"unsupported samplerate %d\n", samplerate);
10362306a36Sopenharmony_ci		return -EINVAL;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (samplerate % 16000)
10762306a36Sopenharmony_ci		clk_44 = true; /* use 44.1 kHz root frequency */
10862306a36Sopenharmony_ci	else
10962306a36Sopenharmony_ci		clk_44 = false;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (!(IS_ERR_OR_NULL(ctx->gpio_44) ||
11262306a36Sopenharmony_ci	      IS_ERR_OR_NULL(ctx->gpio_48))) {
11362306a36Sopenharmony_ci		/*
11462306a36Sopenharmony_ci		 * ensure both GPIOs are LOW first, then drive the
11562306a36Sopenharmony_ci		 * relevant one to HIGH
11662306a36Sopenharmony_ci		 */
11762306a36Sopenharmony_ci		if (clk_44) {
11862306a36Sopenharmony_ci			gpiod_set_value_cansleep(ctx->gpio_48, !clk_44);
11962306a36Sopenharmony_ci			gpiod_set_value_cansleep(ctx->gpio_44, clk_44);
12062306a36Sopenharmony_ci		} else {
12162306a36Sopenharmony_ci			gpiod_set_value_cansleep(ctx->gpio_44, clk_44);
12262306a36Sopenharmony_ci			gpiod_set_value_cansleep(ctx->gpio_48, !clk_44);
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div);
12762306a36Sopenharmony_ci	ret = snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq);
12862306a36Sopenharmony_ci	if (ret < 0) {
12962306a36Sopenharmony_ci		dev_err(rtd->card->dev, "Failed to set WM8804 PLL\n");
13062306a36Sopenharmony_ci		return ret;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL,
13462306a36Sopenharmony_ci				     sysclk, SND_SOC_CLOCK_OUT);
13562306a36Sopenharmony_ci	if (ret < 0) {
13662306a36Sopenharmony_ci		dev_err(rtd->card->dev,
13762306a36Sopenharmony_ci			"Failed to set WM8804 SYSCLK: %d\n", ret);
13862306a36Sopenharmony_ci		return ret;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* set sampling frequency status bits */
14262306a36Sopenharmony_ci	snd_soc_component_update_bits(codec, WM8804_SPDTX4, 0x0f,
14362306a36Sopenharmony_ci				      sampling_freq);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	ctx->sample_rate = samplerate;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/* machine stream operations */
15162306a36Sopenharmony_cistatic struct snd_soc_ops sof_wm8804_ops = {
15262306a36Sopenharmony_ci	.hw_params = sof_wm8804_hw_params,
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ciSND_SOC_DAILINK_DEF(ssp5_pin,
15662306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CPU("SSP5 Pin")));
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciSND_SOC_DAILINK_DEF(ssp5_codec,
15962306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CODEC("i2c-1AEC8804:00", "wm8804-spdif")));
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ciSND_SOC_DAILINK_DEF(platform,
16262306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:0e.0")));
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic struct snd_soc_dai_link dailink[] = {
16562306a36Sopenharmony_ci	/* back ends */
16662306a36Sopenharmony_ci	{
16762306a36Sopenharmony_ci		.name = "SSP5-Codec",
16862306a36Sopenharmony_ci		.id = 0,
16962306a36Sopenharmony_ci		.no_pcm = 1,
17062306a36Sopenharmony_ci		.dpcm_playback = 1,
17162306a36Sopenharmony_ci		.dpcm_capture = 1,
17262306a36Sopenharmony_ci		.ops = &sof_wm8804_ops,
17362306a36Sopenharmony_ci		SND_SOC_DAILINK_REG(ssp5_pin, ssp5_codec, platform),
17462306a36Sopenharmony_ci	},
17562306a36Sopenharmony_ci};
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/* SoC card */
17862306a36Sopenharmony_cistatic struct snd_soc_card sof_wm8804_card = {
17962306a36Sopenharmony_ci	.name = "wm8804", /* sof- prefix added automatically */
18062306a36Sopenharmony_ci	.owner = THIS_MODULE,
18162306a36Sopenharmony_ci	.dai_link = dailink,
18262306a36Sopenharmony_ci	.num_links = ARRAY_SIZE(dailink),
18362306a36Sopenharmony_ci};
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci /* i2c-<HID>:00 with HID being 8 chars */
18662306a36Sopenharmony_cistatic char codec_name[SND_ACPI_I2C_ID_LEN];
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * to control the HifiBerry Digi+ PRO, it's required to toggle GPIO to
19062306a36Sopenharmony_ci * select the clock source. On the Up2 board, this means
19162306a36Sopenharmony_ci * Pin29/BCM5/Linux GPIO 430 and Pin 31/BCM6/ Linux GPIO 404.
19262306a36Sopenharmony_ci *
19362306a36Sopenharmony_ci * Using the ACPI device name is not very nice, but since we only use
19462306a36Sopenharmony_ci * the value for the Up2 board there is no risk of conflict with other
19562306a36Sopenharmony_ci * platforms.
19662306a36Sopenharmony_ci */
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic struct gpiod_lookup_table up2_gpios_table = {
19962306a36Sopenharmony_ci	/* .dev_id is set during probe */
20062306a36Sopenharmony_ci	.table = {
20162306a36Sopenharmony_ci		GPIO_LOOKUP("INT3452:01", 73, "BCM-GPIO5", GPIO_ACTIVE_HIGH),
20262306a36Sopenharmony_ci		GPIO_LOOKUP("INT3452:01", 74, "BCM-GPIO6", GPIO_ACTIVE_HIGH),
20362306a36Sopenharmony_ci		{ },
20462306a36Sopenharmony_ci	},
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int sof_wm8804_probe(struct platform_device *pdev)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct snd_soc_card *card;
21062306a36Sopenharmony_ci	struct snd_soc_acpi_mach *mach;
21162306a36Sopenharmony_ci	struct sof_card_private *ctx;
21262306a36Sopenharmony_ci	struct acpi_device *adev;
21362306a36Sopenharmony_ci	int dai_index = 0;
21462306a36Sopenharmony_ci	int ret;
21562306a36Sopenharmony_ci	int i;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
21862306a36Sopenharmony_ci	if (!ctx)
21962306a36Sopenharmony_ci		return -ENOMEM;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	mach = pdev->dev.platform_data;
22262306a36Sopenharmony_ci	card = &sof_wm8804_card;
22362306a36Sopenharmony_ci	card->dev = &pdev->dev;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	dmi_check_system(sof_wm8804_quirk_table);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (sof_wm8804_quirk & SOF_WM8804_UP2_QUIRK) {
22862306a36Sopenharmony_ci		up2_gpios_table.dev_id = dev_name(&pdev->dev);
22962306a36Sopenharmony_ci		gpiod_add_lookup_table(&up2_gpios_table);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		/*
23262306a36Sopenharmony_ci		 * The gpios are required for specific boards with
23362306a36Sopenharmony_ci		 * local oscillators, and optional in other cases.
23462306a36Sopenharmony_ci		 * Since we can't identify when they are needed, use
23562306a36Sopenharmony_ci		 * the GPIO as non-optional
23662306a36Sopenharmony_ci		 */
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		ctx->gpio_44 = devm_gpiod_get(&pdev->dev, "BCM-GPIO5",
23962306a36Sopenharmony_ci					      GPIOD_OUT_LOW);
24062306a36Sopenharmony_ci		if (IS_ERR(ctx->gpio_44)) {
24162306a36Sopenharmony_ci			ret = PTR_ERR(ctx->gpio_44);
24262306a36Sopenharmony_ci			dev_err(&pdev->dev,
24362306a36Sopenharmony_ci				"could not get BCM-GPIO5: %d\n",
24462306a36Sopenharmony_ci				ret);
24562306a36Sopenharmony_ci			return ret;
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		ctx->gpio_48 = devm_gpiod_get(&pdev->dev, "BCM-GPIO6",
24962306a36Sopenharmony_ci					      GPIOD_OUT_LOW);
25062306a36Sopenharmony_ci		if (IS_ERR(ctx->gpio_48)) {
25162306a36Sopenharmony_ci			ret = PTR_ERR(ctx->gpio_48);
25262306a36Sopenharmony_ci			dev_err(&pdev->dev,
25362306a36Sopenharmony_ci				"could not get BCM-GPIO6: %d\n",
25462306a36Sopenharmony_ci				ret);
25562306a36Sopenharmony_ci			return ret;
25662306a36Sopenharmony_ci		}
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* fix index of codec dai */
26062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(dailink); i++) {
26162306a36Sopenharmony_ci		if (!strcmp(dailink[i].codecs->name, "i2c-1AEC8804:00")) {
26262306a36Sopenharmony_ci			dai_index = i;
26362306a36Sopenharmony_ci			break;
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/* fixup codec name based on HID */
26862306a36Sopenharmony_ci	adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
26962306a36Sopenharmony_ci	if (adev) {
27062306a36Sopenharmony_ci		snprintf(codec_name, sizeof(codec_name),
27162306a36Sopenharmony_ci			 "%s%s", "i2c-", acpi_dev_name(adev));
27262306a36Sopenharmony_ci		dailink[dai_index].codecs->name = codec_name;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	acpi_dev_put(adev);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	snd_soc_card_set_drvdata(card, ctx);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return devm_snd_soc_register_card(&pdev->dev, card);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic void sof_wm8804_remove(struct platform_device *pdev)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	if (sof_wm8804_quirk & SOF_WM8804_UP2_QUIRK)
28462306a36Sopenharmony_ci		gpiod_remove_lookup_table(&up2_gpios_table);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic struct platform_driver sof_wm8804_driver = {
28862306a36Sopenharmony_ci	.driver = {
28962306a36Sopenharmony_ci		.name = "sof-wm8804",
29062306a36Sopenharmony_ci		.pm = &snd_soc_pm_ops,
29162306a36Sopenharmony_ci	},
29262306a36Sopenharmony_ci	.probe = sof_wm8804_probe,
29362306a36Sopenharmony_ci	.remove_new = sof_wm8804_remove,
29462306a36Sopenharmony_ci};
29562306a36Sopenharmony_cimodule_platform_driver(sof_wm8804_driver);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC Intel(R) SOF + WM8804 Machine driver");
29862306a36Sopenharmony_ciMODULE_AUTHOR("Pierre-Louis Bossart");
29962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
30062306a36Sopenharmony_ciMODULE_ALIAS("platform:sof-wm8804");
301