mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
219
sound/soc/pxa/Kconfig
Normal file
219
sound/soc/pxa/Kconfig
Normal file
|
@ -0,0 +1,219 @@
|
|||
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
|
||||
|
||||
config SND_PXA2XX_SOC_AC97
|
||||
tristate
|
||||
select AC97_BUS
|
||||
select SND_ARM
|
||||
select SND_PXA2XX_LIB_AC97
|
||||
select SND_SOC_AC97_BUS
|
||||
|
||||
config SND_PXA2XX_SOC_I2S
|
||||
tristate
|
||||
|
||||
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 && I2C
|
||||
select SND_PXA2XX_SOC_I2S
|
||||
select SND_SOC_WM8731
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on Sharp
|
||||
Zaurus SL-C7x0 models (Corgi, Shepherd, Husky).
|
||||
|
||||
config SND_PXA2XX_SOC_SPITZ
|
||||
tristate "SoC Audio support for Sharp Zaurus SL-Cxx00"
|
||||
depends on SND_PXA2XX_SOC && PXA_SHARP_Cxx00 && I2C
|
||||
select SND_PXA2XX_SOC_I2S
|
||||
select SND_SOC_WM8750
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on Sharp
|
||||
Zaurus SL-Cxx00 models (Spitz, Borzoi and Akita).
|
||||
|
||||
config SND_PXA2XX_SOC_Z2
|
||||
tristate "SoC Audio support for Zipit Z2"
|
||||
depends on SND_PXA2XX_SOC && MACH_ZIPIT2 && I2C
|
||||
select SND_PXA2XX_SOC_I2S
|
||||
select SND_SOC_WM8750
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on Zipit Z2.
|
||||
|
||||
config SND_PXA2XX_SOC_POODLE
|
||||
tristate "SoC Audio support for Poodle"
|
||||
depends on SND_PXA2XX_SOC && MACH_POODLE && I2C
|
||||
select SND_PXA2XX_SOC_I2S
|
||||
select SND_SOC_WM8731
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on Sharp
|
||||
Zaurus SL-5600 model (Poodle).
|
||||
|
||||
config SND_PXA2XX_SOC_TOSA
|
||||
tristate "SoC AC97 Audio support for Tosa"
|
||||
depends on SND_PXA2XX_SOC && MACH_TOSA
|
||||
depends on MFD_TC6393XB
|
||||
select SND_PXA2XX_SOC_AC97
|
||||
select SND_SOC_WM9712
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on Sharp
|
||||
Zaurus SL-C6000x models (Tosa).
|
||||
|
||||
config SND_PXA2XX_SOC_E740
|
||||
tristate "SoC AC97 Audio support for e740"
|
||||
depends on SND_PXA2XX_SOC && MACH_E740
|
||||
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
|
||||
select SND_SOC_WM9712
|
||||
select SND_PXA2XX_SOC_AC97
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on the
|
||||
Toshiba e800 PDA
|
||||
|
||||
config SND_PXA2XX_SOC_EM_X270
|
||||
tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300"
|
||||
depends on SND_PXA2XX_SOC && (MACH_EM_X270 || MACH_EXEDA || \
|
||||
MACH_CM_X300)
|
||||
select SND_PXA2XX_SOC_AC97
|
||||
select SND_SOC_WM9712
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on
|
||||
CompuLab EM-x270, eXeda and CM-X300 machines.
|
||||
|
||||
config SND_PXA2XX_SOC_PALM27X
|
||||
bool "SoC Audio support for Palm T|X, T5, E2 and LifeDrive"
|
||||
depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || \
|
||||
MACH_PALMT5 || MACH_PALMTE2)
|
||||
select SND_PXA2XX_SOC_AC97
|
||||
select SND_SOC_WM9712
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on
|
||||
Palm T|X, T5, E2 or LifeDrive handheld computer.
|
||||
|
||||
config SND_PXA910_SOC
|
||||
tristate "SoC Audio for Marvell PXA910 chip"
|
||||
depends on ARCH_MMP && SND
|
||||
select SND_PCM
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on the
|
||||
Marvell PXA910 reference platform.
|
||||
|
||||
config SND_SOC_TTC_DKB
|
||||
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"
|
||||
depends on SND_PXA2XX_SOC && MACH_ZYLONITE
|
||||
select SND_PXA2XX_SOC_AC97
|
||||
select SND_PXA_SOC_SSP
|
||||
select SND_SOC_WM9713
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on the
|
||||
Marvell Zylonite reference platform.
|
||||
|
||||
config SND_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.
|
53
sound/soc/pxa/Makefile
Normal file
53
sound/soc/pxa/Makefile
Normal file
|
@ -0,0 +1,53 @@
|
|||
# PXA Platform Support
|
||||
snd-soc-pxa2xx-objs := pxa2xx-pcm.o
|
||||
snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
|
||||
snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
|
||||
snd-soc-pxa-ssp-objs := pxa-ssp.o
|
||||
snd-soc-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
|
169
sound/soc/pxa/brownstone.c
Normal file
169
sound/soc/pxa/brownstone.c
Normal file
|
@ -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");
|
343
sound/soc/pxa/corgi.c
Normal file
343
sound/soc/pxa/corgi.c
Normal file
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* corgi.c -- SoC audio for Corgi
|
||||
*
|
||||
* Copyright 2005 Wolfson Microelectronics PLC.
|
||||
* Copyright 2005 Openedhand Ltd.
|
||||
*
|
||||
* Authors: Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
* Richard Purdie <richard@openedhand.com>
|
||||
*
|
||||
* 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/moduleparam.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <mach/corgi.h>
|
||||
#include <mach/audio.h>
|
||||
|
||||
#include "../codecs/wm8731.h"
|
||||
#include "pxa2xx-i2s.h"
|
||||
|
||||
#define CORGI_HP 0
|
||||
#define CORGI_MIC 1
|
||||
#define CORGI_LINE 2
|
||||
#define CORGI_HEADSET 3
|
||||
#define CORGI_HP_OFF 4
|
||||
#define CORGI_SPK_ON 0
|
||||
#define CORGI_SPK_OFF 1
|
||||
|
||||
/* audio clock in Hz - rounded from 12.235MHz */
|
||||
#define CORGI_AUDIO_CLOCK 12288000
|
||||
|
||||
static int corgi_jack_func;
|
||||
static int corgi_spk_func;
|
||||
|
||||
static void corgi_ext_control(struct snd_soc_dapm_context *dapm)
|
||||
{
|
||||
snd_soc_dapm_mutex_lock(dapm);
|
||||
|
||||
/* set up jack connection */
|
||||
switch (corgi_jack_func) {
|
||||
case CORGI_HP:
|
||||
/* set = unmute headphone */
|
||||
gpio_set_value(CORGI_GPIO_MUTE_L, 1);
|
||||
gpio_set_value(CORGI_GPIO_MUTE_R, 1);
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
|
||||
break;
|
||||
case CORGI_MIC:
|
||||
/* reset = mute headphone */
|
||||
gpio_set_value(CORGI_GPIO_MUTE_L, 0);
|
||||
gpio_set_value(CORGI_GPIO_MUTE_R, 0);
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
|
||||
break;
|
||||
case CORGI_LINE:
|
||||
gpio_set_value(CORGI_GPIO_MUTE_L, 0);
|
||||
gpio_set_value(CORGI_GPIO_MUTE_R, 0);
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
|
||||
break;
|
||||
case CORGI_HEADSET:
|
||||
gpio_set_value(CORGI_GPIO_MUTE_L, 0);
|
||||
gpio_set_value(CORGI_GPIO_MUTE_R, 1);
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
|
||||
break;
|
||||
}
|
||||
|
||||
if (corgi_spk_func == CORGI_SPK_ON)
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
|
||||
else
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
|
||||
|
||||
/* signal a DAPM event */
|
||||
snd_soc_dapm_sync_unlocked(dapm);
|
||||
|
||||
snd_soc_dapm_mutex_unlock(dapm);
|
||||
}
|
||||
|
||||
static int corgi_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
||||
/* check the jack status at stream startup */
|
||||
corgi_ext_control(&rtd->card->dapm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we need to unmute the HP at shutdown as the mute burns power on corgi */
|
||||
static void corgi_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
/* set = unmute headphone */
|
||||
gpio_set_value(CORGI_GPIO_MUTE_L, 1);
|
||||
gpio_set_value(CORGI_GPIO_MUTE_R, 1);
|
||||
}
|
||||
|
||||
static int corgi_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
unsigned int clk = 0;
|
||||
int ret = 0;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
case 8000:
|
||||
case 16000:
|
||||
case 48000:
|
||||
case 96000:
|
||||
clk = 12288000;
|
||||
break;
|
||||
case 11025:
|
||||
case 22050:
|
||||
case 44100:
|
||||
clk = 11289600;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set the codec system clock for DAC and ADC */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set the I2S system clock as input (unused) */
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops corgi_ops = {
|
||||
.startup = corgi_startup,
|
||||
.hw_params = corgi_hw_params,
|
||||
.shutdown = corgi_shutdown,
|
||||
};
|
||||
|
||||
static int corgi_get_jack(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = corgi_jack_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int corgi_set_jack(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (corgi_jack_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
corgi_jack_func = ucontrol->value.integer.value[0];
|
||||
corgi_ext_control(&card->dapm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int corgi_get_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = corgi_spk_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int corgi_set_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (corgi_spk_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
corgi_spk_func = ucontrol->value.integer.value[0];
|
||||
corgi_ext_control(&card->dapm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int corgi_amp_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int corgi_mic_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* corgi machine dapm widgets */
|
||||
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
|
||||
SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
|
||||
SND_SOC_DAPM_LINE("Line Jack", NULL),
|
||||
SND_SOC_DAPM_HP("Headset Jack", NULL),
|
||||
};
|
||||
|
||||
/* Corgi machine audio map (connections to the codec pins) */
|
||||
static const struct snd_soc_dapm_route corgi_audio_map[] = {
|
||||
|
||||
/* headset Jack - in = micin, out = LHPOUT*/
|
||||
{"Headset Jack", NULL, "LHPOUT"},
|
||||
|
||||
/* headphone connected to LHPOUT1, RHPOUT1 */
|
||||
{"Headphone Jack", NULL, "LHPOUT"},
|
||||
{"Headphone Jack", NULL, "RHPOUT"},
|
||||
|
||||
/* speaker connected to LOUT, ROUT */
|
||||
{"Ext Spk", NULL, "ROUT"},
|
||||
{"Ext Spk", NULL, "LOUT"},
|
||||
|
||||
/* mic is connected to MICIN (via right channel of headphone jack) */
|
||||
{"MICIN", NULL, "Mic Jack"},
|
||||
|
||||
/* Same as the above but no mic bias for line signals */
|
||||
{"MICIN", NULL, "Line Jack"},
|
||||
};
|
||||
|
||||
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
|
||||
"Off"};
|
||||
static const char *spk_function[] = {"On", "Off"};
|
||||
static const struct soc_enum corgi_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(5, jack_function),
|
||||
SOC_ENUM_SINGLE_EXT(2, spk_function),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
|
||||
SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
|
||||
corgi_set_jack),
|
||||
SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
|
||||
corgi_set_spk),
|
||||
};
|
||||
|
||||
/*
|
||||
* Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
|
||||
*/
|
||||
static int corgi_wm8731_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, "LLINEIN");
|
||||
snd_soc_dapm_nc_pin(dapm, "RLINEIN");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* corgi digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link corgi_dai = {
|
||||
.name = "WM8731",
|
||||
.stream_name = "WM8731",
|
||||
.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 corgi = {
|
||||
.name = "Corgi",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = &corgi_dai,
|
||||
.num_links = 1,
|
||||
|
||||
.controls = wm8731_corgi_controls,
|
||||
.num_controls = ARRAY_SIZE(wm8731_corgi_controls),
|
||||
.dapm_widgets = wm8731_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
|
||||
.dapm_routes = corgi_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(corgi_audio_map),
|
||||
};
|
||||
|
||||
static int corgi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &corgi;
|
||||
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 corgi_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 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");
|
193
sound/soc/pxa/e740_wm9705.c
Normal file
193
sound/soc/pxa/e740_wm9705.c
Normal file
|
@ -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");
|
175
sound/soc/pxa/e750_wm9705.c
Normal file
175
sound/soc/pxa/e750_wm9705.c
Normal file
|
@ -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");
|
156
sound/soc/pxa/e800_wm9712.c
Normal file
156
sound/soc/pxa/e800_wm9712.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* e800-wm9712.c -- SoC audio for e800
|
||||
*
|
||||
* 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 <asm/mach-types.h>
|
||||
#include <mach/audio.h>
|
||||
#include <mach/eseries-gpio.h>
|
||||
|
||||
#include "../codecs/wm9712.h"
|
||||
#include "pxa2xx-ac97.h"
|
||||
|
||||
static int e800_spk_amp_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
if (event & SND_SOC_DAPM_PRE_PMU)
|
||||
gpio_set_value(GPIO_E800_SPK_AMP_ON, 1);
|
||||
else if (event & SND_SOC_DAPM_POST_PMD)
|
||||
gpio_set_value(GPIO_E800_SPK_AMP_ON, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int e800_hp_amp_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
if (event & SND_SOC_DAPM_PRE_PMU)
|
||||
gpio_set_value(GPIO_E800_HP_AMP_OFF, 0);
|
||||
else if (event & SND_SOC_DAPM_POST_PMD)
|
||||
gpio_set_value(GPIO_E800_HP_AMP_OFF, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget e800_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic (Internal1)", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic (Internal2)", NULL),
|
||||
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||
SND_SOC_DAPM_PGA_E("Headphone Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
|
||||
e800_hp_amp_event, SND_SOC_DAPM_PRE_PMU |
|
||||
SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_PGA_E("Speaker Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
|
||||
e800_spk_amp_event, SND_SOC_DAPM_PRE_PMU |
|
||||
SND_SOC_DAPM_POST_PMD),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
{"Headphone Jack", NULL, "HPOUTL"},
|
||||
{"Headphone Jack", NULL, "HPOUTR"},
|
||||
{"Headphone Jack", NULL, "Headphone Amp"},
|
||||
|
||||
{"Speaker Amp", NULL, "MONOOUT"},
|
||||
{"Speaker", NULL, "Speaker Amp"},
|
||||
|
||||
{"MIC1", NULL, "Mic (Internal1)"},
|
||||
{"MIC2", NULL, "Mic (Internal2)"},
|
||||
};
|
||||
|
||||
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",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = e800_dai,
|
||||
.num_links = ARRAY_SIZE(e800_dai),
|
||||
|
||||
.dapm_widgets = e800_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(e800_dapm_widgets),
|
||||
.dapm_routes = audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
||||
};
|
||||
|
||||
static struct gpio e800_audio_gpios[] = {
|
||||
{ GPIO_E800_SPK_AMP_ON, GPIOF_OUT_INIT_HIGH, "Headphone amp" },
|
||||
{ GPIO_E800_HP_AMP_OFF, GPIOF_OUT_INIT_HIGH, "Speaker amp" },
|
||||
};
|
||||
|
||||
static int e800_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &e800;
|
||||
int ret;
|
||||
|
||||
ret = gpio_request_array(e800_audio_gpios,
|
||||
ARRAY_SIZE(e800_audio_gpios));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
|
||||
ret = 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 int e800_remove(struct platform_device *pdev)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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 v2");
|
||||
MODULE_ALIAS("platform:e800-audio");
|
96
sound/soc/pxa/em-x270.c
Normal file
96
sound/soc/pxa/em-x270.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* SoC audio driver for EM-X270, eXeda and CM-X300
|
||||
*
|
||||
* Copyright 2007, 2009 CompuLab, Ltd.
|
||||
*
|
||||
* Author: Mike Rapoport <mike@compulab.co.il>
|
||||
*
|
||||
* Copied from tosa.c:
|
||||
* Copyright 2005 Wolfson Microelectronics PLC.
|
||||
* Copyright 2005 Openedhand Ltd.
|
||||
*
|
||||
* Authors: Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
* Richard Purdie <richard@openedhand.com>
|
||||
*
|
||||
* 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/moduleparam.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <mach/audio.h>
|
||||
|
||||
#include "../codecs/wm9712.h"
|
||||
#include "pxa2xx-ac97.h"
|
||||
|
||||
static struct snd_soc_dai_link em_x270_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 em_x270 = {
|
||||
.name = "EM-X270",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = em_x270_dai,
|
||||
.num_links = ARRAY_SIZE(em_x270_dai),
|
||||
};
|
||||
|
||||
static struct platform_device *em_x270_snd_device;
|
||||
|
||||
static int __init em_x270_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!(machine_is_em_x270() || machine_is_exeda()
|
||||
|| machine_is_cm_x300()))
|
||||
return -ENODEV;
|
||||
|
||||
em_x270_snd_device = platform_device_alloc("soc-audio", -1);
|
||||
if (!em_x270_snd_device)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(em_x270_snd_device, &em_x270);
|
||||
ret = platform_device_add(em_x270_snd_device);
|
||||
|
||||
if (ret)
|
||||
platform_device_put(em_x270_snd_device);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit em_x270_exit(void)
|
||||
{
|
||||
platform_device_unregister(em_x270_snd_device);
|
||||
}
|
||||
|
||||
module_init(em_x270_init);
|
||||
module_exit(em_x270_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Mike Rapoport");
|
||||
MODULE_DESCRIPTION("ALSA SoC EM-X270, eXeda and CM-X300");
|
||||
MODULE_LICENSE("GPL");
|
241
sound/soc/pxa/hx4700.c
Normal file
241
sound/soc/pxa/hx4700.c
Normal file
|
@ -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");
|
105
sound/soc/pxa/imote2.c
Normal file
105
sound/soc/pxa/imote2.c
Normal file
|
@ -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");
|
553
sound/soc/pxa/magician.c
Normal file
553
sound/soc/pxa/magician.c
Normal file
|
@ -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");
|
218
sound/soc/pxa/mioa701_wm9713.c
Normal file
218
sound/soc/pxa/mioa701_wm9713.c
Normal file
|
@ -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->dapm->card->rtd[0].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");
|
258
sound/soc/pxa/mmp-pcm.c
Normal file
258
sound/soc/pxa/mmp-pcm.c
Normal file
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* 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 | \
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP)
|
||||
|
||||
static struct snd_pcm_hardware mmp_pcm_hardware[] = {
|
||||
{
|
||||
.info = MMP_PCM_INFO,
|
||||
.period_bytes_min = 1024,
|
||||
.period_bytes_max = 2048,
|
||||
.periods_min = 2,
|
||||
.periods_max = 32,
|
||||
.buffer_bytes_max = 4096,
|
||||
.fifo_size = 32,
|
||||
},
|
||||
{
|
||||
.info = MMP_PCM_INFO,
|
||||
.period_bytes_min = 1024,
|
||||
.period_bytes_max = 2048,
|
||||
.periods_min = 2,
|
||||
.periods_max = 32,
|
||||
.buffer_bytes_max = 4096,
|
||||
.fifo_size = 32,
|
||||
},
|
||||
};
|
||||
|
||||
static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
|
||||
struct dma_slave_config slave_config;
|
||||
int ret;
|
||||
|
||||
ret =
|
||||
snd_dmaengine_pcm_prepare_slave_config(substream, params,
|
||||
&slave_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dmaengine_slave_config(chan, &slave_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
struct mmp_dma_data *dma_data = param;
|
||||
bool found = false;
|
||||
char *devname;
|
||||
|
||||
devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name,
|
||||
dma_data->ssp_id);
|
||||
if ((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");
|
485
sound/soc/pxa/mmp-sspa.c
Normal file
485
sound/soc/pxa/mmp-sspa.c
Normal file
|
@ -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");
|
92
sound/soc/pxa/mmp-sspa.h
Normal file
92
sound/soc/pxa/mmp-sspa.h
Normal file
|
@ -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 */
|
185
sound/soc/pxa/palm27x.c
Normal file
185
sound/soc/pxa/palm27x.c
Normal file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* linux/sound/soc/pxa/palm27x.c
|
||||
*
|
||||
* SoC Audio driver for Palm T|X, T5 and LifeDrive
|
||||
*
|
||||
* based on tosa.c
|
||||
*
|
||||
* Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <mach/audio.h>
|
||||
#include <linux/platform_data/asoc-palm27x.h>
|
||||
|
||||
#include "../codecs/wm9712.h"
|
||||
#include "pxa2xx-ac97.h"
|
||||
|
||||
static struct snd_soc_jack hs_jack;
|
||||
|
||||
/* Headphones jack detection DAPM pins */
|
||||
static struct snd_soc_jack_pin hs_jack_pins[] = {
|
||||
{
|
||||
.pin = "Headphone Jack",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
};
|
||||
|
||||
/* Headphones jack detection gpios */
|
||||
static struct snd_soc_jack_gpio hs_jack_gpios[] = {
|
||||
[0] = {
|
||||
/* gpio is set on per-platform basis */
|
||||
.name = "hp-gpio",
|
||||
.report = SND_JACK_HEADPHONE,
|
||||
.debounce_time = 200,
|
||||
},
|
||||
};
|
||||
|
||||
/* Palm27x machine dapm widgets */
|
||||
static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_SPK("Ext. Speaker", NULL),
|
||||
SND_SOC_DAPM_MIC("Ext. Microphone", NULL),
|
||||
};
|
||||
|
||||
/* PalmTX audio map */
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
/* headphone connected to HPOUTL, HPOUTR */
|
||||
{"Headphone Jack", NULL, "HPOUTL"},
|
||||
{"Headphone Jack", NULL, "HPOUTR"},
|
||||
|
||||
/* ext speaker connected to ROUT2, LOUT2 */
|
||||
{"Ext. Speaker", NULL, "LOUT2"},
|
||||
{"Ext. Speaker", NULL, "ROUT2"},
|
||||
|
||||
/* mic connected to MIC1 */
|
||||
{"Ext. Microphone", NULL, "MIC1"},
|
||||
};
|
||||
|
||||
static struct snd_soc_card palm27x_asoc;
|
||||
|
||||
static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
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_name = "pxa2xx-ac97",
|
||||
.codec_dai_name = "wm9712-hifi",
|
||||
.codec_name = "wm9712-codec",
|
||||
.platform_name = "pxa-pcm-audio",
|
||||
.init = palm27x_ac97_init,
|
||||
},
|
||||
{
|
||||
.name = "AC97 Aux",
|
||||
.stream_name = "AC97 Aux",
|
||||
.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",
|
||||
.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 int palm27x_asoc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!(machine_is_palmtx() || machine_is_palmt5() ||
|
||||
machine_is_palmld() || machine_is_palmte2()))
|
||||
return -ENODEV;
|
||||
|
||||
if (!pdev->dev.platform_data) {
|
||||
dev_err(&pdev->dev, "please supply platform_data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hs_jack_gpios[0].gpio = ((struct palm27x_asoc_info *)
|
||||
(pdev->dev.platform_data))->jack_gpio;
|
||||
|
||||
palm27x_asoc.dev = &pdev->dev;
|
||||
|
||||
ret = snd_soc_register_card(&palm27x_asoc);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int palm27x_asoc_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_card(&palm27x_asoc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_platform_driver(palm27x_wm9712_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
|
||||
MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive");
|
||||
MODULE_LICENSE("GPL");
|
312
sound/soc/pxa/poodle.c
Normal file
312
sound/soc/pxa/poodle.c
Normal file
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* poodle.c -- SoC audio for Poodle
|
||||
*
|
||||
* Copyright 2005 Wolfson Microelectronics PLC.
|
||||
* Copyright 2005 Openedhand Ltd.
|
||||
*
|
||||
* Authors: Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
* Richard Purdie <richard@openedhand.com>
|
||||
*
|
||||
* 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/moduleparam.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/hardware/locomo.h>
|
||||
#include <mach/poodle.h>
|
||||
#include <mach/audio.h>
|
||||
|
||||
#include "../codecs/wm8731.h"
|
||||
#include "pxa2xx-i2s.h"
|
||||
|
||||
#define POODLE_HP 1
|
||||
#define POODLE_HP_OFF 0
|
||||
#define POODLE_SPK_ON 1
|
||||
#define POODLE_SPK_OFF 0
|
||||
|
||||
/* audio clock in Hz - rounded from 12.235MHz */
|
||||
#define POODLE_AUDIO_CLOCK 12288000
|
||||
|
||||
static int poodle_jack_func;
|
||||
static int poodle_spk_func;
|
||||
|
||||
static void poodle_ext_control(struct snd_soc_dapm_context *dapm)
|
||||
{
|
||||
/* set up jack connection */
|
||||
if (poodle_jack_func == POODLE_HP) {
|
||||
/* set = unmute headphone */
|
||||
locomo_gpio_write(&poodle_locomo_device.dev,
|
||||
POODLE_LOCOMO_GPIO_MUTE_L, 1);
|
||||
locomo_gpio_write(&poodle_locomo_device.dev,
|
||||
POODLE_LOCOMO_GPIO_MUTE_R, 1);
|
||||
snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
|
||||
} else {
|
||||
locomo_gpio_write(&poodle_locomo_device.dev,
|
||||
POODLE_LOCOMO_GPIO_MUTE_L, 0);
|
||||
locomo_gpio_write(&poodle_locomo_device.dev,
|
||||
POODLE_LOCOMO_GPIO_MUTE_R, 0);
|
||||
snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
|
||||
}
|
||||
|
||||
/* set the enpoints to their new connetion states */
|
||||
if (poodle_spk_func == POODLE_SPK_ON)
|
||||
snd_soc_dapm_enable_pin(dapm, "Ext Spk");
|
||||
else
|
||||
snd_soc_dapm_disable_pin(dapm, "Ext Spk");
|
||||
|
||||
/* signal a DAPM event */
|
||||
snd_soc_dapm_sync(dapm);
|
||||
}
|
||||
|
||||
static int poodle_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
||||
/* check the jack status at stream startup */
|
||||
poodle_ext_control(&rtd->card->dapm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we need to unmute the HP at shutdown as the mute burns power on poodle */
|
||||
static void poodle_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
/* set = unmute headphone */
|
||||
locomo_gpio_write(&poodle_locomo_device.dev,
|
||||
POODLE_LOCOMO_GPIO_MUTE_L, 1);
|
||||
locomo_gpio_write(&poodle_locomo_device.dev,
|
||||
POODLE_LOCOMO_GPIO_MUTE_R, 1);
|
||||
}
|
||||
|
||||
static int poodle_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
unsigned int clk = 0;
|
||||
int ret = 0;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
case 8000:
|
||||
case 16000:
|
||||
case 48000:
|
||||
case 96000:
|
||||
clk = 12288000;
|
||||
break;
|
||||
case 11025:
|
||||
case 22050:
|
||||
case 44100:
|
||||
clk = 11289600;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set the codec system clock for DAC and ADC */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set the I2S system clock as input (unused) */
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops poodle_ops = {
|
||||
.startup = poodle_startup,
|
||||
.hw_params = poodle_hw_params,
|
||||
.shutdown = poodle_shutdown,
|
||||
};
|
||||
|
||||
static int poodle_get_jack(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = poodle_jack_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int poodle_set_jack(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (poodle_jack_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
poodle_jack_func = ucontrol->value.integer.value[0];
|
||||
poodle_ext_control(&card->dapm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int poodle_get_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = poodle_spk_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int poodle_set_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (poodle_spk_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
poodle_spk_func = ucontrol->value.integer.value[0];
|
||||
poodle_ext_control(&card->dapm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int poodle_amp_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
if (SND_SOC_DAPM_EVENT_ON(event))
|
||||
locomo_gpio_write(&poodle_locomo_device.dev,
|
||||
POODLE_LOCOMO_GPIO_AMP_ON, 0);
|
||||
else
|
||||
locomo_gpio_write(&poodle_locomo_device.dev,
|
||||
POODLE_LOCOMO_GPIO_AMP_ON, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* poodle machine dapm widgets */
|
||||
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
|
||||
};
|
||||
|
||||
/* Corgi machine connections to the codec pins */
|
||||
static const struct snd_soc_dapm_route poodle_audio_map[] = {
|
||||
|
||||
/* headphone connected to LHPOUT1, RHPOUT1 */
|
||||
{"Headphone Jack", NULL, "LHPOUT"},
|
||||
{"Headphone Jack", NULL, "RHPOUT"},
|
||||
|
||||
/* speaker connected to LOUT, ROUT */
|
||||
{"Ext Spk", NULL, "ROUT"},
|
||||
{"Ext Spk", NULL, "LOUT"},
|
||||
};
|
||||
|
||||
static const char *jack_function[] = {"Off", "Headphone"};
|
||||
static const char *spk_function[] = {"Off", "On"};
|
||||
static const struct soc_enum poodle_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(2, jack_function),
|
||||
SOC_ENUM_SINGLE_EXT(2, spk_function),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new wm8731_poodle_controls[] = {
|
||||
SOC_ENUM_EXT("Jack Function", poodle_enum[0], poodle_get_jack,
|
||||
poodle_set_jack),
|
||||
SOC_ENUM_EXT("Speaker Function", poodle_enum[1], poodle_get_spk,
|
||||
poodle_set_spk),
|
||||
};
|
||||
|
||||
/*
|
||||
* Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
|
||||
*/
|
||||
static int poodle_wm8731_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, "LLINEIN");
|
||||
snd_soc_dapm_nc_pin(dapm, "RLINEIN");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* poodle digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link poodle_dai = {
|
||||
.name = "WM8731",
|
||||
.stream_name = "WM8731",
|
||||
.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 poodle = {
|
||||
.name = "Poodle",
|
||||
.dai_link = &poodle_dai,
|
||||
.num_links = 1,
|
||||
.owner = THIS_MODULE,
|
||||
|
||||
.controls = wm8731_poodle_controls,
|
||||
.num_controls = ARRAY_SIZE(wm8731_poodle_controls),
|
||||
.dapm_widgets = wm8731_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
|
||||
.dapm_routes = poodle_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(poodle_audio_map),
|
||||
};
|
||||
|
||||
static int poodle_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &poodle;
|
||||
int ret;
|
||||
|
||||
locomo_gpio_set_dir(&poodle_locomo_device.dev,
|
||||
POODLE_LOCOMO_GPIO_AMP_ON, 0);
|
||||
/* should we mute HP at startup - burning power ?*/
|
||||
locomo_gpio_set_dir(&poodle_locomo_device.dev,
|
||||
POODLE_LOCOMO_GPIO_MUTE_L, 0);
|
||||
locomo_gpio_set_dir(&poodle_locomo_device.dev,
|
||||
POODLE_LOCOMO_GPIO_MUTE_R, 0);
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
|
||||
ret = snd_soc_register_card(card);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int poodle_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 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");
|
842
sound/soc/pxa/pxa-ssp.c
Normal file
842
sound/soc/pxa/pxa-ssp.c
Normal file
|
@ -0,0 +1,842 @@
|
|||
/*
|
||||
* pxa-ssp.c -- ALSA Soc Audio Layer
|
||||
*
|
||||
* Copyright 2005,2008 Wolfson Microelectronics PLC.
|
||||
* Author: Liam Girdwood
|
||||
* Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* TODO:
|
||||
* o Test network mode for > 16bit sample size
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pxa2xx_ssp.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pxa2xx-lib.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#include "../../arm/pxa2xx-pcm.h"
|
||||
#include "pxa-ssp.h"
|
||||
|
||||
/*
|
||||
* SSP audio private data
|
||||
*/
|
||||
struct ssp_priv {
|
||||
struct ssp_device *ssp;
|
||||
unsigned int sysclk;
|
||||
int dai_fmt;
|
||||
#ifdef CONFIG_PM
|
||||
uint32_t cr0;
|
||||
uint32_t cr1;
|
||||
uint32_t to;
|
||||
uint32_t psp;
|
||||
#endif
|
||||
};
|
||||
|
||||
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));
|
||||
|
||||
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 void pxa_ssp_enable(struct ssp_device *ssp)
|
||||
{
|
||||
uint32_t sscr0;
|
||||
|
||||
sscr0 = __raw_readl(ssp->mmio_base + SSCR0) | SSCR0_SSE;
|
||||
__raw_writel(sscr0, ssp->mmio_base + SSCR0);
|
||||
}
|
||||
|
||||
static void pxa_ssp_disable(struct ssp_device *ssp)
|
||||
{
|
||||
uint32_t sscr0;
|
||||
|
||||
sscr0 = __raw_readl(ssp->mmio_base + SSCR0) & ~SSCR0_SSE;
|
||||
__raw_writel(sscr0, ssp->mmio_base + SSCR0);
|
||||
}
|
||||
|
||||
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 *cpu_dai)
|
||||
{
|
||||
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct ssp_device *ssp = priv->ssp;
|
||||
struct snd_dmaengine_dai_dma_data *dma;
|
||||
int ret = 0;
|
||||
|
||||
if (!cpu_dai->active) {
|
||||
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 *cpu_dai)
|
||||
{
|
||||
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct ssp_device *ssp = priv->ssp;
|
||||
|
||||
if (!cpu_dai->active) {
|
||||
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 = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct ssp_device *ssp = priv->ssp;
|
||||
|
||||
if (!cpu_dai->active)
|
||||
clk_enable(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 = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct ssp_device *ssp = priv->ssp;
|
||||
uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE;
|
||||
|
||||
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);
|
||||
|
||||
if (cpu_dai->active)
|
||||
pxa_ssp_enable(ssp);
|
||||
else
|
||||
clk_disable(ssp->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define pxa_ssp_suspend NULL
|
||||
#define pxa_ssp_resume NULL
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ssp_set_clkdiv - set SSP clock divider
|
||||
* @div: serial clock rate divider
|
||||
*/
|
||||
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)
|
||||
{
|
||||
u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0);
|
||||
u32 div;
|
||||
|
||||
if (ssp->type == PXA25x_SSP)
|
||||
div = ((sscr0 >> 8) & 0xff) * 2 + 2;
|
||||
else
|
||||
div = ((sscr0 >> 8) & 0xfff) + 1;
|
||||
return div;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the SSP ports SYSCLK.
|
||||
*/
|
||||
static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct ssp_device *ssp = priv->ssp;
|
||||
int val;
|
||||
|
||||
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 %u\n",
|
||||
cpu_dai->id, clk_id, freq);
|
||||
|
||||
switch (clk_id) {
|
||||
case PXA_SSP_CLK_NET_PLL:
|
||||
sscr0 |= SSCR0_MOD;
|
||||
break;
|
||||
case PXA_SSP_CLK_PLL:
|
||||
/* Internal PLL is fixed */
|
||||
if (ssp->type == PXA25x_SSP)
|
||||
priv->sysclk = 1843200;
|
||||
else
|
||||
priv->sysclk = 13000000;
|
||||
break;
|
||||
case PXA_SSP_CLK_EXT:
|
||||
priv->sysclk = freq;
|
||||
sscr0 |= SSCR0_ECS;
|
||||
break;
|
||||
case PXA_SSP_CLK_NET:
|
||||
priv->sysclk = freq;
|
||||
sscr0 |= SSCR0_NCS | SSCR0_MOD;
|
||||
break;
|
||||
case PXA_SSP_CLK_AUDIO:
|
||||
priv->sysclk = 0;
|
||||
pxa_ssp_set_scr(ssp, 1);
|
||||
sscr0 |= SSCR0_ACS;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* The SSP clock must be disabled when changing SSP clock mode
|
||||
* on PXA2xx. On PXA3xx it must be enabled when doing so. */
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the SSP clock dividers.
|
||||
*/
|
||||
static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
|
||||
int div_id, int div)
|
||||
{
|
||||
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 = (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 = pxa_ssp_read_reg(ssp, SSACD);
|
||||
val &= ~SSACD_SCDB;
|
||||
if (ssp->type == PXA3xx_SSP)
|
||||
val &= ~SSACD_SCDX8;
|
||||
switch (div) {
|
||||
case PXA_SSP_CLK_SCDB_1:
|
||||
val |= SSACD_SCDB;
|
||||
break;
|
||||
case PXA_SSP_CLK_SCDB_4:
|
||||
break;
|
||||
case PXA_SSP_CLK_SCDB_8:
|
||||
if (ssp->type == PXA3xx_SSP)
|
||||
val |= SSACD_SCDX8;
|
||||
else
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
pxa_ssp_write_reg(ssp, SSACD, val);
|
||||
break;
|
||||
case PXA_SSP_DIV_SCR:
|
||||
pxa_ssp_set_scr(ssp, div);
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the PLL frequency pxa27x and (afaik - pxa320 only)
|
||||
*/
|
||||
static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
|
||||
int source, unsigned int freq_in, unsigned int freq_out)
|
||||
{
|
||||
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 (ssp->type == PXA3xx_SSP)
|
||||
pxa_ssp_write_reg(ssp, SSACDD, 0);
|
||||
|
||||
switch (freq_out) {
|
||||
case 5622000:
|
||||
break;
|
||||
case 11345000:
|
||||
ssacd |= (0x1 << 4);
|
||||
break;
|
||||
case 12235000:
|
||||
ssacd |= (0x2 << 4);
|
||||
break;
|
||||
case 14857000:
|
||||
ssacd |= (0x3 << 4);
|
||||
break;
|
||||
case 32842000:
|
||||
ssacd |= (0x4 << 4);
|
||||
break;
|
||||
case 48000000:
|
||||
ssacd |= (0x5 << 4);
|
||||
break;
|
||||
case 0:
|
||||
/* Disable */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* PXA3xx has a clock ditherer which can be used to generate
|
||||
* a wider range of frequencies - calculate a value for it.
|
||||
*/
|
||||
if (ssp->type == PXA3xx_SSP) {
|
||||
u32 val;
|
||||
u64 tmp = 19968;
|
||||
tmp *= 1000000;
|
||||
do_div(tmp, freq_out);
|
||||
val = tmp;
|
||||
|
||||
val = (val << 16) | 64;
|
||||
pxa_ssp_write_reg(ssp, SSACDD, val);
|
||||
|
||||
ssacd |= (0x6 << 4);
|
||||
|
||||
dev_dbg(&ssp->pdev->dev,
|
||||
"Using SSACDD %x to supply %uHz\n",
|
||||
val, freq_out);
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pxa_ssp_write_reg(ssp, SSACD, ssacd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the active slots in TDM/Network mode
|
||||
*/
|
||||
static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
|
||||
{
|
||||
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct ssp_device *ssp = priv->ssp;
|
||||
u32 sscr0;
|
||||
|
||||
sscr0 = pxa_ssp_read_reg(ssp, SSCR0);
|
||||
sscr0 &= ~(SSCR0_MOD | SSCR0_SlotsPerFrm(8) | SSCR0_EDSS | SSCR0_DSS);
|
||||
|
||||
/* 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tristate the SSP DAI lines
|
||||
*/
|
||||
static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai,
|
||||
int tristate)
|
||||
{
|
||||
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct ssp_device *ssp = priv->ssp;
|
||||
u32 sscr1;
|
||||
|
||||
sscr1 = pxa_ssp_read_reg(ssp, SSCR1);
|
||||
if (tristate)
|
||||
sscr1 &= ~SSCR1_TTE;
|
||||
else
|
||||
sscr1 |= SSCR1_TTE;
|
||||
pxa_ssp_write_reg(ssp, SSCR1, sscr1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the SSP DAI format.
|
||||
* The SSP Port must be inactive before calling this function as the
|
||||
* physical interface format is changed.
|
||||
*/
|
||||
static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct ssp_device *ssp = priv->ssp;
|
||||
u32 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 = 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_SCFR;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
sscr1 |= SSCR1_SCLKDIR | SSCR1_SCFR;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
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_PSP;
|
||||
sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
|
||||
/* See hw_params() */
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
sspsp |= SSPSP_FSRT;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
sscr0 |= SSCR0_MOD | SSCR0_PSP;
|
||||
sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* Since we are configuring the timings for the format by hand
|
||||
* we have to defer some things until hw_params() where we
|
||||
* know parameters like the sample size.
|
||||
*/
|
||||
priv->dai_fmt = fmt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the SSP audio DMA parameters and sample size.
|
||||
* Can be called multiple times by oss emulation.
|
||||
*/
|
||||
static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct ssp_device *ssp = priv->ssp;
|
||||
int chn = params_channels(params);
|
||||
u32 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;
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
|
||||
|
||||
/* 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 (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE)
|
||||
return 0;
|
||||
|
||||
/* clear selected SSP bits */
|
||||
sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS);
|
||||
|
||||
/* bit size */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
if (ssp->type == PXA3xx_SSP)
|
||||
sscr0 |= SSCR0_FPCKE;
|
||||
sscr0 |= SSCR0_DataSize(16);
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8));
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16));
|
||||
break;
|
||||
}
|
||||
pxa_ssp_write_reg(ssp, SSCR0, sscr0);
|
||||
|
||||
switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
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;
|
||||
}
|
||||
|
||||
/* When we use a network mode, we always require TDM slots
|
||||
* - complain loudly and fail if they've not been set up yet.
|
||||
*/
|
||||
if ((sscr0 & SSCR0_MOD) && !ttsa) {
|
||||
dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dump_registers(ssp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pxa_ssp_set_running_bit(struct snd_pcm_substream *substream,
|
||||
struct ssp_device *ssp, int value)
|
||||
{
|
||||
uint32_t 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 *cpu_dai)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct ssp_device *ssp = priv->ssp;
|
||||
int val;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
pxa_ssp_enable(ssp);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pxa_ssp_set_running_bit(substream, ssp, 1);
|
||||
val = pxa_ssp_read_reg(ssp, SSSR);
|
||||
pxa_ssp_write_reg(ssp, SSSR, val);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
pxa_ssp_set_running_bit(substream, ssp, 1);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
pxa_ssp_set_running_bit(substream, ssp, 0);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
pxa_ssp_disable(ssp);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
pxa_ssp_set_running_bit(substream, ssp, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
dump_registers(ssp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pxa_ssp_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct device *dev = dai->dev;
|
||||
struct ssp_priv *priv;
|
||||
int ret;
|
||||
|
||||
priv = kzalloc(sizeof(struct ssp_priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (dev->of_node) {
|
||||
struct device_node *ssp_handle;
|
||||
|
||||
ssp_handle = of_parse_phandle(dev->of_node, "port", 0);
|
||||
if (!ssp_handle) {
|
||||
dev_err(dev, "unable to get 'port' phandle\n");
|
||||
ret = -ENODEV;
|
||||
goto err_priv;
|
||||
}
|
||||
|
||||
priv->ssp = pxa_ssp_request_of(ssp_handle, "SoC audio");
|
||||
if (priv->ssp == NULL) {
|
||||
ret = -ENODEV;
|
||||
goto err_priv;
|
||||
}
|
||||
} else {
|
||||
priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio");
|
||||
if (priv->ssp == NULL) {
|
||||
ret = -ENODEV;
|
||||
goto err_priv;
|
||||
}
|
||||
}
|
||||
|
||||
priv->dai_fmt = (unsigned int) -1;
|
||||
snd_soc_dai_set_drvdata(dai, priv);
|
||||
|
||||
return 0;
|
||||
|
||||
err_priv:
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pxa_ssp_remove(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct ssp_priv *priv = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
pxa_ssp_free(priv->ssp);
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
|
||||
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
|
||||
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
|
||||
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
|
||||
|
||||
#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static const struct snd_soc_dai_ops pxa_ssp_dai_ops = {
|
||||
.startup = pxa_ssp_startup,
|
||||
.shutdown = pxa_ssp_shutdown,
|
||||
.trigger = pxa_ssp_trigger,
|
||||
.hw_params = pxa_ssp_hw_params,
|
||||
.set_sysclk = pxa_ssp_set_dai_sysclk,
|
||||
.set_clkdiv = pxa_ssp_set_dai_clkdiv,
|
||||
.set_pll = pxa_ssp_set_dai_pll,
|
||||
.set_fmt = pxa_ssp_set_dai_fmt,
|
||||
.set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
|
||||
.set_tristate = pxa_ssp_set_dai_tristate,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver pxa_ssp_dai = {
|
||||
.probe = pxa_ssp_probe,
|
||||
.remove = pxa_ssp_remove,
|
||||
.suspend = pxa_ssp_suspend,
|
||||
.resume = pxa_ssp_resume,
|
||||
.playback = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = PXA_SSP_RATES,
|
||||
.formats = PXA_SSP_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = PXA_SSP_RATES,
|
||||
.formats = PXA_SSP_FORMATS,
|
||||
},
|
||||
.ops = &pxa_ssp_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver pxa_ssp_component = {
|
||||
.name = "pxa-ssp",
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id pxa_ssp_of_ids[] = {
|
||||
{ .compatible = "mrvl,pxa-ssp-dai" },
|
||||
{}
|
||||
};
|
||||
#endif
|
||||
|
||||
static int asoc_ssp_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_component(&pdev->dev, &pxa_ssp_component,
|
||||
&pxa_ssp_dai, 1);
|
||||
}
|
||||
|
||||
static int asoc_ssp_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver asoc_ssp_driver = {
|
||||
.driver = {
|
||||
.name = "pxa-ssp-dai",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(pxa_ssp_of_ids),
|
||||
},
|
||||
|
||||
.probe = asoc_ssp_probe,
|
||||
.remove = asoc_ssp_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(asoc_ssp_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
MODULE_DESCRIPTION("PXA SSP/PCM SoC Interface");
|
||||
MODULE_LICENSE("GPL");
|
45
sound/soc/pxa/pxa-ssp.h
Normal file
45
sound/soc/pxa/pxa-ssp.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* ASoC PXA SSP port support
|
||||
*
|
||||
* 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 _PXA_SSP_H
|
||||
#define _PXA_SSP_H
|
||||
|
||||
/* pxa DAI SSP IDs */
|
||||
#define PXA_DAI_SSP1 0
|
||||
#define PXA_DAI_SSP2 1
|
||||
#define PXA_DAI_SSP3 2
|
||||
#define PXA_DAI_SSP4 3
|
||||
|
||||
/* SSP clock sources */
|
||||
#define PXA_SSP_CLK_PLL 0
|
||||
#define PXA_SSP_CLK_EXT 1
|
||||
#define PXA_SSP_CLK_NET 2
|
||||
#define PXA_SSP_CLK_AUDIO 3
|
||||
#define PXA_SSP_CLK_NET_PLL 4
|
||||
|
||||
/* SSP audio dividers */
|
||||
#define PXA_SSP_AUDIO_DIV_ACDS 0
|
||||
#define PXA_SSP_AUDIO_DIV_SCDB 1
|
||||
#define PXA_SSP_DIV_SCR 2
|
||||
|
||||
/* SSP ACDS audio dividers values */
|
||||
#define PXA_SSP_CLK_AUDIO_DIV_1 0
|
||||
#define PXA_SSP_CLK_AUDIO_DIV_2 1
|
||||
#define PXA_SSP_CLK_AUDIO_DIV_4 2
|
||||
#define PXA_SSP_CLK_AUDIO_DIV_8 3
|
||||
#define PXA_SSP_CLK_AUDIO_DIV_16 4
|
||||
#define PXA_SSP_CLK_AUDIO_DIV_32 5
|
||||
|
||||
/* SSP divider bypass */
|
||||
#define PXA_SSP_CLK_SCDB_4 0
|
||||
#define PXA_SSP_CLK_SCDB_1 1
|
||||
#define PXA_SSP_CLK_SCDB_8 2
|
||||
|
||||
#define PXA_SSP_PLL_OUT 0
|
||||
|
||||
#endif
|
275
sound/soc/pxa/pxa2xx-ac97.c
Normal file
275
sound/soc/pxa/pxa2xx-ac97.c
Normal file
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: Dec 02, 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.
|
||||
*/
|
||||
|
||||
#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/regs-ac97.h>
|
||||
#include <mach/audio.h>
|
||||
|
||||
#include "pxa2xx-ac97.h"
|
||||
|
||||
static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97)
|
||||
{
|
||||
pxa2xx_ac97_try_warm_reset(ac97);
|
||||
|
||||
pxa2xx_ac97_finish_reset(ac97);
|
||||
}
|
||||
|
||||
static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)
|
||||
{
|
||||
pxa2xx_ac97_try_cold_reset(ac97);
|
||||
|
||||
pxa2xx_ac97_finish_reset(ac97);
|
||||
}
|
||||
|
||||
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 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 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 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 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 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,
|
||||
};
|
||||
|
||||
static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dma_data = &pxa2xx_ac97_pcm_stereo_out;
|
||||
else
|
||||
dma_data = &pxa2xx_ac97_pcm_stereo_in;
|
||||
|
||||
snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dma_data = &pxa2xx_ac97_pcm_aux_mono_out;
|
||||
else
|
||||
dma_data = &pxa2xx_ac97_pcm_aux_mono_in;
|
||||
|
||||
snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
return -ENODEV;
|
||||
else
|
||||
snd_soc_dai_set_dma_data(cpu_dai, substream,
|
||||
&pxa2xx_ac97_pcm_mic_mono_in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
|
||||
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000)
|
||||
|
||||
static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = {
|
||||
.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.
|
||||
*/
|
||||
static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
|
||||
{
|
||||
.name = "pxa2xx-ac97",
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = PXA2XX_AC97_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.capture = {
|
||||
.stream_name = "AC97 Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = PXA2XX_AC97_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.ops = &pxa_ac97_hifi_dai_ops,
|
||||
},
|
||||
{
|
||||
.name = "pxa2xx-ac97-aux",
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Aux Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = PXA2XX_AC97_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.capture = {
|
||||
.stream_name = "AC97 Aux Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = PXA2XX_AC97_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.ops = &pxa_ac97_aux_dai_ops,
|
||||
},
|
||||
{
|
||||
.name = "pxa2xx-ac97-mic",
|
||||
.ac97_control = 1,
|
||||
.capture = {
|
||||
.stream_name = "AC97 Mic Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = PXA2XX_AC97_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.ops = &pxa_ac97_mic_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver pxa_ac97_component = {
|
||||
.name = "pxa-ac97",
|
||||
};
|
||||
|
||||
static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
static int pxa2xx_ac97_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
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");
|
||||
MODULE_LICENSE("GPL");
|
17
sound/soc/pxa/pxa2xx-ac97.h
Normal file
17
sound/soc/pxa/pxa2xx-ac97.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* linux/sound/soc/pxa/pxa2xx-ac97.h
|
||||
*
|
||||
* 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_AC97_H
|
||||
#define _PXA2XX_AC97_H
|
||||
|
||||
/* pxa2xx DAI ID's */
|
||||
#define PXA2XX_DAI_AC97_HIFI 0
|
||||
#define PXA2XX_DAI_AC97_AUX 1
|
||||
#define PXA2XX_DAI_AC97_MIC 2
|
||||
|
||||
#endif
|
408
sound/soc/pxa/pxa2xx-i2s.c
Normal file
408
sound/soc/pxa/pxa2xx-i2s.c
Normal file
|
@ -0,0 +1,408 @@
|
|||
/*
|
||||
* pxa2xx-i2s.c -- ALSA Soc Audio Layer
|
||||
*
|
||||
* Copyright 2005 Wolfson Microelectronics PLC.
|
||||
* Author: Liam Girdwood
|
||||
* lrg@slimlogic.co.uk
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pxa2xx-lib.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/audio.h>
|
||||
|
||||
#include "pxa2xx-i2s.h"
|
||||
|
||||
/*
|
||||
* I2S Controller Register and Bit Definitions
|
||||
*/
|
||||
#define SACR0 __REG(0x40400000) /* Global Control Register */
|
||||
#define SACR1 __REG(0x40400004) /* Serial Audio I 2 S/MSB-Justified Control Register */
|
||||
#define SASR0 __REG(0x4040000C) /* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */
|
||||
#define SAIMR __REG(0x40400014) /* Serial Audio Interrupt Mask Register */
|
||||
#define SAICR __REG(0x40400018) /* Serial Audio Interrupt Clear Register */
|
||||
#define SADIV __REG(0x40400060) /* Audio Clock Divider Register. */
|
||||
#define SADR __REG(0x40400080) /* Serial Audio Data Register (TX and RX FIFO access Register). */
|
||||
|
||||
#define SACR0_RFTH(x) ((x) << 12) /* Rx FIFO Interrupt or DMA Trigger Threshold */
|
||||
#define SACR0_TFTH(x) ((x) << 8) /* Tx FIFO Interrupt or DMA Trigger Threshold */
|
||||
#define SACR0_STRF (1 << 5) /* FIFO Select for EFWR Special Function */
|
||||
#define SACR0_EFWR (1 << 4) /* Enable EFWR Function */
|
||||
#define SACR0_RST (1 << 3) /* FIFO, i2s Register Reset */
|
||||
#define SACR0_BCKD (1 << 2) /* Bit Clock Direction */
|
||||
#define SACR0_ENB (1 << 0) /* Enable I2S Link */
|
||||
#define SACR1_ENLBF (1 << 5) /* Enable Loopback */
|
||||
#define SACR1_DRPL (1 << 4) /* Disable Replaying Function */
|
||||
#define SACR1_DREC (1 << 3) /* Disable Recording Function */
|
||||
#define SACR1_AMSL (1 << 0) /* Specify Alternate Mode */
|
||||
|
||||
#define SASR0_I2SOFF (1 << 7) /* Controller Status */
|
||||
#define SASR0_ROR (1 << 6) /* Rx FIFO Overrun */
|
||||
#define SASR0_TUR (1 << 5) /* Tx FIFO Underrun */
|
||||
#define SASR0_RFS (1 << 4) /* Rx FIFO Service Request */
|
||||
#define SASR0_TFS (1 << 3) /* Tx FIFO Service Request */
|
||||
#define SASR0_BSY (1 << 2) /* I2S Busy */
|
||||
#define SASR0_RNE (1 << 1) /* Rx FIFO Not Empty */
|
||||
#define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */
|
||||
|
||||
#define SAICR_ROR (1 << 6) /* Clear Rx FIFO Overrun Interrupt */
|
||||
#define SAICR_TUR (1 << 5) /* Clear Tx FIFO Underrun Interrupt */
|
||||
|
||||
#define SAIMR_ROR (1 << 6) /* Enable Rx FIFO Overrun Condition Interrupt */
|
||||
#define SAIMR_TUR (1 << 5) /* Enable Tx FIFO Underrun Condition Interrupt */
|
||||
#define SAIMR_RFS (1 << 4) /* Enable Rx FIFO Service Interrupt */
|
||||
#define SAIMR_TFS (1 << 3) /* Enable Tx FIFO Service Interrupt */
|
||||
|
||||
struct pxa_i2s_port {
|
||||
u32 sadiv;
|
||||
u32 sacr0;
|
||||
u32 sacr1;
|
||||
u32 saimr;
|
||||
int master;
|
||||
u32 fmt;
|
||||
};
|
||||
static struct pxa_i2s_port pxa_i2s;
|
||||
static struct clk *clk_i2s;
|
||||
static int clk_ena = 0;
|
||||
|
||||
static 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 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->cpu_dai;
|
||||
|
||||
if (IS_ERR(clk_i2s))
|
||||
return PTR_ERR(clk_i2s);
|
||||
|
||||
if (!cpu_dai->active)
|
||||
SACR0 = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* wait for I2S controller to be ready */
|
||||
static int pxa_i2s_wait(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* flush the Rx FIFO */
|
||||
for(i = 0; i < 16; i++)
|
||||
SADR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
/* interface format */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
pxa_i2s.fmt = 0;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
pxa_i2s.fmt = SACR1_AMSL;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
pxa_i2s.master = 1;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
pxa_i2s.master = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
if (clk_id != PXA2XX_I2S_SYSCLK)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
|
||||
if (WARN_ON(IS_ERR(clk_i2s)))
|
||||
return -EINVAL;
|
||||
clk_prepare_enable(clk_i2s);
|
||||
clk_ena = 1;
|
||||
pxa_i2s_wait();
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dma_data = &pxa2xx_i2s_pcm_stereo_out;
|
||||
else
|
||||
dma_data = &pxa2xx_i2s_pcm_stereo_in;
|
||||
|
||||
snd_soc_dai_set_dma_data(dai, substream, dma_data);
|
||||
|
||||
/* is port used by another stream */
|
||||
if (!(SACR0 & SACR0_ENB)) {
|
||||
SACR0 = 0;
|
||||
if (pxa_i2s.master)
|
||||
SACR0 |= SACR0_BCKD;
|
||||
|
||||
SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
|
||||
SACR1 |= pxa_i2s.fmt;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
SAIMR |= SAIMR_TFS;
|
||||
else
|
||||
SAIMR |= SAIMR_RFS;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
case 8000:
|
||||
SADIV = 0x48;
|
||||
break;
|
||||
case 11025:
|
||||
SADIV = 0x34;
|
||||
break;
|
||||
case 16000:
|
||||
SADIV = 0x24;
|
||||
break;
|
||||
case 22050:
|
||||
SADIV = 0x1a;
|
||||
break;
|
||||
case 44100:
|
||||
SADIV = 0xd;
|
||||
break;
|
||||
case 48000:
|
||||
SADIV = 0xc;
|
||||
break;
|
||||
case 96000: /* not in manual and possibly slightly inaccurate */
|
||||
SADIV = 0x6;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
SACR1 &= ~SACR1_DRPL;
|
||||
else
|
||||
SACR1 &= ~SACR1_DREC;
|
||||
SACR0 |= SACR0_ENB;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
SACR1 |= SACR1_DRPL;
|
||||
SAIMR &= ~SAIMR_TFS;
|
||||
} else {
|
||||
SACR1 |= SACR1_DREC;
|
||||
SAIMR &= ~SAIMR_RFS;
|
||||
}
|
||||
|
||||
if ((SACR1 & (SACR1_DREC | SACR1_DRPL)) == (SACR1_DREC | SACR1_DRPL)) {
|
||||
SACR0 &= ~SACR0_ENB;
|
||||
pxa_i2s_wait();
|
||||
if (clk_ena) {
|
||||
clk_disable_unprepare(clk_i2s);
|
||||
clk_ena = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai)
|
||||
{
|
||||
/* store registers */
|
||||
pxa_i2s.sacr0 = SACR0;
|
||||
pxa_i2s.sacr1 = SACR1;
|
||||
pxa_i2s.saimr = SAIMR;
|
||||
pxa_i2s.sadiv = SADIV;
|
||||
|
||||
/* deactivate link */
|
||||
SACR0 &= ~SACR0_ENB;
|
||||
pxa_i2s_wait();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa2xx_i2s_resume(struct snd_soc_dai *dai)
|
||||
{
|
||||
pxa_i2s_wait();
|
||||
|
||||
SACR0 = pxa_i2s.sacr0 & ~SACR0_ENB;
|
||||
SACR1 = pxa_i2s.sacr1;
|
||||
SAIMR = pxa_i2s.saimr;
|
||||
SADIV = pxa_i2s.sadiv;
|
||||
|
||||
SACR0 = pxa_i2s.sacr0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define pxa2xx_i2s_suspend NULL
|
||||
#define pxa2xx_i2s_resume NULL
|
||||
#endif
|
||||
|
||||
static int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
clk_i2s = clk_get(dai->dev, "I2SCLK");
|
||||
if (IS_ERR(clk_i2s))
|
||||
return PTR_ERR(clk_i2s);
|
||||
|
||||
/*
|
||||
* PXA Developer's Manual:
|
||||
* If SACR0[ENB] is toggled in the middle of a normal operation,
|
||||
* the SACR0[RST] bit must also be set and cleared to reset all
|
||||
* I2S controller registers.
|
||||
*/
|
||||
SACR0 = SACR0_RST;
|
||||
SACR0 = 0;
|
||||
/* Make sure RPL and REC are disabled */
|
||||
SACR1 = SACR1_DRPL | SACR1_DREC;
|
||||
/* Along with FIFO servicing */
|
||||
SAIMR &= ~(SAIMR_RFS | SAIMR_TFS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa2xx_i2s_remove(struct snd_soc_dai *dai)
|
||||
{
|
||||
clk_put(clk_i2s);
|
||||
clk_i2s = ERR_PTR(-ENOENT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
|
||||
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
|
||||
|
||||
static const struct snd_soc_dai_ops pxa_i2s_dai_ops = {
|
||||
.startup = pxa2xx_i2s_startup,
|
||||
.shutdown = pxa2xx_i2s_shutdown,
|
||||
.trigger = pxa2xx_i2s_trigger,
|
||||
.hw_params = pxa2xx_i2s_hw_params,
|
||||
.set_fmt = pxa2xx_i2s_set_dai_fmt,
|
||||
.set_sysclk = pxa2xx_i2s_set_dai_sysclk,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver pxa_i2s_dai = {
|
||||
.probe = pxa2xx_i2s_probe,
|
||||
.remove = pxa2xx_i2s_remove,
|
||||
.suspend = pxa2xx_i2s_suspend,
|
||||
.resume = pxa2xx_i2s_resume,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = PXA2XX_I2S_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.capture = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = PXA2XX_I2S_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.ops = &pxa_i2s_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver pxa_i2s_component = {
|
||||
.name = "pxa-i2s",
|
||||
};
|
||||
|
||||
static int pxa2xx_i2s_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_component(&pdev->dev, &pxa_i2s_component,
|
||||
&pxa_i2s_dai, 1);
|
||||
}
|
||||
|
||||
static int pxa2xx_i2s_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver pxa2xx_i2s_driver = {
|
||||
.probe = pxa2xx_i2s_drv_probe,
|
||||
.remove = pxa2xx_i2s_drv_remove,
|
||||
|
||||
.driver = {
|
||||
.name = "pxa2xx-i2s",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init pxa2xx_i2s_init(void)
|
||||
{
|
||||
clk_i2s = ERR_PTR(-ENOENT);
|
||||
return platform_driver_register(&pxa2xx_i2s_driver);
|
||||
}
|
||||
|
||||
static void __exit pxa2xx_i2s_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pxa2xx_i2s_driver);
|
||||
}
|
||||
|
||||
module_init(pxa2xx_i2s_init);
|
||||
module_exit(pxa2xx_i2s_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
|
||||
MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:pxa2xx-i2s");
|
18
sound/soc/pxa/pxa2xx-i2s.h
Normal file
18
sound/soc/pxa/pxa2xx-i2s.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* linux/sound/soc/pxa/pxa2xx-i2s.h
|
||||
*
|
||||
* 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_I2S_H
|
||||
#define _PXA2XX_I2S_H
|
||||
|
||||
/* pxa2xx DAI ID's */
|
||||
#define PXA2XX_DAI_I2S 0
|
||||
|
||||
/* I2S clock */
|
||||
#define PXA2XX_I2S_SYSCLK 0
|
||||
|
||||
#endif
|
158
sound/soc/pxa/pxa2xx-pcm.c
Normal file
158
sound/soc/pxa/pxa2xx-pcm.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: Nov 30, 2004
|
||||
* Copyright: (C) 2004 MontaVista Software, Inc.
|
||||
*
|
||||
* 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/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 "../../arm/pxa2xx-pcm.h"
|
||||
|
||||
static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
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 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)
|
||||
return 0;
|
||||
|
||||
/* this may get called several times by oss emulation
|
||||
* with different params */
|
||||
if (prtd->params == NULL) {
|
||||
prtd->params = dma;
|
||||
ret = pxa_request_dma("name", DMA_PRIO_LOW,
|
||||
pxa2xx_pcm_dma_irq, substream);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
prtd->dma_ch = ret;
|
||||
} else if (prtd->params != dma) {
|
||||
pxa_free_dma(prtd->dma_ch);
|
||||
prtd->params = dma;
|
||||
ret = pxa_request_dma("name", DMA_PRIO_LOW,
|
||||
pxa2xx_pcm_dma_irq, substream);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
prtd->dma_ch = ret;
|
||||
}
|
||||
|
||||
return __pxa2xx_pcm_hw_params(substream, params);
|
||||
}
|
||||
|
||||
static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
|
||||
|
||||
__pxa2xx_pcm_hw_free(substream);
|
||||
|
||||
if (prtd->dma_ch >= 0) {
|
||||
pxa_free_dma(prtd->dma_ch);
|
||||
prtd->dma_ch = -1;
|
||||
prtd->params = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops pxa2xx_pcm_ops = {
|
||||
.open = __pxa2xx_pcm_open,
|
||||
.close = __pxa2xx_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pxa2xx_pcm_hw_params,
|
||||
.hw_free = pxa2xx_pcm_hw_free,
|
||||
.prepare = __pxa2xx_pcm_prepare,
|
||||
.trigger = pxa2xx_pcm_trigger,
|
||||
.pointer = pxa2xx_pcm_pointer,
|
||||
.mmap = pxa2xx_pcm_mmap,
|
||||
};
|
||||
|
||||
static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
int ret;
|
||||
|
||||
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
|
||||
ret = pxa2xx_pcm_preallocate_dma_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
|
||||
ret = pxa2xx_pcm_preallocate_dma_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
static int pxa2xx_soc_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_platform(&pdev->dev, &pxa2xx_soc_platform);
|
||||
}
|
||||
|
||||
static int pxa2xx_soc_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#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");
|
||||
MODULE_LICENSE("GPL");
|
339
sound/soc/pxa/raumfeld.c
Normal file
339
sound/soc/pxa/raumfeld.c
Normal file
|
@ -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");
|
363
sound/soc/pxa/spitz.c
Normal file
363
sound/soc/pxa/spitz.c
Normal file
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
* spitz.c -- SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita
|
||||
*
|
||||
* Copyright 2005 Wolfson Microelectronics PLC.
|
||||
* Copyright 2005 Openedhand Ltd.
|
||||
*
|
||||
* Authors: Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
* Richard Purdie <richard@openedhand.com>
|
||||
*
|
||||
* 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/moduleparam.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <mach/spitz.h>
|
||||
#include "../codecs/wm8750.h"
|
||||
#include "pxa2xx-i2s.h"
|
||||
|
||||
#define SPITZ_HP 0
|
||||
#define SPITZ_MIC 1
|
||||
#define SPITZ_LINE 2
|
||||
#define SPITZ_HEADSET 3
|
||||
#define SPITZ_HP_OFF 4
|
||||
#define SPITZ_SPK_ON 0
|
||||
#define SPITZ_SPK_OFF 1
|
||||
|
||||
/* audio clock in Hz - rounded from 12.235MHz */
|
||||
#define SPITZ_AUDIO_CLOCK 12288000
|
||||
|
||||
static int spitz_jack_func;
|
||||
static int spitz_spk_func;
|
||||
static int spitz_mic_gpio;
|
||||
|
||||
static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
|
||||
{
|
||||
snd_soc_dapm_mutex_lock(dapm);
|
||||
|
||||
if (spitz_spk_func == SPITZ_SPK_ON)
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
|
||||
else
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
|
||||
|
||||
/* set up jack connection */
|
||||
switch (spitz_jack_func) {
|
||||
case SPITZ_HP:
|
||||
/* enable and unmute hp jack, disable mic bias */
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
|
||||
gpio_set_value(SPITZ_GPIO_MUTE_L, 1);
|
||||
gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
|
||||
break;
|
||||
case SPITZ_MIC:
|
||||
/* enable mic jack and bias, mute hp */
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
|
||||
gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
|
||||
gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
|
||||
break;
|
||||
case SPITZ_LINE:
|
||||
/* enable line jack, disable mic bias and mute hp */
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
|
||||
gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
|
||||
gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
|
||||
break;
|
||||
case SPITZ_HEADSET:
|
||||
/* enable and unmute headset jack enable mic bias, mute L hp */
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
|
||||
gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
|
||||
gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
|
||||
break;
|
||||
case SPITZ_HP_OFF:
|
||||
|
||||
/* jack removed, everything off */
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
|
||||
gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
|
||||
gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
snd_soc_dapm_sync_unlocked(dapm);
|
||||
|
||||
snd_soc_dapm_mutex_unlock(dapm);
|
||||
}
|
||||
|
||||
static int spitz_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
||||
/* check the jack status at stream startup */
|
||||
spitz_ext_control(&rtd->card->dapm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spitz_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
unsigned int clk = 0;
|
||||
int ret = 0;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
case 8000:
|
||||
case 16000:
|
||||
case 48000:
|
||||
case 96000:
|
||||
clk = 12288000;
|
||||
break;
|
||||
case 11025:
|
||||
case 22050:
|
||||
case 44100:
|
||||
clk = 11289600;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set the codec system clock for DAC and ADC */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set the I2S system clock as input (unused) */
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops spitz_ops = {
|
||||
.startup = spitz_startup,
|
||||
.hw_params = spitz_hw_params,
|
||||
};
|
||||
|
||||
static int spitz_get_jack(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = spitz_jack_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spitz_set_jack(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (spitz_jack_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
spitz_jack_func = ucontrol->value.integer.value[0];
|
||||
spitz_ext_control(&card->dapm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int spitz_get_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = spitz_spk_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spitz_set_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (spitz_spk_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
spitz_spk_func = ucontrol->value.integer.value[0];
|
||||
spitz_ext_control(&card->dapm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* spitz machine dapm widgets */
|
||||
static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
|
||||
SND_SOC_DAPM_SPK("Ext Spk", NULL),
|
||||
SND_SOC_DAPM_LINE("Line Jack", NULL),
|
||||
|
||||
/* headset is a mic and mono headphone */
|
||||
SND_SOC_DAPM_HP("Headset Jack", NULL),
|
||||
};
|
||||
|
||||
/* Spitz machine audio_map */
|
||||
static const struct snd_soc_dapm_route spitz_audio_map[] = {
|
||||
|
||||
/* headphone connected to LOUT1, ROUT1 */
|
||||
{"Headphone Jack", NULL, "LOUT1"},
|
||||
{"Headphone Jack", NULL, "ROUT1"},
|
||||
|
||||
/* headset connected to ROUT1 and LINPUT1 with bias (def below) */
|
||||
{"Headset Jack", NULL, "ROUT1"},
|
||||
|
||||
/* ext speaker connected to LOUT2, ROUT2 */
|
||||
{"Ext Spk", NULL , "ROUT2"},
|
||||
{"Ext Spk", NULL , "LOUT2"},
|
||||
|
||||
/* mic is connected to input 1 - with bias */
|
||||
{"LINPUT1", NULL, "Mic Bias"},
|
||||
{"Mic Bias", NULL, "Mic Jack"},
|
||||
|
||||
/* line is connected to input 1 - no bias */
|
||||
{"LINPUT1", NULL, "Line Jack"},
|
||||
};
|
||||
|
||||
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
|
||||
"Off"};
|
||||
static const char *spk_function[] = {"On", "Off"};
|
||||
static const struct soc_enum spitz_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(5, jack_function),
|
||||
SOC_ENUM_SINGLE_EXT(2, spk_function),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new wm8750_spitz_controls[] = {
|
||||
SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack,
|
||||
spitz_set_jack),
|
||||
SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk,
|
||||
spitz_set_spk),
|
||||
};
|
||||
|
||||
/*
|
||||
* Logic for a wm8750 as connected on a Sharp SL-Cxx00 Device
|
||||
*/
|
||||
static int spitz_wm8750_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, "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");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* spitz digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link spitz_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 = 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",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = &spitz_dai,
|
||||
.num_links = 1,
|
||||
|
||||
.controls = wm8750_spitz_controls,
|
||||
.num_controls = ARRAY_SIZE(wm8750_spitz_controls),
|
||||
.dapm_widgets = wm8750_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
|
||||
.dapm_routes = spitz_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(spitz_audio_map),
|
||||
};
|
||||
|
||||
static struct platform_device *spitz_snd_device;
|
||||
|
||||
static int __init spitz_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
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) {
|
||||
ret = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
platform_set_drvdata(spitz_snd_device, &snd_soc_spitz);
|
||||
|
||||
ret = platform_device_add(spitz_snd_device);
|
||||
if (ret)
|
||||
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);
|
||||
module_exit(spitz_exit);
|
||||
|
||||
MODULE_AUTHOR("Richard Purdie");
|
||||
MODULE_DESCRIPTION("ALSA SoC Spitz");
|
||||
MODULE_LICENSE("GPL");
|
281
sound/soc/pxa/tosa.c
Normal file
281
sound/soc/pxa/tosa.c
Normal file
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* tosa.c -- SoC audio for Tosa
|
||||
*
|
||||
* Copyright 2005 Wolfson Microelectronics PLC.
|
||||
* Copyright 2005 Openedhand Ltd.
|
||||
*
|
||||
* Authors: Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
* Richard Purdie <richard@openedhand.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* GPIO's
|
||||
* 1 - Jack Insertion
|
||||
* 5 - Hookswitch (headset answer/hang up switch)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <mach/tosa.h>
|
||||
#include <mach/audio.h>
|
||||
|
||||
#include "../codecs/wm9712.h"
|
||||
#include "pxa2xx-ac97.h"
|
||||
|
||||
#define TOSA_HP 0
|
||||
#define TOSA_MIC_INT 1
|
||||
#define TOSA_HEADSET 2
|
||||
#define TOSA_HP_OFF 3
|
||||
#define TOSA_SPK_ON 0
|
||||
#define TOSA_SPK_OFF 1
|
||||
|
||||
static int tosa_jack_func;
|
||||
static int tosa_spk_func;
|
||||
|
||||
static void tosa_ext_control(struct snd_soc_dapm_context *dapm)
|
||||
{
|
||||
|
||||
snd_soc_dapm_mutex_lock(dapm);
|
||||
|
||||
/* set up jack connection */
|
||||
switch (tosa_jack_func) {
|
||||
case TOSA_HP:
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)");
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
|
||||
break;
|
||||
case TOSA_MIC_INT:
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Mic (Internal)");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
|
||||
break;
|
||||
case TOSA_HEADSET:
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)");
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
|
||||
break;
|
||||
}
|
||||
|
||||
if (tosa_spk_func == TOSA_SPK_ON)
|
||||
snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
|
||||
else
|
||||
snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
|
||||
|
||||
snd_soc_dapm_sync_unlocked(dapm);
|
||||
|
||||
snd_soc_dapm_mutex_unlock(dapm);
|
||||
}
|
||||
|
||||
static int tosa_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
||||
/* check the jack status at stream startup */
|
||||
tosa_ext_control(&rtd->card->dapm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops tosa_ops = {
|
||||
.startup = tosa_startup,
|
||||
};
|
||||
|
||||
static int tosa_get_jack(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = tosa_jack_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tosa_set_jack(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (tosa_jack_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
tosa_jack_func = ucontrol->value.integer.value[0];
|
||||
tosa_ext_control(&card->dapm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tosa_get_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = tosa_spk_func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tosa_set_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (tosa_spk_func == ucontrol->value.integer.value[0])
|
||||
return 0;
|
||||
|
||||
tosa_spk_func = ucontrol->value.integer.value[0];
|
||||
tosa_ext_control(&card->dapm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* tosa dapm event handlers */
|
||||
static int tosa_hp_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
gpio_set_value(TOSA_GPIO_L_MUTE, SND_SOC_DAPM_EVENT_ON(event) ? 1 :0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* tosa machine dapm widgets */
|
||||
static const struct snd_soc_dapm_widget tosa_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event),
|
||||
SND_SOC_DAPM_HP("Headset Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
|
||||
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||
};
|
||||
|
||||
/* tosa audio map */
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
|
||||
/* headphone connected to HPOUTL, HPOUTR */
|
||||
{"Headphone Jack", NULL, "HPOUTL"},
|
||||
{"Headphone Jack", NULL, "HPOUTR"},
|
||||
|
||||
/* ext speaker connected to LOUT2, ROUT2 */
|
||||
{"Speaker", NULL, "LOUT2"},
|
||||
{"Speaker", NULL, "ROUT2"},
|
||||
|
||||
/* internal mic is connected to mic1, mic2 differential - with bias */
|
||||
{"MIC1", NULL, "Mic Bias"},
|
||||
{"MIC2", NULL, "Mic Bias"},
|
||||
{"Mic Bias", NULL, "Mic (Internal)"},
|
||||
|
||||
/* headset is connected to HPOUTR, and LINEINR with bias */
|
||||
{"Headset Jack", NULL, "HPOUTR"},
|
||||
{"LINEINR", NULL, "Mic Bias"},
|
||||
{"Mic Bias", NULL, "Headset Jack"},
|
||||
};
|
||||
|
||||
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
|
||||
"Off"};
|
||||
static const char *spk_function[] = {"On", "Off"};
|
||||
static const struct soc_enum tosa_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(5, jack_function),
|
||||
SOC_ENUM_SINGLE_EXT(2, spk_function),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new tosa_controls[] = {
|
||||
SOC_ENUM_EXT("Jack Function", tosa_enum[0], tosa_get_jack,
|
||||
tosa_set_jack),
|
||||
SOC_ENUM_EXT("Speaker Function", tosa_enum[1], tosa_get_spk,
|
||||
tosa_set_spk),
|
||||
};
|
||||
|
||||
static int tosa_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, "OUT3");
|
||||
snd_soc_dapm_nc_pin(dapm, "MONOOUT");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_link tosa_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",
|
||||
.init = tosa_ac97_init,
|
||||
.ops = &tosa_ops,
|
||||
},
|
||||
{
|
||||
.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",
|
||||
.ops = &tosa_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card tosa = {
|
||||
.name = "Tosa",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = tosa_dai,
|
||||
.num_links = ARRAY_SIZE(tosa_dai),
|
||||
|
||||
.controls = tosa_controls,
|
||||
.num_controls = ARRAY_SIZE(tosa_controls),
|
||||
.dapm_widgets = tosa_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
|
||||
.dapm_routes = audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
||||
};
|
||||
|
||||
static int tosa_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = ⤩
|
||||
int ret;
|
||||
|
||||
ret = gpio_request_one(TOSA_GPIO_L_MUTE, GPIOF_OUT_INIT_LOW,
|
||||
"Headphone Jack");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
|
||||
ret = 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 int tosa_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
|
||||
gpio_free(TOSA_GPIO_L_MUTE);
|
||||
snd_soc_unregister_card(card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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");
|
171
sound/soc/pxa/ttc-dkb.c
Normal file
171
sound/soc/pxa/ttc-dkb.c
Normal file
|
@ -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");
|
231
sound/soc/pxa/z2.c
Normal file
231
sound/soc/pxa/z2.c
Normal file
|
@ -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");
|
283
sound/soc/pxa/zylonite.c
Normal file
283
sound/soc/pxa/zylonite.c
Normal file
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* zylonite.c -- SoC audio for Zylonite
|
||||
*
|
||||
* Copyright 2008 Wolfson Microelectronics PLC.
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "../codecs/wm9713.h"
|
||||
#include "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[] = {
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Microphone", NULL),
|
||||
SND_SOC_DAPM_MIC("Handset Microphone", NULL),
|
||||
SND_SOC_DAPM_SPK("Multiactor", NULL),
|
||||
SND_SOC_DAPM_SPK("Headset Earpiece", NULL),
|
||||
};
|
||||
|
||||
/* Currently supported audio map */
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
|
||||
/* Headphone output connected to HPL/HPR */
|
||||
{ "Headphone", NULL, "HPL" },
|
||||
{ "Headphone", NULL, "HPR" },
|
||||
|
||||
/* On-board earpiece */
|
||||
{ "Headset Earpiece", NULL, "OUT3" },
|
||||
|
||||
/* Headphone mic */
|
||||
{ "MIC2A", NULL, "Mic Bias" },
|
||||
{ "Mic Bias", NULL, "Headset Microphone" },
|
||||
|
||||
/* On-board mic */
|
||||
{ "MIC1", NULL, "Mic Bias" },
|
||||
{ "Mic Bias", NULL, "Handset Microphone" },
|
||||
|
||||
/* Multiactor differentially connected over SPKL/SPKR */
|
||||
{ "Multiactor", NULL, "SPKL" },
|
||||
{ "Multiactor", NULL, "SPKR" },
|
||||
};
|
||||
|
||||
static int zylonite_wm9713_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
if (clk_pout)
|
||||
snd_soc_dai_set_pll(rtd->codec_dai, 0, 0,
|
||||
clk_get_rate(pout), 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
unsigned int pll_out = 0;
|
||||
unsigned int wm9713_div = 0;
|
||||
int ret = 0;
|
||||
int rate = params_rate(params);
|
||||
int width = snd_pcm_format_physical_width(params_format(params));
|
||||
|
||||
/* Only support ratios that we can generate neatly from the AC97
|
||||
* based master clock - in particular, this excludes 44.1kHz.
|
||||
* In most applications the voice DAC will be used for telephony
|
||||
* data so multiples of 8kHz will be the common case.
|
||||
*/
|
||||
switch (rate) {
|
||||
case 8000:
|
||||
wm9713_div = 12;
|
||||
break;
|
||||
case 16000:
|
||||
wm9713_div = 6;
|
||||
break;
|
||||
case 48000:
|
||||
wm9713_div = 2;
|
||||
break;
|
||||
default:
|
||||
/* Don't support OSS emulation */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Add 1 to the width for the leading clock cycle */
|
||||
pll_out = rate * (width + 1) * 8;
|
||||
|
||||
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, 0, pll_out);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (clk_pout)
|
||||
ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_PLL_DIV,
|
||||
WM9713_PCMDIV(wm9713_div));
|
||||
else
|
||||
ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV,
|
||||
WM9713_PCMDIV(wm9713_div));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops zylonite_voice_ops = {
|
||||
.hw_params = zylonite_voice_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link zylonite_dai[] = {
|
||||
{
|
||||
.name = "AC97",
|
||||
.stream_name = "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",
|
||||
.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",
|
||||
.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",
|
||||
.owner = THIS_MODULE,
|
||||
.probe = &zylonite_probe,
|
||||
.remove = &zylonite_remove,
|
||||
.suspend_post = &zylonite_suspend_post,
|
||||
.resume_pre = &zylonite_resume_pre,
|
||||
.dai_link = zylonite_dai,
|
||||
.num_links = ARRAY_SIZE(zylonite_dai),
|
||||
|
||||
.dapm_widgets = zylonite_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(zylonite_dapm_widgets),
|
||||
.dapm_routes = audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
||||
};
|
||||
|
||||
static struct platform_device *zylonite_snd_ac97_device;
|
||||
|
||||
static int __init zylonite_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
zylonite_snd_ac97_device = platform_device_alloc("soc-audio", -1);
|
||||
if (!zylonite_snd_ac97_device)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(zylonite_snd_ac97_device, &zylonite);
|
||||
|
||||
ret = platform_device_add(zylonite_snd_ac97_device);
|
||||
if (ret != 0)
|
||||
platform_device_put(zylonite_snd_ac97_device);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit zylonite_exit(void)
|
||||
{
|
||||
platform_device_unregister(zylonite_snd_ac97_device);
|
||||
}
|
||||
|
||||
module_init(zylonite_init);
|
||||
module_exit(zylonite_exit);
|
||||
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
MODULE_DESCRIPTION("ALSA SoC WM9713 Zylonite");
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Add table
Add a link
Reference in a new issue