ASoC: Updates for v3.11

Not a big release subsystem wise, the main changes have been some nice
 improvements on the driver side:
 
 - Lots of cleanups and fixes for Blackfin, SGTL5000 and UX500.
 - Generalisation of the Bluetooth and HDMI stub drivers.
 - New CODEC drivers for SSM2518 and RT5640.
 - New machine driver for Tegra CPUs with RT5640.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIcBAABAgAGBQJRvz1tAAoJELSic+t+oim9x20QAIswcfaLkogh6AEhrP+ayzRA
 xuwrROZoaoHXCoD1xC/YkFYDbYWp69ms3JR5oYrplOpq1h5+jkmbAcsq2mllLL7y
 02uQP73acysze6YKB00wG/khAxmqTLENbiOLbQ2w+X7Ao6Gh0v/BRmNU34OSpj6R
 VDp9eNdLBJ4iZQ8m+S8NTn2bdxtqlRDVNP6aC6L8kxt2khuGTa3acGyFcer8hPwx
 WZFsP9lt5J+8rrZ6l3OH3ZpbldJRB+63ff5O+q0s5KihSfWEeVy3NgK7AG7chG4a
 mffoqymgrtjJDosuhNIwU0mjFLWdFoPI7byTi9Hd5WBJ+d3d1uNr1iFxD7+c+fWd
 Xr5PimssX983D9dbJN3yikzhQt7A2TK9OUCHXdWlLf2fCeYH5PwLySqaDPvmayvh
 5UccHo2j7sDcrztazCFqcfbSdEW/VA5mlHexz1xW9eGDWJncHDYdFPxDp45rwno4
 ZjilEOHNP/1HJl+Nl0D8HArYsk/svLY7t5SpITDjAMPHcCSND4ZH+WknnCFMl2sq
 R67LaeW4UHFUJk7fZHx3a3Gr90OsEC1yb0NaJJ59e8o8fV4e47wNp/yUjCULvSuP
 9136KjBQEofyR5xDiBM6HudFT1wEwpIva5YQoKKZgjqX2sYhwjH5SwfsSd0r25Mh
 PFKtvO1/U63w/AJyn/sM
 =gWf9
 -----END PGP SIGNATURE-----

Merge tag 'asoc-v3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next

ASoC: Updates for v3.11

Not a big release subsystem wise, the main changes have been some nice
improvements on the driver side:

- Lots of cleanups and fixes for Blackfin, SGTL5000 and UX500.
- Generalisation of the Bluetooth and HDMI stub drivers.
- New CODEC drivers for SSM2518 and RT5640.
- New machine driver for Tegra CPUs with RT5640.
This commit is contained in:
Takashi Iwai 2013-06-21 17:52:58 +02:00
commit 0a1801e26c
157 changed files with 8800 additions and 3067 deletions

View file

@ -0,0 +1,62 @@
Wolfson Arizona class audio SoCs
These devices are audio SoCs with extensive digital capabilites and a range
of analogue I/O.
Required properties:
- compatible : one of the following chip-specific strings:
"wlf,wm5102"
"wlf,wm5110"
- reg : I2C slave address when connected using I2C, chip select number when
using SPI.
- interrupts : The interrupt line the /IRQ signal for the device is
connected to.
- interrupt-controller : Arizona class devices contain interrupt controllers
and may provide interrupt services to other devices.
- interrupt-parent : The parent interrupt controller.
- #interrupt-cells: the number of cells to describe an IRQ, this should be 2.
The first cell is the IRQ number.
The second cell is the flags, encoded as the trigger masks from
Documentation/devicetree/bindings/interrupts.txt
- gpio-controller : Indicates this device is a GPIO controller.
- #gpio-cells : Must be 2. The first cell is the pin number and the
second cell is used to specify optional parameters (currently unused).
- AVDD1-supply, DBVDD1-supply, DBVDD2-supply, DBVDD3-supply, CPVDD-supply,
SPKVDDL-supply, SPKVDDR-supply : power supplies for the device, as covered
in Documentation/devicetree/bindings/regulator/regulator.txt
Optional properties:
- wlf,reset : GPIO specifier for the GPIO controlling /RESET
- wlf,ldoena : GPIO specifier for the GPIO controlling LDOENA
- wlf,gpio-defaults : A list of GPIO configuration register values. If
absent, no configuration of these registers is performed. If any
entry has a value that is out of range for a 16 bit register then
the chip default will be used. If present exactly five values must
be specified.
Example:
codec: wm5102@1a {
compatible = "wlf,wm5102";
reg = <0x1a>;
interrupts = <347>;
#interrupt-cells = <2>;
interrupt-parent = <&gic>;
gpio-controller;
#gpio-cells = <2>;
wlf,gpio-defaults = <
0x00000000, /* AIF1TXLRCLK */
0xffffffff,
0xffffffff,
0xffffffff,
0xffffffff,
>;
};

View file

@ -0,0 +1,23 @@
Analog Devices ADAU1701
Required properties:
- compatible: Should contain "adi,adau1701"
- reg: The i2c address. Value depends on the state of ADDR0
and ADDR1, as wired in hardware.
Optional properties:
- reset-gpio: A GPIO spec to define which pin is connected to the
chip's !RESET pin. If specified, the driver will
assert a hardware reset at probe time.
Examples:
i2c_bus {
adau1701@34 {
compatible = "adi,adau1701";
reg = <0x34>;
reset-gpio = <&gpio 23 0>;
};
};

View file

@ -0,0 +1,46 @@
Freescale i.MX audio complex with WM8962 codec
Required properties:
- compatible : "fsl,imx-audio-wm8962"
- model : The user-visible name of this sound complex
- ssi-controller : The phandle of the i.MX SSI controller
- audio-codec : The phandle of the WM8962 audio codec
- audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
the second being the connection's source. Valid names could be power
supplies, WM8962 pins, and the jacks on the board:
Power supplies:
* Mic Bias
Board connectors:
* Mic Jack
* Headphone Jack
* Ext Spk
- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
- mux-ext-port : The external port of the i.MX audio muxer
Note: The AUDMUX port numbering should start at 1, which is consistent with
hardware manual.
Example:
sound {
compatible = "fsl,imx6q-sabresd-wm8962",
"fsl,imx-audio-wm8962";
model = "wm8962-audio";
ssi-controller = <&ssi2>;
audio-codec = <&codec>;
audio-routing =
"Headphone Jack", "HPOUTL",
"Headphone Jack", "HPOUTR",
"Ext Spk", "SPKOUTL",
"Ext Spk", "SPKOUTR",
"MICBIAS", "AMIC",
"IN3R", "MICBIAS",
"DMIC", "MICBIAS",
"DMICDAT", "DMIC";
mux-int-port = <2>;
mux-ext-port = <3>;
};

View file

