From 875f4355698bebcf0bb299e510d558972442553b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 May 2018 17:34:45 -0500 Subject: [PATCH 1/2] ASoC: wm8804: Add ACPI support HID made of either Wolfson/CirrusLogic PCI ID + 8804 identifier. Signed-off-by: Pierre-Louis Bossart --- sound/soc/codecs/wm8804-i2c.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c index f27464c2c5bad5..79541960f45d94 100644 --- a/sound/soc/codecs/wm8804-i2c.c +++ b/sound/soc/codecs/wm8804-i2c.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "wm8804.h" @@ -40,17 +41,29 @@ static const struct i2c_device_id wm8804_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id); +#if defined(CONFIG_OF) static const struct of_device_id wm8804_of_match[] = { { .compatible = "wlf,wm8804", }, { } }; MODULE_DEVICE_TABLE(of, wm8804_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id wm8804_acpi_match[] = { + { "1AEC8804", 0 }, /* Wolfson PCI ID + part ID */ + { "10138804", 0 }, /* Cirrus Logic PCI ID + part ID */ + { }, +}; +MODULE_DEVICE_TABLE(acpi, wm8804_acpi_match); +#endif static struct i2c_driver wm8804_i2c_driver = { .driver = { .name = "wm8804", .pm = &wm8804_pm, - .of_match_table = wm8804_of_match, + .of_match_table = of_match_ptr(wm8804_of_match), + .acpi_match_table = ACPI_PTR(wm8804_acpi_match), }, .probe = wm8804_i2c_probe, .remove = wm8804_i2c_remove, From 9ccf650c51279ec13c7924343d88e983b4df6660 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 May 2018 18:19:44 -0500 Subject: [PATCH 2/2] ASoC: Intel: boards: initial support for Hifiberry Digiplus w/ WM8804 Based on code from sound/soc/bcm/hifiberry-digi.c in RPI tree TODO: add support for DIGI+ IO (capture) and Digi+ PRO Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/Kconfig | 10 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_wm8804.c | 263 ++++++++++++++++++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_wm8804.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 0b67c81a86d048..585d6f6016292b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -261,6 +261,16 @@ config SND_SOC_INTEL_BXT_PCM512x_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXT_WM8804_MACH + tristate "Broxton with Wolfson/Cirrus WM8804 codec for Hifiberry Digi+" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_WM8804_I2C + help + This adds support for ASoC machine driver for Broxton platforms + with Hifiberry Digiplus w/ Wolfson/Cirrus WM8804 I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + config SND_SOC_INTEL_BXT_TDF8532_MACH tristate "ASoC Audio driver for BXT with TDF8532 in I2S mode" depends on X86 && ACPI && I2C diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index c85f0b9bd3792c..73f8efb98ee1fc 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -7,6 +7,7 @@ snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o snd-soc-sst-bxt-pcm512x-objs := bxt_pcm512x.o +snd-soc-sst-bxt-wm8804-objs := bxt_wm8804.o snd-soc-sst_bxt_tdf8532-objs := bxt_tdf8532.o snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o @@ -32,6 +33,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXT_PCM512x_MACH) += snd-soc-sst-bxt-pcm512x.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_WM8804_MACH) += snd-soc-sst-bxt-wm8804.o obj-$(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) += snd-soc-sst_bxt_tdf8532.o obj-$(CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o diff --git a/sound/soc/intel/boards/bxt_wm8804.c b/sound/soc/intel/boards/bxt_wm8804.c new file mode 100644 index 00000000000000..de6a4669731bd2 --- /dev/null +++ b/sound/soc/intel/boards/bxt_wm8804.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * bxt-wm8804.c - ASoC machine driver for Up and Up2 board + * based on WM8804/Hifiberry Digi+ + * Copyright (c) 2018, Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/wm8804.h" +#include "../atom/sst-atom-controls.h" +#include + +#include "../../codecs/wm8804.h" + +static short int auto_shutdown_output; +module_param(auto_shutdown_output, short, 0660); +MODULE_PARM_DESC(auto_shutdown_output, "Shutdown SP/DIF output if playback is stopped"); + +#define CLK_44EN_RATE 22579200UL +#define CLK_48EN_RATE 24576000UL + +static bool snd_rpi_hifiberry_is_digipro; +static struct gpio_desc *snd_rpi_hifiberry_clk44gpio; +static struct gpio_desc *snd_rpi_hifiberry_clk48gpio; + +static int samplerate = 44100; + +static uint32_t snd_rpi_hifiberry_digi_enable_clock(int sample_rate) +{ + switch (sample_rate) { + case 11025: + case 22050: + case 44100: + case 88200: + case 176400: + gpiod_set_value_cansleep(snd_rpi_hifiberry_clk44gpio, 1); + gpiod_set_value_cansleep(snd_rpi_hifiberry_clk48gpio, 0); + return CLK_44EN_RATE; + default: + gpiod_set_value_cansleep(snd_rpi_hifiberry_clk48gpio, 1); + gpiod_set_value_cansleep(snd_rpi_hifiberry_clk44gpio, 0); + return CLK_48EN_RATE; + } +} + +static int snd_rpi_hifiberry_digi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *codec = rtd->codec_dai->component; + + /* enable TX output */ + snd_soc_component_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); + + /* Initialize Digi+ Pro hardware */ + if (snd_rpi_hifiberry_is_digipro) { + struct snd_soc_dai_link *dai = rtd->dai_link; + + dai->name = "HiFiBerry Digi+ Pro"; + dai->stream_name = "HiFiBerry Digi+ Pro HiFi"; + } + + return 0; +} + +static int snd_rpi_hifiberry_digi_startup(struct snd_pcm_substream *substream) +{ + /* turn on digital output */ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *codec = rtd->codec_dai->component; + + snd_soc_component_update_bits(codec, WM8804_PWRDN, 0x3c, 0x00); + return 0; +} + +static void snd_rpi_hifiberry_digi_shutdown(struct snd_pcm_substream *substream) +{ + /* turn off output */ + if (auto_shutdown_output) { + /* turn off output */ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *codec = rtd->codec_dai->component; + + snd_soc_component_update_bits(codec, WM8804_PWRDN, 0x3c, 0x3c); + } +} + +static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *codec = codec_dai->component; + + int sysclk = 27000000; /* This is fixed on this board */ + + long mclk_freq = 0; + int mclk_div = 1; + int sampling_freq = 1; + + int ret; + + samplerate = params_rate(params); + + if (samplerate <= 96000) { + mclk_freq = samplerate * 256; + mclk_div = WM8804_MCLKDIV_256FS; + } else { + mclk_freq = samplerate * 128; + mclk_div = WM8804_MCLKDIV_128FS; + } + + if (snd_rpi_hifiberry_is_digipro) + sysclk = snd_rpi_hifiberry_digi_enable_clock(samplerate); + + switch (samplerate) { + case 32000: + sampling_freq = 0x03; + break; + case 44100: + sampling_freq = 0x00; + break; + case 48000: + sampling_freq = 0x02; + break; + case 88200: + sampling_freq = 0x08; + break; + case 96000: + sampling_freq = 0x0a; + break; + case 176400: + sampling_freq = 0x0c; + break; + case 192000: + sampling_freq = 0x0e; + break; + default: + dev_err(codec->dev, + "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n", + samplerate); + } + + snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div); + snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq); + + ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL, + sysclk, SND_SOC_CLOCK_OUT); + + if (ret < 0) { + dev_err(codec->dev, + "Failed to set WM8804 SYSCLK: %d\n", ret); + return ret; + } + + /* Enable TX output */ + snd_soc_component_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); + + /* Power on */ + snd_soc_component_update_bits(codec, WM8804_PWRDN, 0x9, 0); + + /* set sampling frequency status bits */ + snd_soc_component_update_bits(codec, WM8804_SPDTX4, 0x0f, + sampling_freq); + + return 0; +} + +/* machine stream operations */ +static struct snd_soc_ops snd_rpi_hifiberry_digi_ops = { + .hw_params = snd_rpi_hifiberry_digi_hw_params, + .startup = snd_rpi_hifiberry_digi_startup, + .shutdown = snd_rpi_hifiberry_digi_shutdown, +}; + +static struct snd_soc_dai_link dailink[] = { + /* CODEC<->CODEC link */ + /* back ends */ + { + .name = "SSP5-Codec", + .id = 0, + .cpu_dai_name = "SSP5 Pin", + .platform_name = "sof-audio", + .no_pcm = 1, + .codec_dai_name = "wm8804-spdif", + .codec_name = "i2c-1AEC8804:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .nonatomic = true, + .dpcm_playback = 1, + .ops = &snd_rpi_hifiberry_digi_ops, + .init = snd_rpi_hifiberry_digi_init, + }, +}; + +/* SoC card */ +static struct snd_soc_card bxt_wm8804_card = { + .name = "bxt-wm8804", + .owner = THIS_MODULE, + .dai_link = dailink, + .num_links = ARRAY_SIZE(dailink), +}; + + /* i2c-:00 with HID being 8 chars */ +static char codec_name[SND_ACPI_I2C_ID_LEN]; + +static int bxt_wm8804_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct snd_soc_acpi_mach *mach; + const char *i2c_name = NULL; + int dai_index = 0; + int ret_val = 0; + int i; + + mach = (&pdev->dev)->platform_data; + card = &bxt_wm8804_card; + card->dev = &pdev->dev; + + /* fix index of codec dai */ + for (i = 0; i < ARRAY_SIZE(dailink); i++) { + if (!strcmp(dailink[i].codec_name, "i2c-1AEC8804:00")) { + dai_index = i; + break; + } + } + + /* fixup codec name based on HID */ + i2c_name = acpi_dev_get_first_match_name(mach->id, NULL, -1); + if (i2c_name) { + snprintf(codec_name, sizeof(codec_name), + "%s%s", "i2c-", i2c_name); + dailink[dai_index].codec_name = codec_name; + } + + ret_val = devm_snd_soc_register_card(&pdev->dev, card); + if (ret_val) { + dev_err(&pdev->dev, + "snd_soc_register_card failed %d\n", ret_val); + return ret_val; + } + platform_set_drvdata(pdev, card); + return ret_val; +} + +static struct platform_driver bxt_wm8804_driver = { + .driver = { + .name = "bxt-wm8804", + }, + .probe = bxt_wm8804_probe, +}; +module_platform_driver(bxt_wm8804_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) Broxton + WM8804 Machine driver"); +MODULE_AUTHOR("Pierre-Louis Bossart"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt-wm8804");