From 7e0fb11c01dc895d85f5f7266bc98a085e60fd1a Mon Sep 17 00:00:00 2001 From: Tim Gover Date: Fri, 7 Sep 2018 20:55:49 +0100 Subject: [PATCH] ASoC: RPi composite driver for PCM512x based cards Create a new composite driver to support PCM512x based Pi-HAT DAC soundcards in order to reduce the maintenance overhead for upstream changes. This replaces the specific drivers for the following cards: * Allo Piano DAC * DionAudio Loco V2 * Justboom DAC * IQAudioIO DAC --- sound/soc/bcm/Kconfig | 10 + sound/soc/bcm/Makefile | 10 +- sound/soc/bcm/allo-piano-dac.c | 120 --------- sound/soc/bcm/dionaudio_loco-v2.c | 115 -------- sound/soc/bcm/iqaudio-dac.c | 221 --------------- sound/soc/bcm/justboom-dac.c | 145 ---------- sound/soc/bcm/rpi-pcm512x-soundcard.c | 372 ++++++++++++++++++++++++++ 7 files changed, 384 insertions(+), 609 deletions(-) delete mode 100644 sound/soc/bcm/allo-piano-dac.c delete mode 100644 sound/soc/bcm/dionaudio_loco-v2.c delete mode 100644 sound/soc/bcm/iqaudio-dac.c delete mode 100644 sound/soc/bcm/justboom-dac.c create mode 100644 sound/soc/bcm/rpi-pcm512x-soundcard.c diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig index ed792277c145f2..a14f40395fe970 100644 --- a/sound/soc/bcm/Kconfig +++ b/sound/soc/bcm/Kconfig @@ -91,6 +91,7 @@ config SND_BCM2708_SOC_JUSTBOOM_DAC tristate "Support for JustBoom DAC" depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S select SND_SOC_PCM512x + select SND_RPI_PCM512x_SOUNDCARD help Say Y or M if you want to add support for JustBoom DAC. @@ -106,6 +107,7 @@ config SND_BCM2708_SOC_IQAUDIO_DAC tristate "Support for IQaudIO-DAC" depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S select SND_SOC_PCM512x_I2C + select SND_RPI_PCM512x_SOUNDCARD help Say Y or M if you want to add support for IQaudIO-DAC. @@ -158,6 +160,7 @@ config SND_BCM2708_SOC_DIONAUDIO_LOCO_V2 tristate "Support for Dion Audio LOCO-V2 DAC-AMP" depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S select SND_SOC_PCM5122 + select SND_RPI_PCM512x_SOUNDCARD help Say Y or M if you want to add support for Dion Audio LOCO-V2. @@ -165,6 +168,7 @@ config SND_BCM2708_SOC_ALLO_PIANO_DAC tristate "Support for Allo Piano DAC" depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S select SND_SOC_PCM512x_I2C + select SND_RPI_PCM512x_SOUNDCARD help Say Y or M if you want to add support for Allo Piano DAC. @@ -223,3 +227,9 @@ config SND_RPI_WM8804_SOUNDCARD help Say Y or M if you want to add support for the Raspberry Pi generic driver for WM8804 based soundcards. + +config SND_RPI_PCM512x_SOUNDCARD + tristate "Support for Raspberry Pi generic PCM512x soundcards" + help + Say Y or M if you want to add support for the Raspberry Pi + generic driver for PCM512x based soundcards. diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile index b49d83d435fdcf..c38cb6dcd5909e 100644 --- a/sound/soc/bcm/Makefile +++ b/sound/soc/bcm/Makefile @@ -14,41 +14,35 @@ snd-soc-googlevoicehat-codec-objs := googlevoicehat-codec.o # BCM2708 Machine Support snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.o snd-soc-hifiberry-dacplusadc-objs := hifiberry_dacplusadc.o -snd-soc-justboom-dac-objs := justboom-dac.o snd-soc-rpi-cirrus-objs := rpi-cirrus.o snd-soc-rpi-proto-objs := rpi-proto.o -snd-soc-iqaudio-dac-objs := iqaudio-dac.o snd-soc-audioinjector-pi-soundcard-objs := audioinjector-pi-soundcard.o snd-soc-audioinjector-octo-soundcard-objs := audioinjector-octo-soundcard.o snd-soc-digidac1-soundcard-objs := digidac1-soundcard.o snd-soc-dionaudio-loco-objs := dionaudio_loco.o -snd-soc-dionaudio-loco-v2-objs := dionaudio_loco-v2.o snd-soc-allo-boss-dac-objs := allo-boss-dac.o -snd-soc-allo-piano-dac-objs := allo-piano-dac.o snd-soc-allo-piano-dac-plus-objs := allo-piano-dac-plus.o snd-soc-allo-katana-codec-objs := allo-katana-codec.o snd-soc-pisound-objs := pisound.o snd-soc-fe-pi-audio-objs := fe-pi-audio.o snd-soc-rpi-simple-soundcard-objs := rpi-simple-soundcard.o snd-soc-rpi-wm8804-soundcard-objs := rpi-wm8804-soundcard.o +snd-soc-rpi-pcm512x-soundcard-objs := rpi-pcm512x-soundcard.o obj-$(CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD) += snd-soc-googlevoicehat-codec.o obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC) += snd-soc-hifiberry-dacplusadc.o -obj-$(CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC) += snd-soc-justboom-dac.o obj-$(CONFIG_SND_BCM2708_SOC_RPI_CIRRUS) += snd-soc-rpi-cirrus.o obj-$(CONFIG_SND_BCM2708_SOC_RPI_PROTO) += snd-soc-rpi-proto.o -obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o obj-$(CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD) += snd-soc-audioinjector-pi-soundcard.o obj-$(CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD) += snd-soc-audioinjector-octo-soundcard.o obj-$(CONFIG_SND_DIGIDAC1_SOUNDCARD) += snd-soc-digidac1-soundcard.o obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO) += snd-soc-dionaudio-loco.o -obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO_V2) += snd-soc-dionaudio-loco-v2.o obj-$(CONFIG_SND_BCM2708_SOC_ALLO_BOSS_DAC) += snd-soc-allo-boss-dac.o -obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC) += snd-soc-allo-piano-dac.o obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS) += snd-soc-allo-piano-dac-plus.o obj-$(CONFIG_SND_BCM2708_SOC_ALLO_KATANA_DAC) += snd-soc-allo-katana-codec.o obj-$(CONFIG_SND_PISOUND) += snd-soc-pisound.o obj-$(CONFIG_SND_BCM2708_SOC_FE_PI_AUDIO) += snd-soc-fe-pi-audio.o obj-$(CONFIG_SND_RPI_SIMPLE_SOUNDCARD) += snd-soc-rpi-simple-soundcard.o obj-$(CONFIG_SND_RPI_WM8804_SOUNDCARD) += snd-soc-rpi-wm8804-soundcard.o +obj-$(CONFIG_SND_RPI_PCM512x_SOUNDCARD) += snd-soc-rpi-pcm512x-soundcard.o diff --git a/sound/soc/bcm/allo-piano-dac.c b/sound/soc/bcm/allo-piano-dac.c deleted file mode 100644 index df5831763aa458..00000000000000 --- a/sound/soc/bcm/allo-piano-dac.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * ALSA ASoC Machine Driver for Allo Piano DAC - * - * Author: Baswaraj K - * Copyright 2016 - * based on code by Daniel Matuschek - * based on code by Florian Meier - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include -#include - -#include -#include -#include -#include - -static bool digital_gain_0db_limit = true; - -static int snd_allo_piano_dac_init(struct snd_soc_pcm_runtime *rtd) -{ - if (digital_gain_0db_limit) { - int ret; - struct snd_soc_card *card = rtd->card; - - ret = snd_soc_limit_volume(card, "Digital Playback Volume", - 207); - if (ret < 0) - dev_warn(card->dev, "Failed to set volume limit: %d\n", - ret); - } - - return 0; -} - -static struct snd_soc_dai_link snd_allo_piano_dac_dai[] = { -{ - .name = "Piano DAC", - .stream_name = "Piano DAC HiFi", - .cpu_dai_name = "bcm2708-i2s.0", - .codec_dai_name = "pcm512x-hifi", - .platform_name = "bcm2708-i2s.0", - .codec_name = "pcm512x.1-004c", - .dai_fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .init = snd_allo_piano_dac_init, -}, -}; - -/* audio machine driver */ -static struct snd_soc_card snd_allo_piano_dac = { - .name = "PianoDAC", - .owner = THIS_MODULE, - .dai_link = snd_allo_piano_dac_dai, - .num_links = ARRAY_SIZE(snd_allo_piano_dac_dai), -}; - -static int snd_allo_piano_dac_probe(struct platform_device *pdev) -{ - int ret = 0; - - snd_allo_piano_dac.dev = &pdev->dev; - - if (pdev->dev.of_node) { - struct device_node *i2s_node; - struct snd_soc_dai_link *dai; - - dai = &snd_allo_piano_dac_dai[0]; - i2s_node = of_parse_phandle(pdev->dev.of_node, - "i2s-controller", 0); - - if (i2s_node) { - dai->cpu_dai_name = NULL; - dai->cpu_of_node = i2s_node; - dai->platform_name = NULL; - dai->platform_of_node = i2s_node; - } - - digital_gain_0db_limit = !of_property_read_bool( - pdev->dev.of_node, "allo,24db_digital_gain"); - } - - ret = devm_snd_soc_register_card(&pdev->dev, &snd_allo_piano_dac); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "snd_soc_register_card() failed: %d\n", ret); - - return ret; -} - -static const struct of_device_id snd_allo_piano_dac_of_match[] = { - { .compatible = "allo,piano-dac", }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, snd_allo_piano_dac_of_match); - -static struct platform_driver snd_allo_piano_dac_driver = { - .driver = { - .name = "snd-allo-piano-dac", - .owner = THIS_MODULE, - .of_match_table = snd_allo_piano_dac_of_match, - }, - .probe = snd_allo_piano_dac_probe, -}; - -module_platform_driver(snd_allo_piano_dac_driver); - -MODULE_AUTHOR("Baswaraj K "); -MODULE_DESCRIPTION("ALSA ASoC Machine Driver for Allo Piano DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/bcm/dionaudio_loco-v2.c b/sound/soc/bcm/dionaudio_loco-v2.c deleted file mode 100644 index 192cbfd7baca58..00000000000000 --- a/sound/soc/bcm/dionaudio_loco-v2.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * ASoC Driver for Dion Audio LOCO-V2 DAC-AMP - * - * Author: Miquel Blauw - * Copyright 2017 - * - * Based on the software of the RPi-DAC writen by Florian Meier - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include -#include - -#include -#include -#include -#include -#include - -static bool digital_gain_0db_limit = true; - -static int snd_rpi_dionaudio_loco_v2_init(struct snd_soc_pcm_runtime *rtd) -{ - if (digital_gain_0db_limit) { - int ret; - struct snd_soc_card *card = rtd->card; - - ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); - if (ret < 0) - dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); - } - - return 0; -} - -static struct snd_soc_dai_link snd_rpi_dionaudio_loco_v2_dai[] = { -{ - .name = "DionAudio LOCO-V2", - .stream_name = "DionAudio LOCO-V2 DAC-AMP", - .cpu_dai_name = "bcm2708-i2s.0", - .codec_dai_name = "pcm512x-hifi", - .platform_name = "bcm2708-i2s.0", - .codec_name = "pcm512x.1-004d", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .init = snd_rpi_dionaudio_loco_v2_init, -},}; - -/* audio machine driver */ -static struct snd_soc_card snd_rpi_dionaudio_loco_v2 = { - .name = "Dion Audio LOCO-V2", - .dai_link = snd_rpi_dionaudio_loco_v2_dai, - .num_links = ARRAY_SIZE(snd_rpi_dionaudio_loco_v2_dai), -}; - -static int snd_rpi_dionaudio_loco_v2_probe(struct platform_device *pdev) -{ - int ret = 0; - - snd_rpi_dionaudio_loco_v2.dev = &pdev->dev; - - if (pdev->dev.of_node) { - struct device_node *i2s_node; - struct snd_soc_dai_link *dai = - &snd_rpi_dionaudio_loco_v2_dai[0]; - - i2s_node = of_parse_phandle(pdev->dev.of_node, - "i2s-controller", 0); - if (i2s_node) { - dai->cpu_dai_name = NULL; - dai->cpu_of_node = i2s_node; - dai->platform_name = NULL; - dai->platform_of_node = i2s_node; - } - - digital_gain_0db_limit = !of_property_read_bool( - pdev->dev.of_node, "dionaudio,24db_digital_gain"); - } - - ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_dionaudio_loco_v2); - if (ret) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); - - return ret; -} - -static const struct of_device_id dionaudio_of_match[] = { - { .compatible = "dionaudio,dionaudio-loco-v2", }, - {}, -}; -MODULE_DEVICE_TABLE(of, dionaudio_of_match); - -static struct platform_driver snd_rpi_dionaudio_loco_v2_driver = { - .driver = { - .name = "snd-rpi-dionaudio-loco-v2", - .owner = THIS_MODULE, - .of_match_table = dionaudio_of_match, - }, - .probe = snd_rpi_dionaudio_loco_v2_probe, -}; - -module_platform_driver(snd_rpi_dionaudio_loco_v2_driver); - -MODULE_AUTHOR("Miquel Blauw "); -MODULE_DESCRIPTION("ASoC Driver for DionAudio LOCO-V2"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/bcm/iqaudio-dac.c b/sound/soc/bcm/iqaudio-dac.c deleted file mode 100644 index 4583d199fd83d2..00000000000000 --- a/sound/soc/bcm/iqaudio-dac.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * ASoC Driver for IQaudIO DAC - * - * Author: Florian Meier - * Copyright 2013 - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -static bool digital_gain_0db_limit = true; - -static struct gpio_desc *mute_gpio; - -static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd) -{ - if (digital_gain_0db_limit) - { - int ret; - struct snd_soc_card *card = rtd->card; - - ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); - if (ret < 0) - dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); - } - - return 0; -} - -static void snd_rpi_iqaudio_gpio_mute(struct snd_soc_card *card) -{ - if (mute_gpio) { - dev_info(card->dev, "%s: muting amp using GPIO22\n", - __func__); - gpiod_set_value_cansleep(mute_gpio, 0); - } -} - -static void snd_rpi_iqaudio_gpio_unmute(struct snd_soc_card *card) -{ - if (mute_gpio) { - dev_info(card->dev, "%s: un-muting amp using GPIO22\n", - __func__); - gpiod_set_value_cansleep(mute_gpio, 1); - } -} - -static int snd_rpi_iqaudio_set_bias_level(struct snd_soc_card *card, - struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) -{ - struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai *codec_dai; - - rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); - codec_dai = rtd->codec_dai; - - if (dapm->dev != codec_dai->dev) - return 0; - - switch (level) { - case SND_SOC_BIAS_PREPARE: - if (dapm->bias_level != SND_SOC_BIAS_STANDBY) - break; - - /* UNMUTE AMP */ - snd_rpi_iqaudio_gpio_unmute(card); - - break; - case SND_SOC_BIAS_STANDBY: - if (dapm->bias_level != SND_SOC_BIAS_PREPARE) - break; - - /* MUTE AMP */ - snd_rpi_iqaudio_gpio_mute(card); - - break; - default: - break; - } - - return 0; -} - -static struct snd_soc_dai_link snd_rpi_iqaudio_dac_dai[] = { -{ - .cpu_dai_name = "bcm2708-i2s.0", - .codec_dai_name = "pcm512x-hifi", - .platform_name = "bcm2708-i2s.0", - .codec_name = "pcm512x.1-004c", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .init = snd_rpi_iqaudio_dac_init, -}, -}; - -/* audio machine driver */ -static struct snd_soc_card snd_rpi_iqaudio_dac = { - .owner = THIS_MODULE, - .dai_link = snd_rpi_iqaudio_dac_dai, - .num_links = ARRAY_SIZE(snd_rpi_iqaudio_dac_dai), -}; - -static int snd_rpi_iqaudio_dac_probe(struct platform_device *pdev) -{ - int ret = 0; - bool gpio_unmute = false; - - snd_rpi_iqaudio_dac.dev = &pdev->dev; - - if (pdev->dev.of_node) { - struct device_node *i2s_node; - struct snd_soc_card *card = &snd_rpi_iqaudio_dac; - struct snd_soc_dai_link *dai = &snd_rpi_iqaudio_dac_dai[0]; - bool auto_gpio_mute = false; - - i2s_node = of_parse_phandle(pdev->dev.of_node, - "i2s-controller", 0); - if (i2s_node) { - dai->cpu_dai_name = NULL; - dai->cpu_of_node = i2s_node; - dai->platform_name = NULL; - dai->platform_of_node = i2s_node; - } - - digital_gain_0db_limit = !of_property_read_bool( - pdev->dev.of_node, "iqaudio,24db_digital_gain"); - - if (of_property_read_string(pdev->dev.of_node, "card_name", - &card->name)) - card->name = "IQaudIODAC"; - - if (of_property_read_string(pdev->dev.of_node, "dai_name", - &dai->name)) - dai->name = "IQaudIO DAC"; - - if (of_property_read_string(pdev->dev.of_node, - "dai_stream_name", &dai->stream_name)) - dai->stream_name = "IQaudIO DAC HiFi"; - - /* gpio_unmute - one time unmute amp using GPIO */ - gpio_unmute = of_property_read_bool(pdev->dev.of_node, - "iqaudio-dac,unmute-amp"); - - /* auto_gpio_mute - mute/unmute amp using GPIO */ - auto_gpio_mute = of_property_read_bool(pdev->dev.of_node, - "iqaudio-dac,auto-mute-amp"); - - if (auto_gpio_mute || gpio_unmute) { - mute_gpio = devm_gpiod_get_optional(&pdev->dev, "mute", - GPIOD_OUT_LOW); - if (IS_ERR(mute_gpio)) { - ret = PTR_ERR(mute_gpio); - dev_err(&pdev->dev, - "Failed to get mute gpio: %d\n", ret); - return ret; - } - - if (auto_gpio_mute && mute_gpio) - snd_rpi_iqaudio_dac.set_bias_level = - snd_rpi_iqaudio_set_bias_level; - } - } - - ret = snd_soc_register_card(&snd_rpi_iqaudio_dac); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "snd_soc_register_card() failed: %d\n", ret); - return ret; - } - - if (gpio_unmute && mute_gpio) - snd_rpi_iqaudio_gpio_unmute(&snd_rpi_iqaudio_dac); - - return 0; -} - -static int snd_rpi_iqaudio_dac_remove(struct platform_device *pdev) -{ - snd_rpi_iqaudio_gpio_mute(&snd_rpi_iqaudio_dac); - - return snd_soc_unregister_card(&snd_rpi_iqaudio_dac); -} - -static const struct of_device_id iqaudio_of_match[] = { - { .compatible = "iqaudio,iqaudio-dac", }, - {}, -}; -MODULE_DEVICE_TABLE(of, iqaudio_of_match); - -static struct platform_driver snd_rpi_iqaudio_dac_driver = { - .driver = { - .name = "snd-rpi-iqaudio-dac", - .owner = THIS_MODULE, - .of_match_table = iqaudio_of_match, - }, - .probe = snd_rpi_iqaudio_dac_probe, - .remove = snd_rpi_iqaudio_dac_remove, -}; - -module_platform_driver(snd_rpi_iqaudio_dac_driver); - -MODULE_AUTHOR("Florian Meier "); -MODULE_DESCRIPTION("ASoC Driver for IQAudio DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/bcm/justboom-dac.c b/sound/soc/bcm/justboom-dac.c deleted file mode 100644 index 9e7bb9f68ee2ef..00000000000000 --- a/sound/soc/bcm/justboom-dac.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * ASoC Driver for JustBoom DAC Raspberry Pi HAT Sound Card - * - * Author: Milan Neskovic - * Copyright 2016 - * based on code by Daniel Matuschek - * based on code by Florian Meier - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include -#include - -#include -#include -#include -#include -#include - -#include "../codecs/pcm512x.h" - -static bool digital_gain_0db_limit = true; - -static int snd_rpi_justboom_dac_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *component = rtd->codec_dai->component; - snd_soc_component_update_bits(component, PCM512x_GPIO_EN, 0x08, 0x08); - snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_4, 0xf, 0x02); - snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08,0x08); - - if (digital_gain_0db_limit) - { - int ret; - struct snd_soc_card *card = rtd->card; - - ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); - if (ret < 0) - dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); - } - - return 0; -} - -static int snd_rpi_justboom_dac_startup(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = rtd->codec_dai->component; - snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08,0x08); - return 0; -} - -static void snd_rpi_justboom_dac_shutdown(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = rtd->codec_dai->component; - snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08,0x00); -} - -/* machine stream operations */ -static struct snd_soc_ops snd_rpi_justboom_dac_ops = { - .startup = snd_rpi_justboom_dac_startup, - .shutdown = snd_rpi_justboom_dac_shutdown, -}; - -static struct snd_soc_dai_link snd_rpi_justboom_dac_dai[] = { -{ - .name = "JustBoom DAC", - .stream_name = "JustBoom DAC HiFi", - .cpu_dai_name = "bcm2708-i2s.0", - .codec_dai_name = "pcm512x-hifi", - .platform_name = "bcm2708-i2s.0", - .codec_name = "pcm512x.1-004d", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .ops = &snd_rpi_justboom_dac_ops, - .init = snd_rpi_justboom_dac_init, -}, -}; - -/* audio machine driver */ -static struct snd_soc_card snd_rpi_justboom_dac = { - .name = "snd_rpi_justboom_dac", - .driver_name = "JustBoomDac", - .owner = THIS_MODULE, - .dai_link = snd_rpi_justboom_dac_dai, - .num_links = ARRAY_SIZE(snd_rpi_justboom_dac_dai), -}; - -static int snd_rpi_justboom_dac_probe(struct platform_device *pdev) -{ - int ret = 0; - - snd_rpi_justboom_dac.dev = &pdev->dev; - - if (pdev->dev.of_node) { - struct device_node *i2s_node; - struct snd_soc_dai_link *dai = &snd_rpi_justboom_dac_dai[0]; - i2s_node = of_parse_phandle(pdev->dev.of_node, - "i2s-controller", 0); - - if (i2s_node) { - dai->cpu_dai_name = NULL; - dai->cpu_of_node = i2s_node; - dai->platform_name = NULL; - dai->platform_of_node = i2s_node; - } - - digital_gain_0db_limit = !of_property_read_bool( - pdev->dev.of_node, "justboom,24db_digital_gain"); - } - - ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_justboom_dac); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "snd_soc_register_card() failed: %d\n", ret); - - return ret; -} - -static const struct of_device_id snd_rpi_justboom_dac_of_match[] = { - { .compatible = "justboom,justboom-dac", }, - {}, -}; -MODULE_DEVICE_TABLE(of, snd_rpi_justboom_dac_of_match); - -static struct platform_driver snd_rpi_justboom_dac_driver = { - .driver = { - .name = "snd-rpi-justboom-dac", - .owner = THIS_MODULE, - .of_match_table = snd_rpi_justboom_dac_of_match, - }, - .probe = snd_rpi_justboom_dac_probe, -}; - -module_platform_driver(snd_rpi_justboom_dac_driver); - -MODULE_AUTHOR("Milan Neskovic "); -MODULE_DESCRIPTION("ASoC Driver for JustBoom PI DAC HAT Sound Card"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/bcm/rpi-pcm512x-soundcard.c b/sound/soc/bcm/rpi-pcm512x-soundcard.c new file mode 100644 index 00000000000000..66289208f36e21 --- /dev/null +++ b/sound/soc/bcm/rpi-pcm512x-soundcard.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * rpi-pcm512x.c -- ALSA SoC Raspberry Pi soundcard. + * + * Copyright (C) 2018 Raspberry Pi. + * + * Authors: Tim Gover + * + * Generic driver for Pi Hat PCM512x DAC sound cards + * + * Based upon code from: + * + * allo-piano-dac.c + * by Baswaraj K + * based on code by Florian Meier + * + * dionaudio_loco-v2.c + * by Miquel Blauw + * + * justboom-dac.c + * by Milan Neskovic + * + * iqaudio-dac.c + * Florian Meier + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "../codecs/pcm512x.h" + +/* Parameters for generic functions */ +struct snd_rpi_pcm512x_drvdata { + /* Required - pointer to the DAI structure */ + struct snd_soc_dai_link *dai; + /* Required - snd_soc_card name */ + const char *card_name; + /* Optional DT node names if card info is defined in DT */ + const char *card_name_dt; + const char *dai_name_dt; + const char *dai_stream_name_dt; + const char *digital_gain_0db_name_dt; + /* Optional probe extension - called prior to register_card */ + int (*probe)(struct platform_device *pdev, struct snd_soc_card *card); +}; + +static bool digital_gain_0db_limit = true; + +static int snd_rpi_pcm512x_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct snd_soc_card *card = rtd->card; + + if (!digital_gain_0db_limit) + return 0; + + ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); + if (ret < 0) + dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); + return 0; +} + +static struct snd_soc_dai_link snd_allo_piano_dac_dai[] = { +{ + .name = "Piano DAC", + .stream_name = "Piano DAC HiFi", +}, +}; + +static struct snd_rpi_pcm512x_drvdata drvdata_allo_piano_dac = { + .card_name = "PianoDAC", + .dai = snd_allo_piano_dac_dai, + .digital_gain_0db_name_dt = "allo,24db_digital_gain", +}; + +static struct snd_soc_dai_link snd_dion_audio_loco_v2_dai[] = { +{ + .name = "DionAudio LOCO-V2", + .stream_name = "DionAudio LOCO-V2 DAC-AMP", + .codec_name = "pcm512x.1-004d", +}, +}; + +static struct snd_rpi_pcm512x_drvdata drvdata_dionaudio_loco_v2 = { + .card_name = "Dion Audio LOCO-V2", + .dai = snd_dion_audio_loco_v2_dai, + .digital_gain_0db_name_dt = "dionaudio,24db_digital_gain", +}; + +static struct gpio_desc *mute_gpio; +static void snd_rpi_iqaudio_gpio_mute(struct snd_soc_card *card) +{ + if (mute_gpio) { + dev_info(card->dev, "%s: muting amp using GPIO22\n", + __func__); + gpiod_set_value_cansleep(mute_gpio, 0); + } +} + +static void snd_rpi_iqaudio_gpio_unmute(struct snd_soc_card *card) +{ + if (mute_gpio) { + dev_info(card->dev, "%s: un-muting amp using GPIO22\n", + __func__); + gpiod_set_value_cansleep(mute_gpio, 1); + } +} + +static int snd_rpi_iqaudio_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) +{ + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; + + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + codec_dai = rtd->codec_dai; + + if (dapm->dev != codec_dai->dev) + return 0; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level != SND_SOC_BIAS_STANDBY) + break; + + /* UNMUTE AMP */ + snd_rpi_iqaudio_gpio_unmute(card); + + break; + case SND_SOC_BIAS_STANDBY: + if (dapm->bias_level != SND_SOC_BIAS_PREPARE) + break; + + /* MUTE AMP */ + snd_rpi_iqaudio_gpio_mute(card); + + break; + default: + break; + } + + return 0; +} + +static int snd_rpi_iqaudio_dac_probe(struct platform_device *pdev, + struct snd_soc_card *card) +{ + bool gpio_unmute; + bool auto_gpio_mute; + + /* gpio_unmute - one time unmute amp using GPIO */ + gpio_unmute = of_property_read_bool(pdev->dev.of_node, + "iqaudio-dac,unmute-amp"); + + /* auto_gpio_mute - mute/unmute amp using GPIO */ + auto_gpio_mute = of_property_read_bool(pdev->dev.of_node, + "iqaudio-dac,auto-mute-amp"); + + if (auto_gpio_mute || gpio_unmute) { + mute_gpio = devm_gpiod_get_optional(&pdev->dev, "mute", + GPIOD_OUT_LOW); + if (IS_ERR(mute_gpio)) { + int ret = PTR_ERR(mute_gpio); + + dev_err(&pdev->dev, + "Failed to get mute gpio: %d\n", ret); + return ret; + } + + if (auto_gpio_mute && mute_gpio) + card->set_bias_level = snd_rpi_iqaudio_set_bias_level; + } + return 0; +} + +static struct snd_soc_dai_link snd_iqaudio_dac_dai[] = { +{ + .name = "IQaudIO DAC", + .stream_name = "IQaudIO DAC HiFi", +}, +}; + +static struct snd_rpi_pcm512x_drvdata drvdata_iqaudio_dac = { + .card_name = "IQaudIO DAC", + .dai = snd_iqaudio_dac_dai, + .digital_gain_0db_name_dt = "iqaudio,24db_digital_gain", + .card_name_dt = "card_name", + .dai_name_dt = "dai_name", + .dai_stream_name_dt = "dai_stream_name", + .probe = snd_rpi_iqaudio_dac_probe, +}; + +static int snd_rpi_justboom_dac_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *comp = rtd->codec_dai->component; + + snd_soc_component_update_bits(comp, PCM512x_GPIO_EN, 0x08, 0x08); + snd_soc_component_update_bits(comp, PCM512x_GPIO_OUTPUT_4, 0xf, 0x02); + snd_soc_component_update_bits(comp, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + + return snd_rpi_pcm512x_init(rtd); +} + +static int snd_rpi_justboom_dac_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *comp = rtd->codec_dai->component; + + snd_soc_component_update_bits(comp, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + return 0; +} + +static void snd_rpi_justboom_dac_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *comp = rtd->codec_dai->component; + + snd_soc_component_update_bits(comp, PCM512x_GPIO_CONTROL_1, 0x08, 0x00); +}; + +static struct snd_soc_ops snd_rpi_justboom_dac_ops = { + .startup = snd_rpi_justboom_dac_startup, + .shutdown = snd_rpi_justboom_dac_shutdown, +}; + +static struct snd_soc_dai_link snd_justboom_dac_dai[] = { +{ + .name = "JustBoom DAC", + .stream_name = "JustBoom DAC HiFi", + .codec_name = "pcm512x.1-004d", + .ops = &snd_rpi_justboom_dac_ops, + .init = snd_rpi_justboom_dac_init, +}, +}; + +static struct snd_rpi_pcm512x_drvdata drvdata_justboom_dac = { + .card_name = "snd_rpi_justboom_dac", + .dai = snd_justboom_dac_dai, + .digital_gain_0db_name_dt = "justboom,24db_digital_gain", +}; + +static const struct of_device_id snd_rpi_pcm512x_of_match[] = { + { .compatible = "allo,allo-piano-dac", + .data = (void *) &drvdata_allo_piano_dac }, + { .compatible = "dionaudio,dionaudio-loco-v2", + .data = (void *) &drvdata_dionaudio_loco_v2 }, + { .compatible = "iqaudio,iqaudio-dac", + .data = (void *) &drvdata_iqaudio_dac }, + { .compatible = "justboom,justboom-dac", + .data = (void *) &drvdata_justboom_dac }, + {}, +}; + +static struct snd_soc_card snd_rpi_pcm512x = { + .driver_name = "RPi-PCM512x", + .owner = THIS_MODULE, + .dai_link = NULL, + .num_links = 1, +}; + +static int snd_rpi_pcm512x_probe(struct platform_device *pdev) +{ + int ret = 0; + const struct of_device_id *of_id; + + snd_rpi_pcm512x.dev = &pdev->dev; + of_id = of_match_node(snd_rpi_pcm512x_of_match, pdev->dev.of_node); + + if (pdev->dev.of_node && of_id->data) { + struct device_node *i2s_node; + struct snd_rpi_pcm512x_drvdata *drvdata = + (struct snd_rpi_pcm512x_drvdata *) of_id->data; + struct snd_soc_dai_link *dai = drvdata->dai; + + snd_soc_card_set_drvdata(&snd_rpi_pcm512x, drvdata); + + if (!dai->init) + dai->init = snd_rpi_pcm512x_init; + if (!dai->codec_dai_name) + dai->codec_dai_name = "pcm512x-hifi"; + if (!dai->codec_name) + dai->codec_name = "pcm512x.1-004c"; + if (!dai->dai_fmt) + dai->dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + + snd_rpi_pcm512x.dai_link = dai; + i2s_node = of_parse_phandle(pdev->dev.of_node, + "i2s-controller", 0); + if (!i2s_node) { + pr_err("Failed to find i2s-controller DT node\n"); + return -ENODEV; + } + + if (drvdata->digital_gain_0db_name_dt) + digital_gain_0db_limit = !of_property_read_bool( + pdev->dev.of_node, + drvdata->digital_gain_0db_name_dt); + + snd_rpi_pcm512x.name = drvdata->card_name; + + /* If requested by in drvdata get card & DAI names from DT */ + if (drvdata->card_name_dt) + of_property_read_string(i2s_node, + drvdata->card_name_dt, + &snd_rpi_pcm512x.name); + + if (drvdata->dai_name_dt) + of_property_read_string(i2s_node, + drvdata->dai_name_dt, + &dai->name); + + if (drvdata->dai_stream_name_dt) + of_property_read_string(i2s_node, + drvdata->dai_stream_name_dt, + &dai->stream_name); + + dai->cpu_of_node = i2s_node; + dai->platform_of_node = i2s_node; + + if (drvdata->probe) { + ret = drvdata->probe(pdev, &snd_rpi_pcm512x); + if (ret < 0) { + dev_err(&pdev->dev, "Custom probe failed %d\n", + ret); + return ret; + } + } + + pr_debug("%s card: %s dai: %s stream: %s\n", __func__, + snd_rpi_pcm512x.name, + dai->name, dai->stream_name); + } + + ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_pcm512x); + if (ret && ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to register card %d\n", ret); + + return ret; +} + +static struct platform_driver snd_rpi_pcm512x_driver = { + .driver = { + .name = "snd-rpi-pcm512x", + .owner = THIS_MODULE, + .of_match_table = snd_rpi_pcm512x_of_match, + }, + .probe = snd_rpi_pcm512x_probe, +}; +MODULE_DEVICE_TABLE(of, snd_rpi_pcm512x_of_match); + +module_platform_driver(snd_rpi_pcm512x_driver); + +MODULE_AUTHOR("Tim Gover "); +MODULE_DESCRIPTION("ASoC Raspberry Pi Hat generic DAC driver for PCM512x based cards"); +MODULE_LICENSE("GPL v2");