@ -3,8 +3,11 @@
Required properties:
- compatible: Should be "fsl,<chip>-saif"
- reg: Should contain registers location and length
- interrupts: Should contain ERROR and DMA interrupts
- fsl,saif-dma-channel: APBX DMA channel for the SAIF
- interrupts: Should contain ERROR interrupt number
- dmas: DMA specifier, consisting of a phandle to DMA controller node
and SAIF DMA channel ID.
Refer to dma.txt and fsl-mxs-dma.txt for details.
- dma-names: Must be "rx-tx".
Optional properties:
- fsl,saif-master: phandle to the master SAIF. It's only required for
@ -23,14 +26,16 @@ aliases {
saif0: saif@80042000 {
compatible = "fsl,imx28-saif";
reg = <0x80042000 2000>;
interrupts = <59 80>;
fsl,saif-dma-channel = <4>;
interrupts = <59>;
dmas = <&dma_apbx 4>;
dma-names = "rx-tx";
};
saif1: saif@80046000 {
compatible = "fsl,imx28-saif";
reg = <0x80046000 2000>;
interrupts = <58 81>;
fsl,saif-dma-channel = <5>;
interrupts = <58>;
dmas = <&dma_apbx 5>;
dma-names = "rx-tx";
fsl,saif-master = <&saif0>;
};

View file

@ -0,0 +1,71 @@
NVIDIA Tegra audio complex, with RT5640 CODEC
Required properties:
- compatible : "nvidia,tegra-audio-rt5640"
- clocks : Must contain an entry for each entry in clock-names.
- clock-names : Must include the following entries:
"pll_a" (The Tegra clock of that name),
"pll_a_out0" (The Tegra clock of that name),
"mclk" (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk)
- nvidia,model : The user-visible name of this sound complex.
- nvidia,audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
the second being the connection's source. Valid names for sources and
sinks are the RT5640's pins, and the jacks on the board:
RT5640 pins:
* DMIC1
* DMIC2
* MICBIAS1
* IN1P
* IN1R
* IN2P
* IN2R
* HPOL
* HPOR
* LOUTL
* LOUTR
* MONOP
* MONON
* SPOLP
* SPOLN
* SPORP
* SPORN
Board connectors:
* Headphones
* Speakers
- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's
connected to the CODEC.
- nvidia,audio-codec : The phandle of the RT5640 audio codec. This binding
assumes that AIF1 on the CODEC is connected to Tegra.
Optional properties:
- nvidia,hp-det-gpios : The GPIO that detects headphones are plugged in
Example:
sound {
compatible = "nvidia,tegra-audio-rt5640-dalmore",
"nvidia,tegra-audio-rt5640";
nvidia,model = "NVIDIA Tegra Dalmore";
nvidia,audio-routing =
"Headphones", "HPOR",
"Headphones", "HPOL",
"Speakers", "SPORP",
"Speakers", "SPORN",
"Speakers", "SPOLP",
"Speakers", "SPOLN";
nvidia,i2s-controller = <&tegra_i2s1>;
nvidia,audio-codec = <&rt5640>;
nvidia,hp-det-gpios = <&gpio 143 0>; /* GPIO PR7 */
clocks = <&tegra_car 216>, <&tegra_car 217>, <&tegra_car 120>;
clock-names = "pll_a", "pll_a_out0", "mclk";
};

View file

@ -0,0 +1,30 @@
RT5640 audio CODEC
This device supports I2C only.
Required properties:
- compatible : "realtek,rt5640".
- reg : The I2C address of the device.
- interrupts : The CODEC's interrupt output.
Optional properties:
- realtek,in1-differential
- realtek,in2-differential
Boolean. Indicate MIC1/2 input are differential, rather than single-ended.
- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
Example:
rt5640 {
compatible = "realtek,rt5640";
reg = <0x1c>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(W, 3) GPIO_ACTIVE_HIGH>;
realtek,ldo1-en-gpios =
<&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
};

View file

@ -5,9 +5,12 @@ Required properties:
- reg : the I2C address of the device
- clocks : the clock provider of SYS_MCLK
Example:
codec: sgtl5000@0a {
compatible = "fsl,sgtl5000";
reg = <0x0a>;
clocks = <&clks 150>;
};

View file

@ -0,0 +1,10 @@
Device-Tree bindings for dummy spdif receiver
Required properties:
- compatible: should be "linux,spdif-dir".
Example node:
codec: spdif-receiver {
compatible = "linux,spdif-dir";
};

View file

@ -0,0 +1,10 @@
Device-Tree bindings for dummy spdif transmitter
Required properties:
- compatible: should be "linux,spdif-dit".
Example node:
codec: spdif-transmitter {
compatible = "linux,spdif-dit";
};

View file

@ -0,0 +1,20 @@
SSM2518 audio amplifier
This device supports I2C only.
Required properties:
- compatible : Must be "adi,ssm2518"
- reg : the I2C address of the device. This will either be 0x34 (ADDR pin low)
or 0x35 (ADDR pin high)
Optional properties:
- gpios : GPIO connected to the nSD pin. If the property is not present it is
assumed that the nSD pin is hardwired to always on.
Example:
ssm2518: ssm2518@34 {
compatible = "adi,ssm2518";
reg = <0x34>;
gpios = <&gpio 5 0>;
};

View file

@ -8,9 +8,32 @@ Required properties:
- reg : the I2C address of the device.
Optional properties:
- spk-mono: This is a boolean property. If present, the SPK_MONO bit
of R51 (Class D Control 2) gets set, indicating that the speaker is
in mono mode.
- mic-cfg : Default register value for R48 (Additional Control 4).
If absent, the default should be the register default.
- gpio-cfg : A list of GPIO configuration register values. The list must
be 6 entries long. If absent, no configuration of these registers is
performed. And note that only the value within [0x0, 0xffff] is valid.
Any other value is regarded as setting the GPIO register by its reset
value 0x0.
Example:
codec: wm8962@1a {
compatible = "wlf,wm8962";
reg = <0x1a>;
gpio-cfg = <
0x0000 /* 0:Default */
0x0000 /* 1:Default */
0x0013 /* 2:FN_DMICCLK */
0x0000 /* 3:Default */
0x8014 /* 4:FN_DMICCDAT */
0x0000 /* 5:Default */
>;
};

View file

@ -283,14 +283,6 @@ static struct platform_device bfin_i2s = {
};
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
static struct platform_device bfin_tdm = {
.name = "bfin-tdm",
.id = CONFIG_SND_BF5XX_SPORT_NUM,
/* TODO: add platform data here */
};
#endif
static struct spi_board_info bfin_spi_board_info[] __initdata = {
#if defined(CONFIG_MTD_M25P80) \
|| defined(CONFIG_MTD_M25P80_MODULE)
@ -800,10 +792,6 @@ static struct platform_device *stamp_devices[] __initdata = {
#if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE)
&bfin_i2s,
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
&bfin_tdm,
#endif
};
static int __init ad7160eval_init(void)

View file

@ -493,8 +493,7 @@ static const struct ad7879_platform_data bfin_ad7879_ts_info = {
};
#endif
#if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE) || \
defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
#if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE)
static const u16 bfin_snd_pin[][7] = {
{P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
@ -549,13 +548,6 @@ static struct platform_device bfin_i2s_pcm = {
};
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
static struct platform_device bfin_tdm_pcm = {
.name = "bfin-tdm-pcm-audio",
.id = -1,
};
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
static struct platform_device bfin_ac97_pcm = {
.name = "bfin-ac97-pcm-audio",
@ -575,22 +567,10 @@ static struct platform_device bfin_i2s = {
};
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
static struct platform_device bfin_tdm = {
.name = "bfin-tdm",
.id = CONFIG_SND_BF5XX_SPORT_NUM,
.num_resources = ARRAY_SIZE(bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM]),
.resource = bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM],
.dev = {
.platform_data = &bfin_snd_data[CONFIG_SND_BF5XX_SPORT_NUM],
},
};
#endif
#if defined(CONFIG_SND_BF5XX_SOC_AD1836) \
|| defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
static const char * const ad1836_link[] = {
"bfin-tdm.0",
"bfin-i2s.0",
"spi0.4",
};
static struct platform_device bfin_ad1836_machine = {
@ -1269,10 +1249,6 @@ static struct platform_device *stamp_devices[] __initdata = {
&bfin_i2s_pcm,
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
&bfin_tdm_pcm,
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
&bfin_ac97_pcm,
#endif
@ -1281,10 +1257,6 @@ static struct platform_device *stamp_devices[] __initdata = {
&bfin_i2s,
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
&bfin_tdm,
#endif
#if defined(CONFIG_SND_BF5XX_SOC_AD1836) || \
defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
&bfin_ad1836_machine,

View file

@ -450,14 +450,6 @@ static struct platform_device bfin_i2s = {
};
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
static struct platform_device bfin_tdm = {
.name = "bfin-tdm",
.id = CONFIG_SND_BF5XX_SPORT_NUM,
/* TODO: add platform data here */
};
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
static struct platform_device bfin_ac97 = {
.name = "bfin-ac97",
@ -516,10 +508,6 @@ static struct platform_device *ezkit_devices[] __initdata = {
&bfin_i2s,
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
&bfin_tdm,
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
&bfin_ac97,
#endif

View file

@ -542,8 +542,7 @@ static struct platform_device bfin_dpmc = {
};
#if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE) || \
defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) \
|| defined(CONFIG_SND_BF5XX_AC97) || \
defined(CONFIG_SND_BF5XX_AC97) || \
defined(CONFIG_SND_BF5XX_AC97_MODULE)
#include <asm/bfin_sport.h>
@ -603,13 +602,6 @@ static struct platform_device bfin_i2s_pcm = {
};
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
static struct platform_device bfin_tdm_pcm = {
.name = "bfin-tdm-pcm-audio",
.id = -1,
};
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
static struct platform_device bfin_ac97_pcm = {
.name = "bfin-ac97-pcm-audio",
@ -620,7 +612,7 @@ static struct platform_device bfin_ac97_pcm = {
#if defined(CONFIG_SND_BF5XX_SOC_AD1836) \
|| defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
static const char * const ad1836_link[] = {
"bfin-tdm.0",
"bfin-i2s.0",
"spi0.4",
};
static struct platform_device bfin_ad1836_machine = {
@ -675,20 +667,6 @@ static struct platform_device bfin_i2s = {
};
#endif
#if defined(CONFIG_SND_BF5XX_SOC_TDM) || \
defined(CONFIG_SND_BF5XX_SOC_TDM_MODULE)
static struct platform_device bfin_tdm = {
.name = "bfin-tdm",
.id = CONFIG_SND_BF5XX_SPORT_NUM,
.num_resources =
ARRAY_SIZE(bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM]),
.resource = bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM],
.dev = {
.platform_data = &bfin_snd_data[CONFIG_SND_BF5XX_SPORT_NUM],
},
};
#endif
#if defined(CONFIG_SND_BF5XX_SOC_AC97) || \
defined(CONFIG_SND_BF5XX_SOC_AC97_MODULE)
static struct platform_device bfin_ac97 = {
@ -761,10 +739,6 @@ static struct platform_device *stamp_devices[] __initdata = {
&bfin_i2s_pcm,
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
&bfin_tdm_pcm,
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
&bfin_ac97_pcm,
#endif
@ -792,11 +766,6 @@ static struct platform_device *stamp_devices[] __initdata = {
&bfin_i2s,
#endif
#if defined(CONFIG_SND_BF5XX_SOC_TDM) || \
defined(CONFIG_SND_BF5XX_SOC_TDM_MODULE)
&bfin_tdm,
#endif
#if defined(CONFIG_SND_BF5XX_SOC_AC97) || \
defined(CONFIG_SND_BF5XX_SOC_AC97_MODULE)
&bfin_ac97,

View file

@ -2570,7 +2570,6 @@ static struct platform_device bfin_dpmc = {
};
#if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE) || \
defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) || \
defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
#define SPORT_REQ(x) \
@ -2628,13 +2627,6 @@ static struct platform_device bfin_i2s_pcm = {
};
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
static struct platform_device bfin_tdm_pcm = {
.name = "bfin-tdm-pcm-audio",
.id = -1,
};
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
static struct platform_device bfin_ac97_pcm = {
.name = "bfin-ac97-pcm-audio",
@ -2645,7 +2637,7 @@ static struct platform_device bfin_ac97_pcm = {
#if defined(CONFIG_SND_BF5XX_SOC_AD1836) \
|| defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
static const char * const ad1836_link[] = {
"bfin-tdm.0",
"bfin-i2s.0",
"spi0.4",
};
static struct platform_device bfin_ad1836_machine = {
@ -2699,18 +2691,6 @@ static struct platform_device bfin_i2s = {
};
#endif
#if defined(CONFIG_SND_BF5XX_SOC_TDM) || defined(CONFIG_SND_BF5XX_SOC_TDM_MODULE)
static struct platform_device bfin_tdm = {
.name = "bfin-tdm",
.id = CONFIG_SND_BF5XX_SPORT_NUM,
.num_resources = ARRAY_SIZE(bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM]),
.resource = bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM],
.dev = {
.platform_data = &bfin_snd_data[CONFIG_SND_BF5XX_SPORT_NUM],
},
};
#endif
#if defined(CONFIG_SND_BF5XX_SOC_AC97) || defined(CONFIG_SND_BF5XX_SOC_AC97_MODULE)
static struct platform_device bfin_ac97 = {
.name = "bfin-ac97",
@ -2935,10 +2915,6 @@ static struct platform_device *stamp_devices[] __initdata = {
&bfin_i2s_pcm,
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
&bfin_tdm_pcm,
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
&bfin_ac97_pcm,
#endif
@ -2961,10 +2937,6 @@ static struct platform_device *stamp_devices[] __initdata = {
&bfin_i2s,
#endif
#if defined(CONFIG_SND_BF5XX_SOC_TDM) || defined(CONFIG_SND_BF5XX_SOC_TDM_MODULE)
&bfin_tdm,
#endif
#if defined(CONFIG_SND_BF5XX_SOC_AC97) || defined(CONFIG_SND_BF5XX_SOC_AC97_MODULE)
&bfin_ac97,
#endif

View file

@ -1393,7 +1393,6 @@ static struct platform_device bfin_dpmc = {
};
#if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE) || \
defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) || \
defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
#define SPORT_REQ(x) \
@ -1461,13 +1460,6 @@ static struct platform_device bfin_i2s_pcm = {
};
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
static struct platform_device bfin_tdm_pcm = {
.name = "bfin-tdm-pcm-audio",
.id = -1,
};
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
static struct platform_device bfin_ac97_pcm = {
.name = "bfin-ac97-pcm-audio",
@ -1501,18 +1493,6 @@ static struct platform_device bfin_i2s = {
};
#endif
#if defined(CONFIG_SND_BF5XX_SOC_TDM) || defined(CONFIG_SND_BF5XX_SOC_TDM_MODULE)
static struct platform_device bfin_tdm = {
.name = "bfin-tdm",
.id = CONFIG_SND_BF5XX_SPORT_NUM,
.num_resources = ARRAY_SIZE(bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM]),
.resource = bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM],
.dev = {
.platform_data = &bfin_snd_data[CONFIG_SND_BF5XX_SPORT_NUM],
},
};
#endif
#if defined(CONFIG_SND_BF5XX_SOC_AC97) || defined(CONFIG_SND_BF5XX_SOC_AC97_MODULE)
static struct platform_device bfin_ac97 = {
.name = "bfin-ac97",
@ -1646,9 +1626,7 @@ static struct platform_device *ezkit_devices[] __initdata = {
#if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE)
&bfin_i2s_pcm,
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
&bfin_tdm_pcm,
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
&bfin_ac97_pcm,
#endif
@ -1661,10 +1639,6 @@ static struct platform_device *ezkit_devices[] __initdata = {
&bfin_i2s,
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
&bfin_tdm,
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
&bfin_ac97,
#endif

View file

@ -523,14 +523,6 @@ static struct platform_device bfin_i2s = {
};
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
static struct platform_device bfin_tdm = {
.name = "bfin-tdm",
.id = CONFIG_SND_BF5XX_SPORT_NUM,
/* TODO: add platform data here */
};
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
static struct platform_device bfin_ac97 = {
.name = "bfin-ac97",
@ -542,7 +534,7 @@ static struct platform_device bfin_ac97 = {
#if defined(CONFIG_SND_BF5XX_SOC_AD1836) \
|| defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
static const char * const ad1836_link[] = {
"bfin-tdm.0",
"bfin-i2s.0",
"spi0.4",
};
static struct platform_device bfin_ad1836_machine = {
@ -611,10 +603,6 @@ static struct platform_device *ezkit_devices[] __initdata = {
&bfin_i2s,
#endif
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
&bfin_tdm,
#endif
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
&bfin_ac97,
#endif

View file

@ -821,7 +821,7 @@ static struct platform_device bfin_i2s = {
#if defined(CONFIG_SND_BF5XX_SOC_AD1836) \
|| defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
static const char * const ad1836_link[] = {
"bfin-tdm.0",
"bfin-i2s.0",
"spi0.76",
};
static struct platform_device bfin_ad1836_machine = {

View file

@ -16,9 +16,13 @@
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/machine.h>
#include <linux/slab.h>
#include <linux/mfd/arizona/core.h>
@ -344,6 +348,17 @@ static int arizona_runtime_resume(struct device *dev)
switch (arizona->type) {
case WM5102:
if (arizona->external_dcvdd) {
ret = regmap_update_bits(arizona->regmap,
ARIZONA_ISOLATION_CONTROL,
ARIZONA_ISOLATE_DCVDD1, 0);
if (ret != 0) {
dev_err(arizona->dev,
"Failed to connect DCVDD: %d\n", ret);
goto err;
}
}
ret = wm5102_patch(arizona);
if (ret != 0) {
dev_err(arizona->dev, "Failed to apply patch: %d\n",
@ -365,6 +380,28 @@ static int arizona_runtime_resume(struct device *dev)
goto err;
}
if (arizona->external_dcvdd) {
ret = regmap_update_bits(arizona->regmap,
ARIZONA_ISOLATION_CONTROL,
ARIZONA_ISOLATE_DCVDD1, 0);
if (ret != 0) {
dev_err(arizona->dev,
"Failed to connect DCVDD: %d\n", ret);
goto err;
}
}
break;
}
switch (arizona->type) {
case WM5102:
ret = wm5102_patch(arizona);
if (ret != 0) {
dev_err(arizona->dev, "Failed to apply patch: %d\n",
ret);
goto err;
}
default:
break;
}
@ -385,9 +422,22 @@ static int arizona_runtime_resume(struct device *dev)
static int arizona_runtime_suspend(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
int ret;
dev_dbg(arizona->dev, "Entering AoD mode\n");
if (arizona->external_dcvdd) {
ret = regmap_update_bits(arizona->regmap,
ARIZONA_ISOLATION_CONTROL,
ARIZONA_ISOLATE_DCVDD1,
ARIZONA_ISOLATE_DCVDD1);
if (ret != 0) {
dev_err(arizona->dev, "Failed to isolate DCVDD: %d\n",
ret);
return ret;
}
}
regulator_disable(arizona->dcvdd);
regcache_cache_only(arizona->regmap, true);
regcache_mark_dirty(arizona->regmap);
@ -397,6 +447,26 @@ static int arizona_runtime_suspend(struct device *dev)
#endif
#ifdef CONFIG_PM_SLEEP
static int arizona_suspend(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
dev_dbg(arizona->dev, "Suspend, disabling IRQ\n");
disable_irq(arizona->irq);
return 0;
}
static int arizona_suspend_late(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
dev_dbg(arizona->dev, "Late suspend, reenabling IRQ\n");
enable_irq(arizona->irq);
return 0;
}
static int arizona_resume_noirq(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
@ -422,13 +492,78 @@ const struct dev_pm_ops arizona_pm_ops = {
SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
arizona_runtime_resume,
NULL)
SET_SYSTEM_SLEEP_PM_OPS(NULL, arizona_resume)
SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume)
#ifdef CONFIG_PM_SLEEP
.suspend_late = arizona_suspend_late,
.resume_noirq = arizona_resume_noirq,
#endif
};
EXPORT_SYMBOL_GPL(arizona_pm_ops);
#ifdef CONFIG_OF
int arizona_of_get_type(struct device *dev)
{
const struct of_device_id *id = of_match_device(arizona_of_match, dev);
if (id)
return (int)id->data;
else
return 0;
}
EXPORT_SYMBOL_GPL(arizona_of_get_type);
static int arizona_of_get_core_pdata(struct arizona *arizona)
{
int ret, i;
arizona->pdata.reset = of_get_named_gpio(arizona->dev->of_node,
"wlf,reset", 0);
if (arizona->pdata.reset < 0)
arizona->pdata.reset = 0;
arizona->pdata.ldoena = of_get_named_gpio(arizona->dev->of_node,
"wlf,ldoena", 0);
if (arizona->pdata.ldoena < 0)
arizona->pdata.ldoena = 0;
ret = of_property_read_u32_array(arizona->dev->of_node,
"wlf,gpio-defaults",
arizona->pdata.gpio_defaults,
ARRAY_SIZE(arizona->pdata.gpio_defaults));
if (ret >= 0) {
/*
* All values are literal except out of range values
* which are chip default, translate into platform
* data which uses 0 as chip default and out of range
* as zero.
*/
for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
if (arizona->pdata.gpio_defaults[i] > 0xffff)
arizona->pdata.gpio_defaults[i] = 0;
if (arizona->pdata.gpio_defaults[i] == 0)
arizona->pdata.gpio_defaults[i] = 0x10000;
}
} else {
dev_err(arizona->dev, "Failed to parse GPIO defaults: %d\n",
ret);
}
return 0;
}
const struct of_device_id arizona_of_match[] = {
{ .compatible = "wlf,wm5102", .data = (void *)WM5102 },
{ .compatible = "wlf,wm5110", .data = (void *)WM5110 },
{},
};
EXPORT_SYMBOL_GPL(arizona_of_match);
#else
static inline int arizona_of_get_core_pdata(struct arizona *arizona)
{
return 0;
}
#endif
static struct mfd_cell early_devs[] = {
{ .name = "arizona-ldo1" },
};
@ -462,6 +597,8 @@ int arizona_dev_init(struct arizona *arizona)
dev_set_drvdata(arizona->dev, arizona);
mutex_init(&arizona->clk_lock);
arizona_of_get_core_pdata(arizona);
if (dev_get_platdata(arizona->dev))
memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
sizeof(arizona->pdata));
@ -536,6 +673,63 @@ int arizona_dev_init(struct arizona *arizona)
regcache_cache_only(arizona->regmap, false);
/* Verify that this is a chip we know about */
ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
if (ret != 0) {
dev_err(dev, "Failed to read ID register: %d\n", ret);
goto err_reset;
}
switch (reg) {
case 0x5102:
case 0x5110:
break;
default:
dev_err(arizona->dev, "Unknown device ID: %x\n", reg);
goto err_reset;
}
/* If we have a /RESET GPIO we'll already be reset */
if (!arizona->pdata.reset) {
regcache_mark_dirty(arizona->regmap);
ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
if (ret != 0) {
dev_err(dev, "Failed to reset device: %d\n", ret);
goto err_reset;
}
msleep(1);
ret = regcache_sync(arizona->regmap);
if (ret != 0) {
dev_err(dev, "Failed to sync device: %d\n", ret);
goto err_reset;
}
}
/* Ensure device startup is complete */
switch (arizona->type) {
case WM5102:
ret = regmap_read(arizona->regmap, 0x19, &val);
if (ret != 0)
dev_err(dev,
"Failed to check write sequencer state: %d\n",
ret);
else if (val & 0x01)
break;
/* Fall through */
default:
ret = arizona_wait_for_boot(arizona);
if (ret != 0) {
dev_err(arizona->dev,
"Device failed initial boot: %d\n", ret);
goto err_reset;
}
break;
}
/* Read the device ID information & do device specific stuff */
ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
if (ret != 0) {
dev_err(dev, "Failed to read ID register: %d\n", ret);
@ -581,45 +775,6 @@ int arizona_dev_init(struct arizona *arizona)
dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
/* If we have a /RESET GPIO we'll already be reset */
if (!arizona->pdata.reset) {
regcache_mark_dirty(arizona->regmap);
ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
if (ret != 0) {
dev_err(dev, "Failed to reset device: %d\n", ret);
goto err_reset;
}
msleep(1);
ret = regcache_sync(arizona->regmap);
if (ret != 0) {
dev_err(dev, "Failed to sync device: %d\n", ret);
goto err_reset;
}
}
switch (arizona->type) {
case WM5102:
ret = regmap_read(arizona->regmap, 0x19, &val);
if (ret != 0)
dev_err(dev,
"Failed to check write sequencer state: %d\n",
ret);
else if (val & 0x01)
break;
/* Fall through */
default:
ret = arizona_wait_for_boot(arizona);
if (ret != 0) {
dev_err(arizona->dev,
"Device failed initial boot: %d\n", ret);
goto err_reset;
}
break;
}
if (apply_patch) {
ret = apply_patch(arizona);
if (ret != 0) {
@ -651,6 +806,14 @@ int arizona_dev_init(struct arizona *arizona)
arizona->pdata.gpio_defaults[i]);
}
/*
* LDO1 can only be used to supply DCVDD so if it has no
* consumers then DCVDD is supplied externally.
*/
if (arizona->pdata.ldo1 &&
arizona->pdata.ldo1->num_consumer_supplies == 0)
arizona->external_dcvdd = true;
pm_runtime_set_autosuspend_delay(arizona->dev, 100);
pm_runtime_use_autosuspend(arizona->dev);
pm_runtime_enable(arizona->dev);
@ -697,7 +860,7 @@ int arizona_dev_init(struct arizona *arizona)
if (arizona->pdata.micbias[i].discharge)
val |= ARIZONA_MICB1_DISCH;
if (arizona->pdata.micbias[i].fast_start)
if (arizona->pdata.micbias[i].soft_start)
val |= ARIZONA_MICB1_RATE;
if (arizona->pdata.micbias[i].bypass)
@ -809,6 +972,11 @@ int arizona_dev_exit(struct arizona *arizona)
arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
pm_runtime_disable(arizona->dev);
arizona_irq_exit(arizona);
if (arizona->pdata.reset)
gpio_set_value_cansleep(arizona->pdata.reset, 0);
regulator_disable(arizona->dcvdd);
regulator_bulk_disable(ARRAY_SIZE(arizona->core_supplies),
arizona->core_supplies);
return 0;
}
EXPORT_SYMBOL_GPL(arizona_dev_exit);

View file

@ -27,9 +27,14 @@ static int arizona_i2c_probe(struct i2c_client *i2c,
{
struct arizona *arizona;
const struct regmap_config *regmap_config;
int ret;
int ret, type;
switch (id->driver_data) {
if (i2c->dev.of_node)
type = arizona_of_get_type(&i2c->dev);
else
type = id->driver_data;
switch (type) {
#ifdef CONFIG_MFD_WM5102
case WM5102:
regmap_config = &wm5102_i2c_regmap;
@ -84,6 +89,7 @@ static struct i2c_driver arizona_i2c_driver = {
.name = "arizona",
.owner = THIS_MODULE,
.pm = &arizona_pm_ops,
.of_match_table = of_match_ptr(arizona_of_match),
},
.probe = arizona_i2c_probe,
.remove = arizona_i2c_remove,

View file

@ -27,9 +27,14 @@ static int arizona_spi_probe(struct spi_device *spi)
const struct spi_device_id *id = spi_get_device_id(spi);
struct arizona *arizona;
const struct regmap_config *regmap_config;
int ret;
int ret, type;
switch (id->driver_data) {
if (spi->dev.of_node)
type = arizona_of_get_type(&spi->dev);
else
type = id->driver_data;
switch (type) {
#ifdef CONFIG_MFD_WM5102
case WM5102:
regmap_config = &wm5102_spi_regmap;
@ -84,6 +89,7 @@ static struct spi_driver arizona_spi_driver = {
.name = "arizona",
.owner = THIS_MODULE,
.pm = &arizona_pm_ops,
.of_match_table = of_match_ptr(arizona_of_match),
},
.probe = arizona_spi_probe,
.remove = arizona_spi_remove,

View file

@ -13,6 +13,7 @@
#ifndef _WM5102_H
#define _WM5102_H
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/pm.h>
@ -26,6 +27,8 @@ extern const struct regmap_config wm5110_spi_regmap;
extern const struct dev_pm_ops arizona_pm_ops;
extern const struct of_device_id arizona_of_match[];
extern const struct regmap_irq_chip wm5102_aod;
extern const struct regmap_irq_chip wm5102_irq;
@ -37,4 +40,13 @@ int arizona_dev_exit(struct arizona *arizona);
int arizona_irq_init(struct arizona *arizona);
int arizona_irq_exit(struct arizona *arizona);
#ifdef CONFIG_OF
int arizona_of_get_type(struct device *dev);
#else
static inline int arizona_of_get_type(struct device *dev)
{
return 0;
}
#endif
#endif

View file

@ -65,7 +65,8 @@ static const struct reg_default wm5102_revb_patch[] = {
{ 0x418, 0xa080 },
{ 0x420, 0xa080 },
{ 0x428, 0xe000 },
{ 0x443, 0xDC1A },
{ 0x442, 0x3F0A },
{ 0x443, 0xDC1F },
{ 0x4B0, 0x0066 },
{ 0x458, 0x000b },
{ 0x212, 0x0000 },
@ -424,6 +425,9 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */
{ 0x00000436, 0x0081 }, /* R1078 - DAC Volume Limit 5R */
{ 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */
{ 0x00000440, 0x8FFF }, /* R1088 - DRE Enable */
{ 0x00000442, 0x3F0A }, /* R1090 - DRE Control 2 */
{ 0x00000443, 0xDC1F }, /* R1090 - DRE Control 3 */
{ 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */
{ 0x00000458, 0x000B }, /* R1112 - Noise Gate Control */
{ 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */
@ -1197,6 +1201,9 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DAC_DIGITAL_VOLUME_5R:
case ARIZONA_DAC_VOLUME_LIMIT_5R:
case ARIZONA_NOISE_GATE_SELECT_5R:
case ARIZONA_DRE_ENABLE:
case ARIZONA_DRE_CONTROL_2:
case ARIZONA_DRE_CONTROL_3:
case ARIZONA_DAC_AEC_CONTROL_1:
case ARIZONA_NOISE_GATE_CONTROL:
case ARIZONA_PDM_SPK1_CTRL_1:

View file

@ -2273,18 +2273,22 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_CLOCKING_1:
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
case ARIZONA_DSP2_CONTROL_1:
case ARIZONA_DSP2_CLOCKING_1:
case ARIZONA_DSP2_STATUS_1:
case ARIZONA_DSP2_STATUS_2:
case ARIZONA_DSP2_STATUS_3:
case ARIZONA_DSP3_CONTROL_1:
case ARIZONA_DSP3_CLOCKING_1:
case ARIZONA_DSP3_STATUS_1:
case ARIZONA_DSP3_STATUS_2:
case ARIZONA_DSP3_STATUS_3:
case ARIZONA_DSP4_CONTROL_1:
case ARIZONA_DSP4_CLOCKING_1:
case ARIZONA_DSP4_STATUS_1:
case ARIZONA_DSP4_STATUS_2:
case ARIZONA_DSP4_STATUS_3:
return true;
default:
return false;
@ -2334,12 +2338,16 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_CLOCKING_1:
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
case ARIZONA_DSP2_STATUS_1:
case ARIZONA_DSP2_STATUS_2:
case ARIZONA_DSP2_STATUS_3:
case ARIZONA_DSP3_STATUS_1:
case ARIZONA_DSP3_STATUS_2:
case ARIZONA_DSP3_STATUS_3:
case ARIZONA_DSP4_STATUS_1:
case ARIZONA_DSP4_STATUS_2:
case ARIZONA_DSP4_STATUS_3:
return true;
default:
return false;

View file

@ -58,7 +58,7 @@ struct ssc_device *ssc_request(unsigned int ssc_num)
ssc->user++;
spin_unlock(&user_lock);
clk_enable(ssc->clk);
clk_prepare_enable(ssc->clk);
return ssc;
}
@ -69,7 +69,7 @@ void ssc_free(struct ssc_device *ssc)
spin_lock(&user_lock);
if (ssc->user) {
ssc->user--;
clk_disable(ssc->clk);
clk_disable_unprepare(ssc->clk);
} else {
dev_dbg(&ssc->pdev->dev, "device already free\n");
}
@ -167,10 +167,10 @@ static int ssc_probe(struct platform_device *pdev)
}
/* disable all interrupts */
clk_enable(ssc->clk);
clk_prepare_enable(ssc->clk);
ssc_writel(ssc->regs, IDR, -1);
ssc_readl(ssc->regs, SR);
clk_disable(ssc->clk);
clk_disable_unprepare(ssc->clk);
ssc->irq = platform_get_irq(pdev, 0);
if (!ssc->irq) {

View file

@ -95,6 +95,8 @@ struct arizona {
struct arizona_pdata pdata;
unsigned int external_dcvdd:1;
int irq;
struct irq_domain *virq;
struct regmap_irq_chip_data *aod_irq_chip;

View file

@ -77,7 +77,7 @@ struct arizona_micbias {
int mV; /** Regulated voltage */
unsigned int ext_cap:1; /** External capacitor fitted */
unsigned int discharge:1; /** Actively discharge */
unsigned int fast_start:1; /** Enable aggressive startup ramp rate */
unsigned int soft_start:1; /** Disable aggressive startup ramp rate */
unsigned int bypass:1; /** Use bypass mode */
};

View file

@ -215,6 +215,9 @@
#define ARIZONA_DAC_DIGITAL_VOLUME_6R 0x43D
#define ARIZONA_DAC_VOLUME_LIMIT_6R 0x43E
#define ARIZONA_NOISE_GATE_SELECT_6R 0x43F
#define ARIZONA_DRE_ENABLE 0x440
#define ARIZONA_DRE_CONTROL_2 0x442
#define ARIZONA_DRE_CONTROL_3 0x443
#define ARIZONA_DAC_AEC_CONTROL_1 0x450
#define ARIZONA_NOISE_GATE_CONTROL 0x458
#define ARIZONA_PDM_SPK1_CTRL_1 0x490
@ -1002,6 +1005,7 @@
#define ARIZONA_DSP2_CLOCKING_1 0x1201
#define ARIZONA_DSP2_STATUS_1 0x1204
#define ARIZONA_DSP2_STATUS_2 0x1205
#define ARIZONA_DSP2_STATUS_3 0x1206
#define ARIZONA_DSP2_SCRATCH_0 0x1240
#define ARIZONA_DSP2_SCRATCH_1 0x1241
#define ARIZONA_DSP2_SCRATCH_2 0x1242
@ -1010,6 +1014,7 @@
#define ARIZONA_DSP3_CLOCKING_1 0x1301
#define ARIZONA_DSP3_STATUS_1 0x1304
#define ARIZONA_DSP3_STATUS_2 0x1305
#define ARIZONA_DSP3_STATUS_3 0x1306
#define ARIZONA_DSP3_SCRATCH_0 0x1340
#define ARIZONA_DSP3_SCRATCH_1 0x1341
#define ARIZONA_DSP3_SCRATCH_2 0x1342
@ -1018,6 +1023,7 @@
#define ARIZONA_DSP4_CLOCKING_1 0x1401
#define ARIZONA_DSP4_STATUS_1 0x1404
#define ARIZONA_DSP4_STATUS_2 0x1405
#define ARIZONA_DSP4_STATUS_3 0x1406
#define ARIZONA_DSP4_SCRATCH_0 0x1440
#define ARIZONA_DSP4_SCRATCH_1 0x1441
#define ARIZONA_DSP4_SCRATCH_2 0x1442
@ -3129,6 +3135,47 @@
#define ARIZONA_OUT6R_NGATE_SRC_SHIFT 0 /* OUT6R_NGATE_SRC - [11:0] */
#define ARIZONA_OUT6R_NGATE_SRC_WIDTH 12 /* OUT6R_NGATE_SRC - [11:0] */
/*
* R1088 (0x440) - DRE Enable
*/
#define ARIZONA_DRE3L_ENA 0x0010 /* DRE3L_ENA */
#define ARIZONA_DRE3L_ENA_MASK 0x0010 /* DRE3L_ENA */
#define ARIZONA_DRE3L_ENA_SHIFT 4 /* DRE3L_ENA */
#define ARIZONA_DRE3L_ENA_WIDTH 1 /* DRE3L_ENA */
#define ARIZONA_DRE2R_ENA 0x0008 /* DRE2R_ENA */
#define ARIZONA_DRE2R_ENA_MASK 0x0008 /* DRE2R_ENA */
#define ARIZONA_DRE2R_ENA_SHIFT 3 /* DRE2R_ENA */
#define ARIZONA_DRE2R_ENA_WIDTH 1 /* DRE2R_ENA */
#define ARIZONA_DRE2L_ENA 0x0004 /* DRE2L_ENA */
#define ARIZONA_DRE2L_ENA_MASK 0x0004 /* DRE2L_ENA */
#define ARIZONA_DRE2L_ENA_SHIFT 2 /* DRE2L_ENA */
#define ARIZONA_DRE2L_ENA_WIDTH 1 /* DRE2L_ENA */
#define ARIZONA_DRE1R_ENA 0x0002 /* DRE1R_ENA */
#define ARIZONA_DRE1R_ENA_MASK 0x0002 /* DRE1R_ENA */
#define ARIZONA_DRE1R_ENA_SHIFT 1 /* DRE1R_ENA */
#define ARIZONA_DRE1R_ENA_WIDTH 1 /* DRE1R_ENA */
#define ARIZONA_DRE1L_ENA 0x0001 /* DRE1L_ENA */
#define ARIZONA_DRE1L_ENA_MASK 0x0001 /* DRE1L_ENA */
#define ARIZONA_DRE1L_ENA_SHIFT 0 /* DRE1L_ENA */
#define ARIZONA_DRE1L_ENA_WIDTH 1 /* DRE1L_ENA */
/*
* R1090 (0x442) - DRE Control 2
*/
#define ARIZONA_DRE_T_LOW_MASK 0x3F00 /* DRE_T_LOW - [13:8] */
#define ARIZONA_DRE_T_LOW_SHIFT 8 /* DRE_T_LOW - [13:8] */
#define ARIZONA_DRE_T_LOW_WIDTH 6 /* DRE_T_LOW - [13:8] */
/*
* R1091 (0x443) - DRE Control 3
*/
#define ARIZONA_DRE_GAIN_SHIFT_MASK 0xC000 /* DRE_GAIN_SHIFT - [15:14] */
#define ARIZONA_DRE_GAIN_SHIFT_SHIFT 14 /* DRE_GAIN_SHIFT - [15:14] */
#define ARIZONA_DRE_GAIN_SHIFT_WIDTH 2 /* DRE_GAIN_SHIFT - [15:14] */
#define ARIZONA_DRE_LOW_LEVEL_ABS_MASK 0x000F /* LOW_LEVEL_ABS - [3:0] */
#define ARIZONA_DRE_LOW_LEVEL_ABS_SHIFT 0 /* LOW_LEVEL_ABS - [3:0] */
#define ARIZONA_DRE_LOW_LEVEL_ABS_WIDTH 4 /* LOW_LEVEL_ABS - [3:0] */
/*
* R1104 (0x450) - DAC AEC Control 1
*/

View file

@ -182,6 +182,11 @@ struct wm8994_pdata {
*/
int micdet_delay;
/* Delay between microphone detect completing and reporting on
* insert (specified in ms)
*/
int mic_id_delay;
/* IRQ for microphone detection if brought out directly as a
* signal.
*/

View file

@ -2668,6 +2668,10 @@
/*
* R772 (0x304) - AIF1ADC LRCLK
*/
#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */
#define WM8994_AIF1ADC_LRCLK_DIR 0x0800 /* AIF1ADC_LRCLK_DIR */
#define WM8994_AIF1ADC_LRCLK_DIR_MASK 0x0800 /* AIF1ADC_LRCLK_DIR */
#define WM8994_AIF1ADC_LRCLK_DIR_SHIFT 11 /* AIF1ADC_LRCLK_DIR */
@ -2679,6 +2683,10 @@
/*
* R773 (0x305) - AIF1DAC LRCLK
*/
#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */
#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */
#define WM8994_AIF1DAC_LRCLK_DIR 0x0800 /* AIF1DAC_LRCLK_DIR */
#define WM8994_AIF1DAC_LRCLK_DIR_MASK 0x0800 /* AIF1DAC_LRCLK_DIR */
#define WM8994_AIF1DAC_LRCLK_DIR_SHIFT 11 /* AIF1DAC_LRCLK_DIR */

View file

@ -0,0 +1,22 @@
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#ifndef __LINUX_PLATFORM_DATA_SSM2518_H__
#define __LINUX_PLATFORM_DATA_SSM2518_H__
/**
* struct ssm2518_platform_data - Platform data for the ssm2518 driver
* @enable_gpio: GPIO connected to the nSD pin. Set to -1 if the nSD pin is
* hardwired.
*/
struct ssm2518_platform_data {
int enable_gpio;
};
#endif

22
include/sound/rt5640.h Normal file
View file

@ -0,0 +1,22 @@
/*
* linux/sound/rt5640.h -- Platform data for RT5640
*
* Copyright 2011 Realtek Microelectronics
*
* 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 __LINUX_SND_RT5640_H
#define __LINUX_SND_RT5640_H
struct rt5640_platform_data {
/* IN1 & IN2 can optionally be differential */
bool in1_diff;
bool in2_diff;
int ldo1_en; /* GPIO for LDO1_EN */
};
#endif

View file

@ -311,6 +311,8 @@ struct device;
#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */
#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */
#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */
#define SND_SOC_DAPM_WILL_PMU 0x40 /* called at start of sequence */
#define SND_SOC_DAPM_WILL_PMD 0x80 /* called at start of sequence */
#define SND_SOC_DAPM_PRE_POST_PMD \
(SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)
@ -479,7 +481,6 @@ struct snd_soc_dapm_route {
/* dapm audio path between two widgets */
struct snd_soc_dapm_path {
const char *name;
const char *long_name;
/* source (input) and sink (output) widgets */
struct snd_soc_dapm_widget *source;

View file

@ -51,6 +51,7 @@ source "sound/soc/pxa/Kconfig"
source "sound/soc/samsung/Kconfig"
source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/spear/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
source "sound/soc/ux500/Kconfig"

View file

@ -29,6 +29,7 @@ obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += spear/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
obj-$(CONFIG_SND_SOC) += ux500/

View file

@ -38,8 +38,6 @@
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/pinctrl/consumer.h>
#include <linux/atmel-ssc.h>
#include <sound/core.h>
@ -203,15 +201,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
struct device_node *codec_np, *cpu_np;
struct clk *pllb;
struct snd_soc_card *card = &snd_soc_at91sam9g20ek;
struct pinctrl *pinctrl;
int ret;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
dev_err(&pdev->dev, "Failed to request pinctrl for mck\n");
return PTR_ERR(pinctrl);
}
if (!np) {
if (!(machine_is_at91sam9g20ek() ||
machine_is_at91sam9g20ek_2mmc()))

View file

@ -56,6 +56,23 @@ config SND_SOC_BFIN_EVAL_ADAV80X
Note: This driver assumes that the ADAV80X digital record and playback
interfaces are connected to the first SPORT port on the BF5XX board.
config SND_BF5XX_SOC_AD1836
tristate "SoC AD1836 Audio support for BF5xx"
depends on SND_BF5XX_I2S
select SND_BF5XX_SOC_I2S
select SND_SOC_AD1836
help
Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
config SND_BF5XX_SOC_AD193X
tristate "SoC AD193X Audio support for Blackfin"
depends on SND_BF5XX_I2S
select SND_BF5XX_SOC_I2S
select SND_SOC_AD193X
help
Say Y if you want to add support for AD193X codec on Blackfin.
This driver supports AD1936, AD1937, AD1938 and AD1939.
config SND_BF5XX_SOC_AD73311
tristate "SoC AD73311 Audio support for Blackfin"
depends on SND_BF5XX_I2S
@ -72,33 +89,6 @@ config SND_BFIN_AD73311_SE
Enter the GPIO used to control AD73311's SE pin. Acceptable
values are 0 to 7
config SND_BF5XX_TDM
tristate "SoC I2S(TDM mode) Audio for the ADI BF5xx chip"
depends on (BLACKFIN && SND_SOC)
select SND_BF5XX_SOC_SPORT
help
Say Y or M if you want to add support for codecs attached to
the Blackfin SPORT (synchronous serial ports) interface in TDM
mode.
You will also need to select the audio interfaces to support below.
config SND_BF5XX_SOC_AD1836
tristate "SoC AD1836 Audio support for BF5xx"
depends on SND_BF5XX_TDM
select SND_BF5XX_SOC_TDM
select SND_SOC_AD1836
help
Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
config SND_BF5XX_SOC_AD193X
tristate "SoC AD193X Audio support for Blackfin"
depends on SND_BF5XX_TDM
select SND_BF5XX_SOC_TDM
select SND_SOC_AD193X
help
Say Y if you want to add support for AD193X codec on Blackfin.
This driver supports AD1936, AD1937, AD1938 and AD1939.
config SND_BF5XX_AC97
tristate "SoC AC97 Audio for the ADI BF5xx chip"
depends on BLACKFIN
@ -174,9 +164,6 @@ config SND_BF5XX_SOC_I2S
config SND_BF6XX_SOC_I2S
tristate
config SND_BF5XX_SOC_TDM
tristate
config SND_BF5XX_SOC_AC97
tristate

View file

@ -1,23 +1,19 @@
# Blackfin Platform Support
snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
snd-bf5xx-tdm-objs := bf5xx-tdm-pcm.o
snd-soc-bf5xx-sport-objs := bf5xx-sport.o
snd-soc-bf6xx-sport-objs := bf6xx-sport.o
snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
snd-soc-bf6xx-i2s-objs := bf6xx-i2s.o
snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o
obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.o
obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
obj-$(CONFIG_SND_BF6XX_SOC_SPORT) += snd-soc-bf6xx-sport.o
obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o
obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
obj-$(CONFIG_SND_BF6XX_SOC_I2S) += snd-soc-bf6xx-i2s.o
obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o
# Blackfin Machine Support
snd-ad1836-objs := bf5xx-ad1836.o

View file

@ -39,7 +39,6 @@
#include <asm/dma.h>
#include "bf5xx-ac97-pcm.h"
#include "bf5xx-ac97.h"
#include "bf5xx-sport.h"

View file

@ -1,26 +0,0 @@
/*
* linux/sound/arm/bf5xx-ac97-pcm.h -- ALSA PCM interface for the Blackfin
*
* Copyright 2007 Analog Device Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _BF5XX_AC97_PCM_H
#define _BF5XX_AC97_PCM_H
struct bf5xx_pcm_dma_params {
char *name; /* stream identifier */
};
struct bf5xx_gpio {
u32 sys;
u32 rx;
u32 tx;
u32 clk;
u32 frm;
};
#endif

View file

@ -231,9 +231,9 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
return 0;
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
ret = sport_set_multichannel(sport, 16, 0x3FF, 1);
ret = sport_set_multichannel(sport, 16, 0x3FF, 0x3FF, 1);
#else
ret = sport_set_multichannel(sport, 16, 0x1F, 1);
ret = sport_set_multichannel(sport, 16, 0x1F, 0x1F, 1);
#endif
if (ret) {
pr_err("SPORT is busy!\n");
@ -311,9 +311,9 @@ static int asoc_bfin_ac97_probe(struct platform_device *pdev)
/*SPORT works in TDM mode to simulate AC97 transfers*/
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1);
ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 0x3FF, 1);
#else
ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
ret = sport_set_multichannel(sport_handle, 16, 0x1F, 0x1F, 1);
#endif
if (ret) {
pr_err("SPORT is busy!\n");

View file

@ -30,15 +30,10 @@
#include "../codecs/ad1836.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad1836;
static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
static int bf5xx_ad1836_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
int ret = 0;
@ -49,13 +44,13 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops bf5xx_ad1836_ops = {
.hw_params = bf5xx_ad1836_hw_params,
};
#define BF5XX_AD1836_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \
SND_SOC_DAIFMT_CBM_CFM)
@ -63,9 +58,9 @@ static struct snd_soc_dai_link bf5xx_ad1836_dai = {
.name = "ad1836",
.stream_name = "AD1836",
.codec_dai_name = "ad1836-hifi",
.platform_name = "bfin-tdm-pcm-audio",
.ops = &bf5xx_ad1836_ops,
.platform_name = "bfin-i2s-pcm-audio",
.dai_fmt = BF5XX_AD1836_DAIFMT,
.init = bf5xx_ad1836_init,
};
static struct snd_soc_card bf5xx_ad1836 = {

View file

@ -39,30 +39,16 @@
#include "../codecs/ad193x.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad193x;
static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
static int bf5xx_ad193x_link_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
unsigned int clk = 0;
unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
int ret = 0;
switch (params_rate(params)) {
case 48000:
clk = 24576000;
break;
}
int ret;
/* set the codec system clock for DAC and ADC */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
SND_SOC_CLOCK_IN);
ret = snd_soc_dai_set_sysclk(codec_dai, 0, 24576000, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
@ -71,9 +57,7 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
/* set cpu DAI channel mapping */
ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
channel_map, ARRAY_SIZE(channel_map), channel_map);
ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32);
if (ret < 0)
return ret;
@ -83,30 +67,26 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
#define BF5XX_AD193X_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \
SND_SOC_DAIFMT_CBM_CFM)
static struct snd_soc_ops bf5xx_ad193x_ops = {
.hw_params = bf5xx_ad193x_hw_params,
};
static struct snd_soc_dai_link bf5xx_ad193x_dai[] = {
{
.name = "ad193x",
.stream_name = "AD193X",
.cpu_dai_name = "bfin-tdm.0",
.cpu_dai_name = "bfin-i2s.0",
.codec_dai_name ="ad193x-hifi",
.platform_name = "bfin-tdm-pcm-audio",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "spi0.5",
.ops = &bf5xx_ad193x_ops,
.dai_fmt = BF5XX_AD193X_DAIFMT,
.init = bf5xx_ad193x_link_init,
},
{
.name = "ad193x",
.stream_name = "AD193X",
.cpu_dai_name = "bfin-tdm.1",
.cpu_dai_name = "bfin-i2s.1",
.codec_dai_name ="ad193x-hifi",
.platform_name = "bfin-tdm-pcm-audio",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "spi0.5",
.ops = &bf5xx_ad193x_ops,
.dai_fmt = BF5XX_AD193X_DAIFMT,
.init = bf5xx_ad193x_link_init,
},
};

View file

@ -48,7 +48,6 @@
#include "../codecs/ad1980.h"
#include "bf5xx-ac97-pcm.h"
#include "bf5xx-ac97.h"
static struct snd_soc_card bf5xx_board;

View file

@ -45,7 +45,6 @@
#include "../codecs/ad73311.h"
#include "bf5xx-sport.h"
#include "bf5xx-i2s-pcm.h"
#if CONFIG_SND_BF5XX_SPORT_NUM == 0
#define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1

View file

@ -39,8 +39,8 @@
#include <asm/dma.h>
#include "bf5xx-i2s-pcm.h"
#include "bf5xx-sport.h"
#include "bf5xx-i2s-pcm.h"
static void bf5xx_dma_irq(void *data)
{
@ -50,7 +50,6 @@ static void bf5xx_dma_irq(void *data)
static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
@ -67,10 +66,16 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
snd_pcm_lib_malloc_pages(substream, size);
struct snd_soc_pcm_runtime *rtd = substream->private_data;
unsigned int buffer_size = params_buffer_bytes(params);
struct bf5xx_i2s_pcm_data *dma_data;
return 0;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
if (dma_data->tdm_mode)
buffer_size = buffer_size / params_channels(params) * 8;
return snd_pcm_lib_malloc_pages(substream, buffer_size);
}
static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
@ -82,9 +87,16 @@ static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
int period_bytes = frames_to_bytes(runtime, runtime->period_size);
struct bf5xx_i2s_pcm_data *dma_data;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
if (dma_data->tdm_mode)
period_bytes = period_bytes / runtime->channels * 8;
pr_debug("%s enter\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@ -131,10 +143,15 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
unsigned int diff;
snd_pcm_uframes_t frames;
struct bf5xx_i2s_pcm_data *dma_data;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
pr_debug("%s enter\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
diff = sport_curr_offset_tx(sport);
@ -151,6 +168,8 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
diff = 0;
frames = bytes_to_frames(substream->runtime, diff);
if (dma_data->tdm_mode)
frames = frames * runtime->channels / 8;
return frames;
}
@ -162,11 +181,18 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dma_buffer *buf = &substream->dma_buffer;
struct bf5xx_i2s_pcm_data *dma_data;
int ret;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
pr_debug("%s enter\n", __func__);
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
if (dma_data->tdm_mode)
runtime->hw.buffer_bytes_max /= 4;
else
runtime->hw.info |= SNDRV_PCM_INFO_MMAP;
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
@ -202,6 +228,88 @@ static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
return 0 ;
}
static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int sample_size = runtime->sample_bits / 8;
struct bf5xx_i2s_pcm_data *dma_data;
unsigned int i;
void *src, *dst;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
if (dma_data->tdm_mode) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
src = buf;
dst = runtime->dma_area;
dst += pos * sample_size * 8;
while (count--) {
for (i = 0; i < runtime->channels; i++) {
memcpy(dst + dma_data->map[i] *
sample_size, src, sample_size);
src += sample_size;
}
dst += 8 * sample_size;
}
} else {
src = runtime->dma_area;
src += pos * sample_size * 8;
dst = buf;
while (count--) {
for (i = 0; i < runtime->channels; i++) {
memcpy(dst, src + dma_data->map[i] *
sample_size, sample_size);
dst += sample_size;
}
src += 8 * sample_size;
}
}
} else {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
src = buf;
dst = runtime->dma_area;
dst += frames_to_bytes(runtime, pos);
} else {
src = runtime->dma_area;
src += frames_to_bytes(runtime, pos);
dst = buf;
}
memcpy(dst, src, frames_to_bytes(runtime, count));
}
return 0;
}
static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int sample_size = runtime->sample_bits / 8;
void *buf = runtime->dma_area;
struct bf5xx_i2s_pcm_data *dma_data;
unsigned int offset, size;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
if (dma_data->tdm_mode) {
offset = pos * 8 * sample_size;
size = count * 8 * sample_size;
} else {
offset = frames_to_bytes(runtime, pos);
size = frames_to_bytes(runtime, count);
}
snd_pcm_format_set_silence(runtime->format, buf + offset, size);
return 0;
}
static struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
.open = bf5xx_pcm_open,
.ioctl = snd_pcm_lib_ioctl,
@ -211,57 +319,16 @@ static struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
.trigger = bf5xx_pcm_trigger,
.pointer = bf5xx_pcm_pointer,
.mmap = bf5xx_pcm_mmap,
.copy = bf5xx_pcm_copy,
.silence = bf5xx_pcm_silence,
};
static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
buf->area = dma_alloc_coherent(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
if (!buf->area) {
pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n");
return -ENOMEM;
}
buf->bytes = size;
pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
buf->area, buf->bytes);
return 0;
}
static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
int stream;
for (stream = 0; stream < 2; stream++) {
substream = pcm->streams[stream].substream;
if (!substream)
continue;
buf = &substream->dma_buffer;
if (!buf->area)
continue;
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
}
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
pr_debug("%s enter\n", __func__);
if (!card->dev->dma_mask)
@ -269,27 +336,13 @@ static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd)
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto out;
}
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto out;
}
out:
return ret;
return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
SNDRV_DMA_TYPE_DEV, card->dev, size, size);
}
static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = {
.ops = &bf5xx_pcm_i2s_ops,
.pcm_new = bf5xx_pcm_i2s_new,
.pcm_free = bf5xx_pcm_free_dma_buffers,
};
static int bfin_i2s_soc_platform_probe(struct platform_device *pdev)

View file

@ -1,26 +1,17 @@
/*
* linux/sound/arm/bf5xx-i2s-pcm.h -- ALSA PCM interface for the Blackfin
*
* Copyright 2007 Analog Device Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _BF5XX_I2S_PCM_H
#define _BF5XX_I2S_PCM_H
#ifndef _BF5XX_TDM_PCM_H
#define _BF5XX_TDM_PCM_H
struct bf5xx_pcm_dma_params {
char *name; /* stream identifier */
};
#define BFIN_TDM_DAI_MAX_SLOTS 8
struct bf5xx_gpio {
u32 sys;
u32 rx;
u32 tx;
u32 clk;
u32 frm;
struct bf5xx_i2s_pcm_data {
unsigned int map[BFIN_TDM_DAI_MAX_SLOTS];
bool tdm_mode;
};
#endif

View file

@ -42,6 +42,7 @@
#include <linux/gpio.h>
#include "bf5xx-sport.h"
#include "bf5xx-i2s-pcm.h"
struct bf5xx_i2s_port {
u16 tcr1;
@ -49,6 +50,13 @@ struct bf5xx_i2s_port {
u16 tcr2;
u16 rcr2;
int configured;
unsigned int slots;
unsigned int tx_mask;
unsigned int rx_mask;
struct bf5xx_i2s_pcm_data tx_dma_data;
struct bf5xx_i2s_pcm_data rx_dma_data;
};
static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
@ -74,7 +82,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
ret = -EINVAL;
break;
default:
printk(KERN_ERR "%s: Unknown DAI format type\n", __func__);
dev_err(cpu_dai->dev, "%s: Unknown DAI format type\n",
__func__);
ret = -EINVAL;
break;
}
@ -88,7 +97,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
ret = -EINVAL;
break;
default:
printk(KERN_ERR "%s: Unknown DAI master type\n", __func__);
dev_err(cpu_dai->dev, "%s: Unknown DAI master type\n",
__func__);
ret = -EINVAL;
break;
}
@ -141,14 +151,14 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
bf5xx_i2s->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
bf5xx_i2s->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
}
@ -162,18 +172,76 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
pr_debug("%s enter\n", __func__);
dev_dbg(dai->dev, "%s enter\n", __func__);
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
bf5xx_i2s->configured = 0;
}
static int bf5xx_i2s_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
unsigned int tx_mapped = 0, rx_mapped = 0;
unsigned int slot;
int i;
if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
(rx_num > BFIN_TDM_DAI_MAX_SLOTS))
return -EINVAL;
for (i = 0; i < tx_num; i++) {
slot = tx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(tx_mapped & (1 << slot)))) {
bf5xx_i2s->tx_dma_data.map[i] = slot;
tx_mapped |= 1 << slot;
} else
return -EINVAL;
}
for (i = 0; i < rx_num; i++) {
slot = rx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(rx_mapped & (1 << slot)))) {
bf5xx_i2s->rx_dma_data.map[i] = slot;
rx_mapped |= 1 << slot;
} else
return -EINVAL;
}
return 0;
}
static int bf5xx_i2s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int width)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
if (slots % 8 != 0 || slots > 8)
return -EINVAL;
if (width != 32)
return -EINVAL;
bf5xx_i2s->slots = slots;
bf5xx_i2s->tx_mask = tx_mask;
bf5xx_i2s->rx_mask = rx_mask;
bf5xx_i2s->tx_dma_data.tdm_mode = slots != 0;
bf5xx_i2s->rx_dma_data.tdm_mode = slots != 0;
return sport_set_multichannel(sport_handle, slots, tx_mask, rx_mask, 0);
}
#ifdef CONFIG_PM
static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
pr_debug("%s : sport %d\n", __func__, dai->id);
dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
if (dai->capture_active)
sport_rx_stop(sport_handle);
@ -188,23 +256,24 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret;
pr_debug("%s : sport %d\n", __func__, dai->id);
dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
bf5xx_i2s->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
bf5xx_i2s->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
dev_err(dai->dev, "SPORT is busy!\n");
return -EBUSY;
}
return 0;
return sport_set_multichannel(sport_handle, bf5xx_i2s->slots,
bf5xx_i2s->tx_mask, bf5xx_i2s->rx_mask, 0);
}
#else
@ -212,6 +281,23 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
#define bf5xx_i2s_resume NULL
#endif
static int bf5xx_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
unsigned int i;
for (i = 0; i < BFIN_TDM_DAI_MAX_SLOTS; i++) {
bf5xx_i2s->tx_dma_data.map[i] = i;
bf5xx_i2s->rx_dma_data.map[i] = i;
}
dai->playback_dma_data = &bf5xx_i2s->tx_dma_data;
dai->capture_dma_data = &bf5xx_i2s->rx_dma_data;
return 0;
}
#define BF5XX_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 | \
@ -224,22 +310,25 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
.shutdown = bf5xx_i2s_shutdown,
.hw_params = bf5xx_i2s_hw_params,
.set_fmt = bf5xx_i2s_set_dai_fmt,
.shutdown = bf5xx_i2s_shutdown,
.hw_params = bf5xx_i2s_hw_params,
.set_fmt = bf5xx_i2s_set_dai_fmt,
.set_tdm_slot = bf5xx_i2s_set_tdm_slot,
.set_channel_map = bf5xx_i2s_set_channel_map,
};
static struct snd_soc_dai_driver bf5xx_i2s_dai = {
.probe = bf5xx_i2s_dai_probe,
.suspend = bf5xx_i2s_suspend,
.resume = bf5xx_i2s_resume,
.playback = {
.channels_min = 1,
.channels_max = 2,
.channels_min = 2,
.channels_max = 8,
.rates = BF5XX_I2S_RATES,
.formats = BF5XX_I2S_FORMATS,},
.capture = {
.channels_min = 1,
.channels_max = 2,
.channels_min = 2,
.channels_max = 8,
.rates = BF5XX_I2S_RATES,
.formats = BF5XX_I2S_FORMATS,},
.ops = &bf5xx_i2s_dai_ops,
@ -255,7 +344,7 @@ static int bf5xx_i2s_probe(struct platform_device *pdev)
int ret;
/* configure SPORT for I2S */
sport_handle = sport_init(pdev, 4, 2 * sizeof(u32),
sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
sizeof(struct bf5xx_i2s_port));
if (!sport_handle)
return -ENODEV;
@ -264,7 +353,7 @@ static int bf5xx_i2s_probe(struct platform_device *pdev)
ret = snd_soc_register_component(&pdev->dev, &bf5xx_i2s_component,
&bf5xx_i2s_dai, 1);
if (ret) {
pr_err("Failed to register DAI: %d\n", ret);
dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret);
sport_done(sport_handle);
return ret;
}
@ -276,7 +365,7 @@ static int bf5xx_i2s_remove(struct platform_device *pdev)
{
struct sport_device *sport_handle = platform_get_drvdata(pdev);
pr_debug("%s enter\n", __func__);
dev_dbg(&pdev->dev, "%s enter\n", __func__);
snd_soc_unregister_component(&pdev->dev);
sport_done(sport_handle);

View file

@ -46,10 +46,10 @@
/* note: multichannel is in units of 8 channels,
* tdm_count is # channels NOT / 8 ! */
int sport_set_multichannel(struct sport_device *sport,
int tdm_count, u32 mask, int packed)
int tdm_count, u32 tx_mask, u32 rx_mask, int packed)
{
pr_debug("%s tdm_count=%d mask:0x%08x packed=%d\n", __func__,
tdm_count, mask, packed);
pr_debug("%s tdm_count=%d tx_mask:0x%08x rx_mask:0x%08x packed=%d\n",
__func__, tdm_count, tx_mask, rx_mask, packed);
if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
return -EBUSY;
@ -65,8 +65,8 @@ int sport_set_multichannel(struct sport_device *sport,
sport->regs->mcmc2 = FRAME_DELAY | MCMEN | \
(packed ? (MCDTXPE|MCDRXPE) : 0);
sport->regs->mtcs0 = mask;
sport->regs->mrcs0 = mask;
sport->regs->mtcs0 = tx_mask;
sport->regs->mrcs0 = rx_mask;
sport->regs->mtcs1 = 0;
sport->regs->mrcs1 = 0;
sport->regs->mtcs2 = 0;

View file

@ -128,7 +128,7 @@ void sport_done(struct sport_device *sport);
/* note: multichannel is in units of 8 channels, tdm_count is number of channels
* NOT / 8 ! all channels are enabled by default */
int sport_set_multichannel(struct sport_device *sport, int tdm_count,
u32 mask, int packed);
u32 tx_mask, u32 rx_mask, int packed);
int sport_config_rx(struct sport_device *sport,
unsigned int rcr1, unsigned int rcr2,

View file

@ -40,7 +40,6 @@
#include <linux/gpio.h>
#include "../codecs/ssm2602.h"
#include "bf5xx-sport.h"
#include "bf5xx-i2s-pcm.h"
static struct snd_soc_card bf5xx_ssm2602;

View file

@ -1,345 +0,0 @@
/*
* File: sound/soc/blackfin/bf5xx-tdm-pcm.c
* Author: Barry Song <Barry.Song@analog.com>
*
* Created: Tue June 06 2009
* Description: DMA driver for tdm codec
*
* Modified:
* Copyright 2009 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* 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, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <asm/dma.h>
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
#include "bf5xx-sport.h"
#define PCM_BUFFER_MAX 0x8000
#define FRAGMENT_SIZE_MIN (4*1024)
#define FRAGMENTS_MIN 2
#define FRAGMENTS_MAX 32
static void bf5xx_dma_irq(void *data)
{
struct snd_pcm_substream *pcm = data;
snd_pcm_period_elapsed(pcm);
}
static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_RESUME),
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 8,
.buffer_bytes_max = PCM_BUFFER_MAX,
.period_bytes_min = FRAGMENT_SIZE_MIN,
.period_bytes_max = PCM_BUFFER_MAX/2,
.periods_min = FRAGMENTS_MIN,
.periods_max = FRAGMENTS_MAX,
};
static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
snd_pcm_lib_malloc_pages(substream, size * 4);
return 0;
}
static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
snd_pcm_lib_free_pages(substream);
return 0;
}
static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
int fragsize_bytes = frames_to_bytes(runtime, runtime->period_size);
fragsize_bytes /= runtime->channels;
/* inflate the fragsize to match the dma width of SPORT */
fragsize_bytes *= 8;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
sport_config_tx_dma(sport, runtime->dma_area,
runtime->periods, fragsize_bytes);
} else {
sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
sport_config_rx_dma(sport, runtime->dma_area,
runtime->periods, fragsize_bytes);
}
return 0;
}
static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_tx_start(sport);
else
sport_rx_start(sport);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_tx_stop(sport);
else
sport_rx_stop(sport);
break;
default:
ret = -EINVAL;
}
return ret;
}
static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
unsigned int diff;
snd_pcm_uframes_t frames;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
diff = sport_curr_offset_tx(sport);
frames = diff / (8*4); /* 32 bytes per frame */
} else {
diff = sport_curr_offset_rx(sport);
frames = diff / (8*4);
}
return frames;
}
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dma_buffer *buf = &substream->dma_buffer;
int ret = 0;
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
goto out;
if (sport_handle != NULL) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_handle->tx_buf = buf->area;
else
sport_handle->rx_buf = buf->area;
runtime->private_data = sport_handle;
} else {
pr_err("sport_handle is NULL\n");
ret = -ENODEV;
}
out:
return ret;
}
static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
struct bf5xx_tdm_port *tdm_port = sport->private_data;
unsigned int *src;
unsigned int *dst;
int i;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
src = buf;
dst = (unsigned int *)substream->runtime->dma_area;
dst += pos * 8;
while (count--) {
for (i = 0; i < substream->runtime->channels; i++)
*(dst + tdm_port->tx_map[i]) = *src++;
dst += 8;
}
} else {
src = (unsigned int *)substream->runtime->dma_area;
dst = buf;
src += pos * 8;
while (count--) {
for (i = 0; i < substream->runtime->channels; i++)
*dst++ = *(src + tdm_port->rx_map[i]);
src += 8;
}
}
return 0;
}
static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
{
unsigned char *buf = substream->runtime->dma_area;
buf += pos * 8 * 4;
memset(buf, '\0', count * 8 * 4);
return 0;
}
struct snd_pcm_ops bf5xx_pcm_tdm_ops = {
.open = bf5xx_pcm_open,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = bf5xx_pcm_hw_params,
.hw_free = bf5xx_pcm_hw_free,
.prepare = bf5xx_pcm_prepare,
.trigger = bf5xx_pcm_trigger,
.pointer = bf5xx_pcm_pointer,
.copy = bf5xx_pcm_copy,
.silence = bf5xx_pcm_silence,
};
static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
buf->area = dma_alloc_coherent(pcm->card->dev, size * 4,
&buf->addr, GFP_KERNEL);
if (!buf->area) {
pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n");
return -ENOMEM;
}
buf->bytes = size;
return 0;
}
static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
int stream;
for (stream = 0; stream < 2; stream++) {
substream = pcm->streams[stream].substream;
if (!substream)
continue;
buf = &substream->dma_buffer;
if (!buf->area)
continue;
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
}
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
static int bf5xx_pcm_tdm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)
card->dev->dma_mask = &bf5xx_pcm_dmamask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto out;
}
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto out;
}
out:
return ret;
}
static struct snd_soc_platform_driver bf5xx_tdm_soc_platform = {
.ops = &bf5xx_pcm_tdm_ops,
.pcm_new = bf5xx_pcm_tdm_new,
.pcm_free = bf5xx_pcm_free_dma_buffers,
};
static int bf5xx_soc_platform_probe(struct platform_device *pdev)
{
return snd_soc_register_platform(&pdev->dev, &bf5xx_tdm_soc_platform);
}
static int bf5xx_soc_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_platform(&pdev->dev);
return 0;
}
static struct platform_driver bfin_tdm_driver = {
.driver = {
.name = "bfin-tdm-pcm-audio",
.owner = THIS_MODULE,
},
.probe = bf5xx_soc_platform_probe,
.remove = bf5xx_soc_platform_remove,
};
module_platform_driver(bfin_tdm_driver);
MODULE_AUTHOR("Barry Song");
MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module");
MODULE_LICENSE("GPL");

