diff options
Diffstat (limited to 'sound/soc/pxa')
31 files changed, 4689 insertions, 1442 deletions
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index f82e1069947..2434b6d6167 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -1,12 +1,23 @@ config SND_PXA2XX_SOC tristate "SoC Audio for the Intel PXA2xx chip" depends on ARCH_PXA + select SND_ARM 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_PXA2XX_AC97 tristate select SND_AC97_CODEC @@ -23,10 +34,14 @@ config SND_PXA2XX_SOC_I2S config SND_PXA_SOC_SSP tristate + select PXA_SSP + +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 + depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx && I2C select SND_PXA2XX_SOC_I2S select SND_SOC_WM8731 help @@ -35,16 +50,24 @@ config SND_PXA2XX_SOC_CORGI config SND_PXA2XX_SOC_SPITZ tristate "SoC Audio support for Sharp Zaurus SL-Cxx00" - depends on SND_PXA2XX_SOC && PXA_SHARP_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 + depends on SND_PXA2XX_SOC && MACH_POODLE && I2C select SND_PXA2XX_SOC_I2S select SND_SOC_WM8731 help @@ -61,6 +84,24 @@ config SND_PXA2XX_SOC_TOSA 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 + 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 + 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 @@ -71,22 +112,44 @@ config SND_PXA2XX_SOC_E800 Toshiba e800 PDA config SND_PXA2XX_SOC_EM_X270 - tristate "SoC Audio support for CompuLab EM-x270" - depends on SND_PXA2XX_SOC && MACH_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) 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. + CompuLab EM-x270, eXeda and CM-X300 machines. config SND_PXA2XX_SOC_PALM27X - bool "SoC Audio support for Palm T|X, T5 and LifeDrive" - depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || MACH_PALMT5) + 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) 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 or LifeDrive handheld computer. + 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 + bool "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" @@ -97,3 +160,60 @@ config SND_SOC_ZYLONITE help Say Y if you want to add support for SoC audio on the Marvell Zylonite reference platform. + +config SND_SOC_RAUMFELD + tristate "SoC Audio support Raumfeld audio adapter" + depends on SND_PXA2XX_SOC && (MACH_RAUMFELD_SPEAKER || MACH_RAUMFELD_CONNECTOR) + depends on I2C && SPI_MASTER + select SND_PXA_SOC_SSP + select SND_SOC_CS4270 + select SND_SOC_AK4104 + help + Say Y if you want to add support for SoC audio on Raumfeld devices + +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 + 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 + 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. diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 08a9f279772..2cff67b61dc 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -3,27 +3,51 @@ 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-mmp-objs := mmp-pcm.o +snd-soc-mmp-sspa-objs := mmp-sspa.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 obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o +obj-$(CONFIG_SND_MMP_SOC) += snd-soc-mmp.o +obj-$(CONFIG_SND_MMP_SOC_SSPA) += snd-soc-mmp-sspa.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-raumfeld-objs := raumfeld.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_SOC_RAUMFELD) += snd-soc-raumfeld.o +obj-$(CONFIG_SND_MMP_SOC_BROWNSTONE) += snd-soc-brownstone.o +obj-$(CONFIG_SND_SOC_TTC_DKB) += snd-soc-ttc-dkb.o diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c new file mode 100644 index 00000000000..c8dd53f9c35 --- /dev/null +++ b/sound/soc/pxa/brownstone.c @@ -0,0 +1,169 @@ +/* + * linux/sound/soc/pxa/brownstone.c + * + * Copyright (C) 2011 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. + * + */ + +#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_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + /* set endpoints to not connected */ + snd_soc_dapm_nc_pin(dapm, "HPOUT2P"); + snd_soc_dapm_nc_pin(dapm, "HPOUT2N"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT1N"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT1P"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT2N"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT2P"); + snd_soc_dapm_nc_pin(dapm, "IN1LN"); + snd_soc_dapm_nc_pin(dapm, "IN1LP"); + snd_soc_dapm_nc_pin(dapm, "IN1RP"); + snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN"); + snd_soc_dapm_nc_pin(dapm, "IN2RN"); + snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP"); + snd_soc_dapm_nc_pin(dapm, "IN2LN"); + + return 0; +} + +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; + int sspa_div; + + 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; + } + sspa_div = freq_out; + do_div(sspa_div, sspa_mclk); + + 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 struct snd_soc_ops brownstone_ops = { + .hw_params = brownstone_wm8994_hw_params, +}; + +static struct snd_soc_dai_link brownstone_wm8994_dai[] = { +{ + .name = "WM8994", + .stream_name = "WM8994 HiFi", + .cpu_dai_name = "mmp-sspa-dai.0", + .codec_dai_name = "wm8994-aif1", + .platform_name = "mmp-pcm-audio", + .codec_name = "wm8994-codec", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &brownstone_ops, + .init = brownstone_wm8994_init, +}, +}; + +/* 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), +}; + +static int brownstone_probe(struct platform_device *pdev) +{ + int ret; + + brownstone.dev = &pdev->dev; + ret = snd_soc_register_card(&brownstone); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); + return ret; +} + +static int brownstone_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&brownstone); + return 0; +} + +static struct platform_driver mmp_driver = { + .driver = { + .name = "brownstone-audio", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = brownstone_probe, + .remove = brownstone_remove, +}; + +module_platform_driver(mmp_driver); + +MODULE_AUTHOR("Leo Yan <leoy@marvell.com>"); +MODULE_DESCRIPTION("ALSA SoC Brownstone"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 1ba25a55952..5a88136aa80 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -16,22 +16,19 @@ #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 <sound/soc-dapm.h> #include <asm/mach-types.h> -#include <mach/pxa-regs.h> -#include <mach/hardware.h> #include <mach/corgi.h> #include <mach/audio.h> #include "../codecs/wm8731.h" -#include "pxa2xx-pcm.h" #include "pxa2xx-i2s.h" #define CORGI_HP 0 @@ -48,62 +45,66 @@ static int corgi_jack_func; static int corgi_spk_func; -static void corgi_ext_control(struct snd_soc_codec *codec) +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(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "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"); + 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(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); + 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(codec, "Mic Jack"); - snd_soc_dapm_enable_pin(codec, "Line Jack"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); + 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(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Headset Jack"); + 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(codec, "Ext Spk"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(codec, "Ext Spk"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); /* signal a DAPM event */ - snd_soc_dapm_sync(codec); + 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; - struct snd_soc_codec *codec = rtd->socdev->codec; /* check the jack status at stream startup */ - corgi_ext_control(codec); + corgi_ext_control(&rtd->card->dapm); + return 0; } @@ -119,8 +120,8 @@ 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->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + 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; @@ -138,20 +139,8 @@ static int corgi_hw_params(struct snd_pcm_substream *substream, break; } - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - 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_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk, + ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk, SND_SOC_CLOCK_IN); if (ret < 0) return ret; @@ -181,13 +170,13 @@ static int corgi_get_jack(struct snd_kcontrol *kcontrol, static int corgi_set_jack(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); if (corgi_jack_func == ucontrol->value.integer.value[0]) return 0; corgi_jack_func = ucontrol->value.integer.value[0]; - corgi_ext_control(codec); + corgi_ext_control(&card->dapm); return 1; } @@ -201,13 +190,13 @@ static int corgi_get_spk(struct snd_kcontrol *kcontrol, static int corgi_set_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); if (corgi_spk_func == ucontrol->value.integer.value[0]) return 0; corgi_spk_func = ucontrol->value.integer.value[0]; - corgi_ext_control(codec); + corgi_ext_control(&card->dapm); return 1; } @@ -235,7 +224,7 @@ SND_SOC_DAPM_HP("Headset Jack", NULL), }; /* Corgi machine audio map (connections to the codec pins) */ -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route corgi_audio_map[] = { /* headset Jack - in = micin, out = LHPOUT*/ {"Headset Jack", NULL, "LHPOUT"}, @@ -273,29 +262,14 @@ static const struct snd_kcontrol_new wm8731_corgi_controls[] = { /* * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device */ -static int corgi_wm8731_init(struct snd_soc_codec *codec) +static int corgi_wm8731_init(struct snd_soc_pcm_runtime *rtd) { - int i, err; - - snd_soc_dapm_nc_pin(codec, "LLINEIN"); - snd_soc_dapm_nc_pin(codec, "RLINEIN"); - - /* Add corgi specific controls */ - for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8731_corgi_controls[i], codec, NULL)); - if (err < 0) - return err; - } + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; - /* Add corgi specific widgets */ - snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, - ARRAY_SIZE(wm8731_dapm_widgets)); + snd_soc_dapm_nc_pin(dapm, "LLINEIN"); + snd_soc_dapm_nc_pin(dapm, "RLINEIN"); - /* Set up corgi specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - - snd_soc_dapm_sync(codec); return 0; } @@ -303,66 +277,67 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec) static struct snd_soc_dai_link corgi_dai = { .name = "WM8731", .stream_name = "WM8731", - .cpu_dai = &pxa_i2s_dai, - .codec_dai = &wm8731_dai, + .cpu_dai_name = "pxa2xx-i2s", + .codec_dai_name = "wm8731-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm8731.0-001b", .init = corgi_wm8731_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, .ops = &corgi_ops, }; /* corgi audio machine driver */ -static struct snd_soc_card snd_soc_corgi = { +static struct snd_soc_card corgi = { .name = "Corgi", - .platform = &pxa2xx_soc_platform, + .owner = THIS_MODULE, .dai_link = &corgi_dai, .num_links = 1, -}; - -/* corgi audio private data */ -static struct wm8731_setup_data corgi_wm8731_setup = { - .i2c_bus = 0, - .i2c_address = 0x1b, -}; -/* corgi audio subsystem */ -static struct snd_soc_device corgi_snd_devdata = { - .card = &snd_soc_corgi, - .codec_dev = &soc_codec_dev_wm8731, - .codec_data = &corgi_wm8731_setup, + .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), }; -static struct platform_device *corgi_snd_device; - -static int __init corgi_init(void) +static int corgi_probe(struct platform_device *pdev) { + struct snd_soc_card *card = &corgi; int ret; - if (!(machine_is_corgi() || machine_is_shepherd() || - machine_is_husky())) - return -ENODEV; - - corgi_snd_device = platform_device_alloc("soc-audio", -1); - if (!corgi_snd_device) - return -ENOMEM; - - platform_set_drvdata(corgi_snd_device, &corgi_snd_devdata); - corgi_snd_devdata.dev = &corgi_snd_device->dev; - ret = platform_device_add(corgi_snd_device); + card->dev = &pdev->dev; + ret = snd_soc_register_card(card); if (ret) - platform_device_put(corgi_snd_device); - + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); return ret; } -static void __exit corgi_exit(void) +static int corgi_remove(struct platform_device *pdev) { - platform_device_unregister(corgi_snd_device); + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + return 0; } -module_init(corgi_init); -module_exit(corgi_exit); +static struct platform_driver corgi_driver = { + .driver = { + .name = "corgi-audio", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = corgi_probe, + .remove = corgi_remove, +}; + +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/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c new file mode 100644 index 00000000000..c29fedab2f4 --- /dev/null +++ b/sound/soc/pxa/e740_wm9705.c @@ -0,0 +1,193 @@ +/* + * e740-wm9705.c -- SoC audio for e740 + * + * Copyright 2007 (c) Ian Molton <spyro@f2s.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; version 2 ONLY. + * + */ + +#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> + +#include "../codecs/wm9705.h" +#include "pxa2xx-ac97.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)"}, +}; + +static int e740_ac97_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_nc_pin(dapm, "HPOUTL"); + snd_soc_dapm_nc_pin(dapm, "HPOUTR"); + snd_soc_dapm_nc_pin(dapm, "PHONE"); + snd_soc_dapm_nc_pin(dapm, "LINEINL"); + snd_soc_dapm_nc_pin(dapm, "LINEINR"); + snd_soc_dapm_nc_pin(dapm, "CDINL"); + snd_soc_dapm_nc_pin(dapm, "CDINR"); + snd_soc_dapm_nc_pin(dapm, "PCBEEP"); + snd_soc_dapm_nc_pin(dapm, "MIC2"); + + return 0; +} + +static struct snd_soc_dai_link e740_dai[] = { + { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai_name = "pxa2xx-ac97", + .codec_dai_name = "wm9705-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm9705-codec", + .init = e740_ac97_init, + }, + { + .name = "AC97 Aux", + .stream_name = "AC97 Aux", + .cpu_dai_name = "pxa2xx-ac97-aux", + .codec_dai_name = "wm9705-aux", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm9705-codec", + }, +}; + +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), +}; + +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 = snd_soc_register_card(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) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios)); + snd_soc_unregister_card(card); + return 0; +} + +static struct platform_driver e740_driver = { + .driver = { + .name = "e740-audio", + .owner = THIS_MODULE, + .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/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c new file mode 100644 index 00000000000..ee36aba8806 --- /dev/null +++ b/sound/soc/pxa/e750_wm9705.c @@ -0,0 +1,175 @@ +/* + * e750-wm9705.c -- SoC audio for e750 + * + * Copyright 2007 (c) Ian Molton <spyro@f2s.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; version 2 ONLY. + * + */ + +#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> + +#include "../codecs/wm9705.h" +#include "pxa2xx-ac97.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)"}, +}; + +static int e750_ac97_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_nc_pin(dapm, "LOUT"); + snd_soc_dapm_nc_pin(dapm, "ROUT"); + snd_soc_dapm_nc_pin(dapm, "PHONE"); + snd_soc_dapm_nc_pin(dapm, "LINEINL"); + snd_soc_dapm_nc_pin(dapm, "LINEINR"); + snd_soc_dapm_nc_pin(dapm, "CDINL"); + snd_soc_dapm_nc_pin(dapm, "CDINR"); + snd_soc_dapm_nc_pin(dapm, "PCBEEP"); + snd_soc_dapm_nc_pin(dapm, "MIC2"); + + return 0; +} + +static struct snd_soc_dai_link e750_dai[] = { + { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai_name = "pxa2xx-ac97", + .codec_dai_name = "wm9705-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm9705-codec", + .init = e750_ac97_init, + /* use ops to check startup state */ + }, + { + .name = "AC97 Aux", + .stream_name = "AC97 Aux", + .cpu_dai_name = "pxa2xx-ac97-aux", + .codec_dai_name ="wm9705-aux", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm9705-codec", + }, +}; + +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), +}; + +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 = snd_soc_register_card(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) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios)); + snd_soc_unregister_card(card); + return 0; +} + +static struct platform_driver e750_driver = { + .driver = { + .name = "e750-audio", + .owner = THIS_MODULE, + .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/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index 2e3386dfa0f..24c2078ce70 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -1,8 +1,6 @@ /* * e800-wm9712.c -- SoC audio for e800 * - * Based on tosa.c - * * Copyright 2007 (c) Ian Molton <spyro@f2s.com> * * This program is free software; you can redistribute it and/or modify it @@ -13,77 +11,146 @@ #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/soc-dapm.h> #include <asm/mach-types.h> -#include <mach/pxa-regs.h> -#include <mach/hardware.h> #include <mach/audio.h> +#include <mach/eseries-gpio.h> #include "../codecs/wm9712.h" -#include "pxa2xx-pcm.h" #include "pxa2xx-ac97.h" -static struct snd_soc_card e800; +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); -static struct snd_soc_dai_link e800_dai[] = { + return 0; +} + +static int e800_hp_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { - .name = "AC97 Aux", - .stream_name = "AC97 Aux", - .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], - .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], -}, + 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)"}, +}; + +static struct snd_soc_dai_link e800_dai[] = { + { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai_name = "pxa2xx-ac97", + .codec_dai_name = "wm9712-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm9712-codec", + }, + { + .name = "AC97 Aux", + .stream_name = "AC97 Aux", + .cpu_dai_name = "pxa2xx-ac97-aux", + .codec_dai_name ="wm9712-aux", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm9712-codec", + }, }; static struct snd_soc_card e800 = { .name = "Toshiba e800", - .platform = &pxa2xx_soc_platform, + .owner = THIS_MODULE, .dai_link = e800_dai, .num_links = ARRAY_SIZE(e800_dai), -}; -static struct snd_soc_device e800_snd_devdata = { - .card = &e800, - .codec_dev = &soc_codec_dev_wm9712, + .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 platform_device *e800_snd_device; +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 __init e800_init(void) +static int e800_probe(struct platform_device *pdev) { + struct snd_soc_card *card = &e800; int ret; - if (!machine_is_e800()) - return -ENODEV; - - e800_snd_device = platform_device_alloc("soc-audio", -1); - if (!e800_snd_device) - return -ENOMEM; - - platform_set_drvdata(e800_snd_device, &e800_snd_devdata); - e800_snd_devdata.dev = &e800_snd_device->dev; - ret = platform_device_add(e800_snd_device); - + ret = gpio_request_array(e800_audio_gpios, + ARRAY_SIZE(e800_audio_gpios)); if (ret) - platform_device_put(e800_snd_device); + return ret; + + card->dev = &pdev->dev; + ret = snd_soc_register_card(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 void __exit e800_exit(void) +static int e800_remove(struct platform_device *pdev) { - platform_device_unregister(e800_snd_device); + struct snd_soc_card *card = platform_get_drvdata(pdev); + + gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios)); + snd_soc_unregister_card(card); + return 0; } -module_init(e800_init); -module_exit(e800_exit); +static struct platform_driver e800_driver = { + .driver = { + .name = "e800-audio", + .owner = THIS_MODULE, + .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"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:e800-audio"); diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c index fe4a729ea64..64743a05aea 100644 --- a/sound/soc/pxa/em-x270.c +++ b/sound/soc/pxa/em-x270.c @@ -1,7 +1,7 @@ /* - * em-x270.c -- SoC audio for EM-X270 + * SoC audio driver for EM-X270, eXeda and CM-X300 * - * Copyright 2007 CompuLab, Ltd. + * Copyright 2007, 2009 CompuLab, Ltd. * * Author: Mike Rapoport <mike@compulab.co.il> * @@ -26,59 +26,54 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> -#include <mach/pxa-regs.h> -#include <mach/hardware.h> #include <mach/audio.h> #include "../codecs/wm9712.h" -#include "pxa2xx-pcm.h" #include "pxa2xx-ac97.h" static struct snd_soc_dai_link em_x270_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], - .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], + .cpu_dai_name = "pxa2xx-ac97", + .codec_dai_name = "wm9712-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm9712-codec", }, { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], - .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], + .cpu_dai_name = "pxa2xx-ac97-aux", + .codec_dai_name ="wm9712-aux", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm9712-codec", }, }; static struct snd_soc_card em_x270 = { .name = "EM-X270", - .platform = &pxa2xx_soc_platform, + .owner = THIS_MODULE, .dai_link = em_x270_dai, .num_links = ARRAY_SIZE(em_x270_dai), }; -static struct snd_soc_device em_x270_snd_devdata = { - .card = &em_x270, - .codec_dev = &soc_codec_dev_wm9712, -}; - static struct platform_device *em_x270_snd_device; static int __init em_x270_init(void) { int ret; - if (!machine_is_em_x270()) + 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_snd_devdata); - em_x270_snd_devdata.dev = &em_x270_snd_device->dev; + platform_set_drvdata(em_x270_snd_device, &em_x270); ret = platform_device_add(em_x270_snd_device); if (ret) @@ -97,5 +92,5 @@ module_exit(em_x270_exit); /* Module information */ MODULE_AUTHOR("Mike Rapoport"); -MODULE_DESCRIPTION("ALSA SoC EM-X270"); +MODULE_DESCRIPTION("ALSA SoC EM-X270, eXeda and CM-X300"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c new file mode 100644 index 00000000000..05559a725be --- /dev/null +++ b/sound/soc/pxa/hx4700.c @@ -0,0 +1,241 @@ +/* + * SoC audio for HP iPAQ hx4700 + * + * Copyright (c) 2009 Philipp Zabel + * + * 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. + * + */ + +#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" + +#include "../codecs/ak4641.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 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) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int err; + + /* NC codec pins */ + /* FIXME: is anything connected here? */ + snd_soc_dapm_nc_pin(dapm, "MOUT1"); + snd_soc_dapm_nc_pin(dapm, "MICEXT"); + snd_soc_dapm_nc_pin(dapm, "AUX"); + + /* Jack detection API stuff */ + err = snd_soc_jack_new(codec, "Headphone Jack", + SND_JACK_HEADPHONE, &hs_jack); + if (err) + return err; + + err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pin), + hs_jack_pin); + if (err) + return err; + + err = snd_soc_jack_add_gpios(&hs_jack, 1, &hs_jack_gpio); + + return err; +} + +static int hx4700_card_remove(struct snd_soc_card *card) +{ + snd_soc_jack_free_gpios(&hs_jack, 1, &hs_jack_gpio); + + return 0; +} + +/* hx4700 digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link hx4700_dai = { + .name = "ak4641", + .stream_name = "AK4641", + .cpu_dai_name = "pxa2xx-i2s", + .codec_dai_name = "ak4641-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "ak4641.0-0012", + .init = hx4700_ak4641_init, + .dai_fmt = SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &hx4700_ops, +}; + +/* hx4700 audio machine driver */ +static struct snd_soc_card snd_soc_card_hx4700 = { + .name = "iPAQ hx4700", + .owner = THIS_MODULE, + .remove = hx4700_card_remove, + .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), +}; + +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 = snd_soc_register_card(&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) +{ + snd_soc_unregister_card(&snd_soc_card_hx4700); + + 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", + .owner = THIS_MODULE, + .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/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c new file mode 100644 index 00000000000..fd2f4eda1fd --- /dev/null +++ b/sound/soc/pxa/imote2.c @@ -0,0 +1,105 @@ + +#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 struct snd_soc_ops imote2_asoc_ops = { + .hw_params = imote2_asoc_hw_params, +}; + +static struct snd_soc_dai_link imote2_dai = { + .name = "WM8940", + .stream_name = "WM8940", + .cpu_dai_name = "pxa2xx-i2s", + .codec_dai_name = "wm8940-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm8940-codec.0-0034", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &imote2_asoc_ops, +}; + +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 = snd_soc_register_card(card); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); + return ret; +} + +static int imote2_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 imote2_driver = { + .driver = { + .name = "imote2-audio", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = imote2_probe, + .remove = imote2_remove, +}; + +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/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c new file mode 100644 index 00000000000..259e048681c --- /dev/null +++ b/sound/soc/pxa/magician.c @@ -0,0 +1,553 @@ +/* + * 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> + * + * 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. + * + */ + +#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 acps, acds, width; + unsigned int div4 = PXA_SSP_CLK_SCDB_4; + int ret = 0; + + width = snd_pcm_format_physical_width(params_format(params)); + + /* + * rate = SSPSCLK / (2 * width(16 or 32)) + * SSPSCLK = (ACPS / ACDS) / SSPSCLKDIV(div4 or div1) + */ + switch (params_rate(params)) { + case 8000: + /* off by a factor of 2: bug in the PXA27x audio clock? */ + acps = 32842000; + switch (width) { + case 16: + /* 513156 Hz ~= _2_ * 8000 Hz * 32 (+0.23%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_16; + break; + default: /* 32 */ + /* 1026312 Hz ~= _2_ * 8000 Hz * 64 (+0.23%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_8; + } + break; + case 11025: + acps = 5622000; + switch (width) { + case 16: + /* 351375 Hz ~= 11025 Hz * 32 (-0.41%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_4; + break; + default: /* 32 */ + /* 702750 Hz ~= 11025 Hz * 64 (-0.41%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_2; + } + break; + case 22050: + acps = 5622000; + switch (width) { + case 16: + /* 702750 Hz ~= 22050 Hz * 32 (-0.41%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_2; + break; + default: /* 32 */ + /* 1405500 Hz ~= 22050 Hz * 64 (-0.41%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_1; + } + break; + case 44100: + acps = 5622000; + switch (width) { + case 16: + /* 1405500 Hz ~= 44100 Hz * 32 (-0.41%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_2; + break; + default: /* 32 */ + /* 2811000 Hz ~= 44100 Hz * 64 (-0.41%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_1; + } + break; + case 48000: + acps = 12235000; + switch (width) { + case 16: + /* 1529375 Hz ~= 48000 Hz * 32 (-0.44%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_2; + break; + default: /* 32 */ + /* 3058750 Hz ~= 48000 Hz * 64 (-0.44%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_1; + } + break; + case 96000: + default: + acps = 12235000; + switch (width) { + case 16: + /* 3058750 Hz ~= 96000 Hz * 32 (-0.44%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_1; + break; + default: /* 32 */ + /* 6117500 Hz ~= 96000 Hz * 64 (-0.44%) */ + acds = PXA_SSP_CLK_AUDIO_DIV_2; + div4 = PXA_SSP_CLK_SCDB_1; + break; + } + break; + } + + /* 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; + + 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; + + /* set the SSP audio system clock ACDS divider */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, + PXA_SSP_AUDIO_DIV_ACDS, acds); + if (ret < 0) + return ret; + + /* set the SSP audio system clock SCDB divider4 */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, + PXA_SSP_AUDIO_DIV_SCDB, div4); + if (ret < 0) + return ret; + + /* set SSP audio pll clock */ + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, acps); + 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 struct snd_soc_ops magician_capture_ops = { + .startup = magician_startup, + .hw_params = magician_capture_hw_params, +}; + +static 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.integer.value[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.integer.value[0]) + return 0; + + magician_in_sel = ucontrol->value.integer.value[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 *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), +}; + +/* + * Logic for a uda1380 as connected on a HTC Magician + */ +static int magician_uda1380_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + /* NC codec pins */ + snd_soc_dapm_nc_pin(dapm, "VOUTLHP"); + snd_soc_dapm_nc_pin(dapm, "VOUTRHP"); + + /* FIXME: is anything connected here? */ + snd_soc_dapm_nc_pin(dapm, "VINL"); + snd_soc_dapm_nc_pin(dapm, "VINR"); + + return 0; +} + +/* magician digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link magician_dai[] = { +{ + .name = "uda1380", + .stream_name = "UDA1380 Playback", + .cpu_dai_name = "pxa-ssp-dai.0", + .codec_dai_name = "uda1380-hifi-playback", + .platform_name = "pxa-pcm-audio", + .codec_name = "uda1380-codec.0-0018", + .init = magician_uda1380_init, + .ops = &magician_playback_ops, +}, +{ + .name = "uda1380", + .stream_name = "UDA1380 Capture", + .cpu_dai_name = "pxa2xx-i2s", + .codec_dai_name = "uda1380-hifi-capture", + .platform_name = "pxa-pcm-audio", + .codec_name = "uda1380-codec.0-0018", + .ops = &magician_capture_ops, +} +}; + +/* 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), +}; + +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/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c new file mode 100644 index 00000000000..595eee341e9 --- /dev/null +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -0,0 +1,218 @@ +/* + * Handles the Mitac mioa701 SoC system + * + * Copyright (C) 2008 Robert Jarzmik + * + * 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 in version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * 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 "pxa2xx-ac97.h" +#include "../codecs/wm9713.h" + +#define AC97_GPIO_PULL 0x58 + +/* Use GPIO8 for rear speaker amplifier */ +static int rear_amp_power(struct snd_soc_codec *codec, int power) +{ + unsigned short reg; + + if (power) { + reg = snd_soc_read(codec, AC97_GPIO_CFG); + snd_soc_write(codec, AC97_GPIO_CFG, reg | 0x0100); + reg = snd_soc_read(codec, AC97_GPIO_PULL); + snd_soc_write(codec, AC97_GPIO_PULL, reg | (1<<15)); + } else { + reg = snd_soc_read(codec, AC97_GPIO_CFG); + snd_soc_write(codec, AC97_GPIO_CFG, reg & ~0x0100); + reg = snd_soc_read(codec, AC97_GPIO_PULL); + snd_soc_write(codec, 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_codec *codec = widget->codec; + + return rear_amp_power(codec, 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_codec *codec = rtd->codec; + unsigned short reg; + + /* Prepare GPIO8 for rear speaker amplifier */ + reg = codec->driver->read(codec, AC97_GPIO_CFG); + codec->driver->write(codec, AC97_GPIO_CFG, reg | 0x0100); + + /* Prepare MIC input */ + reg = codec->driver->read(codec, AC97_3D_CONTROL); + codec->driver->write(codec, AC97_3D_CONTROL, reg | 0xc000); + + return 0; +} + +static struct snd_soc_ops mioa701_ops; + +static struct snd_soc_dai_link mioa701_dai[] = { + { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai_name = "pxa2xx-ac97", + .codec_dai_name = "wm9713-hifi", + .codec_name = "wm9713-codec", + .init = mioa701_wm9713_init, + .platform_name = "pxa-pcm-audio", + .ops = &mioa701_ops, + }, + { + .name = "AC97 Aux", + .stream_name = "AC97 Aux", + .cpu_dai_name = "pxa2xx-ac97-aux", + .codec_dai_name ="wm9713-aux", + .codec_name = "wm9713-codec", + .platform_name = "pxa-pcm-audio", + .ops = &mioa701_ops, + }, +}; + +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 = snd_soc_register_card(&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 int mioa701_wm9713_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 mioa701_wm9713_driver = { + .probe = mioa701_wm9713_probe, + .remove = mioa701_wm9713_remove, + .driver = { + .name = "mioa701-wm9713", + .owner = THIS_MODULE, + .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"); diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c new file mode 100644 index 00000000000..5e8d8133017 --- /dev/null +++ b/sound/soc/pxa/mmp-pcm.c @@ -0,0 +1,257 @@ +/* + * linux/sound/soc/pxa/mmp-pcm.c + * + * Copyright (C) 2011 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. + * + */ +#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> + +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) + +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 ((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 platform_device *pdev = to_platform_device(rtd->platform->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 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; + } + + return; +} + +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 struct snd_soc_platform_driver mmp_soc_platform = { + .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 snd_soc_register_platform(&pdev->dev, &mmp_soc_platform); +} + +static int mmp_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_driver mmp_pcm_driver = { + .driver = { + .name = "mmp-pcm-audio", + .owner = THIS_MODULE, + }, + + .probe = mmp_pcm_probe, + .remove = mmp_pcm_remove, +}; + +module_platform_driver(mmp_pcm_driver); + +MODULE_AUTHOR("Leo Yan <leoy@marvell.com>"); +MODULE_DESCRIPTION("MMP Soc Audio DMA module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c new file mode 100644 index 00000000000..5bf5f1f7cac --- /dev/null +++ b/sound/soc/pxa/mmp-sspa.c @@ -0,0 +1,485 @@ +/* + * linux/sound/soc/pxa/mmp-sspa.c + * Base on pxa2xx-ssp.c + * + * Copyright (C) 2011 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/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); + + return; +} + +/* + * 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_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static 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; + struct resource *res; + + 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_kzalloc(&pdev->dev, + 2 * sizeof(struct snd_dmaengine_dai_dma_data), + GFP_KERNEL); + if (priv->dma_params == NULL) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->sspa->mmio_base = devm_ioremap_resource(&pdev->dev, res); + 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", + .owner = THIS_MODULE, + }, + .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"); diff --git a/sound/soc/pxa/mmp-sspa.h b/sound/soc/pxa/mmp-sspa.h new file mode 100644 index 00000000000..ea365cb9e78 --- /dev/null +++ b/sound/soc/pxa/mmp-sspa.h @@ -0,0 +1,92 @@ +/* + * linux/sound/soc/pxa/mmp-sspa.h + * + * Copyright (C) 2011 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 + * + */ +#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/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 4a9cf3083af..17f9521ff6e 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -17,106 +17,44 @@ #include <linux/moduleparam.h> #include <linux/device.h> #include <linux/gpio.h> -#include <linux/interrupt.h> -#include <linux/irq.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> +#include <sound/jack.h> #include <asm/mach-types.h> #include <mach/audio.h> -#include <mach/palmasoc.h> +#include <linux/platform_data/asoc-palm27x.h> #include "../codecs/wm9712.h" -#include "pxa2xx-pcm.h" #include "pxa2xx-ac97.h" -static int palm27x_jack_func = 1; -static int palm27x_spk_func = 1; -static int palm27x_ep_gpio = -1; +static struct snd_soc_jack hs_jack; -static void palm27x_ext_control(struct snd_soc_codec *codec) -{ - if (!palm27x_spk_func) - snd_soc_dapm_enable_pin(codec, "Speaker"); - else - snd_soc_dapm_disable_pin(codec, "Speaker"); - - if (!palm27x_jack_func) - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - else - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - - snd_soc_dapm_sync(codec); -} - -static int palm27x_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->socdev->codec; - - /* check the jack status at stream startup */ - palm27x_ext_control(codec); - return 0; -} - -static struct snd_soc_ops palm27x_ops = { - .startup = palm27x_startup, +/* Headphones jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, }; -static irqreturn_t palm27x_interrupt(int irq, void *v) -{ - palm27x_spk_func = gpio_get_value(palm27x_ep_gpio); - palm27x_jack_func = !palm27x_spk_func; - return IRQ_HANDLED; -} - -static int palm27x_get_jack(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = palm27x_jack_func; - return 0; -} - -static int palm27x_set_jack(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - - if (palm27x_jack_func == ucontrol->value.integer.value[0]) - return 0; - - palm27x_jack_func = ucontrol->value.integer.value[0]; - palm27x_ext_control(codec); - return 1; -} - -static int palm27x_get_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = palm27x_spk_func; - return 0; -} - -static int palm27x_set_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - - if (palm27x_spk_func == ucontrol->value.integer.value[0]) - return 0; - - palm27x_spk_func = ucontrol->value.integer.value[0]; - palm27x_ext_control(codec); - return 1; -} +/* 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, + }, +}; -/* PalmTX machine dapm widgets */ +/* 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("Speaker", NULL), + SND_SOC_DAPM_SPK("Ext. Speaker", NULL), + SND_SOC_DAPM_MIC("Ext. Microphone", NULL), }; /* PalmTX audio map */ @@ -126,142 +64,120 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Headphone Jack", NULL, "HPOUTR"}, /* ext speaker connected to ROUT2, LOUT2 */ - {"Speaker", NULL, "LOUT2"}, - {"Speaker", NULL, "ROUT2"}, -}; + {"Ext. Speaker", NULL, "LOUT2"}, + {"Ext. Speaker", NULL, "ROUT2"}, -static const char *jack_function[] = {"Headphone", "Off"}; -static const char *spk_function[] = {"On", "Off"}; -static const struct soc_enum palm27x_enum[] = { - SOC_ENUM_SINGLE_EXT(2, jack_function), - SOC_ENUM_SINGLE_EXT(2, spk_function), + /* mic connected to MIC1 */ + {"Ext. Microphone", NULL, "MIC1"}, }; -static const struct snd_kcontrol_new palm27x_controls[] = { - SOC_ENUM_EXT("Jack Function", palm27x_enum[0], palm27x_get_jack, - palm27x_set_jack), - SOC_ENUM_EXT("Speaker Function", palm27x_enum[1], palm27x_get_spk, - palm27x_set_spk), -}; +static struct snd_soc_card palm27x_asoc; -static int palm27x_ac97_init(struct snd_soc_codec *codec) +static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd) { - int i, err; - - snd_soc_dapm_nc_pin(codec, "OUT3"); - snd_soc_dapm_nc_pin(codec, "MONOOUT"); - - /* add palm27x specific controls */ - for (i = 0; i < ARRAY_SIZE(palm27x_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&palm27x_controls[i], - codec, NULL)); - if (err < 0) - return err; - } - - /* add palm27x specific widgets */ - snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets, - ARRAY_SIZE(palm27x_dapm_widgets)); - - /* set up palm27x specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - - snd_soc_dapm_sync(codec); - return 0; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int err; + + /* not connected pins */ + snd_soc_dapm_nc_pin(dapm, "OUT3"); + snd_soc_dapm_nc_pin(dapm, "MONOOUT"); + snd_soc_dapm_nc_pin(dapm, "LINEINL"); + snd_soc_dapm_nc_pin(dapm, "LINEINR"); + snd_soc_dapm_nc_pin(dapm, "PCBEEP"); + snd_soc_dapm_nc_pin(dapm, "PHONE"); + snd_soc_dapm_nc_pin(dapm, "MIC2"); + + /* Jack detection API stuff */ + err = snd_soc_jack_new(codec, "Headphone Jack", + SND_JACK_HEADPHONE, &hs_jack); + if (err) + return err; + + err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), + 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; } static struct snd_soc_dai_link palm27x_dai[] = { { .name = "AC97 HiFi", .stream_name = "AC97 HiFi", - .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], - .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], + .cpu_dai_name = "pxa2xx-ac97", + .codec_dai_name = "wm9712-hifi", + .codec_name = "wm9712-codec", + .platform_name = "pxa-pcm-audio", .init = palm27x_ac97_init, - .ops = &palm27x_ops, }, { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], - .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], - .ops = &palm27x_ops, + .cpu_dai_name = "pxa2xx-ac97-aux", + .codec_dai_name = "wm9712-aux", + .codec_name = "wm9712-codec", + .platform_name = "pxa-pcm-audio", }, }; static struct snd_soc_card palm27x_asoc = { .name = "Palm/PXA27x", - .platform = &pxa2xx_soc_platform, + .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) }; -static struct snd_soc_device palm27x_snd_devdata = { - .card = &palm27x_asoc, - .codec_dev = &soc_codec_dev_wm9712, -}; - -static struct platform_device *palm27x_snd_device; - -static int __init palm27x_asoc_init(void) +static int palm27x_asoc_probe(struct platform_device *pdev) { int ret; if (!(machine_is_palmtx() || machine_is_palmt5() || - machine_is_palmld())) + machine_is_palmld() || machine_is_palmte2())) return -ENODEV; - ret = gpio_request(palm27x_ep_gpio, "Headphone Jack"); - if (ret) - return ret; - ret = gpio_direction_input(palm27x_ep_gpio); - if (ret) - goto err_alloc; - - if (request_irq(gpio_to_irq(palm27x_ep_gpio), palm27x_interrupt, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "Headphone jack", NULL)) - goto err_alloc; - - palm27x_snd_device = platform_device_alloc("soc-audio", -1); - if (!palm27x_snd_device) { - ret = -ENOMEM; - goto err_dev; + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "please supply platform_data\n"); + return -ENODEV; } - platform_set_drvdata(palm27x_snd_device, &palm27x_snd_devdata); - palm27x_snd_devdata.dev = &palm27x_snd_device->dev; - ret = platform_device_add(palm27x_snd_device); - - if (ret != 0) - goto put_device; + hs_jack_gpios[0].gpio = ((struct palm27x_asoc_info *) + (pdev->dev.platform_data))->jack_gpio; - return 0; - -put_device: - platform_device_put(palm27x_snd_device); -err_dev: - free_irq(gpio_to_irq(palm27x_ep_gpio), NULL); -err_alloc: - gpio_free(palm27x_ep_gpio); + palm27x_asoc.dev = &pdev->dev; + ret = snd_soc_register_card(&palm27x_asoc); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); return ret; } -static void __exit palm27x_asoc_exit(void) +static int palm27x_asoc_remove(struct platform_device *pdev) { - free_irq(gpio_to_irq(palm27x_ep_gpio), NULL); - gpio_free(palm27x_ep_gpio); - platform_device_unregister(palm27x_snd_device); + snd_soc_unregister_card(&palm27x_asoc); + return 0; } -void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data) -{ - palm27x_ep_gpio = data->jack_gpio; -} +static struct platform_driver palm27x_wm9712_driver = { + .probe = palm27x_asoc_probe, + .remove = palm27x_asoc_remove, + .driver = { + .name = "palm27x-asoc", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, +}; -module_init(palm27x_asoc_init); -module_exit(palm27x_asoc_exit); +module_platform_driver(palm27x_wm9712_driver); /* Module information */ MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 6e9827189ff..21f34006531 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -17,22 +17,19 @@ #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 <sound/soc-dapm.h> #include <asm/mach-types.h> #include <asm/hardware/locomo.h> -#include <mach/pxa-regs.h> -#include <mach/hardware.h> #include <mach/poodle.h> #include <mach/audio.h> #include "../codecs/wm8731.h" -#include "pxa2xx-pcm.h" #include "pxa2xx-i2s.h" #define POODLE_HP 1 @@ -46,7 +43,7 @@ static int poodle_jack_func; static int poodle_spk_func; -static void poodle_ext_control(struct snd_soc_codec *codec) +static void poodle_ext_control(struct snd_soc_dapm_context *dapm) { /* set up jack connection */ if (poodle_jack_func == POODLE_HP) { @@ -55,32 +52,32 @@ static void poodle_ext_control(struct snd_soc_codec *codec) POODLE_LOCOMO_GPIO_MUTE_L, 1); locomo_gpio_write(&poodle_locomo_device.dev, POODLE_LOCOMO_GPIO_MUTE_R, 1); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + 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(codec, "Headphone Jack"); + 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(codec, "Ext Spk"); + snd_soc_dapm_enable_pin(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(codec, "Ext Spk"); + snd_soc_dapm_disable_pin(dapm, "Ext Spk"); /* signal a DAPM event */ - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); } static int poodle_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->socdev->codec; /* check the jack status at stream startup */ - poodle_ext_control(codec); + poodle_ext_control(&rtd->card->dapm); + return 0; } @@ -98,8 +95,8 @@ 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->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + 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; @@ -117,20 +114,8 @@ static int poodle_hw_params(struct snd_pcm_substream *substream, break; } - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - 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_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk, + ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk, SND_SOC_CLOCK_IN); if (ret < 0) return ret; @@ -160,13 +145,13 @@ static int poodle_get_jack(struct snd_kcontrol *kcontrol, static int poodle_set_jack(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); if (poodle_jack_func == ucontrol->value.integer.value[0]) return 0; poodle_jack_func = ucontrol->value.integer.value[0]; - poodle_ext_control(codec); + poodle_ext_control(&card->dapm); return 1; } @@ -180,13 +165,13 @@ static int poodle_get_spk(struct snd_kcontrol *kcontrol, static int poodle_set_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); if (poodle_spk_func == ucontrol->value.integer.value[0]) return 0; poodle_spk_func = ucontrol->value.integer.value[0]; - poodle_ext_control(codec); + poodle_ext_control(&card->dapm); return 1; } @@ -210,7 +195,7 @@ SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event), }; /* Corgi machine connections to the codec pins */ -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route poodle_audio_map[] = { /* headphone connected to LHPOUT1, RHPOUT1 */ {"Headphone Jack", NULL, "LHPOUT"}, @@ -238,30 +223,14 @@ static const struct snd_kcontrol_new wm8731_poodle_controls[] = { /* * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device */ -static int poodle_wm8731_init(struct snd_soc_codec *codec) +static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd) { - int i, err; - - snd_soc_dapm_nc_pin(codec, "LLINEIN"); - snd_soc_dapm_nc_pin(codec, "RLINEIN"); - snd_soc_dapm_enable_pin(codec, "MICIN"); - - /* Add poodle specific controls */ - for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8731_poodle_controls[i], codec, NULL)); - if (err < 0) - return err; - } - - /* Add poodle specific widgets */ - snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, - ARRAY_SIZE(wm8731_dapm_widgets)); + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; - /* Set up poodle specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_nc_pin(dapm, "LLINEIN"); + snd_soc_dapm_nc_pin(dapm, "RLINEIN"); - snd_soc_dapm_sync(codec); return 0; } @@ -269,42 +238,36 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec) static struct snd_soc_dai_link poodle_dai = { .name = "WM8731", .stream_name = "WM8731", - .cpu_dai = &pxa_i2s_dai, - .codec_dai = &wm8731_dai, + .cpu_dai_name = "pxa2xx-i2s", + .codec_dai_name = "wm8731-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm8731.0-001b", .init = poodle_wm8731_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, .ops = &poodle_ops, }; /* poodle audio machine driver */ -static struct snd_soc_card snd_soc_poodle = { +static struct snd_soc_card poodle = { .name = "Poodle", - .platform = &pxa2xx_soc_platform, .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), }; -/* poodle audio private data */ -static struct wm8731_setup_data poodle_wm8731_setup = { - .i2c_bus = 0, - .i2c_address = 0x1b, -}; - -/* poodle audio subsystem */ -static struct snd_soc_device poodle_snd_devdata = { - .card = &snd_soc_poodle, - .codec_dev = &soc_codec_dev_wm8731, - .codec_data = &poodle_wm8731_setup, -}; - -static struct platform_device *poodle_snd_device; - -static int __init poodle_init(void) +static int poodle_probe(struct platform_device *pdev) { + struct snd_soc_card *card = &poodle; int ret; - if (!machine_is_poodle()) - return -ENODEV; - locomo_gpio_set_dir(&poodle_locomo_device.dev, POODLE_LOCOMO_GPIO_AMP_ON, 0); /* should we mute HP at startup - burning power ?*/ @@ -313,29 +276,37 @@ static int __init poodle_init(void) locomo_gpio_set_dir(&poodle_locomo_device.dev, POODLE_LOCOMO_GPIO_MUTE_R, 0); - poodle_snd_device = platform_device_alloc("soc-audio", -1); - if (!poodle_snd_device) - return -ENOMEM; - - platform_set_drvdata(poodle_snd_device, &poodle_snd_devdata); - poodle_snd_devdata.dev = &poodle_snd_device->dev; - ret = platform_device_add(poodle_snd_device); + card->dev = &pdev->dev; + ret = snd_soc_register_card(card); if (ret) - platform_device_put(poodle_snd_device); - + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); return ret; } -static void __exit poodle_exit(void) +static int poodle_remove(struct platform_device *pdev) { - platform_device_unregister(poodle_snd_device); + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + return 0; } -module_init(poodle_init); -module_exit(poodle_exit); +static struct platform_driver poodle_driver = { + .driver = { + .name = "poodle-audio", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = poodle_probe, + .remove = poodle_remove, +}; + +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/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 73cb6b4c2f2..199a8b37755 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -1,4 +1,3 @@ -#define DEBUG /* * pxa-ssp.c -- ALSA Soc Audio Layer * @@ -17,9 +16,15 @@ #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 <asm/irq.h> #include <sound/core.h> #include <sound/pcm.h> @@ -27,245 +32,140 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> -#include <mach/hardware.h> -#include <mach/pxa-regs.h> -#include <mach/regs-ssp.h> -#include <mach/audio.h> -#include <mach/ssp.h> - -#include "pxa2xx-pcm.h" +#include "../../arm/pxa2xx-pcm.h" #include "pxa-ssp.h" /* * SSP audio private data */ struct ssp_priv { - struct ssp_dev dev; + struct ssp_device *ssp; unsigned int sysclk; int dai_fmt; #ifdef CONFIG_PM - struct ssp_state state; + uint32_t cr0; + uint32_t cr1; + uint32_t to; + uint32_t psp; #endif }; -#define PXA2xx_SSP1_BASE 0x41000000 -#define PXA27x_SSP2_BASE 0x41700000 -#define PXA27x_SSP3_BASE 0x41900000 -#define PXA3xx_SSP4_BASE 0x41a00000 - -static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_out = { - .name = "SSP1 PCM Mono out", - .dev_addr = PXA2xx_SSP1_BASE + SSDR, - .drcmr = &DRCMR(14), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH2, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_in = { - .name = "SSP1 PCM Mono in", - .dev_addr = PXA2xx_SSP1_BASE + SSDR, - .drcmr = &DRCMR(13), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_out = { - .name = "SSP1 PCM Stereo out", - .dev_addr = PXA2xx_SSP1_BASE + SSDR, - .drcmr = &DRCMR(14), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH4, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_in = { - .name = "SSP1 PCM Stereo in", - .dev_addr = PXA2xx_SSP1_BASE + SSDR, - .drcmr = &DRCMR(13), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH4, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_out = { - .name = "SSP2 PCM Mono out", - .dev_addr = PXA27x_SSP2_BASE + SSDR, - .drcmr = &DRCMR(16), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH2, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_in = { - .name = "SSP2 PCM Mono in", - .dev_addr = PXA27x_SSP2_BASE + SSDR, - .drcmr = &DRCMR(15), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_out = { - .name = "SSP2 PCM Stereo out", - .dev_addr = PXA27x_SSP2_BASE + SSDR, - .drcmr = &DRCMR(16), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH4, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_in = { - .name = "SSP2 PCM Stereo in", - .dev_addr = PXA27x_SSP2_BASE + SSDR, - .drcmr = &DRCMR(15), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH4, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_out = { - .name = "SSP3 PCM Mono out", - .dev_addr = PXA27x_SSP3_BASE + SSDR, - .drcmr = &DRCMR(67), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH2, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_in = { - .name = "SSP3 PCM Mono in", - .dev_addr = PXA27x_SSP3_BASE + SSDR, - .drcmr = &DRCMR(66), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_out = { - .name = "SSP3 PCM Stereo out", - .dev_addr = PXA27x_SSP3_BASE + SSDR, - .drcmr = &DRCMR(67), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH4, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_in = { - .name = "SSP3 PCM Stereo in", - .dev_addr = PXA27x_SSP3_BASE + SSDR, - .drcmr = &DRCMR(66), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH4, -}; - -static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_out = { - .name = "SSP4 PCM Mono out", - .dev_addr = PXA3xx_SSP4_BASE + SSDR, - .drcmr = &DRCMR(67), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH2, -}; +static void dump_registers(struct ssp_device *ssp) +{ + dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n", + pxa_ssp_read_reg(ssp, SSCR0), pxa_ssp_read_reg(ssp, SSCR1), + pxa_ssp_read_reg(ssp, SSTO)); -static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_in = { - .name = "SSP4 PCM Mono in", - .dev_addr = PXA3xx_SSP4_BASE + SSDR, - .drcmr = &DRCMR(66), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, -}; + dev_dbg(&ssp->pdev->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n", + pxa_ssp_read_reg(ssp, SSPSP), pxa_ssp_read_reg(ssp, SSSR), + pxa_ssp_read_reg(ssp, SSACD)); +} -static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_out = { - .name = "SSP4 PCM Stereo out", - .dev_addr = PXA3xx_SSP4_BASE + SSDR, - .drcmr = &DRCMR(67), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH4, -}; +static void pxa_ssp_enable(struct ssp_device *ssp) +{ + uint32_t sscr0; -static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_in = { - .name = "SSP4 PCM Stereo in", - .dev_addr = PXA3xx_SSP4_BASE + SSDR, - .drcmr = &DRCMR(66), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH4, -}; + sscr0 = __raw_readl(ssp->mmio_base + SSCR0) | SSCR0_SSE; + __raw_writel(sscr0, ssp->mmio_base + SSCR0); +} -static void dump_registers(struct ssp_device *ssp) +static void pxa_ssp_disable(struct ssp_device *ssp) { - dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n", - ssp_read_reg(ssp, SSCR0), ssp_read_reg(ssp, SSCR1), - ssp_read_reg(ssp, SSTO)); + uint32_t sscr0; - dev_dbg(&ssp->pdev->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n", - ssp_read_reg(ssp, SSPSP), ssp_read_reg(ssp, SSSR), - ssp_read_reg(ssp, SSACD)); + sscr0 = __raw_readl(ssp->mmio_base + SSCR0) & ~SSCR0_SSE; + __raw_writel(sscr0, ssp->mmio_base + SSCR0); } -static struct pxa2xx_pcm_dma_params *ssp_dma_params[4][4] = { - { - &pxa_ssp1_pcm_mono_out, &pxa_ssp1_pcm_mono_in, - &pxa_ssp1_pcm_stereo_out, &pxa_ssp1_pcm_stereo_in, - }, - { - &pxa_ssp2_pcm_mono_out, &pxa_ssp2_pcm_mono_in, - &pxa_ssp2_pcm_stereo_out, &pxa_ssp2_pcm_stereo_in, - }, - { - &pxa_ssp3_pcm_mono_out, &pxa_ssp3_pcm_mono_in, - &pxa_ssp3_pcm_stereo_out, &pxa_ssp3_pcm_stereo_in, - }, - { - &pxa_ssp4_pcm_mono_out, &pxa_ssp4_pcm_mono_in, - &pxa_ssp4_pcm_stereo_out, &pxa_ssp4_pcm_stereo_in, - }, -}; +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; + dma->maxburst = 16; + dma->addr = ssp->phys_base + SSDR; +} static int pxa_ssp_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *cpu_dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - struct ssp_priv *priv = cpu_dai->private_data; + 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; if (!cpu_dai->active) { - ret = ssp_init(&priv->dev, cpu_dai->id + 1, SSP_NO_IRQ); - if (ret < 0) - return ret; - ssp_disable(&priv->dev); + clk_enable(ssp->clk); + pxa_ssp_disable(ssp); } + + dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + dma->filter_data = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + &ssp->drcmr_tx : &ssp->drcmr_rx; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma); + return ret; } static void pxa_ssp_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *cpu_dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - struct ssp_priv *priv = cpu_dai->private_data; + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_device *ssp = priv->ssp; if (!cpu_dai->active) { - ssp_disable(&priv->dev); - ssp_exit(&priv->dev); + pxa_ssp_disable(ssp); + clk_disable(ssp->clk); } + + 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) { - struct ssp_priv *priv = cpu_dai->private_data; + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_device *ssp = priv->ssp; if (!cpu_dai->active) - return 0; + clk_enable(ssp->clk); - ssp_save_state(&priv->dev, &priv->state); - clk_disable(priv->dev.ssp->clk); + priv->cr0 = __raw_readl(ssp->mmio_base + SSCR0); + priv->cr1 = __raw_readl(ssp->mmio_base + SSCR1); + priv->to = __raw_readl(ssp->mmio_base + SSTO); + priv->psp = __raw_readl(ssp->mmio_base + SSPSP); + + pxa_ssp_disable(ssp); + clk_disable(ssp->clk); return 0; } static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai) { - struct ssp_priv *priv = cpu_dai->private_data; + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_device *ssp = priv->ssp; + uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE; - if (!cpu_dai->active) - return 0; + clk_enable(ssp->clk); + + __raw_writel(sssr, ssp->mmio_base + SSSR); + __raw_writel(priv->cr0 & ~SSCR0_SSE, ssp->mmio_base + SSCR0); + __raw_writel(priv->cr1, ssp->mmio_base + SSCR1); + __raw_writel(priv->to, ssp->mmio_base + SSTO); + __raw_writel(priv->psp, ssp->mmio_base + SSPSP); - clk_enable(priv->dev.ssp->clk); - ssp_restore_state(&priv->dev, &priv->state); - ssp_enable(&priv->dev); + if (cpu_dai->active) + pxa_ssp_enable(ssp); + else + clk_disable(ssp->clk); return 0; } @@ -279,12 +179,33 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai) * ssp_set_clkdiv - set SSP clock divider * @div: serial clock rate divider */ -static void ssp_set_scr(struct ssp_dev *dev, u32 div) +static void pxa_ssp_set_scr(struct ssp_device *ssp, u32 div) +{ + u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); + + if (ssp->type == PXA25x_SSP) { + sscr0 &= ~0x0000ff00; + sscr0 |= ((div - 2)/2) << 8; /* 2..512 */ + } else { + sscr0 &= ~0x000fff00; + sscr0 |= (div - 1) << 8; /* 1..4096 */ + } + pxa_ssp_write_reg(ssp, SSCR0, sscr0); +} + +/** + * pxa_ssp_get_clkdiv - get SSP clock divider + */ +static u32 pxa_ssp_get_scr(struct ssp_device *ssp) { - struct ssp_device *ssp = dev->ssp; - u32 sscr0 = ssp_read_reg(dev->ssp, SSCR0) & ~SSCR0_SCR; + u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); + u32 div; - ssp_write_reg(ssp, SSCR0, (sscr0 | SSCR0_SerClkDiv(div))); + if (ssp->type == PXA25x_SSP) + div = ((sscr0 >> 8) & 0xff) * 2 + 2; + else + div = ((sscr0 >> 8) & 0xfff) + 1; + return div; } /* @@ -293,15 +214,15 @@ static void ssp_set_scr(struct ssp_dev *dev, u32 div) static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { - struct ssp_priv *priv = cpu_dai->private_data; - struct ssp_device *ssp = priv->dev.ssp; + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_device *ssp = priv->ssp; int val; - u32 sscr0 = ssp_read_reg(ssp, SSCR0) & - ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC); + u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & + ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); dev_dbg(&ssp->pdev->dev, - "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n", + "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n", cpu_dai->id, clk_id, freq); switch (clk_id) { @@ -310,7 +231,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, break; case PXA_SSP_CLK_PLL: /* Internal PLL is fixed */ - if (cpu_is_pxa25x()) + if (ssp->type == PXA25x_SSP) priv->sysclk = 1843200; else priv->sysclk = 13000000; @@ -325,8 +246,8 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, break; case PXA_SSP_CLK_AUDIO: priv->sysclk = 0; - ssp_set_scr(&priv->dev, 1); - sscr0 |= SSCR0_ADC; + pxa_ssp_set_scr(ssp, 1); + sscr0 |= SSCR0_ACS; break; default: return -ENODEV; @@ -334,12 +255,12 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, /* The SSP clock must be disabled when changing SSP clock mode * on PXA2xx. On PXA3xx it must be enabled when doing so. */ - if (!cpu_is_pxa3xx()) - clk_disable(priv->dev.ssp->clk); - val = ssp_read_reg(ssp, SSCR0) | sscr0; - ssp_write_reg(ssp, SSCR0, val); - if (!cpu_is_pxa3xx()) - clk_enable(priv->dev.ssp->clk); + if (ssp->type != PXA3xx_SSP) + clk_disable(ssp->clk); + val = pxa_ssp_read_reg(ssp, SSCR0) | sscr0; + pxa_ssp_write_reg(ssp, SSCR0, val); + if (ssp->type != PXA3xx_SSP) + clk_enable(ssp->clk); return 0; } @@ -350,43 +271,39 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div) { - struct ssp_priv *priv = cpu_dai->private_data; - struct ssp_device *ssp = priv->dev.ssp; + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_device *ssp = priv->ssp; int val; switch (div_id) { case PXA_SSP_AUDIO_DIV_ACDS: - val = (ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div); - ssp_write_reg(ssp, SSACD, val); + val = (pxa_ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div); + pxa_ssp_write_reg(ssp, SSACD, val); break; case PXA_SSP_AUDIO_DIV_SCDB: - val = ssp_read_reg(ssp, SSACD); + val = pxa_ssp_read_reg(ssp, SSACD); val &= ~SSACD_SCDB; -#if defined(CONFIG_PXA3xx) - if (cpu_is_pxa3xx()) + if (ssp->type == PXA3xx_SSP) val &= ~SSACD_SCDX8; -#endif switch (div) { case PXA_SSP_CLK_SCDB_1: val |= SSACD_SCDB; break; case PXA_SSP_CLK_SCDB_4: break; -#if defined(CONFIG_PXA3xx) case PXA_SSP_CLK_SCDB_8: - if (cpu_is_pxa3xx()) + if (ssp->type == PXA3xx_SSP) val |= SSACD_SCDX8; else return -EINVAL; break; -#endif default: return -EINVAL; } - ssp_write_reg(ssp, SSACD, val); + pxa_ssp_write_reg(ssp, SSACD, val); break; case PXA_SSP_DIV_SCR: - ssp_set_scr(&priv->dev, div); + pxa_ssp_set_scr(ssp, div); break; default: return -ENODEV; @@ -398,17 +315,15 @@ static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, /* * 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, unsigned int freq_in, unsigned int freq_out) +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) { - struct ssp_priv *priv = cpu_dai->private_data; - struct ssp_device *ssp = priv->dev.ssp; - u32 ssacd = ssp_read_reg(ssp, SSACD) & ~0x70; + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_device *ssp = priv->ssp; + u32 ssacd = pxa_ssp_read_reg(ssp, SSACD) & ~0x70; -#if defined(CONFIG_PXA3xx) - if (cpu_is_pxa3xx()) - ssp_write_reg(ssp, SSACDD, 0); -#endif + if (ssp->type == PXA3xx_SSP) + pxa_ssp_write_reg(ssp, SSACDD, 0); switch (freq_out) { case 5622000: @@ -433,33 +348,31 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, break; default: -#ifdef CONFIG_PXA3xx /* PXA3xx has a clock ditherer which can be used to generate * a wider range of frequencies - calculate a value for it. */ - if (cpu_is_pxa3xx()) { + if (ssp->type == PXA3xx_SSP) { u32 val; u64 tmp = 19968; tmp *= 1000000; do_div(tmp, freq_out); val = tmp; - val = (val << 16) | 64;; - ssp_write_reg(ssp, SSACDD, val); + val = (val << 16) | 64; + pxa_ssp_write_reg(ssp, SSACDD, val); ssacd |= (0x6 << 4); dev_dbg(&ssp->pdev->dev, - "Using SSACDD %x to supply %dHz\n", + "Using SSACDD %x to supply %uHz\n", val, freq_out); break; } -#endif return -EINVAL; } - ssp_write_reg(ssp, SSACD, ssacd); + pxa_ssp_write_reg(ssp, SSACD, ssacd); return 0; } @@ -468,21 +381,34 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, * Set the active slots in TDM/Network mode */ static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, - unsigned int mask, int slots) + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { - struct ssp_priv *priv = cpu_dai->private_data; - struct ssp_device *ssp = priv->dev.ssp; + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_device *ssp = priv->ssp; u32 sscr0; - sscr0 = ssp_read_reg(ssp, SSCR0) & ~SSCR0_SlotsPerFrm(7); + sscr0 = pxa_ssp_read_reg(ssp, SSCR0); + sscr0 &= ~(SSCR0_MOD | SSCR0_SlotsPerFrm(8) | SSCR0_EDSS | SSCR0_DSS); - /* set number of active slots */ - sscr0 |= SSCR0_SlotsPerFrm(slots); - ssp_write_reg(ssp, SSCR0, sscr0); + /* set slot width */ + if (slot_width > 16) + sscr0 |= SSCR0_EDSS | SSCR0_DataSize(slot_width - 16); + else + sscr0 |= SSCR0_DataSize(slot_width); + + if (slots > 1) { + /* enable network mode */ + sscr0 |= SSCR0_MOD; + + /* set number of active slots */ + sscr0 |= SSCR0_SlotsPerFrm(slots); + + /* set active slot mask */ + pxa_ssp_write_reg(ssp, SSTSA, tx_mask); + pxa_ssp_write_reg(ssp, SSRSA, rx_mask); + } + pxa_ssp_write_reg(ssp, SSCR0, sscr0); - /* set active slot mask */ - ssp_write_reg(ssp, SSTSA, mask); - ssp_write_reg(ssp, SSRSA, mask); return 0; } @@ -492,16 +418,16 @@ static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai, int tristate) { - struct ssp_priv *priv = cpu_dai->private_data; - struct ssp_device *ssp = priv->dev.ssp; + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_device *ssp = priv->ssp; u32 sscr1; - sscr1 = ssp_read_reg(ssp, SSCR1); + sscr1 = pxa_ssp_read_reg(ssp, SSCR1); if (tristate) sscr1 &= ~SSCR1_TTE; else sscr1 |= SSCR1_TTE; - ssp_write_reg(ssp, SSCR1, sscr1); + pxa_ssp_write_reg(ssp, SSCR1, sscr1); return 0; } @@ -514,24 +440,33 @@ static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai, static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - struct ssp_priv *priv = cpu_dai->private_data; - struct ssp_device *ssp = priv->dev.ssp; - u32 sscr0; - u32 sscr1; - u32 sspsp; + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_device *ssp = priv->ssp; + u32 sscr0, sscr1, sspsp, scfr; + + /* 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 (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { + dev_err(&ssp->pdev->dev, + "can't change hardware dai format: stream is in use"); + return -EINVAL; + } /* reset port settings */ - sscr0 = ssp_read_reg(ssp, SSCR0) & - (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC); + sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & + ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7); sspsp = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR; + sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR; break; case SND_SOC_DAIFMT_CBM_CFS: - sscr1 |= SSCR1_SCLKDIR; + sscr1 |= SSCR1_SCLKDIR | SSCR1_SCFR; break; case SND_SOC_DAIFMT_CBS_CFS: break; @@ -539,28 +474,27 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, return -EINVAL; } - ssp_write_reg(ssp, SSCR0, sscr0); - ssp_write_reg(ssp, SSCR1, sscr1); - ssp_write_reg(ssp, SSPSP, sspsp); + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + sspsp |= SSPSP_SFRMP; + break; + case SND_SOC_DAIFMT_NB_IF: + break; + case SND_SOC_DAIFMT_IB_IF: + sspsp |= SSPSP_SCMODE(2); + break; + case SND_SOC_DAIFMT_IB_NF: + sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP; + break; + default: + return -EINVAL; + } switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - sscr0 |= SSCR0_MOD | SSCR0_PSP; + sscr0 |= SSCR0_PSP; sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - sspsp |= SSPSP_FSRT; - break; - case SND_SOC_DAIFMT_NB_IF: - sspsp |= SSPSP_SFRMP | SSPSP_FSRT; - break; - case SND_SOC_DAIFMT_IB_IF: - sspsp |= SSPSP_SFRMP; - break; - default: - return -EINVAL; - } + /* See hw_params() */ break; case SND_SOC_DAIFMT_DSP_A: @@ -568,25 +502,26 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, case SND_SOC_DAIFMT_DSP_B: sscr0 |= SSCR0_MOD | SSCR0_PSP; sscr1 |= SSCR1_TRAIL | SSCR1_RWOT; - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - sspsp |= SSPSP_SFRMP; - break; - case SND_SOC_DAIFMT_IB_IF: - break; - default: - return -EINVAL; - } break; default: return -EINVAL; } - ssp_write_reg(ssp, SSCR0, sscr0); - ssp_write_reg(ssp, SSCR1, sscr1); - ssp_write_reg(ssp, SSPSP, sspsp); + pxa_ssp_write_reg(ssp, SSCR0, sscr0); + pxa_ssp_write_reg(ssp, SSCR1, sscr1); + pxa_ssp_write_reg(ssp, SSPSP, sspsp); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + scfr = pxa_ssp_read_reg(ssp, SSCR1) | SSCR1_SCFR; + pxa_ssp_write_reg(ssp, SSCR1, scfr); + + while (pxa_ssp_read_reg(ssp, SSSR) & SSSR_BSY) + cpu_relax(); + break; + } dump_registers(ssp); @@ -605,71 +540,93 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, */ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_soc_dai *cpu_dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - struct ssp_priv *priv = cpu_dai->private_data; - struct ssp_device *ssp = priv->dev.ssp; - int dma = 0, chn = params_channels(params); + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + struct ssp_device *ssp = priv->ssp; + int chn = params_channels(params); u32 sscr0; u32 sspsp; int width = snd_pcm_format_physical_width(params_format(params)); + int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; + struct snd_dmaengine_dai_dma_data *dma_data; - /* select correct DMA params */ - if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) - dma = 1; /* capture DMA offset is 1,3 */ - if (chn == 2) - dma += 2; /* stereo DMA offset is 2, mono is 0 */ - cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma]; + dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); - dev_dbg(&ssp->pdev->dev, "pxa_ssp_hw_params: dma %d\n", dma); + /* 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 (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) + if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) return 0; /* clear selected SSP bits */ - sscr0 = ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS); - ssp_write_reg(ssp, SSCR0, sscr0); + sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS); /* bit size */ - sscr0 = ssp_read_reg(ssp, SSCR0); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: -#ifdef CONFIG_PXA3xx - if (cpu_is_pxa3xx()) + if (ssp->type == PXA3xx_SSP) sscr0 |= SSCR0_FPCKE; -#endif sscr0 |= SSCR0_DataSize(16); - if (params_channels(params) > 1) - sscr0 |= SSCR0_EDSS; break; case SNDRV_PCM_FORMAT_S24_LE: sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8)); - /* we must be in network mode (2 slots) for 24 bit stereo */ break; case SNDRV_PCM_FORMAT_S32_LE: sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16)); - /* we must be in network mode (2 slots) for 32 bit stereo */ break; } - ssp_write_reg(ssp, SSCR0, sscr0); + pxa_ssp_write_reg(ssp, SSCR0, sscr0); switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - /* Cleared when the DAI format is set */ - sspsp = ssp_read_reg(ssp, SSPSP) | SSPSP_SFRMWDTH(width); - ssp_write_reg(ssp, SSPSP, sspsp); + sspsp = pxa_ssp_read_reg(ssp, SSPSP); + + if ((pxa_ssp_get_scr(ssp) == 4) && (width == 16)) { + /* This is a special case where the bitclk is 64fs + * and we're not dealing with 2*32 bits of audio + * samples. + * + * The SSP values used for that are all found out by + * trying and failing a lot; some of the registers + * needed for that mode are only available on PXA3xx. + */ + if (ssp->type != PXA3xx_SSP) + return -EINVAL; + + sspsp |= SSPSP_SFRMWDTH(width * 2); + sspsp |= SSPSP_SFRMDLY(width * 4); + sspsp |= SSPSP_EDMYSTOP(3); + sspsp |= SSPSP_DMYSTOP(3); + sspsp |= SSPSP_DMYSTRT(1); + } else { + /* 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. + */ + sspsp |= SSPSP_SFRMWDTH(width + 1); + sspsp |= SSPSP_SFRMDLY((width + 1) * 2); + sspsp |= SSPSP_DMYSTRT(1); + } + + pxa_ssp_write_reg(ssp, SSPSP, sspsp); break; default: break; } - /* We always use a network mode so we always require TDM slots + /* When we use a network mode, we always require TDM slots * - complain loudly and fail if they've not been set up yet. */ - if (!(ssp_read_reg(ssp, SSTSA) & 0xf)) { + if ((sscr0 & SSCR0_MOD) && !ttsa) { dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n"); return -EINVAL; } @@ -679,57 +636,66 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, return 0; } +static void pxa_ssp_set_running_bit(struct snd_pcm_substream *substream, + struct ssp_device *ssp, int value) +{ + uint32_t sscr0 = pxa_ssp_read_reg(ssp, SSCR0); + uint32_t sscr1 = pxa_ssp_read_reg(ssp, SSCR1); + uint32_t sspsp = pxa_ssp_read_reg(ssp, SSPSP); + uint32_t sssr = pxa_ssp_read_reg(ssp, SSSR); + + if (value && (sscr0 & SSCR0_SSE)) + pxa_ssp_write_reg(ssp, SSCR0, sscr0 & ~SSCR0_SSE); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (value) + sscr1 |= SSCR1_TSRE; + else + sscr1 &= ~SSCR1_TSRE; + } else { + if (value) + sscr1 |= SSCR1_RSRE; + else + sscr1 &= ~SSCR1_RSRE; + } + + pxa_ssp_write_reg(ssp, SSCR1, sscr1); + + if (value) { + pxa_ssp_write_reg(ssp, SSSR, sssr); + pxa_ssp_write_reg(ssp, SSPSP, sspsp); + pxa_ssp_write_reg(ssp, SSCR0, sscr0 | SSCR0_SSE); + } +} + static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) + struct snd_soc_dai *cpu_dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; int ret = 0; - struct ssp_priv *priv = cpu_dai->private_data; - struct ssp_device *ssp = priv->dev.ssp; + 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: - ssp_enable(&priv->dev); + pxa_ssp_enable(ssp); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - val = ssp_read_reg(ssp, SSCR1); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - val |= SSCR1_TSRE; - else - val |= SSCR1_RSRE; - ssp_write_reg(ssp, SSCR1, val); - val = ssp_read_reg(ssp, SSSR); - ssp_write_reg(ssp, SSSR, val); + pxa_ssp_set_running_bit(substream, ssp, 1); + val = pxa_ssp_read_reg(ssp, SSSR); + pxa_ssp_write_reg(ssp, SSSR, val); break; case SNDRV_PCM_TRIGGER_START: - val = ssp_read_reg(ssp, SSCR1); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - val |= SSCR1_TSRE; - else - val |= SSCR1_RSRE; - ssp_write_reg(ssp, SSCR1, val); - ssp_enable(&priv->dev); + pxa_ssp_set_running_bit(substream, ssp, 1); break; case SNDRV_PCM_TRIGGER_STOP: - val = ssp_read_reg(ssp, SSCR1); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - val &= ~SSCR1_TSRE; - else - val &= ~SSCR1_RSRE; - ssp_write_reg(ssp, SSCR1, val); + pxa_ssp_set_running_bit(substream, ssp, 0); break; case SNDRV_PCM_TRIGGER_SUSPEND: - ssp_disable(&priv->dev); + pxa_ssp_disable(ssp); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - val = ssp_read_reg(ssp, SSCR1); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - val &= ~SSCR1_TSRE; - else - val &= ~SSCR1_RSRE; - ssp_write_reg(ssp, SSCR1, val); + pxa_ssp_set_running_bit(substream, ssp, 0); break; default: @@ -741,9 +707,9 @@ static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } -static int pxa_ssp_probe(struct platform_device *pdev, - struct snd_soc_dai *dai) +static int pxa_ssp_probe(struct snd_soc_dai *dai) { + struct device *dev = dai->dev; struct ssp_priv *priv; int ret; @@ -751,13 +717,30 @@ static int pxa_ssp_probe(struct platform_device *pdev, if (!priv) return -ENOMEM; - priv->dev.ssp = ssp_request(dai->id, "SoC audio"); - if (priv->dev.ssp == NULL) { - ret = -ENODEV; - goto err_priv; + 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"); + return -ENODEV; + } + + priv->ssp = pxa_ssp_request_of(ssp_handle, "SoC audio"); + if (priv->ssp == NULL) { + ret = -ENODEV; + goto err_priv; + } + } else { + priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); + if (priv->ssp == NULL) { + ret = -ENODEV; + goto err_priv; + } } - dai->private_data = priv; + priv->dai_fmt = (unsigned int) -1; + snd_soc_dai_set_drvdata(dai, priv); return 0; @@ -766,164 +749,93 @@ err_priv: return ret; } -static void pxa_ssp_remove(struct platform_device *pdev, - struct snd_soc_dai *dai) +static int pxa_ssp_remove(struct snd_soc_dai *dai) { - struct ssp_priv *priv = dai->private_data; - ssp_free(priv->dev.ssp); + struct ssp_priv *priv = snd_soc_dai_get_drvdata(dai); + + pxa_ssp_free(priv->ssp); + kfree(priv); + 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_44100 | SNDRV_PCM_RATE_48000 | \ + 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) -struct snd_soc_dai pxa_ssp_dai[] = { - { - .name = "pxa2xx-ssp1", - .id = 0, +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 = 2, + .channels_max = 8, .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, .capture = { .channels_min = 1, - .channels_max = 2, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .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, - }, - }, - { .name = "pxa2xx-ssp2", - .id = 1, - .probe = pxa_ssp_probe, - .remove = pxa_ssp_remove, - .suspend = pxa_ssp_suspend, - .resume = pxa_ssp_resume, - .playback = { - .channels_min = 1, - .channels_max = 2, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .capture = { - .channels_min = 1, - .channels_max = 2, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .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, - }, - }, - { - .name = "pxa2xx-ssp3", - .id = 2, - .probe = pxa_ssp_probe, - .remove = pxa_ssp_remove, - .suspend = pxa_ssp_suspend, - .resume = pxa_ssp_resume, - .playback = { - .channels_min = 1, - .channels_max = 2, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .capture = { - .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, - .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, - }, - }, - { - .name = "pxa2xx-ssp4", - .id = 3, - .probe = pxa_ssp_probe, - .remove = pxa_ssp_remove, - .suspend = pxa_ssp_suspend, - .resume = pxa_ssp_resume, - .playback = { - .channels_min = 1, - .channels_max = 2, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .capture = { - .channels_min = 1, - .channels_max = 2, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .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, - }, - }, + .ops = &pxa_ssp_dai_ops, +}; + +static const struct snd_soc_component_driver pxa_ssp_component = { + .name = "pxa-ssp", }; -EXPORT_SYMBOL_GPL(pxa_ssp_dai); -static int __init pxa_ssp_init(void) +#ifdef CONFIG_OF +static const struct of_device_id pxa_ssp_of_ids[] = { + { .compatible = "mrvl,pxa-ssp-dai" }, + {} +}; +#endif + +static int asoc_ssp_probe(struct platform_device *pdev) { - return snd_soc_register_dais(pxa_ssp_dai, ARRAY_SIZE(pxa_ssp_dai)); + return snd_soc_register_component(&pdev->dev, &pxa_ssp_component, + &pxa_ssp_dai, 1); } -module_init(pxa_ssp_init); -static void __exit pxa_ssp_exit(void) +static int asoc_ssp_remove(struct platform_device *pdev) { - snd_soc_unregister_dais(pxa_ssp_dai, ARRAY_SIZE(pxa_ssp_dai)); + snd_soc_unregister_component(&pdev->dev); + return 0; } -module_exit(pxa_ssp_exit); + +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>"); diff --git a/sound/soc/pxa/pxa-ssp.h b/sound/soc/pxa/pxa-ssp.h index 91deadd5567..bc79da221c0 100644 --- a/sound/soc/pxa/pxa-ssp.h +++ b/sound/soc/pxa/pxa-ssp.h @@ -42,6 +42,4 @@ #define PXA_SSP_PLL_OUT 0 -extern struct snd_soc_dai pxa_ssp_dai[4]; - #endif diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 812c2b4d3e0..ae956e3f4b9 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -11,19 +11,21 @@ */ #include <linux/init.h> +#include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/dmaengine.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/pxa-regs.h> #include <mach/regs-ac97.h> +#include <mach/audio.h> -#include "pxa2xx-pcm.h" #include "pxa2xx-ac97.h" static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) @@ -40,122 +42,94 @@ static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) pxa2xx_ac97_finish_reset(ac97); } -struct snd_ac97_bus_ops soc_ac97_ops = { +static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .read = pxa2xx_ac97_read, .write = pxa2xx_ac97_write, .warm_reset = pxa2xx_ac97_warm_reset, .reset = pxa2xx_ac97_cold_reset, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = { - .name = "AC97 PCM Stereo out", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(12), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = { - .name = "AC97 PCM Stereo in", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(11), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_stereo_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = { - .name = "AC97 Aux PCM (Slot 5) Mono out", - .dev_addr = __PREG(MODR), - .drcmr = &DRCMR(10), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { + .addr = __PREG(MODR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = { - .name = "AC97 Aux PCM (Slot 5) Mono in", - .dev_addr = __PREG(MODR), - .drcmr = &DRCMR(9), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { + .addr = __PREG(MODR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { - .name = "AC97 Mic PCM (Slot 6) Mono in", - .dev_addr = __PREG(MCDR), - .drcmr = &DRCMR(8), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { + .addr = __PREG(MCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, }; -#ifdef CONFIG_PM -static int pxa2xx_ac97_suspend(struct snd_soc_dai *dai) -{ - return pxa2xx_ac97_hw_suspend(); -} - -static int pxa2xx_ac97_resume(struct snd_soc_dai *dai) -{ - return pxa2xx_ac97_hw_resume(); -} - -#else -#define pxa2xx_ac97_suspend NULL -#define pxa2xx_ac97_resume NULL -#endif - -static int pxa2xx_ac97_probe(struct platform_device *pdev, - struct snd_soc_dai *dai) -{ - return pxa2xx_ac97_hw_probe(pdev); -} - -static void pxa2xx_ac97_remove(struct platform_device *pdev, - struct snd_soc_dai *dai) -{ - pxa2xx_ac97_hw_remove(pdev); -} - static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_soc_dai *cpu_dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct snd_dmaengine_dai_dma_data *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out; + dma_data = &pxa2xx_ac97_pcm_stereo_out; else - cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_in; + dma_data = &pxa2xx_ac97_pcm_stereo_in; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); return 0; } static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_soc_dai *cpu_dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct snd_dmaengine_dai_dma_data *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out; + dma_data = &pxa2xx_ac97_pcm_aux_mono_out; else - cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_in; + 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_hw_mic_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_soc_dai *cpu_dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) return -ENODEV; else - cpu_dai->dma_data = &pxa2xx_ac97_pcm_mic_mono_in; + snd_soc_dai_set_dma_data(cpu_dai, substream, + &pxa2xx_ac97_pcm_mic_mono_in); return 0; } @@ -164,19 +138,26 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, 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 = { + .hw_params = pxa2xx_ac97_hw_params, +}; + +static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = { + .hw_params = pxa2xx_ac97_hw_aux_params, +}; + +static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { + .hw_params = pxa2xx_ac97_hw_mic_params, +}; + /* * There is only 1 physical AC97 interface for pxa2xx, but it * has extra fifo's that can be used for aux DACs and ADCs. */ -struct snd_soc_dai pxa_ac97_dai[] = { +static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { { .name = "pxa2xx-ac97", - .id = 0, .ac97_control = 1, - .probe = pxa2xx_ac97_probe, - .remove = pxa2xx_ac97_remove, - .suspend = pxa2xx_ac97_suspend, - .resume = pxa2xx_ac97_resume, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, @@ -189,12 +170,10 @@ struct snd_soc_dai pxa_ac97_dai[] = { .channels_max = 2, .rates = PXA2XX_AC97_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .ops = { - .hw_params = pxa2xx_ac97_hw_params,}, + .ops = &pxa_ac97_hifi_dai_ops, }, { .name = "pxa2xx-ac97-aux", - .id = 1, .ac97_control = 1, .playback = { .stream_name = "AC97 Aux Playback", @@ -208,12 +187,10 @@ struct snd_soc_dai pxa_ac97_dai[] = { .channels_max = 1, .rates = PXA2XX_AC97_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .ops = { - .hw_params = pxa2xx_ac97_hw_aux_params,}, + .ops = &pxa_ac97_aux_dai_ops, }, { .name = "pxa2xx-ac97-mic", - .id = 2, .ac97_control = 1, .capture = { .stream_name = "AC97 Mic Capture", @@ -221,25 +198,77 @@ struct snd_soc_dai pxa_ac97_dai[] = { .channels_max = 1, .rates = PXA2XX_AC97_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .ops = { - .hw_params = pxa2xx_ac97_hw_mic_params,}, + .ops = &pxa_ac97_mic_dai_ops, }, }; -EXPORT_SYMBOL_GPL(pxa_ac97_dai); -EXPORT_SYMBOL_GPL(soc_ac97_ops); +static const struct snd_soc_component_driver pxa_ac97_component = { + .name = "pxa-ac97", +}; -static int __init pxa_ac97_init(void) +static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) { - return snd_soc_register_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); + int ret; + + 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; + } + + ret = snd_soc_set_ac97_ops(&pxa2xx_ac97_ops); + if (ret != 0) + return ret; + + /* 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 snd_soc_register_component(&pdev->dev, &pxa_ac97_component, + pxa_ac97_dai_driver, ARRAY_SIZE(pxa_ac97_dai_driver)); } -module_init(pxa_ac97_init); -static void __exit pxa_ac97_exit(void) +static int pxa2xx_ac97_dev_remove(struct platform_device *pdev) { - snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); + snd_soc_unregister_component(&pdev->dev); + snd_soc_set_ac97_ops(NULL); + 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(); } -module_exit(pxa_ac97_exit); + +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", + .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP + .pm = &pxa2xx_ac97_pm_ops, +#endif + }, +}; + +module_platform_driver(pxa2xx_ac97_driver); MODULE_AUTHOR("Nicolas Pitre"); MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h index e390de8edcd..a49c21ba384 100644 --- a/sound/soc/pxa/pxa2xx-ac97.h +++ b/sound/soc/pxa/pxa2xx-ac97.h @@ -14,9 +14,4 @@ #define PXA2XX_DAI_AC97_AUX 1 #define PXA2XX_DAI_AC97_MIC 2 -extern struct snd_soc_dai pxa_ac97_dai[3]; - -/* platform data */ -extern struct snd_ac97_bus_ops pxa2xx_ac97_ops; - #endif diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 517991fb109..c0d648d3339 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -17,28 +17,19 @@ #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/pxa-regs.h> -#include <mach/pxa2xx-gpio.h> #include <mach/audio.h> -#include "pxa2xx-pcm.h" #include "pxa2xx-i2s.h" -struct pxa2xx_gpio { - u32 sys; - u32 rx; - u32 tx; - u32 clk; - u32 frm; -}; - /* * I2S Controller Register and Bit Definitions */ @@ -89,51 +80,35 @@ struct pxa_i2s_port { }; static struct pxa_i2s_port pxa_i2s; static struct clk *clk_i2s; - -static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { - .name = "I2S PCM Stereo out", - .dev_addr = __PREG(SADR), - .drcmr = &DRCMR(3), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static int clk_ena = 0; + +static unsigned long pxa2xx_i2s_pcm_stereo_out_req = 3; +static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = { + .addr = __PREG(SADR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_i2s_pcm_stereo_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { - .name = "I2S PCM Stereo in", - .dev_addr = __PREG(SADR), - .drcmr = &DRCMR(2), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, -}; - -static struct pxa2xx_gpio gpio_bus[] = { - { /* I2S SoC Slave */ - .rx = GPIO29_SDATA_IN_I2S_MD, - .tx = GPIO30_SDATA_OUT_I2S_MD, - .clk = GPIO28_BITCLK_IN_I2S_MD, - .frm = GPIO31_SYNC_I2S_MD, - }, - { /* I2S SoC Master */ - .rx = GPIO29_SDATA_IN_I2S_MD, - .tx = GPIO30_SDATA_OUT_I2S_MD, - .clk = GPIO28_BITCLK_OUT_I2S_MD, - .frm = GPIO31_SYNC_I2S_MD, - }, +static unsigned long pxa2xx_i2s_pcm_stereo_in_req = 2; +static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = { + .addr = __PREG(SADR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_i2s_pcm_stereo_in_req, }; 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->dai->cpu_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; if (IS_ERR(clk_i2s)) return PTR_ERR(clk_i2s); - if (!cpu_dai->active) { - SACR0 |= SACR0_RST; + if (!cpu_dai->active) SACR0 = 0; - } return 0; } @@ -181,9 +156,6 @@ static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai, if (clk_id != PXA2XX_I2S_SYSCLK) return -ENODEV; - if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT) - pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys); - return 0; } @@ -191,27 +163,24 @@ static int pxa2xx_i2s_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->dai->cpu_dai; - - pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx); - pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx); - pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm); - pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk); - BUG_ON(IS_ERR(clk_i2s)); - clk_enable(clk_i2s); + 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) - cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out; + dma_data = &pxa2xx_i2s_pcm_stereo_out; else - cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in; + 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; - SACR1 = 0; if (pxa_i2s.master) SACR0 |= SACR0_BCKD; @@ -257,6 +226,10 @@ static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 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: @@ -283,21 +256,19 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream, SAIMR &= ~SAIMR_RFS; } - if (SACR1 & (SACR1_DREC | SACR1_DRPL)) { + if ((SACR1 & (SACR1_DREC | SACR1_DRPL)) == (SACR1_DREC | SACR1_DRPL)) { SACR0 &= ~SACR0_ENB; pxa_i2s_wait(); - clk_disable(clk_i2s); + if (clk_ena) { + clk_disable_unprepare(clk_i2s); + clk_ena = 0; + } } - - clk_put(clk_i2s); } #ifdef CONFIG_PM static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai) { - if (!dai->active) - return 0; - /* store registers */ pxa_i2s.sacr0 = SACR0; pxa_i2s.sacr1 = SACR1; @@ -312,16 +283,14 @@ static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai) static int pxa2xx_i2s_resume(struct snd_soc_dai *dai) { - if (!dai->active) - return 0; - pxa_i2s_wait(); - SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB; + SACR0 = pxa_i2s.sacr0 & ~SACR0_ENB; SACR1 = pxa_i2s.sacr1; SAIMR = pxa_i2s.saimr; SADIV = pxa_i2s.sadiv; - SACR0 |= SACR0_ENB; + + SACR0 = pxa_i2s.sacr0; return 0; } @@ -331,13 +300,51 @@ static int pxa2xx_i2s_resume(struct snd_soc_dai *dai) #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); + + 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) -struct snd_soc_dai pxa_i2s_dai = { - .name = "pxa2xx-i2s", - .id = 0, +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 = { @@ -350,45 +357,29 @@ struct snd_soc_dai pxa_i2s_dai = { .channels_max = 2, .rates = PXA2XX_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .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, - }, + .ops = &pxa_i2s_dai_ops, + .symmetric_rates = 1, }; -EXPORT_SYMBOL_GPL(pxa_i2s_dai); +static const struct snd_soc_component_driver pxa_i2s_component = { + .name = "pxa-i2s", +}; -static int pxa2xx_i2s_probe(struct platform_device *dev) +static int pxa2xx_i2s_drv_probe(struct platform_device *pdev) { - int ret; - - clk_i2s = clk_get(&dev->dev, "I2SCLK"); - if (IS_ERR(clk_i2s)) - return PTR_ERR(clk_i2s); - - pxa_i2s_dai.dev = &dev->dev; - ret = snd_soc_register_dai(&pxa_i2s_dai); - if (ret != 0) - clk_put(clk_i2s); - - return ret; + return snd_soc_register_component(&pdev->dev, &pxa_i2s_component, + &pxa_i2s_dai, 1); } -static int __devexit pxa2xx_i2s_remove(struct platform_device *dev) +static int pxa2xx_i2s_drv_remove(struct platform_device *pdev) { - snd_soc_unregister_dai(&pxa_i2s_dai); - clk_put(clk_i2s); - clk_i2s = ERR_PTR(-ENOENT); + snd_soc_unregister_component(&pdev->dev); return 0; } static struct platform_driver pxa2xx_i2s_driver = { - .probe = pxa2xx_i2s_probe, - .remove = __devexit_p(pxa2xx_i2s_remove), + .probe = pxa2xx_i2s_drv_probe, + .remove = pxa2xx_i2s_drv_remove, .driver = { .name = "pxa2xx-i2s", @@ -398,11 +389,6 @@ static struct platform_driver pxa2xx_i2s_driver = { static int __init pxa2xx_i2s_init(void) { - if (cpu_is_pxa27x()) - gpio_bus[1].sys = GPIO113_I2S_SYSCLK_MD; - else - gpio_bus[1].sys = GPIO32_SYSCLK_I2S_MD; - clk_i2s = ERR_PTR(-ENOENT); return platform_driver_register(&pxa2xx_i2s_driver); } @@ -419,3 +405,4 @@ module_exit(pxa2xx_i2s_exit); 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/sound/soc/pxa/pxa2xx-i2s.h b/sound/soc/pxa/pxa2xx-i2s.h index e2def441153..070f3c6059f 100644 --- a/sound/soc/pxa/pxa2xx-i2s.h +++ b/sound/soc/pxa/pxa2xx-i2s.h @@ -15,6 +15,4 @@ /* I2S clock */ #define PXA2XX_I2S_SYSCLK 0 -extern struct snd_soc_dai pxa_i2s_dai; - #endif diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 53b9fb127a6..42f2f017598 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -11,12 +11,17 @@ */ #include <linux/dma-mapping.h> +#include <linux/module.h> +#include <linux/dmaengine.h> +#include <linux/of.h> + +#include <mach/dma.h> #include <sound/core.h> #include <sound/soc.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> -#include "pxa2xx-pcm.h" #include "../../arm/pxa2xx-pcm.h" static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, @@ -25,9 +30,11 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct pxa2xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct pxa2xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data; + struct snd_dmaengine_dai_dma_data *dma; int ret; + dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + /* return if this is a bufferless transfer e.g. * codec <--> BT codec or GSM modem -- lg FIXME */ if (!dma) @@ -37,7 +44,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, * with different params */ if (prtd->params == NULL) { prtd->params = dma; - ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("name", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) return ret; @@ -45,7 +52,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, } else if (prtd->params != dma) { pxa_free_dma(prtd->dma_ch); prtd->params = dma; - ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("name", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) return ret; @@ -64,6 +71,7 @@ static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) if (prtd->dma_ch >= 0) { pxa_free_dma(prtd->dma_ch); prtd->dma_ch = -1; + prtd->params = NULL; } return 0; @@ -81,26 +89,24 @@ static struct snd_pcm_ops pxa2xx_pcm_ops = { .mmap = pxa2xx_pcm_mmap, }; -static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK; - -static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd) { - int ret = 0; + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret; - if (!card->dev->dma_mask) - card->dev->dma_mask = &pxa2xx_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_32BIT_MASK; + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; - if (dai->playback.channels_min) { + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) goto out; } - if (dai->capture.channels_min) { + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) @@ -110,25 +116,42 @@ static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, return ret; } -struct snd_soc_platform pxa2xx_soc_platform = { - .name = "pxa2xx-audio", - .pcm_ops = &pxa2xx_pcm_ops, +static struct snd_soc_platform_driver pxa2xx_soc_platform = { + .ops = &pxa2xx_pcm_ops, .pcm_new = pxa2xx_soc_pcm_new, .pcm_free = pxa2xx_pcm_free_dma_buffers, }; -EXPORT_SYMBOL_GPL(pxa2xx_soc_platform); -static int __init pxa2xx_soc_platform_init(void) +static int pxa2xx_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pxa2xx_soc_platform); + return snd_soc_register_platform(&pdev->dev, &pxa2xx_soc_platform); } -module_init(pxa2xx_soc_platform_init); -static void __exit pxa2xx_soc_platform_exit(void) +static int pxa2xx_soc_platform_remove(struct platform_device *pdev) { - snd_soc_unregister_platform(&pxa2xx_soc_platform); + snd_soc_unregister_platform(&pdev->dev); + return 0; } -module_exit(pxa2xx_soc_platform_exit); + +#ifdef CONFIG_OF +static const struct of_device_id snd_soc_pxa_audio_match[] = { + { .compatible = "mrvl,pxa-pcm-audio" }, + { } +}; +#endif + +static struct platform_driver pxa_pcm_driver = { + .driver = { + .name = "pxa-pcm-audio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(snd_soc_pxa_audio_match), + }, + + .probe = pxa2xx_soc_platform_probe, + .remove = pxa2xx_soc_platform_remove, +}; + +module_platform_driver(pxa_pcm_driver); MODULE_AUTHOR("Nicolas Pitre"); MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module"); diff --git a/sound/soc/pxa/pxa2xx-pcm.h b/sound/soc/pxa/pxa2xx-pcm.h deleted file mode 100644 index 60c3b20aeeb..00000000000 --- a/sound/soc/pxa/pxa2xx-pcm.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip - * - * Author: Nicolas Pitre - * Created: Nov 30, 2004 - * Copyright: MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _PXA2XX_PCM_H -#define _PXA2XX_PCM_H - -/* platform data */ -extern struct snd_soc_platform pxa2xx_soc_platform; - -#endif diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c new file mode 100644 index 00000000000..08370659549 --- /dev/null +++ b/sound/soc/pxa/raumfeld.c @@ -0,0 +1,339 @@ +/* + * raumfeld_audio.c -- SoC audio for Raumfeld audio devices + * + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> + * + * based on code from: + * + * Wolfson Microelectronics PLC. + * Openedhand Ltd. + * Liam Girdwood <lrg@slimlogic.co.uk> + * Richard Purdie <richard@openedhand.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. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#include <asm/mach-types.h> + +#include "pxa-ssp.h" + +#define GPIO_SPDIF_RESET (38) +#define GPIO_MCLK_RESET (111) +#define GPIO_CODEC_RESET (120) + +static struct i2c_client *max9486_client; +static struct i2c_board_info max9486_hwmon_info = { + I2C_BOARD_INFO("max9485", 0x63), +}; + +#define MAX9485_MCLK_FREQ_112896 0x22 +#define MAX9485_MCLK_FREQ_122880 0x23 +#define MAX9485_MCLK_FREQ_225792 0x32 +#define MAX9485_MCLK_FREQ_245760 0x33 + +static void set_max9485_clk(char clk) +{ + i2c_master_send(max9486_client, &clk, 1); +} + +static void raumfeld_enable_audio(bool en) +{ + if (en) { + gpio_set_value(GPIO_MCLK_RESET, 1); + + /* wait some time to let the clocks become stable */ + msleep(100); + + gpio_set_value(GPIO_SPDIF_RESET, 1); + gpio_set_value(GPIO_CODEC_RESET, 1); + } else { + gpio_set_value(GPIO_MCLK_RESET, 0); + gpio_set_value(GPIO_SPDIF_RESET, 0); + gpio_set_value(GPIO_CODEC_RESET, 0); + } +} + +/* CS4270 */ +static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + /* set freq to 0 to enable all possible codec sample rates */ + return snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); +} + +static void raumfeld_cs4270_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + /* set freq to 0 to enable all possible codec sample rates */ + snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); +} + +static int raumfeld_cs4270_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 fmt, clk = 0; + int ret = 0; + + switch (params_rate(params)) { + case 44100: + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + clk = 11289600; + break; + case 48000: + set_max9485_clk(MAX9485_MCLK_FREQ_122880); + clk = 12288000; + break; + case 88200: + set_max9485_clk(MAX9485_MCLK_FREQ_225792); + clk = 22579200; + break; + case 96000: + set_max9485_clk(MAX9485_MCLK_FREQ_245760); + clk = 24576000; + break; + default: + return -EINVAL; + } + + fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + + /* setup the CODEC DAI */ + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0); + if (ret < 0) + return ret; + + /* setup the CPU DAI */ + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops raumfeld_cs4270_ops = { + .startup = raumfeld_cs4270_startup, + .shutdown = raumfeld_cs4270_shutdown, + .hw_params = raumfeld_cs4270_hw_params, +}; + +static int raumfeld_analog_suspend(struct snd_soc_card *card) +{ + raumfeld_enable_audio(false); + return 0; +} + +static int raumfeld_analog_resume(struct snd_soc_card *card) +{ + raumfeld_enable_audio(true); + return 0; +} + +/* AK4104 */ + +static int raumfeld_ak4104_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 fmt, ret = 0, clk = 0; + + switch (params_rate(params)) { + case 44100: + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + clk = 11289600; + break; + case 48000: + set_max9485_clk(MAX9485_MCLK_FREQ_122880); + clk = 12288000; + break; + case 88200: + set_max9485_clk(MAX9485_MCLK_FREQ_225792); + clk = 22579200; + break; + case 96000: + set_max9485_clk(MAX9485_MCLK_FREQ_245760); + clk = 24576000; + break; + default: + return -EINVAL; + } + + fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF; + + /* setup the CODEC DAI */ + ret = snd_soc_dai_set_fmt(codec_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* setup the CPU DAI */ + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops raumfeld_ak4104_ops = { + .hw_params = raumfeld_ak4104_hw_params, +}; + +#define DAI_LINK_CS4270 \ +{ \ + .name = "CS4270", \ + .stream_name = "CS4270", \ + .cpu_dai_name = "pxa-ssp-dai.0", \ + .platform_name = "pxa-pcm-audio", \ + .codec_dai_name = "cs4270-hifi", \ + .codec_name = "cs4270.0-0048", \ + .ops = &raumfeld_cs4270_ops, \ +} + +#define DAI_LINK_AK4104 \ +{ \ + .name = "ak4104", \ + .stream_name = "Playback", \ + .cpu_dai_name = "pxa-ssp-dai.1", \ + .codec_dai_name = "ak4104-hifi", \ + .platform_name = "pxa-pcm-audio", \ + .ops = &raumfeld_ak4104_ops, \ + .codec_name = "spi0.0", \ +} + +static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] = +{ + DAI_LINK_CS4270, + DAI_LINK_AK4104, +}; + +static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] = +{ + DAI_LINK_CS4270, +}; + +static struct snd_soc_card snd_soc_raumfeld_connector = { + .name = "Raumfeld Connector", + .owner = THIS_MODULE, + .dai_link = snd_soc_raumfeld_connector_dai, + .num_links = ARRAY_SIZE(snd_soc_raumfeld_connector_dai), + .suspend_post = raumfeld_analog_suspend, + .resume_pre = raumfeld_analog_resume, +}; + +static struct snd_soc_card snd_soc_raumfeld_speaker = { + .name = "Raumfeld Speaker", + .owner = THIS_MODULE, + .dai_link = snd_soc_raumfeld_speaker_dai, + .num_links = ARRAY_SIZE(snd_soc_raumfeld_speaker_dai), + .suspend_post = raumfeld_analog_suspend, + .resume_pre = raumfeld_analog_resume, +}; + +static struct platform_device *raumfeld_audio_device; + +static int __init raumfeld_audio_init(void) +{ + int ret; + + if (!machine_is_raumfeld_speaker() && + !machine_is_raumfeld_connector()) + return 0; + + max9486_client = i2c_new_device(i2c_get_adapter(0), + &max9486_hwmon_info); + + if (!max9486_client) + return -ENOMEM; + + set_max9485_clk(MAX9485_MCLK_FREQ_122880); + + /* Register analog device */ + raumfeld_audio_device = platform_device_alloc("soc-audio", 0); + if (!raumfeld_audio_device) + return -ENOMEM; + + if (machine_is_raumfeld_speaker()) + platform_set_drvdata(raumfeld_audio_device, + &snd_soc_raumfeld_speaker); + + if (machine_is_raumfeld_connector()) + platform_set_drvdata(raumfeld_audio_device, + &snd_soc_raumfeld_connector); + + ret = platform_device_add(raumfeld_audio_device); + if (ret < 0) { + platform_device_put(raumfeld_audio_device); + return ret; + } + + raumfeld_enable_audio(true); + return 0; +} + +static void __exit raumfeld_audio_exit(void) +{ + raumfeld_enable_audio(false); + + platform_device_unregister(raumfeld_audio_device); + + i2c_unregister_device(max9486_client); + + gpio_free(GPIO_MCLK_RESET); + gpio_free(GPIO_CODEC_RESET); + gpio_free(GPIO_SPDIF_RESET); +} + +module_init(raumfeld_audio_init); +module_exit(raumfeld_audio_exit); + +/* Module information */ +MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); +MODULE_DESCRIPTION("Raumfeld audio SoC"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index a3b9e6bdf97..1373b017a95 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -23,14 +23,10 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> -#include <mach/pxa-regs.h> -#include <mach/hardware.h> #include <mach/spitz.h> #include "../codecs/wm8750.h" -#include "pxa2xx-pcm.h" #include "pxa2xx-i2s.h" #define SPITZ_HP 0 @@ -46,73 +42,79 @@ static int spitz_jack_func; static int spitz_spk_func; +static int spitz_mic_gpio; -static void spitz_ext_control(struct snd_soc_codec *codec) +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(codec, "Ext Spk"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(codec, "Ext Spk"); + 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(codec, "Headset Jack"); - snd_soc_dapm_disable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_enable_pin(codec, "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"); + 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(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_enable_pin(codec, "Mic Jack"); + 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(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); - snd_soc_dapm_disable_pin(codec, "Mic Jack"); - snd_soc_dapm_enable_pin(codec, "Line Jack"); + 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(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_enable_pin(codec, "Headset Jack"); + 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(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); - snd_soc_dapm_disable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); + 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(codec); + + 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; - struct snd_soc_codec *codec = rtd->socdev->codec; /* check the jack status at stream startup */ - spitz_ext_control(codec); + spitz_ext_control(&rtd->card->dapm); + return 0; } @@ -120,8 +122,8 @@ 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->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + 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; @@ -139,18 +141,6 @@ static int spitz_hw_params(struct snd_pcm_substream *substream, break; } - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - 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_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - /* set the codec system clock for DAC and ADC */ ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, SND_SOC_CLOCK_IN); @@ -181,13 +171,13 @@ static int spitz_get_jack(struct snd_kcontrol *kcontrol, static int spitz_set_jack(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); if (spitz_jack_func == ucontrol->value.integer.value[0]) return 0; spitz_jack_func = ucontrol->value.integer.value[0]; - spitz_ext_control(codec); + spitz_ext_control(&card->dapm); return 1; } @@ -201,27 +191,20 @@ static int spitz_get_spk(struct snd_kcontrol *kcontrol, static int spitz_set_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); if (spitz_spk_func == ucontrol->value.integer.value[0]) return 0; spitz_spk_func = ucontrol->value.integer.value[0]; - spitz_ext_control(codec); + spitz_ext_control(&card->dapm); return 1; } static int spitz_mic_bias(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { - if (machine_is_borzoi() || machine_is_spitz()) - gpio_set_value(SPITZ_GPIO_MIC_BIAS, - SND_SOC_DAPM_EVENT_ON(event)); - - if (machine_is_akita()) - gpio_set_value(AKITA_GPIO_MIC_BIAS, - SND_SOC_DAPM_EVENT_ON(event)); - + gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event)); return 0; } @@ -237,7 +220,7 @@ static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { }; /* Spitz machine audio_map */ -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route spitz_audio_map[] = { /* headphone connected to LOUT1, ROUT1 */ {"Headphone Jack", NULL, "LOUT1"}, @@ -276,35 +259,20 @@ static const struct snd_kcontrol_new wm8750_spitz_controls[] = { /* * Logic for a wm8750 as connected on a Sharp SL-Cxx00 Device */ -static int spitz_wm8750_init(struct snd_soc_codec *codec) +static int spitz_wm8750_init(struct snd_soc_pcm_runtime *rtd) { - int i, err; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; /* NC codec pins */ - snd_soc_dapm_nc_pin(codec, "RINPUT1"); - snd_soc_dapm_nc_pin(codec, "LINPUT2"); - snd_soc_dapm_nc_pin(codec, "RINPUT2"); - snd_soc_dapm_nc_pin(codec, "LINPUT3"); - snd_soc_dapm_nc_pin(codec, "RINPUT3"); - snd_soc_dapm_nc_pin(codec, "OUT3"); - snd_soc_dapm_nc_pin(codec, "MONO1"); - - /* Add spitz specific controls */ - for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8750_spitz_controls[i], codec, NULL)); - if (err < 0) - return err; - } - - /* Add spitz specific widgets */ - snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets, - ARRAY_SIZE(wm8750_dapm_widgets)); - - /* Set up spitz specific audio paths */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_nc_pin(dapm, "RINPUT1"); + snd_soc_dapm_nc_pin(dapm, "LINPUT2"); + snd_soc_dapm_nc_pin(dapm, "RINPUT2"); + snd_soc_dapm_nc_pin(dapm, "LINPUT3"); + snd_soc_dapm_nc_pin(dapm, "RINPUT3"); + snd_soc_dapm_nc_pin(dapm, "OUT3"); + snd_soc_dapm_nc_pin(dapm, "MONO1"); - snd_soc_dapm_sync(codec); return 0; } @@ -312,31 +280,29 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec) static struct snd_soc_dai_link spitz_dai = { .name = "wm8750", .stream_name = "WM8750", - .cpu_dai = &pxa_i2s_dai, - .codec_dai = &wm8750_dai, + .cpu_dai_name = "pxa2xx-i2s", + .codec_dai_name = "wm8750-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm8750.0-001b", .init = spitz_wm8750_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, .ops = &spitz_ops, }; /* spitz audio machine driver */ static struct snd_soc_card snd_soc_spitz = { .name = "Spitz", - .platform = &pxa2xx_soc_platform, + .owner = THIS_MODULE, .dai_link = &spitz_dai, .num_links = 1, -}; - -/* spitz audio private data */ -static struct wm8750_setup_data spitz_wm8750_setup = { - .i2c_bus = 0, - .i2c_address = 0x1b, -}; -/* spitz audio subsystem */ -static struct snd_soc_device spitz_snd_devdata = { - .card = &snd_soc_spitz, - .codec_dev = &soc_codec_dev_wm8750, - .codec_data = &spitz_wm8750_setup, + .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), }; static struct platform_device *spitz_snd_device; @@ -348,23 +314,45 @@ static int __init spitz_init(void) if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita())) return -ENODEV; + if (machine_is_borzoi() || machine_is_spitz()) + spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS; + else + spitz_mic_gpio = AKITA_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; + spitz_snd_device = platform_device_alloc("soc-audio", -1); - if (!spitz_snd_device) - return -ENOMEM; + if (!spitz_snd_device) { + ret = -ENOMEM; + goto err2; + } - platform_set_drvdata(spitz_snd_device, &spitz_snd_devdata); - spitz_snd_devdata.dev = &spitz_snd_device->dev; - ret = platform_device_add(spitz_snd_device); + platform_set_drvdata(spitz_snd_device, &snd_soc_spitz); + ret = platform_device_add(spitz_snd_device); if (ret) - platform_device_put(spitz_snd_device); + goto err3; + + return 0; +err3: + platform_device_put(spitz_snd_device); +err2: + gpio_free(spitz_mic_gpio); +err1: return ret; } static void __exit spitz_exit(void) { platform_device_unregister(spitz_snd_device); + gpio_free(spitz_mic_gpio); } module_init(spitz_init); diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index c77194f74c9..4a956d1cb26 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -26,20 +26,14 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/tosa.h> -#include <mach/pxa-regs.h> -#include <mach/hardware.h> #include <mach/audio.h> #include "../codecs/wm9712.h" -#include "pxa2xx-pcm.h" #include "pxa2xx-ac97.h" -static struct snd_soc_card tosa; - #define TOSA_HP 0 #define TOSA_MIC_INT 1 #define TOSA_HEADSET 2 @@ -50,42 +44,47 @@ static struct snd_soc_card tosa; static int tosa_jack_func; static int tosa_spk_func; -static void tosa_ext_control(struct snd_soc_codec *codec) +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(codec, "Mic (Internal)"); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); + 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(codec, "Mic (Internal)"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); + 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(codec, "Mic (Internal)"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Headset Jack"); + 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(codec, "Speaker"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker"); else - snd_soc_dapm_disable_pin(codec, "Speaker"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); - snd_soc_dapm_sync(codec); + 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; - struct snd_soc_codec *codec = rtd->socdev->codec; /* check the jack status at stream startup */ - tosa_ext_control(codec); + tosa_ext_control(&rtd->card->dapm); + return 0; } @@ -103,13 +102,13 @@ static int tosa_get_jack(struct snd_kcontrol *kcontrol, static int tosa_set_jack(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); if (tosa_jack_func == ucontrol->value.integer.value[0]) return 0; tosa_jack_func = ucontrol->value.integer.value[0]; - tosa_ext_control(codec); + tosa_ext_control(&card->dapm); return 1; } @@ -123,13 +122,13 @@ static int tosa_get_spk(struct snd_kcontrol *kcontrol, static int tosa_set_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); if (tosa_spk_func == ucontrol->value.integer.value[0]) return 0; tosa_spk_func = ucontrol->value.integer.value[0]; - tosa_ext_control(codec); + tosa_ext_control(&card->dapm); return 1; } @@ -186,29 +185,14 @@ static const struct snd_kcontrol_new tosa_controls[] = { tosa_set_spk), }; -static int tosa_ac97_init(struct snd_soc_codec *codec) +static int tosa_ac97_init(struct snd_soc_pcm_runtime *rtd) { - int i, err; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_nc_pin(codec, "OUT3"); - snd_soc_dapm_nc_pin(codec, "MONOOUT"); + snd_soc_dapm_nc_pin(dapm, "OUT3"); + snd_soc_dapm_nc_pin(dapm, "MONOOUT"); - /* add tosa specific controls */ - for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&tosa_controls[i],codec, NULL)); - if (err < 0) - return err; - } - - /* add tosa specific widgets */ - snd_soc_dapm_new_controls(codec, tosa_dapm_widgets, - ARRAY_SIZE(tosa_dapm_widgets)); - - /* set up tosa specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - - snd_soc_dapm_sync(codec); return 0; } @@ -216,91 +200,82 @@ static struct snd_soc_dai_link tosa_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], - .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], + .cpu_dai_name = "pxa2xx-ac97", + .codec_dai_name = "wm9712-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm9712-codec", .init = tosa_ac97_init, .ops = &tosa_ops, }, { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], - .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], + .cpu_dai_name = "pxa2xx-ac97-aux", + .codec_dai_name = "wm9712-aux", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm9712-codec", .ops = &tosa_ops, }, }; -static int tosa_probe(struct platform_device *dev) -{ - int ret; - - ret = gpio_request(TOSA_GPIO_L_MUTE, "Headphone Jack"); - if (ret) - return ret; - ret = gpio_direction_output(TOSA_GPIO_L_MUTE, 0); - if (ret) - gpio_free(TOSA_GPIO_L_MUTE); - - return ret; -} - -static int tosa_remove(struct platform_device *dev) -{ - gpio_free(TOSA_GPIO_L_MUTE); - return 0; -} - static struct snd_soc_card tosa = { .name = "Tosa", - .platform = &pxa2xx_soc_platform, + .owner = THIS_MODULE, .dai_link = tosa_dai, .num_links = ARRAY_SIZE(tosa_dai), - .probe = tosa_probe, - .remove = tosa_remove, -}; -static struct snd_soc_device tosa_snd_devdata = { - .card = &tosa, - .codec_dev = &soc_codec_dev_wm9712, + .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), }; -static struct platform_device *tosa_snd_device; - -static int __init tosa_init(void) +static int tosa_probe(struct platform_device *pdev) { + struct snd_soc_card *card = ⤩ int ret; - if (!machine_is_tosa()) - return -ENODEV; - - tosa_snd_device = platform_device_alloc("soc-audio", -1); - if (!tosa_snd_device) { - ret = -ENOMEM; - goto err_alloc; - } - - platform_set_drvdata(tosa_snd_device, &tosa_snd_devdata); - tosa_snd_devdata.dev = &tosa_snd_device->dev; - ret = platform_device_add(tosa_snd_device); - - if (!ret) - return 0; + ret = gpio_request_one(TOSA_GPIO_L_MUTE, GPIOF_OUT_INIT_LOW, + "Headphone Jack"); + if (ret) + return ret; - platform_device_put(tosa_snd_device); + card->dev = &pdev->dev; -err_alloc: + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); + gpio_free(TOSA_GPIO_L_MUTE); + } return ret; } -static void __exit tosa_exit(void) +static int tosa_remove(struct platform_device *pdev) { - platform_device_unregister(tosa_snd_device); + struct snd_soc_card *card = platform_get_drvdata(pdev); + + gpio_free(TOSA_GPIO_L_MUTE); + snd_soc_unregister_card(card); + return 0; } -module_init(tosa_init); -module_exit(tosa_exit); +static struct platform_driver tosa_driver = { + .driver = { + .name = "tosa-audio", + .owner = THIS_MODULE, + .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/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c new file mode 100644 index 00000000000..9d7c5b7e953 --- /dev/null +++ b/sound/soc/pxa/ttc-dkb.c @@ -0,0 +1,171 @@ +/* + * linux/sound/soc/pxa/ttc_dkb.c + * + * Copyright (C) 2012 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 <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_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_disable_pin(dapm, "Headset Mic 2"); + snd_soc_dapm_disable_pin(dapm, "Headset Stereophone"); + + /* Headset jack detection */ + snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE + | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, + &hs_jack); + snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), + hs_jack_pins); + snd_soc_jack_new(codec, "Microphone Jack", SND_JACK_MICROPHONE, + &mic_jack); + snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins), + mic_jack_pins); + + /* headphone, microphone detection & headset short detection */ + pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE, + SND_JACK_BTN_0, SND_JACK_BTN_1, SND_JACK_BTN_2); + pm860x_mic_jack_detect(codec, &hs_jack, SND_JACK_MICROPHONE); + + return 0; +} + +/* ttc/td-dkb digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link ttc_pm860x_hifi_dai[] = { +{ + .name = "88pm860x i2s", + .stream_name = "audio playback", + .codec_name = "88pm860x-codec", + .platform_name = "mmp-pcm-audio", + .cpu_dai_name = "pxa-ssp-dai.1", + .codec_dai_name = "88pm860x-i2s", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + .init = ttc_pm860x_init, +}, +}; + +/* 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 = snd_soc_register_card(card); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); + + return ret; +} + +static int ttc_dkb_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 ttc_dkb_driver = { + .driver = { + .name = "ttc-dkb-audio", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = ttc_dkb_probe, + .remove = ttc_dkb_remove, +}; + +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/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c new file mode 100644 index 00000000000..76ccb172d0a --- /dev/null +++ b/sound/soc/pxa/z2.c @@ -0,0 +1,231 @@ +/* + * 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> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#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) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + /* NC codec pins */ + snd_soc_dapm_disable_pin(dapm, "LINPUT3"); + snd_soc_dapm_disable_pin(dapm, "RINPUT3"); + snd_soc_dapm_disable_pin(dapm, "OUT3"); + snd_soc_dapm_disable_pin(dapm, "MONO1"); + + /* Jack detection API stuff */ + ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, + &hs_jack); + if (ret) + goto err; + + ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), + 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 struct snd_soc_ops z2_ops = { + .hw_params = z2_hw_params, +}; + +/* z2 digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link z2_dai = { + .name = "wm8750", + .stream_name = "WM8750", + .cpu_dai_name = "pxa2xx-i2s", + .codec_dai_name = "wm8750-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm8750.0-001b", + .init = z2_wm8750_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &z2_ops, +}; + +/* 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), +}; + +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/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index f8e9ecd589d..23bf991e95d 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -14,18 +14,28 @@ #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 <sound/soc-dapm.h> #include "../codecs/wm9713.h" -#include "pxa2xx-pcm.h" #include "pxa2xx-ac97.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[] = { @@ -59,23 +69,12 @@ static const struct snd_soc_dapm_route audio_map[] = { { "Multiactor", NULL, "SPKR" }, }; -static int zylonite_wm9713_init(struct snd_soc_codec *codec) +static int zylonite_wm9713_init(struct snd_soc_pcm_runtime *rtd) { - /* Currently we only support use of the AC97 clock here. If - * CLK_POUT is selected by SW15 then the clock API will need - * to be used to request and enable it here. - */ - - snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets, - ARRAY_SIZE(zylonite_dapm_widgets)); - - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + if (clk_pout) + snd_soc_dai_set_pll(rtd->codec_dai, 0, 0, + clk_get_rate(pout), 0); - /* Static setup for now */ - snd_soc_dapm_enable_pin(codec, "Headphone"); - snd_soc_dapm_enable_pin(codec, "Headset Earpiece"); - - snd_soc_dapm_sync(codec); return 0; } @@ -83,63 +82,61 @@ 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->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; unsigned int pll_out = 0; - unsigned int acds = 0; unsigned int wm9713_div = 0; int ret = 0; + int rate = params_rate(params); + int width = snd_pcm_format_physical_width(params_format(params)); - switch (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; - pll_out = 2048000; break; case 16000: wm9713_div = 6; - pll_out = 4096000; break; case 48000: - default: wm9713_div = 2; - pll_out = 12288000; - acds = 1; break; + default: + /* Don't support OSS emulation */ + return -EINVAL; } - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; + /* Add 1 to the width for the leading clock cycle */ + pll_out = rate * (width + 1) * 8; - ret = snd_soc_dai_set_tdm_slot(cpu_dai, - params_channels(params), - params_channels(params)); + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1); if (ret < 0) return ret; - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out); + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, pll_out); if (ret < 0) return ret; - ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_AUDIO_DIV_ACDS, acds); + 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; - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1); + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); if (ret < 0) return ret; - /* Note that if the PLL is in use the WM9713_PCMCLK_PLL_DIV needs - * to be set instead. - */ - ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV, - WM9713_PCMDIV(wm9713_div)); + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); if (ret < 0) return ret; @@ -154,35 +151,104 @@ static struct snd_soc_dai_link zylonite_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], - .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI], + .codec_name = "wm9713-codec", + .platform_name = "pxa-pcm-audio", + .cpu_dai_name = "pxa2xx-ac97", + .codec_dai_name = "wm9713-hifi", .init = zylonite_wm9713_init, }, { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], - .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX], + .codec_name = "wm9713-codec", + .platform_name = "pxa-pcm-audio", + .cpu_dai_name = "pxa2xx-ac97-aux", + .codec_dai_name = "wm9713-aux", }, { .name = "WM9713 Voice", .stream_name = "WM9713 Voice", - .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP3], - .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE], + .codec_name = "wm9713-codec", + .platform_name = "pxa-pcm-audio", + .cpu_dai_name = "pxa-ssp-dai.2", + .codec_dai_name = "wm9713-voice", .ops = &zylonite_voice_ops, }, }; +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", - .platform = &pxa2xx_soc_platform, + .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), -}; -static struct snd_soc_device zylonite_snd_ac97_devdata = { - .card = &zylonite, - .codec_dev = &soc_codec_dev_wm9713, + .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; @@ -195,9 +261,7 @@ static int __init zylonite_init(void) if (!zylonite_snd_ac97_device) return -ENOMEM; - platform_set_drvdata(zylonite_snd_ac97_device, - &zylonite_snd_ac97_devdata); - zylonite_snd_ac97_devdata.dev = &zylonite_snd_ac97_device->dev; + platform_set_drvdata(zylonite_snd_ac97_device, &zylonite); ret = platform_device_add(zylonite_snd_ac97_device); if (ret != 0) |
