mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-26 01:30:54 +07:00
Merge remote-tracking branch 'asoc/fix/fsl' into asoc-linus
This commit is contained in:
commit
986a622df3
62
Documentation/devicetree/bindings/mfd/arizona.txt
Normal file
62
Documentation/devicetree/bindings/mfd/arizona.txt
Normal 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,
|
||||
>;
|
||||
};
|
23
Documentation/devicetree/bindings/sound/adi,adau1701.txt
Normal file
23
Documentation/devicetree/bindings/sound/adi,adau1701.txt
Normal 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>;
|
||||
};
|
||||
};
|
46
Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt
Normal file
46
Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt
Normal 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>;
|
||||
};
|
@ -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>;
|
||||
};
|
||||
|
@ -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";
|
||||
};
|
30
Documentation/devicetree/bindings/sound/rt5640.txt
Normal file
30
Documentation/devicetree/bindings/sound/rt5640.txt
Normal 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>;
|
||||
};
|
@ -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>;
|
||||
};
|
||||
|
10
Documentation/devicetree/bindings/sound/spdif-receiver.txt
Normal file
10
Documentation/devicetree/bindings/sound/spdif-receiver.txt
Normal 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";
|
||||
};
|
@ -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";
|
||||
};
|
20
Documentation/devicetree/bindings/sound/ssm2518.txt
Normal file
20
Documentation/devicetree/bindings/sound/ssm2518.txt
Normal 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>;
|
||||
};
|
@ -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 */
|
||||
>;
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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, ®);
|
||||
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, ®);
|
||||
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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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 */
|
||||
|
22
include/linux/platform_data/ssm2518.h
Normal file
22
include/linux/platform_data/ssm2518.h
Normal 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
22
include/sound/rt5640.h
Normal 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
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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/
|
||||
|
@ -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()))
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -39,7 +39,6 @@
|
||||
|
||||
#include <asm/dma.h>
|
||||
|
||||
#include "bf5xx-ac97-pcm.h"
|
||||
#include "bf5xx-ac97.h"
|
||||
#include "bf5xx-sport.h"
|
||||
|
||||
|
@ -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
|
@ -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");
|
||||
|
@ -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 = {
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -48,7 +48,6 @@
|
||||
|
||||
#include "../codecs/ad1980.h"
|
||||
|
||||
#include "bf5xx-ac97-pcm.h"
|
||||
#include "bf5xx-ac97.h"
|
||||
|
||||
static struct snd_soc_card bf5xx_board;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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");
|
@ -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
|
@ -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");
|
||||
|
@ -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
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
};
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
||||
|
@ -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");
|
@ -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);
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
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
2092
sound/soc/codecs/rt5640.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -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, ®);
|
||||
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, ®);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
#define _SGTL5000_H
|
||||
|
||||
/*
|
||||
* Register values.
|
||||
* Registers addresses
|
||||
*/
|
||||
#define SGTL5000_CHIP_ID 0x0000
|
||||
#define SGTL5000_CHIP_DIG_POWER 0x0002
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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
856
sound/soc/codecs/ssm2518.c
Normal 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");
|
20
sound/soc/codecs/ssm2518.h
Normal file
20
sound/soc/codecs/ssm2518.h
Normal 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
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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(®ion->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(®ion->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(®ion->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(®ion->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(®ion->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);
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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");
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user