View file

@ -1,18 +0,0 @@
/*
* sound/soc/blackfin/bf5xx-tdm-pcm.h -- ALSA PCM interface for the Blackfin
*
* Copyright 2009 Analog Device Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _BF5XX_TDM_PCM_H
#define _BF5XX_TDM_PCM_H
struct bf5xx_pcm_dma_params {
char *name; /* stream identifier */
};
#endif

View file

@ -1,328 +0,0 @@
/*
* File: sound/soc/blackfin/bf5xx-tdm.c
* Author: Barry Song <Barry.Song@analog.com>
*
* Created: Thurs June 04 2009
* Description: Blackfin I2S(TDM) CPU DAI driver
* Even though TDM mode can be as part of I2S DAI, but there
* are so much difference in configuration and data flow,
* it's very ugly to integrate I2S and TDM into a module
*
* Modified:
* Copyright 2009 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* 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, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <asm/irq.h>
#include <asm/portmux.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include "bf5xx-sport.h"
#include "bf5xx-tdm.h"
static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
int ret = 0;
/* interface format:support TDM,slave mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
break;
default:
printk(KERN_ERR "%s: Unknown DAI format type\n", __func__);
ret = -EINVAL;
break;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
break;
case SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_CBM_CFS:
case SND_SOC_DAIFMT_CBS_CFM:
ret = -EINVAL;
break;
default:
printk(KERN_ERR "%s: Unknown DAI master type\n", __func__);
ret = -EINVAL;
break;
}
return ret;
}
static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int ret = 0;
bf5xx_tdm->tcr2 &= ~0x1f;
bf5xx_tdm->rcr2 &= ~0x1f;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S32_LE:
bf5xx_tdm->tcr2 |= 31;
bf5xx_tdm->rcr2 |= 31;
sport_handle->wdsize = 4;
break;
/* at present, we only support 32bit transfer */
default:
pr_err("not supported PCM format yet\n");
return -EINVAL;
break;
}
if (!bf5xx_tdm->configured) {
/*
* TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we
* need to configure both of them at the time when the first
* stream is opened.
*
* CPU DAI:slave mode.
*/
ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1,
bf5xx_tdm->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1,
bf5xx_tdm->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
bf5xx_tdm->configured = 1;
}
return 0;
}
static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
bf5xx_tdm->configured = 0;
}
static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int i;
unsigned int slot;
unsigned int tx_mapped = 0, rx_mapped = 0;
if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
(rx_num > BFIN_TDM_DAI_MAX_SLOTS))
return -EINVAL;
for (i = 0; i < tx_num; i++) {
slot = tx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(tx_mapped & (1 << slot)))) {
bf5xx_tdm->tx_map[i] = slot;
tx_mapped |= 1 << slot;
} else
return -EINVAL;
}
for (i = 0; i < rx_num; i++) {
slot = rx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(rx_mapped & (1 << slot)))) {
bf5xx_tdm->rx_map[i] = slot;
rx_mapped |= 1 << slot;
} else
return -EINVAL;
}
return 0;
}
#ifdef CONFIG_PM
static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
if (dai->playback_active)
sport_tx_stop(sport);
if (dai->capture_active)
sport_rx_stop(sport);
/* isolate sync/clock pins from codec while sports resume */
peripheral_free_list(sport->pin_req);
return 0;
}
static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
{
int ret;
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
ret = sport_set_multichannel(sport, 8, 0xFF, 1);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
}
ret = sport_config_rx(sport, 0, 0x1F, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
}
ret = sport_config_tx(sport, 0, 0x1F, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
}
peripheral_request_list(sport->pin_req, "soc-audio");
return 0;
}
#else
#define bf5xx_tdm_suspend NULL
#define bf5xx_tdm_resume NULL
#endif
static const struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
.hw_params = bf5xx_tdm_hw_params,
.set_fmt = bf5xx_tdm_set_dai_fmt,
.shutdown = bf5xx_tdm_shutdown,
.set_channel_map = bf5xx_tdm_set_channel_map,
};
static struct snd_soc_dai_driver bf5xx_tdm_dai = {
.suspend = bf5xx_tdm_suspend,
.resume = bf5xx_tdm_resume,
.playback = {
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE,},
.capture = {
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE,},
.ops = &bf5xx_tdm_dai_ops,
};
static const struct snd_soc_component_driver bf5xx_tdm_component = {
.name = "bf5xx-tdm",
};
static int bfin_tdm_probe(struct platform_device *pdev)
{
struct sport_device *sport_handle;
int ret;
/* configure SPORT for TDM */
sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
sizeof(struct bf5xx_tdm_port));
if (!sport_handle)
return -ENODEV;
/* SPORT works in TDM mode */
ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
goto sport_config_err;
}
ret = sport_config_rx(sport_handle, 0, 0x1F, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
goto sport_config_err;
}
ret = sport_config_tx(sport_handle, 0, 0x1F, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
goto sport_config_err;
}
ret = snd_soc_register_component(&pdev->dev, &bf5xx_tdm_component,
&bf5xx_tdm_dai, 1);
if (ret) {
pr_err("Failed to register DAI: %d\n", ret);
goto sport_config_err;
}
return 0;
sport_config_err:
sport_done(sport_handle);
return ret;
}
static int bfin_tdm_remove(struct platform_device *pdev)
{
struct sport_device *sport_handle = platform_get_drvdata(pdev);
snd_soc_unregister_component(&pdev->dev);
sport_done(sport_handle);
return 0;
}
static struct platform_driver bfin_tdm_driver = {
.probe = bfin_tdm_probe,
.remove = bfin_tdm_remove,
.driver = {
.name = "bfin-tdm",
.owner = THIS_MODULE,
},
};
module_platform_driver(bfin_tdm_driver);
/* Module information */
MODULE_AUTHOR("Barry Song");
MODULE_DESCRIPTION("TDM driver for ADI Blackfin");
MODULE_LICENSE("GPL");

