ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/sound/soc/pxa/Kconfig b/marvell/linux/sound/soc/pxa/Kconfig
new file mode 100644
index 0000000..9b36f7f
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/Kconfig
@@ -0,0 +1,263 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_PXA2XX_SOC
+ tristate "SoC Audio for the Intel PXA2xx chip"
+ depends on ARCH_PXA || COMPILE_TEST
+ select SND_PXA2XX_LIB
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the PXA2xx AC97, I2S or SSP interface. You will also need
+ to select the audio interfaces to support below.
+
+config SND_MMP_SOC
+ bool "Soc Audio for Marvell MMP chips"
+ depends on ARCH_MMP
+ select MMP_SRAM
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select SND_ARM
+ help
+ Say Y if you want to add support for codecs attached to
+ the MMP SSPA interface.
+
+config SND_PXA_SOC
+ tristate "SoC Audio for MARVELL PXA chips with pdma"
+ depends on ARCH_MMP && MMP_PDMA
+ select SND_SOC_GENERIC_DMAENGINE_PCM if MMP_PDMA
+ help
+ Say Y or M if you want to add support for ssp
+ which uses pdma as pcm platform driver. You
+ will also need to select the audio interfaces
+ to support below.
+
+config SND_PXA2XX_AC97
+ tristate
+
+config SND_PXA2XX_SOC_AC97
+ tristate
+ select AC97_BUS_NEW
+ select SND_PXA2XX_LIB
+ select SND_PXA2XX_LIB_AC97
+ select SND_SOC_AC97_BUS_NEW
+
+config SND_PXA2XX_SOC_I2S
+ select SND_PXA2XX_LIB
+ tristate
+
+config SND_PXA_SOC_SSP
+ tristate "Soc Audio via PXA2xx/PXA3xx SSP ports"
+ depends on PLAT_PXA
+ select PXA_SSP
+ select SND_PXA2XX_LIB
+
+config SND_MMP_SOC_SSPA
+ tristate
+
+config SND_PXA2XX_SOC_CORGI
+ tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
+ depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx && I2C
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_WM8731
+ help
+ Say Y if you want to add support for SoC audio on Sharp
+ Zaurus SL-C7x0 models (Corgi, Shepherd, Husky).
+
+config SND_PXA2XX_SOC_SPITZ
+ tristate "SoC Audio support for Sharp Zaurus SL-Cxx00"
+ depends on SND_PXA2XX_SOC && PXA_SHARP_Cxx00 && I2C
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_WM8750
+ help
+ Say Y if you want to add support for SoC audio on Sharp
+ Zaurus SL-Cxx00 models (Spitz, Borzoi and Akita).
+
+config SND_PXA2XX_SOC_Z2
+ tristate "SoC Audio support for Zipit Z2"
+ depends on SND_PXA2XX_SOC && MACH_ZIPIT2 && I2C
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_WM8750
+ help
+ Say Y if you want to add support for SoC audio on Zipit Z2.
+
+config SND_PXA2XX_SOC_POODLE
+ tristate "SoC Audio support for Poodle"
+ depends on SND_PXA2XX_SOC && MACH_POODLE && I2C
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_WM8731
+ help
+ Say Y if you want to add support for SoC audio on Sharp
+ Zaurus SL-5600 model (Poodle).
+
+config SND_PXA2XX_SOC_TOSA
+ tristate "SoC AC97 Audio support for Tosa"
+ depends on SND_PXA2XX_SOC && MACH_TOSA
+ depends on MFD_TC6393XB
+ depends on AC97_BUS=n
+ select SND_PXA2XX_SOC_AC97
+ select SND_SOC_WM9712
+ help
+ Say Y if you want to add support for SoC audio on Sharp
+ Zaurus SL-C6000x models (Tosa).
+
+config SND_PXA2XX_SOC_E740
+ tristate "SoC AC97 Audio support for e740"
+ depends on SND_PXA2XX_SOC && MACH_E740
+ depends on AC97_BUS=n
+ select SND_SOC_WM9705
+ select SND_PXA2XX_SOC_AC97
+ help
+ Say Y if you want to add support for SoC audio on the
+ toshiba e740 PDA
+
+config SND_PXA2XX_SOC_E750
+ tristate "SoC AC97 Audio support for e750"
+ depends on SND_PXA2XX_SOC && MACH_E750
+ depends on AC97_BUS=n
+ select SND_SOC_WM9705
+ select SND_PXA2XX_SOC_AC97
+ help
+ Say Y if you want to add support for SoC audio on the
+ toshiba e750 PDA
+
+config SND_PXA2XX_SOC_E800
+ tristate "SoC AC97 Audio support for e800"
+ depends on SND_PXA2XX_SOC && MACH_E800
+ depends on AC97_BUS=n
+ select SND_SOC_WM9712
+ select SND_PXA2XX_SOC_AC97
+ help
+ Say Y if you want to add support for SoC audio on the
+ Toshiba e800 PDA
+
+config SND_PXA2XX_SOC_EM_X270
+ tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300"
+ depends on SND_PXA2XX_SOC && (MACH_EM_X270 || MACH_EXEDA || \
+ MACH_CM_X300)
+ depends on AC97_BUS=n
+ select SND_PXA2XX_SOC_AC97
+ select SND_SOC_WM9712
+ help
+ Say Y if you want to add support for SoC audio on
+ CompuLab EM-x270, eXeda and CM-X300 machines.
+
+config SND_PXA2XX_SOC_PALM27X
+ bool "SoC Audio support for Palm T|X, T5, E2 and LifeDrive"
+ depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || \
+ MACH_PALMT5 || MACH_PALMTE2)
+ depends on AC97_BUS=n
+ select SND_PXA2XX_SOC_AC97
+ select SND_SOC_WM9712
+ help
+ Say Y if you want to add support for SoC audio on
+ Palm T|X, T5, E2 or LifeDrive handheld computer.
+
+config SND_PXA910_SOC
+ tristate "SoC Audio for Marvell PXA910 chip"
+ depends on ARCH_MMP && SND
+ select SND_PCM
+ help
+ Say Y if you want to add support for SoC audio on the
+ Marvell PXA910 reference platform.
+
+config SND_SOC_TTC_DKB
+ tristate "SoC Audio support for TTC DKB"
+ depends on SND_PXA910_SOC && MACH_TTC_DKB && I2C=y
+ select PXA_SSP
+ select SND_PXA_SOC_SSP
+ select SND_MMP_SOC
+ select MFD_88PM860X
+ select SND_SOC_88PM860X
+ help
+ Say Y if you want to add support for SoC audio on TTC DKB
+
+
+config SND_SOC_ZYLONITE
+ tristate "SoC Audio support for Marvell Zylonite"
+ depends on SND_PXA2XX_SOC && MACH_ZYLONITE
+ depends on AC97_BUS=n
+ select SND_PXA2XX_SOC_AC97
+ select SND_PXA_SOC_SSP
+ select SND_SOC_WM9713
+ help
+ Say Y if you want to add support for SoC audio on the
+ Marvell Zylonite reference platform.
+
+config SND_PXA2XX_SOC_HX4700
+ tristate "SoC Audio support for HP iPAQ hx4700"
+ depends on SND_PXA2XX_SOC && MACH_H4700 && I2C
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_AK4641
+ help
+ Say Y if you want to add support for SoC audio on the
+ HP iPAQ hx4700.
+
+config SND_PXA2XX_SOC_MAGICIAN
+ tristate "SoC Audio support for HTC Magician"
+ depends on SND_PXA2XX_SOC && MACH_MAGICIAN && I2C
+ select SND_PXA2XX_SOC_I2S
+ select SND_PXA_SOC_SSP
+ select SND_SOC_UDA1380
+ help
+ Say Y if you want to add support for SoC audio on the
+ HTC Magician.
+
+config SND_PXA2XX_SOC_MIOA701
+ tristate "SoC Audio support for MIO A701"
+ depends on SND_PXA2XX_SOC && MACH_MIOA701
+ depends on AC97_BUS=n
+ select SND_PXA2XX_SOC_AC97
+ select SND_SOC_WM9713
+ help
+ Say Y if you want to add support for SoC audio on the
+ MIO A701.
+
+config SND_PXA2XX_SOC_IMOTE2
+ tristate "SoC Audio support for IMote 2"
+ depends on SND_PXA2XX_SOC && MACH_INTELMOTE2 && I2C
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_WM8940
+ help
+ Say Y if you want to add support for SoC audio on the
+ IMote 2.
+
+config SND_MMP_SOC_BROWNSTONE
+ tristate "SoC Audio support for Marvell Brownstone"
+ depends on SND_MMP_SOC && MACH_BROWNSTONE && I2C
+ select SND_MMP_SOC_SSPA
+ select MFD_WM8994
+ select SND_SOC_WM8994
+ help
+ Say Y if you want to add support for SoC audio on the
+ Marvell Brownstone reference platform.
+
+config SND_PXA_SSP_DUMP
+ bool "PXA SSP data dump"
+ help
+ Say Y if you want to enable PXA ssp data dump for SoC
+ audio on the MARVELL pxa988/1088/1L88 reference
+ platform. currently it dumps audio data from audio
+ buffer in sram and save it in file under /data/.
+
+config SND_SOC_PXA_NAU8810
+ bool "SoC Audio support for PXA NAU8810"
+ depends on ARCH_MMP && SND
+ select SND_PCM
+ select PXA_SSP
+ select SND_PXA_SOC_SSP
+ select SND_PXA_SSP_DUMP
+ select SND_MMP_SOC
+ select SND_PXA_SOC
+ help
+ Say Y if you want to add support for SoC audio on the
+ ASR PXA NAU8810 reference platform.
+
+config SND_SOC_PXA_ALC5616
+ bool "SoC Audio support for PXA ALC5616"
+ depends on ARCH_MMP && SND
+ select SND_PCM
+ select PXA_SSP
+ select SND_PXA_SOC_SSP
+ select SND_PXA_SSP_DUMP
+ select SND_MMP_SOC
+ select SND_PXA_SOC
+ help
+ Say Y if you want to add support for SoC audio on the
+ ASR PXA ALC5616 reference platform.
diff --git a/marvell/linux/sound/soc/pxa/Makefile b/marvell/linux/sound/soc/pxa/Makefile
new file mode 100644
index 0000000..260efb0
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/Makefile
@@ -0,0 +1,115 @@
+# SPDX-License-Identifier: GPL-2.0
+# PXA Platform Support
+snd-soc-pxa2xx-objs := pxa2xx-pcm.o
+snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
+snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
+snd-soc-pxa-ssp-objs := pxa-ssp.o
+snd-soc-ssp-dummy-objs := ssp-dummy.o
+snd-soc-mmp-objs := mmp-pcm.o
+snd-soc-mmp-sspa-objs := mmp-sspa.o
+snd-soc-pxa-objs := pxa-pcm.o
+
+obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o
+obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o
+obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
+
+# PXA Machine Support
+snd-soc-corgi-objs := corgi.o
+snd-soc-poodle-objs := poodle.o
+snd-soc-tosa-objs := tosa.o
+snd-soc-e740-objs := e740_wm9705.o
+snd-soc-e750-objs := e750_wm9705.o
+snd-soc-e800-objs := e800_wm9712.o
+snd-soc-spitz-objs := spitz.o
+snd-soc-em-x270-objs := em-x270.o
+snd-soc-palm27x-objs := palm27x.o
+snd-soc-zylonite-objs := zylonite.o
+snd-soc-hx4700-objs := hx4700.o
+snd-soc-magician-objs := magician.o
+snd-soc-mioa701-objs := mioa701_wm9713.o
+snd-soc-z2-objs := z2.o
+snd-soc-imote2-objs := imote2.o
+snd-soc-brownstone-objs := brownstone.o
+snd-soc-ttc-dkb-objs := ttc-dkb.o
+
+obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
+obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
+obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
+obj-$(CONFIG_SND_PXA2XX_SOC_E740) += snd-soc-e740.o
+obj-$(CONFIG_SND_PXA2XX_SOC_E750) += snd-soc-e750.o
+obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
+obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
+obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
+obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
+obj-$(CONFIG_SND_PXA2XX_SOC_HX4700) += snd-soc-hx4700.o
+obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
+obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
+obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o
+obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
+obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o
+obj-$(CONFIG_SND_MMP_SOC_BROWNSTONE) += snd-soc-brownstone.o
+obj-$(CONFIG_SND_SOC_TTC_DKB) += snd-soc-ttc-dkb.o
+
+obj-$(CONFIG_SND_MMP_SOC) += snd-soc-mmp.o
+obj-$(CONFIG_SND_MMP_SOC_SSPA) += snd-soc-mmp-sspa.o
+obj-$(CONFIG_SND_PXA_SOC) += snd-soc-pxa.o
+
+snd-soc-pxa-nau8810-objs := pxa-nau8810.o
+snd-soc-pxa-alc5616-objs := pxa-alc5616.o
+snd-soc-pxa-sndcard-objs := pxa-sndcard.o
+
+ifeq ($(CONFIG_LINUX_ALSA_SSP_CONTROLLER_ENABLE),y)
+
+ifeq ($(CONFIG_CODEC_PM812),y)
+obj-y += snd-soc-pxa-ssp.o
+obj-y += snd-soc-pxa-sndcard.o
+endif
+
+ifeq ($(CONFIG_CODEC_PM805),y)
+obj-y += snd-soc-pxa-ssp.o
+obj-y += snd-soc-pxa-sndcard.o
+endif
+
+ifeq ($(CONFIG_CODEC_NAU8810),y)
+obj-y += snd-soc-pxa-ssp.o
+obj-y += snd-soc-pxa-sndcard.o
+endif
+
+ifeq ($(CONFIG_CODEC_ALC5616),y)
+obj-y += snd-soc-pxa-ssp.o
+obj-y += snd-soc-pxa-sndcard.o
+endif
+
+endif
+
+ifeq ($(CONFIG_LINUX_ALSA_SSP_CONTROLLER_DISABLE),y)
+
+ifeq ($(CONFIG_MODEM_AUDIO_CONTROLLER_GSSP),y)
+ifeq ($(CONFIG_CODEC_NAU8810),y)
+obj-y += snd-soc-ssp-dummy.o
+obj-y += snd-soc-pxa-nau8810.o
+endif
+endif
+
+ifeq ($(CONFIG_MODEM_AUDIO_CONTROLLER_GSSP),y)
+ifeq ($(CONFIG_CODEC_ALC5616),y)
+obj-y += snd-soc-ssp-dummy.o
+obj-y += snd-soc-pxa-alc5616.o
+endif
+endif
+
+ifeq ($(CONFIG_MODEM_AUDIO_CONTROLLER_SSPA),y)
+ifeq ($(CONFIG_CODEC_NAU8810),y)
+obj-y += snd-soc-ssp-dummy.o
+obj-y += snd-soc-pxa-nau8810.o
+endif
+endif
+
+ifeq ($(CONFIG_MODEM_AUDIO_CONTROLLER_SSPA),y)
+ifeq ($(CONFIG_CODEC_ALC5616),y)
+obj-y += snd-soc-ssp-dummy.o
+obj-y += snd-soc-pxa-alc5616.o
+endif
+endif
+
+endif
diff --git a/marvell/linux/sound/soc/pxa/brownstone.c b/marvell/linux/sound/soc/pxa/brownstone.c
new file mode 100644
index 0000000..53b1435
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/brownstone.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * linux/sound/soc/pxa/brownstone.c
+ *
+ * Copyright (C) 2011 Marvell International Ltd.
+ */
+
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "../codecs/wm8994.h"
+#include "mmp-sspa.h"
+
+static const struct snd_kcontrol_new brownstone_dapm_control[] = {
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static const struct snd_soc_dapm_widget brownstone_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Main Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route brownstone_audio_map[] = {
+ {"Ext Spk", NULL, "SPKOUTLP"},
+ {"Ext Spk", NULL, "SPKOUTLN"},
+ {"Ext Spk", NULL, "SPKOUTRP"},
+ {"Ext Spk", NULL, "SPKOUTRN"},
+
+ {"Headset Stereophone", NULL, "HPOUT1L"},
+ {"Headset Stereophone", NULL, "HPOUT1R"},
+
+ {"IN1RN", NULL, "Headset Mic"},
+
+ {"DMIC1DAT", NULL, "MICBIAS1"},
+ {"MICBIAS1", NULL, "Main Mic"},
+};
+
+static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int freq_out, sspa_mclk, sysclk;
+
+ if (params_rate(params) > 11025) {
+ freq_out = params_rate(params) * 512;
+ sysclk = params_rate(params) * 256;
+ sspa_mclk = params_rate(params) * 64;
+ } else {
+ freq_out = params_rate(params) * 1024;
+ sysclk = params_rate(params) * 512;
+ sspa_mclk = params_rate(params) * 64;
+ }
+
+ snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0);
+ snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk);
+ snd_soc_dai_set_pll(cpu_dai, MMP_SSPA_CLK, 0, freq_out, sspa_mclk);
+
+ /* set wm8994 sysclk */
+ snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, sysclk, 0);
+
+ return 0;
+}
+
+/* machine stream operations */
+static const struct snd_soc_ops brownstone_ops = {
+ .hw_params = brownstone_wm8994_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(wm8994,
+ DAILINK_COMP_ARRAY(COMP_CPU("mmp-sspa-dai.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8994-codec", "wm8994-aif1")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("mmp-pcm-audio")));
+
+static struct snd_soc_dai_link brownstone_wm8994_dai[] = {
+{
+ .name = "WM8994",
+ .stream_name = "WM8994 HiFi",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &brownstone_ops,
+ SND_SOC_DAILINK_REG(wm8994),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card brownstone = {
+ .name = "brownstone",
+ .owner = THIS_MODULE,
+ .dai_link = brownstone_wm8994_dai,
+ .num_links = ARRAY_SIZE(brownstone_wm8994_dai),
+
+ .controls = brownstone_dapm_control,
+ .num_controls = ARRAY_SIZE(brownstone_dapm_control),
+ .dapm_widgets = brownstone_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(brownstone_dapm_widgets),
+ .dapm_routes = brownstone_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(brownstone_audio_map),
+ .fully_routed = true,
+};
+
+static int brownstone_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ brownstone.dev = &pdev->dev;
+ ret = devm_snd_soc_register_card(&pdev->dev, &brownstone);
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ return ret;
+}
+
+static struct platform_driver mmp_driver = {
+ .driver = {
+ .name = "brownstone-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = brownstone_probe,
+};
+
+module_platform_driver(mmp_driver);
+
+MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
+MODULE_DESCRIPTION("ALSA SoC Brownstone");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:brownstone-audio");
diff --git a/marvell/linux/sound/soc/pxa/corgi.c b/marvell/linux/sound/soc/pxa/corgi.c
new file mode 100644
index 0000000..d810823
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/corgi.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * corgi.c -- SoC audio for Corgi
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Richard Purdie <richard@openedhand.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <mach/corgi.h>
+#include <mach/audio.h>
+
+#include "../codecs/wm8731.h"
+#include "pxa2xx-i2s.h"
+
+#define CORGI_HP 0
+#define CORGI_MIC 1
+#define CORGI_LINE 2
+#define CORGI_HEADSET 3
+#define CORGI_HP_OFF 4
+#define CORGI_SPK_ON 0
+#define CORGI_SPK_OFF 1
+
+ /* audio clock in Hz - rounded from 12.235MHz */
+#define CORGI_AUDIO_CLOCK 12288000
+
+static int corgi_jack_func;
+static int corgi_spk_func;
+
+static void corgi_ext_control(struct snd_soc_dapm_context *dapm)
+{
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* set up jack connection */
+ switch (corgi_jack_func) {
+ case CORGI_HP:
+ /* set = unmute headphone */
+ gpio_set_value(CORGI_GPIO_MUTE_L, 1);
+ gpio_set_value(CORGI_GPIO_MUTE_R, 1);
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+ break;
+ case CORGI_MIC:
+ /* reset = mute headphone */
+ gpio_set_value(CORGI_GPIO_MUTE_L, 0);
+ gpio_set_value(CORGI_GPIO_MUTE_R, 0);
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+ break;
+ case CORGI_LINE:
+ gpio_set_value(CORGI_GPIO_MUTE_L, 0);
+ gpio_set_value(CORGI_GPIO_MUTE_R, 0);
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+ break;
+ case CORGI_HEADSET:
+ gpio_set_value(CORGI_GPIO_MUTE_L, 0);
+ gpio_set_value(CORGI_GPIO_MUTE_R, 1);
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
+ break;
+ }
+
+ if (corgi_spk_func == CORGI_SPK_ON)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static int corgi_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ /* check the jack status at stream startup */
+ corgi_ext_control(&rtd->card->dapm);
+
+ return 0;
+}
+
+/* we need to unmute the HP at shutdown as the mute burns power on corgi */
+static void corgi_shutdown(struct snd_pcm_substream *substream)
+{
+ /* set = unmute headphone */
+ gpio_set_value(CORGI_GPIO_MUTE_L, 1);
+ gpio_set_value(CORGI_GPIO_MUTE_R, 1);
+}
+
+static int corgi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ unsigned int clk = 0;
+ int ret = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ clk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk = 11289600;
+ break;
+ }
+
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as input (unused) */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_ops corgi_ops = {
+ .startup = corgi_startup,
+ .hw_params = corgi_hw_params,
+ .shutdown = corgi_shutdown,
+};
+
+static int corgi_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = corgi_jack_func;
+ return 0;
+}
+
+static int corgi_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (corgi_jack_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ corgi_jack_func = ucontrol->value.enumerated.item[0];
+ corgi_ext_control(&card->dapm);
+ return 1;
+}
+
+static int corgi_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = corgi_spk_func;
+ return 0;
+}
+
+static int corgi_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (corgi_spk_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ corgi_spk_func = ucontrol->value.enumerated.item[0];
+ corgi_ext_control(&card->dapm);
+ return 1;
+}
+
+static int corgi_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+static int corgi_mic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+/* corgi machine dapm widgets */
+static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
+SND_SOC_DAPM_HP("Headphone Jack", NULL),
+SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
+SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
+SND_SOC_DAPM_LINE("Line Jack", NULL),
+SND_SOC_DAPM_HP("Headset Jack", NULL),
+};
+
+/* Corgi machine audio map (connections to the codec pins) */
+static const struct snd_soc_dapm_route corgi_audio_map[] = {
+
+ /* headset Jack - in = micin, out = LHPOUT*/
+ {"Headset Jack", NULL, "LHPOUT"},
+
+ /* headphone connected to LHPOUT1, RHPOUT1 */
+ {"Headphone Jack", NULL, "LHPOUT"},
+ {"Headphone Jack", NULL, "RHPOUT"},
+
+ /* speaker connected to LOUT, ROUT */
+ {"Ext Spk", NULL, "ROUT"},
+ {"Ext Spk", NULL, "LOUT"},
+
+ /* mic is connected to MICIN (via right channel of headphone jack) */
+ {"MICIN", NULL, "Mic Jack"},
+
+ /* Same as the above but no mic bias for line signals */
+ {"MICIN", NULL, "Line Jack"},
+};
+
+static const char * const jack_function[] = {"Headphone", "Mic", "Line",
+ "Headset", "Off"};
+static const char * const spk_function[] = {"On", "Off"};
+static const struct soc_enum corgi_enum[] = {
+ SOC_ENUM_SINGLE_EXT(5, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
+ SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
+ corgi_set_jack),
+ SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
+ corgi_set_spk),
+};
+
+/* corgi digital audio interface glue - connects codec <--> CPU */
+SND_SOC_DAILINK_DEFS(wm8731,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link corgi_dai = {
+ .name = "WM8731",
+ .stream_name = "WM8731",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &corgi_ops,
+ SND_SOC_DAILINK_REG(wm8731),
+};
+
+/* corgi audio machine driver */
+static struct snd_soc_card corgi = {
+ .name = "Corgi",
+ .owner = THIS_MODULE,
+ .dai_link = &corgi_dai,
+ .num_links = 1,
+
+ .controls = wm8731_corgi_controls,
+ .num_controls = ARRAY_SIZE(wm8731_corgi_controls),
+ .dapm_widgets = wm8731_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
+ .dapm_routes = corgi_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(corgi_audio_map),
+ .fully_routed = true,
+};
+
+static int corgi_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &corgi;
+ int ret;
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ return ret;
+}
+
+static struct platform_driver corgi_driver = {
+ .driver = {
+ .name = "corgi-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = corgi_probe,
+};
+
+module_platform_driver(corgi_driver);
+
+/* Module information */
+MODULE_AUTHOR("Richard Purdie");
+MODULE_DESCRIPTION("ALSA SoC Corgi");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:corgi-audio");
diff --git a/marvell/linux/sound/soc/pxa/e740_wm9705.c b/marvell/linux/sound/soc/pxa/e740_wm9705.c
new file mode 100644
index 0000000..eafa148
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/e740_wm9705.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * e740-wm9705.c -- SoC audio for e740
+ *
+ * Copyright 2007 (c) Ian Molton <spyro@f2s.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <mach/audio.h>
+#include <mach/eseries-gpio.h>
+
+#include <asm/mach-types.h>
+
+#define E740_AUDIO_OUT 1
+#define E740_AUDIO_IN 2
+
+static int e740_audio_power;
+
+static void e740_sync_audio_power(int status)
+{
+ gpio_set_value(GPIO_E740_WM9705_nAVDD2, !status);
+ gpio_set_value(GPIO_E740_AMP_ON, (status & E740_AUDIO_OUT) ? 1 : 0);
+ gpio_set_value(GPIO_E740_MIC_ON, (status & E740_AUDIO_IN) ? 1 : 0);
+}
+
+static int e740_mic_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ e740_audio_power |= E740_AUDIO_IN;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ e740_audio_power &= ~E740_AUDIO_IN;
+
+ e740_sync_audio_power(e740_audio_power);
+
+ return 0;
+}
+
+static int e740_output_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ e740_audio_power |= E740_AUDIO_OUT;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ e740_audio_power &= ~E740_AUDIO_OUT;
+
+ e740_sync_audio_power(e740_audio_power);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget e740_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
+ SND_SOC_DAPM_PGA_E("Output Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e740_output_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("Mic Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e740_mic_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Output Amp", NULL, "LOUT"},
+ {"Output Amp", NULL, "ROUT"},
+ {"Output Amp", NULL, "MONOOUT"},
+
+ {"Speaker", NULL, "Output Amp"},
+ {"Headphone Jack", NULL, "Output Amp"},
+
+ {"MIC1", NULL, "Mic Amp"},
+ {"Mic Amp", NULL, "Mic (Internal)"},
+};
+
+SND_SOC_DAILINK_DEFS(ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9705-codec", "wm9705-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+SND_SOC_DAILINK_DEFS(ac97_aux,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9705-codec", "wm9705-aux")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link e740_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ SND_SOC_DAILINK_REG(ac97),
+ },
+ {
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ SND_SOC_DAILINK_REG(ac97_aux),
+ },
+};
+
+static struct snd_soc_card e740 = {
+ .name = "Toshiba e740",
+ .owner = THIS_MODULE,
+ .dai_link = e740_dai,
+ .num_links = ARRAY_SIZE(e740_dai),
+
+ .dapm_widgets = e740_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(e740_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .fully_routed = true,
+};
+
+static struct gpio e740_audio_gpios[] = {
+ { GPIO_E740_MIC_ON, GPIOF_OUT_INIT_LOW, "Mic amp" },
+ { GPIO_E740_AMP_ON, GPIOF_OUT_INIT_LOW, "Output amp" },
+ { GPIO_E740_WM9705_nAVDD2, GPIOF_OUT_INIT_HIGH, "Audio power" },
+};
+
+static int e740_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &e740;
+ int ret;
+
+ ret = gpio_request_array(e740_audio_gpios,
+ ARRAY_SIZE(e740_audio_gpios));
+ if (ret)
+ return ret;
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios));
+ }
+ return ret;
+}
+
+static int e740_remove(struct platform_device *pdev)
+{
+ gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios));
+ return 0;
+}
+
+static struct platform_driver e740_driver = {
+ .driver = {
+ .name = "e740-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = e740_probe,
+ .remove = e740_remove,
+};
+
+module_platform_driver(e740_driver);
+
+/* Module information */
+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
+MODULE_DESCRIPTION("ALSA SoC driver for e740");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:e740-audio");
diff --git a/marvell/linux/sound/soc/pxa/e750_wm9705.c b/marvell/linux/sound/soc/pxa/e750_wm9705.c
new file mode 100644
index 0000000..d75510d
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/e750_wm9705.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * e750-wm9705.c -- SoC audio for e750
+ *
+ * Copyright 2007 (c) Ian Molton <spyro@f2s.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <mach/audio.h>
+#include <mach/eseries-gpio.h>
+
+#include <asm/mach-types.h>
+
+static int e750_spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ gpio_set_value(GPIO_E750_SPK_AMP_OFF, 0);
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ gpio_set_value(GPIO_E750_SPK_AMP_OFF, 1);
+
+ return 0;
+}
+
+static int e750_hp_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ gpio_set_value(GPIO_E750_HP_AMP_OFF, 0);
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ gpio_set_value(GPIO_E750_HP_AMP_OFF, 1);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget e750_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
+ SND_SOC_DAPM_PGA_E("Headphone Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e750_hp_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("Speaker Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e750_spk_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Amp", NULL, "HPOUTL"},
+ {"Headphone Amp", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "Headphone Amp"},
+
+ {"Speaker Amp", NULL, "MONOOUT"},
+ {"Speaker", NULL, "Speaker Amp"},
+
+ {"MIC1", NULL, "Mic (Internal)"},
+};
+
+SND_SOC_DAILINK_DEFS(ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9705-codec", "wm9705-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+SND_SOC_DAILINK_DEFS(ac97_aux,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9705-codec", "wm9705-aux")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link e750_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ SND_SOC_DAILINK_REG(ac97),
+ /* use ops to check startup state */
+ },
+ {
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ SND_SOC_DAILINK_REG(ac97_aux),
+ },
+};
+
+static struct snd_soc_card e750 = {
+ .name = "Toshiba e750",
+ .owner = THIS_MODULE,
+ .dai_link = e750_dai,
+ .num_links = ARRAY_SIZE(e750_dai),
+
+ .dapm_widgets = e750_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(e750_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .fully_routed = true,
+};
+
+static struct gpio e750_audio_gpios[] = {
+ { GPIO_E750_HP_AMP_OFF, GPIOF_OUT_INIT_HIGH, "Headphone amp" },
+ { GPIO_E750_SPK_AMP_OFF, GPIOF_OUT_INIT_HIGH, "Speaker amp" },
+};
+
+static int e750_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &e750;
+ int ret;
+
+ ret = gpio_request_array(e750_audio_gpios,
+ ARRAY_SIZE(e750_audio_gpios));
+ if (ret)
+ return ret;
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios));
+ }
+ return ret;
+}
+
+static int e750_remove(struct platform_device *pdev)
+{
+ gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios));
+ return 0;
+}
+
+static struct platform_driver e750_driver = {
+ .driver = {
+ .name = "e750-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = e750_probe,
+ .remove = e750_remove,
+};
+
+module_platform_driver(e750_driver);
+
+/* Module information */
+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
+MODULE_DESCRIPTION("ALSA SoC driver for e750");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:e750-audio");
diff --git a/marvell/linux/sound/soc/pxa/e800_wm9712.c b/marvell/linux/sound/soc/pxa/e800_wm9712.c
new file mode 100644
index 0000000..56d543d
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/e800_wm9712.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * e800-wm9712.c -- SoC audio for e800
+ *
+ * Copyright 2007 (c) Ian Molton <spyro@f2s.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <mach/audio.h>
+#include <mach/eseries-gpio.h>
+
+static int e800_spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ gpio_set_value(GPIO_E800_SPK_AMP_ON, 1);
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ gpio_set_value(GPIO_E800_SPK_AMP_ON, 0);
+
+ return 0;
+}
+
+static int e800_hp_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ gpio_set_value(GPIO_E800_HP_AMP_OFF, 0);
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ gpio_set_value(GPIO_E800_HP_AMP_OFF, 1);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget e800_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic (Internal1)", NULL),
+ SND_SOC_DAPM_MIC("Mic (Internal2)", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_PGA_E("Headphone Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e800_hp_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("Speaker Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e800_spk_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "Headphone Amp"},
+
+ {"Speaker Amp", NULL, "MONOOUT"},
+ {"Speaker", NULL, "Speaker Amp"},
+
+ {"MIC1", NULL, "Mic (Internal1)"},
+ {"MIC2", NULL, "Mic (Internal2)"},
+};
+
+
+SND_SOC_DAILINK_DEFS(ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+SND_SOC_DAILINK_DEFS(ac97_aux,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-aux")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link e800_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ SND_SOC_DAILINK_REG(ac97),
+ },
+ {
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ SND_SOC_DAILINK_REG(ac97_aux),
+ },
+};
+
+static struct snd_soc_card e800 = {
+ .name = "Toshiba e800",
+ .owner = THIS_MODULE,
+ .dai_link = e800_dai,
+ .num_links = ARRAY_SIZE(e800_dai),
+
+ .dapm_widgets = e800_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(e800_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+static struct gpio e800_audio_gpios[] = {
+ { GPIO_E800_SPK_AMP_ON, GPIOF_OUT_INIT_HIGH, "Headphone amp" },
+ { GPIO_E800_HP_AMP_OFF, GPIOF_OUT_INIT_HIGH, "Speaker amp" },
+};
+
+static int e800_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &e800;
+ int ret;
+
+ ret = gpio_request_array(e800_audio_gpios,
+ ARRAY_SIZE(e800_audio_gpios));
+ if (ret)
+ return ret;
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios));
+ }
+ return ret;
+}
+
+static int e800_remove(struct platform_device *pdev)
+{
+ gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios));
+ return 0;
+}
+
+static struct platform_driver e800_driver = {
+ .driver = {
+ .name = "e800-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = e800_probe,
+ .remove = e800_remove,
+};
+
+module_platform_driver(e800_driver);
+
+/* Module information */
+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
+MODULE_DESCRIPTION("ALSA SoC driver for e800");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:e800-audio");
diff --git a/marvell/linux/sound/soc/pxa/em-x270.c b/marvell/linux/sound/soc/pxa/em-x270.c
new file mode 100644
index 0000000..9076ea7
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/em-x270.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SoC audio driver for EM-X270, eXeda and CM-X300
+ *
+ * Copyright 2007, 2009 CompuLab, Ltd.
+ *
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copied from tosa.c:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Richard Purdie <richard@openedhand.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <mach/audio.h>
+
+SND_SOC_DAILINK_DEFS(ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+SND_SOC_DAILINK_DEFS(ac97_aux,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-aux")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link em_x270_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ SND_SOC_DAILINK_REG(ac97),
+ },
+ {
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ SND_SOC_DAILINK_REG(ac97_aux),
+ },
+};
+
+static struct snd_soc_card em_x270 = {
+ .name = "EM-X270",
+ .owner = THIS_MODULE,
+ .dai_link = em_x270_dai,
+ .num_links = ARRAY_SIZE(em_x270_dai),
+};
+
+static struct platform_device *em_x270_snd_device;
+
+static int __init em_x270_init(void)
+{
+ int ret;
+
+ if (!(machine_is_em_x270() || machine_is_exeda()
+ || machine_is_cm_x300()))
+ return -ENODEV;
+
+ em_x270_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!em_x270_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(em_x270_snd_device, &em_x270);
+ ret = platform_device_add(em_x270_snd_device);
+
+ if (ret)
+ platform_device_put(em_x270_snd_device);
+
+ return ret;
+}
+
+static void __exit em_x270_exit(void)
+{
+ platform_device_unregister(em_x270_snd_device);
+}
+
+module_init(em_x270_init);
+module_exit(em_x270_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mike Rapoport");
+MODULE_DESCRIPTION("ALSA SoC EM-X270, eXeda and CM-X300");
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/sound/soc/pxa/hx4700.c b/marvell/linux/sound/soc/pxa/hx4700.c
new file mode 100644
index 0000000..0139343
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/hx4700.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SoC audio for HP iPAQ hx4700
+ *
+ * Copyright (c) 2009 Philipp Zabel
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/hx4700.h>
+#include <asm/mach-types.h>
+#include "pxa2xx-i2s.h"
+
+static struct snd_soc_jack hs_jack;
+
+/* Headphones jack detection DAPM pin */
+static struct snd_soc_jack_pin hs_jack_pin[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Speaker",
+ /* disable speaker when hp jack is inserted */
+ .mask = SND_JACK_HEADPHONE,
+ .invert = 1,
+ },
+};
+
+/* Headphones jack detection GPIO */
+static struct snd_soc_jack_gpio hs_jack_gpio = {
+ .gpio = GPIO75_HX4700_EARPHONE_nDET,
+ .invert = true,
+ .name = "hp-gpio",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 200,
+};
+
+/*
+ * iPAQ hx4700 uses I2S for capture and playback.
+ */
+static int hx4700_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+
+ /* set the I2S system clock as output */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ /* inform codec driver about clock freq *
+ * (PXA I2S always uses divider 256) */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 256 * params_rate(params),
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_ops hx4700_ops = {
+ .hw_params = hx4700_hw_params,
+};
+
+static int hx4700_spk_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(GPIO107_HX4700_SPK_nSD, !!SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+static int hx4700_hp_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(GPIO92_HX4700_HP_DRIVER, !!SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+/* hx4700 machine dapm widgets */
+static const struct snd_soc_dapm_widget hx4700_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", hx4700_hp_power),
+ SND_SOC_DAPM_SPK("Speaker", hx4700_spk_power),
+ SND_SOC_DAPM_MIC("Built-in Microphone", NULL),
+};
+
+/* hx4700 machine audio_map */
+static const struct snd_soc_dapm_route hx4700_audio_map[] = {
+
+ /* Headphone connected to LOUT, ROUT */
+ {"Headphone Jack", NULL, "LOUT"},
+ {"Headphone Jack", NULL, "ROUT"},
+
+ /* Speaker connected to MOUT2 */
+ {"Speaker", NULL, "MOUT2"},
+
+ /* Microphone connected to MICIN */
+ {"MICIN", NULL, "Built-in Microphone"},
+ {"AIN", NULL, "MICOUT"},
+};
+
+/*
+ * Logic for a ak4641 as connected on a HP iPAQ hx4700
+ */
+static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int err;
+
+ /* Jack detection API stuff */
+ err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &hs_jack, hs_jack_pin,
+ ARRAY_SIZE(hs_jack_pin));
+ if (err)
+ return err;
+
+ err = snd_soc_jack_add_gpios(&hs_jack, 1, &hs_jack_gpio);
+
+ return err;
+}
+
+/* hx4700 digital audio interface glue - connects codec <--> CPU */
+SND_SOC_DAILINK_DEFS(ak4641,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("ak4641.0-0012", "ak4641-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link hx4700_dai = {
+ .name = "ak4641",
+ .stream_name = "AK4641",
+ .init = hx4700_ak4641_init,
+ .dai_fmt = SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &hx4700_ops,
+ SND_SOC_DAILINK_REG(ak4641),
+};
+
+/* hx4700 audio machine driver */
+static struct snd_soc_card snd_soc_card_hx4700 = {
+ .name = "iPAQ hx4700",
+ .owner = THIS_MODULE,
+ .dai_link = &hx4700_dai,
+ .num_links = 1,
+ .dapm_widgets = hx4700_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hx4700_dapm_widgets),
+ .dapm_routes = hx4700_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(hx4700_audio_map),
+ .fully_routed = true,
+};
+
+static struct gpio hx4700_audio_gpios[] = {
+ { GPIO107_HX4700_SPK_nSD, GPIOF_OUT_INIT_HIGH, "SPK_POWER" },
+ { GPIO92_HX4700_HP_DRIVER, GPIOF_OUT_INIT_LOW, "EP_POWER" },
+};
+
+static int hx4700_audio_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (!machine_is_h4700())
+ return -ENODEV;
+
+ ret = gpio_request_array(hx4700_audio_gpios,
+ ARRAY_SIZE(hx4700_audio_gpios));
+ if (ret)
+ return ret;
+
+ snd_soc_card_hx4700.dev = &pdev->dev;
+ ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700);
+ if (ret)
+ gpio_free_array(hx4700_audio_gpios,
+ ARRAY_SIZE(hx4700_audio_gpios));
+
+ return ret;
+}
+
+static int hx4700_audio_remove(struct platform_device *pdev)
+{
+ gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
+ gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
+
+ gpio_free_array(hx4700_audio_gpios, ARRAY_SIZE(hx4700_audio_gpios));
+ return 0;
+}
+
+static struct platform_driver hx4700_audio_driver = {
+ .driver = {
+ .name = "hx4700-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = hx4700_audio_probe,
+ .remove = hx4700_audio_remove,
+};
+
+module_platform_driver(hx4700_audio_driver);
+
+MODULE_AUTHOR("Philipp Zabel");
+MODULE_DESCRIPTION("ALSA SoC iPAQ hx4700");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:hx4700-audio");
diff --git a/marvell/linux/sound/soc/pxa/imote2.c b/marvell/linux/sound/soc/pxa/imote2.c
new file mode 100644
index 0000000..514e177
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/imote2.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+
+#include "../codecs/wm8940.h"
+#include "pxa2xx-i2s.h"
+
+static int imote2_asoc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ unsigned int clk = 0;
+ int ret;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ clk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk = 11289600;
+ break;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as input (unused) */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, clk,
+ SND_SOC_CLOCK_OUT);
+
+ return ret;
+}
+
+static const struct snd_soc_ops imote2_asoc_ops = {
+ .hw_params = imote2_asoc_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(wm8940,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8940-codec.0-0034",
+ "wm8940-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link imote2_dai = {
+ .name = "WM8940",
+ .stream_name = "WM8940",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &imote2_asoc_ops,
+ SND_SOC_DAILINK_REG(wm8940),
+};
+
+static struct snd_soc_card imote2 = {
+ .name = "Imote2",
+ .owner = THIS_MODULE,
+ .dai_link = &imote2_dai,
+ .num_links = 1,
+};
+
+static int imote2_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &imote2;
+ int ret;
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ return ret;
+}
+
+static struct platform_driver imote2_driver = {
+ .driver = {
+ .name = "imote2-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = imote2_probe,
+};
+
+module_platform_driver(imote2_driver);
+
+MODULE_AUTHOR("Jonathan Cameron");
+MODULE_DESCRIPTION("ALSA SoC Imote 2");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imote2-audio");
diff --git a/marvell/linux/sound/soc/pxa/magician.c b/marvell/linux/sound/soc/pxa/magician.c
new file mode 100644
index 0000000..6483cff
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/magician.c
@@ -0,0 +1,433 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SoC audio for HTC Magician
+ *
+ * Copyright (c) 2006 Philipp Zabel <philipp.zabel@gmail.com>
+ *
+ * based on spitz.c,
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Richard Purdie <richard@openedhand.com>
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/uda1380.h>
+
+#include <mach/magician.h>
+#include <asm/mach-types.h>
+#include "../codecs/uda1380.h"
+#include "pxa2xx-i2s.h"
+#include "pxa-ssp.h"
+
+#define MAGICIAN_MIC 0
+#define MAGICIAN_MIC_EXT 1
+
+static int magician_hp_switch;
+static int magician_spk_switch = 1;
+static int magician_in_sel = MAGICIAN_MIC;
+
+static void magician_ext_control(struct snd_soc_dapm_context *dapm)
+{
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (magician_spk_switch)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
+ if (magician_hp_switch)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+
+ switch (magician_in_sel) {
+ case MAGICIAN_MIC:
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Call Mic");
+ break;
+ case MAGICIAN_MIC_EXT:
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Call Mic");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Mic");
+ break;
+ }
+
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static int magician_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ /* check the jack status at stream startup */
+ magician_ext_control(&rtd->card->dapm);
+
+ return 0;
+}
+
+/*
+ * Magician uses SSP port for playback.
+ */
+static int magician_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ unsigned int width;
+ int ret = 0;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MSB |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ width = snd_pcm_format_physical_width(params_format(params));
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 0, 1, width);
+ if (ret < 0)
+ return ret;
+
+ /* set audio clock as clock source */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Magician uses I2S for capture.
+ */
+static int magician_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as output */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_ops magician_capture_ops = {
+ .startup = magician_startup,
+ .hw_params = magician_capture_hw_params,
+};
+
+static const struct snd_soc_ops magician_playback_ops = {
+ .startup = magician_startup,
+ .hw_params = magician_playback_hw_params,
+};
+
+static int magician_get_hp(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = magician_hp_switch;
+ return 0;
+}
+
+static int magician_set_hp(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (magician_hp_switch == ucontrol->value.integer.value[0])
+ return 0;
+
+ magician_hp_switch = ucontrol->value.integer.value[0];
+ magician_ext_control(&card->dapm);
+ return 1;
+}
+
+static int magician_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = magician_spk_switch;
+ return 0;
+}
+
+static int magician_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (magician_spk_switch == ucontrol->value.integer.value[0])
+ return 0;
+
+ magician_spk_switch = ucontrol->value.integer.value[0];
+ magician_ext_control(&card->dapm);
+ return 1;
+}
+
+static int magician_get_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = magician_in_sel;
+ return 0;
+}
+
+static int magician_set_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (magician_in_sel == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ magician_in_sel = ucontrol->value.enumerated.item[0];
+
+ switch (magician_in_sel) {
+ case MAGICIAN_MIC:
+ gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 1);
+ break;
+ case MAGICIAN_MIC_EXT:
+ gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 0);
+ }
+
+ return 1;
+}
+
+static int magician_spk_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+static int magician_hp_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(EGPIO_MAGICIAN_EP_POWER, SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+static int magician_mic_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+/* magician machine dapm widgets */
+static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", magician_hp_power),
+ SND_SOC_DAPM_SPK("Speaker", magician_spk_power),
+ SND_SOC_DAPM_MIC("Call Mic", magician_mic_bias),
+ SND_SOC_DAPM_MIC("Headset Mic", magician_mic_bias),
+};
+
+/* magician machine audio_map */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* Headphone connected to VOUTL, VOUTR */
+ {"Headphone Jack", NULL, "VOUTL"},
+ {"Headphone Jack", NULL, "VOUTR"},
+
+ /* Speaker connected to VOUTL, VOUTR */
+ {"Speaker", NULL, "VOUTL"},
+ {"Speaker", NULL, "VOUTR"},
+
+ /* Mics are connected to VINM */
+ {"VINM", NULL, "Headset Mic"},
+ {"VINM", NULL, "Call Mic"},
+};
+
+static const char * const input_select[] = {"Call Mic", "Headset Mic"};
+static const struct soc_enum magician_in_sel_enum =
+ SOC_ENUM_SINGLE_EXT(2, input_select);
+
+static const struct snd_kcontrol_new uda1380_magician_controls[] = {
+ SOC_SINGLE_BOOL_EXT("Headphone Switch",
+ (unsigned long)&magician_hp_switch,
+ magician_get_hp, magician_set_hp),
+ SOC_SINGLE_BOOL_EXT("Speaker Switch",
+ (unsigned long)&magician_spk_switch,
+ magician_get_spk, magician_set_spk),
+ SOC_ENUM_EXT("Input Select", magician_in_sel_enum,
+ magician_get_input, magician_set_input),
+};
+
+/* magician digital audio interface glue - connects codec <--> CPU */
+SND_SOC_DAILINK_DEFS(playback,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.0")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018",
+ "uda1380-hifi-playback")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+SND_SOC_DAILINK_DEFS(capture,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018",
+ "uda1380-hifi-capture")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link magician_dai[] = {
+{
+ .name = "uda1380",
+ .stream_name = "UDA1380 Playback",
+ .ops = &magician_playback_ops,
+ SND_SOC_DAILINK_REG(playback),
+},
+{
+ .name = "uda1380",
+ .stream_name = "UDA1380 Capture",
+ .ops = &magician_capture_ops,
+ SND_SOC_DAILINK_REG(capture),
+}
+};
+
+/* magician audio machine driver */
+static struct snd_soc_card snd_soc_card_magician = {
+ .name = "Magician",
+ .owner = THIS_MODULE,
+ .dai_link = magician_dai,
+ .num_links = ARRAY_SIZE(magician_dai),
+
+ .controls = uda1380_magician_controls,
+ .num_controls = ARRAY_SIZE(uda1380_magician_controls),
+ .dapm_widgets = uda1380_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .fully_routed = true,
+};
+
+static struct platform_device *magician_snd_device;
+
+/*
+ * FIXME: move into magician board file once merged into the pxa tree
+ */
+static struct uda1380_platform_data uda1380_info = {
+ .gpio_power = EGPIO_MAGICIAN_CODEC_POWER,
+ .gpio_reset = EGPIO_MAGICIAN_CODEC_RESET,
+ .dac_clk = UDA1380_DAC_CLK_WSPLL,
+};
+
+static struct i2c_board_info i2c_board_info[] = {
+ {
+ I2C_BOARD_INFO("uda1380", 0x18),
+ .platform_data = &uda1380_info,
+ },
+};
+
+static int __init magician_init(void)
+{
+ int ret;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+
+ if (!machine_is_magician())
+ return -ENODEV;
+
+ adapter = i2c_get_adapter(0);
+ if (!adapter)
+ return -ENODEV;
+ client = i2c_new_device(adapter, i2c_board_info);
+ i2c_put_adapter(adapter);
+ if (!client)
+ return -ENODEV;
+
+ ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER");
+ if (ret)
+ goto err_request_spk;
+ ret = gpio_request(EGPIO_MAGICIAN_EP_POWER, "EP_POWER");
+ if (ret)
+ goto err_request_ep;
+ ret = gpio_request(EGPIO_MAGICIAN_MIC_POWER, "MIC_POWER");
+ if (ret)
+ goto err_request_mic;
+ ret = gpio_request(EGPIO_MAGICIAN_IN_SEL0, "IN_SEL0");
+ if (ret)
+ goto err_request_in_sel0;
+ ret = gpio_request(EGPIO_MAGICIAN_IN_SEL1, "IN_SEL1");
+ if (ret)
+ goto err_request_in_sel1;
+
+ gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0);
+
+ magician_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!magician_snd_device) {
+ ret = -ENOMEM;
+ goto err_pdev;
+ }
+
+ platform_set_drvdata(magician_snd_device, &snd_soc_card_magician);
+ ret = platform_device_add(magician_snd_device);
+ if (ret) {
+ platform_device_put(magician_snd_device);
+ goto err_pdev;
+ }
+
+ return 0;
+
+err_pdev:
+ gpio_free(EGPIO_MAGICIAN_IN_SEL1);
+err_request_in_sel1:
+ gpio_free(EGPIO_MAGICIAN_IN_SEL0);
+err_request_in_sel0:
+ gpio_free(EGPIO_MAGICIAN_MIC_POWER);
+err_request_mic:
+ gpio_free(EGPIO_MAGICIAN_EP_POWER);
+err_request_ep:
+ gpio_free(EGPIO_MAGICIAN_SPK_POWER);
+err_request_spk:
+ return ret;
+}
+
+static void __exit magician_exit(void)
+{
+ platform_device_unregister(magician_snd_device);
+
+ gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0);
+ gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0);
+ gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0);
+
+ gpio_free(EGPIO_MAGICIAN_IN_SEL1);
+ gpio_free(EGPIO_MAGICIAN_IN_SEL0);
+ gpio_free(EGPIO_MAGICIAN_MIC_POWER);
+ gpio_free(EGPIO_MAGICIAN_EP_POWER);
+ gpio_free(EGPIO_MAGICIAN_SPK_POWER);
+}
+
+module_init(magician_init);
+module_exit(magician_exit);
+
+MODULE_AUTHOR("Philipp Zabel");
+MODULE_DESCRIPTION("ALSA SoC Magician");
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/sound/soc/pxa/mioa701_wm9713.c b/marvell/linux/sound/soc/pxa/mioa701_wm9713.c
new file mode 100644
index 0000000..129eb52
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/mioa701_wm9713.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Handles the Mitac mioa701 SoC system
+ *
+ * Copyright (C) 2008 Robert Jarzmik
+ *
+ * This is a little schema of the sound interconnections :
+ *
+ * Sagem X200 Wolfson WM9713
+ * +--------+ +-------------------+ Rear Speaker
+ * | | | | /-+
+ * | +--->----->---+MONOIN SPKL+--->----+-+ |
+ * | GSM | | | | | |
+ * | +--->----->---+PCBEEP SPKR+--->----+-+ |
+ * | CHIP | | | \-+
+ * | +---<-----<---+MONO |
+ * | | | | Front Speaker
+ * +--------+ | | /-+
+ * | HPL+--->----+-+ |
+ * | | | | |
+ * | OUT3+--->----+-+ |
+ * | | \-+
+ * | |
+ * | | Front Micro
+ * | | +
+ * | MIC1+-----<--+o+
+ * | | +
+ * +-------------------+ ---
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-types.h>
+#include <mach/audio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+
+#include "../codecs/wm9713.h"
+
+#define AC97_GPIO_PULL 0x58
+
+/* Use GPIO8 for rear speaker amplifier */
+static int rear_amp_power(struct snd_soc_component *component, int power)
+{
+ unsigned short reg;
+
+ if (power) {
+ reg = snd_soc_component_read32(component, AC97_GPIO_CFG);
+ snd_soc_component_write(component, AC97_GPIO_CFG, reg | 0x0100);
+ reg = snd_soc_component_read32(component, AC97_GPIO_PULL);
+ snd_soc_component_write(component, AC97_GPIO_PULL, reg | (1<<15));
+ } else {
+ reg = snd_soc_component_read32(component, AC97_GPIO_CFG);
+ snd_soc_component_write(component, AC97_GPIO_CFG, reg & ~0x0100);
+ reg = snd_soc_component_read32(component, AC97_GPIO_PULL);
+ snd_soc_component_write(component, AC97_GPIO_PULL, reg & ~(1<<15));
+ }
+
+ return 0;
+}
+
+static int rear_amp_event(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kctl, int event)
+{
+ struct snd_soc_card *card = widget->dapm->card;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_component *component;
+
+ rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
+ component = rtd->codec_dai->component;
+ return rear_amp_power(component, SND_SOC_DAPM_EVENT_ON(event));
+}
+
+/* mioa701 machine dapm widgets */
+static const struct snd_soc_dapm_widget mioa701_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Front Speaker", NULL),
+ SND_SOC_DAPM_SPK("Rear Speaker", rear_amp_event),
+ SND_SOC_DAPM_MIC("Headset", NULL),
+ SND_SOC_DAPM_LINE("GSM Line Out", NULL),
+ SND_SOC_DAPM_LINE("GSM Line In", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Front Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Call Mic */
+ {"Mic Bias", NULL, "Front Mic"},
+ {"MIC1", NULL, "Mic Bias"},
+
+ /* Headset Mic */
+ {"LINEL", NULL, "Headset Mic"},
+ {"LINER", NULL, "Headset Mic"},
+
+ /* GSM Module */
+ {"MONOIN", NULL, "GSM Line Out"},
+ {"PCBEEP", NULL, "GSM Line Out"},
+ {"GSM Line In", NULL, "MONO"},
+
+ /* headphone connected to HPL, HPR */
+ {"Headset", NULL, "HPL"},
+ {"Headset", NULL, "HPR"},
+
+ /* front speaker connected to HPL, OUT3 */
+ {"Front Speaker", NULL, "HPL"},
+ {"Front Speaker", NULL, "OUT3"},
+
+ /* rear speaker connected to SPKL, SPKR */
+ {"Rear Speaker", NULL, "SPKL"},
+ {"Rear Speaker", NULL, "SPKR"},
+};
+
+static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = rtd->codec_dai->component;
+
+ /* Prepare GPIO8 for rear speaker amplifier */
+ snd_soc_component_update_bits(component, AC97_GPIO_CFG, 0x100, 0x100);
+
+ /* Prepare MIC input */
+ snd_soc_component_update_bits(component, AC97_3D_CONTROL, 0xc000, 0xc000);
+
+ return 0;
+}
+
+static struct snd_soc_ops mioa701_ops;
+
+SND_SOC_DAILINK_DEFS(ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9713-codec", "wm9713-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+SND_SOC_DAILINK_DEFS(ac97_aux,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9713-codec", "wm9713-aux")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link mioa701_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .init = mioa701_wm9713_init,
+ .ops = &mioa701_ops,
+ SND_SOC_DAILINK_REG(ac97),
+ },
+ {
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ .ops = &mioa701_ops,
+ SND_SOC_DAILINK_REG(ac97_aux),
+ },
+};
+
+static struct snd_soc_card mioa701 = {
+ .name = "MioA701",
+ .owner = THIS_MODULE,
+ .dai_link = mioa701_dai,
+ .num_links = ARRAY_SIZE(mioa701_dai),
+
+ .dapm_widgets = mioa701_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mioa701_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+static int mioa701_wm9713_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ if (!machine_is_mioa701())
+ return -ENODEV;
+
+ mioa701.dev = &pdev->dev;
+ rc = devm_snd_soc_register_card(&pdev->dev, &mioa701);
+ if (!rc)
+ dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will "
+ "lead to overheating and possible destruction of your device."
+ " Do not use without a good knowledge of mio's board design!\n");
+ return rc;
+}
+
+static struct platform_driver mioa701_wm9713_driver = {
+ .probe = mioa701_wm9713_probe,
+ .driver = {
+ .name = "mioa701-wm9713",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+
+module_platform_driver(mioa701_wm9713_driver);
+
+/* Module information */
+MODULE_AUTHOR("Robert Jarzmik (rjarzmik@free.fr)");
+MODULE_DESCRIPTION("ALSA SoC WM9713 MIO A701");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mioa701-wm9713");
diff --git a/marvell/linux/sound/soc/pxa/mmp-pcm.c b/marvell/linux/sound/soc/pxa/mmp-pcm.c
new file mode 100644
index 0000000..e9f9642
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/mmp-pcm.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * linux/sound/soc/pxa/mmp-pcm.c
+ *
+ * Copyright (C) 2011 Marvell International Ltd.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/platform_data/dma-mmp_tdma.h>
+#include <linux/platform_data/mmp_audio.h>
+
+#include <sound/pxa2xx-lib.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#define DRV_NAME "mmp-pcm"
+
+struct mmp_dma_data {
+ int ssp_id;
+ struct resource *dma_res;
+};
+
+#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \
+ SNDRV_PCM_INFO_MMAP_VALID | \
+ SNDRV_PCM_INFO_INTERLEAVED | \
+ SNDRV_PCM_INFO_PAUSE | \
+ SNDRV_PCM_INFO_RESUME | \
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP)
+
+static struct snd_pcm_hardware mmp_pcm_hardware[] = {
+ {
+ .info = MMP_PCM_INFO,
+ .period_bytes_min = 1024,
+ .period_bytes_max = 2048,
+ .periods_min = 2,
+ .periods_max = 32,
+ .buffer_bytes_max = 4096,
+ .fifo_size = 32,
+ },
+ {
+ .info = MMP_PCM_INFO,
+ .period_bytes_min = 1024,
+ .period_bytes_max = 2048,
+ .periods_min = 2,
+ .periods_max = 32,
+ .buffer_bytes_max = 4096,
+ .fifo_size = 32,
+ },
+};
+
+static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct dma_slave_config slave_config;
+ int ret;
+
+ ret =
+ snd_dmaengine_pcm_prepare_slave_config(substream, params,
+ &slave_config);
+ if (ret)
+ return ret;
+
+ ret = dmaengine_slave_config(chan, &slave_config);
+ if (ret)
+ return ret;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+ struct mmp_dma_data *dma_data = param;
+ bool found = false;
+ char *devname;
+
+ devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name,
+ dma_data->ssp_id);
+ if (devname && (strcmp(dev_name(chan->device->dev), devname) == 0) &&
+ (chan->chan_id == dma_data->dma_res->start)) {
+ found = true;
+ }
+
+ kfree(devname);
+ return found;
+}
+
+static int mmp_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ struct platform_device *pdev = to_platform_device(component->dev);
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct mmp_dma_data dma_data;
+ struct resource *r;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream);
+ if (!r)
+ return -EBUSY;
+
+ snd_soc_set_runtime_hwparams(substream,
+ &mmp_pcm_hardware[substream->stream]);
+
+ dma_data.dma_res = r;
+ dma_data.ssp_id = cpu_dai->id;
+
+ return snd_dmaengine_pcm_open_request_chan(substream, filter,
+ &dma_data);
+}
+
+static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long off = vma->vm_pgoff;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ return remap_pfn_range(vma, vma->vm_start,
+ __phys_to_pfn(runtime->dma_addr) + off,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+static const struct snd_pcm_ops mmp_pcm_ops = {
+ .open = mmp_pcm_open,
+ .close = snd_dmaengine_pcm_close_release_chan,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = mmp_pcm_hw_params,
+ .trigger = snd_dmaengine_pcm_trigger,
+ .pointer = snd_dmaengine_pcm_pointer,
+ .mmap = mmp_pcm_mmap,
+};
+
+static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+ struct gen_pool *gpool;
+
+ gpool = sram_get_gpool("asram");
+ if (!gpool)
+ return;
+
+ for (stream = 0; stream < 2; stream++) {
+ size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
+
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+ gen_pool_free(gpool, (unsigned long)buf->area, size);
+ buf->area = NULL;
+ }
+
+}
+
+static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
+ int stream)
+{
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
+ struct gen_pool *gpool;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = substream->pcm->card->dev;
+ buf->private_data = NULL;
+
+ gpool = sram_get_gpool("asram");
+ if (!gpool)
+ return -ENOMEM;
+
+ buf->area = gen_pool_dma_alloc(gpool, size, &buf->addr);
+ if (!buf->area)
+ return -ENOMEM;
+ buf->bytes = size;
+ return 0;
+}
+
+static int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret = 0, stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+
+ ret = mmp_pcm_preallocate_dma_buffer(substream, stream);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ mmp_pcm_free_dma_buffers(pcm);
+ return ret;
+}
+
+static const struct snd_soc_component_driver mmp_soc_component = {
+ .name = DRV_NAME,
+ .ops = &mmp_pcm_ops,
+ .pcm_new = mmp_pcm_new,
+ .pcm_free = mmp_pcm_free_dma_buffers,
+};
+
+static int mmp_pcm_probe(struct platform_device *pdev)
+{
+ struct mmp_audio_platdata *pdata = pdev->dev.platform_data;
+
+ if (pdata) {
+ mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max =
+ pdata->buffer_max_playback;
+ mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max =
+ pdata->period_max_playback;
+ mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max =
+ pdata->buffer_max_capture;
+ mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max =
+ pdata->period_max_capture;
+ }
+ return devm_snd_soc_register_component(&pdev->dev, &mmp_soc_component,
+ NULL, 0);
+}
+
+static struct platform_driver mmp_pcm_driver = {
+ .driver = {
+ .name = "mmp-pcm-audio",
+ },
+
+ .probe = mmp_pcm_probe,
+};
+
+module_platform_driver(mmp_pcm_driver);
+
+MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
+MODULE_DESCRIPTION("MMP Soc Audio DMA module");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mmp-pcm-audio");
diff --git a/marvell/linux/sound/soc/pxa/mmp-sspa.c b/marvell/linux/sound/soc/pxa/mmp-sspa.c
new file mode 100644
index 0000000..e3e5425
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/mmp-sspa.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * linux/sound/soc/pxa/mmp-sspa.c
+ * Base on pxa2xx-ssp.c
+ *
+ * Copyright (C) 2011 Marvell International Ltd.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/pxa2xx_ssp.h>
+#include <linux/io.h>
+#include <linux/dmaengine.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
+#include "mmp-sspa.h"
+
+/*
+ * SSPA audio private data
+ */
+struct sspa_priv {
+ struct ssp_device *sspa;
+ struct snd_dmaengine_dai_dma_data *dma_params;
+ struct clk *audio_clk;
+ struct clk *sysclk;
+ int dai_fmt;
+ int running_cnt;
+};
+
+static void mmp_sspa_write_reg(struct ssp_device *sspa, u32 reg, u32 val)
+{
+ __raw_writel(val, sspa->mmio_base + reg);
+}
+
+static u32 mmp_sspa_read_reg(struct ssp_device *sspa, u32 reg)
+{
+ return __raw_readl(sspa->mmio_base + reg);
+}
+
+static void mmp_sspa_tx_enable(struct ssp_device *sspa)
+{
+ unsigned int sspa_sp;
+
+ sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP);
+ sspa_sp |= SSPA_SP_S_EN;
+ sspa_sp |= SSPA_SP_WEN;
+ mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+}
+
+static void mmp_sspa_tx_disable(struct ssp_device *sspa)
+{
+ unsigned int sspa_sp;
+
+ sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP);
+ sspa_sp &= ~SSPA_SP_S_EN;
+ sspa_sp |= SSPA_SP_WEN;
+ mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+}
+
+static void mmp_sspa_rx_enable(struct ssp_device *sspa)
+{
+ unsigned int sspa_sp;
+
+ sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP);
+ sspa_sp |= SSPA_SP_S_EN;
+ sspa_sp |= SSPA_SP_WEN;
+ mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
+}
+
+static void mmp_sspa_rx_disable(struct ssp_device *sspa)
+{
+ unsigned int sspa_sp;
+
+ sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP);
+ sspa_sp &= ~SSPA_SP_S_EN;
+ sspa_sp |= SSPA_SP_WEN;
+ mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
+}
+
+static int mmp_sspa_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sspa_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ clk_enable(priv->sysclk);
+ clk_enable(priv->sspa->clk);
+
+ return 0;
+}
+
+static void mmp_sspa_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sspa_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable(priv->sspa->clk);
+ clk_disable(priv->sysclk);
+
+}
+
+/*
+ * Set the SSP ports SYSCLK.
+ */
+static int mmp_sspa_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct sspa_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+
+ switch (clk_id) {
+ case MMP_SSPA_CLK_AUDIO:
+ ret = clk_set_rate(priv->audio_clk, freq);
+ if (ret)
+ return ret;
+ break;
+ case MMP_SSPA_CLK_PLL:
+ case MMP_SSPA_CLK_VCXO:
+ /* not support yet */
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mmp_sspa_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct sspa_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+
+ switch (pll_id) {
+ case MMP_SYSCLK:
+ ret = clk_set_rate(priv->sysclk, freq_out);
+ if (ret)
+ return ret;
+ break;
+ case MMP_SSPA_CLK:
+ ret = clk_set_rate(priv->sspa->clk, freq_out);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * Set up the sspa dai format. The sspa port must be inactive
+ * before calling this function as the physical
+ * interface format is changed.
+ */
+static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ssp_device *sspa = sspa_priv->sspa;
+ u32 sspa_sp, sspa_ctrl;
+
+ /* check if we need to change anything at all */
+ if (sspa_priv->dai_fmt == fmt)
+ return 0;
+
+ /* we can only change the settings if the port is not in use */
+ if ((mmp_sspa_read_reg(sspa, SSPA_TXSP) & SSPA_SP_S_EN) ||
+ (mmp_sspa_read_reg(sspa, SSPA_RXSP) & SSPA_SP_S_EN)) {
+ dev_err(&sspa->pdev->dev,
+ "can't change hardware dai format: stream is in use\n");
+ return -EINVAL;
+ }
+
+ /* reset port settings */
+ sspa_sp = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH;
+ sspa_ctrl = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ sspa_sp |= SSPA_SP_MSL;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ sspa_sp |= SSPA_SP_FSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ sspa_sp |= SSPA_TXSP_FPER(63);
+ sspa_sp |= SSPA_SP_FWID(31);
+ sspa_ctrl |= SSPA_CTL_XDATDLY(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+ mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
+
+ sspa_sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH);
+ mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+ mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
+
+ /*
+ * FIXME: hw issue, for the tx serial port,
+ * can not config the master/slave mode;
+ * so must clean this bit.
+ * The master/slave mode has been set in the
+ * rx port.
+ */
+ sspa_sp &= ~SSPA_SP_MSL;
+ mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+
+ mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl);
+ mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl);
+
+ /* Since we are configuring the timings for the format by hand
+ * we have to defer some things until hw_params() where we
+ * know parameters like the sample size.
+ */
+ sspa_priv->dai_fmt = fmt;
+ return 0;
+}
+
+/*
+ * Set the SSPA audio DMA parameters and sample size.
+ * Can be called multiple times by oss emulation.
+ */
+static int mmp_sspa_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai);
+ struct ssp_device *sspa = sspa_priv->sspa;
+ struct snd_dmaengine_dai_dma_data *dma_params;
+ u32 sspa_ctrl;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_TXCTL);
+ else
+ sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_RXCTL);
+
+ sspa_ctrl &= ~SSPA_CTL_XFRLEN1_MASK;
+ sspa_ctrl |= SSPA_CTL_XFRLEN1(params_channels(params) - 1);
+ sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK;
+ sspa_ctrl |= SSPA_CTL_XWDLEN1(SSPA_CTL_32_BITS);
+ sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_8_BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_16_BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_20_BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_24_BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_32_BITS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl);
+ mmp_sspa_write_reg(sspa, SSPA_TXFIFO_LL, 0x1);
+ } else {
+ mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl);
+ mmp_sspa_write_reg(sspa, SSPA_RXFIFO_UL, 0x0);
+ }
+
+ dma_params = &sspa_priv->dma_params[substream->stream];
+ dma_params->addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ (sspa->phys_base + SSPA_TXD) :
+ (sspa->phys_base + SSPA_RXD);
+ snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params);
+ return 0;
+}
+
+static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai);
+ struct ssp_device *sspa = sspa_priv->sspa;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /*
+ * whatever playback or capture, must enable rx.
+ * this is a hw issue, so need check if rx has been
+ * enabled or not; if has been enabled by another
+ * stream, do not enable again.
+ */
+ if (!sspa_priv->running_cnt)
+ mmp_sspa_rx_enable(sspa);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mmp_sspa_tx_enable(sspa);
+
+ sspa_priv->running_cnt++;
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ sspa_priv->running_cnt--;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mmp_sspa_tx_disable(sspa);
+
+ /* have no capture stream, disable rx port */
+ if (!sspa_priv->running_cnt)
+ mmp_sspa_rx_disable(sspa);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mmp_sspa_probe(struct snd_soc_dai *dai)
+{
+ struct sspa_priv *priv = dev_get_drvdata(dai->dev);
+
+ snd_soc_dai_set_drvdata(dai, priv);
+ return 0;
+
+}
+
+#define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000
+#define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops mmp_sspa_dai_ops = {
+ .startup = mmp_sspa_startup,
+ .shutdown = mmp_sspa_shutdown,
+ .trigger = mmp_sspa_trigger,
+ .hw_params = mmp_sspa_hw_params,
+ .set_sysclk = mmp_sspa_set_dai_sysclk,
+ .set_pll = mmp_sspa_set_dai_pll,
+ .set_fmt = mmp_sspa_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver mmp_sspa_dai = {
+ .probe = mmp_sspa_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 128,
+ .rates = MMP_SSPA_RATES,
+ .formats = MMP_SSPA_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MMP_SSPA_RATES,
+ .formats = MMP_SSPA_FORMATS,
+ },
+ .ops = &mmp_sspa_dai_ops,
+};
+
+static const struct snd_soc_component_driver mmp_sspa_component = {
+ .name = "mmp-sspa",
+};
+
+static int asoc_mmp_sspa_probe(struct platform_device *pdev)
+{
+ struct sspa_priv *priv;
+
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct sspa_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->sspa = devm_kzalloc(&pdev->dev,
+ sizeof(struct ssp_device), GFP_KERNEL);
+ if (priv->sspa == NULL)
+ return -ENOMEM;
+
+ priv->dma_params = devm_kcalloc(&pdev->dev,
+ 2, sizeof(struct snd_dmaengine_dai_dma_data),
+ GFP_KERNEL);
+ if (priv->dma_params == NULL)
+ return -ENOMEM;
+
+ priv->sspa->mmio_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->sspa->mmio_base))
+ return PTR_ERR(priv->sspa->mmio_base);
+
+ priv->sspa->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->sspa->clk))
+ return PTR_ERR(priv->sspa->clk);
+
+ priv->audio_clk = clk_get(NULL, "mmp-audio");
+ if (IS_ERR(priv->audio_clk))
+ return PTR_ERR(priv->audio_clk);
+
+ priv->sysclk = clk_get(NULL, "mmp-sysclk");
+ if (IS_ERR(priv->sysclk)) {
+ clk_put(priv->audio_clk);
+ return PTR_ERR(priv->sysclk);
+ }
+ clk_enable(priv->audio_clk);
+ priv->dai_fmt = (unsigned int) -1;
+ platform_set_drvdata(pdev, priv);
+
+ return devm_snd_soc_register_component(&pdev->dev, &mmp_sspa_component,
+ &mmp_sspa_dai, 1);
+}
+
+static int asoc_mmp_sspa_remove(struct platform_device *pdev)
+{
+ struct sspa_priv *priv = platform_get_drvdata(pdev);
+
+ clk_disable(priv->audio_clk);
+ clk_put(priv->audio_clk);
+ clk_put(priv->sysclk);
+ return 0;
+}
+
+static struct platform_driver asoc_mmp_sspa_driver = {
+ .driver = {
+ .name = "mmp-sspa-dai",
+ },
+ .probe = asoc_mmp_sspa_probe,
+ .remove = asoc_mmp_sspa_remove,
+};
+
+module_platform_driver(asoc_mmp_sspa_driver);
+
+MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
+MODULE_DESCRIPTION("MMP SSPA SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mmp-sspa-dai");
diff --git a/marvell/linux/sound/soc/pxa/mmp-sspa.h b/marvell/linux/sound/soc/pxa/mmp-sspa.h
new file mode 100644
index 0000000..7d1b7c7
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/mmp-sspa.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * linux/sound/soc/pxa/mmp-sspa.h
+ *
+ * Copyright (C) 2011 Marvell International Ltd.
+ */
+#ifndef _MMP_SSPA_H
+#define _MMP_SSPA_H
+
+/*
+ * SSPA Registers
+ */
+#define SSPA_RXD (0x00)
+#define SSPA_RXID (0x04)
+#define SSPA_RXCTL (0x08)
+#define SSPA_RXSP (0x0c)
+#define SSPA_RXFIFO_UL (0x10)
+#define SSPA_RXINT_MASK (0x14)
+#define SSPA_RXC (0x18)
+#define SSPA_RXFIFO_NOFS (0x1c)
+#define SSPA_RXFIFO_SIZE (0x20)
+
+#define SSPA_TXD (0x80)
+#define SSPA_TXID (0x84)
+#define SSPA_TXCTL (0x88)
+#define SSPA_TXSP (0x8c)
+#define SSPA_TXFIFO_LL (0x90)
+#define SSPA_TXINT_MASK (0x94)
+#define SSPA_TXC (0x98)
+#define SSPA_TXFIFO_NOFS (0x9c)
+#define SSPA_TXFIFO_SIZE (0xa0)
+
+/* SSPA Control Register */
+#define SSPA_CTL_XPH (1 << 31) /* Read Phase */
+#define SSPA_CTL_XFIG (1 << 15) /* Transmit Zeros when FIFO Empty */
+#define SSPA_CTL_JST (1 << 3) /* Audio Sample Justification */
+#define SSPA_CTL_XFRLEN2_MASK (7 << 24)
+#define SSPA_CTL_XFRLEN2(x) ((x) << 24) /* Transmit Frame Length in Phase 2 */
+#define SSPA_CTL_XWDLEN2_MASK (7 << 21)
+#define SSPA_CTL_XWDLEN2(x) ((x) << 21) /* Transmit Word Length in Phase 2 */
+#define SSPA_CTL_XDATDLY(x) ((x) << 19) /* Tansmit Data Delay */
+#define SSPA_CTL_XSSZ2_MASK (7 << 16)
+#define SSPA_CTL_XSSZ2(x) ((x) << 16) /* Transmit Sample Audio Size */
+#define SSPA_CTL_XFRLEN1_MASK (7 << 8)
+#define SSPA_CTL_XFRLEN1(x) ((x) << 8) /* Transmit Frame Length in Phase 1 */
+#define SSPA_CTL_XWDLEN1_MASK (7 << 5)
+#define SSPA_CTL_XWDLEN1(x) ((x) << 5) /* Transmit Word Length in Phase 1 */
+#define SSPA_CTL_XSSZ1_MASK (7 << 0)
+#define SSPA_CTL_XSSZ1(x) ((x) << 0) /* XSSZ1 */
+
+#define SSPA_CTL_8_BITS (0x0) /* Sample Size */
+#define SSPA_CTL_12_BITS (0x1)
+#define SSPA_CTL_16_BITS (0x2)
+#define SSPA_CTL_20_BITS (0x3)
+#define SSPA_CTL_24_BITS (0x4)
+#define SSPA_CTL_32_BITS (0x5)
+
+/* SSPA Serial Port Register */
+#define SSPA_SP_WEN (1 << 31) /* Write Configuration Enable */
+#define SSPA_SP_MSL (1 << 18) /* Master Slave Configuration */
+#define SSPA_SP_CLKP (1 << 17) /* CLKP Polarity Clock Edge Select */
+#define SSPA_SP_FSP (1 << 16) /* FSP Polarity Clock Edge Select */
+#define SSPA_SP_FFLUSH (1 << 2) /* FIFO Flush */
+#define SSPA_SP_S_RST (1 << 1) /* Active High Reset Signal */
+#define SSPA_SP_S_EN (1 << 0) /* Serial Clock Domain Enable */
+#define SSPA_SP_FWID(x) ((x) << 20) /* Frame-Sync Width */
+#define SSPA_TXSP_FPER(x) ((x) << 4) /* Frame-Sync Active */
+
+/* sspa clock sources */
+#define MMP_SSPA_CLK_PLL 0
+#define MMP_SSPA_CLK_VCXO 1
+#define MMP_SSPA_CLK_AUDIO 3
+
+/* sspa pll id */
+#define MMP_SYSCLK 0
+#define MMP_SSPA_CLK 1
+
+#endif /* _MMP_SSPA_H */
diff --git a/marvell/linux/sound/soc/pxa/palm27x.c b/marvell/linux/sound/soc/pxa/palm27x.c
new file mode 100644
index 0000000..b92ea1a
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/palm27x.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * linux/sound/soc/pxa/palm27x.c
+ *
+ * SoC Audio driver for Palm T|X, T5 and LifeDrive
+ *
+ * based on tosa.c
+ *
+ * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include <asm/mach-types.h>
+#include <mach/audio.h>
+#include <linux/platform_data/asoc-palm27x.h>
+
+static struct snd_soc_jack hs_jack;
+
+/* Headphones jack detection DAPM pins */
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+/* Headphones jack detection gpios */
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+ [0] = {
+ /* gpio is set on per-platform basis */
+ .name = "hp-gpio",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 200,
+ },
+};
+
+/* Palm27x machine dapm widgets */
+static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext. Speaker", NULL),
+ SND_SOC_DAPM_MIC("Ext. Microphone", NULL),
+};
+
+/* PalmTX audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* headphone connected to HPOUTL, HPOUTR */
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Headphone Jack", NULL, "HPOUTR"},
+
+ /* ext speaker connected to ROUT2, LOUT2 */
+ {"Ext. Speaker", NULL, "LOUT2"},
+ {"Ext. Speaker", NULL, "ROUT2"},
+
+ /* mic connected to MIC1 */
+ {"MIC1", NULL, "Ext. Microphone"},
+};
+
+static struct snd_soc_card palm27x_asoc;
+
+static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int err;
+
+ /* Jack detection API stuff */
+ err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &hs_jack, hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
+ if (err)
+ return err;
+
+ err = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+
+ return err;
+}
+
+SND_SOC_DAILINK_DEFS(hifi,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+SND_SOC_DAILINK_DEFS(aux,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-aux")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link palm27x_dai[] = {
+{
+ .name = "AC97 HiFi",
+ .stream_name = "AC97 HiFi",
+ .init = palm27x_ac97_init,
+ SND_SOC_DAILINK_REG(hifi),
+},
+{
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ SND_SOC_DAILINK_REG(aux),
+},
+};
+
+static struct snd_soc_card palm27x_asoc = {
+ .name = "Palm/PXA27x",
+ .owner = THIS_MODULE,
+ .dai_link = palm27x_dai,
+ .num_links = ARRAY_SIZE(palm27x_dai),
+ .dapm_widgets = palm27x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(palm27x_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .fully_routed = true,
+};
+
+static int palm27x_asoc_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (!(machine_is_palmtx() || machine_is_palmt5() ||
+ machine_is_palmld() || machine_is_palmte2()))
+ return -ENODEV;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "please supply platform_data\n");
+ return -ENODEV;
+ }
+
+ hs_jack_gpios[0].gpio = ((struct palm27x_asoc_info *)
+ (pdev->dev.platform_data))->jack_gpio;
+
+ palm27x_asoc.dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, &palm27x_asoc);
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ return ret;
+}
+
+static struct platform_driver palm27x_wm9712_driver = {
+ .probe = palm27x_asoc_probe,
+ .driver = {
+ .name = "palm27x-asoc",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+
+module_platform_driver(palm27x_wm9712_driver);
+
+/* Module information */
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:palm27x-asoc");
diff --git a/marvell/linux/sound/soc/pxa/poodle.c b/marvell/linux/sound/soc/pxa/poodle.c
new file mode 100644
index 0000000..48d5c22
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/poodle.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * poodle.c -- SoC audio for Poodle
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Richard Purdie <richard@openedhand.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware/locomo.h>
+#include <mach/poodle.h>
+#include <mach/audio.h>
+
+#include "../codecs/wm8731.h"
+#include "pxa2xx-i2s.h"
+
+#define POODLE_HP 1
+#define POODLE_HP_OFF 0
+#define POODLE_SPK_ON 1
+#define POODLE_SPK_OFF 0
+
+ /* audio clock in Hz - rounded from 12.235MHz */
+#define POODLE_AUDIO_CLOCK 12288000
+
+static int poodle_jack_func;
+static int poodle_spk_func;
+
+static void poodle_ext_control(struct snd_soc_dapm_context *dapm)
+{
+ /* set up jack connection */
+ if (poodle_jack_func == POODLE_HP) {
+ /* set = unmute headphone */
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_L, 1);
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_R, 1);
+ snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
+ } else {
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_L, 0);
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_R, 0);
+ snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
+ }
+
+ /* set the enpoints to their new connetion states */
+ if (poodle_spk_func == POODLE_SPK_ON)
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk");
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync(dapm);
+}
+
+static int poodle_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ /* check the jack status at stream startup */
+ poodle_ext_control(&rtd->card->dapm);
+
+ return 0;
+}
+
+/* we need to unmute the HP at shutdown as the mute burns power on poodle */
+static void poodle_shutdown(struct snd_pcm_substream *substream)
+{
+ /* set = unmute headphone */
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_L, 1);
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_R, 1);
+}
+
+static int poodle_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ unsigned int clk = 0;
+ int ret = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ clk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk = 11289600;
+ break;
+ }
+
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as input (unused) */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_ops poodle_ops = {
+ .startup = poodle_startup,
+ .hw_params = poodle_hw_params,
+ .shutdown = poodle_shutdown,
+};
+
+static int poodle_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = poodle_jack_func;
+ return 0;
+}
+
+static int poodle_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (poodle_jack_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ poodle_jack_func = ucontrol->value.enumerated.item[0];
+ poodle_ext_control(&card->dapm);
+ return 1;
+}
+
+static int poodle_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = poodle_spk_func;
+ return 0;
+}
+
+static int poodle_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (poodle_spk_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ poodle_spk_func = ucontrol->value.enumerated.item[0];
+ poodle_ext_control(&card->dapm);
+ return 1;
+}
+
+static int poodle_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_AMP_ON, 0);
+ else
+ locomo_gpio_write(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_AMP_ON, 1);
+
+ return 0;
+}
+
+/* poodle machine dapm widgets */
+static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
+SND_SOC_DAPM_HP("Headphone Jack", NULL),
+SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
+SND_SOC_DAPM_MIC("Microphone", NULL),
+};
+
+/* Corgi machine connections to the codec pins */
+static const struct snd_soc_dapm_route poodle_audio_map[] = {
+
+ /* headphone connected to LHPOUT1, RHPOUT1 */
+ {"Headphone Jack", NULL, "LHPOUT"},
+ {"Headphone Jack", NULL, "RHPOUT"},
+
+ /* speaker connected to LOUT, ROUT */
+ {"Ext Spk", NULL, "ROUT"},
+ {"Ext Spk", NULL, "LOUT"},
+
+ {"MICIN", NULL, "Microphone"},
+};
+
+static const char * const jack_function[] = {"Off", "Headphone"};
+static const char * const spk_function[] = {"Off", "On"};
+static const struct soc_enum poodle_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new wm8731_poodle_controls[] = {
+ SOC_ENUM_EXT("Jack Function", poodle_enum[0], poodle_get_jack,
+ poodle_set_jack),
+ SOC_ENUM_EXT("Speaker Function", poodle_enum[1], poodle_get_spk,
+ poodle_set_spk),
+};
+
+/* poodle digital audio interface glue - connects codec <--> CPU */
+SND_SOC_DAILINK_DEFS(wm8731,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link poodle_dai = {
+ .name = "WM8731",
+ .stream_name = "WM8731",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &poodle_ops,
+ SND_SOC_DAILINK_REG(wm8731),
+};
+
+/* poodle audio machine driver */
+static struct snd_soc_card poodle = {
+ .name = "Poodle",
+ .dai_link = &poodle_dai,
+ .num_links = 1,
+ .owner = THIS_MODULE,
+
+ .controls = wm8731_poodle_controls,
+ .num_controls = ARRAY_SIZE(wm8731_poodle_controls),
+ .dapm_widgets = wm8731_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
+ .dapm_routes = poodle_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(poodle_audio_map),
+ .fully_routed = true,
+};
+
+static int poodle_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &poodle;
+ int ret;
+
+ locomo_gpio_set_dir(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_AMP_ON, 0);
+ /* should we mute HP at startup - burning power ?*/
+ locomo_gpio_set_dir(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_L, 0);
+ locomo_gpio_set_dir(&poodle_locomo_device.dev,
+ POODLE_LOCOMO_GPIO_MUTE_R, 0);
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ return ret;
+}
+
+static struct platform_driver poodle_driver = {
+ .driver = {
+ .name = "poodle-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = poodle_probe,
+};
+
+module_platform_driver(poodle_driver);
+
+/* Module information */
+MODULE_AUTHOR("Richard Purdie");
+MODULE_DESCRIPTION("ALSA SoC Poodle");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:poodle-audio");
diff --git a/marvell/linux/sound/soc/pxa/pxa-alc5616.c b/marvell/linux/sound/soc/pxa/pxa-alc5616.c
new file mode 100644
index 0000000..b194ae2
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/pxa-alc5616.c
@@ -0,0 +1,148 @@
+/*
+ * linux/sound/soc/pxa/pxa-alc5616.c
+ *
+ * Copyright (C) 2013 Marvell International Ltd.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/88pm8xxx-headset.h>
+#include <linux/features.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <sound/pcm_params.h>
+#include <linux/pxa2xx_ssp.h>
+#include <linux/input.h>
+#include "pxa-ssp.h"
+
+#ifdef CONFIG_SND_PXA_SSP_DUMP
+int ssp_playback_enable;
+int ssp_capture_enable;
+int gssp_playback_enable;
+int gssp_capture_enable;
+#endif
+
+//extern void enable_5616_MClock(void);
+extern void codec_rt5616_Enable_Headsetdetection(void);
+
+static int pxa_alc5616_hifi_startup(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int pxa_alc5616_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ return 0;
+}
+
+static int pxa_alc5616_hifi_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int pxa_alc5616_late_probe(struct snd_soc_card *card)
+{
+ //enable_5616_MClock();
+ //codec_rt5616_Enable_Headsetdetection();
+ printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
+
+ return 0;
+}
+
+static const struct snd_soc_ops pxa_alc5616_machine_ops[] = {
+ {
+ .startup = pxa_alc5616_hifi_startup,
+ .hw_params = pxa_alc5616_hw_params,
+ .prepare = pxa_alc5616_hifi_prepare,
+ },
+};
+
+#ifdef CONFIG_CPU_ASR1901
+SND_SOC_DAILINK_DEFS(alc5616,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("alc5616.1-001b", "alc5616-pcm")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-ssp-dai.1")));
+#else
+SND_SOC_DAILINK_DEFS(alc5616,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("alc5616.0-001b", "alc5616-pcm")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-ssp-dai.1")));
+#endif
+
+static struct snd_soc_dai_link pxa_alc5616_dai[] = {
+ {
+ .name = "alc5616 pcm",
+ .stream_name = "mrvl-ssp",
+ .ops = &pxa_alc5616_machine_ops[0],
+ SND_SOC_DAILINK_REG(alc5616),
+ },
+};
+
+static struct snd_soc_card pxa_alc5616_card = {
+ .name = "pxa-alc5616-dkb-voice",
+ .dai_link = pxa_alc5616_dai,
+ .num_links = ARRAY_SIZE(pxa_alc5616_dai),
+ .late_probe = pxa_alc5616_late_probe,
+};
+
+static struct of_device_id pxa_alc5616_dt_ids[] = {
+ { .compatible = "ASRMICRO,asrmicro-snd-card", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pxa_alc5616_dt_ids);
+
+static int pxa_alc5616_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card = &pxa_alc5616_card;
+
+ pr_pm_debug(KERN_INFO"[5616 card probe: begin] %s/L%d.\n", __FUNCTION__, __LINE__);
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ //if (ret)
+ //dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
+
+ pr_pm_debug(KERN_INFO"[5616 card probe: end] %s/L%d.\n", __FUNCTION__, __LINE__);
+
+ return ret;
+}
+
+static struct platform_driver pxa_alc5616_driver = {
+ .driver = {
+ .name = "pxa-alc5616-platform",
+ .of_match_table = pxa_alc5616_dt_ids,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = pxa_alc5616_probe,
+};
+
+module_platform_driver(pxa_alc5616_driver);
+
+/* Module information */
+MODULE_AUTHOR("wenchen@asrmicro.com");
+MODULE_DESCRIPTION("ALSA SoC TTC DKB");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa-alc5616-dkb-voice");
diff --git a/marvell/linux/sound/soc/pxa/pxa-nau8810.c b/marvell/linux/sound/soc/pxa/pxa-nau8810.c
new file mode 100644
index 0000000..3568d99
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/pxa-nau8810.c
@@ -0,0 +1,168 @@
+/*
+ * linux/sound/soc/pxa/pxa-nau8810.c
+ *
+ * Copyright (C) 2013 Marvell International Ltd.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/88pm8xxx-headset.h>
+#include <linux/features.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <sound/pcm_params.h>
+#include <linux/pxa2xx_ssp.h>
+#include <linux/input.h>
+#include "pxa-ssp.h"
+
+#ifdef CONFIG_SND_PXA_SSP_DUMP
+int ssp_playback_enable;
+int ssp_capture_enable;
+int gssp_playback_enable;
+int gssp_capture_enable;
+#endif
+
+#ifndef CONFIG_CPU_ASR18XX
+u32 sscr0_hifi;
+u32 sscr1_hifi;
+#else
+u32 top_ctrl_hifi;
+u32 fifo_ctrl_hifi;
+u32 rwot_ctrl_hifi;
+u32 network_ctrl_hifi;
+u32 int_en_hifi;
+#endif
+
+static int pxa_nau8810_hifi_startup(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int pxa_nau8810_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ return 0;
+}
+
+static int pxa_nau8810_hifi_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int pxa_nau8810_late_probe(struct snd_soc_card *card)
+{
+ return 0;
+}
+
+static struct snd_soc_ops pxa_nau8810_machine_ops[] = {
+ {
+ .startup = pxa_nau8810_hifi_startup,
+ .hw_params = pxa_nau8810_hw_params,
+ .prepare = pxa_nau8810_hifi_prepare,
+ },
+};
+
+#if 1
+SND_SOC_DAILINK_DEFS(nau8810,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("nau8810.1-001a", "nau8810-pcm")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-ssp-dai.1")));
+
+static struct snd_soc_dai_link pxa_nau8810_dai[] = {
+ {
+ .name = "nau8810 pcm",
+ .stream_name = "mrvl-ssp",
+ .ops = &pxa_nau8810_machine_ops[0],
+ SND_SOC_DAILINK_REG(nau8810),
+ },
+};
+#else
+static struct snd_soc_dai_link pxa_nau8810_dai[] = {
+ {
+ .name = "nau8810 pcm",
+ .stream_name = "mrvl-ssp",
+ .codec_name = "nau8810.1-001a",
+ .platform_name = "pxa-ssp-dai.1",
+ .cpu_dai_name = "pxa-ssp-dai.1",
+ .codec_dai_name = "nau8810-pcm",
+ .ops = &pxa_nau8810_machine_ops[0],
+ },
+};
+#endif
+
+static struct snd_soc_card pxa_nau8810_card = {
+ .name = "pxa-nau8810-dkb-voice",
+ .dai_link = pxa_nau8810_dai,
+ .num_links = ARRAY_SIZE(pxa_nau8810_dai),
+ .late_probe = pxa_nau8810_late_probe,
+};
+
+static struct of_device_id pxa_nau8810_dt_ids[] = {
+ { .compatible = "ASRMICRO,asrmicro-snd-card", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pxa_nau8810_dt_ids);
+
+static int pxa_nau8810_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card = &pxa_nau8810_card;
+
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+
+ printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
+
+ return ret;
+}
+
+static int pxa_nau8810_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static struct platform_driver pxa_nau8810_driver = {
+ .driver = {
+ .name = "pxa-nau8810-platform",
+ .owner = THIS_MODULE,
+ .of_match_table = pxa_nau8810_dt_ids,
+ },
+ .probe = pxa_nau8810_probe,
+ .remove = pxa_nau8810_remove,
+};
+
+module_platform_driver(pxa_nau8810_driver);
+
+/* Module information */
+MODULE_AUTHOR("wenchen@asrmicro.com");
+MODULE_DESCRIPTION("ALSA SoC TTC DKB");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa-nau8810-dkb-voice");
diff --git a/marvell/linux/sound/soc/pxa/pxa-pcm.c b/marvell/linux/sound/soc/pxa/pxa-pcm.c
new file mode 100644
index 0000000..c9bd854
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/pxa-pcm.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 Marvell International Ltd.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include <linux/dma/pxa-dma.h>
+
+static const struct snd_pcm_hardware pxa_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192 - 32,
+ .periods_min = 1,
+ .periods_max = 256, //PAGE_SIZE/sizeof(pxa_dma_desc),
+ .buffer_bytes_max = 128 * 1024,
+ .fifo_size = 32,
+};
+
+static const struct snd_dmaengine_pcm_config pxa_dmaengine_pcm_config = {
+ .pcm_hardware = &pxa_pcm_hardware,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .prealloc_buffer_size = 128 * 1024,
+};
+
+int pxa_pcm_platform_register(struct device *dev)
+{
+ return snd_dmaengine_pcm_register(dev, &pxa_dmaengine_pcm_config, 0);
+}
+EXPORT_SYMBOL_GPL(pxa_pcm_platform_register);
+
+void pxa_pcm_platform_unregister(struct device *dev)
+{
+ snd_dmaengine_pcm_unregister(dev);
+}
+EXPORT_SYMBOL_GPL(pxa_pcm_platform_unregister);
+
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/sound/soc/pxa/pxa-sndcard.c b/marvell/linux/sound/soc/pxa/pxa-sndcard.c
new file mode 100644
index 0000000..9a3d2eb
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/pxa-sndcard.c
@@ -0,0 +1,977 @@
+/*
+ * linux/sound/soc/pxa/pxa-sndcard.c
+ *
+ * Copyright (C) 2013 Marvell International Ltd.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/88pm8xxx-headset.h>
+#include <linux/features.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <sound/pcm_params.h>
+#include <linux/pxa2xx_ssp.h>
+#include <linux/input.h>
+#include "pxa-ssp.h"
+
+#ifdef CONFIG_SND_PXA_SSP_DUMP
+int ssp_playback_enable;
+int ssp_capture_enable;
+int gssp_playback_enable;
+int gssp_capture_enable;
+#endif
+
+static int gssp_master = 1;
+
+/* CODEC I2S config is Master in config/defconfig_asr1802sp201 */
+#ifdef CONFIG_CODEC_I2S_SLAVE
+
+/* CODEC is Slave, SSP is Master. */
+static int ssp_master = 1;
+
+#else
+
+/* CODEC is Master, SSP is slave. */
+static int ssp_master = 0; //0: PXA SSP as slave, hardware codec as master
+
+#endif
+
+
+u32 top_ctrl_hifi;
+u32 fifo_ctrl_hifi;
+u32 rwot_ctrl_hifi;
+u32 network_ctrl_hifi;
+u32 int_en_hifi;
+
+/* format for ASR1802SL */
+unsigned int channels;
+unsigned int rate;
+unsigned int sample_bits;
+unsigned int frame_bits;
+
+/*
+ * SSP audio private data
+ */
+struct ssp_priv {
+ struct ssp_device *ssp;
+ unsigned int sysclk;
+ int dai_fmt;
+#ifdef CONFIG_PM
+ uint32_t cr0;
+ uint32_t cr1;
+ uint32_t to;
+ uint32_t psp;
+#endif
+};
+
+extern int get_frame_bits(void);
+extern int get_status_switch_frame_bits(void);
+extern int get_status_IFS(void);
+extern int get_status_SFRMP(void);
+extern int get_status_SCMODE(void);
+extern u32 get_reg_value_from_UI(int offset);
+extern int get_status_debug_tools(void);
+
+int get_ssp_role(void)
+{
+ return ssp_master;
+}
+
+void set_ssp_role(int role)
+{
+ ssp_master = role;
+ printk(KERN_INFO"%s: SSP1 role is %s.\n", __FUNCTION__, ssp_master?"Master":"Slave");
+ return;
+}
+
+static ssize_t ssp_master_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", ssp_master?"SSP is Master!":"SSP is Slave!");
+}
+
+static ssize_t ssp_master_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+
+ ret = kstrtoint(buf, 10, &ssp_master);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(ssp_master, 0644, ssp_master_show, ssp_master_set);
+
+static ssize_t gssp_master_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", gssp_master);
+}
+
+static ssize_t gssp_master_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+
+ ret = kstrtoint(buf, 10, &gssp_master);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(gssp_master, 0644, gssp_master_show, gssp_master_set);
+
+static ssize_t gssp_record_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", has_feat_hifi_gssp_record());
+}
+
+static DEVICE_ATTR(gssp_record, 0444, gssp_record_show, NULL);
+
+#ifdef CONFIG_SND_PXA_SSP_DUMP
+static DEVICE_INT_ATTR(ssp_playback_dump, 0644, ssp_playback_enable);
+static DEVICE_INT_ATTR(ssp_capture_dump, 0644, ssp_capture_enable);
+static DEVICE_INT_ATTR(gssp_playback_dump, 0644, gssp_playback_enable);
+static DEVICE_INT_ATTR(gssp_capture_dump, 0644, gssp_capture_enable);
+#endif
+
+static int pxa_sndcard_hifi_startup(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int pxa_sndcard_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ channels = params_channels(params);
+ rate = params_rate(params);
+ sample_bits = snd_pcm_format_physical_width(params_format(params));
+ frame_bits = sample_bits * channels;
+
+ if (get_status_switch_frame_bits())
+ {
+ /* for debug */
+ frame_bits = get_frame_bits();
+ } else {
+ /* get frame length from file format */
+ frame_bits = sample_bits * channels;
+ }
+
+ if (ssp_master)
+ {
+ snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ }
+ else
+ {
+ snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+ snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+ }
+ /*mclk is input clock of codec, it depends on codec configuration
+ *transfered by callback function
+ */
+ snd_soc_dai_set_sysclk(codec_dai, 1, rate, SND_SOC_CLOCK_IN);
+ return 0;
+}
+extern void ssp_dump_registers(struct ssp_device *ssp);
+extern struct ssp_device *g_ssp;
+
+static int pxa_sndcard_hifi_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ssp_device *ssp = priv->ssp;
+
+ u32 top_ctrl, fifo_ctrl, rwot_ctrl, network_ctrl, psp_ctrl, int_en;
+ int scmode, status_debug_tool;
+
+ if (has_feat_hifi_gssp_record()) {
+
+ if(ssp_master) {
+ /* master mmode, drive SCLK and SFRM */
+ top_ctrl_hifi = top_ctrl_hifi & (~0x18);
+ /* Transmit/Receive service request enable
+ * TXFIFO/RXFIFO threshold as 0x8
+ */
+ fifo_ctrl_hifi = fifo_ctrl_hifi | 0xCE7; //RX_AUTO_FULL_CTRL_EN (0x1<<17)???
+ /* Receive without transmit */
+ //rwot_ctrl_hifi = rwot_ctrl_hifi | 0x1;
+ } else {
+ /* slave mmode, receive SCLK and SFRM */
+ top_ctrl_hifi = top_ctrl_hifi | 0x18;
+ /* Transmit/Receive service request enable
+ * TXFIFO/RXFIFO threshold as 0x8
+ */
+ fifo_ctrl_hifi = fifo_ctrl_hifi | 0xCE7; //RX_AUTO_FULL_CTRL_EN (0x1<<17)???
+ /* Receive without transmit */
+ //rwot_ctrl_hifi = rwot_ctrl_hifi | 0x1;
+ }
+
+ /* Frame Rate Divider Ctrl */
+ network_ctrl_hifi = network_ctrl_hifi | (1 << 1);
+ /* TIM/RIM */
+ int_en_hifi = int_en_hifi | (0x3 << 4);
+ /* 32bit data, Frame format as PSP */
+ top_ctrl_hifi = top_ctrl_hifi | (0x1f << 5) | (0x3 << 1);
+ __raw_writel(top_ctrl_hifi, ssp->mmio_base + TOP_CTRL);
+ __raw_writel(fifo_ctrl_hifi, ssp->mmio_base + FIFO_CTRL);
+ __raw_writel(rwot_ctrl_hifi, ssp->mmio_base + RWOT_CTRL);
+ __raw_writel(int_en_hifi, ssp->mmio_base + INT_EN);
+ __raw_writel(network_ctrl_hifi, ssp->mmio_base + NET_WORK_CTRL);
+ __raw_writel(0x00010018, ssp->mmio_base + PSP_CTRL);
+
+ } else {
+
+ top_ctrl = __raw_readl(ssp->mmio_base + TOP_CTRL);
+ fifo_ctrl = __raw_readl(ssp->mmio_base + FIFO_CTRL);
+ rwot_ctrl = __raw_readl(ssp->mmio_base + RWOT_CTRL);
+ int_en = __raw_readl(ssp->mmio_base + INT_EN);
+ network_ctrl = __raw_readl(ssp->mmio_base + NET_WORK_CTRL);
+
+ if(ssp_master){
+ /* ssp is Master */
+#if 1
+ //deleted for debug
+ top_ctrl = 0;
+ fifo_ctrl = 0;
+ int_en = 0;
+ psp_ctrl = 0;
+ network_ctrl = 0;
+ scmode = get_status_SCMODE();
+ status_debug_tool = get_status_debug_tools();
+ //rwot_ctrl = 0;
+
+ //Register:TOP_CTRL[0x00]
+ /* TXD Three-State Enable */
+ top_ctrl = top_ctrl | (0x1 << 17);
+
+ /*
+ *One:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here
+ *IFS: bit15[TOP_CTRL]
+ */
+ if (get_status_IFS()) {
+ top_ctrl = top_ctrl | (0x01 << 15);
+ } else {
+ top_ctrl &= ~(0x01 << 15);
+ }
+
+ /* TRAIL:bit13 DMA bursts */
+ top_ctrl = top_ctrl | (0x1 << 13);
+
+ if (64 == frame_bits) {
+ /* 32bit data size, Frame format as PSP */
+ top_ctrl = top_ctrl | (0x1f << 5);
+ } else if (32 == frame_bits) {
+ /* 32bit data size, Frame format as PSP */
+ top_ctrl = top_ctrl | (0x1f << 5);
+ } else if (16 == frame_bits) {
+ /* 16bit data size, Frame format as PSP */
+ top_ctrl = top_ctrl | (0x0f << 5);
+ }
+
+ /* slave mode, receive SCLK and SFRM */
+ //top_ctrl = top_ctrl | (0x3 << 3);
+ /* master mode, receive SCLK and SFRM */
+ top_ctrl &= ~(0x3 << 3);
+
+ /* Frame format as PSP */
+ top_ctrl = top_ctrl | (0x3 << 1);
+
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(TOP_CTRL), ssp->mmio_base + TOP_CTRL);
+ } else {
+ __raw_writel(top_ctrl, ssp->mmio_base + TOP_CTRL);
+ }
+#if 1
+ /*
+ *Register:FIFO_CTRL[0x04]
+ *Transmit/Receive service request enable
+ *TXFIFO/RXFIFO threshold as 0x8
+ *to avoid interrupt triggering
+ *when ssp configured for transporting 48kHz data
+ */
+
+ //fifo_ctrl = fifo_ctrl | (0x1 << 16);
+ fifo_ctrl = fifo_ctrl | (0x3 << 10);
+
+ /*RX FIFO Trigger Threshold*/
+ fifo_ctrl = fifo_ctrl | (0x7 << 5);
+
+ /*TX FIFO Trigger Threshold*/
+ fifo_ctrl = fifo_ctrl | (0x7 << 0);
+
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(FIFO_CTRL), ssp->mmio_base + FIFO_CTRL);
+ } else {
+ __raw_writel(fifo_ctrl, ssp->mmio_base + FIFO_CTRL);
+ }
+#else
+ /* test2 */
+ fifo_ctrl = fifo_ctrl | (0x1 << 17); /* RX_AUTO_FULL_CTRL_EN */
+ fifo_ctrl &= ~(0x1 << 16);/* FIFO_UNPACKING */
+ fifo_ctrl = fifo_ctrl | (0x1 << 11);/* RX_REQ_EN */
+ fifo_ctrl = fifo_ctrl | (0x1 << 10);/* TX_REQ_EN */
+ fifo_ctrl = fifo_ctrl | (0x7 << 5);/* RX_THRES_7 */
+ fifo_ctrl = fifo_ctrl | (0x7 << 0);/* TX_THRES_7 */
+ __raw_writel(fifo_ctrl, ssp->mmio_base + FIFO_CTRL);
+#endif
+
+#if 0
+ __raw_writel(int_en, ssp->mmio_base + INT_EN);
+#else
+ /* test3 */
+ //int_en = int_en | (0x1 << 3);/* TX_FIFO_INT_EN */
+ //int_en = int_en | (0x1 << 2);/* RX_FIFO_INT_EN */
+ int_en = int_en | (0x1 << 1);/* TIMEOUT_EN */
+ int_en = int_en | (0x1 << 0);/* PERI_TRAIL_EN */
+
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(INT_EN), ssp->mmio_base + INT_EN);
+ } else {
+ __raw_writel(int_en, ssp->mmio_base + INT_EN);
+ }
+#endif
+
+#if 0
+ __raw_writel(0, ssp->mmio_base + TO);
+#else
+ /* test4
+ * set no timeout interrupt here
+ * the interrupt is triggered, which depends on the frequency of the sample rate
+ * If the sample rate is 8KHz, the interrupt is triggered too fast and there is no
+ * interrupt handler to deal with it.
+ */
+ __raw_writel(0x0, ssp->mmio_base + TO);
+#endif
+
+ if (64 == frame_bits) {
+ /* Serial Frame Width:32, SFRMWDTH */
+ psp_ctrl = psp_ctrl | (0x20 << 12);
+ } else if (32 == frame_bits) {
+ /* Register:PSP_CTRL[0x18] */
+ /* SFRMWDTH:must be needed when slave */
+ /* Serial Frame Width:16, SFRMWDTH */
+ psp_ctrl = psp_ctrl | (0x10 << 12);
+ } else if (16 == frame_bits) {
+ /* Serial Frame Width:8, SFRMWDTH */
+ psp_ctrl = psp_ctrl | (0x08 << 12);
+ }
+
+ /*
+ *Two:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here
+ *Frame Polarity bit[4]
+ */
+ /* SFRMP */
+ //psp_ctrl = psp_ctrl | (0x1 << 4);
+ if (get_status_SFRMP()) {
+ psp_ctrl = psp_ctrl | (0x1 << 4);
+ } else {
+ psp_ctrl &= ~(0x01 << 4);
+ }
+
+
+ /* FSRT */
+ psp_ctrl = psp_ctrl | (0x1 << 3);
+
+ /*
+ *Three:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here
+ *Bitclk Polarity bit[1:0]
+ */
+ /* SCMODE */
+#if 0
+ //psp_ctrl = psp_ctrl | (0x0 << 0);
+ psp_ctrl = psp_ctrl | (0x1 << 0);
+ //psp_ctrl = psp_ctrl | (0x2 << 0);
+ //psp_ctrl = psp_ctrl | (0x3 << 0);
+#else
+ //for test
+ if (0x3 == scmode) {
+ psp_ctrl = psp_ctrl | (0x3 << 0);
+ } else if (0x2 == scmode) {
+ psp_ctrl = psp_ctrl | (0x2 << 0);
+ } else if (0x1 == scmode) {
+ psp_ctrl = psp_ctrl | (0x1 << 0);
+ } else if (0x0 == scmode) {
+ psp_ctrl = psp_ctrl | (0x0 << 0);
+ }
+#endif
+
+ /* PSP_CTRL[0x18] */
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(PSP_CTRL), ssp->mmio_base + PSP_CTRL);
+ } else {
+ __raw_writel(psp_ctrl, ssp->mmio_base + PSP_CTRL);
+ }
+
+ if ((32 == frame_bits) || (16 == frame_bits)) {
+ /* NETWORK_CTRL[0x1C] */
+ /* FRDC: must be needed when slave */
+
+ /* Disable the network mode */
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(NET_WORK_CTRL), ssp->mmio_base + NET_WORK_CTRL);
+ } else {
+ __raw_writel(0, ssp->mmio_base + NET_WORK_CTRL);
+ }
+ } else if (64 == frame_bits) {
+ /* Enable the network mode */
+ network_ctrl = network_ctrl | (0x3<<12);/* RX slot time */
+ network_ctrl = network_ctrl | (0x3<<4);/* TX slot time */
+ network_ctrl = network_ctrl | (0x1<<1);/* FRDC */
+ network_ctrl = network_ctrl | (0x1<<0);/* NET_WORK_MOD */
+
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(NET_WORK_CTRL), ssp->mmio_base + NET_WORK_CTRL);
+ } else {
+ __raw_writel(network_ctrl, ssp->mmio_base + NET_WORK_CTRL);
+ }
+ }
+
+ //rwot_ctrl = rwot_ctrl | (1 << 0);
+ //__raw_writel(rwot_ctrl, ssp->mmio_base + RWOT_CTRL);
+
+#if 0
+ top_ctrl |= 1;
+ __raw_writel(top_ctrl, ssp->mmio_base + TOP_CTRL);
+#endif
+
+ // printk(KERN_INFO"master: TOP_CTRL 0x%08x FIFO_CTRL 0x%08x PSP_CTRL 0x%08x\n", top_ctrl, fifo_ctrl, psp_ctrl);
+#endif
+ } else {
+ /* ssp is slave */
+#if 1
+ //deleted for debug
+ top_ctrl = 0;
+ fifo_ctrl = 0;
+ int_en = 0;
+ psp_ctrl = 0;
+ network_ctrl = 0;
+ scmode = get_status_SCMODE();
+ status_debug_tool = get_status_debug_tools();
+ //rwot_ctrl = 0;
+
+ //Register:TOP_CTRL[0x00]
+
+ /* TXD Three-State Enable */
+ top_ctrl = top_ctrl | (0x1 << 17);
+
+ /*
+ *One:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here
+ *IFS: bit15[TOP_CTRL]
+ */
+ if (get_status_IFS()) {
+ top_ctrl = top_ctrl | (0x01 << 15);
+ } else {
+ top_ctrl &= ~(0x01 << 15);
+ }
+
+ /* TRAIL:bit13 DMA bursts */
+ top_ctrl = top_ctrl | (0x1 << 13);
+
+ if (64 == frame_bits) {
+ /* 32bit data size, Frame format as PSP */
+ top_ctrl = top_ctrl | (0x1f << 5);
+ } else if (32 == frame_bits) {
+ /* 32bit data size, Frame format as PSP */
+ top_ctrl = top_ctrl | (0x1f << 5);
+ } else if (16 == frame_bits) {
+ /* 16bit data size, Frame format as PSP */
+ top_ctrl = top_ctrl | (0x0f << 5);
+ }
+
+ /* slave mode, receive SCLK and SFRM */
+ top_ctrl = top_ctrl | (0x3 << 3);
+ /* master mode, receive SCLK and SFRM */
+ //top_ctrl &= ~(0x3 << 3);
+
+ /* Frame format as PSP */
+ top_ctrl = top_ctrl | (0x3 << 1);
+
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(TOP_CTRL), ssp->mmio_base + TOP_CTRL);
+ } else {
+ __raw_writel(top_ctrl, ssp->mmio_base + TOP_CTRL);
+ }
+#if 1
+ /*
+ *Register:FIFO_CTRL[0x04]
+ *Transmit/Receive service request enable
+ *TXFIFO/RXFIFO threshold as 0x8
+ */
+
+ //fifo_ctrl = fifo_ctrl | (0x1 << 16);
+ fifo_ctrl = fifo_ctrl | (0x3 << 10);
+
+ /*RX FIFO Trigger Threshold*/
+ fifo_ctrl = fifo_ctrl | (0x3 << 5);
+
+ /*TX FIFO Trigger Threshold*/
+ fifo_ctrl = fifo_ctrl | (0x3 << 0);
+
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(FIFO_CTRL), ssp->mmio_base + FIFO_CTRL);
+ } else {
+ __raw_writel(fifo_ctrl, ssp->mmio_base + FIFO_CTRL);
+ }
+#else
+ /* test2 */
+ fifo_ctrl = fifo_ctrl | (0x1 << 17); /* RX_AUTO_FULL_CTRL_EN */
+ fifo_ctrl &= ~(0x1 << 16);/* FIFO_UNPACKING */
+ fifo_ctrl = fifo_ctrl | (0x1 << 11);/* RX_REQ_EN */
+ fifo_ctrl = fifo_ctrl | (0x1 << 10);/* TX_REQ_EN */
+ fifo_ctrl = fifo_ctrl | (0x7 << 5);/* RX_THRES_7 */
+ fifo_ctrl = fifo_ctrl | (0x7 << 0);/* TX_THRES_7 */
+ __raw_writel(fifo_ctrl, ssp->mmio_base + FIFO_CTRL);
+#endif
+
+#if 1
+ __raw_writel(int_en, ssp->mmio_base + INT_EN);
+#else
+ /* test3 */
+ int_en = int_en | (0x1 << 3);/* TX_FIFO_INT_EN */
+ int_en = int_en | (0x1 << 2);/* RX_FIFO_INT_EN */
+ int_en = int_en | (0x1 << 1);/* TIMEOUT_EN */
+ int_en = int_en | (0x1 << 0);/* PERI_TRAIL_EN */
+
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(INT_EN), ssp->mmio_base + INT_EN);
+ } else {
+ __raw_writel(int_en, ssp->mmio_base + INT_EN);
+ }
+#endif
+
+#if 1
+ __raw_writel(0, ssp->mmio_base + TO);
+#else
+ /* test4 */
+ __raw_writel(0x200, ssp->mmio_base + TO);
+#endif
+
+ if (64 == frame_bits) {
+ /* Serial Frame Width:32, SFRMWDTH */
+ psp_ctrl = psp_ctrl | (0x20 << 12);
+ } else if (32 == frame_bits) {
+ /* Register:PSP_CTRL[0x18] */
+ /* SFRMWDTH:must be needed when slave */
+ /* Serial Frame Width:16, SFRMWDTH */
+ psp_ctrl = psp_ctrl | (0x10 << 12);
+ } else if (16 == frame_bits) {
+ /* Serial Frame Width:8, SFRMWDTH */
+ psp_ctrl = psp_ctrl | (0x08 << 12);
+ }
+
+ /*
+ *Two:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here
+ *Frame Polarity bit[4]
+ */
+ /* SFRMP */
+ //psp_ctrl = psp_ctrl | (0x1 << 4);
+ if (get_status_SFRMP()) {
+ psp_ctrl = psp_ctrl | (0x1 << 4);
+ } else {
+ psp_ctrl &= ~(0x1 << 4);
+ }
+
+
+ /* FSRT */
+ psp_ctrl = psp_ctrl | (0x1 << 3);
+
+ /*
+ *Three:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here
+ *Bitclk Polarity bit[1:0]
+ */
+ /* SCMODE */
+#if 0
+ //psp_ctrl = psp_ctrl | (0x0 << 0);
+ psp_ctrl = psp_ctrl | (0x1 << 0);
+ //psp_ctrl = psp_ctrl | (0x2 << 0);
+ //psp_ctrl = psp_ctrl | (0x3 << 0);
+#else
+ //for debug
+ if (0x3 == scmode) {
+ psp_ctrl = psp_ctrl | (0x3 << 0);
+ } else if (0x2 == scmode) {
+ psp_ctrl = psp_ctrl | (0x2 << 0);
+ } else if (0x1 == scmode) {
+ psp_ctrl = psp_ctrl | (0x1 << 0);
+ } else if (0x0 == scmode) {
+ psp_ctrl = psp_ctrl | (0x0 << 0);
+ }
+#endif
+
+ /* PSP_CTRL[0x18] */
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(PSP_CTRL), ssp->mmio_base + PSP_CTRL);
+ } else {
+ __raw_writel(psp_ctrl, ssp->mmio_base + PSP_CTRL);
+ }
+
+ if ((32 == frame_bits) || (16 == frame_bits)) {
+ /* NETWORK_CTRL[0x1C] */
+ /* FRDC: must be needed when slave */
+
+ /* Disable the network mode */
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(NET_WORK_CTRL), ssp->mmio_base + NET_WORK_CTRL);
+ } else {
+ __raw_writel(0, ssp->mmio_base + NET_WORK_CTRL);
+ }
+ } else if (64 == frame_bits) {
+ /* Enable the network mode */
+ network_ctrl = network_ctrl | (0x3<<12);/* RX slot time */
+ network_ctrl = network_ctrl | (0x3<<4);/* TX slot time */
+ network_ctrl = network_ctrl | (0x1<<1);/* FRDC */
+ network_ctrl = network_ctrl | (0x1<<0);/* NET_WORK_MOD */
+
+ if (status_debug_tool) {
+ __raw_writel(get_reg_value_from_UI(NET_WORK_CTRL), ssp->mmio_base + NET_WORK_CTRL);
+ } else {
+ __raw_writel(network_ctrl, ssp->mmio_base + NET_WORK_CTRL);
+ }
+ }
+
+ //rwot_ctrl = rwot_ctrl | (1 << 0);
+ //__raw_writel(rwot_ctrl, ssp->mmio_base + RWOT_CTRL);
+
+#endif
+ }
+
+ }
+
+ //ssp_dump_registers(g_ssp);
+
+ return 0;
+}
+
+static int pxa_sndcard_lofi_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* support narrow band: 8K */
+ cpu_dai->driver->playback.rates = SNDRV_PCM_RATE_8000;
+ cpu_dai->driver->capture.rates = SNDRV_PCM_RATE_8000;
+ codec_dai->driver->playback.channels_max = 1;
+ codec_dai->driver->capture.channels_max = 1;
+
+ return 0;
+}
+
+static int pxa_sndcard_dummy_nb_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* support both 8K*/
+ cpu_dai->driver->playback.rates = SNDRV_PCM_RATE_8000;
+ cpu_dai->driver->capture.rates = SNDRV_PCM_RATE_8000;
+ codec_dai->driver->playback.channels_max = 1;
+ codec_dai->driver->capture.channels_max = 1;
+ codec_dai->driver->playback.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ codec_dai->driver->capture.formats = SNDRV_PCM_FMTBIT_S32_LE;
+
+ return 0;
+}
+
+static int pxa_sndcard_dummy_wb_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* support both 16K*/
+ cpu_dai->driver->playback.rates = SNDRV_PCM_RATE_16000;
+ cpu_dai->driver->capture.rates = SNDRV_PCM_RATE_16000;
+ codec_dai->driver->playback.channels_max = 1;
+ codec_dai->driver->capture.channels_max = 1;
+ codec_dai->driver->playback.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ codec_dai->driver->capture.formats = SNDRV_PCM_FMTBIT_S32_LE;
+
+ return 0;
+}
+
+static int pxa_sndcard_pcm_hifi_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* support gssp hifi record: 44.1k */
+ cpu_dai->driver->playback.rates = SNDRV_PCM_RATE_44100;
+ cpu_dai->driver->capture.rates = SNDRV_PCM_RATE_44100;
+ codec_dai->driver->playback.channels_max = 2;
+ codec_dai->driver->capture.channels_max = 2;
+ codec_dai->driver->playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ codec_dai->driver->capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
+
+ return 0;
+}
+
+static int pxa_sndcard_lofi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ /*
+ * Since gssp is used for hifi record on HelanLTE A0,
+ * enable gssp when startup to eliminate noise.
+ * So move re-init gssp registers to pxa-ssp.c.
+ * Do nothing here.
+ */
+ return 0;
+}
+
+static int pxa_sndcard_lofi_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+//static struct snd_soc_jack hs_jack, hk_jack;
+
+static int pxa_sndcard_late_probe(struct snd_soc_card *card)
+{
+ return 0;
+}
+
+static struct snd_soc_ops pxa_sndcard_machine_ops[] = {
+{
+ .startup = pxa_sndcard_hifi_startup,
+ .hw_params = pxa_sndcard_hw_params,
+ .prepare = pxa_sndcard_hifi_prepare,
+},
+{
+ .startup = pxa_sndcard_lofi_startup,
+ .hw_params = pxa_sndcard_lofi_hw_params,
+ .prepare = pxa_sndcard_lofi_prepare,
+},
+{
+ .startup = pxa_sndcard_dummy_nb_startup,
+ .hw_params = pxa_sndcard_lofi_hw_params,
+ .prepare = pxa_sndcard_lofi_prepare,
+},
+{
+ .startup = pxa_sndcard_dummy_wb_startup,
+ .hw_params = pxa_sndcard_lofi_hw_params,
+ .prepare = pxa_sndcard_lofi_prepare,
+},
+{
+ .startup = pxa_sndcard_pcm_hifi_startup,
+ .hw_params = pxa_sndcard_lofi_hw_params,
+ .prepare = pxa_sndcard_lofi_prepare,
+},
+};
+
+#if defined(CONFIG_CODEC_ALC5616)
+
+SND_SOC_DAILINK_DEFS(alc5616,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("alc5616.0-001b", "alc5616-pcm")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-ssp-dai.1")));
+
+static struct snd_soc_dai_link pxa_alc5616_dai[] = {
+ {
+ .name = "alc5616 pcm",
+ .stream_name = "mrvl-ssp",
+ .ops = &pxa_sndcard_machine_ops[0],
+ SND_SOC_DAILINK_REG(alc5616),
+ },
+};
+
+static struct snd_soc_card pxa_alc5616_card = {
+ .name = "pxa-alc5616-dkb-voice",
+ .dai_link = pxa_alc5616_dai,
+ .num_links = ARRAY_SIZE(pxa_alc5616_dai),
+ .late_probe = pxa_sndcard_late_probe,
+};
+
+#else
+
+SND_SOC_DAILINK_DEFS(sspbt,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("sspbt.0-001c", "sspbt-pcm")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-ssp-dai.1")));
+
+static struct snd_soc_dai_link pxa_sspbt_dai[] = {
+ {
+ .name = "sspbt pcm",
+ .stream_name = "mrvl-ssp",
+ .ops = &pxa_sndcard_machine_ops[0],
+ SND_SOC_DAILINK_REG(sspbt),
+ },
+};
+
+static struct snd_soc_card pxa_sspbt_card = {
+ .name = "pxa-sspbt-dkb-voice",
+ .dai_link = pxa_sspbt_dai,
+ .num_links = ARRAY_SIZE(pxa_sspbt_dai),
+ .late_probe = pxa_sndcard_late_probe,
+};
+#endif
+
+#ifdef CONFIG_PM
+static int pxa_sndcard_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int pxa_sndcard_resume(struct device *dev)
+{
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pxa_sndcard_pm_ops, pxa_sndcard_suspend,
+ pxa_sndcard_resume);
+
+static struct of_device_id pxa_sndcard_dt_ids[] = {
+ { .compatible = "ASRMICRO,asrmicro-snd-card", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pxa_sndcard_dt_ids);
+
+static int pxa_sndcard_probe(struct platform_device *pdev)
+{
+#if defined(CONFIG_CODEC_ALC5616)
+ struct snd_soc_card *card = &pxa_alc5616_card;
+#else
+ struct snd_soc_card *card = &pxa_sspbt_card;
+#endif
+
+ int ret;
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
+ ret);
+
+ /* add ssp_master sysfs entries */
+ ret = device_create_file(&pdev->dev, &dev_attr_ssp_master);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "%s: failed to add ssp_master sysfs files: %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ /* add gssp_master sysfs entries */
+ ret = device_create_file(&pdev->dev, &dev_attr_gssp_master);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "%s: failed to add gssp_master sysfs files: %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ /* add gssp_record sysfs entries */
+ ret = device_create_file(&pdev->dev, &dev_attr_gssp_record);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "%s: failed to add gssp_record sysfs files: %d\n",
+ __func__, ret);
+ goto err;
+ }
+#ifdef CONFIG_SND_PXA_SSP_DUMP
+ /* add ssp_playback_dump sysfs entries */
+ ret = device_create_file(&pdev->dev, &dev_attr_ssp_playback_dump.attr);
+ if (ret < 0)
+ dev_err(&pdev->dev,
+ "%s: failed to add ssp_playback_dump sysfs files: %d\n",
+ __func__, ret);
+ /* add ssp_capture_dump sysfs entries */
+ ret = device_create_file(&pdev->dev, &dev_attr_ssp_capture_dump.attr);
+ if (ret < 0)
+ dev_err(&pdev->dev,
+ "%s: failed to add ssp_capture_dump sysfs files: %d\n",
+ __func__, ret);
+ /* add gssp_playback_dump sysfs entries */
+ ret = device_create_file(&pdev->dev, &dev_attr_gssp_playback_dump.attr);
+ if (ret < 0)
+ dev_err(&pdev->dev,
+ "%s: failed to add gssp_playback_dump sysfs files: %d\n",
+ __func__, ret);
+ /* add gssp_capture_dump sysfs entries */
+ ret = device_create_file(&pdev->dev, &dev_attr_gssp_capture_dump.attr);
+ if (ret < 0)
+ dev_err(&pdev->dev,
+ "%s: failed to add gssp_capture_dump sysfs files: %d\n",
+ __func__, ret);
+#endif
+ return ret;
+err:
+ snd_soc_unregister_card(card);
+ return ret;
+}
+
+static int pxa_sndcard_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ device_remove_file(&pdev->dev, &dev_attr_ssp_master);
+ device_remove_file(&pdev->dev, &dev_attr_gssp_master);
+#ifdef CONFIG_SND_PXA_SSP_DUMP
+ device_remove_file(&pdev->dev, &dev_attr_ssp_playback_dump.attr);
+ device_remove_file(&pdev->dev, &dev_attr_ssp_capture_dump.attr);
+ device_remove_file(&pdev->dev, &dev_attr_gssp_playback_dump.attr);
+ device_remove_file(&pdev->dev, &dev_attr_gssp_capture_dump.attr);
+#endif
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static struct platform_driver pxa_sndcard_driver = {
+ .driver = {
+ .name = "pxa-sndcard-hifi",
+ .owner = THIS_MODULE,
+ .of_match_table = pxa_sndcard_dt_ids,
+ .pm = &pxa_sndcard_pm_ops,
+ },
+ .probe = pxa_sndcard_probe,
+ .remove = pxa_sndcard_remove,
+};
+
+module_platform_driver(pxa_sndcard_driver);
+
+/* Module information */
+MODULE_AUTHOR("wenchen@asrmicro.com");
+MODULE_DESCRIPTION("ALSA SoC TTC DKB");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa-sndcard-hifi");
diff --git a/marvell/linux/sound/soc/pxa/pxa-ssp.c b/marvell/linux/sound/soc/pxa/pxa-ssp.c
new file mode 100644
index 0000000..0f0b360
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/pxa-ssp.c
@@ -0,0 +1,2293 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pxa-ssp.c -- ALSA Soc Audio Layer
+ *
+ * Copyright 2005,2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * TODO:
+ * o Test network mode for > 16bit sample size
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pxa2xx_ssp.h>
+#include <linux/of.h>
+#include <linux/dmaengine.h>
+#include <linux/delay.h>
+#include <linux/platform_data/mmp_audio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/features.h>
+#include <linux/debugfs.h>
+
+#include <asm/irq.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
+#include <linux/dma-mapping.h>
+#include "pxa-ssp.h"
+#include <linux/pxa2xx_ssp.h>
+
+
+extern u32 top_ctrl_hifi;
+extern u32 fifo_ctrl_hifi;
+extern u32 rwot_ctrl_hifi;
+extern u32 network_ctrl_hifi;
+extern u32 int_en_hifi;
+
+extern void set_ssp_role(int role);
+extern int get_ssp_role(void);
+
+static int reg_ssp = 0xffff;
+struct dentry *ssp_dump_reg = NULL;
+struct ssp_device *g_ssp = NULL;
+
+#define APBC_SSP1_CLK_RST_ADDR (0xD4015020)
+#define PMU_SQU_CLK_GATE_CTRL_ADDR (0xD428281C)
+#define SQU_CTRL_0_ADDR (0xD42A0000)
+#define FCCR_ADDR (0xD4050008)
+#define ISCCR2_ADDR (0xD4050044)
+
+#define ICU_DMA_SEAGULL_INT_STATUS_ADDR (0xD4282120)
+
+#define DMA_BASE_ADDR (0xD4000000)
+
+static void __iomem *APBC_SSP1_CLK_RST_reg = NULL;
+static void __iomem *PMU_SQU_CLK_GATE_CTRL_reg = NULL;
+static void __iomem *SQU_CTRL_0_reg = NULL;
+static void __iomem *FCCR_reg = NULL;
+static void __iomem *ISCCR2_reg = NULL;
+
+static void __iomem *ICU_DMA_SEAGULL_INT_STATUS_reg = NULL;
+
+static void __iomem *DMA_BASE_reg = NULL;
+
+/* Debug Master/SLave, Frame sync, BitClk for SSP */
+typedef enum{
+ SSP_BCLK_16_FS = 0,
+ SSP_BCLK_32_FS,
+ SSP_BCLK_64_FS,
+ SSP_BCLK_MAX_FS = SSP_BCLK_64_FS
+} SSP_BCLK_TYPE;
+
+typedef enum{
+ SAMPLE_FREQUENCY_8000 = 0,
+ SAMPLE_FREQUENCY_16000,
+ SAMPLE_FREQUENCY_44100,
+ MAX_SAMPLE_FREQUENCY = SAMPLE_FREQUENCY_44100
+} SAMPLE_FREQUENCY_ID;
+
+typedef enum{
+ SSP_FRAME_BITS_16FS = 0,
+ SSP_FRAME_BITS_32FS,
+ SSP_FRAME_BITS_64FS,
+ SSP_FRAME_BITS_MAX_FS = SSP_FRAME_BITS_64FS
+} SSP_FRAME_BITS;
+
+static int g_gssp_bclk_type = SSP_BCLK_32_FS;
+static int g_switch_frame_bits = SSP_FRAME_BITS_32FS;
+static int g_enable_switch_frame_bits = 0;
+static int g_status_debug_tools = 0;/* debug tools for ssp register */
+static int g_status_IFS = 1;/* SSP_TOP_CTRL bit15 */
+static int g_status_SFRMP = 0;/* SSP_PSP_CTRL bit4 */
+static int g_status_SCMODE = 1;/* SSP_PSP_CTRL bit1~0 0x0~0x3 */
+static int g_frame_bits = (1 << SSP_FRAME_BITS_32FS) * 16;
+static int Audio_WB_AMR_Enable = 0;
+
+u32 top_ctrl_UI = 0;
+u32 fifo_ctrl_UI = 0;
+u32 int_en_UI = 0;
+u32 psp_ctrl_UI = 0;
+u32 network_ctrl_UI = 0;
+
+u32 get_reg_value_from_UI(int offset)
+{
+ if (TOP_CTRL == offset) {
+ /* SSP_TOP_CTRL */
+ return top_ctrl_UI;
+ } else if (FIFO_CTRL == offset) {
+ /* SSP_FIFO_CTRL */
+ return fifo_ctrl_UI;
+ } else if (INT_EN == offset) {
+ /* SSP_INT_CTRL */
+ return int_en_UI;
+ } else if (PSP_CTRL == offset) {
+ /* SSP_PSP_CTRL */
+ return psp_ctrl_UI;
+ } else if (NET_WORK_CTRL == offset) {
+ /* SSP_NET_WORK_CTRL */
+ return network_ctrl_UI;
+ }
+
+ return 0;
+}
+
+/* Enable the clk */
+static void UI_enable_clk(struct ssp_device *ssp)
+{
+ clk_prepare_enable(ssp->clk);
+ return;
+}
+
+/* Disable the clk */
+static void UI_disable_clk(struct ssp_device *ssp)
+{
+ clk_disable_unprepare(ssp->clk);
+
+ return;
+}
+
+/* Enable the ssp */
+static void UI_enable_ssp(struct ssp_device *ssp)
+{
+ uint32_t top_ctrl;
+
+ top_ctrl = __raw_readl(ssp->mmio_base + TOP_CTRL) | TOP_SSE;
+ __raw_writel(top_ctrl, ssp->mmio_base + TOP_CTRL);
+ return;
+}
+
+/* Disable the ssp */
+static void UI_disable_ssp(struct ssp_device *ssp)
+{
+ uint32_t top_ctrl;
+
+ top_ctrl = __raw_readl(ssp->mmio_base + TOP_CTRL) & ~TOP_SSE;
+ __raw_writel(top_ctrl, ssp->mmio_base + TOP_CTRL);
+ return;
+}
+
+/* Master/Slave switch */
+static void UI_switch_SSP_role(void)
+{
+ int ssp_role;
+
+ ssp_role = get_ssp_role();
+ set_ssp_role(++ssp_role % 2);
+
+ return;
+}
+
+/* Frame sync */
+static void UI_Reverse_WBNB(void)
+{
+ if (Audio_WB_AMR_Enable)
+ Audio_WB_AMR_Enable = 0;
+ else
+ Audio_WB_AMR_Enable = 1;
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s/L%d, Audio_WB_AMR_Enable is %s.\n", __FUNCTION__, __LINE__, Audio_WB_AMR_Enable?"WB":"NB");
+#endif
+ return;
+}
+
+/* BitClk */
+static int set_BCLK_type(int bclk_type)
+{
+ if(bclk_type > SSP_BCLK_MAX_FS){
+ printk(KERN_INFO"BCLK_MAX_FS = %d, bclk_type = %d.\n", g_gssp_bclk_type, bclk_type);
+
+ return -1;
+ }
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"old = %d, new = %d.\n", g_gssp_bclk_type, bclk_type);
+#endif
+ g_gssp_bclk_type = bclk_type;
+ return 0;
+}
+
+static void UI_switch_bitclk_FS(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"switch_bitclk_fs.\n");
+#endif
+ set_BCLK_type((g_gssp_bclk_type + 1) % 4);
+ return;
+}
+
+/* FRAME BITS */
+static int set_frame_bits(int switch_frame_bits)
+{
+ g_switch_frame_bits = switch_frame_bits;
+ g_frame_bits = (1 << switch_frame_bits) * 16;
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_switch_frame_bits=%d, g_frame_bits=%d.\n", __FUNCTION__, g_switch_frame_bits, g_frame_bits);
+#endif
+ return 0;
+}
+
+int get_frame_bits(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_frame_bits=%d.\n", __FUNCTION__, g_frame_bits);
+#endif
+ return g_frame_bits;
+}
+
+static void UI_switch_frame_bits(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"switch frame bits.\n");
+#endif
+ set_frame_bits((g_switch_frame_bits + 1) % 3);
+ return;
+}
+
+/* Enable/Disable switch frame bits */
+static int enable_switch_frame_bits(int status)
+{
+ g_enable_switch_frame_bits = status;
+
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_enable_switch_frame_bits=%d.\n", __FUNCTION__, g_enable_switch_frame_bits);
+#endif
+ return 0;
+}
+
+int get_status_switch_frame_bits(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_enable_switch_frame_bits=%d.\n", __FUNCTION__, g_enable_switch_frame_bits);
+#endif
+ return g_enable_switch_frame_bits;
+}
+
+static void UI_enable_switch_frame_bits(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"enable/disable switch frame bits.\n");
+#endif
+ enable_switch_frame_bits((g_enable_switch_frame_bits + 1) % 2);
+ return;
+}
+
+/* open debug interface for several registers control in pxa_88pm805_hifi_prepare() pxa-88pm805.c */
+static int switch_debug_tools(int status)
+{
+ g_status_debug_tools = status;
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_status_debug_tools=%d.\n", __FUNCTION__, g_status_debug_tools);
+#endif
+ return 0;
+}
+
+int get_status_debug_tools(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_status_debug_tools=%d.\n", __FUNCTION__, g_status_debug_tools);
+#endif
+ return g_status_debug_tools;
+}
+
+static void UI_switch_debug_tools(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"UI_switch_debug_tools.\n");
+#endif
+ switch_debug_tools((g_status_debug_tools + 1) % 2);
+ return;
+}
+
+/* Display all global control variables */
+static void UI_display_global_ctrl_var(void)
+{
+ printk(KERN_INFO"g_gssp_bclk_type = %d.\n", g_gssp_bclk_type);
+ printk(KERN_INFO"g_switch_frame_bits = %d.\n", g_switch_frame_bits);
+ printk(KERN_INFO"g_enable_switch_frame_bits = %d.\n", g_enable_switch_frame_bits);
+ printk(KERN_INFO"g_status_debug_tools = %d.\n", g_status_debug_tools);
+ printk(KERN_INFO"g_status_IFS = %d.\n", g_status_IFS);
+ printk(KERN_INFO"g_status_SFRMP = %d.\n", g_status_SFRMP);
+ printk(KERN_INFO"g_status_SCMODE = %d.\n", g_status_SCMODE);
+ printk(KERN_INFO"g_frame_bits = %d.\n", g_frame_bits);
+ printk(KERN_INFO"Audio_WB_AMR_Enable = %d.\n", Audio_WB_AMR_Enable);
+
+ return;
+}
+
+/* SSP_TOP_CTRL bit15: IFS = Invert Frame Signal */
+static int switch_IFS(int status)
+{
+ g_status_IFS = status;
+
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_status_IFS=%d.\n", __FUNCTION__, g_status_IFS);
+#endif
+ return 0;
+}
+
+int get_status_IFS(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_status_IFS=%d.\n", __FUNCTION__, g_status_IFS);
+#endif
+ return g_status_IFS;
+}
+
+static void UI_switch_IFS(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"UI_switch_IFS.\n");
+#endif
+ switch_IFS((g_status_IFS + 1) % 2);
+ return;
+}
+
+/* SSP_PSP_CTRL bit4: SFRMP = Serial Frame Polarity */
+static int switch_SFRMP(int status)
+{
+ g_status_SFRMP = status;
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_status_SFRMP=%d.\n", __FUNCTION__, g_status_SFRMP);
+#endif
+ return 0;
+}
+
+int get_status_SFRMP(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_status_SFRMP=%d.\n", __FUNCTION__, g_status_SFRMP);
+#endif
+ return g_status_SFRMP;
+}
+
+static void UI_switch_SFRMP(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"UI_switch_SFRMP.\n");
+#endif
+ switch_SFRMP((g_status_SFRMP + 1) % 2);
+ return;
+}
+
+/* SSP_PSP_CTRL bit1~0: SCMODE = Serial Bit-rate Clock Mode */
+static int switch_SCMODE(int status)
+{
+ g_status_SCMODE = status;
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_status_SCMODE=%d.\n", __FUNCTION__, g_status_SCMODE);
+#endif
+ return 0;
+}
+
+int get_status_SCMODE(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: g_status_SCMODE=%d.\n", __FUNCTION__, g_status_SCMODE);
+#endif
+ return g_status_SCMODE;
+}
+
+static void UI_switch_SCMODE(void)
+{
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"UI_switch_SCMODE.\n");
+#endif
+ switch_SCMODE((g_status_SCMODE + 1) % 4);
+ return;
+}
+
+#if 0
+/* config I2S format */
+static void UI_config_SSP_I2S_format(void)
+{
+ /* write the register */
+ pxa_ssp_write_reg(g_ssp, 0x00, 0x0002a3e7);
+ pxa_ssp_write_reg(g_ssp, 0x04, 0x00000c63);
+ pxa_ssp_write_reg(g_ssp, 0x08, 0x0000000f);
+ pxa_ssp_write_reg(g_ssp, 0x0C, 0x00000200);
+
+ pxa_ssp_write_reg(g_ssp, 0x18, 0x00010009);
+ pxa_ssp_write_reg(g_ssp, 0x1C, 0x00000000);
+
+ /* Enable the ssp */
+ UI_enable_ssp(g_ssp);
+ return;
+}
+
+/* Open SSP clock */
+static void UI_config_APBC_SSP1_CLK_RST(void)
+{
+ uint32_t reg_value = 0;
+#if 1
+ int i;
+
+ /* APBC_SSP1_CLK_RST */
+ reg_value = ioread32(APBC_SSP1_CLK_RST_reg);
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s/L%d, APBC_SSP1_CLK_RST: read before write 0x07: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+ /* Reset ssp1 clock */
+ reg_value = 0x7;
+ iowrite32(reg_value, APBC_SSP1_CLK_RST_reg);
+
+ reg_value = ioread32(APBC_SSP1_CLK_RST_reg);
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s/L%d, APBC_SSP1_CLK_RST: read after write 0x07: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+ for (i=0; i<5; i++)
+ {
+ }
+
+ /* Enable SSP1 clock */
+ reg_value = 0x3;
+ iowrite32(reg_value, APBC_SSP1_CLK_RST_reg);
+
+#ifdef DEBUG_SSP1
+ reg_value = ioread32(APBC_SSP1_CLK_RST_reg);
+ printk(KERN_INFO"%s/L%d, APBC_SSP1_CLK_RST: read after write 0x03: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+#endif
+
+}
+
+/* init the SSP clock */
+static void UI_init_SSP_clk(void)
+{
+ uint32_t reg_value = 0;
+#if 0
+ int i;
+
+ /* APBC_SSP1_CLK_RST */
+ reg_value = ioread32(APBC_SSP1_CLK_RST_reg);
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s/L%d, APBC_SSP1_CLK_RST: read before write 0x07: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+ /* Reset ssp1 clock */
+ reg_value = 0x7;
+ iowrite32(reg_value, APBC_SSP1_CLK_RST_reg);
+
+ reg_value = ioread32(APBC_SSP1_CLK_RST_reg);
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s/L%d, APBC_SSP1_CLK_RST: read after write 0x07: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+ for (i=0; i<5; i++)
+ {
+ }
+
+ /* Enable SSP1 clock */
+ reg_value = 0x3;
+ iowrite32(reg_value, APBC_SSP1_CLK_RST_reg);
+
+#ifdef DEBUG_SSP1
+ reg_value = ioread32(APBC_SSP1_CLK_RST_reg);
+ printk(KERN_INFO"%s/L%d, APBC_SSP1_CLK_RST: read after write 0x03: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+#endif
+
+ /* PMU_SQU_CLK_GATE_CTRL */
+ reg_value = ioread32(PMU_SQU_CLK_GATE_CTRL_reg);
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s/L%d, PMU_SQU_CLK_GATE_CTRL: read before write: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+ reg_value &= ~(0x1<<30);
+
+ iowrite32(reg_value, PMU_SQU_CLK_GATE_CTRL_reg);
+
+#ifdef DEBUG_SSP1
+ reg_value = ioread32(PMU_SQU_CLK_GATE_CTRL_reg);
+ printk(KERN_INFO"%s/L%d, PMU_SQU_CLK_GATE_CTRL: read after write: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+ /* SQU_CTRL_0 */
+ reg_value = ioread32(SQU_CTRL_0_reg);
+
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s/L%d, SQU_CTRL_0: read before write: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+ reg_value |= (0x7<<2);
+
+ iowrite32(reg_value, SQU_CTRL_0_reg);
+
+#ifdef DEBUG_SSP1
+ reg_value = ioread32(SQU_CTRL_0_reg);
+ printk(KERN_INFO"%s/L%d, SQU_CTRL_0 : read after write: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+ /* FCCR */
+ reg_value = ioread32(FCCR_reg);
+
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s/L%d, FCCR: read before write: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+ reg_value &= ~(0x1<<28);
+
+ iowrite32(reg_value, FCCR_reg);
+
+#ifdef DEBUG_SSP1
+ reg_value = ioread32(FCCR_reg);
+ printk(KERN_INFO"%s/L%d, FCCR : read after write: reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+ return;
+}
+
+/* test BitClk of ISCCR2 */
+
+static void UI_read_ISCCR2(void)
+{
+ uint32_t reg_value = 0;
+
+ reg_value = ioread32(ISCCR2_reg);
+ printk(KERN_INFO"%s/L%d, ISCCR2: read: reg_value=0x%x\n", __FUNCTION__, __LINE__, reg_value);
+
+ return;
+}
+#endif
+
+static void UI_write_ISCCR2(uint32_t write_reg_value)
+{
+#ifdef DEBUG_SSP1
+ uint32_t reg_value;
+
+ reg_value = ioread32(ISCCR2_reg);
+ printk(KERN_INFO"%s/L%d,ISCCR2: read before write: reg_value=0x%x\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+ iowrite32(write_reg_value, ISCCR2_reg);
+
+#ifdef DEBUG_SSP1
+ reg_value = ioread32(ISCCR2_reg);
+ printk(KERN_INFO"%s/L%d,ISCCR2: read after write: reg_value=0x%x\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+ return;
+}
+
+static void UI_read_SSP_regs(void)
+{
+ uint32_t reg_value = 0;
+ int i;
+
+ /* APBC_SSP1_CLK_RST */
+ reg_value = ioread32(APBC_SSP1_CLK_RST_reg);
+ printk(KERN_INFO"%s/L%d, APBC_SSP1_CLK_RST: read : reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+
+ /* PMU_SQU_CLK_GATE_CTRL */
+ reg_value = ioread32(PMU_SQU_CLK_GATE_CTRL_reg);
+ printk(KERN_INFO"%s/L%d, PMU_SQU_CLK_GATE_CTRL: read : reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+
+ /* SQU_CTRL_0 */
+ reg_value = ioread32(SQU_CTRL_0_reg);
+ printk(KERN_INFO"%s/L%d, SQU_CTRL_0: read : reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+
+ /* FCCR */
+ reg_value = ioread32(FCCR_reg);
+ printk(KERN_INFO"%s/L%d, FCCR: read : reg_value=0x%x.\n", __FUNCTION__, __LINE__, reg_value);
+
+ /* ISCCR2 */
+ reg_value = ioread32(ISCCR2_reg);
+ printk(KERN_INFO"%s/L%d, ISCCR2: read: reg_value=0x%x\n", __FUNCTION__, __LINE__, reg_value);
+
+ /* ICU_DMA_SEAGULL_INT_STATUS */
+ reg_value = ioread32(ICU_DMA_SEAGULL_INT_STATUS_reg);
+ printk(KERN_INFO"%s/L%d, DMA_DADRX[0xD4282120]: read: reg_value=0x%x\n", __FUNCTION__, __LINE__, reg_value);
+
+ for (i=0; i<=15; i++)
+ {
+ reg_value = ioread32(DMA_BASE_reg + 4*i);
+ printk(KERN_INFO"%s/L%d, DMA_BASE[0xD4000000 + 0x%x]: read: reg_value=0x%x\n", __FUNCTION__, __LINE__, (4*i), reg_value);
+ }
+
+ reg_value = ioread32(DMA_BASE_reg + 0xF0);
+ printk(KERN_INFO"%s/L%d, DMA_BASE[0xD4000000 + 0xF0]: read: reg_value=0x%x\n", __FUNCTION__, __LINE__, reg_value);
+
+ for (i=0; i<=64; i++)
+ {
+ reg_value = ioread32(DMA_BASE_reg + 0x200 + 4*i);
+ printk(KERN_INFO"%s/L%d, DMA_BASE[0xD4000000 + 0x%x]: read: reg_value=0x%x\n", __FUNCTION__, __LINE__, (0x200 + 4*i), reg_value);
+ }
+
+ return;
+}
+
+/* SSP1 Clock: APBC_SSP1_CLK_RST */
+#if 0
+static void UI_read_APBC_SSP1_CLK_RST(void)
+{
+ uint32_t reg_value = 0;
+
+ reg_value = ioread32(APBC_SSP1_CLK_RST_reg);
+ printk(KERN_INFO"%s/L%d, APBC_SSP1_CLK_RST: read: reg_value=0x%x\n", __FUNCTION__, __LINE__, reg_value);
+
+ return;
+}
+#endif
+
+static void UI_write_APBC_SSP1_CLK_RST(uint32_t write_reg_value)
+{
+#ifdef DEBUG_SSP1
+ uint32_t reg_value;
+
+ reg_value = ioread32(APBC_SSP1_CLK_RST_reg);
+ printk(KERN_INFO"%s/L%d, APBC_SSP1_CLK_RST: read before write: reg_value=0x%x\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+ iowrite32(write_reg_value, APBC_SSP1_CLK_RST_reg);
+
+#ifdef DEBUG_SSP1
+ reg_value = ioread32(APBC_SSP1_CLK_RST_reg);
+ printk(KERN_INFO"%s/L%d, APBC_SSP1_CLK_RST: read after write: reg_value=0x%x\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+ return;
+}
+
+
+/* config SSP bitclk */
+void UI_config_SSP_BCLK(SSP_BCLK_TYPE bclk_type, SAMPLE_FREQUENCY_ID sample_frequency_id)
+{
+ /****************************************************************
+ * Base Clock is 156MHz *
+ *****************************************************************
+ *F_bit-clock = (156*1024K)*(M/N)/(4,6,8Div)
+ *F_frame-clock = F_bit-clock/Frame-width
+ * = (156*1024K)*(M/N)/[(4,6,8Div)*Frame_width]
+ * = (156*1024K)*M/[(4,6,8Div)*Frame_width*N]
+ *
+ *Example:2channels, 44.1KHz, 16bits
+ *M=0x211=529; N=0x07242=29250; Frame_width = 32; (4,6,8Div)=2;
+ *F_frame-clock = (156*1024K)*M/[(4,6,8Div)*Frame_width*N]
+ * ~=44.1 KHz
+ *
+ ****************************************************************/
+ unsigned int numerator = 0x07242; /* 29250 */
+ unsigned int denominator = 0x060;
+ unsigned int isscr = 0;
+#ifdef DEBUG_SSP1
+ uint32_t reg_value = 0;
+#endif
+ //unsigned int sscr0_frdc = 0;
+
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s/L%d, bclk_type=%d, sample_frequency_id=%d.\n", __FUNCTION__, __LINE__, bclk_type, sample_frequency_id);
+#endif
+
+ if (sample_frequency_id > MAX_SAMPLE_FREQUENCY)
+ {
+ sample_frequency_id = SAMPLE_FREQUENCY_44100;
+ }
+
+ if (bclk_type > SSP_BCLK_MAX_FS)
+ {
+ printk(KERN_INFO"Error. bclk_type=%d.\n", bclk_type);
+ return;
+ }
+
+ /*
+ *bit14~0:NOM
+ */
+ isscr = numerator;
+
+ /*
+ *bit26~15:DENOM
+ */
+ if (sample_frequency_id == SAMPLE_FREQUENCY_8000)
+ {
+ /* 8000 Hz */
+#if 1
+ isscr |= (denominator << (15 + sample_frequency_id + bclk_type - 1));
+#else
+ if (SSP_BCLK_64_FS == bclk_type) {
+ isscr |= (denominator << (15 + sample_frequency_id + 1));
+ } else if (SSP_BCLK_32_FS == bclk_type) {
+ isscr |= (denominator << (15 + sample_frequency_id + 0));
+ } else {
+ /* SSP_BCLK_16_FS */
+ isscr |= (denominator << (15 + sample_frequency_id - 1));
+ }
+#endif
+ }
+ else if (sample_frequency_id == SAMPLE_FREQUENCY_16000)
+ {
+ /* 16000 Hz */
+#if 1
+ isscr |= (denominator << (15 + sample_frequency_id + bclk_type - 1));
+#else
+ if (SSP_BCLK_64_FS == bclk_type) {
+ isscr |= (denominator << (15 + sample_frequency_id + 1));
+ } else if (SSP_BCLK_32_FS == bclk_type) {
+ isscr |= (denominator << (15 + sample_frequency_id + 0));
+ } else {
+ /* SSP_BCLK_16_FS */
+ isscr |= (denominator << (15 + sample_frequency_id - 1));
+ }
+#endif
+ }
+ else if (sample_frequency_id == SAMPLE_FREQUENCY_44100)
+ {
+ /* 44100 Hz */
+ denominator = 0x211; /* 529 */
+#if 1
+ isscr |= (denominator << (15 + bclk_type - 1));
+#else
+ if (SSP_BCLK_64_FS == bclk_type) {
+ isscr |= (denominator << (15 + 1 ));
+ } else if (SSP_BCLK_32_FS == bclk_type) {
+ isscr |= (denominator << (15 + 0));
+ } else {
+ /* SSP_BCLK_16_FS */
+ isscr |= (denominator << (15 - 1));
+ }
+#endif
+ }
+
+ /*
+ *bit31:SYSCLK_EN
+ *bit30:SYSCLK_BASE <0>=26MHz,<1>=156MHz
+ *bit29:BITCLK_EN
+ *bit28~27:BITCLK_DIV_468
+ */
+#if 1
+ /* DIV_468 = 2 = reserved */
+ isscr |= 0xE0000000;
+#else
+ if (SSP_BCLK_64_FS == bclk_type) {
+ /* DIV_468 = 2 = reserved */
+ isscr |= 0xE0000000;
+ } else if (SSP_BCLK_32_FS == bclk_type) {
+ /* DIV_468 = 2 = reserved */
+ isscr |= 0xE0000000;
+ } else if (SSP_BCLK_16_FS == bclk_type) {
+ /* DIV_468 = 2 = reserved */
+ isscr |= 0xE0000000;
+ /* DIV_468 = 4 */
+ //isscr |= 0xE8000000;
+ }
+#endif
+
+#ifdef DEBUG_SSP1
+ reg_value = ioread32(ISCCR2_reg);
+ printk(KERN_INFO"%s/L%d,ISCCR2: read before write: reg_value=0x%x\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+ /* ISCCR2 */
+ iowrite32(isscr, ISCCR2_reg);
+
+#if 0
+
+ if (wb)
+ {
+ /* Sample frequece: 16K */
+ iowrite32(0xF840130B, ISCCR2_reg);
+ }
+ else
+ {
+ /* Sample frequece: 8K */
+ iowrite32(0xF820130B, ISCCR2_reg);
+ }
+
+#endif
+
+#ifdef DEBUG_SSP1
+ reg_value = ioread32(ISCCR2_reg);
+ printk(KERN_INFO"%s/L%d, ISCCR2: read after write: reg_value=0x%x\n", __FUNCTION__, __LINE__, reg_value);
+#endif
+
+#if 0
+ sscr0_frdc = (1 << bclk_type) - 1;
+ sspConfiguration->SSCR0_HIGH &= (~(0x07 << 8));
+ sspConfiguration->SSCR0_HIGH |= (sscr0_frdc << 8);
+
+ printk(KERN_INFO"GSSP_CLOCK_REG_ADDR:0x%lx, sscr0_frdc: 0x%lx, wb:%d", *(UINT32 *)GSSP_CLOCK_REG_ADDR, sscr0_frdc, wb);
+#endif
+
+ return;
+
+}
+
+#if 0
+static void test_ssp1(void)
+{
+ UI_config_APBC_SSP1_CLK_RST();
+ UI_init_SSP_clk();
+ //UI_config_SSP_BCLK(g_gssp_bclk_type, Audio_WB_AMR_Enable);
+ UI_config_SSP_BCLK(SSP_BCLK_32_FS, SAMPLE_FREQUENCY_44100);
+ UI_config_SSP_I2S_format();
+
+ return;
+}
+#endif
+
+/*
+ * SSP audio private data
+ */
+struct ssp_priv {
+ struct ssp_device *ssp;
+ unsigned int sysclk;
+ int dai_fmt;
+ unsigned int burst_size;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pin_ssp;
+ struct pinctrl_state *pin_gpio;
+ int mfp;
+ int usr_cnt;
+ bool mfp_init;
+#ifdef CONFIG_PM
+ uint32_t cr0;
+ uint32_t cr1;
+ uint32_t to;
+ uint32_t psp;
+ uint32_t top_ctrl;
+ uint32_t fifo_ctrl;
+ uint32_t int_en;
+ uint32_t network_ctrl;
+ uint32_t rwot_ctrl;
+#endif
+ /*
+ * FixMe: for port 5 (gssp), it is shared by ap
+ * and cp. When AP want to handle it, AP need to
+ * configure APB to connect gssp. Also reset gssp
+ * clk to clear the potential impact from cp
+ */
+ void __iomem *apbcp_base;
+};
+
+static ssize_t ssp_mfp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ssp_priv *priv = dev_get_drvdata(dev);
+ if (!priv)
+ return sprintf(buf, "%s\n", "get ssp-priv failed!!!\n");
+
+ if (priv->ssp->port_id == 2)
+ printk(KERN_INFO"i2s pin mfp setting:\n");
+ else if (priv->ssp->port_id == 5)
+ printk(KERN_INFO"gssp pin mfp setting:\n");
+
+ return sprintf(buf, "%s\n", (priv->mfp ? "ssp" : "gpio"));
+}
+
+static ssize_t ssp_mfp_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret, i;
+ u32 pins_ssp;
+ struct ssp_priv *priv = dev_get_drvdata(dev);
+ struct device_node *np = dev->of_node;
+
+ if (!priv)
+ return -EINVAL;
+
+ if (IS_ERR(priv->pin_ssp) || IS_ERR(priv->pin_gpio))
+ return -EINVAL;
+
+ ret = kstrtoint(buf, 10, &priv->mfp);
+ if (ret)
+ return ret;
+
+ /* mfp = 0, set to gpio; mfp = 1, set to ssp */
+ if (priv->mfp) {
+ ret = pinctrl_select_state(priv->pinctrl, priv->pin_ssp);
+ if (ret) {
+ dev_err(dev, "could not set ssp pins\n");
+ goto err_out;
+ }
+ } else {
+ ret = pinctrl_select_state(priv->pinctrl, priv->pin_gpio);
+ if (ret) {
+ dev_err(dev, "could not set default(gpio) pins\n");
+ goto err_out;
+ }
+
+ for (i = 0; i < 4; i++) {
+ pins_ssp = of_get_named_gpio(np, "ssp-gpio", i);
+ gpio_request(pins_ssp, NULL);
+ gpio_direction_input(pins_ssp);
+ gpio_free(pins_ssp);
+ }
+ }
+
+ if (priv->ssp->port_id == 2)
+ printk(KERN_INFO"i2s pin set to %s\n", (priv->mfp ? "ssp" : "gpio"));
+ else if (priv->ssp->port_id == 5)
+ printk(KERN_INFO"gssp pin set to %s\n", (priv->mfp ? "ssp" : "gpio"));
+
+err_out:
+ return count;
+}
+
+static DEVICE_ATTR(gssp_mfp, 0644, ssp_mfp_show, ssp_mfp_set);
+static DEVICE_ATTR(ssp_mfp, 0644, ssp_mfp_show, ssp_mfp_set);
+
+static void dump_registers(struct ssp_device *ssp)
+{
+ dev_dbg(&ssp->pdev->dev, "TOP_CTRL 0x%08x FIFO_CTRL 0x%08x INT_EN 0x%08x\n",
+ pxa_ssp_read_reg(ssp, TOP_CTRL), pxa_ssp_read_reg(ssp, FIFO_CTRL),
+ pxa_ssp_read_reg(ssp, INT_EN));
+
+ dev_dbg(&ssp->pdev->dev, "NET_WORK_CTRL 0x%08x RWOT_CTRL 0x%08x TO 0x%08x\n",
+ pxa_ssp_read_reg(ssp, NET_WORK_CTRL), pxa_ssp_read_reg(ssp, RWOT_CTRL),
+ pxa_ssp_read_reg(ssp, TO));
+
+ dev_dbg(&ssp->pdev->dev, "PSP_CTRL 0x%08x STATUS 0x%08x NET_WORK_STATUS 0x%08x\n",
+ pxa_ssp_read_reg(ssp, PSP_CTRL), pxa_ssp_read_reg(ssp, STATUS),
+ pxa_ssp_read_reg(ssp, NET_WORK_STATUS));
+}
+
+static void pxa_ssp_enable(struct ssp_device *ssp)
+{
+ uint32_t top_ctrl;
+
+ if ((has_feat_hifi_gssp_record())
+ && (2 == ssp->port_id)) {
+ top_ctrl_hifi |= TOP_SSE;
+ __raw_writel(top_ctrl_hifi, ssp->mmio_base + TOP_CTRL);
+ } else {
+ top_ctrl = __raw_readl(ssp->mmio_base + TOP_CTRL) | TOP_SSE;
+ __raw_writel(top_ctrl, ssp->mmio_base + TOP_CTRL);
+ }
+}
+
+static void pxa_ssp_disable(struct ssp_device *ssp)
+{
+ uint32_t top_ctrl;
+
+ if ((has_feat_hifi_gssp_record())
+ && (2 == ssp->port_id)) {
+ top_ctrl_hifi |= TOP_SSE;
+ __raw_writel(top_ctrl_hifi, ssp->mmio_base + TOP_CTRL);
+ } else {
+ top_ctrl = __raw_readl(ssp->mmio_base + TOP_CTRL) & ~TOP_SSE;
+ __raw_writel(top_ctrl, ssp->mmio_base + TOP_CTRL);
+ }
+
+}
+
+static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4,
+ int out, struct snd_dmaengine_dai_dma_data *dma)
+{
+ dma->addr_width = width4 ? DMA_SLAVE_BUSWIDTH_4_BYTES :
+ DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ if (dma->maxburst > PXA_SSP_FIFO_DEPTH)
+ dma->maxburst = PXA_SSP_FIFO_DEPTH;
+
+ dma->addr = ssp->phys_base + DATAR;
+}
+
+static int pxa_ssp_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ssp_device *ssp = priv->ssp;
+ struct snd_dmaengine_dai_dma_data *dma;
+ int ret = 0;
+
+ ret = pinctrl_select_state(priv->pinctrl, priv->pin_ssp);
+ if (ret) {
+ dev_err(cpu_dai->dev, "could not set ssp pins\n");
+ return ret;
+ }
+ priv->mfp_init = true;
+ priv->mfp = 1;
+
+ if (!cpu_dai->active)
+ {
+ clk_prepare_enable(ssp->clk);
+ priv->usr_cnt = 0;
+ }
+
+ dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
+#if 0
+ dma->filter_data = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ &ssp->drcmr_tx : &ssp->drcmr_rx;
+#endif
+ snd_soc_dai_set_dma_data(cpu_dai, substream, dma);
+
+#ifdef DEBUG_SSP1
+ printk(KERN_INFO"%s: drcmr_tx=%d, drcmr_rx=%d.\n", __FUNCTION__, ssp->drcmr_tx, ssp->drcmr_rx);
+#endif
+ return ret;
+}
+
+static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ssp_device *ssp = priv->ssp;
+ int ret = 0;
+
+ if (!cpu_dai->active) {
+ pxa_ssp_disable(ssp);
+ clk_disable_unprepare(ssp->clk);
+
+ ret = pinctrl_select_state(priv->pinctrl, priv->pin_gpio);
+ if (ret) {
+ dev_err(cpu_dai->dev, "could not set GPIO pins\n");
+ }
+ }
+
+ kfree(snd_soc_dai_get_dma_data(cpu_dai, substream));
+ snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
+}
+
+#ifdef CONFIG_PM
+
+static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+
+static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+
+#else
+#define pxa_ssp_suspend NULL
+#define pxa_ssp_resume NULL
+#endif
+
+
+/*
+ * Set the SSP ports SYSCLK.
+ */
+static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ return 0;
+}
+
+/*
+ * Set the SSP clock dividers.
+ */
+static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ return 0;
+}
+
+/*
+ * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
+ */
+static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ return 0;
+}
+
+/*
+ * Set the active slots in TDM/Network mode
+ */
+static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ssp_device *ssp = priv->ssp;
+
+ u32 network_ctrl, top_ctrl;
+
+ network_ctrl = pxa_ssp_read_reg(ssp, NET_WORK_CTRL);
+ top_ctrl = pxa_ssp_read_reg(ssp, TOP_CTRL);
+
+ network_ctrl &= ~(NET_FRDC(8) | NET_WORK_MODE | RTSA_MASK | TTSA_MASK);
+ top_ctrl &= ~TOP_DSS_MASK;
+
+ top_ctrl |= TOP_DSS(slot_width);
+
+ if (slots > 1) {
+ /* enable network mode */
+ network_ctrl |= NET_WORK_MODE;
+
+ /* set number of active slots */
+ network_ctrl |= NET_FRDC(slots);
+
+ /* set active slot mask */
+ network_ctrl |= TTSA(tx_mask);
+ network_ctrl |= RTSA(rx_mask);
+ }
+ pxa_ssp_write_reg(ssp, TOP_CTRL, top_ctrl);
+ pxa_ssp_write_reg(ssp, NET_WORK_CTRL, network_ctrl);
+
+ return 0;
+}
+
+/*
+ * Tristate the SSP DAI lines
+ */
+static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai,
+ int tristate)
+{
+ struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ssp_device *ssp = priv->ssp;
+
+ u32 top_ctrl;
+
+ top_ctrl = pxa_ssp_read_reg(ssp, TOP_CTRL);
+ if (tristate)
+ top_ctrl &= ~TOP_TTE;
+ else
+ top_ctrl |= TOP_TTE;
+ pxa_ssp_write_reg(ssp, TOP_CTRL, top_ctrl);
+
+ return 0;
+}
+
+/*
+ * Set up the SSP DAI format.
+ * The SSP Port must be inactive before calling this function as the
+ * physical interface format is changed.
+ */
+static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ssp_device *ssp = priv->ssp;
+ u32 network_ctrl, fifo_ctrl, top_ctrl, psp_ctrl, rwot_ctrl;
+
+ /* check if we need to change anything at all */
+ if (priv->dai_fmt == fmt)
+ return 0;
+
+ /* we can only change the settings if the port is not in use */
+ if (priv->usr_cnt) {
+ dev_err(&ssp->pdev->dev,
+ "can't change hardware dai format: stream is in use");
+ return -EINVAL;
+ }
+
+ /* reset port settings */
+ if ((has_feat_hifi_gssp_record())
+ && (2 == ssp->port_id))
+ network_ctrl = network_ctrl_hifi & ~NET_WORK_MODE;
+ else
+ network_ctrl = pxa_ssp_read_reg(ssp, NET_WORK_CTRL) &
+ ~NET_WORK_MODE;
+ fifo_ctrl = FIFO_RFT(7) | FIFO_TFT(7);
+ psp_ctrl = pxa_ssp_read_reg(ssp, PSP_CTRL);
+ top_ctrl = pxa_ssp_read_reg(ssp, TOP_CTRL);
+ rwot_ctrl =pxa_ssp_read_reg(ssp, RWOT_CTRL);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ top_ctrl |= TOP_SFRMDIR | TOP_SCLKDIR | TOP_SCFR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ top_ctrl |= TOP_SCLKDIR | TOP_SCFR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ //psp_ctrl |= PSP_SFRMP;
+ psp_ctrl |= 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ psp_ctrl |= PSP_SCMODE(2);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ //psp_ctrl |= PSP_SCMODE(2) | PSP_SFRMP;
+ psp_ctrl |= PSP_SCMODE(2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ top_ctrl |= TOP_FRF_PSP;
+ top_ctrl |= TOP_TRAIL;
+ rwot_ctrl = RWOT_RWOT;
+ /* See hw_params() */
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ psp_ctrl |= PSP_FSRT;
+ case SND_SOC_DAIFMT_DSP_B:
+ top_ctrl |= TOP_FRF_PSP;
+ top_ctrl |= TOP_TRAIL;
+ network_ctrl |= NET_WORK_MODE;
+ rwot_ctrl = RWOT_RWOT;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if ((has_feat_hifi_gssp_record())
+ && (2 == ssp->port_id)) {
+ top_ctrl_hifi = top_ctrl;
+ fifo_ctrl_hifi = fifo_ctrl;
+ rwot_ctrl_hifi = rwot_ctrl;
+ network_ctrl_hifi = network_ctrl;
+ }
+ pxa_ssp_write_reg(ssp, TOP_CTRL, top_ctrl);
+ pxa_ssp_write_reg(ssp, FIFO_CTRL, fifo_ctrl);
+ pxa_ssp_write_reg(ssp, NET_WORK_CTRL, network_ctrl);
+ pxa_ssp_write_reg(ssp, RWOT_CTRL, rwot_ctrl);
+ pxa_ssp_write_reg(ssp, PSP_CTRL, psp_ctrl);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ if ((has_feat_hifi_gssp_record())
+ && (2 == ssp->port_id))
+ top_ctrl = top_ctrl_hifi | TOP_SCFR;
+ else
+ top_ctrl = pxa_ssp_read_reg(ssp, TOP_CTRL) | TOP_SCFR;
+ pxa_ssp_write_reg(ssp, TOP_CTRL, top_ctrl);
+
+ while (pxa_ssp_read_reg(ssp, STATUS) & STATUS_BSY)
+ cpu_relax();
+ break;
+ }
+
+ dump_registers(ssp);
+
+#if 0
+ if (get_ssp_role())
+ {
+ /* parameters: 32FS, 44.1k */
+ UI_config_SSP_BCLK(0, 2);
+
+ /* parameters: 32FS, WB:16k */
+ //UI_config_SSP_BCLK(0, 1);
+
+ /* parameters: 32FS, NB:8k */
+ //UI_config_SSP_BCLK(0, 0);
+ }
+#endif
+
+ /* Since we are configuring the timings for the format by hand
+ * we have to defer some things until hw_params() where we
+ * know parameters like the sample size.
+ */
+ priv->dai_fmt = fmt;
+
+ return 0;
+}
+
+/*
+ * Set the SSP audio DMA parameters and sample size.
+ * Can be called multiple times by oss emulation.
+ */
+static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ssp_device *ssp = priv->ssp;
+ int chn = params_channels(params);
+ u32 top_ctrl;
+ u32 psp_ctrl;
+ u32 network_ctrl;
+ int ttsa;
+ int width = snd_pcm_format_physical_width(params_format(params));
+ struct snd_dmaengine_dai_dma_data *dma_data;
+
+ unsigned int rate = params_rate(params);
+#if 0
+ unsigned int sample_bits = snd_pcm_format_physical_width(params_format(params));
+ unsigned int frame_bits = sample_bits * channels;
+
+ if (get_ssp_role())
+ {
+ if (get_status_switch_frame_bits()){
+ /* for debug */
+ frame_bits = get_frame_bits();
+ } else {
+ /* get frame length from file format */
+ frame_bits = sample_bits * channels;
+ }
+
+ //clk_set_rate(ssp->clk, 2048000);//2.048MHz
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ clk_set_rate(ssp->clk, rate * 32);
+ } else {
+ //clk_set_rate(ssp->clk, 6500000);//Record ok
+ clk_set_rate(ssp->clk, rate * 32);
+ }
+
+ //UI_read_SSP_regs();
+
+ if ((8000 == rate) && (64 == frame_bits))
+ {
+ /* parameters: 64FS, NB:8k */
+ UI_config_SSP_BCLK(SSP_BCLK_64_FS, SAMPLE_FREQUENCY_8000);
+ }
+ else if ((16000 == rate) && (64 == frame_bits))
+ {
+ /* parameters: 64FS, WB:16k */
+ UI_config_SSP_BCLK(SSP_BCLK_64_FS, SAMPLE_FREQUENCY_16000);
+ }
+ else if ((44100 == rate) && (64 == frame_bits))
+ {
+ /* parameters: 64FS, 44.1k */
+ UI_config_SSP_BCLK(SSP_BCLK_64_FS, SAMPLE_FREQUENCY_44100);
+ }
+ else if ((8000 == rate) && (32 == frame_bits))
+ {
+ /* parameters: 32FS, NB:8k */
+ UI_config_SSP_BCLK(SSP_BCLK_32_FS, SAMPLE_FREQUENCY_8000);
+ }
+ else if ((16000 == rate) && (32 == frame_bits))
+ {
+ /* parameters: 32FS, WB:16k */
+ UI_config_SSP_BCLK(SSP_BCLK_32_FS, SAMPLE_FREQUENCY_16000);
+ }
+ else if ((44100 == rate) && (32 == frame_bits))
+ {
+ /* parameters: 32FS, 44.1k */
+ UI_config_SSP_BCLK(SSP_BCLK_32_FS, SAMPLE_FREQUENCY_44100);
+ }
+ else if ((8000 == rate) && (16 == frame_bits))
+ {
+ /* parameters: 16FS, NB:8k */
+ UI_config_SSP_BCLK(SSP_BCLK_16_FS, SAMPLE_FREQUENCY_8000);
+ }
+ else if ((16000 == rate) && (16 == frame_bits))
+ {
+ /* parameters: 16FS, WB:16k */
+ UI_config_SSP_BCLK(SSP_BCLK_16_FS, SAMPLE_FREQUENCY_16000);
+ }
+ else if ((44100 == rate) && (16 == frame_bits))
+ {
+ /* parameters: 16FS, 44.1k */
+ UI_config_SSP_BCLK(SSP_BCLK_16_FS, SAMPLE_FREQUENCY_44100);
+ }
+ }
+#endif
+ /* the bitclk rate depends on the sample rate, the sample size and chn*/
+ clk_set_rate(ssp->clk, rate * width * chn);
+ network_ctrl = pxa_ssp_read_reg(ssp, NET_WORK_CTRL);
+ ttsa = (network_ctrl & TTSA_MASK) >> TTSA_BASE;
+
+ dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+ dma_data->maxburst = priv->burst_size;
+
+ if ((has_feat_hifi_gssp_record())
+ && (2 == ssp->port_id))
+ ttsa = 0;
+ /* Network mode with one active slot (ttsa == 1) can be used
+ * to force 16-bit frame width on the wire (for S16_LE), even
+ * with two channels. Use 16-bit DMA transfers for this case.
+ */
+
+ pxa_ssp_set_dma_params(ssp,
+ ((chn == 2) && (ttsa != 1)) || (width == 32),
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK, dma_data);
+
+ /* we can only change the settings if the port is not in use */
+ if (priv->usr_cnt)
+ return 0;
+
+ /* clear selected SSP bits */
+ if ((has_feat_hifi_gssp_record())
+ && (2 == ssp->port_id))
+ top_ctrl = top_ctrl_hifi & ~(TOP_DSS_MASK);
+ else
+ top_ctrl = pxa_ssp_read_reg(ssp, TOP_CTRL) &
+ ~(TOP_DSS_MASK);
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ top_ctrl |= TOP_DSS(16);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ top_ctrl |= TOP_DSS(24);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ top_ctrl |= TOP_DSS(32);
+ break;
+ }
+ if ((has_feat_hifi_gssp_record())
+ && (2 == ssp->port_id))
+ top_ctrl_hifi = top_ctrl;
+ pxa_ssp_write_reg(ssp, TOP_CTRL, top_ctrl);
+
+ switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ psp_ctrl = pxa_ssp_read_reg(ssp, PSP_CTRL);
+
+ /* The frame width is the width the LRCLK is
+ * asserted for; the delay is expressed in
+ * half cycle units. We need the extra cycle
+ * because the data starts clocking out one BCLK
+ * after LRCLK changes polarity.
+ */
+ psp_ctrl |= PSP_SFRMWDTH(width + 1);
+ psp_ctrl |= PSP_FSRT;
+
+ pxa_ssp_write_reg(ssp, PSP_CTRL, psp_ctrl);
+ break;
+ default:
+ break;
+ }
+
+ /* When we use a network mode, we always require TDM slots
+ * - complain loudly and fail if they've not been set up yet.
+ */
+ if ((network_ctrl & NET_WORK_MODE) && !ttsa) {
+ dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n");
+ return -EINVAL;
+ }
+
+ dump_registers(ssp);
+ return 0;
+}
+
+static void pxa_ssp_set_running_bit(struct snd_pcm_substream *substream,
+ struct ssp_device *ssp, int value)
+{
+ uint32_t fifo_ctrl = pxa_ssp_read_reg(ssp, FIFO_CTRL);
+
+ /*
+ * here we only enable/disable SSP TX/RX DMA request. ssp enable/
+ * disable is handled in startup/shutdown to avoid noise.
+ */
+ if ((has_feat_hifi_gssp_record())
+ && (ssp->port_id == 2))
+ fifo_ctrl = fifo_ctrl_hifi;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (value)
+ fifo_ctrl |= FIFO_TSRE;
+ else
+ fifo_ctrl &= ~FIFO_TSRE;
+ } else {
+ if (value)
+ fifo_ctrl |= FIFO_RSRE;
+ else
+ fifo_ctrl &= ~FIFO_RSRE;
+ }
+
+ if ((has_feat_hifi_gssp_record())
+ && (ssp->port_id == 2))
+ fifo_ctrl_hifi = fifo_ctrl;
+ pxa_ssp_write_reg(ssp, FIFO_CTRL, fifo_ctrl);
+}
+
+static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
+{
+ int ret = 0;
+ struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ssp_device *ssp = priv->ssp;
+ int val;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ pxa_ssp_enable(ssp);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pxa_ssp_set_running_bit(substream, ssp, 1);
+
+ val = pxa_ssp_read_reg(ssp, STATUS);
+ pxa_ssp_write_reg(ssp, STATUS, val);
+
+ break;
+ case SNDRV_PCM_TRIGGER_START:
+ priv->usr_cnt++;
+ pxa_ssp_set_running_bit(substream, ssp, 1);
+ if(1 == priv->usr_cnt){
+ pxa_ssp_enable(ssp);
+ }
+
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ priv->usr_cnt--;
+ pxa_ssp_set_running_bit(substream, ssp, 0);
+ if(0 == priv->usr_cnt){
+ pxa_ssp_disable(ssp);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ pxa_ssp_disable(ssp);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pxa_ssp_set_running_bit(substream, ssp, 0);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ dump_registers(ssp);
+
+ return ret;
+}
+
+/* ssp_dump_reg */
+/* debug fs for ssp register interface of read and write. */
+void ssp_dump_registers(struct ssp_device *ssp)
+{
+ printk(KERN_INFO"[SSP_TOP_CTRL:0xD401B800]=0x%08x.\n", pxa_ssp_read_reg(ssp, TOP_CTRL));/* 0x00 */
+ printk(KERN_INFO"[SSP_FIFO_CTRL:0xD401B804]=0x%08x.\n", pxa_ssp_read_reg(ssp, FIFO_CTRL));/* 0x04 */
+ printk(KERN_INFO"[SSP_INT_EN:0xD401B808]=0x%08x.\n", pxa_ssp_read_reg(ssp, INT_EN));/* 0x08 */
+ printk(KERN_INFO"[SSP_TO:0xD401B80C]=0x%08x\n", pxa_ssp_read_reg(ssp, TO));/* 0x0C */
+
+ printk(KERN_INFO"[SSP_DATAR:0xD401B810]=0x%08x.\n", pxa_ssp_read_reg(ssp, DATAR));/* 0x10 */
+ printk(KERN_INFO"[SSP_STATUS:0xD401B814]=0x%08x.\n", pxa_ssp_read_reg(ssp, STATUS));/* 0x14 */
+ printk(KERN_INFO"[SSP_PSP_CTRL:0xD401B818]=0x%08x.\n", pxa_ssp_read_reg(ssp, PSP_CTRL));/* 0x18 */
+ printk(KERN_INFO"[SSP_NET_WORK_CTRL:0xD401B81C]=0x%08x.\n", pxa_ssp_read_reg(ssp, NET_WORK_CTRL));/* 0x1C */
+
+ printk(KERN_INFO"[SSP_NET_WORK_STATUS:0xD401B820]=0x%08x.\n", pxa_ssp_read_reg(ssp, NET_WORK_STATUS));/* 0x20 */
+ printk(KERN_INFO"[SSP_RWOT_CTRL:0xD401B824]=0x%08x\n", pxa_ssp_read_reg(ssp, RWOT_CTRL));/* 0x24 */
+ printk(KERN_INFO"[SSP_RWOT_CCM:0xD401B828]=0x%08x.\n", pxa_ssp_read_reg(ssp, RWOT_CCM));/* 0x28 */
+ printk(KERN_INFO"[SSP_RWOT_CVWRn:0xD401B82C]=0x%08x.\n", pxa_ssp_read_reg(ssp, RWOT_CVWRn));/* 0x2C */
+
+ UI_read_SSP_regs();
+
+ printk(KERN_INFO"[top_ctrl_UI=0x%08x. suggested value Slave=0x0002a3fe, Master=0x0002a3e6 for L/R 16bits.\n", top_ctrl_UI);/* 0x00 */
+ printk(KERN_INFO"[fifo_ctrl_UI=0x%08x.suggested value=0x00000c63 for L/R 16bits.\n", fifo_ctrl_UI);/* 0x04 */
+ printk(KERN_INFO"[int_en_UI=0x%08x.suggested value=0x0000000f for L/R 16bits.\n", int_en_UI);/* 0x08 */
+ printk(KERN_INFO"[psp_ctrl_UI=0x%08x.suggested value=0x00010009 for L/R 16bits.\n", psp_ctrl_UI);/* 0x18 */
+ printk(KERN_INFO"[network_ctrl_UI=0x%08x.suggested value=0x00000000 for L/R 16bits.\n", network_ctrl_UI);/* 0x1C */
+}
+
+static ssize_t ssp_dump_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ uint32_t reg_val = 0;
+
+ if (reg_ssp == 0xffff) {
+ /* dump all registers */
+ printk(KERN_INFO"%s/L%d:dump all registers.\n", __FUNCTION__, __LINE__);
+ ssp_dump_registers(g_ssp);
+ } else {
+ /* read the register for reg_ssp */
+ reg_val = pxa_ssp_read_reg(g_ssp, reg_ssp);
+ printk(KERN_INFO"%s/L%d:[0x%02x]=0x%08x.\n", __FUNCTION__, __LINE__, reg_ssp, reg_val);
+ }
+
+ return 0;
+}
+
+/*
+ *read all registers:
+ *echo + > /sys/kernel/debug/ssp_reg
+ *
+ *read offset of register:
+ *echo 0x00 > /sys/kernel/debug/ssp_reg
+ *
+ *write offset of register:
+ *echo 0x00 0x0000a3ff > /sys/kernel/debug/ssp_reg
+ *
+ */
+static ssize_t ssp_dump_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ uint32_t reg_val;
+ //uint32_t reg_val_SSP_TOP_CTRL;
+ int i = 0;
+
+ char messages[20];
+ memset(messages, '\0', 20);
+
+ if (copy_from_user(messages, user_buf, count))
+ return -EFAULT;
+
+ if ('+' == messages[0]) {
+ /* enable to get all the reg value */
+ reg_ssp = 0xffff;
+ /* dump all registers */
+ printk(KERN_INFO"%s/L%d:dump all registers.\n", __FUNCTION__, __LINE__);
+ ssp_dump_registers(g_ssp);
+
+ } else {
+ if (messages[1] != 'x') {
+ pr_err("Right format: 0x[addr]\n");
+ return -EINVAL;
+ }
+
+ if (strlen(messages) > 5) {
+ while (messages[i] != ' ')
+ i++;
+ messages[i] = '\0';
+ if (kstrtouint(messages, 16, ®_ssp) < 0)
+ return -EINVAL;
+ i++;
+ if (kstrtouint(messages + i, 16, ®_val) < 0)
+ return -EINVAL;
+
+ printk(KERN_INFO"%s/L%d: [0x%02x]=0x%08x.\n", __FUNCTION__, __LINE__, reg_ssp, reg_val);
+
+ if (reg_ssp == 0xFF)
+ {
+ /* write the ISCCR2 */
+ UI_write_ISCCR2(reg_val);
+ }
+ else if (reg_ssp == 0xEE)
+ {
+ /* write the APBC_SSP1_CLK_RST */
+ UI_write_APBC_SSP1_CLK_RST(reg_val);
+ }
+ else
+ {
+ if (TOP_CTRL == reg_ssp) {
+ /* SSP_TOP_CTRL */
+ top_ctrl_UI = reg_val;
+ printk(KERN_INFO"suggest top_ctrl_UI with Slave=0x0002a3fe, Master=0x0002a3e6 for L/R 16bits.\n");/* 0x00 */
+ } else if (FIFO_CTRL == reg_ssp) {
+ /* SSP_FIFO_CTRL */
+ fifo_ctrl_UI = reg_val;
+ printk(KERN_INFO"suggest fifo_ctrl_UI with 0x00000c63 for L/R 16bits.\n");
+ } else if (INT_EN == reg_ssp) {
+ /* SSP_INT_CTRL */
+ int_en_UI = reg_val;
+ printk(KERN_INFO"suggest int_en_UI with 0x0000000f for L/R 16bits.\n");
+ } else if (PSP_CTRL == reg_ssp) {
+ /* SSP_PSP_CTRL */
+ psp_ctrl_UI = reg_val;
+ printk(KERN_INFO"suggest psp_ctrl_UI with 0x00010009 for L/R 16bits.\n");
+ } else if (NET_WORK_CTRL == reg_ssp) {
+ /* SSP_NET_WORK_CTRL */
+ network_ctrl_UI = reg_val;
+ printk(KERN_INFO"suggest psp_ctrl_UI with 0x00000000 for L/R 16bits.\n");
+ }
+ }
+
+ } else {
+ /* point out the register address for read. */
+ if (kstrtouint(messages, 16, ®_ssp) < 0)
+ return -EINVAL;
+
+ /* read the register for reg_ssp */
+ reg_val = pxa_ssp_read_reg(g_ssp, reg_ssp);
+ //reg_val = ioread32(ssp1_reg_addr);
+ printk(KERN_INFO"%s/L%d:[0x%02x]=0x%08x.\n", __FUNCTION__, __LINE__, reg_ssp, reg_val);
+ }
+ }
+
+ return count;
+}
+
+static const struct file_operations ssp_dump_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = ssp_dump_read,
+ .write = ssp_dump_write,
+};
+
+static inline int ssp_dump_debugfs_init(void)
+{
+
+ ssp_dump_reg = debugfs_create_file("ssp_reg", S_IRUGO | S_IFREG,
+ NULL, NULL, &ssp_dump_ops);
+
+ if (ssp_dump_reg == NULL) {
+ pr_err("create ssp debugfs error!\n");
+ return -ENOENT;
+ } else if (ssp_dump_reg == ERR_PTR(-ENODEV)) {
+ pr_err("CONFIG_DEBUG_FS is not enabled!\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static void ssp_dump_debugfs_remove(void)
+{
+ if (NULL != ssp_dump_reg){
+ debugfs_remove_recursive(ssp_dump_reg);
+ }
+
+ return;
+}
+
+/* ssp_ctrl */
+struct dentry *ssp_ctrl = NULL;
+
+static ssize_t ssp_ctrl_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+
+ printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
+
+ return 0;
+}
+
+/*
+ *control command:
+ *: echo 1 > /sys/kernel/debug/ssp_ctrl
+ *: echo 2 > /sys/kernel/debug/ssp_ctrl
+ *
+ *: echo 3 > /sys/kernel/debug/ssp_ctrl
+ *: echo 4 > /sys/kernel/debug/ssp_ctrl
+ *
+ *: echo 5 > /sys/kernel/debug/ssp_ctrl
+ *: echo 6 > /sys/kernel/debug/ssp_ctrl
+ */
+static char msg[10];
+void init_ssp_i2s_addr(void);
+
+static ssize_t ssp_ctrl_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ size_t tmp_count = 0;
+ int rate;
+
+ printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
+
+ memset(msg, 0x00, sizeof(msg));
+ tmp_count = count;
+
+ if (tmp_count >= sizeof(msg)){
+ tmp_count = sizeof(msg) - 1;
+ }
+
+ /* copy the content from user space to kernel space */
+ ret = copy_from_user(msg, user_buf, tmp_count);
+ if (ret){
+ printk(KERN_ALERT"copy from user fail \n");
+ return -EFAULT;
+ }
+
+ switch (msg[0]){
+ case '0':/* input command# echo 0 > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+
+ rate = clk_get_rate(g_ssp->clk);
+
+ clk_set_rate(g_ssp->clk, 6500000);
+
+ rate = clk_get_rate(g_ssp->clk);
+#if 0
+ UI_read_ISCCR2();
+ UI_read_APBC_SSP1_CLK_RST();
+#endif
+ break;
+
+ case '1':/* input command# echo 1 > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+
+ rate = clk_get_rate(g_ssp->clk);
+
+ UI_read_SSP_regs();
+
+ //test_ssp1();
+#if 0
+ UI_config_APBC_SSP1_CLK_RST();
+ UI_init_SSP_clk();
+ UI_config_SSP_BCLK(g_gssp_bclk_type, Audio_WB_AMR_Enable);
+ UI_config_SSP_I2S_format();
+#endif
+ break;
+
+ case '2':/* input command# echo 2 > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+
+ UI_switch_SSP_role();
+ break;
+
+ case '3':/* input command# echo 3 > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+
+ break;
+
+ case '4':/* input command# echo 4 > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+
+ UI_Reverse_WBNB();
+ break;
+
+ case '5':/* input command# echo 5 > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+
+ UI_switch_bitclk_FS();
+ break;
+
+ case '6':/* input command# echo 6 > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+ UI_enable_clk(g_ssp);
+ break;
+
+ case '7':/* input command# echo 7 > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+ UI_disable_clk(g_ssp);
+ break;
+
+ case '8':/* input command# echo 8 > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+ UI_enable_ssp(g_ssp);
+ break;
+
+ case '9':/* input command# echo 9 > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+ UI_disable_ssp(g_ssp);
+ break;
+
+ case 'a':/* input command# echo a > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+ UI_switch_frame_bits();
+ break;
+
+ case 'b':/* input command# echo b > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+ UI_enable_switch_frame_bits();
+ break;
+
+ case 'c':/* input command# echo c > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+ UI_switch_IFS();
+ break;
+
+ case 'd':/* input command# echo d > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+ UI_switch_SFRMP();
+ break;
+
+ case 'e':/* input command# echo e > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+ UI_switch_SCMODE();
+ break;
+
+ case 'f':/* input command# echo f > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+ UI_switch_debug_tools();
+ break;
+
+ case 'g':/* input command# echo g > /sys/kernel/debug/ssp_ctrl */
+ printk(KERN_INFO "input %c. \n", msg[0]);
+ UI_display_global_ctrl_var();
+ break;
+
+ default:/* input command# */
+ printk(KERN_INFO "input invalid. \n");
+ break;
+ }
+
+ return tmp_count;
+}
+
+static const struct file_operations ssp_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = ssp_ctrl_read,
+ .write = ssp_ctrl_write,
+};
+
+static inline int ssp_ctrl_debugfs_init(void)
+{
+
+ ssp_ctrl = debugfs_create_file("ssp_ctrl", S_IRUGO | S_IFREG,
+ NULL, NULL, &ssp_ctrl_ops);
+
+ if (ssp_ctrl == NULL) {
+ pr_err("create ssp_ctrl debugfs error!\n");
+ return -ENOENT;
+ } else if (ssp_ctrl == ERR_PTR(-ENODEV)) {
+ pr_err("CONFIG_DEBUG_FS is not enabled!\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static void ssp_ctrl_debugfs_remove(void)
+{
+ if (NULL != ssp_ctrl){
+ debugfs_remove_recursive(ssp_ctrl);
+ }
+
+ return;
+}
+
+void init_ssp_i2s_addr(void)
+{
+ /* APBC_SSP1_CLK_RST */
+#if 0
+ if (!request_mem_region(APBC_SSP1_CLK_RST_ADDR, 4, "APBC_SSP1_CLK_RST_REG")) {
+ printk(KERN_INFO"%s:L%d, error request_mem_region\n", __FUNCTION__, __LINE__);
+ return;
+ }
+#endif
+
+ APBC_SSP1_CLK_RST_reg = ioremap_nocache(APBC_SSP1_CLK_RST_ADDR, 4);
+ if (APBC_SSP1_CLK_RST_reg == NULL) {
+ printk(KERN_INFO"%s:L%d, error ioremap\n", __FUNCTION__, __LINE__);
+ return;
+ }
+
+ /* PMU_SQU_CLK_GATE_CTRL */
+#if 0
+ if (!request_mem_region(PMU_SQU_CLK_GATE_CTRL_ADDR, 4, "PMU_SQU_CLK_GATE_CTRL_REG")) {
+ printk(KERN_INFO"%s:L%d, error request_mem_region\n", __FUNCTION__, __LINE__);
+ return;
+ }
+#endif
+
+ PMU_SQU_CLK_GATE_CTRL_reg = ioremap_nocache(PMU_SQU_CLK_GATE_CTRL_ADDR, 4);
+ if (PMU_SQU_CLK_GATE_CTRL_reg == NULL) {
+ printk(KERN_INFO"%s:L%d, error ioremap\n", __FUNCTION__, __LINE__);
+ return;
+ }
+
+ /* SQU_CTRL_0 */
+#if 0
+ if (!request_mem_region(SQU_CTRL_0_ADDR, 4, "SQU_CTRL_0_REG")) {
+ printk(KERN_INFO"%s:L%d, error request_mem_region\n", __FUNCTION__, __LINE__);
+ return;
+ }
+#endif
+
+ SQU_CTRL_0_reg = ioremap_nocache(SQU_CTRL_0_ADDR, 4);
+ if (SQU_CTRL_0_reg == NULL) {
+ printk(KERN_INFO"%s:L%d, error ioremap\n", __FUNCTION__, __LINE__);
+ return;
+ }
+
+ /* FCCR */
+#if 0
+ if (!request_mem_region(FCCR_ADDR, 4, "FCCR_REG")) {
+ printk(KERN_INFO"%s:L%d, error request_mem_region\n", __FUNCTION__, __LINE__);
+ return;
+ }
+#endif
+
+ FCCR_reg = ioremap_nocache(FCCR_ADDR, 4);
+ if ( FCCR_reg == NULL) {
+ printk(KERN_INFO"%s:L%d, error ioremap\n", __FUNCTION__, __LINE__);
+ return;
+ }
+
+ /* ISCCR2 */
+#if 0
+ if (!request_mem_region(ISCCR2_ADDR, 4, "ISCCR2_REG")) {
+ printk(KERN_INFO"%s:L%d, error request_mem_region\n", __FUNCTION__, __LINE__);
+ return;
+ }
+#endif
+
+ ISCCR2_reg = ioremap_nocache(ISCCR2_ADDR, 4);
+ if ( ISCCR2_reg == NULL) {
+ printk(KERN_INFO"%s:L%d, error ioremap\n", __FUNCTION__, __LINE__);
+ return;
+ }
+
+ /* ICU_DMA_SEAGULL_INT_STATUS */
+ ICU_DMA_SEAGULL_INT_STATUS_reg = ioremap_nocache(ICU_DMA_SEAGULL_INT_STATUS_ADDR, 4);
+ if ( ICU_DMA_SEAGULL_INT_STATUS_reg == NULL) {
+ printk(KERN_INFO"%s:L%d, error ioremap\n", __FUNCTION__, __LINE__);
+ return;
+ }
+
+ /* DMA_BASE */
+ DMA_BASE_reg = ioremap_nocache(DMA_BASE_ADDR, 0x304);
+ if ( DMA_BASE_reg == NULL) {
+ printk(KERN_INFO"%s:L%d, error ioremap\n", __FUNCTION__, __LINE__);
+ return;
+ }
+
+ return;
+}
+
+static void free_ssp_i2s_clk(void)
+{
+ /* APBC_SSP1_CLK_RST */
+ iounmap(APBC_SSP1_CLK_RST_reg);
+ //release_mem_region(APBC_SSP1_CLK_RST_ADDR, 4);
+
+ /* PMU_SQU_CLK_GATE_CTRL */
+ iounmap(PMU_SQU_CLK_GATE_CTRL_reg);
+ //release_mem_region(PMU_SQU_CLK_GATE_CTRL_ADDR, 4);
+
+ /* SQU_CTRL_0 */
+ iounmap(SQU_CTRL_0_reg);
+ //release_mem_region(SQU_CTRL_0_ADDR, 4);
+
+ /* FCCR */
+ iounmap(FCCR_reg);
+ //release_mem_region(FCCR_ADDR, 4);
+
+ /* ISCCR2 */
+ iounmap(ISCCR2_reg);
+ //release_mem_region(ISCCR2_ADDR, 4);
+
+ /* ICU_DMA_SEAGULL_INT_STATUS */
+ iounmap(ICU_DMA_SEAGULL_INT_STATUS_reg);
+
+ /* DMA_BASE */
+ iounmap(DMA_BASE_reg);
+
+ return;
+}
+
+static int pxa_ssp_probe(struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->dev;
+ struct ssp_priv *priv;
+ struct mmp_audio_sspdata *pdata;
+ int ret;
+
+ priv = kzalloc(sizeof(struct ssp_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (dev->of_node) {
+ struct device_node *ssp_handle;
+
+ ssp_handle = of_parse_phandle(dev->of_node, "port", 0);
+ if (!ssp_handle) {
+ dev_err(dev, "unable to get 'port' phandle\n");
+ ret = -ENODEV;
+ goto err_priv;
+ }
+
+ priv->ssp = pxa_ssp_request_of(ssp_handle, "SoC audio");
+ if (priv->ssp == NULL) {
+ ret = -ENODEV;
+ goto err_priv;
+ }
+
+ of_property_read_u32(dev->of_node, "burst_size",
+ &priv->burst_size);
+ } else {
+
+ priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio");
+ if (priv->ssp == NULL) {
+ ret = -ENODEV;
+ goto err_priv;
+ }
+
+ pdata = dev_get_platdata(dai->dev);
+ priv->burst_size = 4;
+ }
+ priv->burst_size = 4;
+
+ /*
+ * FixMe: for port 5 (gssp), it is shared by ap
+ * and cp. When AP want to handle it, AP need to
+ * configure APB to connect gssp. Also reset gssp
+ * clk to clear the potential impact from cp
+ */
+ if (priv->ssp->port_id == 5) {
+ priv->apbcp_base = devm_ioremap(dev, APBCONTROL_BASE,
+ APBCONTROL_SIZE);
+ if (priv->apbcp_base == NULL) {
+ dev_err(dev, "failed to ioremap() registers\n");
+ ret = -ENODEV;
+ goto err_priv;
+ }
+ }
+
+ priv->pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(priv->pinctrl)) {
+ ret = PTR_ERR(priv->pinctrl);
+ goto err_priv;
+ }
+ priv->pin_ssp = pinctrl_lookup_state(priv->pinctrl, "ssp");
+ if (IS_ERR(priv->pin_ssp)) {
+ dev_err(dev, "could not get ssp pinstate\n");
+ ret = IS_ERR(priv->pin_ssp);
+ goto err_priv;
+ }
+
+ priv->pin_gpio = pinctrl_lookup_state(priv->pinctrl, "default");
+ if (IS_ERR(priv->pin_gpio)) {
+ dev_err(dev, "could not get default(gpio) pinstate\n");
+ ret = IS_ERR(priv->pin_gpio);
+ goto err_priv;
+ }
+
+ ret = pinctrl_select_state(priv->pinctrl, priv->pin_ssp);
+ if (ret) {
+ printk(KERN_INFO"could not set ssp pins\n");
+ goto err_priv;
+ }
+
+ priv->dai_fmt = (unsigned int) -1;
+ snd_soc_dai_set_drvdata(dai, priv);
+ priv->usr_cnt = 0;
+ priv->mfp_init = false;
+
+ /* clear gssp init clock status */
+ clk_prepare_enable(priv->ssp->clk);
+ clk_disable_unprepare(priv->ssp->clk);
+
+ /* init the global variable */
+ g_ssp = priv->ssp;
+
+ /* init debug tool */
+ ssp_dump_debugfs_init();
+ ssp_ctrl_debugfs_init();
+
+ /* init register address */
+ init_ssp_i2s_addr();
+
+ return 0;
+
+err_priv:
+ kfree(priv);
+ return ret;
+}
+
+static int pxa_ssp_remove(struct snd_soc_dai *dai)
+{
+ struct ssp_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ pxa_ssp_free(priv->ssp);
+ kfree(priv);
+
+ //free debug tool
+ ssp_dump_debugfs_remove();
+ ssp_ctrl_debugfs_remove();
+ free_ssp_i2s_clk();
+
+ return 0;
+}
+
+#define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops pxa_ssp_dai_ops = {
+ .startup = pxa_ssp_startup,
+ .shutdown = pxa_ssp_shutdown,
+ .trigger = pxa_ssp_trigger,
+ .hw_params = pxa_ssp_hw_params,
+ .set_sysclk = pxa_ssp_set_dai_sysclk,
+ .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+ .set_pll = pxa_ssp_set_dai_pll,
+ .set_fmt = pxa_ssp_set_dai_fmt,
+ .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+ .set_tristate = pxa_ssp_set_dai_tristate,
+};
+
+static struct snd_soc_dai_driver pxa_ssp_dai = {
+ .probe = pxa_ssp_probe,
+ .remove = pxa_ssp_remove,
+ .suspend = pxa_ssp_suspend,
+ .resume = pxa_ssp_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .ops = &pxa_ssp_dai_ops,
+};
+
+static const struct snd_soc_component_driver pxa_ssp_component = {
+ .name = "pxa-ssp",
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pxa_ssp_of_ids[] = {
+ { .compatible = "asr,pxa-ssp-dai" },
+};
+#endif
+
+static int asoc_ssp_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ char const *platform_driver_name;
+ int ret;
+
+ if (of_property_read_string(np,
+ "platform_driver_name",
+ &platform_driver_name)) {
+ dev_err(&pdev->dev,
+ "Missing platform_driver_name property in the DT\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_register_component(&pdev->dev, &pxa_ssp_component,
+ &pxa_ssp_dai, 1);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register DAI\n");
+ return ret;
+ }
+
+ if (strcmp(platform_driver_name, "tdma_platform") == 0) {
+ //ret = mmp_pcm_platform_register(&pdev->dev);
+ /* add ssp_mfp sysfs entries */
+ ret = device_create_file(&pdev->dev, &dev_attr_ssp_mfp);
+ if (ret < 0)
+ dev_err(&pdev->dev,
+ "%s: failed to add ssp_mfp sysfs files: %d\n",
+ __func__, ret);
+ } else if (strcmp(platform_driver_name, "pdma_platform") == 0) {
+ ret = pxa_pcm_platform_register(&pdev->dev);
+ /* add gssp_mfp sysfs entries */
+ ret = device_create_file(&pdev->dev, &dev_attr_gssp_mfp);
+ if (ret < 0)
+ dev_err(&pdev->dev,
+ "%s: failed to add gssp_mfp sysfs files: %d\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int asoc_ssp_remove(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ char const *platform_driver_name;
+
+ if (of_property_read_string(np,
+ "platform_driver_name",
+ &platform_driver_name)) {
+ dev_err(&pdev->dev,
+ "Missing platform_driver_name property in the DT\n");
+ return -EINVAL;
+ }
+
+ if (strcmp(platform_driver_name, "tdma_platform") == 0) {
+ device_remove_file(&pdev->dev, &dev_attr_ssp_mfp);
+ //mmp_pcm_platform_unregister(&pdev->dev);
+ } else if (strcmp(platform_driver_name, "pdma_platform") == 0) {
+ device_remove_file(&pdev->dev, &dev_attr_gssp_mfp);
+ snd_soc_unregister_component(&pdev->dev);
+ }
+
+ return 0;
+}
+
+static struct platform_driver asoc_ssp_driver = {
+ .driver = {
+ .name = "pxa-ssp-dai",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pxa_ssp_of_ids),
+ },
+
+ .probe = asoc_ssp_probe,
+ .remove = asoc_ssp_remove,
+};
+
+module_platform_driver(asoc_ssp_driver);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("PXA SSP/PCM SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa-ssp-dai");
diff --git a/marvell/linux/sound/soc/pxa/pxa-ssp.h b/marvell/linux/sound/soc/pxa/pxa-ssp.h
new file mode 100644
index 0000000..fc6b147
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/pxa-ssp.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ASoC PXA SSP port support
+ */
+
+#ifndef _PXA_SSP_H
+#define _PXA_SSP_H
+
+/* pxa DAI SSP IDs */
+#define PXA_DAI_SSP1 0
+#define PXA_DAI_SSP2 1
+#define PXA_DAI_SSP3 2
+#define PXA_DAI_SSP4 3
+
+/* SSP clock sources */
+#define PXA_SSP_CLK_PLL 0
+#define PXA_SSP_CLK_EXT 1
+#define PXA_SSP_CLK_NET 2
+#define PXA_SSP_CLK_AUDIO 3
+#define PXA_SSP_CLK_NET_PLL 4
+
+/* SSP audio dividers */
+#define PXA_SSP_AUDIO_DIV_ACDS 0
+#define PXA_SSP_AUDIO_DIV_SCDB 1
+#define PXA_SSP_DIV_SCR 2
+#define PXA_SSP_AUDIO_DIV_ACPS 3
+
+/* SSP ACDS audio dividers values */
+#define PXA_SSP_CLK_AUDIO_DIV_1 0
+#define PXA_SSP_CLK_AUDIO_DIV_2 1
+#define PXA_SSP_CLK_AUDIO_DIV_4 2
+#define PXA_SSP_CLK_AUDIO_DIV_8 3
+#define PXA_SSP_CLK_AUDIO_DIV_16 4
+#define PXA_SSP_CLK_AUDIO_DIV_32 5
+
+/* SSP divider bypass */
+#define PXA_SSP_CLK_SCDB_4 0
+#define PXA_SSP_CLK_SCDB_1 1
+#define PXA_SSP_CLK_SCDB_8 2
+
+#define PXA_SSP_PLL_OUT 0
+
+#define PXA_SSP_FIFO_DEPTH 16
+
+/*
+ * FixMe: for port 5 (gssp), it is shared by ap
+ * and cp. When AP want to handle it, AP need to
+ * configure APB to connect gssp. Also reset gspp
+ * clk to clear the potential impact from cp
+ */
+#define APBCONTROL_BASE 0xD403B000
+#define APBCONTROL_SIZE 0x3C
+#define APBC_GBS 0xC
+#define APBC_GCER 0x34
+
+#define GSSP_BUS_APB_SEL 0x1
+#define GSSP_CLK_SEL_MASK 0x3
+#define GSSP_CLK_SEL_OFF 0x8
+#define GSSP_CLK_EN (1 << 0) /* APB Bus Clock Enable */
+#define GSSP_FNCLK_EN (1 << 1) /* Functional Clock Enable */
+#define GSSP_RST (1 << 2) /* Reset Generation */
+
+//#define DEBUG_SSP1
+
+int mmp_pcm_platform_register(struct device *dev);
+void mmp_pcm_platform_unregister(struct device *dev);
+int pxa_pcm_platform_register(struct device *dev);
+void pxa_pcm_platform_unregister(struct device *dev);
+
+#endif
diff --git a/marvell/linux/sound/soc/pxa/pxa2xx-ac97.c b/marvell/linux/sound/soc/pxa/pxa2xx-ac97.c
new file mode 100644
index 0000000..bf28187
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/pxa2xx-ac97.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
+ *
+ * Author: Nicolas Pitre
+ * Created: Dec 02, 2004
+ * Copyright: MontaVista Software Inc.
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
+
+#include <sound/ac97/controller.h>
+#include <sound/core.h>
+#include <sound/ac97_codec.h>
+#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-ac97.h>
+#include <mach/audio.h>
+
+static void pxa2xx_ac97_warm_reset(struct ac97_controller *adrv)
+{
+ pxa2xx_ac97_try_warm_reset();
+
+ pxa2xx_ac97_finish_reset();
+}
+
+static void pxa2xx_ac97_cold_reset(struct ac97_controller *adrv)
+{
+ pxa2xx_ac97_try_cold_reset();
+
+ pxa2xx_ac97_finish_reset();
+}
+
+static int pxa2xx_ac97_read_actrl(struct ac97_controller *adrv, int slot,
+ unsigned short reg)
+{
+ return pxa2xx_ac97_read(slot, reg);
+}
+
+static int pxa2xx_ac97_write_actrl(struct ac97_controller *adrv, int slot,
+ unsigned short reg, unsigned short val)
+{
+ return pxa2xx_ac97_write(slot, reg, val);
+}
+
+static struct ac97_controller_ops pxa2xx_ac97_ops = {
+ .read = pxa2xx_ac97_read_actrl,
+ .write = pxa2xx_ac97_write_actrl,
+ .warm_reset = pxa2xx_ac97_warm_reset,
+ .reset = pxa2xx_ac97_cold_reset,
+};
+
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
+ .addr = __PREG(PCDR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .chan_name = "pcm_pcm_stereo_in",
+ .maxburst = 32,
+};
+
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
+ .addr = __PREG(PCDR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .chan_name = "pcm_pcm_stereo_out",
+ .maxburst = 32,
+};
+
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
+ .addr = __PREG(MODR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
+ .chan_name = "pcm_aux_mono_out",
+ .maxburst = 16,
+};
+
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
+ .addr = __PREG(MODR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
+ .chan_name = "pcm_aux_mono_in",
+ .maxburst = 16,
+};
+
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
+ .addr = __PREG(MCDR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES,
+ .chan_name = "pcm_aux_mic_mono",
+ .maxburst = 16,
+};
+
+static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_dmaengine_dai_dma_data *dma_data;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_data = &pxa2xx_ac97_pcm_stereo_out;
+ else
+ dma_data = &pxa2xx_ac97_pcm_stereo_in;
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
+
+ return 0;
+}
+
+static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_dmaengine_dai_dma_data *dma_data;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_data = &pxa2xx_ac97_pcm_aux_mono_out;
+ else
+ dma_data = &pxa2xx_ac97_pcm_aux_mono_in;
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
+
+ return 0;
+}
+
+static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return -ENODEV;
+ snd_soc_dai_set_dma_data(cpu_dai, substream,
+ &pxa2xx_ac97_pcm_mic_mono_in);
+
+ return 0;
+}
+
+#define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = {
+ .startup = pxa2xx_ac97_hifi_startup,
+};
+
+static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = {
+ .startup = pxa2xx_ac97_aux_startup,
+};
+
+static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
+ .startup = pxa2xx_ac97_mic_startup,
+};
+
+/*
+ * There is only 1 physical AC97 interface for pxa2xx, but it
+ * has extra fifo's that can be used for aux DACs and ADCs.
+ */
+static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
+{
+ .name = "pxa2xx-ac97",
+ .bus_control = true,
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PXA2XX_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PXA2XX_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &pxa_ac97_hifi_dai_ops,
+},
+{
+ .name = "pxa2xx-ac97-aux",
+ .bus_control = true,
+ .playback = {
+ .stream_name = "AC97 Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = PXA2XX_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "AC97 Aux Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = PXA2XX_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &pxa_ac97_aux_dai_ops,
+},
+{
+ .name = "pxa2xx-ac97-mic",
+ .bus_control = true,
+ .capture = {
+ .stream_name = "AC97 Mic Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = PXA2XX_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &pxa_ac97_mic_dai_ops,
+},
+};
+
+static const struct snd_soc_component_driver pxa_ac97_component = {
+ .name = "pxa-ac97",
+ .ops = &pxa2xx_pcm_ops,
+ .pcm_new = pxa2xx_soc_pcm_new,
+ .pcm_free = pxa2xx_pcm_free_dma_buffers,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pxa2xx_ac97_dt_ids[] = {
+ { .compatible = "marvell,pxa250-ac97", },
+ { .compatible = "marvell,pxa270-ac97", },
+ { .compatible = "marvell,pxa300-ac97", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pxa2xx_ac97_dt_ids);
+
+#endif
+
+static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct ac97_controller *ctrl;
+ pxa2xx_audio_ops_t *pdata = pdev->dev.platform_data;
+ void **codecs_pdata;
+
+ if (pdev->id != -1) {
+ dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n");
+ return -ENXIO;
+ }
+
+ ret = pxa2xx_ac97_hw_probe(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "PXA2xx AC97 hw probe error (%d)\n", ret);
+ return ret;
+ }
+
+ codecs_pdata = pdata ? pdata->codec_pdata : NULL;
+ ctrl = snd_ac97_controller_register(&pxa2xx_ac97_ops, &pdev->dev,
+ AC97_SLOTS_AVAILABLE_ALL,
+ codecs_pdata);
+ if (IS_ERR(ctrl))
+ return PTR_ERR(ctrl);
+
+ platform_set_drvdata(pdev, ctrl);
+ /* Punt most of the init to the SoC probe; we may need the machine
+ * driver to do interesting things with the clocking to get us up
+ * and running.
+ */
+ return devm_snd_soc_register_component(&pdev->dev, &pxa_ac97_component,
+ pxa_ac97_dai_driver, ARRAY_SIZE(pxa_ac97_dai_driver));
+}
+
+static int pxa2xx_ac97_dev_remove(struct platform_device *pdev)
+{
+ struct ac97_controller *ctrl = platform_get_drvdata(pdev);
+
+ snd_ac97_controller_unregister(ctrl);
+ pxa2xx_ac97_hw_remove(pdev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pxa2xx_ac97_dev_suspend(struct device *dev)
+{
+ return pxa2xx_ac97_hw_suspend();
+}
+
+static int pxa2xx_ac97_dev_resume(struct device *dev)
+{
+ return pxa2xx_ac97_hw_resume();
+}
+
+static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops,
+ pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume);
+#endif
+
+static struct platform_driver pxa2xx_ac97_driver = {
+ .probe = pxa2xx_ac97_dev_probe,
+ .remove = pxa2xx_ac97_dev_remove,
+ .driver = {
+ .name = "pxa2xx-ac97",
+#ifdef CONFIG_PM_SLEEP
+ .pm = &pxa2xx_ac97_pm_ops,
+#endif
+ .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids),
+ },
+};
+
+module_platform_driver(pxa2xx_ac97_driver);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-ac97");
diff --git a/marvell/linux/sound/soc/pxa/pxa2xx-i2s.c b/marvell/linux/sound/soc/pxa/pxa2xx-i2s.c
new file mode 100644
index 0000000..9f7fb73
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/pxa2xx-i2s.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pxa2xx-i2s.c -- ALSA Soc Audio Layer
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * lrg@slimlogic.co.uk
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
+
+#include <mach/hardware.h>
+#include <mach/audio.h>
+
+#include "pxa2xx-i2s.h"
+
+/*
+ * I2S Controller Register and Bit Definitions
+ */
+#define SACR0 __REG(0x40400000) /* Global Control Register */
+#define SACR1 __REG(0x40400004) /* Serial Audio I 2 S/MSB-Justified Control Register */
+#define SASR0 __REG(0x4040000C) /* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */
+#define SAIMR __REG(0x40400014) /* Serial Audio Interrupt Mask Register */
+#define SAICR __REG(0x40400018) /* Serial Audio Interrupt Clear Register */
+#define SADIV __REG(0x40400060) /* Audio Clock Divider Register. */
+#define SADR __REG(0x40400080) /* Serial Audio Data Register (TX and RX FIFO access Register). */
+
+#define SACR0_RFTH(x) ((x) << 12) /* Rx FIFO Interrupt or DMA Trigger Threshold */
+#define SACR0_TFTH(x) ((x) << 8) /* Tx FIFO Interrupt or DMA Trigger Threshold */
+#define SACR0_STRF (1 << 5) /* FIFO Select for EFWR Special Function */
+#define SACR0_EFWR (1 << 4) /* Enable EFWR Function */
+#define SACR0_RST (1 << 3) /* FIFO, i2s Register Reset */
+#define SACR0_BCKD (1 << 2) /* Bit Clock Direction */
+#define SACR0_ENB (1 << 0) /* Enable I2S Link */
+#define SACR1_ENLBF (1 << 5) /* Enable Loopback */
+#define SACR1_DRPL (1 << 4) /* Disable Replaying Function */
+#define SACR1_DREC (1 << 3) /* Disable Recording Function */
+#define SACR1_AMSL (1 << 0) /* Specify Alternate Mode */
+
+#define SASR0_I2SOFF (1 << 7) /* Controller Status */
+#define SASR0_ROR (1 << 6) /* Rx FIFO Overrun */
+#define SASR0_TUR (1 << 5) /* Tx FIFO Underrun */
+#define SASR0_RFS (1 << 4) /* Rx FIFO Service Request */
+#define SASR0_TFS (1 << 3) /* Tx FIFO Service Request */
+#define SASR0_BSY (1 << 2) /* I2S Busy */
+#define SASR0_RNE (1 << 1) /* Rx FIFO Not Empty */
+#define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */
+
+#define SAICR_ROR (1 << 6) /* Clear Rx FIFO Overrun Interrupt */
+#define SAICR_TUR (1 << 5) /* Clear Tx FIFO Underrun Interrupt */
+
+#define SAIMR_ROR (1 << 6) /* Enable Rx FIFO Overrun Condition Interrupt */
+#define SAIMR_TUR (1 << 5) /* Enable Tx FIFO Underrun Condition Interrupt */
+#define SAIMR_RFS (1 << 4) /* Enable Rx FIFO Service Interrupt */
+#define SAIMR_TFS (1 << 3) /* Enable Tx FIFO Service Interrupt */
+
+struct pxa_i2s_port {
+ u32 sadiv;
+ u32 sacr0;
+ u32 sacr1;
+ u32 saimr;
+ int master;
+ u32 fmt;
+};
+static struct pxa_i2s_port pxa_i2s;
+static struct clk *clk_i2s;
+static int clk_ena = 0;
+
+static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = {
+ .addr = __PREG(SADR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .chan_name = "tx",
+ .maxburst = 32,
+};
+
+static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = {
+ .addr = __PREG(SADR),
+ .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .chan_name = "rx",
+ .maxburst = 32,
+};
+
+static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ if (IS_ERR(clk_i2s))
+ return PTR_ERR(clk_i2s);
+
+ if (!cpu_dai->active)
+ SACR0 = 0;
+
+ return 0;
+}
+
+/* wait for I2S controller to be ready */
+static int pxa_i2s_wait(void)
+{
+ int i;
+
+ /* flush the Rx FIFO */
+ for (i = 0; i < 16; i++)
+ SADR;
+ return 0;
+}
+
+static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ pxa_i2s.fmt = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ pxa_i2s.fmt = SACR1_AMSL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ pxa_i2s.master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ pxa_i2s.master = 0;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ if (clk_id != PXA2XX_I2S_SYSCLK)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_dmaengine_dai_dma_data *dma_data;
+
+ if (WARN_ON(IS_ERR(clk_i2s)))
+ return -EINVAL;
+ clk_prepare_enable(clk_i2s);
+ clk_ena = 1;
+ pxa_i2s_wait();
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_data = &pxa2xx_i2s_pcm_stereo_out;
+ else
+ dma_data = &pxa2xx_i2s_pcm_stereo_in;
+
+ snd_soc_dai_set_dma_data(dai, substream, dma_data);
+
+ /* is port used by another stream */
+ if (!(SACR0 & SACR0_ENB)) {
+ SACR0 = 0;
+ if (pxa_i2s.master)
+ SACR0 |= SACR0_BCKD;
+
+ SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
+ SACR1 |= pxa_i2s.fmt;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ SAIMR |= SAIMR_TFS;
+ else
+ SAIMR |= SAIMR_RFS;
+
+ switch (params_rate(params)) {
+ case 8000:
+ SADIV = 0x48;
+ break;
+ case 11025:
+ SADIV = 0x34;
+ break;
+ case 16000:
+ SADIV = 0x24;
+ break;
+ case 22050:
+ SADIV = 0x1a;
+ break;
+ case 44100:
+ SADIV = 0xd;
+ break;
+ case 48000:
+ SADIV = 0xc;
+ break;
+ case 96000: /* not in manual and possibly slightly inaccurate */
+ SADIV = 0x6;
+ break;
+ }
+
+ return 0;
+}
+
+static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ SACR1 &= ~SACR1_DRPL;
+ else
+ SACR1 &= ~SACR1_DREC;
+ SACR0 |= SACR0_ENB;
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ SACR1 |= SACR1_DRPL;
+ SAIMR &= ~SAIMR_TFS;
+ } else {
+ SACR1 |= SACR1_DREC;
+ SAIMR &= ~SAIMR_RFS;
+ }
+
+ if ((SACR1 & (SACR1_DREC | SACR1_DRPL)) == (SACR1_DREC | SACR1_DRPL)) {
+ SACR0 &= ~SACR0_ENB;
+ pxa_i2s_wait();
+ if (clk_ena) {
+ clk_disable_unprepare(clk_i2s);
+ clk_ena = 0;
+ }
+ }
+}
+
+#ifdef CONFIG_PM
+static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai)
+{
+ /* store registers */
+ pxa_i2s.sacr0 = SACR0;
+ pxa_i2s.sacr1 = SACR1;
+ pxa_i2s.saimr = SAIMR;
+ pxa_i2s.sadiv = SADIV;
+
+ /* deactivate link */
+ SACR0 &= ~SACR0_ENB;
+ pxa_i2s_wait();
+ return 0;
+}
+
+static int pxa2xx_i2s_resume(struct snd_soc_dai *dai)
+{
+ pxa_i2s_wait();
+
+ SACR0 = pxa_i2s.sacr0 & ~SACR0_ENB;
+ SACR1 = pxa_i2s.sacr1;
+ SAIMR = pxa_i2s.saimr;
+ SADIV = pxa_i2s.sadiv;
+
+ SACR0 = pxa_i2s.sacr0;
+
+ return 0;
+}
+
+#else
+#define pxa2xx_i2s_suspend NULL
+#define pxa2xx_i2s_resume NULL
+#endif
+
+static int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
+{
+ clk_i2s = clk_get(dai->dev, "I2SCLK");
+ if (IS_ERR(clk_i2s))
+ return PTR_ERR(clk_i2s);
+
+ /*
+ * PXA Developer's Manual:
+ * If SACR0[ENB] is toggled in the middle of a normal operation,
+ * the SACR0[RST] bit must also be set and cleared to reset all
+ * I2S controller registers.
+ */
+ SACR0 = SACR0_RST;
+ SACR0 = 0;
+ /* Make sure RPL and REC are disabled */
+ SACR1 = SACR1_DRPL | SACR1_DREC;
+ /* Along with FIFO servicing */
+ SAIMR &= ~(SAIMR_RFS | SAIMR_TFS);
+
+ snd_soc_dai_init_dma_data(dai, &pxa2xx_i2s_pcm_stereo_out,
+ &pxa2xx_i2s_pcm_stereo_in);
+
+ return 0;
+}
+
+static int pxa2xx_i2s_remove(struct snd_soc_dai *dai)
+{
+ clk_put(clk_i2s);
+ clk_i2s = ERR_PTR(-ENOENT);
+ return 0;
+}
+
+#define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+
+static const struct snd_soc_dai_ops pxa_i2s_dai_ops = {
+ .startup = pxa2xx_i2s_startup,
+ .shutdown = pxa2xx_i2s_shutdown,
+ .trigger = pxa2xx_i2s_trigger,
+ .hw_params = pxa2xx_i2s_hw_params,
+ .set_fmt = pxa2xx_i2s_set_dai_fmt,
+ .set_sysclk = pxa2xx_i2s_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver pxa_i2s_dai = {
+ .probe = pxa2xx_i2s_probe,
+ .remove = pxa2xx_i2s_remove,
+ .suspend = pxa2xx_i2s_suspend,
+ .resume = pxa2xx_i2s_resume,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PXA2XX_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PXA2XX_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &pxa_i2s_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static const struct snd_soc_component_driver pxa_i2s_component = {
+ .name = "pxa-i2s",
+ .ops = &pxa2xx_pcm_ops,
+ .pcm_new = pxa2xx_soc_pcm_new,
+ .pcm_free = pxa2xx_pcm_free_dma_buffers,
+};
+
+static int pxa2xx_i2s_drv_probe(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev, &pxa_i2s_component,
+ &pxa_i2s_dai, 1);
+}
+
+static struct platform_driver pxa2xx_i2s_driver = {
+ .probe = pxa2xx_i2s_drv_probe,
+
+ .driver = {
+ .name = "pxa2xx-i2s",
+ },
+};
+
+static int __init pxa2xx_i2s_init(void)
+{
+ clk_i2s = ERR_PTR(-ENOENT);
+ return platform_driver_register(&pxa2xx_i2s_driver);
+}
+
+static void __exit pxa2xx_i2s_exit(void)
+{
+ platform_driver_unregister(&pxa2xx_i2s_driver);
+}
+
+module_init(pxa2xx_i2s_init);
+module_exit(pxa2xx_i2s_exit);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
+MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-i2s");
diff --git a/marvell/linux/sound/soc/pxa/pxa2xx-i2s.h b/marvell/linux/sound/soc/pxa/pxa2xx-i2s.h
new file mode 100644
index 0000000..263568d
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/pxa2xx-i2s.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * linux/sound/soc/pxa/pxa2xx-i2s.h
+ */
+
+#ifndef _PXA2XX_I2S_H
+#define _PXA2XX_I2S_H
+
+/* I2S clock */
+#define PXA2XX_I2S_SYSCLK 0
+
+#endif
diff --git a/marvell/linux/sound/soc/pxa/pxa2xx-pcm.c b/marvell/linux/sound/soc/pxa/pxa2xx-pcm.c
new file mode 100644
index 0000000..74b56fa
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/pxa2xx-pcm.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip
+ *
+ * Author: Nicolas Pitre
+ * Created: Nov 30, 2004
+ * Copyright: (C) 2004 MontaVista Software, Inc.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/dmaengine.h>
+#include <linux/of.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
+
+static const struct snd_soc_component_driver pxa2xx_soc_platform = {
+ .ops = &pxa2xx_pcm_ops,
+ .pcm_new = pxa2xx_soc_pcm_new,
+ .pcm_free = pxa2xx_pcm_free_dma_buffers,
+};
+
+static int pxa2xx_soc_platform_probe(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_component(&pdev->dev, &pxa2xx_soc_platform,
+ NULL, 0);
+}
+
+static struct platform_driver pxa_pcm_driver = {
+ .driver = {
+ .name = "pxa-pcm-audio",
+ },
+
+ .probe = pxa2xx_soc_platform_probe,
+};
+
+module_platform_driver(pxa_pcm_driver);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa-pcm-audio");
diff --git a/marvell/linux/sound/soc/pxa/spitz.c b/marvell/linux/sound/soc/pxa/spitz.c
new file mode 100644
index 0000000..f7babff
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/spitz.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * spitz.c -- SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Richard Purdie <richard@openedhand.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <mach/spitz.h>
+#include "../codecs/wm8750.h"
+#include "pxa2xx-i2s.h"
+
+#define SPITZ_HP 0
+#define SPITZ_MIC 1
+#define SPITZ_LINE 2
+#define SPITZ_HEADSET 3
+#define SPITZ_HP_OFF 4
+#define SPITZ_SPK_ON 0
+#define SPITZ_SPK_OFF 1
+
+ /* audio clock in Hz - rounded from 12.235MHz */
+#define SPITZ_AUDIO_CLOCK 12288000
+
+static int spitz_jack_func;
+static int spitz_spk_func;
+static int spitz_mic_gpio;
+
+static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
+{
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (spitz_spk_func == SPITZ_SPK_ON)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
+
+ /* set up jack connection */
+ switch (spitz_jack_func) {
+ case SPITZ_HP:
+ /* enable and unmute hp jack, disable mic bias */
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
+ gpio_set_value(SPITZ_GPIO_MUTE_L, 1);
+ gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
+ break;
+ case SPITZ_MIC:
+ /* enable mic jack and bias, mute hp */
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
+ gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
+ gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
+ break;
+ case SPITZ_LINE:
+ /* enable line jack, disable mic bias and mute hp */
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
+ gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
+ gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
+ break;
+ case SPITZ_HEADSET:
+ /* enable and unmute headset jack enable mic bias, mute L hp */
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
+ gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
+ gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
+ break;
+ case SPITZ_HP_OFF:
+
+ /* jack removed, everything off */
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+ gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
+ gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
+ break;
+ }
+
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static int spitz_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ /* check the jack status at stream startup */
+ spitz_ext_control(&rtd->card->dapm);
+
+ return 0;
+}
+
+static int spitz_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ unsigned int clk = 0;
+ int ret = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ clk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk = 11289600;
+ break;
+ }
+
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as input (unused) */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_ops spitz_ops = {
+ .startup = spitz_startup,
+ .hw_params = spitz_hw_params,
+};
+
+static int spitz_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = spitz_jack_func;
+ return 0;
+}
+
+static int spitz_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (spitz_jack_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ spitz_jack_func = ucontrol->value.enumerated.item[0];
+ spitz_ext_control(&card->dapm);
+ return 1;
+}
+
+static int spitz_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = spitz_spk_func;
+ return 0;
+}
+
+static int spitz_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (spitz_spk_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ spitz_spk_func = ucontrol->value.enumerated.item[0];
+ spitz_ext_control(&card->dapm);
+ return 1;
+}
+
+static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+/* spitz machine dapm widgets */
+static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_LINE("Line Jack", NULL),
+
+ /* headset is a mic and mono headphone */
+ SND_SOC_DAPM_HP("Headset Jack", NULL),
+};
+
+/* Spitz machine audio_map */
+static const struct snd_soc_dapm_route spitz_audio_map[] = {
+
+ /* headphone connected to LOUT1, ROUT1 */
+ {"Headphone Jack", NULL, "LOUT1"},
+ {"Headphone Jack", NULL, "ROUT1"},
+
+ /* headset connected to ROUT1 and LINPUT1 with bias (def below) */
+ {"Headset Jack", NULL, "ROUT1"},
+
+ /* ext speaker connected to LOUT2, ROUT2 */
+ {"Ext Spk", NULL, "ROUT2"},
+ {"Ext Spk", NULL, "LOUT2"},
+
+ /* mic is connected to input 1 - with bias */
+ {"LINPUT1", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Mic Jack"},
+
+ /* line is connected to input 1 - no bias */
+ {"LINPUT1", NULL, "Line Jack"},
+};
+
+static const char * const jack_function[] = {"Headphone", "Mic", "Line",
+ "Headset", "Off"};
+static const char * const spk_function[] = {"On", "Off"};
+static const struct soc_enum spitz_enum[] = {
+ SOC_ENUM_SINGLE_EXT(5, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new wm8750_spitz_controls[] = {
+ SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack,
+ spitz_set_jack),
+ SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk,
+ spitz_set_spk),
+};
+
+/* spitz digital audio interface glue - connects codec <--> CPU */
+SND_SOC_DAILINK_DEFS(wm8750,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-001b", "wm8750-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link spitz_dai = {
+ .name = "wm8750",
+ .stream_name = "WM8750",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &spitz_ops,
+ SND_SOC_DAILINK_REG(wm8750),
+};
+
+/* spitz audio machine driver */
+static struct snd_soc_card snd_soc_spitz = {
+ .name = "Spitz",
+ .owner = THIS_MODULE,
+ .dai_link = &spitz_dai,
+ .num_links = 1,
+
+ .controls = wm8750_spitz_controls,
+ .num_controls = ARRAY_SIZE(wm8750_spitz_controls),
+ .dapm_widgets = wm8750_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
+ .dapm_routes = spitz_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(spitz_audio_map),
+ .fully_routed = true,
+};
+
+static int spitz_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_spitz;
+ int ret;
+
+ if (machine_is_akita())
+ spitz_mic_gpio = AKITA_GPIO_MIC_BIAS;
+ else
+ spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
+
+ ret = gpio_request(spitz_mic_gpio, "MIC GPIO");
+ if (ret)
+ goto err1;
+
+ ret = gpio_direction_output(spitz_mic_gpio, 0);
+ if (ret)
+ goto err2;
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ goto err2;
+ }
+
+ return 0;
+
+err2:
+ gpio_free(spitz_mic_gpio);
+err1:
+ return ret;
+}
+
+static int spitz_remove(struct platform_device *pdev)
+{
+ gpio_free(spitz_mic_gpio);
+ return 0;
+}
+
+static struct platform_driver spitz_driver = {
+ .driver = {
+ .name = "spitz-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = spitz_probe,
+ .remove = spitz_remove,
+};
+
+module_platform_driver(spitz_driver);
+
+MODULE_AUTHOR("Richard Purdie");
+MODULE_DESCRIPTION("ALSA SoC Spitz");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:spitz-audio");
diff --git a/marvell/linux/sound/soc/pxa/ssp-dummy.c b/marvell/linux/sound/soc/pxa/ssp-dummy.c
new file mode 100644
index 0000000..b957901
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/ssp-dummy.c
@@ -0,0 +1,280 @@
+/*
+ * pxa-ssp.c -- ALSA Soc Audio Layer
+ *
+ * Copyright 2005,2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * TODO:
+ * o Test network mode for > 16bit sample size
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pxa2xx_ssp.h>
+#include <linux/of.h>
+#include <linux/dmaengine.h>
+#include <linux/delay.h>
+#include <linux/platform_data/mmp_audio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/features.h>
+
+#include <asm/irq.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
+
+//#include <mach/hardware.h>
+
+//#include "../../arm/pxa2xx-pcm.h"
+#include "pxa-ssp.h"
+#ifndef CONFIG_CPU_ASR18XX
+extern u32 sscr0_hifi;
+extern u32 sscr1_hifi;
+#else
+extern u32 top_ctrl_hifi;
+extern u32 fifo_ctrl_hifi;
+extern u32 rwot_ctrl_hifi;
+extern u32 network_ctrl_hifi;
+extern u32 int_en_hifi;
+#endif
+
+//extern int mmp_pcm_probe(struct platform_device *pdev);
+//extern int pxa_pcm_platform_register(struct device *dev);
+
+static int pxa_ssp_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+
+static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ return;
+}
+
+#ifdef CONFIG_PM
+
+static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+
+static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+
+#else
+#define pxa_ssp_suspend NULL
+#define pxa_ssp_resume NULL
+#endif
+
+
+/*
+ * Set the SSP ports SYSCLK.
+ */
+static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ return 0;
+}
+
+/*
+ * Set the SSP clock dividers.
+ */
+static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ return 0;
+}
+
+/*
+ * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
+ */
+static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ return 0;
+}
+
+/*
+ * Set the active slots in TDM/Network mode
+ */
+static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ return 0;
+}
+
+/*
+ * Tristate the SSP DAI lines
+ */
+static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai,
+ int tristate)
+{
+ return 0;
+}
+
+/*
+ * Set up the SSP DAI format.
+ * The SSP Port must be inactive before calling this function as the
+ * physical interface format is changed.
+ */
+static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ return 0;
+}
+
+/*
+ * Set the SSP audio DMA parameters and sample size.
+ * Can be called multiple times by oss emulation.
+ */
+
+static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+
+static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+
+static int pxa_ssp_probe(struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static int pxa_ssp_remove(struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+#define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops pxa_ssp_dai_ops = {
+ .startup = pxa_ssp_startup,
+ .shutdown = pxa_ssp_shutdown,
+ .trigger = pxa_ssp_trigger,
+ .hw_params = pxa_ssp_hw_params,
+ .set_sysclk = pxa_ssp_set_dai_sysclk,
+ .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+ .set_pll = pxa_ssp_set_dai_pll,
+ .set_fmt = pxa_ssp_set_dai_fmt,
+ .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+ .set_tristate = pxa_ssp_set_dai_tristate,
+};
+
+static struct snd_soc_dai_driver pxa_ssp_dai = {
+ .probe = pxa_ssp_probe,
+ .remove = pxa_ssp_remove,
+ .suspend = pxa_ssp_suspend,
+ .resume = pxa_ssp_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .ops = &pxa_ssp_dai_ops,
+};
+
+static const struct snd_soc_component_driver pxa_ssp_component = {
+ .name = "pxa-ssp",
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pxa_ssp_of_ids[] = {
+ { .compatible = "asr,pxa-ssp-dai" },
+};
+#endif
+
+static int asoc_ssp_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ char const *platform_driver_name;
+ int ret;
+
+ printk(KERN_INFO"[5616 platform probe: begin] %s/L%d.\n", __FUNCTION__, __LINE__);
+
+ if (of_property_read_string(np,
+ "platform_driver_name",
+ &platform_driver_name)) {
+ dev_err(&pdev->dev,
+ "Missing platform_driver_name property in the DT\n");
+ return -EINVAL;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &pxa_ssp_component,
+ &pxa_ssp_dai, 1);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register DAI\n");
+ return ret;
+ }
+
+#if 0 //yjg
+ if (strcmp(platform_driver_name, "tdma_platform") == 0) {
+ printk(KERN_INFO"[5616 platform probe] %s/L%d.\n", __FUNCTION__, __LINE__);
+ ret = mmp_pcm_probe(pdev);
+ } else if (strcmp(platform_driver_name, "pdma_platform") == 0) {
+ printk(KERN_INFO"[5616 platform probe] %s/L%d.\n", __FUNCTION__, __LINE__);
+ ret = pxa_pcm_platform_register(&pdev->dev);
+ }
+#endif
+
+ printk(KERN_INFO"[5616 platform probe: end] %s/L%d.\n", __FUNCTION__, __LINE__);
+
+ return ret;
+}
+
+static struct platform_driver asoc_ssp_driver = {
+ .driver = {
+ .name = "pxa-ssp-dai",
+ .of_match_table = of_match_ptr(pxa_ssp_of_ids),
+ },
+
+ .probe = asoc_ssp_probe,
+ //.remove = asoc_ssp_remove,
+};
+
+module_platform_driver(asoc_ssp_driver);
+
+/* Module information */
+MODULE_AUTHOR("wenchen@asrmicro.com");
+MODULE_DESCRIPTION("ASR SSP/PCM SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/sound/soc/pxa/tosa.c b/marvell/linux/sound/soc/pxa/tosa.c
new file mode 100644
index 0000000..b429db2
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/tosa.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * tosa.c -- SoC audio for Tosa
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
+ * Richard Purdie <richard@openedhand.com>
+ *
+ * GPIO's
+ * 1 - Jack Insertion
+ * 5 - Hookswitch (headset answer/hang up switch)
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <mach/tosa.h>
+#include <mach/audio.h>
+
+#define TOSA_HP 0
+#define TOSA_MIC_INT 1
+#define TOSA_HEADSET 2
+#define TOSA_HP_OFF 3
+#define TOSA_SPK_ON 0
+#define TOSA_SPK_OFF 1
+
+static int tosa_jack_func;
+static int tosa_spk_func;
+
+static void tosa_ext_control(struct snd_soc_dapm_context *dapm)
+{
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* set up jack connection */
+ switch (tosa_jack_func) {
+ case TOSA_HP:
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+ break;
+ case TOSA_MIC_INT:
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Mic (Internal)");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+ break;
+ case TOSA_HEADSET:
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
+ break;
+ }
+
+ if (tosa_spk_func == TOSA_SPK_ON)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
+
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static int tosa_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ /* check the jack status at stream startup */
+ tosa_ext_control(&rtd->card->dapm);
+
+ return 0;
+}
+
+static const struct snd_soc_ops tosa_ops = {
+ .startup = tosa_startup,
+};
+
+static int tosa_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = tosa_jack_func;
+ return 0;
+}
+
+static int tosa_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (tosa_jack_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ tosa_jack_func = ucontrol->value.enumerated.item[0];
+ tosa_ext_control(&card->dapm);
+ return 1;
+}
+
+static int tosa_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = tosa_spk_func;
+ return 0;
+}
+
+static int tosa_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (tosa_spk_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ tosa_spk_func = ucontrol->value.enumerated.item[0];
+ tosa_ext_control(&card->dapm);
+ return 1;
+}
+
+/* tosa dapm event handlers */
+static int tosa_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(TOSA_GPIO_L_MUTE, SND_SOC_DAPM_EVENT_ON(event) ? 1 : 0);
+ return 0;
+}
+
+/* tosa machine dapm widgets */
+static const struct snd_soc_dapm_widget tosa_dapm_widgets[] = {
+SND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event),
+SND_SOC_DAPM_HP("Headset Jack", NULL),
+SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
+SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+/* tosa audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* headphone connected to HPOUTL, HPOUTR */
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Headphone Jack", NULL, "HPOUTR"},
+
+ /* ext speaker connected to LOUT2, ROUT2 */
+ {"Speaker", NULL, "LOUT2"},
+ {"Speaker", NULL, "ROUT2"},
+
+ /* internal mic is connected to mic1, mic2 differential - with bias */
+ {"MIC1", NULL, "Mic Bias"},
+ {"MIC2", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Mic (Internal)"},
+
+ /* headset is connected to HPOUTR, and LINEINR with bias */
+ {"Headset Jack", NULL, "HPOUTR"},
+ {"LINEINR", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Headset Jack"},
+};
+
+static const char * const jack_function[] = {"Headphone", "Mic", "Line",
+ "Headset", "Off"};
+static const char * const spk_function[] = {"On", "Off"};
+static const struct soc_enum tosa_enum[] = {
+ SOC_ENUM_SINGLE_EXT(5, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new tosa_controls[] = {
+ SOC_ENUM_EXT("Jack Function", tosa_enum[0], tosa_get_jack,
+ tosa_set_jack),
+ SOC_ENUM_EXT("Speaker Function", tosa_enum[1], tosa_get_spk,
+ tosa_set_spk),
+};
+
+SND_SOC_DAILINK_DEFS(ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+SND_SOC_DAILINK_DEFS(ac97_aux,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-aux")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link tosa_dai[] = {
+{
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .ops = &tosa_ops,
+ SND_SOC_DAILINK_REG(ac97),
+},
+{
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ .ops = &tosa_ops,
+ SND_SOC_DAILINK_REG(ac97_aux),
+},
+};
+
+static struct snd_soc_card tosa = {
+ .name = "Tosa",
+ .owner = THIS_MODULE,
+ .dai_link = tosa_dai,
+ .num_links = ARRAY_SIZE(tosa_dai),
+
+ .controls = tosa_controls,
+ .num_controls = ARRAY_SIZE(tosa_controls),
+ .dapm_widgets = tosa_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .fully_routed = true,
+};
+
+static int tosa_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = ⤩
+ int ret;
+
+ ret = gpio_request_one(TOSA_GPIO_L_MUTE, GPIOF_OUT_INIT_LOW,
+ "Headphone Jack");
+ if (ret)
+ return ret;
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ gpio_free(TOSA_GPIO_L_MUTE);
+ }
+ return ret;
+}
+
+static int tosa_remove(struct platform_device *pdev)
+{
+ gpio_free(TOSA_GPIO_L_MUTE);
+ return 0;
+}
+
+static struct platform_driver tosa_driver = {
+ .driver = {
+ .name = "tosa-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = tosa_probe,
+ .remove = tosa_remove,
+};
+
+module_platform_driver(tosa_driver);
+
+/* Module information */
+MODULE_AUTHOR("Richard Purdie");
+MODULE_DESCRIPTION("ALSA SoC Tosa");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:tosa-audio");
diff --git a/marvell/linux/sound/soc/pxa/ttc-dkb.c b/marvell/linux/sound/soc/pxa/ttc-dkb.c
new file mode 100644
index 0000000..d8f79e2
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/ttc-dkb.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * linux/sound/soc/pxa/ttc_dkb.c
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <asm/mach-types.h>
+#include <sound/pcm_params.h>
+#include "../codecs/88pm860x-codec.h"
+
+static struct snd_soc_jack hs_jack, mic_jack;
+
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ { .pin = "Headset Stereophone", .mask = SND_JACK_HEADPHONE, },
+};
+
+static struct snd_soc_jack_pin mic_jack_pins[] = {
+ { .pin = "Headset Mic 2", .mask = SND_JACK_MICROPHONE, },
+};
+
+/* ttc machine dapm widgets */
+static const struct snd_soc_dapm_widget ttc_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+ SND_SOC_DAPM_LINE("Lineout Out 1", NULL),
+ SND_SOC_DAPM_LINE("Lineout Out 2", NULL),
+ SND_SOC_DAPM_SPK("Ext Speaker", NULL),
+ SND_SOC_DAPM_MIC("Ext Mic 1", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic 2", NULL),
+ SND_SOC_DAPM_MIC("Ext Mic 3", NULL),
+};
+
+/* ttc machine audio map */
+static const struct snd_soc_dapm_route ttc_audio_map[] = {
+ {"Headset Stereophone", NULL, "HS1"},
+ {"Headset Stereophone", NULL, "HS2"},
+
+ {"Ext Speaker", NULL, "LSP"},
+ {"Ext Speaker", NULL, "LSN"},
+
+ {"Lineout Out 1", NULL, "LINEOUT1"},
+ {"Lineout Out 2", NULL, "LINEOUT2"},
+
+ {"MIC1P", NULL, "Mic1 Bias"},
+ {"MIC1N", NULL, "Mic1 Bias"},
+ {"Mic1 Bias", NULL, "Ext Mic 1"},
+
+ {"MIC2P", NULL, "Mic1 Bias"},
+ {"MIC2N", NULL, "Mic1 Bias"},
+ {"Mic1 Bias", NULL, "Headset Mic 2"},
+
+ {"MIC3P", NULL, "Mic3 Bias"},
+ {"MIC3N", NULL, "Mic3 Bias"},
+ {"Mic3 Bias", NULL, "Ext Mic 3"},
+};
+
+static int ttc_pm860x_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = rtd->codec_dai->component;
+
+ /* Headset jack detection */
+ snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
+ &hs_jack, hs_jack_pins, ARRAY_SIZE(hs_jack_pins));
+ snd_soc_card_jack_new(rtd->card, "Microphone Jack", SND_JACK_MICROPHONE,
+ &mic_jack, mic_jack_pins,
+ ARRAY_SIZE(mic_jack_pins));
+
+ /* headphone, microphone detection & headset short detection */
+ pm860x_hs_jack_detect(component, &hs_jack, SND_JACK_HEADPHONE,
+ SND_JACK_BTN_0, SND_JACK_BTN_1, SND_JACK_BTN_2);
+ pm860x_mic_jack_detect(component, &hs_jack, SND_JACK_MICROPHONE);
+
+ return 0;
+}
+
+/* ttc/td-dkb digital audio interface glue - connects codec <--> CPU */
+SND_SOC_DAILINK_DEFS(i2s,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.1")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("88pm860x-codec", "88pm860x-i2s")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("mmp-pcm-audio")));
+
+static struct snd_soc_dai_link ttc_pm860x_hifi_dai[] = {
+{
+ .name = "88pm860x i2s",
+ .stream_name = "audio playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+ .init = ttc_pm860x_init,
+ SND_SOC_DAILINK_REG(i2s),
+},
+};
+
+/* ttc/td audio machine driver */
+static struct snd_soc_card ttc_dkb_card = {
+ .name = "ttc-dkb-hifi",
+ .owner = THIS_MODULE,
+ .dai_link = ttc_pm860x_hifi_dai,
+ .num_links = ARRAY_SIZE(ttc_pm860x_hifi_dai),
+
+ .dapm_widgets = ttc_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ttc_dapm_widgets),
+ .dapm_routes = ttc_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ttc_audio_map),
+};
+
+static int ttc_dkb_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &ttc_dkb_card;
+ int ret;
+
+ card->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+
+ return ret;
+}
+
+static struct platform_driver ttc_dkb_driver = {
+ .driver = {
+ .name = "ttc-dkb-audio",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = ttc_dkb_probe,
+};
+
+module_platform_driver(ttc_dkb_driver);
+
+/* Module information */
+MODULE_AUTHOR("Qiao Zhou, <zhouqiao@marvell.com>");
+MODULE_DESCRIPTION("ALSA SoC TTC DKB");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ttc-dkb-audio");
diff --git a/marvell/linux/sound/soc/pxa/z2.c b/marvell/linux/sound/soc/pxa/z2.c
new file mode 100644
index 0000000..f9a33cb
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/z2.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * linux/sound/soc/pxa/z2.c
+ *
+ * SoC Audio driver for Aeronix Zipit Z2
+ *
+ * Copyright (C) 2009 Ken McGuire <kenm@desertweyr.com>
+ * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/audio.h>
+#include <mach/z2.h>
+
+#include "../codecs/wm8750.h"
+#include "pxa2xx-i2s.h"
+
+static struct snd_soc_card snd_soc_z2;
+
+static int z2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ unsigned int clk = 0;
+ int ret = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ clk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk = 11289600;
+ break;
+ }
+
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as input (unused) */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_jack hs_jack;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Ext Spk",
+ .mask = SND_JACK_HEADPHONE,
+ .invert = 1
+ },
+};
+
+/* Headset jack detection gpios */
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+ {
+ .gpio = GPIO37_ZIPITZ2_HEADSET_DETECT,
+ .name = "hsdet-gpio",
+ .report = SND_JACK_HEADSET,
+ .debounce_time = 200,
+ .invert = 1,
+ },
+};
+
+/* z2 machine dapm widgets */
+static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+
+ /* headset is a mic and mono headphone */
+ SND_SOC_DAPM_HP("Headset Jack", NULL),
+};
+
+/* Z2 machine audio_map */
+static const struct snd_soc_dapm_route z2_audio_map[] = {
+
+ /* headphone connected to LOUT1, ROUT1 */
+ {"Headphone Jack", NULL, "LOUT1"},
+ {"Headphone Jack", NULL, "ROUT1"},
+
+ /* ext speaker connected to LOUT2, ROUT2 */
+ {"Ext Spk", NULL, "ROUT2"},
+ {"Ext Spk", NULL, "LOUT2"},
+
+ /* mic is connected to R input 2 - with bias */
+ {"RINPUT2", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Mic Jack"},
+};
+
+/*
+ * Logic for a wm8750 as connected on a Z2 Device
+ */
+static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+
+ /* Jack detection API stuff */
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
+ &hs_jack, hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
+ if (ret)
+ goto err;
+
+ ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static const struct snd_soc_ops z2_ops = {
+ .hw_params = z2_hw_params,
+};
+
+/* z2 digital audio interface glue - connects codec <--> CPU */
+SND_SOC_DAILINK_DEFS(wm8750,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-001b", "wm8750-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link z2_dai = {
+ .name = "wm8750",
+ .stream_name = "WM8750",
+ .init = z2_wm8750_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &z2_ops,
+ SND_SOC_DAILINK_REG(wm8750),
+};
+
+/* z2 audio machine driver */
+static struct snd_soc_card snd_soc_z2 = {
+ .name = "Z2",
+ .owner = THIS_MODULE,
+ .dai_link = &z2_dai,
+ .num_links = 1,
+
+ .dapm_widgets = wm8750_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
+ .dapm_routes = z2_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(z2_audio_map),
+ .fully_routed = true,
+};
+
+static struct platform_device *z2_snd_device;
+
+static int __init z2_init(void)
+{
+ int ret;
+
+ if (!machine_is_zipit2())
+ return -ENODEV;
+
+ z2_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!z2_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(z2_snd_device, &snd_soc_z2);
+ ret = platform_device_add(z2_snd_device);
+
+ if (ret)
+ platform_device_put(z2_snd_device);
+
+ return ret;
+}
+
+static void __exit z2_exit(void)
+{
+ platform_device_unregister(z2_snd_device);
+}
+
+module_init(z2_init);
+module_exit(z2_exit);
+
+MODULE_AUTHOR("Ken McGuire <kenm@desertweyr.com>, "
+ "Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC ZipitZ2");
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/sound/soc/pxa/zylonite.c b/marvell/linux/sound/soc/pxa/zylonite.c
new file mode 100644
index 0000000..567dc13
--- /dev/null
+++ b/marvell/linux/sound/soc/pxa/zylonite.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * zylonite.c -- SoC audio for Zylonite
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm9713.h"
+#include "pxa-ssp.h"
+
+/*
+ * There is a physical switch SW15 on the board which changes the MCLK
+ * for the WM9713 between the standard AC97 master clock and the
+ * output of the CLK_POUT signal from the PXA.
+ */
+static int clk_pout;
+module_param(clk_pout, int, 0);
+MODULE_PARM_DESC(clk_pout, "Use CLK_POUT as WM9713 MCLK (SW15 on board).");
+
+static struct clk *pout;
+
+static struct snd_soc_card zylonite;
+
+static const struct snd_soc_dapm_widget zylonite_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Microphone", NULL),
+ SND_SOC_DAPM_MIC("Handset Microphone", NULL),
+ SND_SOC_DAPM_SPK("Multiactor", NULL),
+ SND_SOC_DAPM_SPK("Headset Earpiece", NULL),
+};
+
+/* Currently supported audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* Headphone output connected to HPL/HPR */
+ { "Headphone", NULL, "HPL" },
+ { "Headphone", NULL, "HPR" },
+
+ /* On-board earpiece */
+ { "Headset Earpiece", NULL, "OUT3" },
+
+ /* Headphone mic */
+ { "MIC2A", NULL, "Mic Bias" },
+ { "Mic Bias", NULL, "Headset Microphone" },
+
+ /* On-board mic */
+ { "MIC1", NULL, "Mic Bias" },
+ { "Mic Bias", NULL, "Handset Microphone" },
+
+ /* Multiactor differentially connected over SPKL/SPKR */
+ { "Multiactor", NULL, "SPKL" },
+ { "Multiactor", NULL, "SPKR" },
+};
+
+static int zylonite_wm9713_init(struct snd_soc_pcm_runtime *rtd)
+{
+ if (clk_pout)
+ snd_soc_dai_set_pll(rtd->codec_dai, 0, 0,
+ clk_get_rate(pout), 0);
+
+ return 0;
+}
+
+static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ unsigned int wm9713_div = 0;
+ int ret = 0;
+ int rate = params_rate(params);
+
+ /* Only support ratios that we can generate neatly from the AC97
+ * based master clock - in particular, this excludes 44.1kHz.
+ * In most applications the voice DAC will be used for telephony
+ * data so multiples of 8kHz will be the common case.
+ */
+ switch (rate) {
+ case 8000:
+ wm9713_div = 12;
+ break;
+ case 16000:
+ wm9713_div = 6;
+ break;
+ case 48000:
+ wm9713_div = 2;
+ break;
+ default:
+ /* Don't support OSS emulation */
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ if (clk_pout)
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_PLL_DIV,
+ WM9713_PCMDIV(wm9713_div));
+ else
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV,
+ WM9713_PCMDIV(wm9713_div));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_ops zylonite_voice_ops = {
+ .hw_params = zylonite_voice_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(ac97,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9713-codec", "wm9713-hifi")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+SND_SOC_DAILINK_DEFS(ac97_aux,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9713-codec", "wm9713-aux")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+SND_SOC_DAILINK_DEFS(voice,
+ DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.2")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("wm9713-codec", "wm9713-voice")),
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
+
+static struct snd_soc_dai_link zylonite_dai[] = {
+{
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .init = zylonite_wm9713_init,
+ SND_SOC_DAILINK_REG(ac97),
+},
+{
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ SND_SOC_DAILINK_REG(ac97_aux),
+},
+{
+ .name = "WM9713 Voice",
+ .stream_name = "WM9713 Voice",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &zylonite_voice_ops,
+ SND_SOC_DAILINK_REG(voice),
+},
+};
+
+static int zylonite_probe(struct snd_soc_card *card)
+{
+ int ret;
+
+ if (clk_pout) {
+ pout = clk_get(NULL, "CLK_POUT");
+ if (IS_ERR(pout)) {
+ dev_err(card->dev, "Unable to obtain CLK_POUT: %ld\n",
+ PTR_ERR(pout));
+ return PTR_ERR(pout);
+ }
+
+ ret = clk_enable(pout);
+ if (ret != 0) {
+ dev_err(card->dev, "Unable to enable CLK_POUT: %d\n",
+ ret);
+ clk_put(pout);
+ return ret;
+ }
+
+ dev_dbg(card->dev, "MCLK enabled at %luHz\n",
+ clk_get_rate(pout));
+ }
+
+ return 0;
+}
+
+static int zylonite_remove(struct snd_soc_card *card)
+{
+ if (clk_pout) {
+ clk_disable(pout);
+ clk_put(pout);
+ }
+
+ return 0;
+}
+
+static int zylonite_suspend_post(struct snd_soc_card *card)
+{
+ if (clk_pout)
+ clk_disable(pout);
+
+ return 0;
+}
+
+static int zylonite_resume_pre(struct snd_soc_card *card)
+{
+ int ret = 0;
+
+ if (clk_pout) {
+ ret = clk_enable(pout);
+ if (ret != 0)
+ dev_err(card->dev, "Unable to enable CLK_POUT: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
+static struct snd_soc_card zylonite = {
+ .name = "Zylonite",
+ .owner = THIS_MODULE,
+ .probe = &zylonite_probe,
+ .remove = &zylonite_remove,
+ .suspend_post = &zylonite_suspend_post,
+ .resume_pre = &zylonite_resume_pre,
+ .dai_link = zylonite_dai,
+ .num_links = ARRAY_SIZE(zylonite_dai),
+
+ .dapm_widgets = zylonite_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(zylonite_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+static struct platform_device *zylonite_snd_ac97_device;
+
+static int __init zylonite_init(void)
+{
+ int ret;
+
+ zylonite_snd_ac97_device = platform_device_alloc("soc-audio", -1);
+ if (!zylonite_snd_ac97_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(zylonite_snd_ac97_device, &zylonite);
+
+ ret = platform_device_add(zylonite_snd_ac97_device);
+ if (ret != 0)
+ platform_device_put(zylonite_snd_ac97_device);
+
+ return ret;
+}
+
+static void __exit zylonite_exit(void)
+{
+ platform_device_unregister(zylonite_snd_ac97_device);
+}
+
+module_init(zylonite_init);
+module_exit(zylonite_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("ALSA SoC WM9713 Zylonite");
+MODULE_LICENSE("GPL");