diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile index c890e5ddb0e2ee..0f7340799fb465 100644 --- a/arch/arm/boot/dts/overlays/Makefile +++ b/arch/arm/boot/dts/overlays/Makefile @@ -9,6 +9,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ akkordion-iqdacplus.dtbo \ allo-piano-dac-pcm512x-audio.dtbo \ at86rf233.dtbo \ + audioinjector-addons.dtbo \ audioinjector-wm8731-audio.dtbo \ audremap.dtbo \ bmp085_i2c-sensor.dtbo \ diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index 970c9c9b5c7446..c1883bba938290 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -295,6 +295,12 @@ Params: interrupt GPIO used for INT (default 23) arrays (0=+0pF, 15=+4.5pF, default 15) +Name: audioinjector-addons +Info: Configures the audioinjector.net audio add on soundcards +Load: dtoverlay=audioinjector-addons +Params: + + Name: audioinjector-wm8731-audio Info: Configures the audioinjector.net audio add on soundcard Load: dtoverlay=audioinjector-wm8731-audio diff --git a/arch/arm/boot/dts/overlays/audioinjector-addons-overlay.dts b/arch/arm/boot/dts/overlays/audioinjector-addons-overlay.dts new file mode 100644 index 00000000000000..dbf2f3cacc2e6b --- /dev/null +++ b/arch/arm/boot/dts/overlays/audioinjector-addons-overlay.dts @@ -0,0 +1,50 @@ +// Definitions for audioinjector.net audio add on soundcard +/dts-v1/; +/plugin/; + +/ { + compatible = "brcm,bcm2708"; + + fragment@0 { + target = <&i2s>; + __overlay__ { + status = "okay"; + }; + }; + + fragment@1 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + cs42448: cs42448@48 { + #sound-dai-cells = <0>; + compatible = "cirrus,cs42448"; + reg = <0x48>; + clocks = <&cs42448_mclk>; + clock-names = "mclk"; + status = "okay"; + }; + + cs42448_mclk: codec-mclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <49152000>; + }; + }; + }; + + fragment@2 { + target = <&sound>; + __overlay__ { + compatible = "ai,audioinjector-octo-soundcard"; + mult-gpios = <&gpio 27 0>, <&gpio 22 0>, <&gpio 23 0>, + <&gpio 24 0>; + i2s-controller = <&i2s>; + codec = <&cs42448>; + status = "okay"; + }; + }; +}; diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig index ac50cc68f3a32c..802ba0f149c571 100644 --- a/arch/arm/configs/bcm2709_defconfig +++ b/arch/arm/configs/bcm2709_defconfig @@ -888,6 +888,7 @@ CONFIG_SND_BCM2708_SOC_IQAUDIO_DIGI=m CONFIG_SND_BCM2708_SOC_RASPIDAC3=m CONFIG_SND_BCM2708_SOC_ADAU1977_ADC=m CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD=m +CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD=m CONFIG_SND_DIGIDAC1_SOUNDCARD=m CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO=m CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO_V2=m @@ -897,6 +898,7 @@ CONFIG_SND_PISOUND=m CONFIG_SND_SOC_ADAU1701=m CONFIG_SND_SOC_ADAU7002=m CONFIG_SND_SOC_AK4554=m +CONFIG_SND_SOC_CS42XX8_I2C=m CONFIG_SND_SOC_WM8804_I2C=m CONFIG_SND_SIMPLE_CARD=m CONFIG_SOUND_PRIME=m diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig index 1748fcb84bdc38..bf52db66afb2aa 100644 --- a/arch/arm/configs/bcmrpi_defconfig +++ b/arch/arm/configs/bcmrpi_defconfig @@ -881,6 +881,7 @@ CONFIG_SND_BCM2708_SOC_IQAUDIO_DIGI=m CONFIG_SND_BCM2708_SOC_RASPIDAC3=m CONFIG_SND_BCM2708_SOC_ADAU1977_ADC=m CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD=m +CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD=m CONFIG_SND_DIGIDAC1_SOUNDCARD=m CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO=m CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO_V2=m @@ -890,6 +891,7 @@ CONFIG_SND_PISOUND=m CONFIG_SND_SOC_ADAU1701=m CONFIG_SND_SOC_ADAU7002=m CONFIG_SND_SOC_AK4554=m +CONFIG_SND_SOC_CS42XX8_I2C=m CONFIG_SND_SOC_WM8804_I2C=m CONFIG_SND_SIMPLE_CARD=m CONFIG_SOUND_PRIME=m diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig index e626da6007a232..58d03b97f53363 100644 --- a/sound/soc/bcm/Kconfig +++ b/sound/soc/bcm/Kconfig @@ -118,6 +118,13 @@ config SND_AUDIOINJECTOR_PI_SOUNDCARD help Say Y or M if you want to add support for audioinjector.net Pi Hat +config SND_AUDIOINJECTOR_OCTO_SOUNDCARD + tristate "Support for audioinjector.net Octo channel (Hat) soundcard" + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S + select SND_SOC_CS42XX8_I2C + help + Say Y or M if you want to add support for audioinjector.net octo add on + config SND_DIGIDAC1_SOUNDCARD tristate "Support for Red Rocks Audio DigiDAC1" depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile index 8d2d2073dc2ced..d448aa517bf994 100644 --- a/sound/soc/bcm/Makefile +++ b/sound/soc/bcm/Makefile @@ -23,6 +23,7 @@ snd-soc-iqaudio-dac-objs := iqaudio-dac.o snd-soc-iqaudio-digi-objs := iqaudio_digi.o snd-soc-raspidac3-objs := raspidac3.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 @@ -44,6 +45,7 @@ obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DIGI) += snd-soc-iqaudio-digi.o obj-$(CONFIG_SND_BCM2708_SOC_RASPIDAC3) += snd-soc-raspidac3.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 diff --git a/sound/soc/bcm/audioinjector-octo-soundcard.c b/sound/soc/bcm/audioinjector-octo-soundcard.c new file mode 100644 index 00000000000000..9effea72579864 --- /dev/null +++ b/sound/soc/bcm/audioinjector-octo-soundcard.c @@ -0,0 +1,286 @@ +/* + * ASoC Driver for AudioInjector Pi octo channel soundcard (hat) + * + * Created on: 27-October-2016 + * Author: flatmax@flatmax.org + * based on audioinjector-pi-soundcard.c + * + * Copyright (C) 2016 Flatmax Pty. Ltd. + * + * 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 struct gpio_descs *mult_gpios; +static unsigned int audioinjector_octo_rate; + +static int audioinjector_octo_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + return snd_soc_dai_set_bclk_ratio(rtd->cpu_dai, 64); +} + +static int audioinjector_octo_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + rtd->cpu_dai->driver->playback.channels_min = 8; + rtd->cpu_dai->driver->playback.channels_max = 8; + rtd->cpu_dai->driver->capture.channels_min = 8; + rtd->cpu_dai->driver->capture.channels_max = 8; + rtd->codec_dai->driver->capture.channels_max = 8; + return 0; +} + +static void audioinjector_octo_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + rtd->cpu_dai->driver->playback.channels_min = 2; + rtd->cpu_dai->driver->playback.channels_max = 2; + rtd->cpu_dai->driver->capture.channels_min = 2; + rtd->cpu_dai->driver->capture.channels_max = 2; + rtd->codec_dai->driver->capture.channels_max = 6; +} + +static int audioinjector_octo_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + // set codec DAI configuration + int ret = snd_soc_dai_set_fmt(rtd->codec_dai, + SND_SOC_DAIFMT_CBS_CFS|SND_SOC_DAIFMT_DSP_A| + SND_SOC_DAIFMT_NB_NF); + if (ret < 0) + return ret; + + // set cpu DAI configuration + ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + SND_SOC_DAIFMT_CBM_CFM|SND_SOC_DAIFMT_I2S| + SND_SOC_DAIFMT_NB_NF); + if (ret < 0) + return ret; + + audioinjector_octo_rate = params_rate(params); + + return 0; +} + +static int audioinjector_octo_trigger(struct snd_pcm_substream *substream, + int cmd){ + int mult[4]; + mult[0] = 0; + mult[1] = 0; + mult[2] = 0; + mult[3] = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + switch (audioinjector_octo_rate) { + case 192000: + mult[3] = 1; + case 176640: + mult[0] = 1; + mult[1] = 1; + mult[2] = 1; + break; + case 96000: + mult[3] = 1; + case 88200: + mult[1] = 1; + mult[2] = 1; + break; + case 64000: + mult[3] = 1; + case 58800: + mult[0] = 1; + mult[2] = 1; + break; + case 48000: + mult[3] = 1; + case 44100: + mult[2] = 1; + break; + case 32000: + mult[3] = 1; + case 29400: + mult[0] = 1; + mult[1] = 1; + break; + case 24000: + mult[3] = 1; + case 22050: + mult[1] = 1; + break; + case 16000: + mult[3] = 1; + case 14700: + mult[0] = 1; + break; + case 8000: + mult[3] = 1; + break; + default: + return -EINVAL; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + default: + return -EINVAL; + } + gpiod_set_array_value_cansleep(mult_gpios->ndescs, mult_gpios->desc, + mult); + + return 0; +} + +static struct snd_soc_ops audioinjector_octo_ops = { + .startup = audioinjector_octo_startup, + .shutdown = audioinjector_octo_shutdown, + .hw_params = audioinjector_octo_hw_params, + .trigger = audioinjector_octo_trigger, +}; + +static struct snd_soc_dai_link audioinjector_octo_dai[] = { + { + .name = "AudioInjector Octo", + .stream_name = "AudioInject-HIFI", + .codec_dai_name = "cs42448", + .ops = &audioinjector_octo_ops, + .init = audioinjector_octo_dai_init, + }, +}; + +static const struct snd_soc_dapm_widget audioinjector_octo_widgets[] = { + SND_SOC_DAPM_OUTPUT("OUTPUTS0"), + SND_SOC_DAPM_OUTPUT("OUTPUTS1"), + SND_SOC_DAPM_OUTPUT("OUTPUTS2"), + SND_SOC_DAPM_OUTPUT("OUTPUTS3"), + SND_SOC_DAPM_INPUT("INPUTS0"), + SND_SOC_DAPM_INPUT("INPUTS1"), + SND_SOC_DAPM_INPUT("INPUTS2"), +}; + +static const struct snd_soc_dapm_route audioinjector_octo_route[] = { + /* Balanced outputs */ + {"OUTPUTS0", NULL, "AOUT1L"}, + {"OUTPUTS0", NULL, "AOUT1R"}, + {"OUTPUTS1", NULL, "AOUT2L"}, + {"OUTPUTS1", NULL, "AOUT2R"}, + {"OUTPUTS2", NULL, "AOUT3L"}, + {"OUTPUTS2", NULL, "AOUT3R"}, + {"OUTPUTS3", NULL, "AOUT4L"}, + {"OUTPUTS3", NULL, "AOUT4R"}, + + /* Balanced inputs */ + {"AIN1L", NULL, "INPUTS0"}, + {"AIN1R", NULL, "INPUTS0"}, + {"AIN2L", NULL, "INPUTS1"}, + {"AIN2R", NULL, "INPUTS1"}, + {"AIN3L", NULL, "INPUTS2"}, + {"AIN3R", NULL, "INPUTS2"}, +}; + +static struct snd_soc_card snd_soc_audioinjector_octo = { + .name = "audioinjector-octo-soundcard", + .dai_link = audioinjector_octo_dai, + .num_links = ARRAY_SIZE(audioinjector_octo_dai), + + .dapm_widgets = audioinjector_octo_widgets, + .num_dapm_widgets = ARRAY_SIZE(audioinjector_octo_widgets), + .dapm_routes = audioinjector_octo_route, + .num_dapm_routes = ARRAY_SIZE(audioinjector_octo_route), +}; + +static int audioinjector_octo_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_audioinjector_octo; + int ret; + + card->dev = &pdev->dev; + + if (pdev->dev.of_node) { + struct snd_soc_dai_link *dai = &audioinjector_octo_dai[0]; + struct device_node *i2s_node = + of_parse_phandle(pdev->dev.of_node, + "i2s-controller", 0); + struct device_node *codec_node = + of_parse_phandle(pdev->dev.of_node, + "codec", 0); + + mult_gpios = devm_gpiod_get_array_optional(&pdev->dev, "mult", + GPIOD_OUT_LOW); + if (IS_ERR(mult_gpios)) + return PTR_ERR(mult_gpios); + + if (i2s_node && codec_node) { + dai->cpu_dai_name = NULL; + dai->cpu_of_node = i2s_node; + dai->platform_name = NULL; + dai->platform_of_node = i2s_node; + dai->codec_name = NULL; + dai->codec_of_node = codec_node; + } else + if (!dai->cpu_of_node) { + dev_err(&pdev->dev, + "i2s-controller missing or invalid in DT\n"); + return -EINVAL; + } else { + dev_err(&pdev->dev, + "Property 'codec' missing or invalid\n"); + return -EINVAL; + } + } + + ret = snd_soc_register_card(card); + if (ret != 0) + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + return ret; +} + +static int audioinjector_octo_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + return snd_soc_unregister_card(card); +} + +static const struct of_device_id audioinjector_octo_of_match[] = { + { .compatible = "ai,audioinjector-octo-soundcard", }, + {}, +}; +MODULE_DEVICE_TABLE(of, audioinjector_octo_of_match); + +static struct platform_driver audioinjector_octo_driver = { + .driver = { + .name = "audioinjector-audio", + .owner = THIS_MODULE, + .of_match_table = audioinjector_octo_of_match, + }, + .probe = audioinjector_octo_probe, + .remove = audioinjector_octo_remove, +}; + +module_platform_driver(audioinjector_octo_driver); +MODULE_AUTHOR("Matt Flax "); +MODULE_DESCRIPTION("AudioInjector.net octo Soundcard"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:audioinjector-octo-soundcard"); diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 6ba20498202ed3..c8dd065aea8414 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -239,6 +239,7 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, unsigned int ch1pos, ch2pos, mode, format; uint32_t csreg; + /* * If a stream is already enabled, * the registers are already set properly. @@ -312,6 +313,7 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, switch (params_channels(params)) { case 2: + case 8: format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format); format |= BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(ch1pos)); format |= BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(ch2pos));