View file

@ -1,23 +0,0 @@
/*
* sound/soc/blackfin/bf5xx-tdm.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 _BF5XX_TDM_H
#define _BF5XX_TDM_H
#define BFIN_TDM_DAI_MAX_SLOTS 8
struct bf5xx_tdm_port {
u16 tcr1;
u16 rcr1;
u16 tcr2;
u16 rcr2;
unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS];
unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS];
int configured;
};
#endif

View file

@ -1,7 +1,7 @@
config SND_EP93XX_SOC
tristate "SoC Audio support for the Cirrus Logic EP93xx series"
depends on ARCH_EP93XX && SND_SOC
select SND_SOC_DMAENGINE_PCM
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for codecs attached to
the EP93xx I2S or AC97 interfaces.

View file

@ -314,22 +314,15 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
return 0;
}
static int ep93xx_ac97_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
static int ep93xx_ac97_dai_probe(struct snd_soc_dai *dai)
{
struct ep93xx_dma_data *dma_data;
dai->playback_dma_data = &ep93xx_ac97_pcm_out;
dai->capture_dma_data = &ep93xx_ac97_pcm_in;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dma_data = &ep93xx_ac97_pcm_out;
else
dma_data = &ep93xx_ac97_pcm_in;
snd_soc_dai_set_dma_data(dai, substream, dma_data);
return 0;
}
static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
.startup = ep93xx_ac97_startup,
.trigger = ep93xx_ac97_trigger,
};
@ -337,6 +330,7 @@ static struct snd_soc_dai_driver ep93xx_ac97_dai = {
.name = "ep93xx-ac97",
.id = 0,
.ac97_control = 1,
.probe = ep93xx_ac97_dai_probe,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
@ -403,7 +397,6 @@ static int ep93xx_ac97_probe(struct platform_device *pdev)
return 0;
fail:
platform_set_drvdata(pdev, NULL);
ep93xx_ac97_info = NULL;
dev_set_drvdata(&pdev->dev, NULL);
return ret;
@ -418,7 +411,6 @@ static int ep93xx_ac97_remove(struct platform_device *pdev)
/* disable the AC97 controller */
ep93xx_ac97_write_reg(info, AC97GCR, 0);
platform_set_drvdata(pdev, NULL);
ep93xx_ac97_info = NULL;
dev_set_drvdata(&pdev->dev, NULL);

View file

@ -60,11 +60,10 @@ struct ep93xx_i2s_info {
struct clk *mclk;
struct clk *sclk;
struct clk *lrclk;
struct ep93xx_dma_data *dma_data;
void __iomem *regs;
};
struct ep93xx_dma_data ep93xx_i2s_dma_data[] = {
static struct ep93xx_dma_data ep93xx_i2s_dma_data[] = {
[SNDRV_PCM_STREAM_PLAYBACK] = {
.name = "i2s-pcm-out",
.port = EP93XX_DMA_I2S1,
@ -139,15 +138,11 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
}
}
static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
dai->playback_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK];
dai->capture_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE];
snd_soc_dai_set_dma_data(cpu_dai, substream,
&info->dma_data[substream->stream]);
return 0;
}
@ -338,7 +333,6 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
#endif
static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
.startup = ep93xx_i2s_startup,
.shutdown = ep93xx_i2s_shutdown,
.hw_params = ep93xx_i2s_hw_params,
.set_sysclk = ep93xx_i2s_set_sysclk,
@ -349,6 +343,7 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
static struct snd_soc_dai_driver ep93xx_i2s_dai = {
.symmetric_rates= 1,
.probe = ep93xx_i2s_dai_probe,
.suspend = ep93xx_i2s_suspend,
.resume = ep93xx_i2s_resume,
.playback = {
@ -407,7 +402,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
}
dev_set_drvdata(&pdev->dev, info);
info->dma_data = ep93xx_i2s_dma_data;
err = snd_soc_register_component(&pdev->dev, &ep93xx_i2s_component,
&ep93xx_i2s_dai, 1);

View file

@ -14,20 +14,14 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include <linux/platform_data/dma-ep93xx.h>
#include <mach/hardware.h>
#include <mach/ep93xx-regs.h>
static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
.info = (SNDRV_PCM_INFO_MMAP |
@ -63,134 +57,24 @@ static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
return false;
}
static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
return snd_dmaengine_pcm_open_request_chan(substream,
ep93xx_pcm_dma_filter,
snd_soc_dai_get_dma_data(rtd->cpu_dai, substream));
}
static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
return 0;
}
static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
return dma_mmap_writecombine(substream->pcm->card->dev, vma,
runtime->dma_area,
runtime->dma_addr,
runtime->dma_bytes);
}
static struct snd_pcm_ops ep93xx_pcm_ops = {
.open = ep93xx_pcm_open,
.close = snd_dmaengine_pcm_close_release_chan,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = ep93xx_pcm_hw_params,
.hw_free = ep93xx_pcm_hw_free,
.trigger = snd_dmaengine_pcm_trigger,
.pointer = snd_dmaengine_pcm_pointer_no_residue,
.mmap = ep93xx_pcm_mmap,
};
static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
buf->area = dma_alloc_writecombine(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
buf->bytes = size;
return (buf->area == NULL) ? -ENOMEM : 0;
}
static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
int stream;
for (stream = 0; stream < 2; stream++) {
substream = pcm->streams[stream].substream;
if (!substream)
continue;
buf = &substream->dma_buffer;
if (!buf->area)
continue;
dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
buf->addr);
buf->area = NULL;
}
}
static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32);
static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)
card->dev->dma_mask = &ep93xx_pcm_dmamask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
return ret;
}
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
return ret;
}
return 0;
}
static struct snd_soc_platform_driver ep93xx_soc_platform = {
.ops = &ep93xx_pcm_ops,
.pcm_new = &ep93xx_pcm_new,
.pcm_free = &ep93xx_pcm_free_dma_buffers,
static const struct snd_dmaengine_pcm_config ep93xx_dmaengine_pcm_config = {
.pcm_hardware = &ep93xx_pcm_hardware,
.compat_filter_fn = ep93xx_pcm_dma_filter,
.prealloc_buffer_size = 131072,
};
static int ep93xx_soc_platform_probe(struct platform_device *pdev)
{
return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform);
return snd_dmaengine_pcm_register(&pdev->dev,
&ep93xx_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
SND_DMAENGINE_PCM_FLAG_NO_DT |
SND_DMAENGINE_PCM_FLAG_COMPAT);
}
static int ep93xx_soc_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_platform(&pdev->dev);
snd_dmaengine_pcm_unregister(&pdev->dev);
return 0;
}

View file

@ -1444,7 +1444,7 @@ static int pm860x_codec_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (!res) {
dev_err(&pdev->dev, "Failed to get IRQ resources\n");
goto out;
return -EINVAL;
}
pm860x->irq[i] = res->start + chip->irq_base;
strncpy(pm860x->name[i], res->name, MAX_NAME_LEN);
@ -1454,19 +1454,14 @@ static int pm860x_codec_probe(struct platform_device *pdev)
pm860x_dai, ARRAY_SIZE(pm860x_dai));
if (ret) {
dev_err(&pdev->dev, "Failed to register codec\n");
goto out;
return -EINVAL;
}
return ret;
out:
platform_set_drvdata(pdev, NULL);
return -EINVAL;
}
static int pm860x_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
platform_set_drvdata(pdev, NULL);
return 0;
}

View file

@ -19,7 +19,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311
select SND_SOC_ADAU1373 if I2C
select SND_SOC_ADAV80X
select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
select SND_SOC_ADS117X
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
@ -40,7 +40,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_DA7213 if I2C
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
select SND_SOC_DFBMCS320
select SND_SOC_BT_SCO
select SND_SOC_ISABELLE if I2C
select SND_SOC_JZ4740_CODEC
select SND_SOC_LM4857 if I2C
@ -53,13 +53,15 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX9877 if I2C
select SND_SOC_MC13783 if MFD_MC13XXX
select SND_SOC_ML26124 if I2C
select SND_SOC_OMAP_HDMI_CODEC if OMAP4_DSS_HDMI
select SND_SOC_HDMI_CODEC
select SND_SOC_PCM3008
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
select SND_SOC_SGTL5000 if I2C
select SND_SOC_SI476X if MFD_SI476X_CORE
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
select SND_SOC_SSM2518 if I2C
select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
select SND_SOC_STA32X if I2C
select SND_SOC_STA529 if I2C
@ -263,7 +265,7 @@ config SND_SOC_DA732X
config SND_SOC_DA9055
tristate
config SND_SOC_DFBMCS320
config SND_SOC_BT_SCO
tristate
config SND_SOC_DMIC
@ -287,7 +289,7 @@ config SND_SOC_MAX98095
config SND_SOC_MAX9850
tristate
config SND_SOC_OMAP_HDMI_CODEC
config SND_SOC_HDMI_CODEC
tristate
config SND_SOC_PCM3008
@ -296,6 +298,9 @@ config SND_SOC_PCM3008
config SND_SOC_RT5631
tristate
config SND_SOC_RT5640
tristate
#Freescale sgtl5000 codec
config SND_SOC_SGTL5000
tristate
@ -313,6 +318,9 @@ config SND_SOC_SN95031
config SND_SOC_SPDIF
tristate
config SND_SOC_SSM2518
tristate
config SND_SOC_SSM2602
tristate

View file

@ -27,7 +27,7 @@ snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
snd-soc-dfbmcs320-objs := dfbmcs320.o
snd-soc-bt-sco-objs := bt-sco.o
snd-soc-dmic-objs := dmic.o
snd-soc-isabelle-objs := isabelle.o
snd-soc-jz4740-codec-objs := jz4740.o
@ -41,17 +41,19 @@ snd-soc-max98095-objs := max98095.o
snd-soc-max9850-objs := max9850.o
snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o
snd-soc-omap-hdmi-codec-objs := omap-hdmi.o
snd-soc-hdmi-codec-objs := hdmi.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
snd-soc-sgtl5000-objs := sgtl5000.o
snd-soc-alc5623-objs := alc5623.o
snd-soc-alc5632-objs := alc5632.o
snd-soc-sigmadsp-objs := sigmadsp.o
snd-soc-si476x-objs := si476x.o
snd-soc-sn95031-objs := sn95031.o
snd-soc-spdif-tx-objs := spdif_transciever.o
snd-soc-spdif-tx-objs := spdif_transmitter.o
snd-soc-spdif-rx-objs := spdif_receiver.o
snd-soc-ssm2518-objs := ssm2518.o
snd-soc-ssm2602-objs := ssm2602.o
snd-soc-sta32x-objs := sta32x.o
snd-soc-sta529-objs := sta529.o
@ -154,7 +156,7 @@ obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
@ -168,14 +170,16 @@ obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
obj-$(CONFIG_SND_SOC_OMAP_HDMI_CODEC) += snd-soc-omap-hdmi-codec.o
obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o

View file

@ -1496,6 +1496,12 @@ static const char * const enum_ad_to_slot_map[] = {"AD_OUT1",
"AD_OUT7",
"AD_OUT8",
"zeroes",
"zeroes",
"zeroes",
"zeroes",
"tristate",
"tristate",
"tristate",
"tristate"};
static SOC_ENUM_SINGLE_DECL(soc_enum_adslot0map,
AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_EVEN_SHIFT,
@ -2230,7 +2236,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
int slots, int slot_width)
{
struct snd_soc_codec *codec = dai->codec;
unsigned int val, mask, slots_active;
unsigned int val, mask, slot, slots_active;
mask = BIT(AB8500_DIGIFCONF2_IF0WL0) |
BIT(AB8500_DIGIFCONF2_IF0WL1);
@ -2286,27 +2292,34 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
/* Setup TDM DA according to active tx slots */
if (tx_mask & ~0xff)
return -EINVAL;
mask = AB8500_DASLOTCONFX_SLTODAX_MASK;
tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET;
slots_active = hweight32(tx_mask);
dev_dbg(dai->codec->dev, "%s: Slots, active, TX: %d\n", __func__,
slots_active);
switch (slots_active) {
case 0:
break;
case 1:
/* Slot 9 -> DA_IN1 & DA_IN3 */
snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 11);
snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 11);
snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11);
snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11);
slot = find_first_bit((unsigned long *)&tx_mask, 32);
snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
break;
case 2:
/* Slot 9 -> DA_IN1 & DA_IN3, Slot 11 -> DA_IN2 & DA_IN4 */
snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 9);
snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 9);
snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11);
snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11);
slot = find_first_bit((unsigned long *)&tx_mask, 32);
snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
slot = find_next_bit((unsigned long *)&tx_mask, 32, slot + 1);
snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
break;
case 8:
dev_dbg(dai->codec->dev,
@ -2321,25 +2334,36 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
}
/* Setup TDM AD according to active RX-slots */
if (rx_mask & ~0xff)
return -EINVAL;
rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET;
slots_active = hweight32(rx_mask);
dev_dbg(dai->codec->dev, "%s: Slots, active, RX: %d\n", __func__,
slots_active);
switch (slots_active) {
case 0:
break;
case 1:
/* AD_OUT3 -> slot 0 & 1 */
snd_soc_update_bits(codec, AB8500_ADSLOTSEL1, AB8500_MASK_ALL,
AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN |
AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD);
slot = find_first_bit((unsigned long *)&rx_mask, 32);
snd_soc_update_bits(codec, AB8500_ADSLOTSEL(slot),
AB8500_MASK_SLOT(slot),
AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
break;
case 2:
/* AD_OUT3 -> slot 0, AD_OUT2 -> slot 1 */
slot = find_first_bit((unsigned long *)&rx_mask, 32);
snd_soc_update_bits(codec,
AB8500_ADSLOTSEL1,
AB8500_MASK_ALL,
AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN |
AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD);
AB8500_ADSLOTSEL(slot),
AB8500_MASK_SLOT(slot),
AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot));
slot = find_next_bit((unsigned long *)&rx_mask, 32, slot + 1);
snd_soc_update_bits(codec,
AB8500_ADSLOTSEL(slot),
AB8500_MASK_SLOT(slot),
AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT2, slot));
break;
case 8:
dev_dbg(dai->codec->dev,
@ -2356,6 +2380,11 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
static const struct snd_soc_dai_ops ab8500_codec_ops = {
.set_fmt = ab8500_codec_set_dai_fmt,
.set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
};
static struct snd_soc_dai_driver ab8500_codec_dai[] = {
{
.name = "ab8500-codec-dai.0",
@ -2367,12 +2396,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.rates = AB8500_SUPPORTED_RATE,
.formats = AB8500_SUPPORTED_FMT,
},
.ops = (struct snd_soc_dai_ops[]) {
{
.set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
.set_fmt = ab8500_codec_set_dai_fmt,
}
},
.ops = &ab8500_codec_ops,
.symmetric_rates = 1
},
{
@ -2385,12 +2409,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.rates = AB8500_SUPPORTED_RATE,
.formats = AB8500_SUPPORTED_FMT,
},
.ops = (struct snd_soc_dai_ops[]) {
{
.set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
.set_fmt = ab8500_codec_set_dai_fmt,
}
},
.ops = &ab8500_codec_ops,
.symmetric_rates = 1
}
};

View file

@ -24,6 +24,13 @@
#define AB8500_SUPPORTED_RATE (SNDRV_PCM_RATE_48000)
#define AB8500_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE)
/* AB8500 interface slot offset definitions */
#define AB8500_AD_DATA0_OFFSET 0
#define AB8500_DA_DATA0_OFFSET 8
#define AB8500_AD_DATA1_OFFSET 16
#define AB8500_DA_DATA1_OFFSET 24
/* AB8500 audio bank (0x0d) register definitions */
#define AB8500_POWERUP 0x00
@ -73,6 +80,7 @@
#define AB8500_ADSLOTSEL14 0x2C
#define AB8500_ADSLOTSEL15 0x2D
#define AB8500_ADSLOTSEL16 0x2E
#define AB8500_ADSLOTSEL(slot) (AB8500_ADSLOTSEL1 + (slot >> 1))
#define AB8500_ADSLOTHIZCTRL1 0x2F
#define AB8500_ADSLOTHIZCTRL2 0x30
#define AB8500_ADSLOTHIZCTRL3 0x31
@ -144,6 +152,7 @@
#define AB8500_CACHEREGNUM (AB8500_LAST_REG + 1)
#define AB8500_MASK_ALL 0xFF
#define AB8500_MASK_SLOT(slot) ((slot & 1) ? 0xF0 : 0x0F)
#define AB8500_MASK_NONE 0x00
/* AB8500_POWERUP */
@ -347,28 +356,21 @@
#define AB8500_DIGIFCONF4_IF1WL0 0
/* AB8500_ADSLOTSELX */
#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_ODD 0x00
#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD 0x10
#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD 0x20
#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_ODD 0x30
#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_ODD 0x40
#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_ODD 0x50
#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_ODD 0x60
#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_ODD 0x70
#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_ODD 0x80
#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_ODD 0xF0
#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_EVEN 0x00
#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_EVEN 0x01
#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN 0x02
#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_EVEN 0x03
#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_EVEN 0x04
#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_EVEN 0x05
#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_EVEN 0x06
#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_EVEN 0x07
#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_EVEN 0x08
#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_EVEN 0x0F
#define AB8500_AD_OUT1 0x0
#define AB8500_AD_OUT2 0x1
#define AB8500_AD_OUT3 0x2
#define AB8500_AD_OUT4 0x3
#define AB8500_AD_OUT5 0x4
#define AB8500_AD_OUT6 0x5
#define AB8500_AD_OUT7 0x6
#define AB8500_AD_OUT8 0x7
#define AB8500_ZEROES 0x8
#define AB8500_TRISTATE 0xF
#define AB8500_ADSLOTSELX_EVEN_SHIFT 0
#define AB8500_ADSLOTSELX_ODD_SHIFT 4
#define AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(out, slot) \
((out) << (((slot) & 1) ? \
AB8500_ADSLOTSELX_ODD_SHIFT : AB8500_ADSLOTSELX_EVEN_SHIFT))
/* AB8500_ADSLOTHIZCTRL1 */
/* AB8500_ADSLOTHIZCTRL2 */

View file

