[alsa-devel] [PATCH 0/8] add UniPhier audio system support
This series adds support for Socionext audio system for UniPhier LD11/LD20 SoCs. This driver supports I2S output for Line-In, Line-Out and S/PDIF output.
UniPhier AIO DAI driver provides sound devices such as I2S, S/PDIF. Since the AIO has mixed register map for those I/Os, it is hard to split register areas for each sound devices.
UniPhier EVEA is a codec core for Line-In, Line-Out. Since this core is only in internal of UniPhier series SoC, this driver works independently but only UniPhier SoC can use it.
Katsuhiro Suzuki (8): ASoC: spdif: Add S32_LE support for S/PDIF dummy codec drivers ASoC: uniphier: add DT bindings documentation for UniPhier EVEA ASoC: uniphier: add DT bindings documentation for UniPhier AIO ASoC: uniphier: add support for UniPhier EVEA codec ASoC: uniphier: add support for UniPhier AIO driver ASoC: uniphier: add support for UniPhier LD11/LD20 AIO driver MAINTAINERS: add entries for UniPhier ASoC sound drivers arm64: dts: uniphier: add sound node for UniPhier
.../devicetree/bindings/sound/uniphier,aio.txt | 36 ++ .../devicetree/bindings/sound/uniphier,evea.txt | 26 + MAINTAINERS | 6 + .../boot/dts/socionext/uniphier-ld11-global.dts | 72 +++ arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi | 31 + .../boot/dts/socionext/uniphier-ld20-global.dts | 72 +++ arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi | 35 ++ sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/codecs/spdif_receiver.c | 5 +- sound/soc/codecs/spdif_transmitter.c | 5 +- sound/soc/uniphier/Kconfig | 34 + sound/soc/uniphier/Makefile | 8 + sound/soc/uniphier/aio-core.c | 368 +++++++++++ sound/soc/uniphier/aio-dma.c | 266 ++++++++ sound/soc/uniphier/aio-ld11.c | 343 ++++++++++ sound/soc/uniphier/aio-regctrl.c | 699 +++++++++++++++++++++ sound/soc/uniphier/aio-regctrl.h | 495 +++++++++++++++ sound/soc/uniphier/aio.h | 261 ++++++++ sound/soc/uniphier/evea.c | 567 +++++++++++++++++ 20 files changed, 3327 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/uniphier,aio.txt create mode 100644 Documentation/devicetree/bindings/sound/uniphier,evea.txt create mode 100644 sound/soc/uniphier/Kconfig create mode 100644 sound/soc/uniphier/Makefile create mode 100644 sound/soc/uniphier/aio-core.c create mode 100644 sound/soc/uniphier/aio-dma.c create mode 100644 sound/soc/uniphier/aio-ld11.c create mode 100644 sound/soc/uniphier/aio-regctrl.c create mode 100644 sound/soc/uniphier/aio-regctrl.h create mode 100644 sound/soc/uniphier/aio.h create mode 100644 sound/soc/uniphier/evea.c
AIO on UniPhier can output S/PDIF where no codec is needed. This patch adds S32_LE support for dummy codec drivers.
If one S/PDIF controller has its own limitation, its CPU DAI driver should set the supported format by its own circumstance, since the soc-pcm driver will use the intersection of cpu_dai's formats and codec_dai's formats.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com --- sound/soc/codecs/spdif_receiver.c | 5 +++-- sound/soc/codecs/spdif_transmitter.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c index 7acd05140a81..c8fd6367f6c0 100644 --- a/sound/soc/codecs/spdif_receiver.c +++ b/sound/soc/codecs/spdif_receiver.c @@ -34,10 +34,11 @@ static const struct snd_soc_dapm_route dir_routes[] = { #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
-static const struct snd_soc_codec_driver soc_codec_spdif_dir = { +static struct snd_soc_codec_driver soc_codec_spdif_dir = { .component_driver = { .dapm_widgets = dir_widgets, .num_dapm_widgets = ARRAY_SIZE(dir_widgets), diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c index 063a64ff82d3..037aa1d45559 100644 --- a/sound/soc/codecs/spdif_transmitter.c +++ b/sound/soc/codecs/spdif_transmitter.c @@ -27,7 +27,8 @@ #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE) + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dapm_widget dit_widgets[] = { SND_SOC_DAPM_OUTPUT("spdif-out"), @@ -37,7 +38,7 @@ static const struct snd_soc_dapm_route dit_routes[] = { { "spdif-out", NULL, "Playback" }, };
-static const struct snd_soc_codec_driver soc_codec_spdif_dit = { +static struct snd_soc_codec_driver soc_codec_spdif_dit = { .component_driver = { .dapm_widgets = dit_widgets, .num_dapm_widgets = ARRAY_SIZE(dit_widgets),
The patch
ASoC: spdif: Add S32_LE support for S/PDIF dummy codec drivers
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From eb733366f5f7f416a7d9215a40e00d57aa193361 Mon Sep 17 00:00:00 2001
From: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com Date: Wed, 22 Nov 2017 20:43:14 +0900 Subject: [PATCH] ASoC: spdif: Add S32_LE support for S/PDIF dummy codec drivers
AIO on UniPhier can output S/PDIF where no codec is needed. This patch adds S32_LE support for dummy codec drivers.
If one S/PDIF controller has its own limitation, its CPU DAI driver should set the supported format by its own circumstance, since the soc-pcm driver will use the intersection of cpu_dai's formats and codec_dai's formats.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/codecs/spdif_receiver.c | 5 +++-- sound/soc/codecs/spdif_transmitter.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c index 7acd05140a81..c8fd6367f6c0 100644 --- a/sound/soc/codecs/spdif_receiver.c +++ b/sound/soc/codecs/spdif_receiver.c @@ -34,10 +34,11 @@ static const struct snd_soc_dapm_route dir_routes[] = { #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
-static const struct snd_soc_codec_driver soc_codec_spdif_dir = { +static struct snd_soc_codec_driver soc_codec_spdif_dir = { .component_driver = { .dapm_widgets = dir_widgets, .num_dapm_widgets = ARRAY_SIZE(dir_widgets), diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c index 063a64ff82d3..037aa1d45559 100644 --- a/sound/soc/codecs/spdif_transmitter.c +++ b/sound/soc/codecs/spdif_transmitter.c @@ -27,7 +27,8 @@ #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE) + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dapm_widget dit_widgets[] = { SND_SOC_DAPM_OUTPUT("spdif-out"), @@ -37,7 +38,7 @@ static const struct snd_soc_dapm_route dit_routes[] = { { "spdif-out", NULL, "Playback" }, };
-static const struct snd_soc_codec_driver soc_codec_spdif_dit = { +static struct snd_soc_codec_driver soc_codec_spdif_dit = { .component_driver = { .dapm_widgets = dit_widgets, .num_dapm_widgets = ARRAY_SIZE(dit_widgets),
This patch adds DT binding documentation for UniPhier EVEA that is SoC inner sound codec of UniPhier series.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com --- .../devicetree/bindings/sound/uniphier,evea.txt | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/uniphier,evea.txt
diff --git a/Documentation/devicetree/bindings/sound/uniphier,evea.txt b/Documentation/devicetree/bindings/sound/uniphier,evea.txt new file mode 100644 index 000000000000..3f31b235f18b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/uniphier,evea.txt @@ -0,0 +1,26 @@ +Socionext EVEA - UniPhier SoC internal codec driver + +Required properties: +- compatible : should be "socionext,uniphier-evea". +- reg : offset and length of the register set for the device. +- clock-names : should include following entries: + "evea", "exiv" +- clocks : a list of phandle, should contain an entry for each + entries in clock-names. +- reset-names : should include following entries: + "evea", "exiv", "adamv" +- resets : a list of phandle, should contain reset entries of + reset-names. +- #sound-dai-cells: should be 1. + +Example: + + codec { + compatible = "socionext,uniphier-evea"; + reg = <0x57900000 0x1000>; + clock-names = "evea", "exiv"; + clocks = <&sys_clk 41>, <&sys_clk 42>; + reset-names = "evea", "exiv", "adamv"; + resets = <&sys_rst 41>, <&sys_rst 42>, <&adamv_rst 0>; + #sound-dai-cells = <1>; + };
On Wed, Nov 22, 2017 at 08:43:15PM +0900, Katsuhiro Suzuki wrote:
This patch adds DT binding documentation for UniPhier EVEA that is SoC inner sound codec of UniPhier series.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com
.../devicetree/bindings/sound/uniphier,evea.txt | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/uniphier,evea.txt
Acked-by: Rob Herring robh@kernel.org
The patch
ASoC: uniphier: add DT bindings documentation for UniPhier EVEA
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From e85c8d3e25c09fd9b21ba74e14078ab4c1d977ef Mon Sep 17 00:00:00 2001
From: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com Date: Wed, 22 Nov 2017 20:43:15 +0900 Subject: [PATCH] ASoC: uniphier: add DT bindings documentation for UniPhier EVEA
This patch adds DT binding documentation for UniPhier EVEA that is SoC inner sound codec of UniPhier series.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com Acked-by: Rob Herring robh@kernel.org Signed-off-by: Mark Brown broonie@kernel.org --- .../devicetree/bindings/sound/uniphier,evea.txt | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/uniphier,evea.txt
diff --git a/Documentation/devicetree/bindings/sound/uniphier,evea.txt b/Documentation/devicetree/bindings/sound/uniphier,evea.txt new file mode 100644 index 000000000000..3f31b235f18b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/uniphier,evea.txt @@ -0,0 +1,26 @@ +Socionext EVEA - UniPhier SoC internal codec driver + +Required properties: +- compatible : should be "socionext,uniphier-evea". +- reg : offset and length of the register set for the device. +- clock-names : should include following entries: + "evea", "exiv" +- clocks : a list of phandle, should contain an entry for each + entries in clock-names. +- reset-names : should include following entries: + "evea", "exiv", "adamv" +- resets : a list of phandle, should contain reset entries of + reset-names. +- #sound-dai-cells: should be 1. + +Example: + + codec { + compatible = "socionext,uniphier-evea"; + reg = <0x57900000 0x1000>; + clock-names = "evea", "exiv"; + clocks = <&sys_clk 41>, <&sys_clk 42>; + reset-names = "evea", "exiv", "adamv"; + resets = <&sys_rst 41>, <&sys_rst 42>, <&adamv_rst 0>; + #sound-dai-cells = <1>; + };
This patch adds DT binding documentation for UniPhier AIO audio subsystem.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com --- .../devicetree/bindings/sound/uniphier,aio.txt | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/uniphier,aio.txt
diff --git a/Documentation/devicetree/bindings/sound/uniphier,aio.txt b/Documentation/devicetree/bindings/sound/uniphier,aio.txt new file mode 100644 index 000000000000..73f6c27ae4f7 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/uniphier,aio.txt @@ -0,0 +1,36 @@ +Socionext UniPhier SoC audio driver + +The Socionext UniPhier audio subsystem consists of I2S and S/PDIF blocks in +the same register space. + +Required properties: +- compatible : should be one of the following: + "socionext,uniphier-ld11-aio" + "socionext,uniphier-ld20-aio" +- reg : offset and length of the register set for the device. +- interrupts : should contain I2S or S/PDIF interrupt. +- pinctrl-names : should be "default". +- pinctrl-0 : defined I2S signal pins for an external codec chip. +- clock-names : should include following entries: + "aio" +- clocks : a list of phandle, should contain an entry for each + entry in clock-names. +- reset-names : should include following entries: + "aio" +- resets : a list of phandle, should contain an entry for each + entry in reset-names. +- #sound-dai-cells: should be 1. + +Example: + audio { + compatible = "socionext,uniphier-ld20-aio"; + reg = <0x56000000 0x80000>; + interrupts = <0 144 4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_aout>; + clock-names = "aio"; + clocks = <&sys_clk 40>; + reset-names = "aio"; + resets = <&sys_rst 40>; + #sound-dai-cells = <1>; + };
On Wed, Nov 22, 2017 at 08:43:16PM +0900, Katsuhiro Suzuki wrote:
This patch adds DT binding documentation for UniPhier AIO audio subsystem.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com
.../devicetree/bindings/sound/uniphier,aio.txt | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/uniphier,aio.txt
Acked-by: Rob Herring robh@kernel.org
The patch
ASoC: uniphier: add DT bindings documentation for UniPhier AIO
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 28fb53de064b24eed55540ddeccab517e4babca3 Mon Sep 17 00:00:00 2001
From: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com Date: Fri, 19 Jan 2018 18:25:28 +0900 Subject: [PATCH] ASoC: uniphier: add DT bindings documentation for UniPhier AIO
This patch adds DT binding documentation for UniPhier AIO audio subsystem.
Acked-by: Rob Herring robh@kernel.org Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com Signed-off-by: Mark Brown broonie@kernel.org --- .../devicetree/bindings/sound/uniphier,aio.txt | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/uniphier,aio.txt
diff --git a/Documentation/devicetree/bindings/sound/uniphier,aio.txt b/Documentation/devicetree/bindings/sound/uniphier,aio.txt new file mode 100644 index 000000000000..73f6c27ae4f7 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/uniphier,aio.txt @@ -0,0 +1,36 @@ +Socionext UniPhier SoC audio driver + +The Socionext UniPhier audio subsystem consists of I2S and S/PDIF blocks in +the same register space. + +Required properties: +- compatible : should be one of the following: + "socionext,uniphier-ld11-aio" + "socionext,uniphier-ld20-aio" +- reg : offset and length of the register set for the device. +- interrupts : should contain I2S or S/PDIF interrupt. +- pinctrl-names : should be "default". +- pinctrl-0 : defined I2S signal pins for an external codec chip. +- clock-names : should include following entries: + "aio" +- clocks : a list of phandle, should contain an entry for each + entry in clock-names. +- reset-names : should include following entries: + "aio" +- resets : a list of phandle, should contain an entry for each + entry in reset-names. +- #sound-dai-cells: should be 1. + +Example: + audio { + compatible = "socionext,uniphier-ld20-aio"; + reg = <0x56000000 0x80000>; + interrupts = <0 144 4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_aout>; + clock-names = "aio"; + clocks = <&sys_clk 40>; + reset-names = "aio"; + resets = <&sys_rst 40>; + #sound-dai-cells = <1>; + };
This patch adds EVEA codec driver. This codec core is in inside of UniPhier SoC.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/uniphier/Kconfig | 19 ++ sound/soc/uniphier/Makefile | 3 + sound/soc/uniphier/evea.c | 567 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 591 insertions(+) create mode 100644 sound/soc/uniphier/Kconfig create mode 100644 sound/soc/uniphier/Makefile create mode 100644 sound/soc/uniphier/evea.c
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index d22758165496..84c3582f3982 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -71,6 +71,7 @@ source "sound/soc/stm/Kconfig" source "sound/soc/sunxi/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" +source "sound/soc/uniphier/Kconfig" source "sound/soc/ux500/Kconfig" source "sound/soc/xtensa/Kconfig" source "sound/soc/zte/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 5327f4d6c668..74cd1858d38b 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_SND_SOC) += stm/ obj-$(CONFIG_SND_SOC) += sunxi/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ +obj-$(CONFIG_SND_SOC) += uniphier/ obj-$(CONFIG_SND_SOC) += ux500/ obj-$(CONFIG_SND_SOC) += xtensa/ obj-$(CONFIG_SND_SOC) += zte/ diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig new file mode 100644 index 000000000000..02886a457eaf --- /dev/null +++ b/sound/soc/uniphier/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +config SND_SOC_UNIPHIER + tristate "ASoC support for UniPhier" + depends on (ARCH_UNIPHIER || COMPILE_TEST) + help + Say Y or M if you want to add support for the Socionext + UniPhier SoC audio interfaces. You will also need to select the + audio interfaces to support below. + If unsure select "N". + +config SND_SOC_UNIPHIER_EVEA_CODEC + tristate "UniPhier SoC internal audio codec" + depends on SND_SOC_UNIPHIER + select REGMAP_MMIO + help + This adds Codec driver for Socionext UniPhier LD11/20 SoC + internal DAC. This driver supports Line In / Out and HeadPhone. + Select Y if you use such device. + If unsure select "N". diff --git a/sound/soc/uniphier/Makefile b/sound/soc/uniphier/Makefile new file mode 100644 index 000000000000..3be00d72f5e5 --- /dev/null +++ b/sound/soc/uniphier/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-soc-uniphier-evea-objs := evea.o +obj-$(CONFIG_SND_SOC_UNIPHIER_EVEA_CODEC) += snd-soc-uniphier-evea.o diff --git a/sound/soc/uniphier/evea.c b/sound/soc/uniphier/evea.c new file mode 100644 index 000000000000..0cc9efff1d9a --- /dev/null +++ b/sound/soc/uniphier/evea.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Socionext UniPhier EVEA ADC/DAC codec driver. + * + * Copyright (c) 2016-2017 Socionext Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#define DRV_NAME "evea" +#define EVEA_RATES SNDRV_PCM_RATE_48000 +#define EVEA_FORMATS SNDRV_PCM_FMTBIT_S32_LE + +#define AADCPOW(n) (0x0078 + 0x04 * (n)) +#define AADCPOW_AADC_POWD BIT(0) +#define AHPOUTPOW 0x0098 +#define AHPOUTPOW_HP_ON BIT(4) +#define ALINEPOW 0x009c +#define ALINEPOW_LIN2_POWD BIT(3) +#define ALINEPOW_LIN1_POWD BIT(4) +#define ALO1OUTPOW 0x00a8 +#define ALO1OUTPOW_LO1_ON BIT(4) +#define ALO2OUTPOW 0x00ac +#define ALO2OUTPOW_ADAC2_MUTE BIT(0) +#define ALO2OUTPOW_LO2_ON BIT(4) +#define AANAPOW 0x00b8 +#define AANAPOW_A_POWD BIT(4) +#define ADACSEQ1(n) (0x0144 + 0x40 * (n)) +#define ADACSEQ1_MMUTE BIT(1) +#define ADACSEQ2(n) (0x0160 + 0x40 * (n)) +#define ADACSEQ2_ADACIN_FIX BIT(0) +#define ADAC1ODC 0x0200 +#define ADAC1ODC_HP_DIS_RES_MASK GENMASK(2, 1) +#define ADAC1ODC_HP_DIS_RES_OFF (0x0 << 1) +#define ADAC1ODC_HP_DIS_RES_ON (0x3 << 1) +#define ADAC1ODC_ADAC_RAMPCLT_MASK GENMASK(8, 7) +#define ADAC1ODC_ADAC_RAMPCLT_NORMAL (0x0 << 7) +#define ADAC1ODC_ADAC_RAMPCLT_REDUCE (0x1 << 7) + +struct evea_priv { + struct clk *clk, *clk_exiv; + struct reset_control *rst, *rst_exiv, *rst_adamv; + struct regmap *regmap; + + int switch_lin; + int switch_lo; + int switch_hp; +}; + +static const struct snd_soc_dapm_widget evea_widgets[] = { + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("LIN1_LP"), + SND_SOC_DAPM_INPUT("LIN1_RP"), + SND_SOC_DAPM_INPUT("LIN2_LP"), + SND_SOC_DAPM_INPUT("LIN2_RP"), + SND_SOC_DAPM_INPUT("LIN3_LP"), + SND_SOC_DAPM_INPUT("LIN3_RP"), + + SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("HP1_L"), + SND_SOC_DAPM_OUTPUT("HP1_R"), + SND_SOC_DAPM_OUTPUT("LO2_L"), + SND_SOC_DAPM_OUTPUT("LO2_R"), +}; + +static const struct snd_soc_dapm_route evea_routes[] = { + { "ADC", NULL, "LIN1_LP" }, + { "ADC", NULL, "LIN1_RP" }, + { "ADC", NULL, "LIN2_LP" }, + { "ADC", NULL, "LIN2_RP" }, + { "ADC", NULL, "LIN3_LP" }, + { "ADC", NULL, "LIN3_RP" }, + + { "HP1_L", NULL, "DAC" }, + { "HP1_R", NULL, "DAC" }, + { "LO2_L", NULL, "DAC" }, + { "LO2_R", NULL, "DAC" }, +}; + +static void evea_set_power_state_on(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + regmap_update_bits(map, AANAPOW, AANAPOW_A_POWD, + AANAPOW_A_POWD); + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_ON); + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_ADAC_RAMPCLT_MASK, + ADAC1ODC_ADAC_RAMPCLT_REDUCE); + + regmap_update_bits(map, ADACSEQ2(0), ADACSEQ2_ADACIN_FIX, 0); + regmap_update_bits(map, ADACSEQ2(1), ADACSEQ2_ADACIN_FIX, 0); + regmap_update_bits(map, ADACSEQ2(2), ADACSEQ2_ADACIN_FIX, 0); +} + +static void evea_set_power_state_off(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_ON); + + regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + + regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0); + regmap_update_bits(map, ALO2OUTPOW, ALO2OUTPOW_LO2_ON, 0); + regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0); +} + +static int evea_update_switch_lin(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + if (evea->switch_lin) { + regmap_update_bits(map, ALINEPOW, + ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, + ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD); + + regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, + AADCPOW_AADC_POWD); + regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, + AADCPOW_AADC_POWD); + } else { + regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, 0); + regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, 0); + + regmap_update_bits(map, ALINEPOW, + ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, 0); + } + + return 0; +} + +static int evea_update_switch_lo(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + if (evea->switch_lo) { + regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, 0); + regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, 0); + + regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, + ALO1OUTPOW_LO1_ON); + regmap_update_bits(map, ALO2OUTPOW, + ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON, + ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON); + } else { + regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + + regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0); + regmap_update_bits(map, ALO2OUTPOW, + ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON, + 0); + } + + return 0; +} + +static int evea_update_switch_hp(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + if (evea->switch_hp) { + regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, 0); + + regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, + AHPOUTPOW_HP_ON); + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_OFF); + } else { + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_ON); + + regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + + regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0); + } + + return 0; +} + +static void evea_update_switch_all(struct evea_priv *evea) +{ + evea_update_switch_lin(evea); + evea_update_switch_lo(evea); + evea_update_switch_hp(evea); +} + +static int evea_get_switch_lin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = evea->switch_lin; + + return 0; +} + +static int evea_set_switch_lin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + if (evea->switch_lin == ucontrol->value.integer.value[0]) + return 0; + + evea->switch_lin = ucontrol->value.integer.value[0]; + + return evea_update_switch_lin(evea); +} + +static int evea_get_switch_lo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = evea->switch_lo; + + return 0; +} + +static int evea_set_switch_lo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + if (evea->switch_lo == ucontrol->value.integer.value[0]) + return 0; + + evea->switch_lo = ucontrol->value.integer.value[0]; + + return evea_update_switch_lo(evea); +} + +static int evea_get_switch_hp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = evea->switch_hp; + + return 0; +} + +static int evea_set_switch_hp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + if (evea->switch_hp == ucontrol->value.integer.value[0]) + return 0; + + evea->switch_hp = ucontrol->value.integer.value[0]; + + return evea_update_switch_hp(evea); +} + +static const struct snd_kcontrol_new eva_controls[] = { + SOC_SINGLE_BOOL_EXT("Line Capture Switch", 0, + evea_get_switch_lin, evea_set_switch_lin), + SOC_SINGLE_BOOL_EXT("Line Playback Switch", 0, + evea_get_switch_lo, evea_set_switch_lo), + SOC_SINGLE_BOOL_EXT("Headphone Playback Switch", 0, + evea_get_switch_hp, evea_set_switch_hp), +}; + +static int evea_codec_probe(struct snd_soc_codec *codec) +{ + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + evea->switch_lin = 1; + evea->switch_lo = 1; + evea->switch_hp = 1; + + evea_set_power_state_on(evea); + evea_update_switch_all(evea); + + return 0; +} + +static int evea_codec_suspend(struct snd_soc_codec *codec) +{ + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + evea_set_power_state_off(evea); + + reset_control_assert(evea->rst_adamv); + reset_control_assert(evea->rst_exiv); + reset_control_assert(evea->rst); + + clk_disable_unprepare(evea->clk_exiv); + clk_disable_unprepare(evea->clk); + + return 0; +} + +static int evea_codec_resume(struct snd_soc_codec *codec) +{ + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = clk_prepare_enable(evea->clk); + if (ret) + return ret; + + ret = clk_prepare_enable(evea->clk_exiv); + if (ret) + goto err_out_clock; + + ret = reset_control_deassert(evea->rst); + if (ret) + goto err_out_clock_exiv; + + ret = reset_control_deassert(evea->rst_exiv); + if (ret) + goto err_out_reset; + + ret = reset_control_deassert(evea->rst_adamv); + if (ret) + goto err_out_reset_exiv; + + evea_set_power_state_on(evea); + evea_update_switch_all(evea); + + return 0; + +err_out_reset_exiv: + reset_control_assert(evea->rst_exiv); + +err_out_reset: + reset_control_assert(evea->rst); + +err_out_clock_exiv: + clk_disable_unprepare(evea->clk_exiv); + +err_out_clock: + clk_disable_unprepare(evea->clk); + + return ret; +} + +static struct snd_soc_codec_driver soc_codec_evea = { + .probe = evea_codec_probe, + .suspend = evea_codec_suspend, + .resume = evea_codec_resume, + + .component_driver = { + .dapm_widgets = evea_widgets, + .num_dapm_widgets = ARRAY_SIZE(evea_widgets), + .dapm_routes = evea_routes, + .num_dapm_routes = ARRAY_SIZE(evea_routes), + .controls = eva_controls, + .num_controls = ARRAY_SIZE(eva_controls), + }, +}; + +static struct snd_soc_dai_driver soc_dai_evea[] = { + { + .name = DRV_NAME "-line1", + .playback = { + .stream_name = "Line Out 1", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Line In 1", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + }, + { + .name = DRV_NAME "-hp1", + .playback = { + .stream_name = "Headphone 1", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + }, + { + .name = DRV_NAME "-lo2", + .playback = { + .stream_name = "Line Out 2", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + }, +}; + +static const struct regmap_config evea_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xffc, + .cache_type = REGCACHE_NONE, +}; + +static int evea_probe(struct platform_device *pdev) +{ + struct evea_priv *evea; + struct resource *res; + void __iomem *preg; + int ret; + + evea = devm_kzalloc(&pdev->dev, sizeof(struct evea_priv), GFP_KERNEL); + if (!evea) + return -ENOMEM; + + evea->clk = devm_clk_get(&pdev->dev, "evea"); + if (IS_ERR(evea->clk)) + return PTR_ERR(evea->clk); + + evea->clk_exiv = devm_clk_get(&pdev->dev, "exiv"); + if (IS_ERR(evea->clk_exiv)) + return PTR_ERR(evea->clk_exiv); + + evea->rst = devm_reset_control_get_shared(&pdev->dev, "evea"); + if (IS_ERR(evea->rst)) + return PTR_ERR(evea->rst); + + evea->rst_exiv = devm_reset_control_get_shared(&pdev->dev, "exiv"); + if (IS_ERR(evea->rst_exiv)) + return PTR_ERR(evea->rst_exiv); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + preg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(preg)) + return PTR_ERR(preg); + + evea->regmap = devm_regmap_init_mmio(&pdev->dev, preg, + &evea_regmap_config); + if (IS_ERR(evea->regmap)) + return PTR_ERR(evea->regmap); + + ret = clk_prepare_enable(evea->clk); + if (ret) + return ret; + + ret = clk_prepare_enable(evea->clk_exiv); + if (ret) + goto err_out_clock; + + ret = reset_control_deassert(evea->rst); + if (ret) + goto err_out_clock_exiv; + + ret = reset_control_deassert(evea->rst_exiv); + if (ret) + goto err_out_reset; + + /* ADAMV will hangup if EXIV reset is asserted */ + evea->rst_adamv = devm_reset_control_get_shared(&pdev->dev, "adamv"); + if (IS_ERR(evea->rst_adamv)) { + ret = PTR_ERR(evea->rst_adamv); + goto err_out_reset_exiv; + } + + ret = reset_control_deassert(evea->rst_adamv); + if (ret) + goto err_out_reset_exiv; + + platform_set_drvdata(pdev, evea); + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_evea, + soc_dai_evea, ARRAY_SIZE(soc_dai_evea)); + if (ret) + goto err_out_reset_adamv; + + return 0; + +err_out_reset_adamv: + reset_control_assert(evea->rst_adamv); + +err_out_reset_exiv: + reset_control_assert(evea->rst_exiv); + +err_out_reset: + reset_control_assert(evea->rst); + +err_out_clock_exiv: + clk_disable_unprepare(evea->clk_exiv); + +err_out_clock: + clk_disable_unprepare(evea->clk); + + return ret; +} + +static int evea_remove(struct platform_device *pdev) +{ + struct evea_priv *evea = platform_get_drvdata(pdev); + + snd_soc_unregister_codec(&pdev->dev); + + reset_control_assert(evea->rst_adamv); + reset_control_assert(evea->rst_exiv); + reset_control_assert(evea->rst); + + clk_disable_unprepare(evea->clk_exiv); + clk_disable_unprepare(evea->clk); + + return 0; +} + +static const struct of_device_id evea_of_match[] = { + { .compatible = "socionext,uniphier-evea", }, + {} +}; +MODULE_DEVICE_TABLE(of, evea_of_match); + +static struct platform_driver evea_codec_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(evea_of_match), + }, + .probe = evea_probe, + .remove = evea_remove, +}; +module_platform_driver(evea_codec_driver); + +MODULE_AUTHOR("Katsuhiro Suzuki suzuki.katsuhiro@socionext.com"); +MODULE_DESCRIPTION("UniPhier EVEA codec driver"); +MODULE_LICENSE("GPL v2");
On Wed, Nov 22, 2017 at 08:43:17PM +0900, Katsuhiro Suzuki wrote:
+++ b/sound/soc/uniphier/evea.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Socionext UniPhier EVEA ADC/DAC codec driver.
- Copyright (c) 2016-2017 Socionext Inc.
Make the entire comment a C++ comment, don't mix and match like this - it's ugly. Otherwise this looks good so I'll apply it, please send a followup patch fixing this (and I guess the same thing will apply to other files).
+CC Greg-KH +CC Thomas Gleixner
2017-12-05 3:20 GMT+09:00 Mark Brown broonie@kernel.org:
On Wed, Nov 22, 2017 at 08:43:17PM +0900, Katsuhiro Suzuki wrote:
+++ b/sound/soc/uniphier/evea.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Socionext UniPhier EVEA ADC/DAC codec driver.
- Copyright (c) 2016-2017 Socionext Inc.
Make the entire comment a C++ comment, don't mix and match like this - it's ugly. Otherwise this looks good so I'll apply it, please send a followup patch fixing this (and I guess the same thing will apply to other files).
Indeed ugly, but I think this is intentional to make the SPDX line stand out.
Linus suggested this as far as I understood from the following: https://patchwork.kernel.org/patch/10016201/
If you use C++ comment style for the entire block, it will not stand out.
On Tue, Dec 05, 2017 at 09:58:54AM +0900, Masahiro Yamada wrote:
Indeed ugly, but I think this is intentional to make the SPDX line stand out.
Linus suggested this as far as I understood from the following: https://patchwork.kernel.org/patch/10016201/
If you use C++ comment style for the entire block, it will not stand out.
It's the first line of the file to make it stand out, if you google around you can find some other conversation about how C++ comments are a good idea in general and this might encourage them.
The patch
ASoC: uniphier: add support for UniPhier EVEA codec
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 3a47b1dfa2913038623cec3164adfb2448269fa6 Mon Sep 17 00:00:00 2001
From: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com Date: Wed, 22 Nov 2017 20:43:17 +0900 Subject: [PATCH] ASoC: uniphier: add support for UniPhier EVEA codec
This patch adds EVEA codec driver. This codec core is in inside of UniPhier SoC.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/uniphier/Kconfig | 19 ++ sound/soc/uniphier/Makefile | 3 + sound/soc/uniphier/evea.c | 567 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 591 insertions(+) create mode 100644 sound/soc/uniphier/Kconfig create mode 100644 sound/soc/uniphier/Makefile create mode 100644 sound/soc/uniphier/evea.c
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index d22758165496..84c3582f3982 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -71,6 +71,7 @@ source "sound/soc/stm/Kconfig" source "sound/soc/sunxi/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" +source "sound/soc/uniphier/Kconfig" source "sound/soc/ux500/Kconfig" source "sound/soc/xtensa/Kconfig" source "sound/soc/zte/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 5327f4d6c668..74cd1858d38b 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_SND_SOC) += stm/ obj-$(CONFIG_SND_SOC) += sunxi/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ +obj-$(CONFIG_SND_SOC) += uniphier/ obj-$(CONFIG_SND_SOC) += ux500/ obj-$(CONFIG_SND_SOC) += xtensa/ obj-$(CONFIG_SND_SOC) += zte/ diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig new file mode 100644 index 000000000000..02886a457eaf --- /dev/null +++ b/sound/soc/uniphier/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +config SND_SOC_UNIPHIER + tristate "ASoC support for UniPhier" + depends on (ARCH_UNIPHIER || COMPILE_TEST) + help + Say Y or M if you want to add support for the Socionext + UniPhier SoC audio interfaces. You will also need to select the + audio interfaces to support below. + If unsure select "N". + +config SND_SOC_UNIPHIER_EVEA_CODEC + tristate "UniPhier SoC internal audio codec" + depends on SND_SOC_UNIPHIER + select REGMAP_MMIO + help + This adds Codec driver for Socionext UniPhier LD11/20 SoC + internal DAC. This driver supports Line In / Out and HeadPhone. + Select Y if you use such device. + If unsure select "N". diff --git a/sound/soc/uniphier/Makefile b/sound/soc/uniphier/Makefile new file mode 100644 index 000000000000..3be00d72f5e5 --- /dev/null +++ b/sound/soc/uniphier/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-soc-uniphier-evea-objs := evea.o +obj-$(CONFIG_SND_SOC_UNIPHIER_EVEA_CODEC) += snd-soc-uniphier-evea.o diff --git a/sound/soc/uniphier/evea.c b/sound/soc/uniphier/evea.c new file mode 100644 index 000000000000..0cc9efff1d9a --- /dev/null +++ b/sound/soc/uniphier/evea.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Socionext UniPhier EVEA ADC/DAC codec driver. + * + * Copyright (c) 2016-2017 Socionext Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#define DRV_NAME "evea" +#define EVEA_RATES SNDRV_PCM_RATE_48000 +#define EVEA_FORMATS SNDRV_PCM_FMTBIT_S32_LE + +#define AADCPOW(n) (0x0078 + 0x04 * (n)) +#define AADCPOW_AADC_POWD BIT(0) +#define AHPOUTPOW 0x0098 +#define AHPOUTPOW_HP_ON BIT(4) +#define ALINEPOW 0x009c +#define ALINEPOW_LIN2_POWD BIT(3) +#define ALINEPOW_LIN1_POWD BIT(4) +#define ALO1OUTPOW 0x00a8 +#define ALO1OUTPOW_LO1_ON BIT(4) +#define ALO2OUTPOW 0x00ac +#define ALO2OUTPOW_ADAC2_MUTE BIT(0) +#define ALO2OUTPOW_LO2_ON BIT(4) +#define AANAPOW 0x00b8 +#define AANAPOW_A_POWD BIT(4) +#define ADACSEQ1(n) (0x0144 + 0x40 * (n)) +#define ADACSEQ1_MMUTE BIT(1) +#define ADACSEQ2(n) (0x0160 + 0x40 * (n)) +#define ADACSEQ2_ADACIN_FIX BIT(0) +#define ADAC1ODC 0x0200 +#define ADAC1ODC_HP_DIS_RES_MASK GENMASK(2, 1) +#define ADAC1ODC_HP_DIS_RES_OFF (0x0 << 1) +#define ADAC1ODC_HP_DIS_RES_ON (0x3 << 1) +#define ADAC1ODC_ADAC_RAMPCLT_MASK GENMASK(8, 7) +#define ADAC1ODC_ADAC_RAMPCLT_NORMAL (0x0 << 7) +#define ADAC1ODC_ADAC_RAMPCLT_REDUCE (0x1 << 7) + +struct evea_priv { + struct clk *clk, *clk_exiv; + struct reset_control *rst, *rst_exiv, *rst_adamv; + struct regmap *regmap; + + int switch_lin; + int switch_lo; + int switch_hp; +}; + +static const struct snd_soc_dapm_widget evea_widgets[] = { + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("LIN1_LP"), + SND_SOC_DAPM_INPUT("LIN1_RP"), + SND_SOC_DAPM_INPUT("LIN2_LP"), + SND_SOC_DAPM_INPUT("LIN2_RP"), + SND_SOC_DAPM_INPUT("LIN3_LP"), + SND_SOC_DAPM_INPUT("LIN3_RP"), + + SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("HP1_L"), + SND_SOC_DAPM_OUTPUT("HP1_R"), + SND_SOC_DAPM_OUTPUT("LO2_L"), + SND_SOC_DAPM_OUTPUT("LO2_R"), +}; + +static const struct snd_soc_dapm_route evea_routes[] = { + { "ADC", NULL, "LIN1_LP" }, + { "ADC", NULL, "LIN1_RP" }, + { "ADC", NULL, "LIN2_LP" }, + { "ADC", NULL, "LIN2_RP" }, + { "ADC", NULL, "LIN3_LP" }, + { "ADC", NULL, "LIN3_RP" }, + + { "HP1_L", NULL, "DAC" }, + { "HP1_R", NULL, "DAC" }, + { "LO2_L", NULL, "DAC" }, + { "LO2_R", NULL, "DAC" }, +}; + +static void evea_set_power_state_on(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + regmap_update_bits(map, AANAPOW, AANAPOW_A_POWD, + AANAPOW_A_POWD); + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_ON); + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_ADAC_RAMPCLT_MASK, + ADAC1ODC_ADAC_RAMPCLT_REDUCE); + + regmap_update_bits(map, ADACSEQ2(0), ADACSEQ2_ADACIN_FIX, 0); + regmap_update_bits(map, ADACSEQ2(1), ADACSEQ2_ADACIN_FIX, 0); + regmap_update_bits(map, ADACSEQ2(2), ADACSEQ2_ADACIN_FIX, 0); +} + +static void evea_set_power_state_off(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_ON); + + regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + + regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0); + regmap_update_bits(map, ALO2OUTPOW, ALO2OUTPOW_LO2_ON, 0); + regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0); +} + +static int evea_update_switch_lin(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + if (evea->switch_lin) { + regmap_update_bits(map, ALINEPOW, + ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, + ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD); + + regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, + AADCPOW_AADC_POWD); + regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, + AADCPOW_AADC_POWD); + } else { + regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, 0); + regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, 0); + + regmap_update_bits(map, ALINEPOW, + ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, 0); + } + + return 0; +} + +static int evea_update_switch_lo(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + if (evea->switch_lo) { + regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, 0); + regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, 0); + + regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, + ALO1OUTPOW_LO1_ON); + regmap_update_bits(map, ALO2OUTPOW, + ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON, + ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON); + } else { + regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + + regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0); + regmap_update_bits(map, ALO2OUTPOW, + ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON, + 0); + } + + return 0; +} + +static int evea_update_switch_hp(struct evea_priv *evea) +{ + struct regmap *map = evea->regmap; + + if (evea->switch_hp) { + regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, 0); + + regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, + AHPOUTPOW_HP_ON); + + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_OFF); + } else { + regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK, + ADAC1ODC_HP_DIS_RES_ON); + + regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, + ADACSEQ1_MMUTE); + + regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0); + } + + return 0; +} + +static void evea_update_switch_all(struct evea_priv *evea) +{ + evea_update_switch_lin(evea); + evea_update_switch_lo(evea); + evea_update_switch_hp(evea); +} + +static int evea_get_switch_lin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = evea->switch_lin; + + return 0; +} + +static int evea_set_switch_lin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + if (evea->switch_lin == ucontrol->value.integer.value[0]) + return 0; + + evea->switch_lin = ucontrol->value.integer.value[0]; + + return evea_update_switch_lin(evea); +} + +static int evea_get_switch_lo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = evea->switch_lo; + + return 0; +} + +static int evea_set_switch_lo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + if (evea->switch_lo == ucontrol->value.integer.value[0]) + return 0; + + evea->switch_lo = ucontrol->value.integer.value[0]; + + return evea_update_switch_lo(evea); +} + +static int evea_get_switch_hp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = evea->switch_hp; + + return 0; +} + +static int evea_set_switch_hp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + if (evea->switch_hp == ucontrol->value.integer.value[0]) + return 0; + + evea->switch_hp = ucontrol->value.integer.value[0]; + + return evea_update_switch_hp(evea); +} + +static const struct snd_kcontrol_new eva_controls[] = { + SOC_SINGLE_BOOL_EXT("Line Capture Switch", 0, + evea_get_switch_lin, evea_set_switch_lin), + SOC_SINGLE_BOOL_EXT("Line Playback Switch", 0, + evea_get_switch_lo, evea_set_switch_lo), + SOC_SINGLE_BOOL_EXT("Headphone Playback Switch", 0, + evea_get_switch_hp, evea_set_switch_hp), +}; + +static int evea_codec_probe(struct snd_soc_codec *codec) +{ + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + evea->switch_lin = 1; + evea->switch_lo = 1; + evea->switch_hp = 1; + + evea_set_power_state_on(evea); + evea_update_switch_all(evea); + + return 0; +} + +static int evea_codec_suspend(struct snd_soc_codec *codec) +{ + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + + evea_set_power_state_off(evea); + + reset_control_assert(evea->rst_adamv); + reset_control_assert(evea->rst_exiv); + reset_control_assert(evea->rst); + + clk_disable_unprepare(evea->clk_exiv); + clk_disable_unprepare(evea->clk); + + return 0; +} + +static int evea_codec_resume(struct snd_soc_codec *codec) +{ + struct evea_priv *evea = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = clk_prepare_enable(evea->clk); + if (ret) + return ret; + + ret = clk_prepare_enable(evea->clk_exiv); + if (ret) + goto err_out_clock; + + ret = reset_control_deassert(evea->rst); + if (ret) + goto err_out_clock_exiv; + + ret = reset_control_deassert(evea->rst_exiv); + if (ret) + goto err_out_reset; + + ret = reset_control_deassert(evea->rst_adamv); + if (ret) + goto err_out_reset_exiv; + + evea_set_power_state_on(evea); + evea_update_switch_all(evea); + + return 0; + +err_out_reset_exiv: + reset_control_assert(evea->rst_exiv); + +err_out_reset: + reset_control_assert(evea->rst); + +err_out_clock_exiv: + clk_disable_unprepare(evea->clk_exiv); + +err_out_clock: + clk_disable_unprepare(evea->clk); + + return ret; +} + +static struct snd_soc_codec_driver soc_codec_evea = { + .probe = evea_codec_probe, + .suspend = evea_codec_suspend, + .resume = evea_codec_resume, + + .component_driver = { + .dapm_widgets = evea_widgets, + .num_dapm_widgets = ARRAY_SIZE(evea_widgets), + .dapm_routes = evea_routes, + .num_dapm_routes = ARRAY_SIZE(evea_routes), + .controls = eva_controls, + .num_controls = ARRAY_SIZE(eva_controls), + }, +}; + +static struct snd_soc_dai_driver soc_dai_evea[] = { + { + .name = DRV_NAME "-line1", + .playback = { + .stream_name = "Line Out 1", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Line In 1", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + }, + { + .name = DRV_NAME "-hp1", + .playback = { + .stream_name = "Headphone 1", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + }, + { + .name = DRV_NAME "-lo2", + .playback = { + .stream_name = "Line Out 2", + .formats = EVEA_FORMATS, + .rates = EVEA_RATES, + .channels_min = 2, + .channels_max = 2, + }, + }, +}; + +static const struct regmap_config evea_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xffc, + .cache_type = REGCACHE_NONE, +}; + +static int evea_probe(struct platform_device *pdev) +{ + struct evea_priv *evea; + struct resource *res; + void __iomem *preg; + int ret; + + evea = devm_kzalloc(&pdev->dev, sizeof(struct evea_priv), GFP_KERNEL); + if (!evea) + return -ENOMEM; + + evea->clk = devm_clk_get(&pdev->dev, "evea"); + if (IS_ERR(evea->clk)) + return PTR_ERR(evea->clk); + + evea->clk_exiv = devm_clk_get(&pdev->dev, "exiv"); + if (IS_ERR(evea->clk_exiv)) + return PTR_ERR(evea->clk_exiv); + + evea->rst = devm_reset_control_get_shared(&pdev->dev, "evea"); + if (IS_ERR(evea->rst)) + return PTR_ERR(evea->rst); + + evea->rst_exiv = devm_reset_control_get_shared(&pdev->dev, "exiv"); + if (IS_ERR(evea->rst_exiv)) + return PTR_ERR(evea->rst_exiv); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + preg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(preg)) + return PTR_ERR(preg); + + evea->regmap = devm_regmap_init_mmio(&pdev->dev, preg, + &evea_regmap_config); + if (IS_ERR(evea->regmap)) + return PTR_ERR(evea->regmap); + + ret = clk_prepare_enable(evea->clk); + if (ret) + return ret; + + ret = clk_prepare_enable(evea->clk_exiv); + if (ret) + goto err_out_clock; + + ret = reset_control_deassert(evea->rst); + if (ret) + goto err_out_clock_exiv; + + ret = reset_control_deassert(evea->rst_exiv); + if (ret) + goto err_out_reset; + + /* ADAMV will hangup if EXIV reset is asserted */ + evea->rst_adamv = devm_reset_control_get_shared(&pdev->dev, "adamv"); + if (IS_ERR(evea->rst_adamv)) { + ret = PTR_ERR(evea->rst_adamv); + goto err_out_reset_exiv; + } + + ret = reset_control_deassert(evea->rst_adamv); + if (ret) + goto err_out_reset_exiv; + + platform_set_drvdata(pdev, evea); + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_evea, + soc_dai_evea, ARRAY_SIZE(soc_dai_evea)); + if (ret) + goto err_out_reset_adamv; + + return 0; + +err_out_reset_adamv: + reset_control_assert(evea->rst_adamv); + +err_out_reset_exiv: + reset_control_assert(evea->rst_exiv); + +err_out_reset: + reset_control_assert(evea->rst); + +err_out_clock_exiv: + clk_disable_unprepare(evea->clk_exiv); + +err_out_clock: + clk_disable_unprepare(evea->clk); + + return ret; +} + +static int evea_remove(struct platform_device *pdev) +{ + struct evea_priv *evea = platform_get_drvdata(pdev); + + snd_soc_unregister_codec(&pdev->dev); + + reset_control_assert(evea->rst_adamv); + reset_control_assert(evea->rst_exiv); + reset_control_assert(evea->rst); + + clk_disable_unprepare(evea->clk_exiv); + clk_disable_unprepare(evea->clk); + + return 0; +} + +static const struct of_device_id evea_of_match[] = { + { .compatible = "socionext,uniphier-evea", }, + {} +}; +MODULE_DEVICE_TABLE(of, evea_of_match); + +static struct platform_driver evea_codec_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(evea_of_match), + }, + .probe = evea_probe, + .remove = evea_remove, +}; +module_platform_driver(evea_codec_driver); + +MODULE_AUTHOR("Katsuhiro Suzuki suzuki.katsuhiro@socionext.com"); +MODULE_DESCRIPTION("UniPhier EVEA codec driver"); +MODULE_LICENSE("GPL v2");
This patch adds for UniPhier AIO audio sound system. This provides commonly used APIs for input/output control registers and DMA interfaces for UniPhier AIO.
This module provides all sound devices for I2S, S/PDIF and so on. Since the AIO has mixed register map for those I/Os, it is hard to split register areas for each sound devices.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com --- sound/soc/uniphier/Kconfig | 5 + sound/soc/uniphier/Makefile | 4 + sound/soc/uniphier/aio-core.c | 368 +++++++++++++++++++++ sound/soc/uniphier/aio-dma.c | 266 +++++++++++++++ sound/soc/uniphier/aio-regctrl.c | 699 +++++++++++++++++++++++++++++++++++++++ sound/soc/uniphier/aio-regctrl.h | 495 +++++++++++++++++++++++++++ sound/soc/uniphier/aio.h | 261 +++++++++++++++ 7 files changed, 2098 insertions(+) create mode 100644 sound/soc/uniphier/aio-core.c create mode 100644 sound/soc/uniphier/aio-dma.c create mode 100644 sound/soc/uniphier/aio-regctrl.c create mode 100644 sound/soc/uniphier/aio-regctrl.h create mode 100644 sound/soc/uniphier/aio.h
diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig index 02886a457eaf..38a02879b9d5 100644 --- a/sound/soc/uniphier/Kconfig +++ b/sound/soc/uniphier/Kconfig @@ -8,6 +8,11 @@ config SND_SOC_UNIPHIER audio interfaces to support below. If unsure select "N".
+config SND_SOC_UNIPHIER_AIO + tristate + select REGMAP_MMIO + depends on SND_SOC_UNIPHIER + config SND_SOC_UNIPHIER_EVEA_CODEC tristate "UniPhier SoC internal audio codec" depends on SND_SOC_UNIPHIER diff --git a/sound/soc/uniphier/Makefile b/sound/soc/uniphier/Makefile index 3be00d72f5e5..705a60cb2aa5 100644 --- a/sound/soc/uniphier/Makefile +++ b/sound/soc/uniphier/Makefile @@ -1,3 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 +snd-soc-uniphier-aio-objs := aio-regctrl.o aio-dma.o aio-core.o + +obj-$(CONFIG_SND_SOC_UNIPHIER_AIO) += snd-soc-uniphier-aio.o + snd-soc-uniphier-evea-objs := evea.o obj-$(CONFIG_SND_SOC_UNIPHIER_EVEA_CODEC) += snd-soc-uniphier-evea.o diff --git a/sound/soc/uniphier/aio-core.c b/sound/soc/uniphier/aio-core.c new file mode 100644 index 000000000000..08f33f2c7c99 --- /dev/null +++ b/sound/soc/uniphier/aio-core.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Socionext UniPhier AIO ALSA driver. + * + * Copyright (c) 2016-2017 Socionext Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#include "aio.h" +#include "aio-regctrl.h" + +static int match_name(const struct uniphier_aio_spec *spec, + const char *name, int direction) +{ + if (direction == SNDRV_PCM_STREAM_PLAYBACK && + spec->swm.dir != PORT_DIR_OUTPUT) { + return 0; + } + + if (direction == SNDRV_PCM_STREAM_CAPTURE && + spec->swm.dir != PORT_DIR_INPUT) { + return 0; + } + + if (spec->name && strcmp(spec->name, name) == 0) + return 1; + + if (spec->gname && strcmp(spec->gname, name) == 0) + return 1; + + return 0; +} + +/** + * uniphier_aio_find_spec - find HW specification info by name + * @aio: the AIO device pointer + * @name: name of device + * @direction: the direction of substream, SNDRV_PCM_STREAM_* + * + * Find hardware specification information from list by device name. This + * information is used for telling the difference of SoCs to driver. + * + * Specification list is array of 'struct uniphier_aio_spec' which is defined + * in each drivers (see: aio-i2s.c). + * + * Return: The pointer of hardware specification of AIO if successful, + * otherwise NULL on error. + */ +const struct uniphier_aio_spec *find_spec(struct uniphier_aio *aio, + const char *name, int direction) +{ + const struct uniphier_aio_chip_spec *chip_spec = aio->chip->chip_spec; + int i; + + for (i = 0; i < chip_spec->num_aio_specs; i++) { + const struct uniphier_aio_spec *spec = + &chip_spec->aio_specs[i]; + + if (!match_name(spec, name, direction)) + continue; + + return spec; + } + + return NULL; +} + +int uniphier_aio_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct uniphier_aio *aio = uniphier_priv(dai); + struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; + + sub->substream = substream; + aio->dai = dai; + + uniphier_aio_init(sub); + + return 0; +} + +void uniphier_aio_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct uniphier_aio *aio = uniphier_priv(dai); + struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; + + sub->substream = NULL; + aio->dai = NULL; +} + +int uniphier_aio_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct uniphier_aio *aio = uniphier_priv(dai); + struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; + + sub->params = *params; + sub->setting = 1; + + uniphier_aio_port_reset(sub); + uniphier_aio_srcport_reset(sub); + + return 0; +} + +int uniphier_aio_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct uniphier_aio *aio = uniphier_priv(dai); + struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; + + sub->setting = 0; + + return 0; +} + +int uniphier_aio_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct uniphier_aio *aio = uniphier_priv(dai); + struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; + + uniphier_aio_port_set_param(sub, aio->pass_through, &sub->params); + uniphier_aio_srcport_set_param(sub, &sub->params); + if (sub->swm->type == PORT_TYPE_SPDIF && aio->pass_through) + uniphier_aio_oport_set_stream_type(sub, IEC61937_PC_AC3); + uniphier_aio_port_set_enable(sub, 1); + + uniphier_aio_if_set_param(sub, aio->pass_through); + if (sub->swm->type == PORT_TYPE_CONV) { + uniphier_aio_srcif_set_param(sub); + uniphier_aio_srcch_set_param(sub); + uniphier_aio_srcch_set_enable(sub, 1); + } + + return 0; +} + +int uniphier_aio_dai_probe(struct snd_soc_dai *dai) +{ + struct uniphier_aio *aio = uniphier_priv(dai); + int i; + + for (i = 0; i < ARRAY_SIZE(aio->sub); i++) { + struct uniphier_aio_sub *sub = &aio->sub[i]; + const struct uniphier_aio_spec *spec; + + spec = find_spec(aio, dai->name, i); + if (!spec) + continue; + + sub->swm = &spec->swm; + sub->spec = spec; + } + + uniphier_aio_chip_init(aio->chip); + aio->chip->active = 1; + + return 0; +} + +int uniphier_aio_dai_remove(struct snd_soc_dai *dai) +{ + struct uniphier_aio *aio = uniphier_priv(dai); + + aio->chip->active = 0; + + return 0; +} + +int uniphier_aio_dai_suspend(struct snd_soc_dai *dai) +{ + struct uniphier_aio *aio = uniphier_priv(dai); + + reset_control_assert(aio->chip->rst); + clk_disable_unprepare(aio->chip->clk); + + return 0; +} + +int uniphier_aio_dai_resume(struct snd_soc_dai *dai) +{ + struct uniphier_aio *aio = uniphier_priv(dai); + int ret, i; + + if (!aio->chip->active) + return 0; + + ret = clk_prepare_enable(aio->chip->clk); + if (ret) + return ret; + + ret = reset_control_deassert(aio->chip->rst); + if (ret) + goto err_out_clock; + + uniphier_aio_chip_init(aio->chip); + + for (i = 0; i < ARRAY_SIZE(aio->sub); i++) { + struct uniphier_aio_sub *sub = &aio->sub[i]; + + if (!sub->spec || !sub->substream) + continue; + + uniphier_aio_init(sub); + + if (!sub->setting) + continue; + + uniphier_aio_port_reset(sub); + uniphier_aio_srcport_reset(sub); + } + + return 0; + +err_out_clock: + clk_disable_unprepare(aio->chip->clk); + + return ret; +} + +static const struct snd_soc_component_driver uniphier_aio_component = { + .name = "uniphier-aio", +}; + +static int uniphier_aio_probe(struct platform_device *pdev) +{ + struct uniphier_aio_chip *chip; + struct device *dev = &pdev->dev; + int ret, i, j; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->chip_spec = of_device_get_match_data(dev); + if (!chip->chip_spec) + return -EINVAL; + + chip->clk = devm_clk_get(dev, "aio"); + if (IS_ERR(chip->clk)) + return PTR_ERR(chip->clk); + + chip->rst = devm_reset_control_get_shared(dev, "aio"); + if (IS_ERR(chip->rst)) + return PTR_ERR(chip->rst); + + chip->num_aios = chip->chip_spec->num_dais; + chip->aios = devm_kzalloc(dev, + sizeof(struct uniphier_aio) * chip->num_aios, + GFP_KERNEL); + if (!chip->aios) + return -ENOMEM; + + for (i = 0; i < chip->num_aios; i++) { + struct uniphier_aio *aio = &chip->aios[i]; + + aio->chip = chip; + + for (j = 0; j < ARRAY_SIZE(aio->sub); j++) { + struct uniphier_aio_sub *sub = &aio->sub[j]; + + sub->aio = aio; + spin_lock_init(&sub->spin); + } + } + + chip->pdev = pdev; + platform_set_drvdata(pdev, chip); + + ret = clk_prepare_enable(chip->clk); + if (ret) + return ret; + + ret = reset_control_deassert(chip->rst); + if (ret) + goto err_out_clock; + + ret = devm_snd_soc_register_component(dev, &uniphier_aio_component, + chip->chip_spec->dais, + chip->chip_spec->num_dais); + if (ret) { + dev_err(dev, "Register component failed.\n"); + goto err_out_reset; + } + + ret = uniphier_aiodma_soc_register_platform(pdev); + if (ret) { + dev_err(dev, "Register platform failed.\n"); + goto err_out_reset; + } + + return 0; + +err_out_reset: + reset_control_assert(chip->rst); + +err_out_clock: + clk_disable_unprepare(chip->clk); + + return ret; +} + +static int uniphier_aio_remove(struct platform_device *pdev) +{ + struct uniphier_aio_chip *chip = platform_get_drvdata(pdev); + + reset_control_assert(chip->rst); + clk_disable_unprepare(chip->clk); + + return 0; +} + +static const struct of_device_id uniphier_aio_of_match[] = { +#ifdef CONFIG_SND_SOC_UNIPHIER_LD11 + { + .compatible = "socionext,uniphier-ld11-aio", + .data = &uniphier_aio_ld11_spec, + }, + { + .compatible = "socionext,uniphier-ld20-aio", + .data = &uniphier_aio_ld20_spec, + }, +#endif /* CONFIG_SND_SOC_UNIPHIER_LD11 */ + {}, +}; +MODULE_DEVICE_TABLE(of, uniphier_aio_of_match); + +static struct platform_driver uniphier_aio_driver = { + .driver = { + .name = "snd-uniphier-aio", + .of_match_table = of_match_ptr(uniphier_aio_of_match), + }, + .probe = uniphier_aio_probe, + .remove = uniphier_aio_remove, +}; +module_platform_driver(uniphier_aio_driver); + +MODULE_AUTHOR("Katsuhiro Suzuki suzuki.katsuhiro@socionext.com"); +MODULE_DESCRIPTION("UniPhier AIO ALSA driver."); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/uniphier/aio-dma.c b/sound/soc/uniphier/aio-dma.c new file mode 100644 index 000000000000..bac0eb37c2e5 --- /dev/null +++ b/sound/soc/uniphier/aio-dma.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Socionext UniPhier AIO DMA driver. + * + * Copyright (c) 2016-2017 Socionext Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#include "aio.h" +#include "aio-regctrl.h" + +static struct snd_pcm_hardware uniphier_aiodma_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED, + .period_bytes_min = 256, + .period_bytes_max = 4096, + .periods_min = 4, + .periods_max = 1024, + .buffer_bytes_max = 128 * 1024, +}; + +static irqreturn_t uniphier_aiodma_irq(int irq, void *p) +{ + struct platform_device *pdev = p; + struct uniphier_aio_chip *chip = platform_get_drvdata(pdev); + irqreturn_t ret = IRQ_NONE; + int i, j; + + for (i = 0; i < chip->num_aios; i++) { + struct uniphier_aio *aio = &chip->aios[i]; + + for (j = 0; j < ARRAY_SIZE(aio->sub); j++) { + struct uniphier_aio_sub *sub = &aio->sub[j]; + + if (!sub->running) + continue; + + spin_lock(&sub->spin); + uniphier_aio_rb_sync(sub); + uniphier_aio_rb_clear_int(sub); + spin_unlock(&sub->spin); + + snd_pcm_period_elapsed(sub->substream); + + ret = IRQ_HANDLED; + } + } + + return ret; +} + +static int uniphier_aiodma_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_soc_set_runtime_hwparams(substream, &uniphier_aiodma_hw); + + return snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256); +} + +static int uniphier_aiodma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + substream->runtime->dma_bytes = params_buffer_bytes(params); + + return 0; +} + +static int uniphier_aiodma_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + substream->runtime->dma_bytes = 0; + + return 0; +} + +static int uniphier_aiodma_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; + unsigned long flags; + + uniphier_aio_ch_set_param(sub); + + spin_lock_irqsave(&sub->spin, flags); + uniphier_aio_rb_set_buffer(sub, runtime->dma_addr, + runtime->dma_addr + runtime->dma_bytes); + spin_unlock_irqrestore(&sub->spin, flags); + + return 0; +} + +static int uniphier_aiodma_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; + struct device *dev = &aio->chip->pdev->dev; + unsigned long flags; + + spin_lock_irqsave(&sub->spin, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + uniphier_aio_rb_sync(sub); + uniphier_aio_ch_set_enable(sub, 1); + sub->running = 1; + + break; + case SNDRV_PCM_TRIGGER_STOP: + sub->running = 0; + uniphier_aio_ch_set_enable(sub, 0); + + break; + default: + dev_warn(dev, "Unknown trigger %d, ignored.\n", cmd); + break; + } + spin_unlock_irqrestore(&sub->spin, flags); + + return 0; +} + +static snd_pcm_uframes_t uniphier_aiodma_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; + unsigned long flags; + snd_pcm_uframes_t pos; + + spin_lock_irqsave(&sub->spin, flags); + uniphier_aio_rb_sync(sub); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pos = bytes_to_frames(runtime, sub->rd_offs); + else + pos = bytes_to_frames(runtime, sub->wr_offs); + spin_unlock_irqrestore(&sub->spin, flags); + + return pos; +} + +static int uniphier_aiodma_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + return remap_pfn_range(vma, vma->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +static const struct snd_pcm_ops uniphier_aiodma_ops = { + .open = uniphier_aiodma_open, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = uniphier_aiodma_hw_params, + .hw_free = uniphier_aiodma_hw_free, + .prepare = uniphier_aiodma_prepare, + .trigger = uniphier_aiodma_trigger, + .pointer = uniphier_aiodma_pointer, + .mmap = uniphier_aiodma_mmap, +}; + +static int uniphier_aiodma_new(struct snd_soc_pcm_runtime *rtd) +{ + struct device *dev = rtd->card->snd_card->dev; + struct snd_pcm *pcm = rtd->pcm; + int ret; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33)); + if (ret) + return ret; + + return snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV, dev, + uniphier_aiodma_hw.buffer_bytes_max, + uniphier_aiodma_hw.buffer_bytes_max); +} + +static void uniphier_aiodma_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static const struct snd_soc_platform_driver uniphier_soc_platform = { + .pcm_new = uniphier_aiodma_new, + .pcm_free = uniphier_aiodma_free, + .ops = &uniphier_aiodma_ops, +}; + +static const struct regmap_config aiodma_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7fffc, + .cache_type = REGCACHE_NONE, +}; + +/** + * uniphier_aiodma_soc_register_platform - register the AIO DMA + * @pdev: the platform device + * + * Register and setup the DMA of AIO to transfer the sound data to device. + * This function need to call once at driver startup and need NOT to call + * unregister function. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +int uniphier_aiodma_soc_register_platform(struct platform_device *pdev) +{ + struct uniphier_aio_chip *chip = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct resource *res; + void __iomem *preg; + int irq, ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + preg = devm_ioremap_resource(dev, res); + if (IS_ERR(preg)) + return PTR_ERR(preg); + + chip->regmap = devm_regmap_init_mmio(dev, preg, + &aiodma_regmap_config); + if (IS_ERR(chip->regmap)) + return PTR_ERR(chip->regmap); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "Could not get irq.\n"); + return irq; + } + + ret = devm_request_irq(dev, irq, uniphier_aiodma_irq, + IRQF_SHARED, dev_name(dev), pdev); + if (ret) + return ret; + + return devm_snd_soc_register_platform(dev, &uniphier_soc_platform); +} diff --git a/sound/soc/uniphier/aio-regctrl.c b/sound/soc/uniphier/aio-regctrl.c new file mode 100644 index 000000000000..1e3623f2df51 --- /dev/null +++ b/sound/soc/uniphier/aio-regctrl.c @@ -0,0 +1,699 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Socionext UniPhier AIO ALSA driver. + * + * Copyright (c) 2016-2017 Socionext Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#include <linux/bitfield.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#include "aio.h" +#include "aio-regctrl.h" + +/** + * uniphier_aio_chip_init - initialize AIO whole settings + * @chip: the AIO chip pointer + * + * Sets AIO fixed and whole device settings to AIO. + * This function need to call once at driver startup. + * + * The register area that is changed by this function is shared by all + * modules of AIO. But there is not race condition since this function + * has always set the same initialize values. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +int uniphier_aio_chip_init(struct uniphier_aio_chip *chip) +{ + struct regmap *r = chip->regmap; + + regmap_update_bits(r, A2APLLCTR0, + A2APLLCTR0_APLLXPOW_MASK, + A2APLLCTR0_APLLXPOW_PWON); + + regmap_update_bits(r, A2APLLCTR1, A2APLLCTR1_APLL_MASK, + A2APLLCTR1_APLLF2_33MHZ | A2APLLCTR1_APLLA2_33MHZ | + A2APLLCTR1_APLLF1_36MHZ | A2APLLCTR1_APLLA1_36MHZ); + + regmap_update_bits(r, A2EXMCLKSEL0, + A2EXMCLKSEL0_EXMCLK_MASK, + A2EXMCLKSEL0_EXMCLK_OUTPUT); + + regmap_update_bits(r, A2AIOINPUTSEL, A2AIOINPUTSEL_RXSEL_MASK, + A2AIOINPUTSEL_RXSEL_PCMI1_HDMIRX1 | + A2AIOINPUTSEL_RXSEL_PCMI2_SIF | + A2AIOINPUTSEL_RXSEL_PCMI3_EVEA | + A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1); + + if (chip->chip_spec->addr_ext) + regmap_update_bits(r, CDA2D_TEST, CDA2D_TEST_DDR_MODE_MASK, + CDA2D_TEST_DDR_MODE_EXTON0); + else + regmap_update_bits(r, CDA2D_TEST, CDA2D_TEST_DDR_MODE_MASK, + CDA2D_TEST_DDR_MODE_EXTOFF1); + + return 0; +} + +/** + * uniphier_aio_init - initialize AIO substream + * @sub: the AIO substream pointer + * + * Sets fixed settings of each AIO substreams. + * This function need to call once at substream startup. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +int uniphier_aio_init(struct uniphier_aio_sub *sub) +{ + struct device *dev = &sub->aio->chip->pdev->dev; + struct regmap *r = sub->aio->chip->regmap; + + regmap_write(r, A2RBNMAPCTR0(sub->swm->rb.hw), + MAPCTR0_EN | sub->swm->rb.map); + regmap_write(r, A2CHNMAPCTR0(sub->swm->ch.hw), + MAPCTR0_EN | sub->swm->ch.map); + + switch (sub->swm->type) { + case PORT_TYPE_I2S: + case PORT_TYPE_SPDIF: + case PORT_TYPE_EVE: + if (sub->swm->dir == PORT_DIR_INPUT) { + regmap_write(r, A2IIFNMAPCTR0(sub->swm->iif.hw), + MAPCTR0_EN | sub->swm->iif.map); + regmap_write(r, A2IPORTNMAPCTR0(sub->swm->iport.hw), + MAPCTR0_EN | sub->swm->iport.map); + } else { + regmap_write(r, A2OIFNMAPCTR0(sub->swm->oif.hw), + MAPCTR0_EN | sub->swm->oif.map); + regmap_write(r, A2OPORTNMAPCTR0(sub->swm->oport.hw), + MAPCTR0_EN | sub->swm->oport.map); + } + break; + case PORT_TYPE_CONV: + regmap_write(r, A2OIFNMAPCTR0(sub->swm->oif.hw), + MAPCTR0_EN | sub->swm->oif.map); + regmap_write(r, A2OPORTNMAPCTR0(sub->swm->oport.hw), + MAPCTR0_EN | sub->swm->oport.map); + regmap_write(r, A2CHNMAPCTR0(sub->swm->och.hw), + MAPCTR0_EN | sub->swm->och.map); + regmap_write(r, A2IIFNMAPCTR0(sub->swm->iif.hw), + MAPCTR0_EN | sub->swm->iif.map); + break; + default: + dev_err(dev, "Unknown port type %d.\n", sub->swm->type); + return -EINVAL; + } + + return 0; +} + +/** + * uniphier_aio_port_reset - reset AIO port block + * @sub: the AIO substream pointer + * + * Resets the digital signal input/output port block of AIO. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +int uniphier_aio_port_reset(struct uniphier_aio_sub *sub) +{ + struct regmap *r = sub->aio->chip->regmap; + + if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_write(r, AOUTRSTCTR0, BIT(sub->swm->oport.map)); + regmap_write(r, AOUTRSTCTR1, BIT(sub->swm->oport.map)); + } else { + regmap_update_bits(r, IPORTMXRSTCTR(sub->swm->iport.map), + IPORTMXRSTCTR_RSTPI_MASK, + IPORTMXRSTCTR_RSTPI_RESET); + regmap_update_bits(r, IPORTMXRSTCTR(sub->swm->iport.map), + IPORTMXRSTCTR_RSTPI_MASK, + IPORTMXRSTCTR_RSTPI_RELEASE); + } + + return 0; +} + +/** + * uniphier_aio_port_set_param - set parameters of AIO port block + * @sub: the AIO substream pointer + * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM. + * This parameter has no effect if substream is I2S or PCM. + * @params: hardware parameters of ALSA + * + * Set suitable setting to input/output port block of AIO to process the + * specified in params. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +int uniphier_aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through, + const struct snd_pcm_hw_params *params) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 v; + + if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (pass_through) + v = OPORTMXCTR1_I2SLRSEL_LEFT; + else + v = OPORTMXCTR1_I2SLRSEL_I2S; + + v |= OPORTMXCTR1_OUTBITSEL_24 | + OPORTMXCTR1_FSSEL_48; + regmap_write(r, OPORTMXCTR1(sub->swm->oport.map), v); + + if (sub->swm->type == PORT_TYPE_EVE) + v = OPORTMXCTR2_ACLKSEL_A2PLL; + else + v = OPORTMXCTR2_ACLKSEL_A1; + + v |= OPORTMXCTR2_MSSEL_MASTER | + OPORTMXCTR2_EXTLSIFSSEL_36 | + OPORTMXCTR2_DACCKSEL_1_2; + regmap_write(r, OPORTMXCTR2(sub->swm->oport.map), v); + + if (pass_through) + v = OPORTMXCTR3_SRCSEL_STREAM | + OPORTMXCTR3_VALID_STREAM; + else + v = OPORTMXCTR3_SRCSEL_PCM | + OPORTMXCTR3_VALID_PCM; + + v |= OPORTMXCTR3_IECTHUR_IECOUT | + OPORTMXCTR3_PMSEL_PAUSE | + OPORTMXCTR3_PMSW_MUTE_OFF; + regmap_write(r, OPORTMXCTR3(sub->swm->oport.map), v); + } else { + regmap_write(r, IPORTMXCTR1(sub->swm->iport.map), + IPORTMXCTR1_LRSEL_I2S | IPORTMXCTR1_OUTBITSEL_24 | + IPORTMXCTR1_CHSEL_ALL | IPORTMXCTR1_FSSEL_48); + + v = IPORTMXCTR2_ACLKSEL_A1 | + IPORTMXCTR2_MSSEL_SLAVE | + IPORTMXCTR2_EXTLSIFSSEL_36 | + IPORTMXCTR2_DACCKSEL_1_2; + regmap_write(r, IPORTMXCTR2(sub->swm->iport.map), v); + + regmap_write(r, IPORTMXACLKSEL0EX(sub->swm->iport.map), + IPORTMXACLKSEL0EX_ACLKSEL0EX_INTERNAL); + regmap_write(r, IPORTMXEXNOE(sub->swm->iport.map), + IPORTMXEXNOE_PCMINOE_INPUT); + } + + return 0; +} + +/** + * uniphier_aio_port_set_enable - start or stop of AIO port block + * @sub: the AIO substream pointer + * @enable: zero to stop the block, otherwise to start + * + * Start or stop the signal input/output port block of AIO. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +int uniphier_aio_port_set_enable(struct uniphier_aio_sub *sub, int enable) +{ + struct regmap *r = sub->aio->chip->regmap; + + if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_write(r, OPORTMXPATH(sub->swm->oport.map), + sub->swm->oif.map); + + regmap_update_bits(r, OPORTMXMASK(sub->swm->oport.map), + OPORTMXMASK_IUDXMSK_MASK | + OPORTMXMASK_IUXCKMSK_MASK | + OPORTMXMASK_DXMSK_MASK | + OPORTMXMASK_XCKMSK_MASK, + OPORTMXMASK_IUDXMSK_OFF | + OPORTMXMASK_IUXCKMSK_OFF | + OPORTMXMASK_DXMSK_OFF | + OPORTMXMASK_XCKMSK_OFF); + + if (enable) + regmap_write(r, AOUTENCTR0, BIT(sub->swm->oport.map)); + else + regmap_write(r, AOUTENCTR1, BIT(sub->swm->oport.map)); + } else { + regmap_update_bits(r, IPORTMXMASK(sub->swm->iport.map), + IPORTMXMASK_IUXCKMSK_MASK | + IPORTMXMASK_XCKMSK_MASK, + IPORTMXMASK_IUXCKMSK_OFF | + IPORTMXMASK_XCKMSK_OFF); + + if (enable) + regmap_update_bits(r, + IPORTMXCTR2(sub->swm->iport.map), + IPORTMXCTR2_REQEN_MASK, + IPORTMXCTR2_REQEN_ENABLE); + else + regmap_update_bits(r, + IPORTMXCTR2(sub->swm->iport.map), + IPORTMXCTR2_REQEN_MASK, + IPORTMXCTR2_REQEN_DISABLE); + } + + return 0; +} + +/** + * uniphier_aio_if_set_param - set parameters of AIO DMA I/F block + * @sub: the AIO substream pointer + * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM. + * This parameter has no effect if substream is I2S or PCM. + * + * Set suitable setting to DMA interface block of AIO to process the + * specified in settings. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +int uniphier_aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 v; + + if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (pass_through) + v = PBOUTMXCTR0_ENDIAN_0123 | + PBOUTMXCTR0_MEMFMT_STREAM; + else + v = PBOUTMXCTR0_ENDIAN_3210 | + PBOUTMXCTR0_MEMFMT_2CH; + + regmap_write(r, PBOUTMXCTR0(sub->swm->oif.map), v); + regmap_write(r, PBOUTMXCTR1(sub->swm->oif.map), 0); + } else { + regmap_write(r, PBINMXCTR(sub->swm->iif.map), + PBINMXCTR_NCONNECT_CONNECT | + PBINMXCTR_INOUTSEL_IN | + (sub->swm->iport.map << PBINMXCTR_PBINSEL_SHIFT) | + PBINMXCTR_ENDIAN_3210 | + PBINMXCTR_MEMFMT_D0); + } + + return 0; +} + +/** + * uniphier_aio_oport_set_stream_type - set parameters of AIO port block + * @sub: the AIO substream pointer + * @pc: Pc type of IEC61937 + * + * Set special setting to output port block of AIO to output the stream + * via S/PDIF. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +int uniphier_aio_oport_set_stream_type(struct uniphier_aio_sub *sub, + enum IEC61937_PC pc) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 repet = 0, pause = OPORTMXPAUDAT_PAUSEPC_CMN; + + switch (pc) { + case IEC61937_PC_AC3: + repet = OPORTMXREPET_STRLENGTH_AC3 | + OPORTMXREPET_PMLENGTH_AC3; + pause |= OPORTMXPAUDAT_PAUSEPD_AC3; + break; + case IEC61937_PC_MPA: + repet = OPORTMXREPET_STRLENGTH_MPA | + OPORTMXREPET_PMLENGTH_MPA; + pause |= OPORTMXPAUDAT_PAUSEPD_MPA; + break; + case IEC61937_PC_MP3: + repet = OPORTMXREPET_STRLENGTH_MP3 | + OPORTMXREPET_PMLENGTH_MP3; + pause |= OPORTMXPAUDAT_PAUSEPD_MP3; + break; + case IEC61937_PC_DTS1: + repet = OPORTMXREPET_STRLENGTH_DTS1 | + OPORTMXREPET_PMLENGTH_DTS1; + pause |= OPORTMXPAUDAT_PAUSEPD_DTS1; + break; + case IEC61937_PC_DTS2: + repet = OPORTMXREPET_STRLENGTH_DTS2 | + OPORTMXREPET_PMLENGTH_DTS2; + pause |= OPORTMXPAUDAT_PAUSEPD_DTS2; + break; + case IEC61937_PC_DTS3: + repet = OPORTMXREPET_STRLENGTH_DTS3 | + OPORTMXREPET_PMLENGTH_DTS3; + pause |= OPORTMXPAUDAT_PAUSEPD_DTS3; + break; + case IEC61937_PC_AAC: + repet = OPORTMXREPET_STRLENGTH_AAC | + OPORTMXREPET_PMLENGTH_AAC; + pause |= OPORTMXPAUDAT_PAUSEPD_AAC; + break; + case IEC61937_PC_PAUSE: + /* Do nothing */ + break; + } + + regmap_write(r, OPORTMXREPET(sub->swm->oport.map), repet); + regmap_write(r, OPORTMXPAUDAT(sub->swm->oport.map), pause); + + return 0; +} + +/** + * uniphier_aio_srcport_reset - reset AIO SRC port block + * @sub: the AIO substream pointer + * + * Resets the digital signal input/output port with sampling rate converter + * block of AIO. + * This function has no effect if substream is not supported rate converter. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +int uniphier_aio_srcport_reset(struct uniphier_aio_sub *sub) +{ + struct regmap *r = sub->aio->chip->regmap; + + if (sub->substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + regmap_write(r, AOUTSRCRSTCTR0, BIT(sub->swm->oport.map)); + regmap_write(r, AOUTSRCRSTCTR1, BIT(sub->swm->oport.map)); + + return 0; +} + +/** + * uniphier_aio_srcport_set_param - set parameters of AIO SRC port block + * @sub: the AIO substream pointer + * @params: hardware parameters of ALSA + * + * Set suitable setting to input/output port with sampling rate converter + * block of AIO to process the specified in params. + * This function has no effect if substream is not supported rate converter. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +int uniphier_aio_srcport_set_param(struct uniphier_aio_sub *sub, + const struct snd_pcm_hw_params *params) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 v; + + if (sub->substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + regmap_write(r, OPORTMXSRC1CTR(sub->swm->oport.map), + OPORTMXSRC1CTR_THMODE_SRC | + OPORTMXSRC1CTR_SRCPATH_CALC | + OPORTMXSRC1CTR_SYNC_ASYNC | + OPORTMXSRC1CTR_FSIIPSEL_INNER | + OPORTMXSRC1CTR_FSISEL_ACLK); + + switch (params_rate(params)) { + default: + case 48000: + v = OPORTMXRATE_I_ACLKSEL_APLLA1 | + OPORTMXRATE_I_MCKSEL_36 | + OPORTMXRATE_I_FSSEL_48; + break; + case 44100: + v = OPORTMXRATE_I_ACLKSEL_APLLA2 | + OPORTMXRATE_I_MCKSEL_33 | + OPORTMXRATE_I_FSSEL_44_1; + break; + case 32000: + v = OPORTMXRATE_I_ACLKSEL_APLLA1 | + OPORTMXRATE_I_MCKSEL_36 | + OPORTMXRATE_I_FSSEL_32; + break; + } + + regmap_write(r, OPORTMXRATE_I(sub->swm->oport.map), + v | OPORTMXRATE_I_ACLKSRC_APLL | + OPORTMXRATE_I_LRCKSTP_STOP); + regmap_update_bits(r, OPORTMXRATE_I(sub->swm->oport.map), + OPORTMXRATE_I_LRCKSTP_MASK, + OPORTMXRATE_I_LRCKSTP_START); + + return 0; +} + +int uniphier_aio_srcif_set_param(struct uniphier_aio_sub *sub) +{ + struct regmap *r = sub->aio->chip->regmap; + + regmap_write(r, PBINMXCTR(sub->swm->iif.map), + PBINMXCTR_NCONNECT_CONNECT | + PBINMXCTR_INOUTSEL_OUT | + (sub->swm->oport.map << PBINMXCTR_PBINSEL_SHIFT) | + PBINMXCTR_ENDIAN_3210 | + PBINMXCTR_MEMFMT_D0); + + return 0; +} + +int uniphier_aio_srcch_set_param(struct uniphier_aio_sub *sub) +{ + struct regmap *r = sub->aio->chip->regmap; + + regmap_write(r, CDA2D_CHMXCTRL1(sub->swm->och.map), + CDA2D_CHMXCTRL1_INDSIZE_INFINITE); + + regmap_write(r, CDA2D_CHMXSRCAMODE(sub->swm->och.map), + CDA2D_CHMXAMODE_ENDIAN_3210 | + CDA2D_CHMXAMODE_AUPDT_FIX | + CDA2D_CHMXAMODE_TYPE_NORMAL); + + regmap_write(r, CDA2D_CHMXDSTAMODE(sub->swm->och.map), + CDA2D_CHMXAMODE_ENDIAN_3210 | + CDA2D_CHMXAMODE_AUPDT_INC | + CDA2D_CHMXAMODE_TYPE_RING | + (sub->swm->och.map << CDA2D_CHMXAMODE_RSSEL_SHIFT)); + + return 0; +} + +int uniphier_aio_srcch_set_enable(struct uniphier_aio_sub *sub, int enable) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 v; + + if (enable) + v = CDA2D_STRT0_STOP_START; + else + v = CDA2D_STRT0_STOP_STOP; + + regmap_write(r, CDA2D_STRT0, + v | BIT(sub->swm->och.map)); + + return 0; +} + +int uniphier_aio_ch_set_param(struct uniphier_aio_sub *sub) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 v; + + regmap_write(r, CDA2D_CHMXCTRL1(sub->swm->ch.map), + CDA2D_CHMXCTRL1_INDSIZE_INFINITE); + + v = CDA2D_CHMXAMODE_ENDIAN_3210 | + CDA2D_CHMXAMODE_AUPDT_INC | + CDA2D_CHMXAMODE_TYPE_NORMAL | + (sub->swm->rb.map << CDA2D_CHMXAMODE_RSSEL_SHIFT); + if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_write(r, CDA2D_CHMXSRCAMODE(sub->swm->ch.map), v); + else + regmap_write(r, CDA2D_CHMXDSTAMODE(sub->swm->ch.map), v); + + return 0; +} + +int uniphier_aio_ch_set_enable(struct uniphier_aio_sub *sub, int enable) +{ + struct regmap *r = sub->aio->chip->regmap; + + if (enable) { + regmap_write(r, CDA2D_STRT0, + CDA2D_STRT0_STOP_START | BIT(sub->swm->ch.map)); + + regmap_update_bits(r, INTRBIM(0), + BIT(sub->swm->rb.map), + BIT(sub->swm->rb.map)); + } else { + regmap_write(r, CDA2D_STRT0, + CDA2D_STRT0_STOP_STOP | BIT(sub->swm->ch.map)); + + regmap_update_bits(r, INTRBIM(0), + BIT(sub->swm->rb.map), + 0); + } + + return 0; +} + +static u64 uniphier_aio_rb_get_rp(struct uniphier_aio_sub *sub) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 pos_u, pos_l; + int i; + + regmap_write(r, CDA2D_RDPTRLOAD, + CDA2D_RDPTRLOAD_LSFLAG_STORE | BIT(sub->swm->rb.map)); + /* Wait for setup */ + for (i = 0; i < 6; i++) + regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &pos_l); + + regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &pos_l); + regmap_read(r, CDA2D_RBMXRDPTRU(sub->swm->rb.map), &pos_u); + pos_u = FIELD_GET(CDA2D_RBMXPTRU_PTRU_MASK, pos_u); + + return ((u64)pos_u << 32) | pos_l; +} + +static int uniphier_aio_rb_set_rp(struct uniphier_aio_sub *sub, u64 pos) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 tmp; + int i; + + regmap_write(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), (u32)pos); + regmap_write(r, CDA2D_RBMXRDPTRU(sub->swm->rb.map), (u32)(pos >> 32)); + regmap_write(r, CDA2D_RDPTRLOAD, BIT(sub->swm->rb.map)); + /* Wait for setup */ + for (i = 0; i < 6; i++) + regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &tmp); + + return 0; +} + +static u64 uniphier_aio_rb_get_wp(struct uniphier_aio_sub *sub) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 pos_u, pos_l; + int i; + + regmap_write(r, CDA2D_WRPTRLOAD, + CDA2D_WRPTRLOAD_LSFLAG_STORE | BIT(sub->swm->rb.map)); + /* Wait for setup */ + for (i = 0; i < 6; i++) + regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &pos_l); + + regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &pos_l); + regmap_read(r, CDA2D_RBMXWRPTRU(sub->swm->rb.map), &pos_u); + pos_u = FIELD_GET(CDA2D_RBMXPTRU_PTRU_MASK, pos_u); + + return ((u64)pos_u << 32) | pos_l; +} + +static int uniphier_aio_rb_set_wp(struct uniphier_aio_sub *sub, u64 pos) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 tmp; + int i; + + regmap_write(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), + lower_32_bits(pos)); + regmap_write(r, CDA2D_RBMXWRPTRU(sub->swm->rb.map), + upper_32_bits(pos)); + regmap_write(r, CDA2D_WRPTRLOAD, BIT(sub->swm->rb.map)); + /* Wait for setup */ + for (i = 0; i < 6; i++) + regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &tmp); + + return 0; +} + +int uniphier_aio_rb_set_buffer(struct uniphier_aio_sub *sub, + u64 start, u64 end) +{ + struct regmap *r = sub->aio->chip->regmap; + struct snd_pcm_runtime *runtime = sub->substream->runtime; + int byte_per_period = runtime->period_size * + runtime->channels * samples_to_bytes(runtime, 1); + + regmap_write(r, CDA2D_RBMXCNFG(sub->swm->rb.map), 0); + regmap_write(r, CDA2D_RBMXBGNADRS(sub->swm->rb.map), + lower_32_bits(start)); + regmap_write(r, CDA2D_RBMXBGNADRSU(sub->swm->rb.map), + upper_32_bits(start)); + regmap_write(r, CDA2D_RBMXENDADRS(sub->swm->rb.map), + lower_32_bits(end)); + regmap_write(r, CDA2D_RBMXENDADRSU(sub->swm->rb.map), + upper_32_bits(end)); + + regmap_write(r, CDA2D_RBADRSLOAD, BIT(sub->swm->rb.map)); + + uniphier_aio_rb_set_rp(sub, start); + uniphier_aio_rb_set_wp(sub, end - byte_per_period); + + regmap_write(r, CDA2D_RBMXBTH(sub->swm->rb.map), byte_per_period * 2); + regmap_write(r, CDA2D_RBMXRTH(sub->swm->rb.map), byte_per_period * 2); + + if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(r, CDA2D_RBMXIE(sub->swm->rb.map), + CDA2D_RBMXIX_SPACE, + CDA2D_RBMXIX_SPACE); + else + regmap_update_bits(r, CDA2D_RBMXIE(sub->swm->rb.map), + CDA2D_RBMXIX_REMAIN, + CDA2D_RBMXIX_REMAIN); + + sub->rd_offs = 0; + sub->wr_offs = 0; + + return 0; +} + +void uniphier_aio_rb_sync(struct uniphier_aio_sub *sub) +{ + struct snd_pcm_runtime *runtime = sub->substream->runtime; + u64 start = runtime->dma_addr; + int byte_per_period = runtime->period_size * + runtime->channels * samples_to_bytes(runtime, 1); + + if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + sub->rd_offs = uniphier_aio_rb_get_rp(sub) - start; + sub->wr_offs = sub->rd_offs - byte_per_period; + if (sub->rd_offs < byte_per_period) + sub->wr_offs += runtime->dma_bytes; + uniphier_aio_rb_set_wp(sub, sub->wr_offs + start); + } else { + sub->wr_offs = uniphier_aio_rb_get_wp(sub) - start; + sub->rd_offs = sub->wr_offs - byte_per_period; + if (sub->wr_offs < byte_per_period) + sub->rd_offs += runtime->dma_bytes; + uniphier_aio_rb_set_rp(sub, sub->rd_offs + start); + } +} + +int uniphier_aio_rb_clear_int(struct uniphier_aio_sub *sub) +{ + struct regmap *r = sub->aio->chip->regmap; + + if (sub->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_write(r, CDA2D_RBMXIR(sub->swm->rb.map), + CDA2D_RBMXIX_SPACE); + else + regmap_write(r, CDA2D_RBMXIR(sub->swm->rb.map), + CDA2D_RBMXIX_REMAIN); + + return 0; +} diff --git a/sound/soc/uniphier/aio-regctrl.h b/sound/soc/uniphier/aio-regctrl.h new file mode 100644 index 000000000000..d9dfc8e03b89 --- /dev/null +++ b/sound/soc/uniphier/aio-regctrl.h @@ -0,0 +1,495 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Socionext UniPhier AIO ALSA driver. + * + * Copyright (c) 2016-2017 Socionext Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#ifndef SND_UNIPHIER_AIO_REGCTRL_H__ +#define SND_UNIPHIER_AIO_REGCTRL_H__ + +#include <linux/bitops.h> + +#include "aio.h" + +/* SW view */ +#define A2CHNMAPCTR0(n) (0x00000 + 0x40 * (n)) +#define A2RBNMAPCTR0(n) (0x01000 + 0x40 * (n)) +#define A2IPORTNMAPCTR0(n) (0x02000 + 0x40 * (n)) +#define A2IPORTNMAPCTR1(n) (0x02004 + 0x40 * (n)) +#define A2IIFNMAPCTR0(n) (0x03000 + 0x40 * (n)) +#define A2OPORTNMAPCTR0(n) (0x04000 + 0x40 * (n)) +#define A2OPORTNMAPCTR1(n) (0x04004 + 0x40 * (n)) +#define A2OPORTNMAPCTR2(n) (0x04008 + 0x40 * (n)) +#define A2OIFNMAPCTR0(n) (0x05000 + 0x40 * (n)) +#define A2ATNMAPCTR0(n) (0x06000 + 0x40 * (n)) + +#define MAPCTR0_EN 0x80000000 + +/* CTL */ +#define A2APLLCTR0 0x07000 +#define A2APLLCTR0_APLLXPOW_MASK GENMASK(3, 0) +#define A2APLLCTR0_APLLXPOW_PWOFF (0x0 << 0) +#define A2APLLCTR0_APLLXPOW_PWON (0xf << 0) +#define A2APLLCTR1 0x07004 +#define A2APLLCTR1_APLL_MASK (A2APLLCTR1_APLLF2_MASK | \ + A2APLLCTR1_APLLA2_MASK | \ + A2APLLCTR1_APLLF1_MASK | \ + A2APLLCTR1_APLLA1_MASK) +#define A2APLLCTR1_APLLF2_MASK 0x00080808 +#define A2APLLCTR1_APLLF2_33MHZ 0x00000008 +#define A2APLLCTR1_APLLA2_MASK 0x00040404 +#define A2APLLCTR1_APLLA2_33MHZ 0x00000004 +#define A2APLLCTR1_APLLF1_MASK 0x00020202 +#define A2APLLCTR1_APLLF1_36MHZ 0x00000000 +#define A2APLLCTR1_APLLA1_MASK 0x00010101 +#define A2APLLCTR1_APLLA1_36MHZ 0x00000000 +#define A2EXMCLKSEL0 0x07030 +#define A2EXMCLKSEL0_EXMCLK_MASK GENMASK(2, 0) +#define A2EXMCLKSEL0_EXMCLK_OUTPUT (0x0 << 0) +#define A2EXMCLKSEL0_EXMCLK_INPUT (0x7 << 0) +#define A2SSIFSW 0x07050 +#define A2CH22_2CTR 0x07054 +#define A2AIOINPUTSEL 0x070e0 +#define A2AIOINPUTSEL_RXSEL_PCMI1_MASK GENMASK(2, 0) +#define A2AIOINPUTSEL_RXSEL_PCMI1_HDMIRX1 (0x2 << 0) +#define A2AIOINPUTSEL_RXSEL_PCMI2_MASK GENMASK(6, 4) +#define A2AIOINPUTSEL_RXSEL_PCMI2_SIF (0x7 << 4) +#define A2AIOINPUTSEL_RXSEL_PCMI3_MASK GENMASK(10, 8) +#define A2AIOINPUTSEL_RXSEL_PCMI3_EVEA (0x1 << 8) +#define A2AIOINPUTSEL_RXSEL_IECI1_MASK GENMASK(14, 12) +#define A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1 (0x2 << 12) +#define A2AIOINPUTSEL_RXSEL_MASK (A2AIOINPUTSEL_RXSEL_PCMI1_MASK | \ + A2AIOINPUTSEL_RXSEL_PCMI2_MASK | \ + A2AIOINPUTSEL_RXSEL_PCMI3_MASK | \ + A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1) + +/* INTC */ +#define INTCHIM(m) (0x9028 + 0x80 * (m)) +#define INTRBIM(m) (0x9030 + 0x80 * (m)) +#define INTCHID(m) (0xa028 + 0x80 * (m)) +#define INTRBID(m) (0xa030 + 0x80 * (m)) + +/* AIN(PCMINN) */ +#define IPORTMXCTR1(n) (0x22000 + 0x400 * (n)) +#define IPORTMXCTR1_LRSEL_MASK GENMASK(11, 10) +#define IPORTMXCTR1_LRSEL_RIGHT (0x0 << 10) +#define IPORTMXCTR1_LRSEL_LEFT (0x1 << 10) +#define IPORTMXCTR1_LRSEL_I2S (0x2 << 10) +#define IPORTMXCTR1_OUTBITSEL_MASK (0x800003 << 8) +#define IPORTMXCTR1_OUTBITSEL_32 (0x800000 << 8) +#define IPORTMXCTR1_OUTBITSEL_24 (0x000000 << 8) +#define IPORTMXCTR1_OUTBITSEL_20 (0x000001 << 8) +#define IPORTMXCTR1_OUTBITSEL_16 (0x000002 << 8) +#define IPORTMXCTR1_CHSEL_MASK GENMASK(6, 4) +#define IPORTMXCTR1_CHSEL_ALL (0x0 << 4) +#define IPORTMXCTR1_CHSEL_D0_D2 (0x1 << 4) +#define IPORTMXCTR1_CHSEL_D0 (0x2 << 4) +#define IPORTMXCTR1_CHSEL_D1 (0x3 << 4) +#define IPORTMXCTR1_CHSEL_D2 (0x4 << 4) +#define IPORTMXCTR1_CHSEL_DMIX (0x5 << 4) +#define IPORTMXCTR1_FSSEL_MASK GENMASK(3, 0) +#define IPORTMXCTR1_FSSEL_48 (0x0 << 0) +#define IPORTMXCTR1_FSSEL_96 (0x1 << 0) +#define IPORTMXCTR1_FSSEL_192 (0x2 << 0) +#define IPORTMXCTR1_FSSEL_32 (0x3 << 0) +#define IPORTMXCTR1_FSSEL_44_1 (0x4 << 0) +#define IPORTMXCTR1_FSSEL_88_2 (0x5 << 0) +#define IPORTMXCTR1_FSSEL_176_4 (0x6 << 0) +#define IPORTMXCTR1_FSSEL_16 (0x8 << 0) +#define IPORTMXCTR1_FSSEL_22_05 (0x9 << 0) +#define IPORTMXCTR1_FSSEL_24 (0xa << 0) +#define IPORTMXCTR1_FSSEL_8 (0xb << 0) +#define IPORTMXCTR1_FSSEL_11_025 (0xc << 0) +#define IPORTMXCTR1_FSSEL_12 (0xd << 0) +#define IPORTMXCTR2(n) (0x22004 + 0x400 * (n)) +#define IPORTMXCTR2_ACLKSEL_MASK GENMASK(19, 16) +#define IPORTMXCTR2_ACLKSEL_A1 (0x0 << 16) +#define IPORTMXCTR2_ACLKSEL_F1 (0x1 << 16) +#define IPORTMXCTR2_ACLKSEL_A2 (0x2 << 16) +#define IPORTMXCTR2_ACLKSEL_F2 (0x3 << 16) +#define IPORTMXCTR2_ACLKSEL_A2PLL (0x4 << 16) +#define IPORTMXCTR2_ACLKSEL_RX1 (0x5 << 16) +#define IPORTMXCTR2_ACLKSEL_RX2 (0x6 << 16) +#define IPORTMXCTR2_MSSEL_MASK BIT(15) +#define IPORTMXCTR2_MSSEL_SLAVE (0x0 << 15) +#define IPORTMXCTR2_MSSEL_MASTER (0x1 << 15) +#define IPORTMXCTR2_EXTLSIFSSEL_MASK BIT(14) +#define IPORTMXCTR2_EXTLSIFSSEL_36 (0x0 << 14) +#define IPORTMXCTR2_EXTLSIFSSEL_24 (0x1 << 14) +#define IPORTMXCTR2_DACCKSEL_MASK GENMASK(9, 8) +#define IPORTMXCTR2_DACCKSEL_1_2 (0x0 << 8) +#define IPORTMXCTR2_DACCKSEL_1_3 (0x1 << 8) +#define IPORTMXCTR2_DACCKSEL_1_1 (0x2 << 8) +#define IPORTMXCTR2_DACCKSEL_2_3 (0x3 << 8) +#define IPORTMXCTR2_REQEN_MASK BIT(0) +#define IPORTMXCTR2_REQEN_DISABLE (0x0 << 0) +#define IPORTMXCTR2_REQEN_ENABLE (0x1 << 0) +#define IPORTMXCNTCTR(n) (0x22010 + 0x400 * (n)) +#define IPORTMXCOUNTER(n) (0x22014 + 0x400 * (n)) +#define IPORTMXCNTMONI(n) (0x22018 + 0x400 * (n)) +#define IPORTMXACLKSEL0EX(n) (0x22020 + 0x400 * (n)) +#define IPORTMXACLKSEL0EX_ACLKSEL0EX_MASK GENMASK(3, 0) +#define IPORTMXACLKSEL0EX_ACLKSEL0EX_INTERNAL (0x0 << 0) +#define IPORTMXACLKSEL0EX_ACLKSEL0EX_EXTERNAL (0xf << 0) +#define IPORTMXEXNOE(n) (0x22070 + 0x400 * (n)) +#define IPORTMXEXNOE_PCMINOE_MASK BIT(0) +#define IPORTMXEXNOE_PCMINOE_OUTPUT (0x0 << 0) +#define IPORTMXEXNOE_PCMINOE_INPUT (0x1 << 0) +#define IPORTMXMASK(n) (0x22078 + 0x400 * (n)) +#define IPORTMXMASK_IUXCKMSK_MASK GENMASK(18, 16) +#define IPORTMXMASK_IUXCKMSK_ON (0x0 << 16) +#define IPORTMXMASK_IUXCKMSK_OFF (0x7 << 16) +#define IPORTMXMASK_XCKMSK_MASK GENMASK(2, 0) +#define IPORTMXMASK_XCKMSK_ON (0x0 << 0) +#define IPORTMXMASK_XCKMSK_OFF (0x7 << 0) +#define IPORTMXRSTCTR(n) (0x2207c + 0x400 * (n)) +#define IPORTMXRSTCTR_RSTPI_MASK BIT(7) +#define IPORTMXRSTCTR_RSTPI_RELEASE (0x0 << 7) +#define IPORTMXRSTCTR_RSTPI_RESET (0x1 << 7) + +/* AIN(PBinMX) */ +#define PBINMXCTR(n) (0x20200 + 0x40 * (n)) +#define PBINMXCTR_NCONNECT_MASK BIT(15) +#define PBINMXCTR_NCONNECT_CONNECT (0x0 << 15) +#define PBINMXCTR_NCONNECT_DISCONNECT (0x1 << 15) +#define PBINMXCTR_INOUTSEL_MASK BIT(14) +#define PBINMXCTR_INOUTSEL_IN (0x0 << 14) +#define PBINMXCTR_INOUTSEL_OUT (0x1 << 14) +#define PBINMXCTR_PBINSEL_SHIFT (8) +#define PBINMXCTR_ENDIAN_MASK GENMASK(5, 4) +#define PBINMXCTR_ENDIAN_3210 (0x0 << 4) +#define PBINMXCTR_ENDIAN_0123 (0x1 << 4) +#define PBINMXCTR_ENDIAN_1032 (0x2 << 4) +#define PBINMXCTR_ENDIAN_2301 (0x3 << 4) +#define PBINMXCTR_MEMFMT_MASK GENMASK(3, 0) +#define PBINMXCTR_MEMFMT_D0 (0x0 << 0) +#define PBINMXCTR_MEMFMT_5_1CH_DMIX (0x1 << 0) +#define PBINMXCTR_MEMFMT_6CH (0x2 << 0) +#define PBINMXCTR_MEMFMT_4CH (0x3 << 0) +#define PBINMXCTR_MEMFMT_DMIX (0x4 << 0) +#define PBINMXCTR_MEMFMT_1CH (0x5 << 0) +#define PBINMXCTR_MEMFMT_16LR (0x6 << 0) +#define PBINMXCTR_MEMFMT_7_1CH (0x7 << 0) +#define PBINMXCTR_MEMFMT_7_1CH_DMIX (0x8 << 0) +#define PBINMXCTR_MEMFMT_STREAM (0xf << 0) +#define PBINMXPAUSECTR0(n) (0x20204 + 0x40 * (n)) +#define PBINMXPAUSECTR1(n) (0x20208 + 0x40 * (n)) + +/* AOUT */ +#define AOUTENCTR0 0x40040 +#define AOUTENCTR1 0x40044 +#define AOUTENCTR2 0x40048 +#define AOUTRSTCTR0 0x40060 +#define AOUTRSTCTR1 0x40064 +#define AOUTRSTCTR2 0x40068 +#define AOUTSRCRSTCTR0 0x400c0 +#define AOUTSRCRSTCTR1 0x400c4 +#define AOUTSRCRSTCTR2 0x400c8 + +/* AOUT(PCMOUTN) */ +#define OPORTMXCTR1(n) (0x42000 + 0x400 * (n)) +#define OPORTMXCTR1_I2SLRSEL_MASK (0x11 << 10) +#define OPORTMXCTR1_I2SLRSEL_RIGHT (0x00 << 10) +#define OPORTMXCTR1_I2SLRSEL_LEFT (0x01 << 10) +#define OPORTMXCTR1_I2SLRSEL_I2S (0x11 << 10) +#define OPORTMXCTR1_OUTBITSEL_MASK (0x800003 << 8) +#define OPORTMXCTR1_OUTBITSEL_32 (0x800000 << 8) +#define OPORTMXCTR1_OUTBITSEL_24 (0x000000 << 8) +#define OPORTMXCTR1_OUTBITSEL_20 (0x000001 << 8) +#define OPORTMXCTR1_OUTBITSEL_16 (0x000002 << 8) +#define OPORTMXCTR1_FSSEL_MASK GENMASK(3, 0) +#define OPORTMXCTR1_FSSEL_48 (0x0 << 0) +#define OPORTMXCTR1_FSSEL_96 (0x1 << 0) +#define OPORTMXCTR1_FSSEL_192 (0x2 << 0) +#define OPORTMXCTR1_FSSEL_32 (0x3 << 0) +#define OPORTMXCTR1_FSSEL_44_1 (0x4 << 0) +#define OPORTMXCTR1_FSSEL_88_2 (0x5 << 0) +#define OPORTMXCTR1_FSSEL_176_4 (0x6 << 0) +#define OPORTMXCTR1_FSSEL_16 (0x8 << 0) +#define OPORTMXCTR1_FSSEL_22_05 (0x9 << 0) +#define OPORTMXCTR1_FSSEL_24 (0xa << 0) +#define OPORTMXCTR1_FSSEL_8 (0xb << 0) +#define OPORTMXCTR1_FSSEL_11_025 (0xc << 0) +#define OPORTMXCTR1_FSSEL_12 (0xd << 0) +#define OPORTMXCTR2(n) (0x42004 + 0x400 * (n)) +#define OPORTMXCTR2_ACLKSEL_MASK GENMASK(19, 16) +#define OPORTMXCTR2_ACLKSEL_A1 (0x0 << 16) +#define OPORTMXCTR2_ACLKSEL_F1 (0x1 << 16) +#define OPORTMXCTR2_ACLKSEL_A2 (0x2 << 16) +#define OPORTMXCTR2_ACLKSEL_F2 (0x3 << 16) +#define OPORTMXCTR2_ACLKSEL_A2PLL (0x4 << 16) +#define OPORTMXCTR2_ACLKSEL_RX1 (0x5 << 16) +#define OPORTMXCTR2_ACLKSEL_RX2 (0x6 << 16) +#define OPORTMXCTR2_MSSEL_MASK BIT(15) +#define OPORTMXCTR2_MSSEL_SLAVE (0x0 << 15) +#define OPORTMXCTR2_MSSEL_MASTER (0x1 << 15) +#define OPORTMXCTR2_EXTLSIFSSEL_MASK BIT(14) +#define OPORTMXCTR2_EXTLSIFSSEL_36 (0x0 << 14) +#define OPORTMXCTR2_EXTLSIFSSEL_24 (0x1 << 14) +#define OPORTMXCTR2_DACCKSEL_MASK GENMASK(9, 8) +#define OPORTMXCTR2_DACCKSEL_1_2 (0x0 << 8) +#define OPORTMXCTR2_DACCKSEL_1_3 (0x1 << 8) +#define OPORTMXCTR2_DACCKSEL_1_1 (0x2 << 8) +#define OPORTMXCTR2_DACCKSEL_2_3 (0x3 << 8) +#define OPORTMXCTR3(n) (0x42008 + 0x400 * (n)) +#define OPORTMXCTR3_IECTHUR_MASK BIT(19) +#define OPORTMXCTR3_IECTHUR_IECOUT (0x0 << 19) +#define OPORTMXCTR3_IECTHUR_IECIN (0x1 << 19) +#define OPORTMXCTR3_SRCSEL_MASK GENMASK(18, 16) +#define OPORTMXCTR3_SRCSEL_PCM (0x0 << 16) +#define OPORTMXCTR3_SRCSEL_STREAM (0x1 << 16) +#define OPORTMXCTR3_SRCSEL_CDDTS (0x2 << 16) +#define OPORTMXCTR3_VALID_MASK BIT(12) +#define OPORTMXCTR3_VALID_PCM (0x0 << 12) +#define OPORTMXCTR3_VALID_STREAM (0x1 << 12) +#define OPORTMXCTR3_PMSEL_MASK BIT(3) +#define OPORTMXCTR3_PMSEL_MUTE (0x0 << 3) +#define OPORTMXCTR3_PMSEL_PAUSE (0x1 << 3) +#define OPORTMXCTR3_PMSW_MASK BIT(2) +#define OPORTMXCTR3_PMSW_MUTE_OFF (0x0 << 2) +#define OPORTMXCTR3_PMSW_MUTE_ON (0x1 << 2) +#define OPORTMXSRC1CTR(n) (0x4200c + 0x400 * (n)) +#define OPORTMXSRC1CTR_FSIIPNUM_SHIFT (24) +#define OPORTMXSRC1CTR_THMODE_MASK BIT(23) +#define OPORTMXSRC1CTR_THMODE_SRC (0x0 << 23) +#define OPORTMXSRC1CTR_THMODE_BYPASS (0x1 << 23) +#define OPORTMXSRC1CTR_LOCK_MASK BIT(16) +#define OPORTMXSRC1CTR_LOCK_UNLOCK (0x0 << 16) +#define OPORTMXSRC1CTR_LOCK_LOCK (0x1 << 16) +#define OPORTMXSRC1CTR_SRCPATH_MASK BIT(15) +#define OPORTMXSRC1CTR_SRCPATH_BYPASS (0x0 << 15) +#define OPORTMXSRC1CTR_SRCPATH_CALC (0x1 << 15) +#define OPORTMXSRC1CTR_SYNC_MASK BIT(14) +#define OPORTMXSRC1CTR_SYNC_ASYNC (0x0 << 14) +#define OPORTMXSRC1CTR_SYNC_SYNC (0x1 << 14) +#define OPORTMXSRC1CTR_FSOCK_MASK GENMASK(11, 10) +#define OPORTMXSRC1CTR_FSOCK_44_1 (0x0 << 10) +#define OPORTMXSRC1CTR_FSOCK_48 (0x1 << 10) +#define OPORTMXSRC1CTR_FSOCK_32 (0x2 << 10) +#define OPORTMXSRC1CTR_FSICK_MASK GENMASK(9, 8) +#define OPORTMXSRC1CTR_FSICK_44_1 (0x0 << 8) +#define OPORTMXSRC1CTR_FSICK_48 (0x1 << 8) +#define OPORTMXSRC1CTR_FSICK_32 (0x2 << 8) +#define OPORTMXSRC1CTR_FSIIPSEL_MASK GENMASK(5, 4) +#define OPORTMXSRC1CTR_FSIIPSEL_INNER (0x0 << 4) +#define OPORTMXSRC1CTR_FSIIPSEL_OUTER (0x1 << 4) +#define OPORTMXSRC1CTR_FSISEL_MASK GENMASK(3, 0) +#define OPORTMXSRC1CTR_FSISEL_ACLK (0x0 << 0) +#define OPORTMXSRC1CTR_FSISEL_DD (0x1 << 0) +#define OPORTMXDSDMUTEDAT(n) (0x42020 + 0x400 * (n)) +#define OPORTMXDXDFREQMODE(n) (0x42024 + 0x400 * (n)) +#define OPORTMXDSDSEL(n) (0x42028 + 0x400 * (n)) +#define OPORTMXDSDPORT(n) (0x4202c + 0x400 * (n)) +#define OPORTMXACLKSEL0EX(n) (0x42030 + 0x400 * (n)) +#define OPORTMXPATH(n) (0x42040 + 0x400 * (n)) +#define OPORTMXSYNC(n) (0x42044 + 0x400 * (n)) +#define OPORTMXREPET(n) (0x42050 + 0x400 * (n)) +#define OPORTMXREPET_STRLENGTH_AC3 SBF_(IEC61937_FRM_STR_AC3, 16) +#define OPORTMXREPET_STRLENGTH_MPA SBF_(IEC61937_FRM_STR_MPA, 16) +#define OPORTMXREPET_STRLENGTH_MP3 SBF_(IEC61937_FRM_STR_MP3, 16) +#define OPORTMXREPET_STRLENGTH_DTS1 SBF_(IEC61937_FRM_STR_DTS1, 16) +#define OPORTMXREPET_STRLENGTH_DTS2 SBF_(IEC61937_FRM_STR_DTS2, 16) +#define OPORTMXREPET_STRLENGTH_DTS3 SBF_(IEC61937_FRM_STR_DTS3, 16) +#define OPORTMXREPET_STRLENGTH_AAC SBF_(IEC61937_FRM_STR_AAC, 16) +#define OPORTMXREPET_PMLENGTH_AC3 SBF_(IEC61937_FRM_PAU_AC3, 0) +#define OPORTMXREPET_PMLENGTH_MPA SBF_(IEC61937_FRM_PAU_MPA, 0) +#define OPORTMXREPET_PMLENGTH_MP3 SBF_(IEC61937_FRM_PAU_MP3, 0) +#define OPORTMXREPET_PMLENGTH_DTS1 SBF_(IEC61937_FRM_PAU_DTS1, 0) +#define OPORTMXREPET_PMLENGTH_DTS2 SBF_(IEC61937_FRM_PAU_DTS2, 0) +#define OPORTMXREPET_PMLENGTH_DTS3 SBF_(IEC61937_FRM_PAU_DTS3, 0) +#define OPORTMXREPET_PMLENGTH_AAC SBF_(IEC61937_FRM_PAU_AAC, 0) +#define OPORTMXPAUDAT(n) (0x42054 + 0x400 * (n)) +#define OPORTMXPAUDAT_PAUSEPC_CMN (IEC61937_PC_PAUSE << 16) +#define OPORTMXPAUDAT_PAUSEPD_AC3 (IEC61937_FRM_PAU_AC3 * 4) +#define OPORTMXPAUDAT_PAUSEPD_MPA (IEC61937_FRM_PAU_MPA * 4) +#define OPORTMXPAUDAT_PAUSEPD_MP3 (IEC61937_FRM_PAU_MP3 * 4) +#define OPORTMXPAUDAT_PAUSEPD_DTS1 (IEC61937_FRM_PAU_DTS1 * 4) +#define OPORTMXPAUDAT_PAUSEPD_DTS2 (IEC61937_FRM_PAU_DTS2 * 4) +#define OPORTMXPAUDAT_PAUSEPD_DTS3 (IEC61937_FRM_PAU_DTS3 * 4) +#define OPORTMXPAUDAT_PAUSEPD_AAC (IEC61937_FRM_PAU_AAC * 4) +#define OPORTMXRATE_I(n) (0x420e4 + 0x400 * (n)) +#define OPORTMXRATE_I_EQU_MASK BIT(31) +#define OPORTMXRATE_I_EQU_NOTEQUAL (0x0 << 31) +#define OPORTMXRATE_I_EQU_EQUAL (0x1 << 31) +#define OPORTMXRATE_I_SRCBPMD_MASK BIT(29) +#define OPORTMXRATE_I_SRCBPMD_BYPASS (0x0 << 29) +#define OPORTMXRATE_I_SRCBPMD_SRC (0x1 << 29) +#define OPORTMXRATE_I_LRCKSTP_MASK BIT(24) +#define OPORTMXRATE_I_LRCKSTP_START (0x0 << 24) +#define OPORTMXRATE_I_LRCKSTP_STOP (0x1 << 24) +#define OPORTMXRATE_I_ACLKSRC_MASK GENMASK(15, 12) +#define OPORTMXRATE_I_ACLKSRC_APLL (0x0 << 12) +#define OPORTMXRATE_I_ACLKSRC_USB (0x1 << 12) +#define OPORTMXRATE_I_ACLKSRC_HSC (0x3 << 12) +/* if OPORTMXRATE_I_ACLKSRC_APLL */ +#define OPORTMXRATE_I_ACLKSEL_MASK GENMASK(11, 8) +#define OPORTMXRATE_I_ACLKSEL_APLLA1 (0x0 << 8) +#define OPORTMXRATE_I_ACLKSEL_APLLF1 (0x1 << 8) +#define OPORTMXRATE_I_ACLKSEL_APLLA2 (0x2 << 8) +#define OPORTMXRATE_I_ACLKSEL_APLLF2 (0x3 << 8) +#define OPORTMXRATE_I_ACLKSEL_APLL (0x4 << 8) +#define OPORTMXRATE_I_ACLKSEL_HDMI1 (0x5 << 8) +#define OPORTMXRATE_I_ACLKSEL_HDMI2 (0x6 << 8) +#define OPORTMXRATE_I_ACLKSEL_AI1ADCCK (0xc << 8) +#define OPORTMXRATE_I_ACLKSEL_AI2ADCCK (0xd << 8) +#define OPORTMXRATE_I_ACLKSEL_AI3ADCCK (0xe << 8) +#define OPORTMXRATE_I_MCKSEL_MASK GENMASK(7, 4) +#define OPORTMXRATE_I_MCKSEL_36 (0x0 << 4) +#define OPORTMXRATE_I_MCKSEL_33 (0x1 << 4) +#define OPORTMXRATE_I_MCKSEL_HSC27 (0xb << 4) +#define OPORTMXRATE_I_FSSEL_MASK GENMASK(3, 0) +#define OPORTMXRATE_I_FSSEL_48 (0x0 << 0) +#define OPORTMXRATE_I_FSSEL_96 (0x1 << 0) +#define OPORTMXRATE_I_FSSEL_192 (0x2 << 0) +#define OPORTMXRATE_I_FSSEL_32 (0x3 << 0) +#define OPORTMXRATE_I_FSSEL_44_1 (0x4 << 0) +#define OPORTMXRATE_I_FSSEL_88_2 (0x5 << 0) +#define OPORTMXRATE_I_FSSEL_176_4 (0x6 << 0) +#define OPORTMXRATE_I_FSSEL_16 (0x8 << 0) +#define OPORTMXRATE_I_FSSEL_22_05 (0x9 << 0) +#define OPORTMXRATE_I_FSSEL_24 (0xa << 0) +#define OPORTMXRATE_I_FSSEL_8 (0xb << 0) +#define OPORTMXRATE_I_FSSEL_11_025 (0xc << 0) +#define OPORTMXRATE_I_FSSEL_12 (0xd << 0) +#define OPORTMXEXNOE(n) (0x420f0 + 0x400 * (n)) +#define OPORTMXMASK(n) (0x420f8 + 0x400 * (n)) +#define OPORTMXMASK_IUDXMSK_MASK GENMASK(28, 24) +#define OPORTMXMASK_IUDXMSK_ON (0x00 << 24) +#define OPORTMXMASK_IUDXMSK_OFF (0x1f << 24) +#define OPORTMXMASK_IUXCKMSK_MASK GENMASK(18, 16) +#define OPORTMXMASK_IUXCKMSK_ON (0x0 << 16) +#define OPORTMXMASK_IUXCKMSK_OFF (0x7 << 16) +#define OPORTMXMASK_DXMSK_MASK GENMASK(12, 8) +#define OPORTMXMASK_DXMSK_ON (0x00 << 8) +#define OPORTMXMASK_DXMSK_OFF (0x1f << 8) +#define OPORTMXMASK_XCKMSK_MASK GENMASK(2, 0) +#define OPORTMXMASK_XCKMSK_ON (0x0 << 0) +#define OPORTMXMASK_XCKMSK_OFF (0x7 << 0) +#define OPORTMXDEBUG(n) (0x420fc + 0x400 * (n)) +#define OPORTMXT0RSTCTR(n) (0x4211c + 0x400 * (n)) +#define OPORTMXT1RSTCTR(n) (0x4213c + 0x400 * (n)) +#define OPORTMXT2RSTCTR(n) (0x4215c + 0x400 * (n)) +#define OPORTMXT3RSTCTR(n) (0x4217c + 0x400 * (n)) +#define OPORTMXT4RSTCTR(n) (0x4219c + 0x400 * (n)) + +#define SBF_(frame, shift) (((frame) * 2 - 1) << shift) + +/* AOUT(PBoutMX) */ +#define PBOUTMXCTR0(n) (0x40200 + 0x40 * (n)) +#define PBOUTMXCTR0_ENDIAN_MASK GENMASK(5, 4) +#define PBOUTMXCTR0_ENDIAN_3210 (0x0 << 4) +#define PBOUTMXCTR0_ENDIAN_0123 (0x1 << 4) +#define PBOUTMXCTR0_ENDIAN_1032 (0x2 << 4) +#define PBOUTMXCTR0_ENDIAN_2301 (0x3 << 4) +#define PBOUTMXCTR0_MEMFMT_MASK GENMASK(3, 0) +#define PBOUTMXCTR0_MEMFMT_10CH (0x0 << 0) +#define PBOUTMXCTR0_MEMFMT_8CH (0x1 << 0) +#define PBOUTMXCTR0_MEMFMT_6CH (0x2 << 0) +#define PBOUTMXCTR0_MEMFMT_4CH (0x3 << 0) +#define PBOUTMXCTR0_MEMFMT_2CH (0x4 << 0) +#define PBOUTMXCTR0_MEMFMT_STREAM (0x5 << 0) +#define PBOUTMXCTR0_MEMFMT_1CH (0x6 << 0) +#define PBOUTMXCTR1(n) (0x40204 + 0x40 * (n)) +#define PBOUTMXINTCTR(n) (0x40208 + 0x40 * (n)) + +/* A2D(subsystem) */ +#define CDA2D_STRT0 0x10000 +#define CDA2D_STRT0_STOP_MASK BIT(31) +#define CDA2D_STRT0_STOP_START (0x0 << 31) +#define CDA2D_STRT0_STOP_STOP (0x1 << 31) +#define CDA2D_STAT0 0x10020 +#define CDA2D_TEST 0x100a0 +#define CDA2D_TEST_DDR_MODE_MASK GENMASK(3, 2) +#define CDA2D_TEST_DDR_MODE_EXTON0 (0x0 << 2) +#define CDA2D_TEST_DDR_MODE_EXTOFF1 (0x3 << 2) +#define CDA2D_STRTADRSLOAD 0x100b0 + +#define CDA2D_CHMXCTRL1(n) (0x12000 + 0x80 * (n)) +#define CDA2D_CHMXCTRL1_INDSIZE_MASK BIT(0) +#define CDA2D_CHMXCTRL1_INDSIZE_FINITE (0x0 << 0) +#define CDA2D_CHMXCTRL1_INDSIZE_INFINITE (0x1 << 0) +#define CDA2D_CHMXCTRL2(n) (0x12004 + 0x80 * (n)) +#define CDA2D_CHMXSRCAMODE(n) (0x12020 + 0x80 * (n)) +#define CDA2D_CHMXDSTAMODE(n) (0x12024 + 0x80 * (n)) +#define CDA2D_CHMXAMODE_ENDIAN_MASK GENMASK(17, 16) +#define CDA2D_CHMXAMODE_ENDIAN_3210 (0x0 << 16) +#define CDA2D_CHMXAMODE_ENDIAN_0123 (0x1 << 16) +#define CDA2D_CHMXAMODE_ENDIAN_1032 (0x2 << 16) +#define CDA2D_CHMXAMODE_ENDIAN_2301 (0x3 << 16) +#define CDA2D_CHMXAMODE_RSSEL_SHIFT (8) +#define CDA2D_CHMXAMODE_AUPDT_MASK GENMASK(5, 4) +#define CDA2D_CHMXAMODE_AUPDT_INC (0x0 << 4) +#define CDA2D_CHMXAMODE_AUPDT_FIX (0x2 << 4) +#define CDA2D_CHMXAMODE_TYPE_MASK GENMASK(3, 2) +#define CDA2D_CHMXAMODE_TYPE_NORMAL (0x0 << 2) +#define CDA2D_CHMXAMODE_TYPE_RING (0x1 << 2) +#define CDA2D_CHMXSRCSTRTADRS(n) (0x12030 + 0x80 * (n)) +#define CDA2D_CHMXSRCSTRTADRSU(n) (0x12034 + 0x80 * (n)) +#define CDA2D_CHMXDSTSTRTADRS(n) (0x12038 + 0x80 * (n)) +#define CDA2D_CHMXDSTSTRTADRSU(n) (0x1203c + 0x80 * (n)) + +/* A2D(ring buffer) */ +#define CDA2D_RBFLUSH0 0x10040 +#define CDA2D_RBADRSLOAD 0x100b4 +#define CDA2D_RDPTRLOAD 0x100b8 +#define CDA2D_RDPTRLOAD_LSFLAG_LOAD (0x0 << 31) +#define CDA2D_RDPTRLOAD_LSFLAG_STORE (0x1 << 31) +#define CDA2D_WRPTRLOAD 0x100bc +#define CDA2D_WRPTRLOAD_LSFLAG_LOAD (0x0 << 31) +#define CDA2D_WRPTRLOAD_LSFLAG_STORE (0x1 << 31) + +#define CDA2D_RBMXBGNADRS(n) (0x14000 + 0x80 * (n)) +#define CDA2D_RBMXBGNADRSU(n) (0x14004 + 0x80 * (n)) +#define CDA2D_RBMXENDADRS(n) (0x14008 + 0x80 * (n)) +#define CDA2D_RBMXENDADRSU(n) (0x1400c + 0x80 * (n)) +#define CDA2D_RBMXBTH(n) (0x14038 + 0x80 * (n)) +#define CDA2D_RBMXRTH(n) (0x1403c + 0x80 * (n)) +#define CDA2D_RBMXRDPTR(n) (0x14020 + 0x80 * (n)) +#define CDA2D_RBMXRDPTRU(n) (0x14024 + 0x80 * (n)) +#define CDA2D_RBMXWRPTR(n) (0x14028 + 0x80 * (n)) +#define CDA2D_RBMXWRPTRU(n) (0x1402c + 0x80 * (n)) +#define CDA2D_RBMXPTRU_PTRU_MASK GENMASK(1, 0) +#define CDA2D_RBMXCNFG(n) (0x14030 + 0x80 * (n)) +#define CDA2D_RBMXIR(n) (0x14014 + 0x80 * (n)) +#define CDA2D_RBMXIE(n) (0x14018 + 0x80 * (n)) +#define CDA2D_RBMXID(n) (0x1401c + 0x80 * (n)) +#define CDA2D_RBMXIX_SPACE BIT(3) +#define CDA2D_RBMXIX_REMAIN BIT(4) + +int uniphier_aio_chip_init(struct uniphier_aio_chip *chip); +int uniphier_aio_init(struct uniphier_aio_sub *sub); +int uniphier_aio_port_reset(struct uniphier_aio_sub *sub); +int uniphier_aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through, + const struct snd_pcm_hw_params *params); +int uniphier_aio_port_set_enable(struct uniphier_aio_sub *sub, int enable); +int uniphier_aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through); +int uniphier_aio_oport_set_stream_type(struct uniphier_aio_sub *sub, + enum IEC61937_PC iec61937_pc); +int uniphier_aio_srcif_set_param(struct uniphier_aio_sub *sub); +int uniphier_aio_srcch_set_param(struct uniphier_aio_sub *sub); +int uniphier_aio_srcch_set_enable(struct uniphier_aio_sub *sub, int enable); +int uniphier_aio_srcport_reset(struct uniphier_aio_sub *sub); +int uniphier_aio_srcport_set_param(struct uniphier_aio_sub *sub, + const struct snd_pcm_hw_params *params); +int uniphier_aio_ch_set_param(struct uniphier_aio_sub *sub); +int uniphier_aio_ch_set_enable(struct uniphier_aio_sub *sub, int enable); +int uniphier_aio_rb_set_buffer(struct uniphier_aio_sub *sub, + u64 start, u64 end); +void uniphier_aio_rb_sync(struct uniphier_aio_sub *sub); +int uniphier_aio_rb_clear_int(struct uniphier_aio_sub *sub); + +#endif /* SND_UNIPHIER_AIO_REGCTRL_H__ */ diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h new file mode 100644 index 000000000000..542467688821 --- /dev/null +++ b/sound/soc/uniphier/aio.h @@ -0,0 +1,261 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Socionext UniPhier AIO ALSA driver. + * + * Copyright (c) 2016-2017 Socionext Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#ifndef SND_UNIPHIER_AIO_H__ +#define SND_UNIPHIER_AIO_H__ + +#include <linux/spinlock.h> +#include <linux/types.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +struct platform_device; + +enum ID_PORT_TYPE { + PORT_TYPE_UNKNOWN, + PORT_TYPE_I2S, + PORT_TYPE_SPDIF, + PORT_TYPE_EVE, + PORT_TYPE_CONV, +}; + +enum ID_PORT_DIR { + PORT_DIR_UNKNOWN, + PORT_DIR_INPUT, + PORT_DIR_OUTPUT, +}; + +enum IEC61937_PC { + IEC61937_PC_AC3 = 0x0001, + IEC61937_PC_PAUSE = 0x0003, + IEC61937_PC_MPA = 0x0004, + IEC61937_PC_MP3 = 0x0005, + IEC61937_PC_DTS1 = 0x000b, + IEC61937_PC_DTS2 = 0x000c, + IEC61937_PC_DTS3 = 0x000d, + IEC61937_PC_AAC = 0x0007, +}; + +/* IEC61937 Repetition period of data-burst in IEC60958 frames */ +#define IEC61937_FRM_STR_AC3 1536 +#define IEC61937_FRM_STR_MPA 1152 +#define IEC61937_FRM_STR_MP3 1152 +#define IEC61937_FRM_STR_DTS1 512 +#define IEC61937_FRM_STR_DTS2 1024 +#define IEC61937_FRM_STR_DTS3 2048 +#define IEC61937_FRM_STR_AAC 1024 + +/* IEC61937 Repetition period of Pause data-burst in IEC60958 frames */ +#define IEC61937_FRM_PAU_AC3 3 +#define IEC61937_FRM_PAU_MPA 32 +#define IEC61937_FRM_PAU_MP3 32 +#define IEC61937_FRM_PAU_DTS1 3 +#define IEC61937_FRM_PAU_DTS2 3 +#define IEC61937_FRM_PAU_DTS3 3 +#define IEC61937_FRM_PAU_AAC 32 + +#define AUD_HW_PCMIN1 0 +#define AUD_HW_PCMIN2 1 +#define AUD_HW_PCMIN3 2 +#define AUD_HW_IECIN1 3 +#define AUD_HW_DIECIN1 4 + +#define AUD_NAME_PCMIN1 "aio-pcmin1" +#define AUD_NAME_PCMIN2 "aio-pcmin2" +#define AUD_NAME_PCMIN3 "aio-pcmin3" +#define AUD_NAME_IECIN1 "aio-iecin1" +#define AUD_NAME_DIECIN1 "aio-diecin1" + +#define AUD_HW_HPCMOUT1 0 +#define AUD_HW_PCMOUT1 1 +#define AUD_HW_PCMOUT2 2 +#define AUD_HW_PCMOUT3 3 +#define AUD_HW_EPCMOUT1 4 +#define AUD_HW_EPCMOUT2 5 +#define AUD_HW_EPCMOUT3 6 +#define AUD_HW_EPCMOUT6 9 +#define AUD_HW_HIECOUT1 10 +#define AUD_HW_IECOUT1 11 +#define AUD_HW_CMASTER 31 + +#define AUD_NAME_HPCMOUT1 "aio-hpcmout1" +#define AUD_NAME_PCMOUT1 "aio-pcmout1" +#define AUD_NAME_PCMOUT2 "aio-pcmout2" +#define AUD_NAME_PCMOUT3 "aio-pcmout3" +#define AUD_NAME_EPCMOUT1 "aio-epcmout1" +#define AUD_NAME_EPCMOUT2 "aio-epcmout2" +#define AUD_NAME_EPCMOUT3 "aio-epcmout3" +#define AUD_NAME_EPCMOUT6 "aio-epcmout6" +#define AUD_NAME_HIECOUT1 "aio-hiecout1" +#define AUD_NAME_IECOUT1 "aio-iecout1" +#define AUD_NAME_CMASTER "aio-cmaster" + +#define AUD_GNAME_HDMI "aio-hdmi" +#define AUD_GNAME_LINE "aio-line" +#define AUD_GNAME_IEC "aio-iec" + +/* + * This is a selector for virtual register map of AIO. + * + * map: Specify the index of virtual register map. + * hw : Specify the ID of real register map, selector uses this value. + * A meaning of this value depends specification of SoC. + */ +struct uniphier_aio_selector { + int map; + int hw; +}; + +/** + * 'SoftWare MAPping' setting of UniPhier AIO registers. + * + * We have to setup 'virtual' register maps to access 'real' registers of AIO. + * This feature is legacy and meaningless but AIO needs this to work. + * + * Each hardware blocks have own virtual register maps as following: + * + * Address Virtual Real + * ------- --------- --------------- + * 0x12000 DMAC map0 --> [selector] --> DMAC hardware 3 + * 0x12080 DMAC map1 --> [selector] --> DMAC hardware 1 + * ... + * 0x42000 Port map0 --> [selector] --> Port hardware 1 + * 0x42400 Port map1 --> [selector] --> Port hardware 2 + * ... + * + * ch : Input or output channel of DMAC + * rb : Ring buffer + * iport: PCM input port + * iif : Input interface + * oport: PCM output port + * oif : Output interface + * och : Output channel of DMAC for sampling rate converter + * + * These are examples for sound data paths: + * + * For caputure device: + * (outer of AIO) -> iport -> iif -> ch -> rb -> (CPU) + * For playback device: + * (CPU) -> rb -> ch -> oif -> oport -> (outer of AIO) + * For sampling rate converter device: + * (CPU) -> rb -> ch -> oif -> (HW SRC) -> iif -> och -> orb -> (CPU) + */ +struct uniphier_aio_swmap { + int type; + int dir; + + struct uniphier_aio_selector ch; + struct uniphier_aio_selector rb; + struct uniphier_aio_selector iport; + struct uniphier_aio_selector iif; + struct uniphier_aio_selector oport; + struct uniphier_aio_selector oif; + struct uniphier_aio_selector och; +}; + +struct uniphier_aio_spec { + const char *name; + const char *gname; + struct uniphier_aio_swmap swm; +}; + +struct uniphier_aio_chip_spec { + const struct uniphier_aio_spec *aio_specs; + int num_aio_specs; + struct snd_soc_dai_driver *dais; + int num_dais; + + /* DMA access mode, this is workaround for DMA hungup */ + int addr_ext; +}; + +struct uniphier_aio_sub { + struct uniphier_aio *aio; + + /* Guard sub->rd_offs and wr_offs from IRQ handler. */ + spinlock_t spin; + + const struct uniphier_aio_swmap *swm; + const struct uniphier_aio_spec *spec; + + struct snd_pcm_substream *substream; + struct snd_pcm_hw_params params; + + int setting; + int running; + u64 rd_offs; + u64 wr_offs; +}; + +struct uniphier_aio { + struct uniphier_aio_chip *chip; + + struct snd_soc_dai *dai; + + struct uniphier_aio_sub sub[2]; + + u8 iec_status[24]; + int pass_through; +}; + +struct uniphier_aio_chip { + struct platform_device *pdev; + const struct uniphier_aio_chip_spec *chip_spec; + + struct uniphier_aio *aios; + int num_aios; + + struct clk *clk; + struct reset_control *rst; + struct regmap *regmap; + int active; +}; + +static inline struct uniphier_aio *uniphier_priv(struct snd_soc_dai *dai) +{ + struct uniphier_aio_chip *chip = snd_soc_dai_get_drvdata(dai); + + return &chip->aios[dai->id]; +} + +int uniphier_aiodma_soc_register_platform(struct platform_device *pdev); + +int uniphier_aio_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +void uniphier_aio_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int uniphier_aio_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +int uniphier_aio_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int uniphier_aio_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + +int uniphier_aio_dai_probe(struct snd_soc_dai *dai); +int uniphier_aio_dai_remove(struct snd_soc_dai *dai); +int uniphier_aio_dai_suspend(struct snd_soc_dai *dai); +int uniphier_aio_dai_resume(struct snd_soc_dai *dai); + +extern const struct uniphier_aio_chip_spec uniphier_aio_ld11_spec; +extern const struct uniphier_aio_chip_spec uniphier_aio_ld20_spec; + +#endif /* SND_UNIPHIER_AIO_H__ */
On Wed, Nov 22, 2017 at 08:43:18PM +0900, Katsuhiro Suzuki wrote:
sound/soc/uniphier/Makefile | 4 + sound/soc/uniphier/aio-core.c | 368 +++++++++++++++++++++ sound/soc/uniphier/aio-dma.c | 266 +++++++++++++++ sound/soc/uniphier/aio-regctrl.c | 699 +++++++++++++++++++++++++++++++++++++++ sound/soc/uniphier/aio-regctrl.h | 495 +++++++++++++++++++++++++++ sound/soc/uniphier/aio.h | 261 +++++++++++++++
Please split this up more, it looks like there's at least two or three drivers in here and it winds up being quite large. There's at least a DMA and a DAI driver. Looking through this my overall impression is that this is a fairly large and complex audio subsystem with some DSP and routing capacity which is being handled in a board specific fashion rather than generically but it's kind of hard to tell as there's not much description of what's going on so I'm needing to reverse engineer things from the driver.
The code itself looks fairly clean, it's mainly a case of trying to figure out if it's doing what it's supposed to with the limited documentation.
+int uniphier_aio_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
+{
- struct uniphier_aio *aio = uniphier_priv(dai);
- struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
- sub->params = *params;
- sub->setting = 1;
So we don't validate the params at all?
- uniphier_aio_port_reset(sub);
- uniphier_aio_srcport_reset(sub);
Is there a mux in the SoC here?
+static const struct of_device_id uniphier_aio_of_match[] = { +#ifdef CONFIG_SND_SOC_UNIPHIER_LD11
- {
.compatible = "socionext,uniphier-ld11-aio",
.data = &uniphier_aio_ld11_spec,
- },
- {
.compatible = "socionext,uniphier-ld20-aio",
.data = &uniphier_aio_ld20_spec,
- },
+#endif /* CONFIG_SND_SOC_UNIPHIER_LD11 */
Why is there an ifdef here? There's no other conditional code in here, it seems pointless.
for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
struct uniphier_aio_sub *sub = &aio->sub[j];
if (!sub->running)
continue;
spin_lock(&sub->spin);
uniphier_aio_rb_sync(sub);
uniphier_aio_rb_clear_int(sub);
spin_unlock(&sub->spin);
It's not 100% obvious what this does... a comment might help.
+int uniphier_aio_chip_init(struct uniphier_aio_chip *chip) +{
- struct regmap *r = chip->regmap;
- regmap_update_bits(r, A2APLLCTR0,
A2APLLCTR0_APLLXPOW_MASK,
A2APLLCTR0_APLLXPOW_PWON);
- regmap_update_bits(r, A2APLLCTR1, A2APLLCTR1_APLL_MASK,
A2APLLCTR1_APLLF2_33MHZ | A2APLLCTR1_APLLA2_33MHZ |
A2APLLCTR1_APLLF1_36MHZ | A2APLLCTR1_APLLA1_36MHZ);
- regmap_update_bits(r, A2EXMCLKSEL0,
A2EXMCLKSEL0_EXMCLK_MASK,
A2EXMCLKSEL0_EXMCLK_OUTPUT);
- regmap_update_bits(r, A2AIOINPUTSEL, A2AIOINPUTSEL_RXSEL_MASK,
A2AIOINPUTSEL_RXSEL_PCMI1_HDMIRX1 |
A2AIOINPUTSEL_RXSEL_PCMI2_SIF |
A2AIOINPUTSEL_RXSEL_PCMI3_EVEA |
A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1);
This definitely looks like there's some clocking and audio routing within the SoC which should be exposed to userspace, or at the very least machine driver configuration rather than being hard coded.
- switch (pc) {
- case IEC61937_PC_AC3:
repet = OPORTMXREPET_STRLENGTH_AC3 |
OPORTMXREPET_PMLENGTH_AC3;
pause |= OPORTMXPAUDAT_PAUSEPD_AC3;
break;
- case IEC61937_PC_MPA:
repet = OPORTMXREPET_STRLENGTH_MPA |
OPORTMXREPET_PMLENGTH_MPA;
pause |= OPORTMXPAUDAT_PAUSEPD_MPA;
break;
- case IEC61937_PC_MP3:
repet = OPORTMXREPET_STRLENGTH_MP3 |
OPORTMXREPET_PMLENGTH_MP3;
pause |= OPORTMXPAUDAT_PAUSEPD_MP3;
break;
This looks awfully like compressed audio support... should there be integration with the compressed audio API/
This patch adds support for UniPhier AIO sound driver which is included in UniPhier LD11/LD20 SoCs.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com --- sound/soc/uniphier/Kconfig | 10 ++ sound/soc/uniphier/Makefile | 1 + sound/soc/uniphier/aio-ld11.c | 343 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 354 insertions(+) create mode 100644 sound/soc/uniphier/aio-ld11.c
diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig index 38a02879b9d5..8da628a46a9b 100644 --- a/sound/soc/uniphier/Kconfig +++ b/sound/soc/uniphier/Kconfig @@ -13,6 +13,16 @@ config SND_SOC_UNIPHIER_AIO select REGMAP_MMIO depends on SND_SOC_UNIPHIER
+config SND_SOC_UNIPHIER_LD11 + tristate "UniPhier LD11/LD20 Device Driver" + depends on SND_SOC_UNIPHIER + select SND_SOC_UNIPHIER_AIO + help + This adds ASoC driver for Socionext UniPhier LD11/LD20 + input and output that can be used with other codecs. + Select Y if you use such device. + If unsure select "N". + config SND_SOC_UNIPHIER_EVEA_CODEC tristate "UniPhier SoC internal audio codec" depends on SND_SOC_UNIPHIER diff --git a/sound/soc/uniphier/Makefile b/sound/soc/uniphier/Makefile index 705a60cb2aa5..55a77ad1192e 100644 --- a/sound/soc/uniphier/Makefile +++ b/sound/soc/uniphier/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 snd-soc-uniphier-aio-objs := aio-regctrl.o aio-dma.o aio-core.o +snd-soc-uniphier-aio-$(CONFIG_SND_SOC_UNIPHIER_LD11) += aio-ld11.o
obj-$(CONFIG_SND_SOC_UNIPHIER_AIO) += snd-soc-uniphier-aio.o
diff --git a/sound/soc/uniphier/aio-ld11.c b/sound/soc/uniphier/aio-ld11.c new file mode 100644 index 000000000000..b6e3bd11f23a --- /dev/null +++ b/sound/soc/uniphier/aio-ld11.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Socionext UniPhier AIO ALSA driver for LD11/LD20. + * + * Copyright (c) 2016-2017 Socionext Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#include "aio.h" + +static const struct uniphier_aio_spec uniphier_aio_ld11[] = { + /* for HDMI PCM In, Pin:AI1Dx */ + { + .name = AUD_NAME_PCMIN1, + .gname = AUD_GNAME_HDMI, + .swm = { + .type = PORT_TYPE_I2S, + .dir = PORT_DIR_INPUT, + .rb = { 21, 14, }, + .ch = { 21, 14, }, + .iif = { 5, 3, }, + .iport = { 0, AUD_HW_PCMIN1, }, + }, + }, + + /* for SIF In, Pin:AI2Dx */ + { + .name = AUD_NAME_PCMIN2, + .swm = { + .type = PORT_TYPE_I2S, + .dir = PORT_DIR_INPUT, + .rb = { 22, 15, }, + .ch = { 22, 15, }, + .iif = { 6, 4, }, + .iport = { 1, AUD_HW_PCMIN2, }, + }, + }, + + /* for Line In, Pin:AI3Dx */ + { + .name = AUD_NAME_PCMIN3, + .gname = AUD_GNAME_LINE, + .swm = { + .type = PORT_TYPE_EVE, + .dir = PORT_DIR_INPUT, + .rb = { 23, 16, }, + .ch = { 23, 16, }, + .iif = { 7, 5, }, + .iport = { 2, AUD_HW_PCMIN3, }, + }, + }, + + /* for S/PDIF In, Pin:AI1IEC */ + { + .name = AUD_NAME_IECIN1, + .gname = AUD_GNAME_IEC, + .swm = { + .type = PORT_TYPE_SPDIF, + .dir = PORT_DIR_INPUT, + .rb = { 26, 17, }, + .ch = { 26, 17, }, + .iif = { 10, 6, }, + .iport = { 3, AUD_HW_IECIN1, }, + }, + }, + + /* for Speaker, Pin:AO1Dx */ + { + .name = AUD_NAME_HPCMOUT1, + .swm = { + .type = PORT_TYPE_I2S, + .dir = PORT_DIR_OUTPUT, + .rb = { 0, 0, }, + .ch = { 0, 0, }, + .oif = { 0, 0, }, + .oport = { 0, AUD_HW_HPCMOUT1, }, + }, + }, + + /* for HDMI PCM, Pin:AO2Dx */ + { + .name = AUD_NAME_PCMOUT1, + .gname = AUD_GNAME_HDMI, + .swm = { + .type = PORT_TYPE_I2S, + .dir = PORT_DIR_OUTPUT, + .rb = { 0, 0, }, + .ch = { 0, 0, }, + .oif = { 0, 0, }, + .oport = { 3, AUD_HW_PCMOUT1, }, + }, + }, + + /* for Line Out, Pin:LO2_x */ + { + .name = AUD_NAME_PCMOUT2, + .gname = AUD_GNAME_LINE, + .swm = { + .type = PORT_TYPE_EVE, + .dir = PORT_DIR_OUTPUT, + .rb = { 2, 2, }, + .ch = { 2, 2, }, + .oif = { 2, 2, }, + .oport = { 1, AUD_HW_PCMOUT2, }, + }, + }, + + /* for Headphone, Pin:HP1_x */ + { + .name = AUD_NAME_PCMOUT3, + .swm = { + .type = PORT_TYPE_EVE, + .dir = PORT_DIR_OUTPUT, + .rb = { 3, 3, }, + .ch = { 3, 3, }, + .oif = { 3, 3, }, + .oport = { 2, AUD_HW_PCMOUT3, }, + }, + }, + + /* for HW Sampling Rate Converter */ + { + .name = AUD_NAME_EPCMOUT2, + .swm = { + .type = PORT_TYPE_CONV, + .dir = PORT_DIR_OUTPUT, + .rb = { 7, 5, }, + .ch = { 7, 5, }, + .oif = { 7, 5, }, + .oport = { 6, AUD_HW_EPCMOUT2, }, + .och = { 17, 12, }, + .iif = { 1, 1, }, + }, + }, + + /* for HW Sampling Rate Converter 2 */ + { + .name = AUD_NAME_EPCMOUT3, + .swm = { + .type = PORT_TYPE_CONV, + .dir = PORT_DIR_OUTPUT, + .rb = { 8, 6, }, + .ch = { 8, 6, }, + .oif = { 8, 6, }, + .oport = { 7, AUD_HW_EPCMOUT3, }, + .och = { 18, 13, }, + .iif = { 2, 2, }, + }, + }, + + /* for S/PDIF Out, Pin:AO1IEC */ + { + .name = AUD_NAME_HIECOUT1, + .gname = AUD_GNAME_IEC, + .swm = { + .type = PORT_TYPE_SPDIF, + .dir = PORT_DIR_OUTPUT, + .rb = { 1, 1, }, + .ch = { 1, 1, }, + .oif = { 1, 1, }, + .oport = { 12, AUD_HW_HIECOUT1, }, + }, + }, +}; + +static const struct snd_soc_dai_ops uniphier_aio_ops = { + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, +}; + +static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { + { + .name = AUD_GNAME_HDMI, + .probe = uniphier_aio_dai_probe, + .remove = uniphier_aio_dai_remove, + .suspend = uniphier_aio_dai_suspend, + .resume = uniphier_aio_dai_resume, + .playback = { + .stream_name = AUD_NAME_PCMOUT1, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = AUD_NAME_PCMIN1, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_32000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &uniphier_aio_ops, + }, + { + .name = AUD_NAME_PCMIN2, + .probe = uniphier_aio_dai_probe, + .remove = uniphier_aio_dai_remove, + .suspend = uniphier_aio_dai_suspend, + .resume = uniphier_aio_dai_resume, + .capture = { + .stream_name = AUD_NAME_PCMIN2, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &uniphier_aio_ops, + }, + { + .name = AUD_GNAME_LINE, + .probe = uniphier_aio_dai_probe, + .remove = uniphier_aio_dai_remove, + .suspend = uniphier_aio_dai_suspend, + .resume = uniphier_aio_dai_resume, + .playback = { + .stream_name = AUD_NAME_PCMOUT2, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = AUD_NAME_PCMIN3, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &uniphier_aio_ops, + }, + { + .name = AUD_NAME_HPCMOUT1, + .probe = uniphier_aio_dai_probe, + .remove = uniphier_aio_dai_remove, + .suspend = uniphier_aio_dai_suspend, + .resume = uniphier_aio_dai_resume, + .playback = { + .stream_name = AUD_NAME_HPCMOUT1, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &uniphier_aio_ops, + }, + { + .name = AUD_NAME_PCMOUT3, + .probe = uniphier_aio_dai_probe, + .remove = uniphier_aio_dai_remove, + .suspend = uniphier_aio_dai_suspend, + .resume = uniphier_aio_dai_resume, + .playback = { + .stream_name = AUD_NAME_PCMOUT3, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &uniphier_aio_ops, + }, + { + .name = AUD_NAME_HIECOUT1, + .probe = uniphier_aio_dai_probe, + .remove = uniphier_aio_dai_remove, + .suspend = uniphier_aio_dai_suspend, + .resume = uniphier_aio_dai_resume, + .playback = { + .stream_name = AUD_NAME_HIECOUT1, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &uniphier_aio_ops, + }, + { + .name = AUD_NAME_EPCMOUT2, + .probe = uniphier_aio_dai_probe, + .remove = uniphier_aio_dai_remove, + .suspend = uniphier_aio_dai_suspend, + .resume = uniphier_aio_dai_resume, + .playback = { + .stream_name = AUD_NAME_EPCMOUT2, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_32000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &uniphier_aio_ops, + }, + { + .name = AUD_NAME_EPCMOUT3, + .probe = uniphier_aio_dai_probe, + .remove = uniphier_aio_dai_remove, + .suspend = uniphier_aio_dai_suspend, + .resume = uniphier_aio_dai_resume, + .playback = { + .stream_name = AUD_NAME_EPCMOUT3, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_32000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &uniphier_aio_ops, + }, +}; + +const struct uniphier_aio_chip_spec uniphier_aio_ld11_spec = { + .aio_specs = uniphier_aio_ld11, + .num_aio_specs = ARRAY_SIZE(uniphier_aio_ld11), + .dais = uniphier_aio_dai_ld11, + .num_dais = ARRAY_SIZE(uniphier_aio_dai_ld11), + .addr_ext = 0, +}; + +const struct uniphier_aio_chip_spec uniphier_aio_ld20_spec = { + .aio_specs = uniphier_aio_ld11, + .num_aio_specs = ARRAY_SIZE(uniphier_aio_ld11), + .dais = uniphier_aio_dai_ld11, + .num_dais = ARRAY_SIZE(uniphier_aio_dai_ld11), + .addr_ext = 1, +};
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index b05bc2c5e85c..0959fd951f80 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12579,6 +12579,12 @@ F: include/media/soc* F: drivers/media/i2c/soc_camera/ F: drivers/media/platform/soc_camera/
+SOCIONEXT UNIPHIER SOUND DRIVER +M: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: sound/soc/uniphier/ + SOEKRIS NET48XX LED SUPPORT M: Chris Boot bootc@bootc.net S: Maintained
The patch
MAINTAINERS: add entries for UniPhier ASoC sound drivers
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 576f8f46e7c923f830dfa61924ad547447399b05 Mon Sep 17 00:00:00 2001
From: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com Date: Wed, 22 Nov 2017 20:43:20 +0900 Subject: [PATCH] MAINTAINERS: add entries for UniPhier ASoC sound drivers
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com Signed-off-by: Mark Brown broonie@kernel.org --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index aa71ab52fd76..55ae8ea8722a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12581,6 +12581,12 @@ F: include/media/soc* F: drivers/media/i2c/soc_camera/ F: drivers/media/platform/soc_camera/
+SOCIONEXT UNIPHIER SOUND DRIVER +M: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: sound/soc/uniphier/ + SOEKRIS NET48XX LED SUPPORT M: Chris Boot bootc@bootc.net S: Maintained
This patch adds audio controller, codec and simple card node of UniPhier AIO sound system for LD11/20 SoCs.
Signed-off-by: Katsuhiro Suzuki suzuki.katsuhiro@socionext.com --- .../boot/dts/socionext/uniphier-ld11-global.dts | 72 ++++++++++++++++++++++ arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi | 31 ++++++++++ .../boot/dts/socionext/uniphier-ld20-global.dts | 72 ++++++++++++++++++++++ arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi | 35 +++++++++++ 4 files changed, 210 insertions(+)
diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld11-global.dts b/arch/arm64/boot/dts/socionext/uniphier-ld11-global.dts index 2452b2243f42..4eb8d00a8c02 100644 --- a/arch/arm64/boot/dts/socionext/uniphier-ld11-global.dts +++ b/arch/arm64/boot/dts/socionext/uniphier-ld11-global.dts @@ -37,6 +37,29 @@ device_type = "memory"; reg = <0 0x80000000 0 0x40000000>; }; + + soc@0 { + sound { + compatible = "audio-graph-card"; + label = "UniPhier LD11"; + widgets = "Headphone", "Headphone Jack"; + + dais = <&i2s_port2 + &i2s_port4 + &spdif_port0>; + }; + + spdif-out { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + + port@0 { + spdif_tx: endpoint { + remote-endpoint = <&spdif_hiecout1>; + }; + }; + }; + }; };
&serial0 { @@ -72,3 +95,52 @@ &nand { status = "okay"; }; + +&audio { + i2s_port0: port@0 { + i2s_hdmi: endpoint { + }; + }; + + i2s_port1: port@1 { + i2s_pcmin2: endpoint { + }; + }; + + i2s_port2: port@2 { + i2s_line: endpoint { + remote-endpoint = <&evea_line>; + }; + }; + + i2s_port3: port@3 { + i2s_hpcmout1: endpoint { + }; + }; + + i2s_port4: port@4 { + i2s_pcmout3: endpoint { + remote-endpoint = <&evea_hp>; + }; + }; + + spdif_port0: port@5 { + spdif_hiecout1: endpoint { + remote-endpoint = <&spdif_tx>; + }; + }; +}; + +&evea { + port@0 { + evea_line: endpoint { + remote-endpoint = <&i2s_line>; + }; + }; + + port@1 { + evea_hp: endpoint { + remote-endpoint = <&i2s_pcmout3>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi b/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi index 1c63d0ab8a58..875e3d832090 100644 --- a/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi +++ b/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi @@ -186,6 +186,29 @@ <21 217 3>; };
+ audio: audio@56000000 { + compatible = "socionext,uniphier-ld11-aio"; + reg = <0x56000000 0x80000>; + interrupts = <0 144 4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_aout>; + clock-names = "aio"; + clocks = <&sys_clk 40>; + reset-names = "aio"; + resets = <&sys_rst 40>; + #sound-dai-cells = <1>; + }; + + evea: codec@57900000 { + compatible = "socionext,uniphier-evea"; + reg = <0x57900000 0x1000>; + clock-names = "evea", "exiv"; + clocks = <&sys_clk 41>, <&sys_clk 42>; + reset-names = "evea", "exiv", "adamv"; + resets = <&sys_rst 41>, <&sys_rst 42>, <&adamv_rst 0>; + #sound-dai-cells = <1>; + }; + adamv@57920000 { compatible = "socionext,uniphier-ld11-adamv", "simple-mfd", "syscon"; @@ -471,3 +494,11 @@ };
#include "uniphier-pinctrl.dtsi" + +&pinctrl_aout { + drive-strength = <4>; /* default: 4mA */ + group_1 { + pins = "AO1ARC"; + drive-strength = <8>; /* 8mA */ + }; +}; diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld20-global.dts b/arch/arm64/boot/dts/socionext/uniphier-ld20-global.dts index fc2bc9d75d35..ebcd65631bca 100644 --- a/arch/arm64/boot/dts/socionext/uniphier-ld20-global.dts +++ b/arch/arm64/boot/dts/socionext/uniphier-ld20-global.dts @@ -37,6 +37,29 @@ device_type = "memory"; reg = <0 0x80000000 0 0xc0000000>; }; + + soc@0 { + sound { + compatible = "audio-graph-card"; + label = "UniPhier LD20"; + widgets = "Headphone", "Headphone Jack"; + + dais = <&i2s_port2 + &i2s_port4 + &spdif_port0>; + }; + + spdif-out { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + + port@0 { + spdif_tx: endpoint { + remote-endpoint = <&spdif_hiecout1>; + }; + }; + }; + }; };
&serial0 { @@ -54,3 +77,52 @@ &nand { status = "okay"; }; + +&audio { + i2s_port0: port@0 { + i2s_hdmi: endpoint { + }; + }; + + i2s_port1: port@1 { + i2s_pcmin2: endpoint { + }; + }; + + i2s_port2: port@2 { + i2s_line: endpoint { + remote-endpoint = <&evea_line>; + }; + }; + + i2s_port3: port@3 { + i2s_hpcmout1: endpoint { + }; + }; + + i2s_port4: port@4 { + i2s_pcmout3: endpoint { + remote-endpoint = <&evea_hp>; + }; + }; + + spdif_port0: port@5 { + spdif_hiecout1: endpoint { + remote-endpoint = <&spdif_tx>; + }; + }; +}; + +&evea { + port@0 { + evea_line: endpoint { + remote-endpoint = <&i2s_line>; + }; + }; + + port@1 { + evea_hp: endpoint { + remote-endpoint = <&i2s_pcmout3>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi b/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi index 5c81070944cc..d42bb1be1137 100644 --- a/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi +++ b/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi @@ -286,6 +286,29 @@ <21 217 3>; };
+ audio: audio@56000000 { + compatible = "socionext,uniphier-ld20-aio"; + reg = <0x56000000 0x80000>; + interrupts = <0 144 4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_aout>; + clock-names = "aio"; + clocks = <&sys_clk 40>; + reset-names = "aio"; + resets = <&sys_rst 40>; + #sound-dai-cells = <1>; + }; + + evea: codec@57900000 { + compatible = "socionext,uniphier-evea"; + reg = <0x57900000 0x1000>; + clock-names = "evea", "exiv"; + clocks = <&sys_clk 41>, <&sys_clk 42>; + reset-names = "evea", "exiv", "adamv"; + resets = <&sys_rst 41>, <&sys_rst 42>, <&adamv_rst 0>; + #sound-dai-cells = <1>; + }; + adamv@57920000 { compatible = "socionext,uniphier-ld20-adamv", "simple-mfd", "syscon"; @@ -527,3 +550,15 @@ };
#include "uniphier-pinctrl.dtsi" + +&pinctrl_aout { + drive-strength = <4>; /* default: 3.5mA */ + group_1 { + pins = "AO1DACCK"; + drive-strength = <5>; /* 5mA */ + }; + group_2 { + pins = "AO1ARC"; + drive-strength = <11>; /* 11mA */ + }; +};
participants (4)
-
Katsuhiro Suzuki
-
Mark Brown
-
Masahiro Yamada
-
Rob Herring