@ -13,6 +13,9 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@ -87,6 +90,7 @@
#define ADAU1701_FIRMWARE "adau1701.bin"
struct adau1701 {
int gpio_nreset;
unsigned int dai_fmt;
};
@ -180,9 +184,37 @@ static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg)
return value;
}
static int adau1701_load_firmware(struct snd_soc_codec *codec)
static void adau1701_reset(struct snd_soc_codec *codec)
{
return process_sigma_firmware(codec->control_data, ADAU1701_FIRMWARE);
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
if (!gpio_is_valid(adau1701->gpio_nreset))
return;
gpio_set_value(adau1701->gpio_nreset, 0);
/* minimum reset time is 20ns */
udelay(1);
gpio_set_value(adau1701->gpio_nreset, 1);
/* power-up time may be as long as 85ms */
mdelay(85);
}
static int adau1701_init(struct snd_soc_codec *codec)
{
int ret;
struct i2c_client *client = to_i2c_client(codec->dev);
adau1701_reset(codec);
ret = process_sigma_firmware(client, ADAU1701_FIRMWARE);
if (ret) {
dev_warn(codec->dev, "Failed to load firmware\n");
return ret;
}
snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
return 0;
}
static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
@ -452,17 +484,24 @@ static struct snd_soc_dai_driver adau1701_dai = {
.symmetric_rates = 1,
};
#ifdef CONFIG_OF
static const struct of_device_id adau1701_dt_ids[] = {
{ .compatible = "adi,adau1701", },
{ }
};
MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
#endif
static int adau1701_probe(struct snd_soc_codec *codec)
{
int ret;
codec->control_data = to_i2c_client(codec->dev);
ret = adau1701_load_firmware(codec);
ret = adau1701_init(codec);
if (ret)
dev_warn(codec->dev, "Failed to load firmware\n");
return ret;
snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
snd_soc_write(codec, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
return 0;
@ -493,12 +532,29 @@ static int adau1701_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adau1701 *adau1701;
struct device *dev = &client->dev;
int gpio_nreset = -EINVAL;
int ret;
adau1701 = devm_kzalloc(&client->dev, sizeof(*adau1701), GFP_KERNEL);
adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
if (!adau1701)
return -ENOMEM;
if (dev->of_node) {
gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
if (gpio_nreset < 0 && gpio_nreset != -ENOENT)
return gpio_nreset;
}
if (gpio_is_valid(gpio_nreset)) {
ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
"ADAU1701 Reset");
if (ret < 0)
return ret;
}
adau1701->gpio_nreset = gpio_nreset;
i2c_set_clientdata(client, adau1701);
ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
&adau1701_dai, 1);
@ -521,6 +577,7 @@ static struct i2c_driver adau1701_i2c_driver = {
.driver = {
.name = "adau1701",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(adau1701_dt_ids),
},
.probe = adau1701_i2c_probe,
.remove = adau1701_i2c_remove,

View file

@ -1198,6 +1198,13 @@ const struct snd_soc_dai_ops arizona_dai_ops = {
};
EXPORT_SYMBOL_GPL(arizona_dai_ops);
const struct snd_soc_dai_ops arizona_simple_dai_ops = {
.startup = arizona_startup,
.hw_params = arizona_hw_params_rate,
.set_sysclk = arizona_dai_set_sysclk,
};
EXPORT_SYMBOL_GPL(arizona_simple_dai_ops);
int arizona_init_dai(struct arizona_priv *priv, int id)
{
struct arizona_dai_priv *dai_priv = &priv->dai[id];

View file

@ -57,7 +57,7 @@
#define ARIZONA_CLK_98MHZ 5
#define ARIZONA_CLK_147MHZ 6
#define ARIZONA_MAX_DAI 4
#define ARIZONA_MAX_DAI 6
#define ARIZONA_MAX_ADSP 4
struct arizona;
@ -213,6 +213,7 @@ extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir);
extern const struct snd_soc_dai_ops arizona_dai_ops;
extern const struct snd_soc_dai_ops arizona_simple_dai_ops;
#define ARIZONA_FLL_NAME_LEN 20

View file

@ -1,5 +1,5 @@
/*
* Driver for the DFBM-CS320 bluetooth module
* Driver for generic Bluetooth SCO link
* Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
*
* This program is free software; you can redistribute it and/or modify it
@ -15,8 +15,8 @@
#include <sound/soc.h>
static struct snd_soc_dai_driver dfbmcs320_dai = {
.name = "dfbmcs320-pcm",
static struct snd_soc_dai_driver bt_sco_dai = {
.name = "bt-sco-pcm",
.playback = {
.channels_min = 1,
.channels_max = 1,
@ -31,32 +31,41 @@ static struct snd_soc_dai_driver dfbmcs320_dai = {
},
};
static struct snd_soc_codec_driver soc_codec_dev_dfbmcs320;
static struct snd_soc_codec_driver soc_codec_dev_bt_sco;
static int dfbmcs320_probe(struct platform_device *pdev)
static int bt_sco_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_dfbmcs320,
&dfbmcs320_dai, 1);
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco,
&bt_sco_dai, 1);
}
static int dfbmcs320_remove(struct platform_device *pdev)
static int bt_sco_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver dfmcs320_driver = {
static struct platform_device_id bt_sco_driver_ids[] = {
{
.name = "dfbmcs320",
},
{},
};
MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
static struct platform_driver bt_sco_driver = {
.driver = {
.name = "dfbmcs320",
.name = "bt-sco",
.owner = THIS_MODULE,
},
.probe = dfbmcs320_probe,
.remove = dfbmcs320_remove,
.probe = bt_sco_probe,
.remove = bt_sco_remove,
.id_table = bt_sco_driver_ids,
};
module_platform_driver(dfmcs320_driver);
module_platform_driver(bt_sco_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("ASoC DFBM-CS320 bluethooth module driver");
MODULE_DESCRIPTION("ASoC generic bluethooth sco link driver");
MODULE_LICENSE("GPL");

View file

@ -1,5 +1,5 @@
/*
* ALSA SoC codec driver for HDMI audio on OMAP processors.
* ALSA SoC codec driver for HDMI audio codecs.
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
* Author: Ricardo Neri <ricardo.neri@ti.com>
*
@ -23,10 +23,10 @@
#define DRV_NAME "hdmi-audio-codec"
static struct snd_soc_codec_driver omap_hdmi_codec;
static struct snd_soc_codec_driver hdmi_codec;
static struct snd_soc_dai_driver omap_hdmi_codec_dai = {
.name = "omap-hdmi-hifi",
static struct snd_soc_dai_driver hdmi_codec_dai = {
.name = "hdmi-hifi",
.playback = {
.channels_min = 2,
.channels_max = 8,
@ -39,31 +39,31 @@ static struct snd_soc_dai_driver omap_hdmi_codec_dai = {
},
};
static int omap_hdmi_codec_probe(struct platform_device *pdev)
static int hdmi_codec_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &omap_hdmi_codec,
&omap_hdmi_codec_dai, 1);
return snd_soc_register_codec(&pdev->dev, &hdmi_codec,
&hdmi_codec_dai, 1);
}
static int omap_hdmi_codec_remove(struct platform_device *pdev)
static int hdmi_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver omap_hdmi_codec_driver = {
static struct platform_driver hdmi_codec_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = omap_hdmi_codec_probe,
.remove = omap_hdmi_codec_remove,
.probe = hdmi_codec_probe,
.remove = hdmi_codec_remove,
};
module_platform_driver(omap_hdmi_codec_driver);
module_platform_driver(hdmi_codec_driver);
MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
MODULE_DESCRIPTION("ASoC OMAP HDMI codec driver");
MODULE_DESCRIPTION("ASoC generic HDMI codec driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

View file

@ -384,8 +384,6 @@ static int jz4740_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
platform_set_drvdata(pdev, NULL);
return 0;
}

View file

@ -857,6 +857,14 @@ static const struct soc_enum mic2_mux_enum =
static const struct snd_kcontrol_new max98090_mic2_mux =
SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum);
static const char *dmic_mux_text[] = { "ADC", "DMIC" };
static const struct soc_enum dmic_mux_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dmic_mux_text), dmic_mux_text);
static const struct snd_kcontrol_new max98090_dmic_mux =
SOC_DAPM_ENUM_VIRT("DMIC Mux", dmic_mux_enum);
static const char *max98090_micpre_text[] = { "Off", "On" };
static const struct soc_enum max98090_pa1en_enum =
@ -1144,6 +1152,9 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
SND_SOC_DAPM_MUX("MIC2 Mux", SND_SOC_NOPM,
0, 0, &max98090_mic2_mux),
SND_SOC_DAPM_VIRT_MUX("DMIC Mux", SND_SOC_NOPM,
0, 0, &max98090_dmic_mux),
SND_SOC_DAPM_PGA_E("MIC1 Input", M98090_REG_MIC1_INPUT_LEVEL,
M98090_MIC_PA1EN_SHIFT, 0, NULL, 0, max98090_micinput_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
@ -1336,11 +1347,14 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"ADCL", NULL, "SHDN"},
{"ADCR", NULL, "SHDN"},
{"LBENL Mux", "Normal", "ADCL"},
{"LBENL Mux", "Normal", "DMICL"},
{"DMIC Mux", "ADC", "ADCL"},
{"DMIC Mux", "ADC", "ADCR"},
{"DMIC Mux", "DMIC", "DMICL"},
{"DMIC Mux", "DMIC", "DMICR"},
{"LBENL Mux", "Normal", "DMIC Mux"},
{"LBENL Mux", "Loopback", "LTENL Mux"},
{"LBENR Mux", "Normal", "ADCR"},
{"LBENR Mux", "Normal", "DMICR"},
{"LBENR Mux", "Normal", "DMIC Mux"},
{"LBENR Mux", "Loopback", "LTENR Mux"},
{"AIFOUTL", NULL, "LBENL Mux"},
@ -2336,6 +2350,7 @@ static int max98090_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM_RUNTIME
static int max98090_runtime_resume(struct device *dev)
{
struct max98090_priv *max98090 = dev_get_drvdata(dev);
@ -2355,6 +2370,7 @@ static int max98090_runtime_suspend(struct device *dev)
return 0;
}
#endif
static const struct dev_pm_ops max98090_pm = {
SET_RUNTIME_PM_OPS(max98090_runtime_suspend,

2128
sound/soc/codecs/rt5640.c Normal file

File diff suppressed because it is too large Load diff

2092
sound/soc/codecs/rt5640.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/consumer.h>
@ -34,30 +35,30 @@
#define SGTL5000_MAX_REG_OFFSET 0x013A
/* default value of sgtl5000 registers */
static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET] = {
[SGTL5000_CHIP_CLK_CTRL] = 0x0008,
[SGTL5000_CHIP_I2S_CTRL] = 0x0010,
[SGTL5000_CHIP_SSS_CTRL] = 0x0008,
[SGTL5000_CHIP_DAC_VOL] = 0x3c3c,
[SGTL5000_CHIP_PAD_STRENGTH] = 0x015f,
[SGTL5000_CHIP_ANA_HP_CTRL] = 0x1818,
[SGTL5000_CHIP_ANA_CTRL] = 0x0111,
[SGTL5000_CHIP_LINE_OUT_VOL] = 0x0404,
[SGTL5000_CHIP_ANA_POWER] = 0x7060,
[SGTL5000_CHIP_PLL_CTRL] = 0x5000,
[SGTL5000_DAP_BASS_ENHANCE] = 0x0040,
[SGTL5000_DAP_BASS_ENHANCE_CTRL] = 0x051f,
[SGTL5000_DAP_SURROUND] = 0x0040,
[SGTL5000_DAP_EQ_BASS_BAND0] = 0x002f,
[SGTL5000_DAP_EQ_BASS_BAND1] = 0x002f,
[SGTL5000_DAP_EQ_BASS_BAND2] = 0x002f,
[SGTL5000_DAP_EQ_BASS_BAND3] = 0x002f,
[SGTL5000_DAP_EQ_BASS_BAND4] = 0x002f,
[SGTL5000_DAP_MAIN_CHAN] = 0x8000,
[SGTL5000_DAP_AVC_CTRL] = 0x0510,
[SGTL5000_DAP_AVC_THRESHOLD] = 0x1473,
[SGTL5000_DAP_AVC_ATTACK] = 0x0028,
[SGTL5000_DAP_AVC_DECAY] = 0x0050,
static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_CHIP_CLK_CTRL, 0x0008 },
{ SGTL5000_CHIP_I2S_CTRL, 0x0010 },
{ SGTL5000_CHIP_SSS_CTRL, 0x0008 },
{ SGTL5000_CHIP_DAC_VOL, 0x3c3c },
{ SGTL5000_CHIP_PAD_STRENGTH, 0x015f },
{ SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 },
{ SGTL5000_CHIP_ANA_CTRL, 0x0111 },
{ SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 },
{ SGTL5000_CHIP_ANA_POWER, 0x7060 },
{ SGTL5000_CHIP_PLL_CTRL, 0x5000 },
{ SGTL5000_DAP_BASS_ENHANCE, 0x0040 },
{ SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f },
{ SGTL5000_DAP_SURROUND, 0x0040 },
{ SGTL5000_DAP_EQ_BASS_BAND0, 0x002f },
{ SGTL5000_DAP_EQ_BASS_BAND1, 0x002f },
{ SGTL5000_DAP_EQ_BASS_BAND2, 0x002f },
{ SGTL5000_DAP_EQ_BASS_BAND3, 0x002f },
{ SGTL5000_DAP_EQ_BASS_BAND4, 0x002f },
{ SGTL5000_DAP_MAIN_CHAN, 0x8000 },
{ SGTL5000_DAP_AVC_CTRL, 0x0510 },
{ SGTL5000_DAP_AVC_THRESHOLD, 0x1473 },
{ SGTL5000_DAP_AVC_ATTACK, 0x0028 },
{ SGTL5000_DAP_AVC_DECAY, 0x0050 },
};
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
@ -112,6 +113,8 @@ struct sgtl5000_priv {
int fmt; /* i2s data format */
struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
struct ldo_regulator *ldo;
struct regmap *regmap;
struct clk *mclk;
};
/*
@ -151,12 +154,12 @@ static int power_vag_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
break;
case SND_SOC_DAPM_POST_PMD:
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_VAG_POWERUP, 0);
msleep(400);
@ -217,12 +220,11 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
0, SGTL5000_CHIP_DIG_POWER,
1, 0),
SND_SOC_DAPM_SUPPLY("VAG_POWER", SGTL5000_CHIP_ANA_POWER, 7, 0,
power_vag_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0),
SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0),
SND_SOC_DAPM_PRE("VAG_POWER_PRE", power_vag_event),
SND_SOC_DAPM_POST("VAG_POWER_POST", power_vag_event),
};
/* routes for sgtl5000 */
@ -230,16 +232,13 @@ static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = {
{"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */
{"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */
{"ADC", NULL, "VAG_POWER"},
{"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */
{"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */
{"DAC", NULL, "VAG_POWER"},
{"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */
{"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */
{"LO", NULL, "DAC"}, /* dac --> line_out */
{"LINE_IN", NULL, "VAG_POWER"},
{"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */
{"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */
@ -909,10 +908,25 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
if (ret)
return ret;
udelay(10);
regcache_cache_only(sgtl5000->regmap, false);
ret = regcache_sync(sgtl5000->regmap);
if (ret != 0) {
dev_err(codec->dev,
"Failed to restore cache: %d\n", ret);
regcache_cache_only(sgtl5000->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
return ret;
}
}
break;
case SND_SOC_BIAS_OFF:
regcache_cache_only(sgtl5000->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
break;
@ -958,17 +972,76 @@ static struct snd_soc_dai_driver sgtl5000_dai = {
.symmetric_rates = 1,
};
static int sgtl5000_volatile_register(struct snd_soc_codec *codec,
unsigned int reg)
static bool sgtl5000_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case SGTL5000_CHIP_ID:
case SGTL5000_CHIP_ADCDAC_CTRL:
case SGTL5000_CHIP_ANA_STATUS:
return 1;
return true;
}
return 0;
return false;
}
static bool sgtl5000_readable(struct device *dev, unsigned int reg)
{
switch (reg) {
case SGTL5000_CHIP_ID:
case SGTL5000_CHIP_DIG_POWER:
case SGTL5000_CHIP_CLK_CTRL:
case SGTL5000_CHIP_I2S_CTRL:
case SGTL5000_CHIP_SSS_CTRL:
case SGTL5000_CHIP_ADCDAC_CTRL:
case SGTL5000_CHIP_DAC_VOL:
case SGTL5000_CHIP_PAD_STRENGTH:
case SGTL5000_CHIP_ANA_ADC_CTRL:
case SGTL5000_CHIP_ANA_HP_CTRL:
case SGTL5000_CHIP_ANA_CTRL:
case SGTL5000_CHIP_LINREG_CTRL:
case SGTL5000_CHIP_REF_CTRL:
case SGTL5000_CHIP_MIC_CTRL:
case SGTL5000_CHIP_LINE_OUT_CTRL:
case SGTL5000_CHIP_LINE_OUT_VOL:
case SGTL5000_CHIP_ANA_POWER:
case SGTL5000_CHIP_PLL_CTRL:
case SGTL5000_CHIP_CLK_TOP_CTRL:
case SGTL5000_CHIP_ANA_STATUS:
case SGTL5000_CHIP_SHORT_CTRL:
case SGTL5000_CHIP_ANA_TEST2:
case SGTL5000_DAP_CTRL:
case SGTL5000_DAP_PEQ:
case SGTL5000_DAP_BASS_ENHANCE:
case SGTL5000_DAP_BASS_ENHANCE_CTRL:
case SGTL5000_DAP_AUDIO_EQ:
case SGTL5000_DAP_SURROUND:
case SGTL5000_DAP_FLT_COEF_ACCESS:
case SGTL5000_DAP_COEF_WR_B0_MSB:
case SGTL5000_DAP_COEF_WR_B0_LSB:
case SGTL5000_DAP_EQ_BASS_BAND0:
case SGTL5000_DAP_EQ_BASS_BAND1:
case SGTL5000_DAP_EQ_BASS_BAND2:
case SGTL5000_DAP_EQ_BASS_BAND3:
case SGTL5000_DAP_EQ_BASS_BAND4:
case SGTL5000_DAP_MAIN_CHAN:
case SGTL5000_DAP_MIX_CHAN:
case SGTL5000_DAP_AVC_CTRL:
case SGTL5000_DAP_AVC_THRESHOLD:
case SGTL5000_DAP_AVC_ATTACK:
case SGTL5000_DAP_AVC_DECAY:
case SGTL5000_DAP_COEF_WR_B1_MSB:
case SGTL5000_DAP_COEF_WR_B1_LSB:
case SGTL5000_DAP_COEF_WR_B2_MSB:
case SGTL5000_DAP_COEF_WR_B2_LSB:
case SGTL5000_DAP_COEF_WR_A1_MSB:
case SGTL5000_DAP_COEF_WR_A1_LSB:
case SGTL5000_DAP_COEF_WR_A2_MSB:
case SGTL5000_DAP_COEF_WR_A2_LSB:
return true;
default:
return false;
}
}
#ifdef CONFIG_SUSPEND
@ -1214,7 +1287,7 @@ static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec)
static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
{
u16 reg;
int reg;
int ret;
int rev;
int i;
@ -1242,23 +1315,17 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
/* wait for all power rails bring up */
udelay(10);
/* read chip information */
reg = snd_soc_read(codec, SGTL5000_CHIP_ID);
if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
SGTL5000_PARTID_PART_ID) {
dev_err(codec->dev,
"Device with ID register %x is not a sgtl5000\n", reg);
ret = -ENODEV;
goto err_regulator_disable;
}
rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
dev_info(codec->dev, "sgtl5000 revision 0x%x\n", rev);
/*
* workaround for revision 0x11 and later,
* roll back to use internal LDO
*/
ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
if (ret)
goto err_regulator_disable;
rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
if (external_vddd && rev >= 0x11) {
/* disable all regulator first */
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
@ -1300,7 +1367,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
/* setup i2c data ops */
ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
codec->control_data = sgtl5000->regmap;
ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
@ -1391,11 +1459,6 @@ static struct snd_soc_codec_driver sgtl5000_driver = {
.suspend = sgtl5000_suspend,
.resume = sgtl5000_resume,
.set_bias_level = sgtl5000_set_bias_level,
.reg_cache_size = ARRAY_SIZE(sgtl5000_regs),
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = sgtl5000_regs,
.volatile_register = sgtl5000_volatile_register,
.controls = sgtl5000_snd_controls,
.num_controls = ARRAY_SIZE(sgtl5000_snd_controls),
.dapm_widgets = sgtl5000_dapm_widgets,
@ -1404,28 +1467,114 @@ static struct snd_soc_codec_driver sgtl5000_driver = {
.num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes),
};
static const struct regmap_config sgtl5000_regmap = {
.reg_bits = 16,
.val_bits = 16,
.max_register = SGTL5000_MAX_REG_OFFSET,
.volatile_reg = sgtl5000_volatile,
.readable_reg = sgtl5000_readable,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = sgtl5000_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(sgtl5000_reg_defaults),
};
/*
* Write all the default values from sgtl5000_reg_defaults[] array into the
* sgtl5000 registers, to make sure we always start with the sane registers
* values as stated in the datasheet.
*
* Since sgtl5000 does not have a reset line, nor a reset command in software,
* we follow this approach to guarantee we always start from the default values
* and avoid problems like, not being able to probe after an audio playback
* followed by a system reset or a 'reboot' command in Linux
*/
static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
{
int i, ret, val, index;
for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) {
val = sgtl5000_reg_defaults[i].def;
index = sgtl5000_reg_defaults[i].reg;
ret = regmap_write(sgtl5000->regmap, index, val);
if (ret)
return ret;
}
return 0;
}
static int sgtl5000_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct sgtl5000_priv *sgtl5000;
int ret;
int ret, reg, rev;
sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv),
GFP_KERNEL);
if (!sgtl5000)
return -ENOMEM;
sgtl5000->regmap = devm_regmap_init_i2c(client, &sgtl5000_regmap);
if (IS_ERR(sgtl5000->regmap)) {
ret = PTR_ERR(sgtl5000->regmap);
dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
return ret;
}
sgtl5000->mclk = devm_clk_get(&client->dev, NULL);
if (IS_ERR(sgtl5000->mclk)) {
ret = PTR_ERR(sgtl5000->mclk);
dev_err(&client->dev, "Failed to get mclock: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(sgtl5000->mclk);
if (ret)
return ret;
/* read chip information */
ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
if (ret)
goto disable_clk;
if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
SGTL5000_PARTID_PART_ID) {
dev_err(&client->dev,
"Device with ID register %x is not a sgtl5000\n", reg);
ret = -ENODEV;
goto disable_clk;
}
rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
i2c_set_clientdata(client, sgtl5000);
/* Ensure sgtl5000 will start with sane register values */
ret = sgtl5000_fill_defaults(sgtl5000);
if (ret)
goto disable_clk;
ret = snd_soc_register_codec(&client->dev,
&sgtl5000_driver, &sgtl5000_dai, 1);
if (ret)
goto disable_clk;
return 0;
disable_clk:
clk_disable_unprepare(sgtl5000->mclk);
return ret;
}
static int sgtl5000_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
snd_soc_unregister_codec(&client->dev);
clk_disable_unprepare(sgtl5000->mclk);
return 0;
}

View file

@ -12,7 +12,7 @@
#define _SGTL5000_H
/*
* Register values.
* Registers addresses
*/
#define SGTL5000_CHIP_ID 0x0000
#define SGTL5000_CHIP_DIG_POWER 0x0002

View file

@ -883,7 +883,7 @@ static int sn95031_codec_remove(struct snd_soc_codec *codec)
return 0;
}
struct snd_soc_codec_driver sn95031_codec = {
static struct snd_soc_codec_driver sn95031_codec = {
.probe = sn95031_codec_probe,
.remove = sn95031_codec_remove,
.read = sn95031_read,

View file

@ -21,6 +21,7 @@
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <linux/of.h>
#define STUB_RATES SNDRV_PCM_RATE_8000_192000
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
@ -51,12 +52,21 @@ static int spdif_dir_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id spdif_dir_dt_ids[] = {
{ .compatible = "linux,spdif-dir", },
{ }
};
MODULE_DEVICE_TABLE(of, spdif_dir_dt_ids);
#endif
static struct platform_driver spdif_dir_driver = {
.probe = spdif_dir_probe,
.remove = spdif_dir_remove,
.driver = {
.name = "spdif-dir",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(spdif_dir_dt_ids),
},
};

View file

@ -20,6 +20,7 @@
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <linux/of.h>
#define DRV_NAME "spdif-dit"
@ -52,12 +53,21 @@ static int spdif_dit_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id spdif_dit_dt_ids[] = {
{ .compatible = "linux,spdif-dit", },
{ }
};
MODULE_DEVICE_TABLE(of, spdif_dit_dt_ids);
#endif
static struct platform_driver spdif_dit_driver = {
.probe = spdif_dit_probe,
.remove = spdif_dit_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(spdif_dit_dt_ids),
},
};

856
sound/soc/codecs/ssm2518.c Normal file
View file

@ -0,0 +1,856 @@
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/platform_data/ssm2518.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "ssm2518.h"
#define SSM2518_REG_POWER1 0x00
#define SSM2518_REG_CLOCK 0x01
#define SSM2518_REG_SAI_CTRL1 0x02
#define SSM2518_REG_SAI_CTRL2 0x03
#define SSM2518_REG_CHAN_MAP 0x04
#define SSM2518_REG_LEFT_VOL 0x05
#define SSM2518_REG_RIGHT_VOL 0x06
#define SSM2518_REG_MUTE_CTRL 0x07
#define SSM2518_REG_FAULT_CTRL 0x08
#define SSM2518_REG_POWER2 0x09
#define SSM2518_REG_DRC_1 0x0a
#define SSM2518_REG_DRC_2 0x0b
#define SSM2518_REG_DRC_3 0x0c
#define SSM2518_REG_DRC_4 0x0d
#define SSM2518_REG_DRC_5 0x0e
#define SSM2518_REG_DRC_6 0x0f
#define SSM2518_REG_DRC_7 0x10
#define SSM2518_REG_DRC_8 0x11
#define SSM2518_REG_DRC_9 0x12
#define SSM2518_POWER1_RESET BIT(7)
#define SSM2518_POWER1_NO_BCLK BIT(5)
#define SSM2518_POWER1_MCS_MASK (0xf << 1)
#define SSM2518_POWER1_MCS_64FS (0x0 << 1)
#define SSM2518_POWER1_MCS_128FS (0x1 << 1)
#define SSM2518_POWER1_MCS_256FS (0x2 << 1)
#define SSM2518_POWER1_MCS_384FS (0x3 << 1)
#define SSM2518_POWER1_MCS_512FS (0x4 << 1)
#define SSM2518_POWER1_MCS_768FS (0x5 << 1)
#define SSM2518_POWER1_MCS_100FS (0x6 << 1)
#define SSM2518_POWER1_MCS_200FS (0x7 << 1)
#define SSM2518_POWER1_MCS_400FS (0x8 << 1)
#define SSM2518_POWER1_SPWDN BIT(0)
#define SSM2518_CLOCK_ASR BIT(0)
#define SSM2518_SAI_CTRL1_FMT_MASK (0x3 << 5)
#define SSM2518_SAI_CTRL1_FMT_I2S (0x0 << 5)
#define SSM2518_SAI_CTRL1_FMT_LJ (0x1 << 5)
#define SSM2518_SAI_CTRL1_FMT_RJ_24BIT (0x2 << 5)
#define SSM2518_SAI_CTRL1_FMT_RJ_16BIT (0x3 << 5)
#define SSM2518_SAI_CTRL1_SAI_MASK (0x7 << 2)
#define SSM2518_SAI_CTRL1_SAI_I2S (0x0 << 2)
#define SSM2518_SAI_CTRL1_SAI_TDM_2 (0x1 << 2)
#define SSM2518_SAI_CTRL1_SAI_TDM_4 (0x2 << 2)
#define SSM2518_SAI_CTRL1_SAI_TDM_8 (0x3 << 2)
#define SSM2518_SAI_CTRL1_SAI_TDM_16 (0x4 << 2)
#define SSM2518_SAI_CTRL1_SAI_MONO (0x5 << 2)
#define SSM2518_SAI_CTRL1_FS_MASK (0x3)
#define SSM2518_SAI_CTRL1_FS_8000_12000 (0x0)
#define SSM2518_SAI_CTRL1_FS_16000_24000 (0x1)
#define SSM2518_SAI_CTRL1_FS_32000_48000 (0x2)
#define SSM2518_SAI_CTRL1_FS_64000_96000 (0x3)
#define SSM2518_SAI_CTRL2_BCLK_INTERAL BIT(7)
#define SSM2518_SAI_CTRL2_LRCLK_PULSE BIT(6)
#define SSM2518_SAI_CTRL2_LRCLK_INVERT BIT(5)
#define SSM2518_SAI_CTRL2_MSB BIT(4)
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK (0x3 << 2)
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_32 (0x0 << 2)
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_24 (0x1 << 2)
#define SSM2518_SAI_CTRL2_SLOT_WIDTH_16 (0x2 << 2)
#define SSM2518_SAI_CTRL2_BCLK_INVERT BIT(1)
#define SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET 4
#define SSM2518_CHAN_MAP_RIGHT_SLOT_MASK 0xf0
#define SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET 0
#define SSM2518_CHAN_MAP_LEFT_SLOT_MASK 0x0f
#define SSM2518_MUTE_CTRL_ANA_GAIN BIT(5)
#define SSM2518_MUTE_CTRL_MUTE_MASTER BIT(0)
#define SSM2518_POWER2_APWDN BIT(0)
#define SSM2518_DAC_MUTE BIT(6)
#define SSM2518_DAC_FS_MASK 0x07
#define SSM2518_DAC_FS_8000 0x00
#define SSM2518_DAC_FS_16000 0x01
#define SSM2518_DAC_FS_32000 0x02
#define SSM2518_DAC_FS_64000 0x03
#define SSM2518_DAC_FS_128000 0x04
struct ssm2518 {
struct regmap *regmap;
bool right_j;
unsigned int sysclk;
const struct snd_pcm_hw_constraint_list *constraints;
int enable_gpio;
};
static const struct reg_default ssm2518_reg_defaults[] = {
{ 0x00, 0x05 },
{ 0x01, 0x00 },
{ 0x02, 0x02 },
{ 0x03, 0x00 },
{ 0x04, 0x10 },
{ 0x05, 0x40 },
{ 0x06, 0x40 },
{ 0x07, 0x81 },
{ 0x08, 0x0c },
{ 0x09, 0x99 },
{ 0x0a, 0x7c },
{ 0x0b, 0x5b },
{ 0x0c, 0x57 },
{ 0x0d, 0x89 },
{ 0x0e, 0x8c },
{ 0x0f, 0x77 },
{ 0x10, 0x26 },
{ 0x11, 0x1c },
{ 0x12, 0x97 },
};
static const DECLARE_TLV_DB_MINMAX_MUTE(ssm2518_vol_tlv, -7125, 2400);
static const DECLARE_TLV_DB_SCALE(ssm2518_compressor_tlv, -3400, 200, 0);
static const DECLARE_TLV_DB_SCALE(ssm2518_expander_tlv, -8100, 300, 0);
static const DECLARE_TLV_DB_SCALE(ssm2518_noise_gate_tlv, -9600, 300, 0);
static const DECLARE_TLV_DB_SCALE(ssm2518_post_drc_tlv, -2400, 300, 0);
static const DECLARE_TLV_DB_RANGE(ssm2518_limiter_tlv,
0, 7, TLV_DB_SCALE_ITEM(-2200, 200, 0),
7, 15, TLV_DB_SCALE_ITEM(-800, 100, 0),
);
static const char * const ssm2518_drc_peak_detector_attack_time_text[] = {
"0 ms", "0.1 ms", "0.19 ms", "0.37 ms", "0.75 ms", "1.5 ms", "3 ms",
"6 ms", "12 ms", "24 ms", "48 ms", "96 ms", "192 ms", "384 ms",
"768 ms", "1536 ms",
};
static const char * const ssm2518_drc_peak_detector_release_time_text[] = {
"0 ms", "1.5 ms", "3 ms", "6 ms", "12 ms", "24 ms", "48 ms", "96 ms",
"192 ms", "384 ms", "768 ms", "1536 ms", "3072 ms", "6144 ms",
"12288 ms", "24576 ms"
};
static const char * const ssm2518_drc_hold_time_text[] = {
"0 ms", "0.67 ms", "1.33 ms", "2.67 ms", "5.33 ms", "10.66 ms",
"21.32 ms", "42.64 ms", "85.28 ms", "170.56 ms", "341.12 ms",
"682.24 ms", "1364 ms",
};
static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum,
SSM2518_REG_DRC_2, 4, ssm2518_drc_peak_detector_attack_time_text);
static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum,
SSM2518_REG_DRC_2, 0, ssm2518_drc_peak_detector_release_time_text);
static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum,
SSM2518_REG_DRC_6, 4, ssm2518_drc_peak_detector_attack_time_text);
static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum,
SSM2518_REG_DRC_6, 0, ssm2518_drc_peak_detector_release_time_text);
static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum,
SSM2518_REG_DRC_7, 4, ssm2518_drc_hold_time_text);
static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum,
SSM2518_REG_DRC_7, 0, ssm2518_drc_hold_time_text);
static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum,
SSM2518_REG_DRC_9, 0, ssm2518_drc_peak_detector_release_time_text);
static const struct snd_kcontrol_new ssm2518_snd_controls[] = {
SOC_SINGLE("Playback De-emphasis Switch", SSM2518_REG_MUTE_CTRL,
4, 1, 0),
SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2518_REG_LEFT_VOL,
SSM2518_REG_RIGHT_VOL, 0, 0xff, 1, ssm2518_vol_tlv),
SOC_DOUBLE("Master Playback Switch", SSM2518_REG_MUTE_CTRL, 2, 1, 1, 1),
SOC_SINGLE("Amp Low Power Mode Switch", SSM2518_REG_POWER2, 4, 1, 0),
SOC_SINGLE("DAC Low Power Mode Switch", SSM2518_REG_POWER2, 3, 1, 0),
SOC_SINGLE("DRC Limiter Switch", SSM2518_REG_DRC_1, 5, 1, 0),
SOC_SINGLE("DRC Compressor Switch", SSM2518_REG_DRC_1, 4, 1, 0),
SOC_SINGLE("DRC Expander Switch", SSM2518_REG_DRC_1, 3, 1, 0),
SOC_SINGLE("DRC Noise Gate Switch", SSM2518_REG_DRC_1, 2, 1, 0),
SOC_DOUBLE("DRC Switch", SSM2518_REG_DRC_1, 0, 1, 1, 0),
SOC_SINGLE_TLV("DRC Limiter Threshold Volume",
SSM2518_REG_DRC_3, 4, 15, 1, ssm2518_limiter_tlv),
SOC_SINGLE_TLV("DRC Compressor Lower Threshold Volume",
SSM2518_REG_DRC_3, 0, 15, 1, ssm2518_compressor_tlv),
SOC_SINGLE_TLV("DRC Expander Upper Threshold Volume", SSM2518_REG_DRC_4,
4, 15, 1, ssm2518_expander_tlv),
SOC_SINGLE_TLV("DRC Noise Gate Threshold Volume",
SSM2518_REG_DRC_4, 0, 15, 1, ssm2518_noise_gate_tlv),
SOC_SINGLE_TLV("DRC Upper Output Threshold Volume",
SSM2518_REG_DRC_5, 4, 15, 1, ssm2518_limiter_tlv),
SOC_SINGLE_TLV("DRC Lower Output Threshold Volume",
SSM2518_REG_DRC_5, 0, 15, 1, ssm2518_noise_gate_tlv),
SOC_SINGLE_TLV("DRC Post Volume", SSM2518_REG_DRC_8,
2, 15, 1, ssm2518_post_drc_tlv),
SOC_ENUM("DRC Peak Detector Attack Time",
ssm2518_drc_peak_detector_attack_time_enum),
SOC_ENUM("DRC Peak Detector Release Time",
ssm2518_drc_peak_detector_release_time_enum),
SOC_ENUM("DRC Attack Time", ssm2518_drc_attack_time_enum),
SOC_ENUM("DRC Decay Time", ssm2518_drc_decay_time_enum),
SOC_ENUM("DRC Hold Time", ssm2518_drc_hold_time_enum),
SOC_ENUM("DRC Noise Gate Hold Time",
ssm2518_drc_noise_gate_hold_time_enum),
SOC_ENUM("DRC RMS Averaging Time", ssm2518_drc_rms_averaging_time_enum),
};
static const struct snd_soc_dapm_widget ssm2518_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DACL", "HiFi Playback", SSM2518_REG_POWER2, 1, 1),
SND_SOC_DAPM_DAC("DACR", "HiFi Playback", SSM2518_REG_POWER2, 2, 1),
SND_SOC_DAPM_OUTPUT("OUTL"),
SND_SOC_DAPM_OUTPUT("OUTR"),
};
static const struct snd_soc_dapm_route ssm2518_routes[] = {
{ "OUTL", NULL, "DACL" },
{ "OUTR", NULL, "DACR" },
};
struct ssm2518_mcs_lut {
unsigned int rate;
const unsigned int *sysclks;
};
static const unsigned int ssm2518_sysclks_2048000[] = {
2048000, 4096000, 8192000, 12288000, 16384000, 24576000,
3200000, 6400000, 12800000, 0
};
static const unsigned int ssm2518_sysclks_2822000[] = {
2822000, 5644800, 11289600, 16934400, 22579200, 33868800,
4410000, 8820000, 17640000, 0
};
static const unsigned int ssm2518_sysclks_3072000[] = {
3072000, 6144000, 12288000, 16384000, 24576000, 38864000,
4800000, 9600000, 19200000, 0
};
static const struct ssm2518_mcs_lut ssm2518_mcs_lut[] = {
{ 8000, ssm2518_sysclks_2048000, },
{ 11025, ssm2518_sysclks_2822000, },
{ 12000, ssm2518_sysclks_3072000, },
{ 16000, ssm2518_sysclks_2048000, },
{ 24000, ssm2518_sysclks_3072000, },
{ 22050, ssm2518_sysclks_2822000, },
{ 32000, ssm2518_sysclks_2048000, },
{ 44100, ssm2518_sysclks_2822000, },
{ 48000, ssm2518_sysclks_3072000, },
{ 96000, ssm2518_sysclks_3072000, },
};
static const unsigned int ssm2518_rates_2048000[] = {
8000, 16000, 32000,
};
static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2048000 = {
.list = ssm2518_rates_2048000,
.count = ARRAY_SIZE(ssm2518_rates_2048000),
};
static const unsigned int ssm2518_rates_2822000[] = {
11025, 22050, 44100,
};
static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2822000 = {
.list = ssm2518_rates_2822000,
.count = ARRAY_SIZE(ssm2518_rates_2822000),
};
static const unsigned int ssm2518_rates_3072000[] = {
12000, 24000, 48000, 96000,
};
static const struct snd_pcm_hw_constraint_list ssm2518_constraints_3072000 = {
.list = ssm2518_rates_3072000,
.count = ARRAY_SIZE(ssm2518_rates_3072000),
};
static const unsigned int ssm2518_rates_12288000[] = {
8000, 12000, 16000, 24000, 32000, 48000, 96000,
};
static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = {
.list = ssm2518_rates_12288000,
.count = ARRAY_SIZE(ssm2518_rates_12288000),
};
static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
unsigned int rate)
{
const unsigned int *sysclks = NULL;
int i;
for (i = 0; i < ARRAY_SIZE(ssm2518_mcs_lut); i++) {
if (ssm2518_mcs_lut[i].rate == rate) {
sysclks = ssm2518_mcs_lut[i].sysclks;
break;
}
}
if (!sysclks)
return -EINVAL;
for (i = 0; sysclks[i]; i++) {
if (sysclks[i] == ssm2518->sysclk)
return i;
}
return -EINVAL;
}
static int ssm2518_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
unsigned int rate = params_rate(params);
unsigned int ctrl1, ctrl1_mask;
int mcs;
int ret;
mcs = ssm2518_lookup_mcs(ssm2518, rate);
if (mcs < 0)
return mcs;
ctrl1_mask = SSM2518_SAI_CTRL1_FS_MASK;
if (rate >= 8000 && rate <= 12000)
ctrl1 = SSM2518_SAI_CTRL1_FS_8000_12000;
else if (rate >= 16000 && rate <= 24000)
ctrl1 = SSM2518_SAI_CTRL1_FS_16000_24000;
else if (rate >= 32000 && rate <= 48000)
ctrl1 = SSM2518_SAI_CTRL1_FS_32000_48000;
else if (rate >= 64000 && rate <= 96000)
ctrl1 = SSM2518_SAI_CTRL1_FS_64000_96000;
else
return -EINVAL;
if (ssm2518->right_j) {
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_16BIT;
break;
case SNDRV_PCM_FORMAT_S24_LE:
ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT;
break;
default:
return -EINVAL;
}
ctrl1_mask |= SSM2518_SAI_CTRL1_FMT_MASK;
}
/* Disable auto samplerate detection */
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_CLOCK,
SSM2518_CLOCK_ASR, SSM2518_CLOCK_ASR);
if (ret < 0)
return ret;
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1,
ctrl1_mask, ctrl1);
if (ret < 0)
return ret;
return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
SSM2518_POWER1_MCS_MASK, mcs << 1);
}
static int ssm2518_mute(struct snd_soc_dai *dai, int mute)
{
struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
unsigned int val;
if (mute)
val = SSM2518_MUTE_CTRL_MUTE_MASTER;
else
val = 0;
return regmap_update_bits(ssm2518->regmap, SSM2518_REG_MUTE_CTRL,
SSM2518_MUTE_CTRL_MUTE_MASTER, val);
}
static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
unsigned int ctrl1 = 0, ctrl2 = 0;
bool invert_fclk;
int ret;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
invert_fclk = false;
break;
case SND_SOC_DAIFMT_IB_NF:
ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT;
invert_fclk = false;
break;
case SND_SOC_DAIFMT_NB_IF:
invert_fclk = true;
break;
case SND_SOC_DAIFMT_IB_IF:
ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT;
invert_fclk = true;
break;
default:
return -EINVAL;
}
ssm2518->right_j = false;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S;
break;
case SND_SOC_DAIFMT_LEFT_J:
ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ;
invert_fclk = !invert_fclk;
break;
case SND_SOC_DAIFMT_RIGHT_J:
ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT;
ssm2518->right_j = true;
invert_fclk = !invert_fclk;
break;
case SND_SOC_DAIFMT_DSP_A:
ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE;
ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S;
invert_fclk = false;
break;
case SND_SOC_DAIFMT_DSP_B:
ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE;
ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ;
invert_fclk = false;
break;
default:
return -EINVAL;
}
if (invert_fclk)
ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_INVERT;
ret = regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, ctrl1);
if (ret)
return ret;
return regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, ctrl2);
}
static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable)
{
int ret = 0;
if (!enable) {
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
SSM2518_POWER1_SPWDN, SSM2518_POWER1_SPWDN);
regcache_mark_dirty(ssm2518->regmap);
}
if (gpio_is_valid(ssm2518->enable_gpio))
gpio_set_value(ssm2518->enable_gpio, enable);
regcache_cache_only(ssm2518->regmap, !enable);
if (enable) {
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
SSM2518_POWER1_SPWDN | SSM2518_POWER1_RESET, 0x00);
regcache_sync(ssm2518->regmap);
}
return ret;
}
static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
ret = ssm2518_set_power(ssm2518, true);
break;
case SND_SOC_BIAS_OFF:
ret = ssm2518_set_power(ssm2518, false);
break;
}
if (ret)
return ret;
codec->dapm.bias_level = level;
return 0;
}
static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int width)
{
struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
unsigned int ctrl1, ctrl2;
int left_slot, right_slot;
int ret;
if (slots == 0)
return regmap_update_bits(ssm2518->regmap,
SSM2518_REG_SAI_CTRL1, SSM2518_SAI_CTRL1_SAI_MASK,
SSM2518_SAI_CTRL1_SAI_I2S);
if (tx_mask == 0 || rx_mask != 0)
return -EINVAL;
if (slots == 1) {
if (tx_mask != 1)
return -EINVAL;
left_slot = 0;
right_slot = 0;
} else {
/* We assume the left channel < right channel */
left_slot = ffs(tx_mask);
tx_mask &= ~(1 << tx_mask);
if (tx_mask == 0) {
right_slot = left_slot;
} else {
right_slot = ffs(tx_mask);
tx_mask &= ~(1 << tx_mask);
}
}
if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
return -EINVAL;
switch (width) {
case 16:
ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_16;
break;
case 24:
ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_24;
break;
case 32:
ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_32;
break;
default:
return -EINVAL;
}
switch (slots) {
case 1:
ctrl1 = SSM2518_SAI_CTRL1_SAI_MONO;
break;
case 2:
ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_2;
break;
case 4:
ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_4;
break;
case 8:
ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_8;
break;
case 16:
ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_16;
break;
default:
return -EINVAL;
}
ret = regmap_write(ssm2518->regmap, SSM2518_REG_CHAN_MAP,
(left_slot << SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET) |
(right_slot << SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET));
if (ret)
return ret;
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1,
SSM2518_SAI_CTRL1_SAI_MASK, ctrl1);
if (ret)
return ret;
return regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL2,
SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK, ctrl2);
}
static int ssm2518_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
if (ssm2518->constraints)
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, ssm2518->constraints);
return 0;
}
#define SSM2518_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32)
static const struct snd_soc_dai_ops ssm2518_dai_ops = {
.startup = ssm2518_startup,
.hw_params = ssm2518_hw_params,
.digital_mute = ssm2518_mute,
.set_fmt = ssm2518_set_dai_fmt,
.set_tdm_slot = ssm2518_set_tdm_slot,
};
static struct snd_soc_dai_driver ssm2518_dai = {
.name = "ssm2518-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SSM2518_FORMATS,
},
.ops = &ssm2518_dai_ops,
};
static int ssm2518_probe(struct snd_soc_codec *codec)
{
struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
int ret;
codec->control_data = ssm2518->regmap;
ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
return ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF);
}
static int ssm2518_remove(struct snd_soc_codec *codec)
{
ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir)
{
struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if (clk_id != SSM2518_SYSCLK)
return -EINVAL;
switch (source) {
case SSM2518_SYSCLK_SRC_MCLK:
val = 0;
break;
case SSM2518_SYSCLK_SRC_BCLK:
/* In this case the bitclock is used as the system clock, and
* the bitclock signal needs to be connected to the MCLK pin and
* the BCLK pin is left unconnected */
val = SSM2518_POWER1_NO_BCLK;
break;
default:
return -EINVAL;
}
switch (freq) {
case 0:
ssm2518->constraints = NULL;
break;
case 2048000:
case 4096000:
case 8192000:
case 3200000:
case 6400000:
case 12800000:
ssm2518->constraints = &ssm2518_constraints_2048000;
break;
case 2822000:
case 5644800:
case 11289600:
case 16934400:
case 22579200:
case 33868800:
case 4410000:
case 8820000:
case 17640000:
ssm2518->constraints = &ssm2518_constraints_2822000;
break;
case 3072000:
case 6144000:
case 38864000:
case 4800000:
case 9600000:
case 19200000:
ssm2518->constraints = &ssm2518_constraints_3072000;
break;
case 12288000:
case 16384000:
case 24576000:
ssm2518->constraints = &ssm2518_constraints_12288000;
break;
default:
return -EINVAL;
}
ssm2518->sysclk = freq;
return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1,
SSM2518_POWER1_NO_BCLK, val);
}
static struct snd_soc_codec_driver ssm2518_codec_driver = {
.probe = ssm2518_probe,
.remove = ssm2518_remove,
.set_bias_level = ssm2518_set_bias_level,
.set_sysclk = ssm2518_set_sysclk,
.idle_bias_off = true,
.controls = ssm2518_snd_controls,
.num_controls = ARRAY_SIZE(ssm2518_snd_controls),
.dapm_widgets = ssm2518_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets),
.dapm_routes = ssm2518_routes,
.num_dapm_routes = ARRAY_SIZE(ssm2518_routes),
};
static bool ssm2518_register_volatile(struct device *dev, unsigned int reg)
{
return false;
}
static const struct regmap_config ssm2518_regmap_config = {
.val_bits = 8,
.reg_bits = 8,
.max_register = SSM2518_REG_DRC_9,
.volatile_reg = ssm2518_register_volatile,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = ssm2518_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults),
};
static int ssm2518_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct ssm2518_platform_data *pdata = i2c->dev.platform_data;
struct ssm2518 *ssm2518;
int ret;
ssm2518 = devm_kzalloc(&i2c->dev, sizeof(*ssm2518), GFP_KERNEL);
if (ssm2518 == NULL)
return -ENOMEM;
if (pdata) {
ssm2518->enable_gpio = pdata->enable_gpio;
} else if (i2c->dev.of_node) {
ssm2518->enable_gpio = of_get_gpio(i2c->dev.of_node, 0);
if (ssm2518->enable_gpio < 0 && ssm2518->enable_gpio != -ENOENT)
return ssm2518->enable_gpio;
} else {
ssm2518->enable_gpio = -1;
}
if (gpio_is_valid(ssm2518->enable_gpio)) {
ret = devm_gpio_request_one(&i2c->dev, ssm2518->enable_gpio,
GPIOF_OUT_INIT_HIGH, "SSM2518 nSD");
if (ret)
return ret;
}
i2c_set_clientdata(i2c, ssm2518);
ssm2518->regmap = devm_regmap_init_i2c(i2c, &ssm2518_regmap_config);
if (IS_ERR(ssm2518->regmap))
return PTR_ERR(ssm2518->regmap);
/*
* The reset bit is obviously volatile, but we need to be able to cache
* the other bits in the register, so we can't just mark the whole
* register as volatile. Since this is the only place where we'll ever
* touch the reset bit just bypass the cache for this operation.
*/
regcache_cache_bypass(ssm2518->regmap, true);
ret = regmap_write(ssm2518->regmap, SSM2518_REG_POWER1,
SSM2518_POWER1_RESET);
regcache_cache_bypass(ssm2518->regmap, false);
if (ret)
return ret;
ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER2,
SSM2518_POWER2_APWDN, 0x00);
if (ret)
return ret;
ret = ssm2518_set_power(ssm2518, false);
if (ret)
return ret;
return snd_soc_register_codec(&i2c->dev, &ssm2518_codec_driver,
&ssm2518_dai, 1);
}
static int ssm2518_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id ssm2518_i2c_ids[] = {
{ "ssm2518", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids);
static struct i2c_driver ssm2518_driver = {
.driver = {
.name = "ssm2518",
.owner = THIS_MODULE,
},
.probe = ssm2518_i2c_probe,
.remove = ssm2518_i2c_remove,
.id_table = ssm2518_i2c_ids,
};
module_i2c_driver(ssm2518_driver);
MODULE_DESCRIPTION("ASoC SSM2518 driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,20 @@
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#ifndef __SND_SOC_CODECS_SSM2518_H__
#define __SND_SOC_CODECS_SSM2518_H__
#define SSM2518_SYSCLK 0
enum ssm2518_sysclk_src {
SSM2518_SYSCLK_SRC_MCLK = 0,
SSM2518_SYSCLK_SRC_BCLK = 1,
};
#endif

View file

@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/init.h>
#include <linux/spi/spi.h>
@ -972,6 +973,13 @@ static int wm0010_spi_probe(struct spi_device *spi)
}
wm0010->irq = irq;
ret = irq_set_irq_wake(irq, 1);
if (ret) {
dev_err(wm0010->dev, "Failed to set IRQ %d as wake source: %d\n",
irq, ret);
return ret;
}
if (spi->max_speed_hz)
wm0010->board_max_spi_speed = spi->max_speed_hz;
else
@ -995,6 +1003,8 @@ static int wm0010_spi_remove(struct spi_device *spi)
gpio_set_value_cansleep(wm0010->gpio_reset,
wm0010->gpio_reset_value);
irq_set_irq_wake(wm0010->irq, 0);
if (wm0010->irq)
free_irq(wm0010->irq, wm0010);

View file

@ -814,7 +814,20 @@ SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
SOC_VALUE_ENUM("HPOUT1 OSR", wm5102_hpout_osr[0]),
SOC_VALUE_ENUM("HPOUT2 OSR", wm5102_hpout_osr[1]),
SOC_VALUE_ENUM("HPOUT3 OSR", wm5102_hpout_osr[2]),
SOC_VALUE_ENUM("EPOUT OSR", wm5102_hpout_osr[2]),
SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE,
ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0),
SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE,
ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0),
SOC_SINGLE("EPOUT DRE Switch", ARIZONA_DRE_ENABLE,
ARIZONA_DRE3L_ENA_SHIFT, 1, 0),
SOC_SINGLE("DRE Threshold", ARIZONA_DRE_CONTROL_2,
ARIZONA_DRE_T_LOW_SHIFT, 63, 0),
SOC_SINGLE("DRE Low Level ABS", ARIZONA_DRE_CONTROL_3,
ARIZONA_DRE_LOW_LEVEL_ABS_SHIFT, 15, 0),
SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
@ -852,6 +865,15 @@ ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
};
ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
@ -898,6 +920,15 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
@ -1117,6 +1148,56 @@ SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX1_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX3_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX4_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX5_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX7_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX1_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX3_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX4_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX5_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX7_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX8_ENA_SHIFT, 0),
ARIZONA_DSP_WIDGETS(DSP1, "DSP1"),
SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
@ -1189,6 +1270,15 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
@ -1249,6 +1339,14 @@ SND_SOC_DAPM_OUTPUT("MICSUPP"),
{ name, "AIF2RX2", "AIF2RX2" }, \
{ name, "AIF3RX1", "AIF3RX1" }, \
{ name, "AIF3RX2", "AIF3RX2" }, \
{ name, "SLIMRX1", "SLIMRX1" }, \
{ name, "SLIMRX2", "SLIMRX2" }, \
{ name, "SLIMRX3", "SLIMRX3" }, \
{ name, "SLIMRX4", "SLIMRX4" }, \
{ name, "SLIMRX5", "SLIMRX5" }, \
{ name, "SLIMRX6", "SLIMRX6" }, \
{ name, "SLIMRX7", "SLIMRX7" }, \
{ name, "SLIMRX8", "SLIMRX8" }, \
{ name, "EQ1", "EQ1" }, \
{ name, "EQ2", "EQ2" }, \
{ name, "EQ3", "EQ3" }, \
@ -1304,10 +1402,21 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "OUT5L", NULL, "SYSCLK" },
{ "OUT5R", NULL, "SYSCLK" },
{ "IN1L", NULL, "SYSCLK" },
{ "IN1R", NULL, "SYSCLK" },
{ "IN2L", NULL, "SYSCLK" },
{ "IN2R", NULL, "SYSCLK" },
{ "IN3L", NULL, "SYSCLK" },
{ "IN3R", NULL, "SYSCLK" },
{ "MICBIAS1", NULL, "MICVDD" },
{ "MICBIAS2", NULL, "MICVDD" },
{ "MICBIAS3", NULL, "MICVDD" },
{ "Noise Generator", NULL, "SYSCLK" },
{ "Tone Generator 1", NULL, "SYSCLK" },
{ "Tone Generator 2", NULL, "SYSCLK" },
{ "Noise Generator", NULL, "NOISE" },
{ "Tone Generator 1", NULL, "TONE" },
{ "Tone Generator 2", NULL, "TONE" },
@ -1345,13 +1454,41 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "AIF3RX1", NULL, "AIF3 Playback" },
{ "AIF3RX2", NULL, "AIF3 Playback" },
{ "Slim1 Capture", NULL, "SLIMTX1" },
{ "Slim1 Capture", NULL, "SLIMTX2" },
{ "Slim1 Capture", NULL, "SLIMTX3" },
{ "Slim1 Capture", NULL, "SLIMTX4" },
{ "SLIMRX1", NULL, "Slim1 Playback" },
{ "SLIMRX2", NULL, "Slim1 Playback" },
{ "SLIMRX3", NULL, "Slim1 Playback" },
{ "SLIMRX4", NULL, "Slim1 Playback" },
{ "Slim2 Capture", NULL, "SLIMTX5" },
{ "Slim2 Capture", NULL, "SLIMTX6" },
{ "SLIMRX5", NULL, "Slim2 Playback" },
{ "SLIMRX6", NULL, "Slim2 Playback" },
{ "Slim3 Capture", NULL, "SLIMTX7" },
{ "Slim3 Capture", NULL, "SLIMTX8" },
{ "SLIMRX7", NULL, "Slim3 Playback" },
{ "SLIMRX8", NULL, "Slim3 Playback" },
{ "AIF1 Playback", NULL, "SYSCLK" },
{ "AIF2 Playback", NULL, "SYSCLK" },
{ "AIF3 Playback", NULL, "SYSCLK" },
{ "Slim1 Playback", NULL, "SYSCLK" },
{ "Slim2 Playback", NULL, "SYSCLK" },
{ "Slim3 Playback", NULL, "SYSCLK" },
{ "AIF1 Capture", NULL, "SYSCLK" },
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
{ "Slim1 Capture", NULL, "SYSCLK" },
{ "Slim2 Capture", NULL, "SYSCLK" },
{ "Slim3 Capture", NULL, "SYSCLK" },
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@ -1408,6 +1545,15 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
@ -1560,6 +1706,63 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
},
{
.name = "wm5102-slim1",
.id = 4,
.playback = {
.stream_name = "Slim1 Playback",
.channels_min = 1,
.channels_max = 4,
.rates = WM5102_RATES,
.formats = WM5102_FORMATS,
},
.capture = {
.stream_name = "Slim1 Capture",
.channels_min = 1,
.channels_max = 4,
.rates = WM5102_RATES,
.formats = WM5102_FORMATS,
},
.ops = &arizona_simple_dai_ops,
},
{
.name = "wm5102-slim2",
.id = 5,
.playback = {
.stream_name = "Slim2 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM5102_RATES,
.formats = WM5102_FORMATS,
},
.capture = {
.stream_name = "Slim2 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM5102_RATES,
.formats = WM5102_FORMATS,
},
.ops = &arizona_simple_dai_ops,
},
{
.name = "wm5102-slim3",
.id = 6,
.playback = {
.stream_name = "Slim3 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM5102_RATES,
.formats = WM5102_FORMATS,
},
.capture = {
.stream_name = "Slim3 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM5102_RATES,
.formats = WM5102_FORMATS,
},
.ops = &arizona_simple_dai_ops,
},
};
static int wm5102_codec_probe(struct snd_soc_codec *codec)

View file

@ -309,6 +309,15 @@ ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
};
ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
@ -360,6 +369,15 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
@ -550,6 +568,56 @@ SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX1_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX3_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX4_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX5_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX7_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
ARIZONA_SLIMRX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX1_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX2_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX3_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX4_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX5_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX6_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX7_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
ARIZONA_SLIMTX8_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
@ -640,6 +708,15 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
@ -690,6 +767,14 @@ SND_SOC_DAPM_OUTPUT("MICSUPP"),
{ name, "AIF2RX2", "AIF2RX2" }, \
{ name, "AIF3RX1", "AIF3RX1" }, \
{ name, "AIF3RX2", "AIF3RX2" }, \
{ name, "SLIMRX1", "SLIMRX1" }, \
{ name, "SLIMRX2", "SLIMRX2" }, \
{ name, "SLIMRX3", "SLIMRX3" }, \
{ name, "SLIMRX4", "SLIMRX4" }, \
{ name, "SLIMRX5", "SLIMRX5" }, \
{ name, "SLIMRX6", "SLIMRX6" }, \
{ name, "SLIMRX7", "SLIMRX7" }, \
{ name, "SLIMRX8", "SLIMRX8" }, \
{ name, "EQ1", "EQ1" }, \
{ name, "EQ2", "EQ2" }, \
{ name, "EQ3", "EQ3" }, \
@ -736,10 +821,23 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "OUT6L", NULL, "SYSCLK" },
{ "OUT6R", NULL, "SYSCLK" },
{ "IN1L", NULL, "SYSCLK" },
{ "IN1R", NULL, "SYSCLK" },
{ "IN2L", NULL, "SYSCLK" },
{ "IN2R", NULL, "SYSCLK" },
{ "IN3L", NULL, "SYSCLK" },
{ "IN3R", NULL, "SYSCLK" },
{ "IN4L", NULL, "SYSCLK" },
{ "IN4R", NULL, "SYSCLK" },
{ "MICBIAS1", NULL, "MICVDD" },
{ "MICBIAS2", NULL, "MICVDD" },
{ "MICBIAS3", NULL, "MICVDD" },
{ "Noise Generator", NULL, "SYSCLK" },
{ "Tone Generator 1", NULL, "SYSCLK" },
{ "Tone Generator 2", NULL, "SYSCLK" },
{ "Noise Generator", NULL, "NOISE" },
{ "Tone Generator 1", NULL, "TONE" },
{ "Tone Generator 2", NULL, "TONE" },
@ -777,13 +875,41 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "AIF3RX1", NULL, "AIF3 Playback" },
{ "AIF3RX2", NULL, "AIF3 Playback" },
{ "Slim1 Capture", NULL, "SLIMTX1" },
{ "Slim1 Capture", NULL, "SLIMTX2" },
{ "Slim1 Capture", NULL, "SLIMTX3" },
{ "Slim1 Capture", NULL, "SLIMTX4" },
{ "SLIMRX1", NULL, "Slim1 Playback" },
{ "SLIMRX2", NULL, "Slim1 Playback" },
{ "SLIMRX3", NULL, "Slim1 Playback" },
{ "SLIMRX4", NULL, "Slim1 Playback" },
{ "Slim2 Capture", NULL, "SLIMTX5" },
{ "Slim2 Capture", NULL, "SLIMTX6" },
{ "SLIMRX5", NULL, "Slim2 Playback" },
{ "SLIMRX6", NULL, "Slim2 Playback" },
{ "Slim3 Capture", NULL, "SLIMTX7" },
{ "Slim3 Capture", NULL, "SLIMTX8" },
{ "SLIMRX7", NULL, "Slim3 Playback" },
{ "SLIMRX8", NULL, "Slim3 Playback" },
{ "AIF1 Playback", NULL, "SYSCLK" },
{ "AIF2 Playback", NULL, "SYSCLK" },
{ "AIF3 Playback", NULL, "SYSCLK" },
{ "Slim1 Playback", NULL, "SYSCLK" },
{ "Slim2 Playback", NULL, "SYSCLK" },
{ "Slim3 Playback", NULL, "SYSCLK" },
{ "AIF1 Capture", NULL, "SYSCLK" },
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
{ "Slim1 Capture", NULL, "SYSCLK" },
{ "Slim2 Capture", NULL, "SYSCLK" },
{ "Slim3 Capture", NULL, "SYSCLK" },
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@ -829,6 +955,15 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
@ -963,6 +1098,63 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
},
{
.name = "wm5110-slim1",
.id = 4,
.playback = {
.stream_name = "Slim1 Playback",
.channels_min = 1,
.channels_max = 4,
.rates = WM5110_RATES,
.formats = WM5110_FORMATS,
},
.capture = {
.stream_name = "Slim1 Capture",
.channels_min = 1,
.channels_max = 4,
.rates = WM5110_RATES,
.formats = WM5110_FORMATS,
},
.ops = &arizona_simple_dai_ops,
},
{
.name = "wm5110-slim2",
.id = 5,
.playback = {
.stream_name = "Slim2 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM5110_RATES,
.formats = WM5110_FORMATS,
},
.capture = {
.stream_name = "Slim2 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM5110_RATES,
.formats = WM5110_FORMATS,
},
.ops = &arizona_simple_dai_ops,
},
{
.name = "wm5110-slim3",
.id = 6,
.playback = {
.stream_name = "Slim3 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM5110_RATES,
.formats = WM5110_FORMATS,
},
.capture = {
.stream_name = "Slim3 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM5110_RATES,
.formats = WM5110_FORMATS,
},
.ops = &arizona_simple_dai_ops,
},
};
static int wm5110_codec_probe(struct snd_soc_codec *codec)

View file

@ -51,6 +51,7 @@ static const char *wm8962_supply_names[WM8962_NUM_SUPPLIES] = {
/* codec private data */
struct wm8962_priv {
struct wm8962_pdata pdata;
struct regmap *regmap;
struct snd_soc_codec *codec;
@ -1600,7 +1601,6 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
u16 *reg_cache = codec->reg_cache;
int ret;
/* Apply the update (if any) */
@ -1609,16 +1609,19 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
return 0;
/* If the left PGA is enabled hit that VU bit... */
if (snd_soc_read(codec, WM8962_PWR_MGMT_2) & WM8962_HPOUTL_PGA_ENA)
return snd_soc_write(codec, WM8962_HPOUTL_VOLUME,
reg_cache[WM8962_HPOUTL_VOLUME]);
ret = snd_soc_read(codec, WM8962_PWR_MGMT_2);
if (ret & WM8962_HPOUTL_PGA_ENA) {
snd_soc_write(codec, WM8962_HPOUTL_VOLUME,
snd_soc_read(codec, WM8962_HPOUTL_VOLUME));
return 1;
}
/* ...otherwise the right. The VU is stereo. */
if (snd_soc_read(codec, WM8962_PWR_MGMT_2) & WM8962_HPOUTR_PGA_ENA)
return snd_soc_write(codec, WM8962_HPOUTR_VOLUME,
reg_cache[WM8962_HPOUTR_VOLUME]);
if (ret & WM8962_HPOUTR_PGA_ENA)
snd_soc_write(codec, WM8962_HPOUTR_VOLUME,
snd_soc_read(codec, WM8962_HPOUTR_VOLUME));
return 0;
return 1;
}
/* The VU bits for the speakers are in a different register to the mute
@ -2345,12 +2348,13 @@ static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = {
static int wm8962_add_widgets(struct snd_soc_codec *codec)
{
struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct wm8962_pdata *pdata = &wm8962->pdata;
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_add_codec_controls(codec, wm8962_snd_controls,
ARRAY_SIZE(wm8962_snd_controls));
if (pdata && pdata->spk_mono)
if (pdata->spk_mono)
snd_soc_add_codec_controls(codec, wm8962_spk_mono_controls,
ARRAY_SIZE(wm8962_spk_mono_controls));
else
@ -2360,7 +2364,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_new_controls(dapm, wm8962_dapm_widgets,
ARRAY_SIZE(wm8962_dapm_widgets));
if (pdata && pdata->spk_mono)
if (pdata->spk_mono)
snd_soc_dapm_new_controls(dapm, wm8962_dapm_spk_mono_widgets,
ARRAY_SIZE(wm8962_dapm_spk_mono_widgets));
else
@ -2369,7 +2373,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(dapm, wm8962_intercon,
ARRAY_SIZE(wm8962_intercon));
if (pdata && pdata->spk_mono)
if (pdata->spk_mono)
snd_soc_dapm_add_routes(dapm, wm8962_spk_mono_intercon,
ARRAY_SIZE(wm8962_spk_mono_intercon));
else
@ -3333,14 +3337,14 @@ static struct gpio_chip wm8962_template_chip = {
static void wm8962_init_gpio(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
struct wm8962_pdata *pdata = &wm8962->pdata;
int ret;
wm8962->gpio_chip = wm8962_template_chip;
wm8962->gpio_chip.ngpio = WM8962_MAX_GPIO;
wm8962->gpio_chip.dev = codec->dev;
if (pdata && pdata->gpio_base)
if (pdata->gpio_base)
wm8962->gpio_chip.base = pdata->gpio_base;
else
wm8962->gpio_chip.base = -1;
@ -3374,7 +3378,6 @@ static int wm8962_probe(struct snd_soc_codec *codec)
int ret;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
u16 *reg_cache = codec->reg_cache;
int i, trigger, irq_pol;
bool dmicclk, dmicdat;
@ -3421,30 +3424,29 @@ static int wm8962_probe(struct snd_soc_codec *codec)
WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA,
0);
if (pdata) {
/* Apply static configuration for GPIOs */
for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++)
if (pdata->gpio_init[i]) {
wm8962_set_gpio_mode(codec, i + 1);
snd_soc_write(codec, 0x200 + i,
pdata->gpio_init[i] & 0xffff);
}
/* Apply static configuration for GPIOs */
for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++)
if (pdata->gpio_init[i]) {
wm8962_set_gpio_mode(codec, i + 1);
snd_soc_write(codec, 0x200 + i,
pdata->gpio_init[i] & 0xffff);
}
/* Put the speakers into mono mode? */
if (pdata->spk_mono)
reg_cache[WM8962_CLASS_D_CONTROL_2]
|= WM8962_SPK_MONO;
/* Micbias setup, detection enable and detection
* threasholds. */
if (pdata->mic_cfg)
snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4,
WM8962_MICDET_ENA |
WM8962_MICDET_THR_MASK |
WM8962_MICSHORT_THR_MASK |
WM8962_MICBIAS_LVL,
pdata->mic_cfg);
}
/* Put the speakers into mono mode? */
if (pdata->spk_mono)
snd_soc_update_bits(codec, WM8962_CLASS_D_CONTROL_2,
WM8962_SPK_MONO_MASK, WM8962_SPK_MONO);
/* Micbias setup, detection enable and detection
* threasholds. */
if (pdata->mic_cfg)
snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4,
WM8962_MICDET_ENA |
WM8962_MICDET_THR_MASK |
WM8962_MICSHORT_THR_MASK |
WM8962_MICBIAS_LVL,
pdata->mic_cfg);
/* Latch volume update bits */
snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME,
@ -3506,7 +3508,7 @@ static int wm8962_probe(struct snd_soc_codec *codec)
wm8962_init_gpio(codec);
if (wm8962->irq) {
if (pdata && pdata->irq_active_low) {
if (pdata->irq_active_low) {
trigger = IRQF_TRIGGER_LOW;
irq_pol = WM8962_IRQ_POL;
} else {
@ -3584,6 +3586,34 @@ static const struct regmap_config wm8962_regmap = {
.cache_type = REGCACHE_RBTREE,
};
static int wm8962_set_pdata_from_of(struct i2c_client *i2c,
struct wm8962_pdata *pdata)
{
const struct device_node *np = i2c->dev.of_node;
u32 val32;
int i;
if (of_property_read_bool(np, "spk-mono"))
pdata->spk_mono = true;
if (of_property_read_u32(np, "mic-cfg", &val32) >= 0)
pdata->mic_cfg = val32;
if (of_property_read_u32_array(np, "gpio-cfg", pdata->gpio_init,
ARRAY_SIZE(pdata->gpio_init)) >= 0)
for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++) {
/*
* The range of GPIO register value is [0x0, 0xffff]
* While the default value of each register is 0x0
* Any other value will be regarded as default value
*/
if (pdata->gpio_init[i] > 0xffff)
pdata->gpio_init[i] = 0x0;
}
return 0;
}
static int wm8962_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@ -3603,6 +3633,15 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
init_completion(&wm8962->fll_lock);
wm8962->irq = i2c->irq;
/* If platform data was supplied, update the default data in priv */
if (pdata) {
memcpy(&wm8962->pdata, pdata, sizeof(struct wm8962_pdata));
} else if (i2c->dev.of_node) {
ret = wm8962_set_pdata_from_of(i2c, &wm8962->pdata);
if (ret != 0)
return ret;
}
for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++)
wm8962->supplies[i].supply = wm8962_supply_names[i];
@ -3666,7 +3705,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
goto err_enable;
}
if (pdata && pdata->in4_dc_measure) {
if (wm8962->pdata.in4_dc_measure) {
ret = regmap_register_patch(wm8962->regmap,
wm8962_dc_measure,
ARRAY_SIZE(wm8962_dc_measure));
@ -3719,8 +3758,34 @@ static int wm8962_runtime_resume(struct device *dev)
wm8962_reset(wm8962);
/* SYSCLK defaults to on; make sure it is off so we can safely
* write to registers if the device is declocked.
*/
regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
WM8962_SYSCLK_ENA, 0);
/* Ensure we have soft control over all registers */
regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2,
WM8962_CLKREG_OVD, WM8962_CLKREG_OVD);
/* Ensure that the oscillator and PLLs are disabled */
regmap_update_bits(wm8962->regmap, WM8962_PLL2,
WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA,
0);
regcache_sync(wm8962->regmap);
regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP,
WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA,
WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA);
/* Bias enable at 2*5k (fast start-up) */
regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1,
WM8962_BIAS_ENA | WM8962_VMID_SEL_MASK,
WM8962_BIAS_ENA | 0x180);
msleep(5);
return 0;
}

View file

@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gcd.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@ -1498,6 +1499,24 @@ static const char *aif1dac_text[] = {
"AIF1DACDAT", "AIF3DACDAT",
};
static const char *loopback_text[] = {
"None", "ADCDAT",
};
static const struct soc_enum aif1_loopback_enum =
SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, WM8994_AIF1_LOOPBACK_SHIFT, 2,
loopback_text);
static const struct snd_kcontrol_new aif1_loopback =
SOC_DAPM_ENUM("AIF1 Loopback", aif1_loopback_enum);
static const struct soc_enum aif2_loopback_enum =
SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, WM8994_AIF2_LOOPBACK_SHIFT, 2,
loopback_text);
static const struct snd_kcontrol_new aif2_loopback =
SOC_DAPM_ENUM("AIF2 Loopback", aif2_loopback_enum);
static const struct soc_enum aif1dac_enum =
SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 0, 2, aif1dac_text);
@ -1744,6 +1763,9 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0),
SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("AIF1 Loopback", SND_SOC_NOPM, 0, 0, &aif1_loopback),
SND_SOC_DAPM_MUX("AIF2 Loopback", SND_SOC_NOPM, 0, 0, &aif2_loopback),
SND_SOC_DAPM_POST("Debug log", post_ev),
};
@ -1875,9 +1897,9 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIF1DAC2L", NULL, "AIF1DAC Mux" },
{ "AIF1DAC2R", NULL, "AIF1DAC Mux" },
{ "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" },
{ "AIF1DAC Mux", "AIF1DACDAT", "AIF1 Loopback" },
{ "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
{ "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" },
{ "AIF2DAC Mux", "AIF2DACDAT", "AIF2 Loopback" },
{ "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
{ "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" },
{ "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" },
@ -1928,6 +1950,12 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" },
{ "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" },
/* Loopback */
{ "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" },
{ "AIF1 Loopback", "None", "AIF1DACDAT" },
{ "AIF2 Loopback", "ADCDAT", "AIF2ADCDAT" },
{ "AIF2 Loopback", "None", "AIF2DACDAT" },
/* Sidetone */
{ "Left Sidetone", "ADC/DMIC1", "ADCL Mux" },
{ "Left Sidetone", "DMIC2", "DMIC2L" },
@ -2010,15 +2038,16 @@ struct fll_div {
u16 outdiv;
u16 n;
u16 k;
u16 lambda;
u16 clk_ref_div;
u16 fll_fratio;
};
static int wm8994_get_fll_config(struct fll_div *fll,
static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll,
int freq_in, int freq_out)
{
u64 Kpart;
unsigned int K, Ndiv, Nmod;
unsigned int K, Ndiv, Nmod, gcd_fll;
pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out);
@ -2067,20 +2096,32 @@ static int wm8994_get_fll_config(struct fll_div *fll,
Nmod = freq_out % freq_in;
pr_debug("Nmod=%d\n", Nmod);
/* Calculate fractional part - scale up so we can round. */
Kpart = FIXED_FLL_SIZE * (long long)Nmod;
switch (control->type) {
case WM8994:
/* Calculate fractional part - scale up so we can round. */
Kpart = FIXED_FLL_SIZE * (long long)Nmod;
do_div(Kpart, freq_in);
do_div(Kpart, freq_in);
K = Kpart & 0xFFFFFFFF;
K = Kpart & 0xFFFFFFFF;
if ((K % 10) >= 5)
K += 5;
if ((K % 10) >= 5)
K += 5;
/* Move down to proper range now rounding is done */
fll->k = K / 10;
/* Move down to proper range now rounding is done */
fll->k = K / 10;
fll->lambda = 0;
pr_debug("N=%x K=%x\n", fll->n, fll->k);
pr_debug("N=%x K=%x\n", fll->n, fll->k);
break;
default:
gcd_fll = gcd(freq_out, freq_in);
fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll;
fll->lambda = freq_in / gcd_fll;
}
return 0;
}
@ -2144,9 +2185,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
* analysis bugs spewing warnings.
*/
if (freq_out)
ret = wm8994_get_fll_config(&fll, freq_in, freq_out);
ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out);
else
ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in,
ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in,
wm8994->fll[id].out);
if (ret < 0)
return ret;
@ -2191,6 +2232,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
WM8994_FLL1_N_MASK,
fll.n << WM8994_FLL1_N_SHIFT);
if (fll.lambda) {
snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset,
WM8958_FLL1_LAMBDA_MASK,
fll.lambda);
snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA);
} else {
snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
WM8958_FLL1_EFS_ENA, 0);
}
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP |
WM8994_FLL1_REFCLK_DIV_MASK |
@ -2555,17 +2607,24 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct wm8994 *control = wm8994->wm8994;
int ms_reg;
int aif1_reg;
int dac_reg;
int adc_reg;
int ms = 0;
int aif1 = 0;
int lrclk = 0;
switch (dai->id) {
case 1:
ms_reg = WM8994_AIF1_MASTER_SLAVE;
aif1_reg = WM8994_AIF1_CONTROL_1;
dac_reg = WM8994_AIF1DAC_LRCLK;
adc_reg = WM8994_AIF1ADC_LRCLK;
break;
case 2:
ms_reg = WM8994_AIF2_MASTER_SLAVE;
aif1_reg = WM8994_AIF2_CONTROL_1;
dac_reg = WM8994_AIF1DAC_LRCLK;
adc_reg = WM8994_AIF1ADC_LRCLK;
break;
default:
return -EINVAL;
@ -2584,6 +2643,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
aif1 |= WM8994_AIF1_LRCLK_INV;
lrclk |= WM8958_AIF1_LRCLK_INV;
case SND_SOC_DAIFMT_DSP_A:
aif1 |= 0x18;
break;
@ -2622,12 +2682,14 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_IB_IF:
aif1 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV;
lrclk |= WM8958_AIF1_LRCLK_INV;
break;
case SND_SOC_DAIFMT_IB_NF:
aif1 |= WM8994_AIF1_BCLK_INV;
break;
case SND_SOC_DAIFMT_NB_IF:
aif1 |= WM8994_AIF1_LRCLK_INV;
lrclk |= WM8958_AIF1_LRCLK_INV;
break;
default:
return -EINVAL;
@ -2658,6 +2720,10 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
aif1);
snd_soc_update_bits(codec, ms_reg, WM8994_AIF1_MSTR,
ms);
snd_soc_update_bits(codec, dac_reg,
WM8958_AIF1_LRCLK_INV, lrclk);
snd_soc_update_bits(codec, adc_reg,
WM8958_AIF1_LRCLK_INV, lrclk);
return 0;
}
@ -3096,24 +3162,7 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec)
static int wm8994_codec_resume(struct snd_soc_codec *codec)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->wm8994;
int i, ret;
unsigned int val, mask;
if (control->revision < 4) {
/* force a HW read */
ret = regmap_read(control->regmap,
WM8994_POWER_MANAGEMENT_5, &val);
/* modify the cache only */
codec->cache_only = 1;
mask = WM8994_DAC1R_ENA | WM8994_DAC1L_ENA |
WM8994_DAC2R_ENA | WM8994_DAC2L_ENA;
val &= mask;
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
mask, val);
codec->cache_only = 0;
}
for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
if (!wm8994->fll_suspend[i].out)
@ -3495,6 +3544,31 @@ static void wm8958_button_det(struct snd_soc_codec *codec, u16 status)
wm8994->btn_mask);
}
static void wm8958_open_circuit_work(struct work_struct *work)
{
struct wm8994_priv *wm8994 = container_of(work,
struct wm8994_priv,
open_circuit_work.work);
struct device *dev = wm8994->wm8994->dev;
wm1811_micd_stop(wm8994->hubs.codec);
mutex_lock(&wm8994->accdet_lock);
dev_dbg(dev, "Reporting open circuit\n");
wm8994->jack_mic = false;
wm8994->mic_detecting = true;
wm8958_micd_set_rate(wm8994->hubs.codec);
snd_soc_jack_report(wm8994->micdet[0].jack, 0,
wm8994->btn_mask |
SND_JACK_HEADSET);
mutex_unlock(&wm8994->accdet_lock);
}
static void wm8958_mic_id(void *data, u16 status)
{
struct snd_soc_codec *codec = data;
@ -3504,16 +3578,9 @@ static void wm8958_mic_id(void *data, u16 status)
if (!(status & WM8958_MICD_STS)) {
/* If nothing present then clear our statuses */
dev_dbg(codec->dev, "Detected open circuit\n");
wm8994->jack_mic = false;
wm8994->mic_detecting = true;
wm1811_micd_stop(codec);
wm8958_micd_set_rate(codec);
snd_soc_jack_report(wm8994->micdet[0].jack, 0,
wm8994->btn_mask |
SND_JACK_HEADSET);
schedule_delayed_work(&wm8994->open_circuit_work,
msecs_to_jiffies(2500));
return;
}
@ -3598,6 +3665,8 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
pm_runtime_get_sync(codec->dev);
cancel_delayed_work_sync(&wm8994->mic_complete_work);
mutex_lock(&wm8994->accdet_lock);
reg = snd_soc_read(codec, WM1811_JACKDET_CTRL);
@ -3780,11 +3849,33 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
}
EXPORT_SYMBOL_GPL(wm8958_mic_detect);
static void wm8958_mic_work(struct work_struct *work)
{
struct wm8994_priv *wm8994 = container_of(work,
struct wm8994_priv,
mic_complete_work.work);
struct snd_soc_codec *codec = wm8994->hubs.codec;
dev_crit(codec->dev, "MIC WORK %x\n", wm8994->mic_status);
pm_runtime_get_sync(codec->dev);
mutex_lock(&wm8994->accdet_lock);
wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status);
mutex_unlock(&wm8994->accdet_lock);
pm_runtime_put(codec->dev);
dev_crit(codec->dev, "MIC WORK %x DONE\n", wm8994->mic_status);
}
static irqreturn_t wm8958_mic_irq(int irq, void *data)
{
struct wm8994_priv *wm8994 = data;
struct snd_soc_codec *codec = wm8994->hubs.codec;
int reg, count, ret;
int reg, count, ret, id_delay;
/*
* Jack detection may have detected a removal simulataneously
@ -3794,6 +3885,9 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA))
return IRQ_HANDLED;
cancel_delayed_work_sync(&wm8994->mic_complete_work);
cancel_delayed_work_sync(&wm8994->open_circuit_work);
pm_runtime_get_sync(codec->dev);
/* We may occasionally read a detection without an impedence
@ -3846,8 +3940,12 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
goto out;
}
wm8994->mic_status = reg;
id_delay = wm8994->wm8994->pdata.mic_id_delay;
if (wm8994->mic_detecting)
wm8994->mic_id_cb(wm8994->mic_id_cb_data, reg);
schedule_delayed_work(&wm8994->mic_complete_work,
msecs_to_jiffies(id_delay));
else
wm8958_button_det(codec, reg);
@ -3899,6 +3997,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
mutex_init(&wm8994->accdet_lock);
INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap,
wm1811_jackdet_bootstrap);
INIT_DELAYED_WORK(&wm8994->open_circuit_work,
wm8958_open_circuit_work);
switch (control->type) {
case WM8994:
@ -3911,6 +4011,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break;
}
INIT_DELAYED_WORK(&wm8994->mic_complete_work, wm8958_mic_work);
for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
init_completion(&wm8994->fll_locked[i]);

View file

@ -134,6 +134,9 @@ struct wm8994_priv {
struct mutex accdet_lock;
struct wm8994_micdet micdet[2];
struct delayed_work mic_work;
struct delayed_work open_circuit_work;
struct delayed_work mic_complete_work;
u16 mic_status;
bool mic_detecting;
bool jack_mic;
int btn_mask;

View file

@ -21,6 +21,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@ -215,6 +216,36 @@ static struct {
[WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
};
struct wm_coeff_ctl_ops {
int (*xget)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int (*xput)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int (*xinfo)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
};
struct wm_coeff {
struct device *dev;
struct list_head ctl_list;
struct regmap *regmap;
};
struct wm_coeff_ctl {
const char *name;
struct snd_card *card;
struct wm_adsp_alg_region region;
struct wm_coeff_ctl_ops ops;
struct wm_adsp *adsp;
void *private;
unsigned int enabled:1;
struct list_head list;
void *cache;
size_t len;
unsigned int set:1;
struct snd_kcontrol *kcontrol;
};
static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@ -279,7 +310,7 @@ static const struct soc_enum wm_adsp2_rate_enum[] = {
ARIZONA_DSP1_RATE_SHIFT, 0xf,
ARIZONA_RATE_ENUM_SIZE,
arizona_rate_text, arizona_rate_val),
SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1,
ARIZONA_DSP1_RATE_SHIFT, 0xf,
ARIZONA_RATE_ENUM_SIZE,
arizona_rate_text, arizona_rate_val),
@ -334,6 +365,181 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
}
}
static int wm_coeff_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = ctl->len;
return 0;
}
static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
const void *buf, size_t len)
{
struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
struct wm_adsp_alg_region *region = &ctl->region;
const struct wm_adsp_region *mem;
struct wm_adsp *adsp = ctl->adsp;
void *scratch;
int ret;
unsigned int reg;
mem = wm_adsp_find_region(adsp, region->type);
if (!mem) {
adsp_err(adsp, "No base for region %x\n",
region->type);
return -EINVAL;
}
reg = ctl->region.base;
reg = wm_adsp_region_to_reg(mem, reg);
scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
if (!scratch)
return -ENOMEM;
ret = regmap_raw_write(wm_coeff->regmap, reg, scratch,
ctl->len);
if (ret) {
adsp_err(adsp, "Failed to write %zu bytes to %x\n",
ctl->len, reg);
kfree(scratch);
return ret;
}
kfree(scratch);
return 0;
}
static int wm_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
char *p = ucontrol->value.bytes.data;
memcpy(ctl->cache, p, ctl->len);
if (!ctl->enabled) {
ctl->set = 1;
return 0;
}
return wm_coeff_write_control(kcontrol, p, ctl->len);
}
static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
void *buf, size_t len)
{
struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
struct wm_adsp_alg_region *region = &ctl->region;
const struct wm_adsp_region *mem;
struct wm_adsp *adsp = ctl->adsp;
void *scratch;
int ret;
unsigned int reg;
mem = wm_adsp_find_region(adsp, region->type);
if (!mem) {
adsp_err(adsp, "No base for region %x\n",
region->type);
return -EINVAL;
}
reg = ctl->region.base;
reg = wm_adsp_region_to_reg(mem, reg);
scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
if (!scratch)
return -ENOMEM;
ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len);
if (ret) {
adsp_err(adsp, "Failed to read %zu bytes from %x\n",
ctl->len, reg);
kfree(scratch);
return ret;
}
memcpy(buf, scratch, ctl->len);
kfree(scratch);
return 0;
}
static int wm_coeff_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
char *p = ucontrol->value.bytes.data;
memcpy(p, ctl->cache, ctl->len);
return 0;
}
static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff,
struct wm_coeff_ctl *ctl,
const struct snd_kcontrol_new *kctl)
{
int ret;
struct snd_kcontrol *kcontrol;
kcontrol = snd_ctl_new1(kctl, wm_coeff);
ret = snd_ctl_add(ctl->card, kcontrol);
if (ret < 0) {
dev_err(wm_coeff->dev, "Failed to add %s: %d\n",
kctl->name, ret);
return ret;
}
ctl->kcontrol = kcontrol;
return 0;
}
struct wmfw_ctl_work {
struct wm_coeff *wm_coeff;
struct wm_coeff_ctl *ctl;
struct work_struct work;
};
static int wmfw_add_ctl(struct wm_coeff *wm_coeff,
struct wm_coeff_ctl *ctl)
{
struct snd_kcontrol_new *kcontrol;
int ret;
if (!wm_coeff || !ctl || !ctl->name || !ctl->card)
return -EINVAL;
kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
if (!kcontrol)
return -ENOMEM;
kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kcontrol->name = ctl->name;
kcontrol->info = wm_coeff_info;
kcontrol->get = wm_coeff_get;
kcontrol->put = wm_coeff_put;
kcontrol->private_value = (unsigned long)ctl;
ret = wm_coeff_add_kcontrol(wm_coeff,
ctl, kcontrol);
if (ret < 0)
goto err_kcontrol;
kfree(kcontrol);
list_add(&ctl->list, &wm_coeff->ctl_list);
return 0;
err_kcontrol:
kfree(kcontrol);
return ret;
}
static int wm_adsp_load(struct wm_adsp *dsp)
{
LIST_HEAD(buf_list);
@ -547,7 +753,157 @@ static int wm_adsp_load(struct wm_adsp *dsp)
return ret;
}
static int wm_adsp_setup_algs(struct wm_adsp *dsp)
static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff)
{
struct wm_coeff_ctl *ctl;
int ret;
list_for_each_entry(ctl, &wm_coeff->ctl_list,
list) {
if (!ctl->enabled || ctl->set)
continue;
ret = wm_coeff_read_control(ctl->kcontrol,
ctl->cache,
ctl->len);
if (ret < 0)
return ret;
}
return 0;
}
static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff)
{
struct wm_coeff_ctl *ctl;
int ret;
list_for_each_entry(ctl, &wm_coeff->ctl_list,
list) {
if (!ctl->enabled)
continue;
if (ctl->set) {
ret = wm_coeff_write_control(ctl->kcontrol,
ctl->cache,
ctl->len);
if (ret < 0)
return ret;
}
}
return 0;
}
static void wm_adsp_ctl_work(struct work_struct *work)
{
struct wmfw_ctl_work *ctl_work = container_of(work,
struct wmfw_ctl_work,
work);
wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl);
kfree(ctl_work);
}
static int wm_adsp_create_control(struct snd_soc_codec *codec,
const struct wm_adsp_alg_region *region)
{
struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
struct wm_coeff_ctl *ctl;
struct wmfw_ctl_work *ctl_work;
char *name;
char *region_name;
int ret;
name = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!name)
return -ENOMEM;
switch (region->type) {
case WMFW_ADSP1_PM:
region_name = "PM";
break;
case WMFW_ADSP1_DM:
region_name = "DM";
break;
case WMFW_ADSP2_XM:
region_name = "XM";
break;
case WMFW_ADSP2_YM:
region_name = "YM";
break;
case WMFW_ADSP1_ZM:
region_name = "ZM";
break;
default:
ret = -EINVAL;
goto err_name;
}
snprintf(name, PAGE_SIZE, "DSP%d %s %x",
dsp->num, region_name, region->alg);
list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
list) {
if (!strcmp(ctl->name, name)) {
if (!ctl->enabled)
ctl->enabled = 1;
goto found;
}
}
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
if (!ctl) {
ret = -ENOMEM;
goto err_name;
}
ctl->region = *region;
ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
if (!ctl->name) {
ret = -ENOMEM;
goto err_ctl;
}
ctl->enabled = 1;
ctl->set = 0;
ctl->ops.xget = wm_coeff_get;
ctl->ops.xput = wm_coeff_put;
ctl->card = codec->card->snd_card;
ctl->adsp = dsp;
ctl->len = region->len;
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
if (!ctl->cache) {
ret = -ENOMEM;
goto err_ctl_name;
}
ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
if (!ctl_work) {
ret = -ENOMEM;
goto err_ctl_cache;
}
ctl_work->wm_coeff = dsp->wm_coeff;
ctl_work->ctl = ctl;
INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
schedule_work(&ctl_work->work);
found:
kfree(name);
return 0;
err_ctl_cache:
kfree(ctl->cache);
err_ctl_name:
kfree(ctl->name);
err_ctl:
kfree(ctl);
err_name:
kfree(name);
return ret;
}
static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
{
struct regmap *regmap = dsp->regmap;
struct wmfw_adsp1_id_hdr adsp1_id;
@ -730,7 +1086,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP1_DM;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
region->base = be32_to_cpu(adsp1_alg[i].dm);
region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
if (i + 1 < algs) {
region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
region->len -= be32_to_cpu(adsp1_alg[i].dm);
wm_adsp_create_control(codec, region);
} else {
adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id));
}
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@ -738,7 +1103,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP1_ZM;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
region->base = be32_to_cpu(adsp1_alg[i].zm);
region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
if (i + 1 < algs) {
region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
region->len -= be32_to_cpu(adsp1_alg[i].zm);
wm_adsp_create_control(codec, region);
} else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id));
}
break;
case WMFW_ADSP2:
@ -758,7 +1132,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_XM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].xm);
region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
if (i + 1 < algs) {
region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
region->len -= be32_to_cpu(adsp2_alg[i].xm);
wm_adsp_create_control(codec, region);
} else {
adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
}
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@ -766,7 +1149,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_YM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].ym);
region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
if (i + 1 < algs) {
region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
region->len -= be32_to_cpu(adsp2_alg[i].ym);
wm_adsp_create_control(codec, region);
} else {
adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
}
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@ -774,7 +1166,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_ZM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].zm);
region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
if (i + 1 < algs) {
region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
region->len -= be32_to_cpu(adsp2_alg[i].zm);
wm_adsp_create_control(codec, region);
} else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
}
break;
}
}
@ -986,6 +1387,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_soc_codec *codec = w->codec;
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
struct wm_coeff_ctl *ctl;
int ret;
int val;
@ -1023,7 +1425,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
ret = wm_adsp_setup_algs(dsp);
ret = wm_adsp_setup_algs(dsp, codec);
if (ret != 0)
goto err;
@ -1031,6 +1433,16 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
/* Initialize caches for enabled and unset controls */
ret = wm_coeff_init_control_caches(dsp->wm_coeff);
if (ret != 0)
goto err;
/* Sync set controls */
ret = wm_coeff_sync_controls(dsp->wm_coeff);
if (ret != 0)
goto err;
/* Start the core running */
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_CORE_ENA | ADSP1_START,
@ -1047,6 +1459,11 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_SYS_ENA, 0);
list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
list) {
ctl->enabled = 0;
}
break;
default:
@ -1099,6 +1516,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
struct wm_adsp_alg_region *alg_region;
struct wm_coeff_ctl *ctl;
unsigned int val;
int ret;
@ -1164,7 +1582,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
ret = wm_adsp_setup_algs(dsp);
ret = wm_adsp_setup_algs(dsp, codec);
if (ret != 0)
goto err;
@ -1172,6 +1590,16 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
/* Initialize caches for enabled and unset controls */
ret = wm_coeff_init_control_caches(dsp->wm_coeff);
if (ret != 0)
goto err;
/* Sync set controls */
ret = wm_coeff_sync_controls(dsp->wm_coeff);
if (ret != 0)
goto err;
ret = regmap_update_bits(dsp->regmap,
dsp->base + ADSP2_CONTROL,
ADSP2_CORE_ENA | ADSP2_START,
@ -1209,6 +1637,11 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
ret);
}
list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
list) {
ctl->enabled = 0;
}
while (!list_empty(&dsp->alg_regions)) {
alg_region = list_first_entry(&dsp->alg_regions,
struct wm_adsp_alg_region,
@ -1247,36 +1680,48 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
INIT_LIST_HEAD(&adsp->alg_regions);
adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff),
GFP_KERNEL);
if (!adsp->wm_coeff)
return -ENOMEM;
adsp->wm_coeff->regmap = adsp->regmap;
adsp->wm_coeff->dev = adsp->dev;
INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list);
if (dvfs) {
adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
if (IS_ERR(adsp->dvfs)) {
ret = PTR_ERR(adsp->dvfs);
dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
return ret;
goto out_coeff;
}
ret = regulator_enable(adsp->dvfs);
if (ret != 0) {
dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
ret);
return ret;
goto out_coeff;
}
ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
if (ret != 0) {
dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
ret);
return ret;
goto out_coeff;
}
ret = regulator_disable(adsp->dvfs);
if (ret != 0) {
dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
ret);
return ret;
goto out_coeff;
}
}
return 0;
out_coeff:
kfree(adsp->wm_coeff);
return ret;
}
EXPORT_SYMBOL_GPL(wm_adsp2_init);

View file

@ -30,6 +30,7 @@ struct wm_adsp_alg_region {
unsigned int alg;
int type;
unsigned int base;
size_t len;
};
struct wm_adsp {
@ -55,6 +56,8 @@ struct wm_adsp {
bool running;
struct regulator *dvfs;
struct wm_coeff *wm_coeff;
};
#define WM_ADSP1(wname, num) \

View file

@ -54,16 +54,6 @@ config SND_DM6467_SOC_EVM
help
Say Y if you want to add support for SoC audio on TI
config SND_DAVINCI_SOC_SFFSDR
tristate "SoC Audio support for SFFSDR"
depends on SND_DAVINCI_SOC && MACH_SFFSDR
select SND_DAVINCI_SOC_I2S
select SND_SOC_PCM3008
select SFFSDR_FPGA
help
Say Y if you want to add support for SoC audio on
Lyrtech SFFSDR board.
config SND_DA830_SOC_EVM
tristate "SoC Audio support for DA830/OMAP-L137 EVM"
depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM

View file

@ -11,10 +11,8 @@ obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o
# DAVINCI Machine Support
snd-soc-evm-objs := davinci-evm.o
snd-soc-sffsdr-objs := davinci-sffsdr.o
obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
obj-$(CONFIG_SND_DM6467_SOC_EVM) += snd-soc-evm.o
obj-$(CONFIG_SND_DA830_SOC_EVM) += snd-soc-evm.o
obj-$(CONFIG_SND_DA850_SOC_EVM) += snd-soc-evm.o
obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o

View file

@ -1024,7 +1024,7 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of(
struct device_node *np = pdev->dev.of_node;
struct snd_platform_data *pdata = NULL;
const struct of_device_id *match =
of_match_device(of_match_ptr(mcasp_dt_ids), &pdev->dev);
of_match_device(mcasp_dt_ids, &pdev->dev);
const u32 *of_serial_dir32;
u8 *of_serial_dir;
@ -1257,7 +1257,7 @@ static struct platform_driver davinci_mcasp_driver = {
.driver = {
.name = "davinci-mcasp",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(mcasp_dt_ids),
.of_match_table = mcasp_dt_ids,
},
};

View file

@ -1,181 +0,0 @@
/*
* ASoC driver for Lyrtech SFFSDR board.
*
* Author: Hugo Villeneuve
* Copyright (C) 2008 Lyrtech inc
*
* Based on ASoC driver for TI DAVINCI EVM platform, original copyright follow:
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.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 <asm/dma.h>
#include <asm/mach-types.h>
#ifdef CONFIG_SFFSDR_FPGA
#include <asm/plat-sffsdr/sffsdr-fpga.h>
#endif
#include <mach/edma.h>
#include "../codecs/pcm3008.h"
#include "davinci-pcm.h"
#include "davinci-i2s.h"
/*
* CLKX and CLKR are the inputs for the Sample Rate Generator.
* FSX and FSR are outputs, driven by the sample Rate Generator.
*/
#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
SND_SOC_DAIFMT_CBM_CFS | \
SND_SOC_DAIFMT_IB_NF)
static int sffsdr_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int fs;
int ret = 0;
/* Fsref can be 32000, 44100 or 48000. */
fs = params_rate(params);
#ifndef CONFIG_SFFSDR_FPGA
/* Without the FPGA module, the Fs is fixed at 44100 Hz */
if (fs != 44100) {
pr_debug("warning: only 44.1 kHz is supported without SFFSDR FPGA module\n");
return -EINVAL;
}
#endif
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
if (ret < 0)
return ret;
pr_debug("sffsdr_hw_params: rate = %d Hz\n", fs);
#ifndef CONFIG_SFFSDR_FPGA
return 0;
#else
return sffsdr_fpga_set_codec_fs(fs);
#endif
}
static struct snd_soc_ops sffsdr_ops = {
.hw_params = sffsdr_hw_params,
};
/* davinci-sffsdr digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link sffsdr_dai = {
.name = "PCM3008", /* Codec name */
.stream_name = "PCM3008 HiFi",
.cpu_dai_name = "davinci-mcbsp",
.codec_dai_name = "pcm3008-hifi",
.codec_name = "pcm3008-codec",
.platform_name = "davinci-mcbsp",
.ops = &sffsdr_ops,
};
/* davinci-sffsdr audio machine driver */
static struct snd_soc_card snd_soc_sffsdr = {
.name = "DaVinci SFFSDR",
.owner = THIS_MODULE,
.dai_link = &sffsdr_dai,
.num_links = 1,
};
/* sffsdr audio private data */
static struct pcm3008_setup_data sffsdr_pcm3008_setup = {
.dem0_pin = GPIO(45),
.dem1_pin = GPIO(46),
.pdad_pin = GPIO(47),
.pdda_pin = GPIO(38),
};
struct platform_device pcm3008_codec = {
.name = "pcm3008-codec",
.id = 0,
.dev = {
.platform_data = &sffsdr_pcm3008_setup,
},
};
static struct resource sffsdr_snd_resources[] = {
{
.start = DAVINCI_MCBSP_BASE,
.end = DAVINCI_MCBSP_BASE + SZ_8K - 1,
.flags = IORESOURCE_MEM,
},
};
static struct evm_snd_platform_data sffsdr_snd_data = {
.tx_dma_ch = DAVINCI_DMA_MCBSP_TX,
.rx_dma_ch = DAVINCI_DMA_MCBSP_RX,
};
static struct platform_device *sffsdr_snd_device;
static int __init sffsdr_init(void)
{
int ret;
if (!machine_is_sffsdr())
return -EINVAL;
platform_device_register(&pcm3008_codec);
sffsdr_snd_device = platform_device_alloc("soc-audio", 0);
if (!sffsdr_snd_device) {
printk(KERN_ERR "platform device allocation failed\n");
return -ENOMEM;
}
platform_set_drvdata(sffsdr_snd_device, &snd_soc_sffsdr);
platform_device_add_data(sffsdr_snd_device, &sffsdr_snd_data,
sizeof(sffsdr_snd_data));
ret = platform_device_add_resources(sffsdr_snd_device,
sffsdr_snd_resources,
ARRAY_SIZE(sffsdr_snd_resources));
if (ret) {
printk(KERN_ERR "platform device add resources failed\n");
goto error;
}
ret = platform_device_add(sffsdr_snd_device);
if (ret)
goto error;
return ret;
error:
platform_device_put(sffsdr_snd_device);
return ret;
}
static void __exit sffsdr_exit(void)
{
platform_device_unregister(sffsdr_snd_device);
platform_device_unregister(&pcm3008_codec);
}
module_init(sffsdr_init);
module_exit(sffsdr_exit);
MODULE_AUTHOR("Hugo Villeneuve");
MODULE_DESCRIPTION("Lyrtech SFFSDR ASoC driver");
MODULE_LICENSE("GPL");

View file

@ -1,7 +1,7 @@
/*
* ALSA SoC Synopsys I2S Audio Layer
*
* sound/soc/spear/designware_i2s.c
* sound/soc/dwc/designware_i2s.c
*
* Copyright (C) 2010 ST Microelectronics
* Rajeev Kumar <rajeev-dlh.kumar@st.com>
@ -396,7 +396,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
}
if (cap & DWC_I2S_PLAY) {
dev_dbg(&pdev->dev, " SPEAr: play supported\n");
dev_dbg(&pdev->dev, " designware: play supported\n");
dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
dw_i2s_dai->playback.channels_max = pdata->channel;
dw_i2s_dai->playback.formats = pdata->snd_fmts;
@ -404,7 +404,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
}
if (cap & DWC_I2S_RECORD) {
dev_dbg(&pdev->dev, "SPEAr: record supported\n");
dev_dbg(&pdev->dev, "designware: record supported\n");
dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM;
dw_i2s_dai->capture.channels_max = pdata->channel;
dw_i2s_dai->capture.formats = pdata->snd_fmts;

View file

@ -108,18 +108,13 @@ if SND_IMX_SOC
config SND_SOC_IMX_SSI
tristate
config SND_SOC_IMX_PCM
tristate
config SND_SOC_IMX_PCM_FIQ
bool
select FIQ
select SND_SOC_IMX_PCM
config SND_SOC_IMX_PCM_DMA
bool
select SND_SOC_GENERIC_DMAENGINE_PCM
select SND_SOC_IMX_PCM
config SND_SOC_IMX_AUDMUX
tristate
@ -173,6 +168,18 @@ config SND_SOC_EUKREA_TLV320
Enable I2S based access to the TLV320AIC23B codec attached
to the SSI interface
config SND_SOC_IMX_WM8962
tristate "SoC Audio support for i.MX boards with wm8962"
depends on OF && I2C
select SND_SOC_WM8962
select SND_SOC_IMX_PCM_DMA
select SND_SOC_IMX_AUDMUX
select SND_SOC_FSL_SSI
select SND_SOC_FSL_UTILS
help
Say Y if you want to add support for SoC audio on an i.MX board with
a wm8962 codec.
config SND_SOC_IMX_SGTL5000
tristate "SoC Audio support for i.MX boards with sgtl5000"
depends on OF && I2C

View file

@ -30,18 +30,11 @@ obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o
# i.MX Platform Support
snd-soc-imx-ssi-objs := imx-ssi.o
snd-soc-imx-audmux-objs := imx-audmux.o
snd-soc-imx-pcm-objs := imx-pcm.o
ifneq ($(CONFIG_SND_SOC_IMX_PCM_FIQ),)
snd-soc-imx-pcm-objs += imx-pcm-fiq.o
endif
ifneq ($(CONFIG_SND_SOC_IMX_PCM_DMA),)
snd-soc-imx-pcm-objs += imx-pcm-dma.o
endif
obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o
obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o
obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
# i.MX Machine Support
snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
@ -49,6 +42,7 @@ snd-soc-phycore-ac97-objs := phycore-ac97.o
snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
snd-soc-imx-wm8962-objs := imx-wm8962.o
snd-soc-imx-mc13783-objs := imx-mc13783.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
@ -56,4 +50,5 @@ obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o

View file

@ -80,7 +80,7 @@ static struct snd_soc_dai_link eukrea_tlv320_dai = {
.name = "tlv320aic23",
.stream_name = "TLV320AIC23",
.codec_dai_name = "tlv320aic23-hifi",
.platform_name = "imx-fiq-pcm-audio.0",
.platform_name = "imx-ssi.0",
.codec_name = "tlv320aic23-codec.0-001a",
.cpu_dai_name = "imx-ssi.0",
.ops = &eukrea_tlv320_snd_ops,

View file

@ -122,7 +122,6 @@ struct fsl_ssi_private {
bool new_binding;
bool ssi_on_imx;
struct clk *clk;
struct platform_device *imx_pcm_pdev;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct imx_dma_data filter_data_tx;
@ -809,13 +808,9 @@ static int fsl_ssi_probe(struct platform_device *pdev)
}
if (ssi_private->ssi_on_imx) {
ssi_private->imx_pcm_pdev =
platform_device_register_simple("imx-pcm-audio",
-1, NULL, 0);
if (IS_ERR(ssi_private->imx_pcm_pdev)) {
ret = PTR_ERR(ssi_private->imx_pcm_pdev);
ret = imx_pcm_dma_init(pdev);
if (ret)
goto error_dev;
}
}
/*
@ -854,7 +849,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
error_dai:
if (ssi_private->ssi_on_imx)
platform_device_unregister(ssi_private->imx_pcm_pdev);
imx_pcm_dma_exit(pdev);
snd_soc_unregister_component(&pdev->dev);
error_dev:
@ -889,7 +884,7 @@ static int fsl_ssi_remove(struct platform_device *pdev)
if (!ssi_private->new_binding)
platform_device_unregister(ssi_private->pdev);
if (ssi_private->ssi_on_imx) {
platform_device_unregister(ssi_private->imx_pcm_pdev);
imx_pcm_dma_exit(pdev);
clk_disable_unprepare(ssi_private->clk);
clk_put(ssi_private->clk);
}

View file

@ -26,7 +26,6 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/pinctrl/consumer.h>
#include "imx-audmux.h"
@ -247,7 +246,6 @@ EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port);
static int imx_audmux_probe(struct platform_device *pdev)
{
struct resource *res;
struct pinctrl *pinctrl;
const struct of_device_id *of_id =
of_match_device(imx_audmux_dt_ids, &pdev->dev);
@ -256,12 +254,6 @@ static int imx_audmux_probe(struct platform_device *pdev)
if (IS_ERR(audmux_base))
return PTR_ERR(audmux_base);
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
dev_err(&pdev->dev, "setup pinctrl failed!");
return PTR_ERR(pinctrl);
}
audmux_clk = devm_clk_get(&pdev->dev, "audmux");
if (IS_ERR(audmux_clk)) {
dev_dbg(&pdev->dev, "cannot get clock: %ld\n",

View file

@ -64,7 +64,7 @@ static struct snd_soc_dai_link imx_mc13783_dai_mc13783[] = {
.codec_dai_name = "mc13783-hifi",
.codec_name = "mc13783-codec",
.cpu_dai_name = "imx-ssi.0",
.platform_name = "imx-pcm-audio.0",
.platform_name = "imx-ssi.0",
.ops = &imx_mc13783_hifi_ops,
.symmetric_rates = 1,
.dai_fmt = FMT_SSI,

Some files were not shown because too many files have changed in this diff Show more