ASoC: Updates for v4.5

This is quite a busy release on the driver front with a lot of new
 drivers being added but comparatively quiet on the core side with only
 one big change going in and that a fairly straightforward refactoring.
 
  - Conversion of the array of DAI links to a list by Mengdong Lin,
    supporting dynamically adding and removing DAI links.
  - Some more fixes for the topology code, though it is still not final
    and ready for enabling in production.  We really need to get to the
    point where that can be done.
  - A pile of changes for Intel SkyLake drivers which hopefully deliver
    some useful initial functionality for systems with this chipset,
    though there is more work still to come.
  - New drivers for a number of Imagination Technologies IPs.
  - Lots of new features and cleanups for the Renesas drivers.
  - ANC support for WM5110.
  - New driver for Atmel class D speaker drivers.
  - New drivers for Cirrus CS47L24 and WM1831.
  - New driver for Dialog DA7128.
  - New drivers for Realtek RT5659 and RT56156.
  - New driver for Rockchip RK3036.
  - New driver for TI PC3168A
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJWee4AAAoJECTWi3JdVIfQwPkH/jMpijne8cfnOm0jQ0WyvXPd
 HSig93ZAJ2x/+uR7yrmF2BsxhnT6G1lmDb2zxYhHGEAMyBtdA/xtOjO1wTRjXgIo
 9p7ui66uP/sAW+NfHsvXVyf/8vV9nw8t0CsyBf+ijynz6MiDigQR7e8RA7xZR7oQ
 oBabHPZb85OJjxmn2XlqaxUoxNhBdAS3gNoctMy9VZDwSLiunOvYTGBoBA43LQk6
 6DN5VRu7wg05ruN6t5xZ5bd5WAtvgN+tSsRAabmunWDnILCa8OaOmzWPv3QITIva
 PQLauiya4N0/YgyNGYaBY/6pc5u9SHzq0caYUq0I6WY6Phjo7wfU8/+y4NqUPn4=
 =d18k
 -----END PGP SIGNATURE-----

Merge tag 'asoc-v4.5' into asoc-next

ASoC: Updates for v4.5

This is quite a busy release on the driver front with a lot of new
drivers being added but comparatively quiet on the core side with only
one big change going in and that a fairly straightforward refactoring.

 - Conversion of the array of DAI links to a list by Mengdong Lin,
   supporting dynamically adding and removing DAI links.
 - Some more fixes for the topology code, though it is still not final
   and ready for enabling in production.  We really need to get to the
   point where that can be done.
 - A pile of changes for Intel SkyLake drivers which hopefully deliver
   some useful initial functionality for systems with this chipset,
   though there is more work still to come.
 - New drivers for a number of Imagination Technologies IPs.
 - Lots of new features and cleanups for the Renesas drivers.
 - ANC support for WM5110.
 - New driver for Atmel class D speaker drivers.
 - New drivers for Cirrus CS47L24 and WM1831.
 - New driver for Dialog DA7128.
 - New drivers for Realtek RT5659 and RT56156.
 - New driver for Rockchip RK3036.
 - New driver for TI PC3168A

# gpg: Signature made Wed 23 Dec 2015 00:42:40 GMT using RSA key ID 5D5487D0
# gpg: Good signature from "Mark Brown <broonie@sirena.org.uk>"
# gpg:                 aka "Mark Brown <broonie@debian.org>"
# gpg:                 aka "Mark Brown <broonie@kernel.org>"
# gpg:                 aka "Mark Brown <broonie@tardis.ed.ac.uk>"
# gpg:                 aka "Mark Brown <broonie@linaro.org>"
# gpg:                 aka "Mark Brown <Mark.Brown@linaro.org>"
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 3F25 68AA C269 98F9 E813  A1C5 C3F4 36CA 30F5 D8EB
#      Subkey fingerprint: ADE6 68AA 6757 18B5 9FE2  9FEA 24D6 8B72 5D54 87D0
This commit is contained in:
Mark Brown 2016-01-11 13:54:29 +00:00
commit fffe9b89d8
145 changed files with 26523 additions and 3255 deletions

View File

@ -7,6 +7,16 @@ Required properties:
- compatible : "asahi-kasei,ak4613"
- reg : The chip select number on the I2C bus
Optional properties:
- asahi-kasei,in1-single-end : Boolean. Indicate input / output pins are single-ended.
- asahi-kasei,in2-single-end rather than differential.
- asahi-kasei,out1-single-end
- asahi-kasei,out2-single-end
- asahi-kasei,out3-single-end
- asahi-kasei,out4-single-end
- asahi-kasei,out5-single-end
- asahi-kasei,out6-single-end
Example:
&i2c {

View File

@ -16,6 +16,10 @@ Required properties:
Required elements: "pclk", "gclk" and "aclk".
- clocks
Please refer to clock-bindings.txt.
- assigned-clocks
Should be <&classd_gclk>.
- assigned-clock-parents
Should be <&audio_pll_pmc>.
Optional properties:
- pinctrl-names, pinctrl-0
@ -43,6 +47,8 @@ classd: classd@fc048000 {
dma-names = "tx";
clocks = <&classd_clk>, <&classd_gclk>, <&audio_pll_pmc>;
clock-names = "pclk", "gclk", "aclk";
assigned-clocks = <&classd_gclk>;
assigned-clock-parents = <&audio_pll_pmc>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_classd_default>;

View File

@ -0,0 +1,55 @@
* Atmel PDMIC driver under ALSA SoC architecture
Required properties:
- compatible
Should be "atmel,sama5d2-pdmic".
- reg
Should contain PDMIC registers location and length.
- interrupts
Should contain the IRQ line for the PDMIC.
- dmas
One DMA specifiers as described in atmel-dma.txt and dma.txt files.
- dma-names
Must be "rx".
- clock-names
Required elements:
- "pclk" peripheral clock
- "gclk" generated clock
- clocks
Must contain an entry for each required entry in clock-names.
Please refer to clock-bindings.txt.
- atmel,mic-min-freq
The minimal frequency that the micphone supports.
- atmel,mic-max-freq
The maximal frequency that the micphone supports.
Optional properties:
- pinctrl-names, pinctrl-0
Please refer to pinctrl-bindings.txt.
- atmel,model
The user-visible name of this sound card.
The default value is "PDMIC".
- atmel,mic-offset
The offset that should be added.
The range is from -32768 to 32767.
The default value is 0.
Example:
pdmic@f8018000 {
compatible = "atmel,sama5d2-pdmic";
reg = <0xf8018000 0x124>;
interrupts = <48 IRQ_TYPE_LEVEL_HIGH 7>;
dmas = <&dma0
(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1)
| AT91_XDMAC_DT_PERID(50))>;
dma-names = "rx";
clocks = <&pdmic_clk>, <&pdmic_gclk>;
clock-names = "pclk", "gclk";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pdmic_default>;
atmel,model = "PDMIC @ sama5d2_xplained";
atmel,mic-min-freq = <1000000>;
atmel,mic-max-freq = <3246000>;
atmel,mic-offset = <0x0>;
};

View File

@ -0,0 +1,104 @@
Dialog Semiconductor DA7218 Audio Codec bindings
DA7218 is an audio codec with HP detect feature.
======
Required properties:
- compatible : Should be "dlg,da7217" or "dlg,da7218"
- reg: Specifies the I2C slave address
- VDD-supply: VDD power supply for the device
- VDDMIC-supply: VDDMIC power supply for the device
- VDDIO-supply: VDDIO power supply for the device
(See Documentation/devicetree/bindings/regulator/regulator.txt for further
information relating to regulators)
Optional properties:
- interrupt-parent: Specifies the phandle of the interrupt controller to which
the IRQs from DA7218 are delivered to.
- interrupts: IRQ line info for DA7218 chip.
(See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for
further information relating to interrupt properties)
- interrupt-names : Name associated with interrupt line. Should be "wakeup" if
interrupt is to be used to wake system, otherwise "irq" should be used.
- wakeup-source: Flag to indicate this device can wake system (suspend/resume).
- clocks : phandle and clock specifier for codec MCLK.
- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
- dlg,micbias1-lvl-millivolt : Voltage (mV) for Mic Bias 1
[<1200>, <1600>, <1800>, <2000>, <2200>, <2400>, <2600>, <2800>, <3000>]
- dlg,micbias2-lvl-millivolt : Voltage (mV) for Mic Bias 2
[<1200>, <1600>, <1800>, <2000>, <2200>, <2400>, <2600>, <2800>, <3000>]
- dlg,mic1-amp-in-sel : Mic1 input source type
["diff", "se_p", "se_n"]
- dlg,mic2-amp-in-sel : Mic2 input source type
["diff", "se_p", "se_n"]
- dlg,dmic1-data-sel : DMIC1 channel select based on clock edge.
["lrise_rfall", "lfall_rrise"]
- dlg,dmic1-samplephase : When to sample audio from DMIC1.
["on_clkedge", "between_clkedge"]
- dlg,dmic1-clkrate-hz : DMic1 clock frequency (Hz).
[<1500000>, <3000000>]
- dlg,dmic2-data-sel : DMic2 channel select based on clock edge.
["lrise_rfall", "lfall_rrise"]
- dlg,dmic2-samplephase : When to sample audio from DMic2.
["on_clkedge", "between_clkedge"]
- dlg,dmic2-clkrate-hz : DMic2 clock frequency (Hz).
[<1500000>, <3000000>]
- dlg,hp-diff-single-supply : Boolean flag, use single supply for HP
(DA7217 only)
======
Optional Child node - 'da7218_hpldet' (DA7218 only):
Optional properties:
- dlg,jack-rate-us : Time between jack detect measurements (us)
[<5>, <10>, <20>, <40>, <80>, <160>, <320>, <640>]
- dlg,jack-debounce : Number of debounce measurements taken for jack detect
[<0>, <2>, <3>, <4>]
- dlg,jack-threshold-pct : Threshold level for jack detection (% of VDD)
[<84>, <88>, <92>, <96>]
- dlg,comp-inv : Boolean flag, invert comparator output
- dlg,hyst : Boolean flag, enable hysteresis
- dlg,discharge : Boolean flag, auto discharge of Mic Bias on jack removal
======
Example:
codec: da7218@1a {
compatible = "dlg,da7218";
reg = <0x1a>;
interrupt-parent = <&gpio6>;
interrupts = <11 IRQ_TYPE_LEVEL_HIGH>;
wakeup-source;
VDD-supply = <&reg_audio>;
VDDMIC-supply = <&reg_audio>;
VDDIO-supply = <&reg_audio>;
clocks = <&clks 201>;
clock-names = "mclk";
dlg,micbias1-lvl-millivolt = <2600>;
dlg,micbias2-lvl-millivolt = <2600>;
dlg,mic1-amp-in-sel = "diff";
dlg,mic2-amp-in-sel = "diff";
dlg,dmic1-data-sel = "lrise_rfall";
dlg,dmic1-samplephase = "on_clkedge";
dlg,dmic1-clkrate-hz = <3000000>;
dlg,dmic2-data-sel = "lrise_rfall";
dlg,dmic2-samplephase = "on_clkedge";
dlg,dmic2-clkrate-hz = <3000000>;
da7218_hpldet {
dlg,jack-rate-us = <40>;
dlg,jack-debounce = <2>;
dlg,jack-threshold-pct = <84>;
dlg,hyst;
};
};

View File

@ -28,13 +28,15 @@ Optional properties:
- clocks : phandle and clock specifier for codec MCLK.
- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
- dlg,ldo-lvl : Required internal LDO voltage (mV) level for digital engine
[<1050>, <1100>, <1200>, <1400>]
- dlg,micbias-lvl : Voltage (mV) for Mic Bias
[<1800>, <2000>, <2200>, <2400>, <2600>]
[<1600>, <1800>, <2000>, <2200>, <2400>, <2600>]
- dlg,mic-amp-in-sel : Mic input source type
["diff", "se_p", "se_n"]
Deprecated properties:
- dlg,ldo-lvl : Required internal LDO voltage (mV) level for digital engine
(LDO unavailable in production HW so property no longer required).
======
Child node - 'da7219_aad':

View File

@ -25,6 +25,11 @@ Required properties:
"mem" Peripheral access clock to access registers.
"ipg" Peripheral clock to driver module.
"asrck_<0-f>" Clock sources for input and output clock.
"spba" The spba clock is required when ASRC is placed as a
bus slave of the Shared Peripheral Bus and when two
or more bus masters (CPU, DMA or DSP) try to access
it. This property is optional depending on the SoC
design.
- big-endian : If this property is absent, the little endian mode
will be in use as default. Otherwise, the big endian

View File

@ -27,6 +27,11 @@ Required properties:
derive HCK, SCK and FS.
"fsys" The system clock derived from ahb clock used to
derive HCK, SCK and FS.
"spba" The spba clock is required when ESAI is placed as a
bus slave of the Shared Peripheral Bus and when two
or more bus masters (CPU, DMA or DSP) try to access
it. This property is optional depending on the SoC
design.
- fsl,fifo-depth : The number of elements in the transmit and receive
FIFOs. This number is the maximum allowed value for

View File

@ -27,6 +27,11 @@ Required properties:
Transceiver Clock Diagram" of SoC reference manual.
It can also be referred to TxClk_Source bit of
register SPDIF_STC.
"spba" The spba clock is required when SPDIF is placed as a
bus slave of the Shared Peripheral Bus and when two
or more bus masters (CPU, DMA or DSP) try to access
it. This property is optional depending on the SoC
design.
- big-endian : If this property is absent, the native endian mode
will be in use as default, or the big endian mode

View File

@ -0,0 +1,47 @@
Imagination Technologies I2S Input Controller
Required Properties:
- compatible : Compatible list, must contain "img,i2s-in"
- #sound-dai-cells : Must be equal to 0
- reg : Offset and length of the register set for the device
- clocks : Contains an entry for each entry in clock-names
- clock-names : Must include the following entry:
"sys" The system clock
- dmas: Contains an entry for each entry in dma-names.
- dma-names: Must include the following entry:
"rx" Single DMA channel used by all active I2S channels
- img,i2s-channels : Number of I2S channels instantiated in the I2S in block
Optional Properties:
- interrupts : Contains the I2S in interrupts. Depending on
the configuration, there may be no interrupts, one interrupt,
or an interrupt per I2S channel. For the case where there is
one interrupt per channel, the interrupts should be listed
in ascending channel order
- resets: Contains a phandle to the I2S in reset signal
- reset-names: Contains the reset signal name "rst"
Example:
i2s_in: i2s-in@18100800 {
compatible = "img,i2s-in";
reg = <0x18100800 0x200>;
interrupts = <GIC_SHARED 7 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&mdc 30 0xffffffff 0>;
dma-names = "rx";
clocks = <&cr_periph SYS_CLK_I2S_IN>;
clock-names = "sys";
img,i2s-channels = <6>;
#sound-dai-cells = <0>;
};

View File

@ -0,0 +1,51 @@
Imagination Technologies I2S Output Controller
Required Properties:
- compatible : Compatible list, must contain "img,i2s-out"
- #sound-dai-cells : Must be equal to 0
- reg : Offset and length of the register set for the device
- clocks : Contains an entry for each entry in clock-names
- clock-names : Must include the following entries:
"sys" The system clock
"ref" The reference clock
- dmas: Contains an entry for each entry in dma-names.
- dma-names: Must include the following entry:
"tx" Single DMA channel used by all active I2S channels
- img,i2s-channels : Number of I2S channels instantiated in the I2S out block
- resets: Contains a phandle to the I2S out reset signal
- reset-names: Contains the reset signal name "rst"
Optional Properties:
- interrupts : Contains the I2S out interrupts. Depending on
the configuration, there may be no interrupts, one interrupt,
or an interrupt per I2S channel. For the case where there is
one interrupt per channel, the interrupts should be listed
in ascending channel order
Example:
i2s_out: i2s-out@18100A00 {
compatible = "img,i2s-out";
reg = <0x18100A00 0x200>;
interrupts = <GIC_SHARED 13 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&mdc 23 0xffffffff 0>;
dma-names = "tx";
clocks = <&cr_periph SYS_CLK_I2S_OUT>,
<&clk_core CLK_I2S>;
clock-names = "sys", "ref";
img,i2s-channels = <6>;
resets = <&pistachio_reset PISTACHIO_RESET_I2S_OUT>;
reset-names = "rst";
#sound-dai-cells = <0>;
};

View File

@ -0,0 +1,44 @@
Imagination Technologies Parallel Output Controller
Required Properties:
- compatible : Compatible list, must contain "img,parallel-out".
- #sound-dai-cells : Must be equal to 0
- reg : Offset and length of the register set for the device.
- dmas: Contains an entry for each entry in dma-names.
- dma-names: Must include the following entry:
"tx"
- clocks : Contains an entry for each entry in clock-names.
- clock-names : Includes the following entries:
"sys" The system clock
"ref" The reference clock
- resets: Contains a phandle to the parallel out reset signal
- reset-names: Contains the reset signal name "rst"
Optional Properties:
- interrupts : Contains the parallel out interrupt, if present
Example:
parallel_out: parallel-out@18100C00 {
compatible = "img,parallel-out";
reg = <0x18100C00 0x100>;
interrupts = <GIC_SHARED 19 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&mdc 16 0xffffffff 0>;
dma-names = "tx";
clocks = <&cr_periph SYS_CLK_PAUD_OUT>,
<&clk_core CLK_AUDIO_DAC>;
clock-names = "sys", "ref";
resets = <&pistachio_reset PISTACHIO_RESET_PRL_OUT>;
reset-names = "rst";
#sound-dai-cells = <0>;
};

View File

@ -0,0 +1,18 @@
Pistachio internal DAC DT bindings
Required properties:
- compatible: "img,pistachio-internal-dac"
- img,cr-top : Must contain a phandle to the top level control syscon
node which contains the internal dac control registers
- VDD-supply : Digital power supply regulator (+1.8V or +3.3V)
Examples:
internal_dac: internal-dac {
compatible = "img,pistachio-internal-dac";
img,cr-top = <&cr_top>;
VDD-supply = <&supply3v3>;
};

View File

@ -0,0 +1,41 @@
Imagination Technologies SPDIF Input Controller
Required Properties:
- compatible : Compatible list, must contain "img,spdif-in"
- #sound-dai-cells : Must be equal to 0
- reg : Offset and length of the register set for the device
- dmas: Contains an entry for each entry in dma-names.
- dma-names: Must include the following entry:
"rx"
- clocks : Contains an entry for each entry in clock-names
- clock-names : Includes the following entries:
"sys" The system clock
Optional Properties:
- resets: Should contain a phandle to the spdif in reset signal, if any
- reset-names: Should contain the reset signal name "rst", if a
reset phandle is given
- interrupts : Contains the spdif in interrupt, if present
Example:
spdif_in: spdif-in@18100E00 {
compatible = "img,spdif-in";
reg = <0x18100E00 0x100>;
interrupts = <GIC_SHARED 20 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&mdc 15 0xffffffff 0>;
dma-names = "rx";
clocks = <&cr_periph SYS_CLK_SPDIF_IN>;
clock-names = "sys";
#sound-dai-cells = <0>;
};

View File

@ -0,0 +1,44 @@
Imagination Technologies SPDIF Output Controller
Required Properties:
- compatible : Compatible list, must contain "img,spdif-out"
- #sound-dai-cells : Must be equal to 0
- reg : Offset and length of the register set for the device
- dmas: Contains an entry for each entry in dma-names.
- dma-names: Must include the following entry:
"tx"
- clocks : Contains an entry for each entry in clock-names.
- clock-names : Includes the following entries:
"sys" The system clock
"ref" The reference clock
- resets: Contains a phandle to the spdif out reset signal
- reset-names: Contains the reset signal name "rst"
Optional Properties:
- interrupts : Contains the parallel out interrupt, if present
Example:
spdif_out: spdif-out@18100D00 {
compatible = "img,spdif-out";
reg = <0x18100D00 0x100>;
interrupts = <GIC_SHARED 21 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&mdc 14 0xffffffff 0>;
dma-names = "tx";
clocks = <&cr_periph SYS_CLK_SPDIF_OUT>,
<&clk_core CLK_SPDIF>;
clock-names = "sys", "ref";
resets = <&pistachio_reset PISTACHIO_RESET_SPDIF_OUT>;
reset-names = "rst";
#sound-dai-cells = <0>;
};

View File

@ -0,0 +1,20 @@
Inno audio codec for RK3036
Inno audio codec is integrated inside RK3036 SoC.
Required properties:
- compatible : Should be "rockchip,rk3036-codec".
- reg : The registers of codec.
- clock-names : Should be "acodec_pclk".
- clocks : The clock of codec.
- rockchip,grf : The phandle of grf device node.
Example:
acodec: acodec-ana@20030000 {
compatible = "rk3036-codec";
reg = <0x20030000 0x4000>;
rockchip,grf = <&grf>;
clock-names = "acodec_pclk";
clocks = <&cru ACLK_VCODEC>;
};

View File

@ -7,8 +7,11 @@ Required properties:
"renesas,rcar_sound-gen3" if generation3
Examples with soctypes are:
- "renesas,rcar_sound-r8a7778" (R-Car M1A)
- "renesas,rcar_sound-r8a7779" (R-Car H1)
- "renesas,rcar_sound-r8a7790" (R-Car H2)
- "renesas,rcar_sound-r8a7791" (R-Car M2-W)
- "renesas,rcar_sound-r8a7793" (R-Car M2-N)
- "renesas,rcar_sound-r8a7794" (R-Car E2)
- "renesas,rcar_sound-r8a7795" (R-Car H3)
- reg : Should contain the register physical address.
required register is
@ -34,6 +37,8 @@ Required properties:
see below for detail.
- #sound-dai-cells : it must be 0 if your system is using single DAI
it must be 1 if your system is using multi DAI
Optional properties:
- #clock-cells : it must be 0 if your system has audio_clkout
it must be 1 if your system has audio_clkout0/1/2/3
- clock-frequency : for all audio_clkout0/1/2/3
@ -244,3 +249,80 @@ rcar_sound: sound@ec500000 {
};
};
};
Example: simple sound card
rsnd_ak4643: sound {
compatible = "simple-audio-card";
simple-audio-card,format = "left_j";
simple-audio-card,bitclock-master = <&sndcodec>;
simple-audio-card,frame-master = <&sndcodec>;
sndcpu: simple-audio-card,cpu {
sound-dai = <&rcar_sound>;
};
sndcodec: simple-audio-card,codec {
sound-dai = <&ak4643>;
clocks = <&audio_clock>;
};
};
&rcar_sound {
pinctrl-0 = <&sound_pins &sound_clk_pins>;
pinctrl-names = "default";
/* Single DAI */
#sound-dai-cells = <0>;
status = "okay";
rcar_sound,dai {
dai0 {
playback = <&ssi0 &src2 &dvc0>;
capture = <&ssi1 &src3 &dvc1>;
};
};
};
&ssi1 {
shared-pin;
};
Example: simple sound card for TDM
rsnd_tdm: sound {
compatible = "simple-audio-card";
simple-audio-card,format = "left_j";
simple-audio-card,bitclock-master = <&sndcodec>;
simple-audio-card,frame-master = <&sndcodec>;
sndcpu: simple-audio-card,cpu {
sound-dai = <&rcar_sound>;
dai-tdm-slot-num = <6>;
};
sndcodec: simple-audio-card,codec {
sound-dai = <&xxx>;
};
};
Example: simple sound card for Multi channel
&rcar_sound {
pinctrl-0 = <&sound_pins &sound_clk_pins>;
pinctrl-names = "default";
/* Single DAI */
#sound-dai-cells = <0>;
status = "okay";
rcar_sound,dai {
dai0 {
playback = <&ssi0 &ssi1 &ssi2 &src0 &dvc0>;
};
};
};

View File

@ -4,8 +4,8 @@ Renesas Sampling Rate Convert Sound Card specifies audio DAI connections of SoC
Required properties:
- compatible : "renesas,rsrc-card,<board>"
Examples with soctypes are:
- compatible : "renesas,rsrc-card{,<board>}"
Examples with boards are:
- "renesas,rsrc-card"
- "renesas,rsrc-card,lager"
- "renesas,rsrc-card,koelsch"

View File

@ -19,6 +19,7 @@ Required properties:
- clock-names: should contain followings:
- "i2s_hclk": clock for I2S BUS
- "i2s_clk" : clock for I2S controller
- rockchip,playback-channels: max playback channels, if not set, 8 channels default.
- rockchip,capture-channels: max capture channels, if not set, 2 channels default.
Example for rk3288 I2S controller:
@ -31,5 +32,6 @@ i2s@ff890000 {
dma-names = "tx", "rx";
clock-names = "i2s_hclk", "i2s_clk";
clocks = <&cru HCLK_I2S0>, <&cru SCLK_I2S0>;
rockchip,playback-channels = <8>;
rockchip,capture-channels = <2>;
};

View File

@ -0,0 +1,26 @@
RT5616 audio CODEC
This device supports I2C only.
Required properties:
- compatible : "realtek,rt5616".
- reg : The I2C address of the device.
Pins on the device (for linking into audio routes) for RT5616:
* IN1P
* IN2P
* IN2N
* LOUTL
* LOUTR
* HPOL
* HPOR
Example:
codec: rt5616@1b {
compatible = "realtek,rt5616";
reg = <0x1b>;
};

View File

@ -0,0 +1,75 @@
RT5659/RT5658 audio CODEC
This device supports I2C only.
Required properties:
- compatible : One of "realtek,rt5659" or "realtek,rt5658".
- reg : The I2C address of the device.
- interrupts : The CODEC's interrupt output.
Optional properties:
- realtek,in1-differential
- realtek,in3-differential
- realtek,in4-differential
Boolean. Indicate MIC1/3/4 input are differential, rather than single-ended.
- realtek,dmic1-data-pin
0: dmic1 is not used
1: using IN2N pin as dmic1 data pin
2: using GPIO5 pin as dmic1 data pin
3: using GPIO9 pin as dmic1 data pin
4: using GPIO11 pin as dmic1 data pin
- realtek,dmic2-data-pin
0: dmic2 is not used
1: using IN2P pin as dmic2 data pin
2: using GPIO6 pin as dmic2 data pin
3: using GPIO10 pin as dmic2 data pin
4: using GPIO12 pin as dmic2 data pin
- realtek,jd-src
0: No JD is used
1: using JD3 as JD source
- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
- realtek,reset-gpios : The GPIO that controls the CODEC's RESET pin.
Pins on the device (for linking into audio routes) for RT5659/RT5658:
* DMIC L1
* DMIC R1
* DMIC L2
* DMIC R2
* IN1P
* IN1N
* IN2P
* IN2N
* IN3P
* IN3N
* IN4P
* IN4N
* HPOL
* HPOR
* SPOL
* SPOR
* LOUTL
* LOUTR
* MONOOUT
* PDML
* PDMR
* SPDIF
Example:
rt5659 {
compatible = "realtek,rt5659";
reg = <0x1b>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(W, 3) GPIO_ACTIVE_HIGH>;
realtek,ldo1-en-gpios =
<&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
};

View File

@ -18,7 +18,7 @@ Required properties:
Optional properties:
- realtek,pow-ldo2-gpio : The GPIO that controls the CODEC's POW_LDO2 pin.
- realtek,reset-gpio : The GPIO that controls the CODEC's RESET pin.
- realtek,reset-gpio : The GPIO that controls the CODEC's RESET pin. Active low.
- realtek,in1-differential
- realtek,in2-differential

View File

@ -14,6 +14,9 @@ Required properties:
- "apb": the parent APB clock for this controller
- "codec": the parent module clock
Optional properties:
- allwinner,pa-gpios: gpio to enable external amplifier
Example:
codec: codec@01c22c00 {
#sound-dai-cells = <0>;

View File

@ -0,0 +1,48 @@
Texas Instruments pcm3168a DT bindings
This driver supports both SPI and I2C bus access for this codec
Required properties:
- compatible: "ti,pcm3168a"
- clocks : Contains an entry for each entry in clock-names
- clock-names : Includes the following entries:
"scki" The system clock
- VDD1-supply : Digital power supply regulator 1 (+3.3V)
- VDD2-supply : Digital power supply regulator 2 (+3.3V)
- VCCAD1-supply : ADC power supply regulator 1 (+5V)
- VCCAD2-supply : ADC power supply regulator 2 (+5V)
- VCCDA1-supply : DAC power supply regulator 1 (+5V)
- VCCDA2-supply : DAC power supply regulator 2 (+5V)
For required properties on SPI/I2C, consult SPI/I2C device tree documentation
Examples:
i2c0: i2c0@0 {
...
pcm3168a: audio-codec@44 {
compatible = "ti,pcm3168a";
reg = <0x44>;
clocks = <&clk_core CLK_AUDIO>;
clock-names = "scki";
VDD1-supply = <&supply3v3>;
VDD2-supply = <&supply3v3>;
VCCAD1-supply = <&supply5v0>;
VCCAD2-supply = <&supply5v0>;
VCCDA1-supply = <&supply5v0>;
VCCDA2-supply = <&supply5v0>;
pinctrl-names = "default";
pinctrl-0 = <&dac_clk_pin>;
};
};

View File

@ -0,0 +1,15 @@
WM8974 audio CODEC
This device supports both I2C and SPI (configured with pin strapping
on the board).
Required properties:
- compatible: "wlf,wm8974"
- reg: the I2C address or SPI chip select number of the device
Examples:
codec: wm8974@1a {
compatible = "wlf,wm8974";
reg = <0x1a>;
};

View File

@ -0,0 +1,49 @@
The Imagination Technologies SPDIF Input controller contains the following
controls:
name='IEC958 Capture Mask',index=0
This control returns a mask that shows which of the IEC958 status bits
can be read using the 'IEC958 Capture Default' control.
name='IEC958 Capture Default',index=0
This control returns the status bits contained within the SPDIF stream that
is being received. The 'IEC958 Capture Mask' shows which bits can be read
from this control.
name='SPDIF In Multi Frequency Acquire',index=0
name='SPDIF In Multi Frequency Acquire',index=1
name='SPDIF In Multi Frequency Acquire',index=2
name='SPDIF In Multi Frequency Acquire',index=3
This control is used to attempt acquisition of up to four different sample
rates. The active rate can be obtained by reading the 'SPDIF In Lock Frequency'
control.
When the value of this control is set to {0,0,0,0}, the rate given to hw_params
will determine the single rate the block will capture. Else, the rate given to
hw_params will be ignored, and the block will attempt capture for each of the
four sample rates set here.
If less than four rates are required, the same rate can be specified more than
once
name='SPDIF In Lock Frequency',index=0
This control returns the active capture rate, or 0 if a lock has not been
acquired
name='SPDIF In Lock TRK',index=0
This control is used to modify the locking/jitter rejection characteristics
of the block. Larger values increase the locking range, but reduce jitter
rejection.
name='SPDIF In Lock Acquire Threshold',index=0
This control is used to change the threshold at which a lock is acquired.
name='SPDIF In Lock Release Threshold',index=0
This control is used to change the threshold at which a lock is released.

View File

@ -54,12 +54,13 @@ static int s3c64xx_i2s_cfg_gpio(struct platform_device *pdev)
static struct resource s3c64xx_iis0_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_IIS0, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_I2S0_OUT),
[2] = DEFINE_RES_DMA(DMACH_I2S0_IN),
};
static struct s3c_audio_pdata i2sv3_pdata = {
static struct s3c_audio_pdata i2s0_pdata = {
.cfg_gpio = s3c64xx_i2s_cfg_gpio,
.dma_filter = pl08x_filter_id,
.dma_playback = DMACH_I2S0_OUT,
.dma_capture = DMACH_I2S0_IN,
};
struct platform_device s3c64xx_device_iis0 = {
@ -68,15 +69,20 @@ struct platform_device s3c64xx_device_iis0 = {
.num_resources = ARRAY_SIZE(s3c64xx_iis0_resource),
.resource = s3c64xx_iis0_resource,
.dev = {
.platform_data = &i2sv3_pdata,
.platform_data = &i2s0_pdata,
},
};
EXPORT_SYMBOL(s3c64xx_device_iis0);
static struct resource s3c64xx_iis1_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_IIS1, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_I2S1_OUT),
[2] = DEFINE_RES_DMA(DMACH_I2S1_IN),
};
static struct s3c_audio_pdata i2s1_pdata = {
.cfg_gpio = s3c64xx_i2s_cfg_gpio,
.dma_filter = pl08x_filter_id,
.dma_playback = DMACH_I2S1_OUT,
.dma_capture = DMACH_I2S1_IN,
};
struct platform_device s3c64xx_device_iis1 = {
@ -85,19 +91,20 @@ struct platform_device s3c64xx_device_iis1 = {
.num_resources = ARRAY_SIZE(s3c64xx_iis1_resource),
.resource = s3c64xx_iis1_resource,
.dev = {
.platform_data = &i2sv3_pdata,
.platform_data = &i2s1_pdata,
},
};
EXPORT_SYMBOL(s3c64xx_device_iis1);
static struct resource s3c64xx_iisv4_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_IISV4, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_HSI_I2SV40_TX),
[2] = DEFINE_RES_DMA(DMACH_HSI_I2SV40_RX),
};
static struct s3c_audio_pdata i2sv4_pdata = {
.cfg_gpio = s3c64xx_i2s_cfg_gpio,
.dma_filter = pl08x_filter_id,
.dma_playback = DMACH_HSI_I2SV40_TX,
.dma_capture = DMACH_HSI_I2SV40_RX,
.type = {
.i2s = {
.quirks = QUIRK_PRI_6CHAN,
@ -142,12 +149,13 @@ static int s3c64xx_pcm_cfg_gpio(struct platform_device *pdev)
static struct resource s3c64xx_pcm0_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_PCM0, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_PCM0_TX),
[2] = DEFINE_RES_DMA(DMACH_PCM0_RX),
};
static struct s3c_audio_pdata s3c_pcm0_pdata = {
.cfg_gpio = s3c64xx_pcm_cfg_gpio,
.dma_filter = pl08x_filter_id,
.dma_capture = DMACH_PCM0_RX,
.dma_playback = DMACH_PCM0_TX,
};
struct platform_device s3c64xx_device_pcm0 = {
@ -163,12 +171,13 @@ EXPORT_SYMBOL(s3c64xx_device_pcm0);
static struct resource s3c64xx_pcm1_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_PCM1, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_PCM1_TX),
[2] = DEFINE_RES_DMA(DMACH_PCM1_RX),
};
static struct s3c_audio_pdata s3c_pcm1_pdata = {
.cfg_gpio = s3c64xx_pcm_cfg_gpio,
.dma_filter = pl08x_filter_id,
.dma_playback = DMACH_PCM1_TX,
.dma_capture = DMACH_PCM1_RX,
};
struct platform_device s3c64xx_device_pcm1 = {
@ -196,13 +205,15 @@ static int s3c64xx_ac97_cfg_gpe(struct platform_device *pdev)
static struct resource s3c64xx_ac97_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_AC97, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_AC97_PCMOUT),
[2] = DEFINE_RES_DMA(DMACH_AC97_PCMIN),
[3] = DEFINE_RES_DMA(DMACH_AC97_MICIN),
[4] = DEFINE_RES_IRQ(IRQ_AC97),
[1] = DEFINE_RES_IRQ(IRQ_AC97),
};
static struct s3c_audio_pdata s3c_ac97_pdata;
static struct s3c_audio_pdata s3c_ac97_pdata = {
.dma_playback = DMACH_AC97_PCMOUT,
.dma_filter = pl08x_filter_id,
.dma_capture = DMACH_AC97_PCMIN,
.dma_capture_mic = DMACH_AC97_MICIN,
};
static u64 s3c64xx_ac97_dmamask = DMA_BIT_MASK(32);

View File

@ -14,38 +14,38 @@
#define S3C64XX_DMA_CHAN(name) ((unsigned long)(name))
/* DMA0/SDMA0 */
#define DMACH_UART0 S3C64XX_DMA_CHAN("uart0_tx")
#define DMACH_UART0_SRC2 S3C64XX_DMA_CHAN("uart0_rx")
#define DMACH_UART1 S3C64XX_DMA_CHAN("uart1_tx")
#define DMACH_UART1_SRC2 S3C64XX_DMA_CHAN("uart1_rx")
#define DMACH_UART2 S3C64XX_DMA_CHAN("uart2_tx")
#define DMACH_UART2_SRC2 S3C64XX_DMA_CHAN("uart2_rx")
#define DMACH_UART3 S3C64XX_DMA_CHAN("uart3_tx")
#define DMACH_UART3_SRC2 S3C64XX_DMA_CHAN("uart3_rx")
#define DMACH_PCM0_TX S3C64XX_DMA_CHAN("pcm0_tx")
#define DMACH_PCM0_RX S3C64XX_DMA_CHAN("pcm0_rx")
#define DMACH_I2S0_OUT S3C64XX_DMA_CHAN("i2s0_tx")
#define DMACH_I2S0_IN S3C64XX_DMA_CHAN("i2s0_rx")
#define DMACH_UART0 "uart0_tx"
#define DMACH_UART0_SRC2 "uart0_rx"
#define DMACH_UART1 "uart1_tx"
#define DMACH_UART1_SRC2 "uart1_rx"
#define DMACH_UART2 "uart2_tx"
#define DMACH_UART2_SRC2 "uart2_rx"
#define DMACH_UART3 "uart3_tx"
#define DMACH_UART3_SRC2 "uart3_rx"
#define DMACH_PCM0_TX "pcm0_tx"
#define DMACH_PCM0_RX "pcm0_rx"
#define DMACH_I2S0_OUT "i2s0_tx"
#define DMACH_I2S0_IN "i2s0_rx"
#define DMACH_SPI0_TX S3C64XX_DMA_CHAN("spi0_tx")
#define DMACH_SPI0_RX S3C64XX_DMA_CHAN("spi0_rx")
#define DMACH_HSI_I2SV40_TX S3C64XX_DMA_CHAN("i2s2_tx")
#define DMACH_HSI_I2SV40_RX S3C64XX_DMA_CHAN("i2s2_rx")
#define DMACH_HSI_I2SV40_TX "i2s2_tx"
#define DMACH_HSI_I2SV40_RX "i2s2_rx"
/* DMA1/SDMA1 */
#define DMACH_PCM1_TX S3C64XX_DMA_CHAN("pcm1_tx")
#define DMACH_PCM1_RX S3C64XX_DMA_CHAN("pcm1_rx")
#define DMACH_I2S1_OUT S3C64XX_DMA_CHAN("i2s1_tx")
#define DMACH_I2S1_IN S3C64XX_DMA_CHAN("i2s1_rx")
#define DMACH_PCM1_TX "pcm1_tx"
#define DMACH_PCM1_RX "pcm1_rx"
#define DMACH_I2S1_OUT "i2s1_tx"
#define DMACH_I2S1_IN "i2s1_rx"
#define DMACH_SPI1_TX S3C64XX_DMA_CHAN("spi1_tx")
#define DMACH_SPI1_RX S3C64XX_DMA_CHAN("spi1_rx")
#define DMACH_AC97_PCMOUT S3C64XX_DMA_CHAN("ac97_out")
#define DMACH_AC97_PCMIN S3C64XX_DMA_CHAN("ac97_in")
#define DMACH_AC97_MICIN S3C64XX_DMA_CHAN("ac97_mic")
#define DMACH_PWM S3C64XX_DMA_CHAN("pwm")
#define DMACH_IRDA S3C64XX_DMA_CHAN("irda")
#define DMACH_EXTERNAL S3C64XX_DMA_CHAN("external")
#define DMACH_SECURITY_RX S3C64XX_DMA_CHAN("sec_rx")
#define DMACH_SECURITY_TX S3C64XX_DMA_CHAN("sec_tx")
#define DMACH_AC97_PCMOUT "ac97_out"
#define DMACH_AC97_PCMIN "ac97_in"
#define DMACH_AC97_MICIN "ac97_mic"
#define DMACH_PWM "pwm"
#define DMACH_IRDA "irda"
#define DMACH_EXTERNAL "external"
#define DMACH_SECURITY_RX "sec_rx"
#define DMACH_SECURITY_TX "sec_tx"
enum dma_ch {
DMACH_MAX = 32

View File

@ -65,6 +65,7 @@
#include <linux/platform_data/usb-ohci-s3c2410.h>
#include <plat/usb-phy.h>
#include <plat/regs-spi.h>
#include <linux/platform_data/asoc-s3c.h>
#include <linux/platform_data/spi-s3c64xx.h>
static u64 samsung_device_dma_mask = DMA_BIT_MASK(32);
@ -74,9 +75,15 @@ static u64 samsung_device_dma_mask = DMA_BIT_MASK(32);
static struct resource s3c_ac97_resource[] = {
[0] = DEFINE_RES_MEM(S3C2440_PA_AC97, S3C2440_SZ_AC97),
[1] = DEFINE_RES_IRQ(IRQ_S3C244X_AC97),
[2] = DEFINE_RES_DMA_NAMED(DMACH_PCM_OUT, "PCM out"),
[3] = DEFINE_RES_DMA_NAMED(DMACH_PCM_IN, "PCM in"),
[4] = DEFINE_RES_DMA_NAMED(DMACH_MIC_IN, "Mic in"),
};
static struct s3c_audio_pdata s3c_ac97_pdata = {
#ifdef CONFIG_S3C24XX_DMAC
.dma_filter = s3c24xx_dma_filter,
#endif
.dma_playback = (void *)DMACH_PCM_OUT,
.dma_capture = (void *)DMACH_PCM_IN,
.dma_capture_mic = (void *)DMACH_MIC_IN,
};
struct platform_device s3c_device_ac97 = {
@ -87,6 +94,7 @@ struct platform_device s3c_device_ac97 = {
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &s3c_ac97_pdata,
}
};
#endif /* CONFIG_CPU_S3C2440 */
@ -566,6 +574,14 @@ static struct resource s3c_iis_resource[] = {
[0] = DEFINE_RES_MEM(S3C24XX_PA_IIS, S3C24XX_SZ_IIS),
};
static struct s3c_audio_pdata s3c_iis_platdata = {
#ifdef CONFIG_S3C24XX_DMAC
.dma_filter = s3c24xx_dma_filter,
#endif
.dma_playback = (void *)DMACH_I2S_OUT,
.dma_capture = (void *)DMACH_I2S_IN,
};
struct platform_device s3c_device_iis = {
.name = "s3c24xx-iis",
.id = -1,
@ -574,6 +590,7 @@ struct platform_device s3c_device_iis = {
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &s3c_iis_platdata,
}
};
#endif /* CONFIG_PLAT_S3C24XX */

View File

@ -432,7 +432,7 @@ config STE_DMA40
Support for ST-Ericsson DMA40 controller
config S3C24XX_DMAC
tristate "Samsung S3C24XX DMA support"
bool "Samsung S3C24XX DMA support"
depends on ARCH_S3C24XX
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS

View File

@ -13,6 +13,9 @@
*/
#define S3C64XX_AC97_GPD 0
#define S3C64XX_AC97_GPE 1
#include <linux/dmaengine.h>
extern void s3c64xx_ac97_setup_gpio(int);
struct samsung_i2s {
@ -39,6 +42,11 @@ struct samsung_i2s {
*/
struct s3c_audio_pdata {
int (*cfg_gpio)(struct platform_device *);
dma_filter_fn dma_filter;
void *dma_playback;
void *dma_capture;
void *dma_play_sec;
void *dma_capture_mic;
union {
struct samsung_i2s i2s;
} type;

View File

@ -417,11 +417,13 @@
#define AC97_RATES_MIC_ADC 4
#define AC97_RATES_SPDIF 5
#define AC97_NUM_GPIOS 16
/*
*
*/
struct snd_ac97;
struct snd_ac97_gpio_priv;
struct snd_pcm_chmap;
struct snd_ac97_build_ops {
@ -529,6 +531,7 @@ struct snd_ac97 {
struct delayed_work power_work;
#endif
struct device dev;
struct snd_ac97_gpio_priv *gpio_priv;
struct snd_pcm_chmap *chmaps[2]; /* channel-maps (optional) */
};

109
include/sound/da7218.h Normal file
View File

@ -0,0 +1,109 @@
/*
* da7218.h - DA7218 ASoC Codec Driver Platform Data
*
* Copyright (c) 2015 Dialog Semiconductor
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#ifndef _DA7218_PDATA_H
#define _DA7218_PDATA_H
/* Mic Bias */
enum da7218_micbias_voltage {
DA7218_MICBIAS_1_2V = -1,
DA7218_MICBIAS_1_6V,
DA7218_MICBIAS_1_8V,
DA7218_MICBIAS_2_0V,
DA7218_MICBIAS_2_2V,
DA7218_MICBIAS_2_4V,
DA7218_MICBIAS_2_6V,
DA7218_MICBIAS_2_8V,
DA7218_MICBIAS_3_0V,
};
enum da7218_mic_amp_in_sel {
DA7218_MIC_AMP_IN_SEL_DIFF = 0,
DA7218_MIC_AMP_IN_SEL_SE_P,
DA7218_MIC_AMP_IN_SEL_SE_N,
};
/* DMIC */
enum da7218_dmic_data_sel {
DA7218_DMIC_DATA_LRISE_RFALL = 0,
DA7218_DMIC_DATA_LFALL_RRISE,
};
enum da7218_dmic_samplephase {
DA7218_DMIC_SAMPLE_ON_CLKEDGE = 0,
DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE,
};
enum da7218_dmic_clk_rate {
DA7218_DMIC_CLK_3_0MHZ = 0,
DA7218_DMIC_CLK_1_5MHZ,
};
/* Headphone Detect */
enum da7218_hpldet_jack_rate {
DA7218_HPLDET_JACK_RATE_5US = 0,
DA7218_HPLDET_JACK_RATE_10US,
DA7218_HPLDET_JACK_RATE_20US,
DA7218_HPLDET_JACK_RATE_40US,
DA7218_HPLDET_JACK_RATE_80US,
DA7218_HPLDET_JACK_RATE_160US,
DA7218_HPLDET_JACK_RATE_320US,
DA7218_HPLDET_JACK_RATE_640US,
};
enum da7218_hpldet_jack_debounce {
DA7218_HPLDET_JACK_DEBOUNCE_OFF = 0,
DA7218_HPLDET_JACK_DEBOUNCE_2,
DA7218_HPLDET_JACK_DEBOUNCE_3,
DA7218_HPLDET_JACK_DEBOUNCE_4,
};
enum da7218_hpldet_jack_thr {
DA7218_HPLDET_JACK_THR_84PCT = 0,
DA7218_HPLDET_JACK_THR_88PCT,
DA7218_HPLDET_JACK_THR_92PCT,
DA7218_HPLDET_JACK_THR_96PCT,
};
struct da7218_hpldet_pdata {
enum da7218_hpldet_jack_rate jack_rate;
enum da7218_hpldet_jack_debounce jack_debounce;
enum da7218_hpldet_jack_thr jack_thr;
bool comp_inv;
bool hyst;
bool discharge;
};
struct da7218_pdata {
/* Mic */
enum da7218_micbias_voltage micbias1_lvl;
enum da7218_micbias_voltage micbias2_lvl;
enum da7218_mic_amp_in_sel mic1_amp_in_sel;
enum da7218_mic_amp_in_sel mic2_amp_in_sel;
/* DMIC */
enum da7218_dmic_data_sel dmic1_data_sel;
enum da7218_dmic_data_sel dmic2_data_sel;
enum da7218_dmic_samplephase dmic1_samplephase;
enum da7218_dmic_samplephase dmic2_samplephase;
enum da7218_dmic_clk_rate dmic1_clk_rate;
enum da7218_dmic_clk_rate dmic2_clk_rate;
/* HP Diff Supply - DA7217 only */
bool hp_diff_single_supply;
/* HP Detect - DA7218 only */
struct da7218_hpldet_pdata *hpldet_pdata;
};
#endif /* _DA7218_PDATA_H */

View File

@ -14,17 +14,10 @@
#ifndef __DA7219_PDATA_H
#define __DA7219_PDATA_H
/* LDO */
enum da7219_ldo_lvl_sel {
DA7219_LDO_LVL_SEL_1_05V = 0,
DA7219_LDO_LVL_SEL_1_10V,
DA7219_LDO_LVL_SEL_1_20V,
DA7219_LDO_LVL_SEL_1_40V,
};
/* Mic Bias */
enum da7219_micbias_voltage {
DA7219_MICBIAS_1_8V = 1,
DA7219_MICBIAS_1_6V = 0,
DA7219_MICBIAS_1_8V,
DA7219_MICBIAS_2_0V,
DA7219_MICBIAS_2_2V,
DA7219_MICBIAS_2_4V,
@ -41,9 +34,6 @@ enum da7219_mic_amp_in_sel {
struct da7219_aad_pdata;
struct da7219_pdata {
/* Internal LDO */
enum da7219_ldo_lvl_sel ldo_lvl_sel;
/* Mic */
enum da7219_micbias_voltage micbias_lvl;
enum da7219_mic_amp_in_sel mic_amp_in_sel;

View File

@ -45,6 +45,11 @@ struct i2s_platform_data {
u32 snd_fmts;
u32 snd_rates;
#define DW_I2S_QUIRK_COMP_REG_OFFSET (1 << 0)
unsigned int quirks;
unsigned int i2s_reg_comp1;
unsigned int i2s_reg_comp2;
void *play_dma_data;
void *capture_dma_data;
bool (*filter)(struct dma_chan *chan, void *slave);

49
include/sound/rt5659.h Normal file
View File

@ -0,0 +1,49 @@
/*
* linux/sound/rt5659.h -- Platform data for RT5659
*
* Copyright 2013 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_RT5659_H
#define __LINUX_SND_RT5659_H
enum rt5659_dmic1_data_pin {
RT5659_DMIC1_NULL,
RT5659_DMIC1_DATA_IN2N,
RT5659_DMIC1_DATA_GPIO5,
RT5659_DMIC1_DATA_GPIO9,
RT5659_DMIC1_DATA_GPIO11,
};
enum rt5659_dmic2_data_pin {
RT5659_DMIC2_NULL,
RT5659_DMIC2_DATA_IN2P,
RT5659_DMIC2_DATA_GPIO6,
RT5659_DMIC2_DATA_GPIO10,
RT5659_DMIC2_DATA_GPIO12,
};
enum rt5659_jd_src {
RT5659_JD_NULL,
RT5659_JD3,
};
struct rt5659_platform_data {
bool in1_diff;
bool in3_diff;
bool in4_diff;
int ldo1_en; /* GPIO for LDO1_EN */
int reset; /* GPIO for RESET */
enum rt5659_dmic1_data_pin dmic1_data_pin;
enum rt5659_dmic2_data_pin dmic2_data_pin;
enum rt5659_jd_src jd_src;
};
#endif

View File

@ -92,8 +92,10 @@ struct snd_soc_tplg_kcontrol_ops {
/* Bytes ext operations, for TLV byte controls */
struct snd_soc_tplg_bytes_ext_ops {
u32 id;
int (*get)(unsigned int __user *bytes, unsigned int size);
int (*put)(const unsigned int __user *bytes, unsigned int size);
int (*get)(struct snd_kcontrol *kcontrol, unsigned int __user *bytes,
unsigned int size);
int (*put)(struct snd_kcontrol *kcontrol,
const unsigned int __user *bytes, unsigned int size);
};
/*

View File

@ -110,6 +110,14 @@
.put = snd_soc_put_volsw, \
.private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
max, invert, 0) }
#define SOC_DOUBLE_STS(xname, reg, shift_left, shift_right, max, invert) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
.access = SNDRV_CTL_ELEM_ACCESS_READ | \
SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
.private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
max, invert, 0) }
#define SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
.info = snd_soc_info_volsw, \
@ -293,6 +301,9 @@
{.base = xbase, .num_regs = xregs, \
.mask = xmask }) }
/*
* SND_SOC_BYTES_EXT is deprecated, please USE SND_SOC_BYTES_TLV instead
*/
#define SND_SOC_BYTES_EXT(xname, xcount, xhandler_get, xhandler_put) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_bytes_info_ext, \
@ -1037,6 +1048,9 @@ struct snd_soc_dai_link {
/* pmdown_time is ignored at stop */
unsigned int ignore_pmdown_time:1;
struct list_head list; /* DAI link list of the soc card */
struct snd_soc_dobj dobj; /* For topology */
};
struct snd_soc_codec_conf {
@ -1101,11 +1115,19 @@ struct snd_soc_card {
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level);
int (*add_dai_link)(struct snd_soc_card *,
struct snd_soc_dai_link *link);
void (*remove_dai_link)(struct snd_soc_card *,
struct snd_soc_dai_link *link);
long pmdown_time;
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link;
int num_links;
struct snd_soc_dai_link *dai_link; /* predefined links only */
int num_links; /* predefined links only */
struct list_head dai_link_list; /* all links */
int num_dai_links;
struct list_head rtd_list;
int num_rtd;
@ -1228,8 +1250,10 @@ struct soc_bytes_ext {
struct snd_soc_dobj dobj;
/* used for TLV byte control */
int (*get)(unsigned int __user *bytes, unsigned int size);
int (*put)(const unsigned int __user *bytes, unsigned int size);
int (*get)(struct snd_kcontrol *kcontrol, unsigned int __user *bytes,
unsigned int size);
int (*put)(struct snd_kcontrol *kcontrol, const unsigned int __user *bytes,
unsigned int size);
};
/* multi register control */
@ -1647,6 +1671,11 @@ int snd_soc_of_get_dai_link_codecs(struct device *dev,
struct device_node *of_node,
struct snd_soc_dai_link *dai_link);
int snd_soc_add_dai_link(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link);
void snd_soc_remove_dai_link(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link);
#include <sound/soc-dai.h>
#ifdef CONFIG_DEBUG_FS

View File

@ -243,7 +243,7 @@ struct snd_soc_tplg_manifest {
__le32 control_elems; /* number of control elements */
__le32 widget_elems; /* number of widget elements */
__le32 graph_elems; /* number of graph elements */
__le32 dai_elems; /* number of DAI elements */
__le32 pcm_elems; /* number of PCM elements */
__le32 dai_link_elems; /* number of DAI link elements */
struct snd_soc_tplg_private priv;
} __attribute__((packed));

View File

@ -73,7 +73,8 @@
#define SND_AUDIOCODEC_IEC61937 ((__u32) 0x0000000B)
#define SND_AUDIOCODEC_G723_1 ((__u32) 0x0000000C)
#define SND_AUDIOCODEC_G729 ((__u32) 0x0000000D)
#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_G729
#define SND_AUDIOCODEC_BESPOKE ((__u32) 0x0000000E)
#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_BESPOKE
/*
* Profile and modes are listed with bit masks. This allows for a
@ -312,7 +313,7 @@ struct snd_enc_flac {
struct snd_enc_generic {
__u32 bw; /* encoder bandwidth */
__s32 reserved[15];
__s32 reserved[15]; /* Can be used for SND_AUDIOCODEC_BESPOKE */
} __attribute__((packed, aligned(4)));
union snd_codec_options {

View File

@ -50,6 +50,7 @@ source "sound/soc/jz4740/Kconfig"
source "sound/soc/nuc900/Kconfig"
source "sound/soc/omap/Kconfig"
source "sound/soc/kirkwood/Kconfig"
source "sound/soc/img/Kconfig"
source "sound/soc/intel/Kconfig"
source "sound/soc/mediatek/Kconfig"
source "sound/soc/mxs/Kconfig"

View File

@ -27,6 +27,7 @@ obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += dwc/
obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += jz4740/
obj-$(CONFIG_SND_SOC) += img/
obj-$(CONFIG_SND_SOC) += intel/
obj-$(CONFIG_SND_SOC) += mediatek/
obj-$(CONFIG_SND_SOC) += mxs/

View File

@ -68,4 +68,13 @@ config SND_ATMEL_SOC_CLASSD
help
Say Y if you want to add support for Atmel ASoC driver for boards using
CLASSD.
config SND_ATMEL_SOC_PDMIC
tristate "Atmel ASoC driver for boards using PDMIC"
depends on OF && (ARCH_AT91 || COMPILE_TEST)
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
Say Y if you want to add support for Atmel ASoC driver for boards using
PDMIC.
endif

View File

@ -12,8 +12,10 @@ snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
snd-atmel-soc-wm8904-objs := atmel_wm8904.o
snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
snd-atmel-soc-classd-objs := atmel-classd.o
snd-atmel-soc-pdmic-objs := atmel-pdmic.o
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o

View File

@ -106,7 +106,7 @@ static const struct snd_pcm_hardware atmel_classd_hw = {
.rates = ATMEL_CLASSD_RATES,
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 2,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 256,
@ -145,7 +145,7 @@ static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
.playback = {
.channels_min = 2,
.channels_min = 1,
.channels_max = 2,
.rates = ATMEL_CLASSD_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
@ -171,9 +171,13 @@ atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
return -EINVAL;
}
if (params_channels(params) == 1)
slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
else
slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
slave_config->direction = DMA_MEM_TO_DEV;
slave_config->dst_addr = dd->phy_base + CLASSD_THR;
slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
slave_config->dst_maxburst = 1;
slave_config->src_maxburst = 1;
slave_config->device_fc = false;
@ -486,7 +490,7 @@ static struct snd_soc_dai_driver atmel_classd_codec_dai = {
.name = ATMEL_CLASSD_CODEC_DAI_NAME,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_min = 1,
.channels_max = 2,
.rates = ATMEL_CLASSD_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@ -636,8 +640,10 @@ static int atmel_classd_probe(struct platform_device *pdev)
/* register sound card */
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
if (!card) {
ret = -ENOMEM;
goto unregister_codec;
}
snd_soc_card_set_drvdata(card, dd);
platform_set_drvdata(pdev, card);
@ -645,16 +651,20 @@ static int atmel_classd_probe(struct platform_device *pdev)
ret = atmel_classd_asoc_card_init(dev, card);
if (ret) {
dev_err(dev, "failed to init sound card\n");
return ret;
goto unregister_codec;
}
ret = devm_snd_soc_register_card(dev, card);
if (ret) {
dev_err(dev, "failed to register sound card: %d\n", ret);
return ret;
goto unregister_codec;
}
return 0;
unregister_codec:
snd_soc_unregister_codec(dev);
return ret;
}
static int atmel_classd_remove(struct platform_device *pdev)

View File

@ -0,0 +1,738 @@
/* Atmel PDMIC driver
*
* Copyright (C) 2015 Atmel
*
* Author: Songjun Wu <songjun.wu@atmel.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 or later
* as published by the Free Software Foundation.
*/
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include "atmel-pdmic.h"
struct atmel_pdmic_pdata {
u32 mic_min_freq;
u32 mic_max_freq;
s32 mic_offset;
const char *card_name;
};
struct atmel_pdmic {
dma_addr_t phy_base;
struct regmap *regmap;
struct clk *pclk;
struct clk *gclk;
int irq;
struct snd_pcm_substream *substream;
const struct atmel_pdmic_pdata *pdata;
};
static const struct of_device_id atmel_pdmic_of_match[] = {
{
.compatible = "atmel,sama5d2-pdmic",
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(of, atmel_pdmic_of_match);
#define PDMIC_OFFSET_MAX_VAL S16_MAX
#define PDMIC_OFFSET_MIN_VAL S16_MIN
static struct atmel_pdmic_pdata *atmel_pdmic_dt_init(struct device *dev)
{
struct device_node *np = dev->of_node;
struct atmel_pdmic_pdata *pdata;
if (!np) {
dev_err(dev, "device node not found\n");
return ERR_PTR(-EINVAL);
}
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
if (of_property_read_string(np, "atmel,model", &pdata->card_name))
pdata->card_name = "PDMIC";
if (of_property_read_u32(np, "atmel,mic-min-freq",
&pdata->mic_min_freq)) {
dev_err(dev, "failed to get mic-min-freq\n");
return ERR_PTR(-EINVAL);
}
if (of_property_read_u32(np, "atmel,mic-max-freq",
&pdata->mic_max_freq)) {
dev_err(dev, "failed to get mic-max-freq\n");
return ERR_PTR(-EINVAL);
}
if (pdata->mic_max_freq < pdata->mic_min_freq) {
dev_err(dev,
"mic-max-freq should not less than mic-min-freq\n");
return ERR_PTR(-EINVAL);
}
if (of_property_read_s32(np, "atmel,mic-offset", &pdata->mic_offset))
pdata->mic_offset = 0;
if (pdata->mic_offset > PDMIC_OFFSET_MAX_VAL) {
dev_warn(dev,
"mic-offset value %d is larger than the max value %d, the max value is specified\n",
pdata->mic_offset, PDMIC_OFFSET_MAX_VAL);
pdata->mic_offset = PDMIC_OFFSET_MAX_VAL;
} else if (pdata->mic_offset < PDMIC_OFFSET_MIN_VAL) {
dev_warn(dev,
"mic-offset value %d is less than the min value %d, the min value is specified\n",
pdata->mic_offset, PDMIC_OFFSET_MIN_VAL);
pdata->mic_offset = PDMIC_OFFSET_MIN_VAL;
}
return pdata;
}
/* cpu dai component */
static int atmel_pdmic_cpu_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
int ret;
ret = clk_prepare_enable(dd->gclk);
if (ret)
return ret;
ret = clk_prepare_enable(dd->pclk);
if (ret)
return ret;
/* Clear all bits in the Control Register(PDMIC_CR) */
regmap_write(dd->regmap, PDMIC_CR, 0);
dd->substream = substream;
/* Enable the overrun error interrupt */
regmap_write(dd->regmap, PDMIC_IER, PDMIC_IER_OVRE);
return 0;
}
static void atmel_pdmic_cpu_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
/* Disable the overrun error interrupt */
regmap_write(dd->regmap, PDMIC_IDR, PDMIC_IDR_OVRE);
clk_disable_unprepare(dd->gclk);
clk_disable_unprepare(dd->pclk);
}
static int atmel_pdmic_cpu_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
u32 val;
/* Clean the PDMIC Converted Data Register */
return regmap_read(dd->regmap, PDMIC_CDR, &val);
}
static const struct snd_soc_dai_ops atmel_pdmic_cpu_dai_ops = {
.startup = atmel_pdmic_cpu_dai_startup,
.shutdown = atmel_pdmic_cpu_dai_shutdown,
.prepare = atmel_pdmic_cpu_dai_prepare,
};
#define ATMEL_PDMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver atmel_pdmic_cpu_dai = {
.capture = {
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = ATMEL_PDMIC_FORMATS,},
.ops = &atmel_pdmic_cpu_dai_ops,
};
static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = {
.name = "atmel-pdmic",
};
/* platform */
#define ATMEL_PDMIC_MAX_BUF_SIZE (64 * 1024)
#define ATMEL_PDMIC_PREALLOC_BUF_SIZE ATMEL_PDMIC_MAX_BUF_SIZE
static const struct snd_pcm_hardware atmel_pdmic_hw = {
.info = SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
| SNDRV_PCM_INFO_INTERLEAVED
| SNDRV_PCM_INFO_RESUME
| SNDRV_PCM_INFO_PAUSE,
.formats = ATMEL_PDMIC_FORMATS,
.buffer_bytes_max = ATMEL_PDMIC_MAX_BUF_SIZE,
.period_bytes_min = 256,
.period_bytes_max = 32 * 1024,
.periods_min = 2,
.periods_max = 256,
};
static int
atmel_pdmic_platform_configure_dma(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct dma_slave_config *slave_config)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
int ret;
ret = snd_hwparams_to_dma_slave_config(substream, params,
slave_config);
if (ret) {
dev_err(rtd->platform->dev,
"hw params to dma slave configure failed\n");
return ret;
}
slave_config->src_addr = dd->phy_base + PDMIC_CDR;
slave_config->src_maxburst = 1;
slave_config->dst_maxburst = 1;
return 0;
}
static const struct snd_dmaengine_pcm_config
atmel_pdmic_dmaengine_pcm_config = {
.prepare_slave_config = atmel_pdmic_platform_configure_dma,
.pcm_hardware = &atmel_pdmic_hw,
.prealloc_buffer_size = ATMEL_PDMIC_PREALLOC_BUF_SIZE,
};
/* codec */
/* Mic Gain = dgain * 2^(-scale) */
struct mic_gain {
unsigned int dgain;
unsigned int scale;
};
/* range from -90 dB to 90 dB */
static const struct mic_gain mic_gain_table[] = {
{ 1, 15}, { 1, 14}, /* -90, -84 dB */
{ 3, 15}, { 1, 13}, { 3, 14}, { 1, 12}, /* -81, -78, -75, -72 dB */
{ 5, 14}, { 13, 15}, /* -70, -68 dB */
{ 9, 14}, { 21, 15}, { 23, 15}, { 13, 14}, /* -65 ~ -62 dB */
{ 29, 15}, { 33, 15}, { 37, 15}, { 41, 15}, /* -61 ~ -58 dB */
{ 23, 14}, { 13, 13}, { 58, 15}, { 65, 15}, /* -57 ~ -54 dB */
{ 73, 15}, { 41, 14}, { 23, 13}, { 13, 12}, /* -53 ~ -50 dB */
{ 29, 13}, { 65, 14}, { 73, 14}, { 41, 13}, /* -49 ~ -46 dB */
{ 23, 12}, { 207, 15}, { 29, 12}, { 65, 13}, /* -45 ~ -42 dB */
{ 73, 13}, { 41, 12}, { 23, 11}, { 413, 15}, /* -41 ~ -38 dB */
{ 463, 15}, { 519, 15}, { 583, 15}, { 327, 14}, /* -37 ~ -34 dB */
{ 367, 14}, { 823, 15}, { 231, 13}, { 1036, 15}, /* -33 ~ -30 dB */
{ 1163, 15}, { 1305, 15}, { 183, 12}, { 1642, 15}, /* -29 ~ -26 dB */
{ 1843, 15}, { 2068, 15}, { 145, 11}, { 2603, 15}, /* -25 ~ -22 dB */
{ 365, 12}, { 3277, 15}, { 3677, 15}, { 4125, 15}, /* -21 ~ -18 dB */
{ 4629, 15}, { 5193, 15}, { 5827, 15}, { 3269, 14}, /* -17 ~ -14 dB */
{ 917, 12}, { 8231, 15}, { 9235, 15}, { 5181, 14}, /* -13 ~ -10 dB */
{11627, 15}, {13045, 15}, {14637, 15}, {16423, 15}, /* -9 ~ -6 dB */
{18427, 15}, {20675, 15}, { 5799, 13}, {26029, 15}, /* -5 ~ -2 dB */
{ 7301, 13}, { 1, 0}, {18383, 14}, {10313, 13}, /* -1 ~ 2 dB */
{23143, 14}, {25967, 14}, {29135, 14}, {16345, 13}, /* 3 ~ 6 dB */
{ 4585, 11}, {20577, 13}, { 1443, 9}, {25905, 13}, /* 7 ~ 10 dB */
{14533, 12}, { 8153, 11}, { 2287, 9}, {20529, 12}, /* 11 ~ 14 dB */
{11517, 11}, { 6461, 10}, {28997, 12}, { 4067, 9}, /* 15 ~ 18 dB */
{18253, 11}, { 10, 0}, {22979, 11}, {25783, 11}, /* 19 ~ 22 dB */
{28929, 11}, {32459, 11}, { 9105, 9}, {20431, 10}, /* 23 ~ 26 dB */
{22925, 10}, {12861, 9}, { 7215, 8}, {16191, 9}, /* 27 ~ 30 dB */
{ 9083, 8}, {20383, 9}, {11435, 8}, { 6145, 7}, /* 31 ~ 34 dB */
{ 3599, 6}, {32305, 9}, {18123, 8}, {20335, 8}, /* 35 ~ 38 dB */
{ 713, 3}, { 100, 0}, { 7181, 6}, { 8057, 6}, /* 39 ~ 42 dB */
{ 565, 2}, {20287, 7}, {11381, 6}, {25539, 7}, /* 43 ~ 46 dB */
{ 1791, 3}, { 4019, 4}, { 9019, 5}, {20239, 6}, /* 47 ~ 50 dB */
{ 5677, 4}, {25479, 6}, { 7147, 4}, { 8019, 4}, /* 51 ~ 54 dB */
{17995, 5}, {20191, 5}, {11327, 4}, {12709, 4}, /* 55 ~ 58 dB */
{ 3565, 2}, { 1000, 0}, { 1122, 0}, { 1259, 0}, /* 59 ~ 62 dB */
{ 2825, 1}, {12679, 3}, { 7113, 2}, { 7981, 2}, /* 63 ~ 66 dB */
{ 8955, 2}, {20095, 3}, {22547, 3}, {12649, 2}, /* 67 ~ 70 dB */
{28385, 3}, { 3981, 0}, {17867, 2}, {20047, 2}, /* 71 ~ 74 dB */
{11247, 1}, {12619, 1}, {14159, 1}, {31773, 2}, /* 75 ~ 78 dB */
{17825, 1}, {10000, 0}, {11220, 0}, {12589, 0}, /* 79 ~ 82 dB */
{28251, 1}, {15849, 0}, {17783, 0}, {19953, 0}, /* 83 ~ 86 dB */
{22387, 0}, {25119, 0}, {28184, 0}, {31623, 0}, /* 87 ~ 90 dB */
};
static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
0, 1, TLV_DB_SCALE_ITEM(-9000, 600, 0),
2, 5, TLV_DB_SCALE_ITEM(-8100, 300, 0),
6, 7, TLV_DB_SCALE_ITEM(-7000, 200, 0),
8, ARRAY_SIZE(mic_gain_table)-1, TLV_DB_SCALE_ITEM(-6500, 100, 0),
);
int pdmic_get_mic_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
unsigned int dgain_val, scale_val;
int i;
dgain_val = (snd_soc_read(codec, PDMIC_DSPR1) & PDMIC_DSPR1_DGAIN_MASK)
>> PDMIC_DSPR1_DGAIN_SHIFT;
scale_val = (snd_soc_read(codec, PDMIC_DSPR0) & PDMIC_DSPR0_SCALE_MASK)
>> PDMIC_DSPR0_SCALE_SHIFT;
for (i = 0; i < ARRAY_SIZE(mic_gain_table); i++) {
if ((mic_gain_table[i].dgain == dgain_val) &&
(mic_gain_table[i].scale == scale_val))
ucontrol->value.integer.value[0] = i;
}
return 0;
}
static int pdmic_put_mic_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
int max = mc->max;
unsigned int val;
int ret;
val = ucontrol->value.integer.value[0];
if (val > max)
return -EINVAL;
ret = snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_MASK,
mic_gain_table[val].dgain << PDMIC_DSPR1_DGAIN_SHIFT);
if (ret < 0)
return ret;
ret = snd_soc_update_bits(codec, PDMIC_DSPR0, PDMIC_DSPR0_SCALE_MASK,
mic_gain_table[val].scale << PDMIC_DSPR0_SCALE_SHIFT);
if (ret < 0)
return ret;
return 0;
}
static const struct snd_kcontrol_new atmel_pdmic_snd_controls[] = {
SOC_SINGLE_EXT_TLV("Mic Capture Volume", PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_SHIFT,
ARRAY_SIZE(mic_gain_table)-1, 0,
pdmic_get_mic_volsw, pdmic_put_mic_volsw, mic_gain_tlv),
SOC_SINGLE("High Pass Filter Switch", PDMIC_DSPR0,
PDMIC_DSPR0_HPFBYP_SHIFT, 1, 1),
SOC_SINGLE("SINCC Filter Switch", PDMIC_DSPR0, PDMIC_DSPR0_SINBYP_SHIFT, 1, 1),
};
static int atmel_pdmic_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_OFFSET_MASK,
(u32)(dd->pdata->mic_offset << PDMIC_DSPR1_OFFSET_SHIFT));
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_pdmic = {
.probe = atmel_pdmic_codec_probe,
.controls = atmel_pdmic_snd_controls,
.num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls),
};
/* codec dai component */
#define PDMIC_MR_PRESCAL_MAX_VAL 127
static int
atmel_pdmic_codec_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *codec_dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_codec *codec = codec_dai->codec;
unsigned int rate_min = substream->runtime->hw.rate_min;
unsigned int rate_max = substream->runtime->hw.rate_max;
int fs = params_rate(params);
int bits = params_width(params);
unsigned long pclk_rate, gclk_rate;
unsigned int f_pdmic;
u32 mr_val, dspr0_val, pclk_prescal, gclk_prescal;
if (params_channels(params) != 1) {
dev_err(codec->dev,
"only supports one channel\n");
return -EINVAL;
}
if ((fs < rate_min) || (fs > rate_max)) {
dev_err(codec->dev,
"sample rate is %dHz, min rate is %dHz, max rate is %dHz\n",
fs, rate_min, rate_max);
return -EINVAL;
}
switch (bits) {
case 16:
dspr0_val = (PDMIC_DSPR0_SIZE_16_BITS
<< PDMIC_DSPR0_SIZE_SHIFT);
break;
case 32:
dspr0_val = (PDMIC_DSPR0_SIZE_32_BITS
<< PDMIC_DSPR0_SIZE_SHIFT);
break;
default:
return -EINVAL;
}
if ((fs << 7) > (rate_max << 6)) {
f_pdmic = fs << 6;
dspr0_val |= PDMIC_DSPR0_OSR_64 << PDMIC_DSPR0_OSR_SHIFT;
} else {
f_pdmic = fs << 7;
dspr0_val |= PDMIC_DSPR0_OSR_128 << PDMIC_DSPR0_OSR_SHIFT;
}
pclk_rate = clk_get_rate(dd->pclk);
gclk_rate = clk_get_rate(dd->gclk);
/* PRESCAL = SELCK/(2*f_pdmic) - 1*/
pclk_prescal = (u32)(pclk_rate/(f_pdmic << 1)) - 1;
gclk_prescal = (u32)(gclk_rate/(f_pdmic << 1)) - 1;
if ((pclk_prescal > PDMIC_MR_PRESCAL_MAX_VAL) ||
(gclk_rate/((gclk_prescal + 1) << 1) <
pclk_rate/((pclk_prescal + 1) << 1))) {
mr_val = gclk_prescal << PDMIC_MR_PRESCAL_SHIFT;
mr_val |= PDMIC_MR_CLKS_GCK << PDMIC_MR_CLKS_SHIFT;
} else {
mr_val = pclk_prescal << PDMIC_MR_PRESCAL_SHIFT;
mr_val |= PDMIC_MR_CLKS_PCK << PDMIC_MR_CLKS_SHIFT;
}
snd_soc_update_bits(codec, PDMIC_MR,
PDMIC_MR_PRESCAL_MASK | PDMIC_MR_CLKS_MASK, mr_val);
snd_soc_update_bits(codec, PDMIC_DSPR0,
PDMIC_DSPR0_OSR_MASK | PDMIC_DSPR0_SIZE_MASK, dspr0_val);
return 0;
}
static int atmel_pdmic_codec_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai)
{
struct snd_soc_codec *codec = codec_dai->codec;
snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK,
PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT);
return 0;
}
static int atmel_pdmic_codec_dai_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *codec_dai)
{
struct snd_soc_codec *codec = codec_dai->codec;
u32 val;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
val = PDMIC_CR_ENPDM_EN << PDMIC_CR_ENPDM_SHIFT;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
val = PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT;
break;
default:
return -EINVAL;
}
snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK, val);
return 0;
}
static const struct snd_soc_dai_ops atmel_pdmic_codec_dai_ops = {
.hw_params = atmel_pdmic_codec_dai_hw_params,
.prepare = atmel_pdmic_codec_dai_prepare,
.trigger = atmel_pdmic_codec_dai_trigger,
};
#define ATMEL_PDMIC_CODEC_DAI_NAME "atmel-pdmic-hifi"
static struct snd_soc_dai_driver atmel_pdmic_codec_dai = {
.name = ATMEL_PDMIC_CODEC_DAI_NAME,
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = ATMEL_PDMIC_FORMATS,
},
.ops = &atmel_pdmic_codec_dai_ops,
};
/* ASoC sound card */
static int atmel_pdmic_asoc_card_init(struct device *dev,
struct snd_soc_card *card)
{
struct snd_soc_dai_link *dai_link;
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
if (!dai_link)
return -ENOMEM;
dai_link->name = "PDMIC";
dai_link->stream_name = "PDMIC PCM";
dai_link->codec_dai_name = ATMEL_PDMIC_CODEC_DAI_NAME;
dai_link->cpu_dai_name = dev_name(dev);
dai_link->codec_name = dev_name(dev);
dai_link->platform_name = dev_name(dev);
card->dai_link = dai_link;
card->num_links = 1;
card->name = dd->pdata->card_name;
card->dev = dev;
return 0;
}
static void atmel_pdmic_get_sample_rate(struct atmel_pdmic *dd,
unsigned int *rate_min, unsigned int *rate_max)
{
u32 mic_min_freq = dd->pdata->mic_min_freq;
u32 mic_max_freq = dd->pdata->mic_max_freq;
u32 clk_max_rate = (u32)(clk_get_rate(dd->pclk) >> 1);
u32 clk_min_rate = (u32)(clk_get_rate(dd->gclk) >> 8);
if (mic_max_freq > clk_max_rate)
mic_max_freq = clk_max_rate;
if (mic_min_freq < clk_min_rate)
mic_min_freq = clk_min_rate;
*rate_min = DIV_ROUND_CLOSEST(mic_min_freq, 128);
*rate_max = mic_max_freq >> 6;
}
/* PDMIC interrupt handler */
static irqreturn_t atmel_pdmic_interrupt(int irq, void *dev_id)
{
struct atmel_pdmic *dd = (struct atmel_pdmic *)dev_id;
u32 pdmic_isr;
irqreturn_t ret = IRQ_NONE;
regmap_read(dd->regmap, PDMIC_ISR, &pdmic_isr);
if (pdmic_isr & PDMIC_ISR_OVRE) {
regmap_update_bits(dd->regmap, PDMIC_CR, PDMIC_CR_ENPDM_MASK,
PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT);
snd_pcm_stop_xrun(dd->substream);
ret = IRQ_HANDLED;
}
return ret;
}
/* regmap configuration */
#define ATMEL_PDMIC_REG_MAX 0x124
static const struct regmap_config atmel_pdmic_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = ATMEL_PDMIC_REG_MAX,
};
static int atmel_pdmic_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct atmel_pdmic *dd;
struct resource *res;
void __iomem *io_base;
const struct atmel_pdmic_pdata *pdata;
struct snd_soc_card *card;
unsigned int rate_min, rate_max;
int ret;
pdata = atmel_pdmic_dt_init(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
if (!dd)
return -ENOMEM;
dd->pdata = pdata;
dd->irq = platform_get_irq(pdev, 0);
if (dd->irq < 0) {
ret = dd->irq;
dev_err(dev, "failed to could not get irq: %d\n", ret);
return ret;
}
dd->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(dd->pclk)) {
ret = PTR_ERR(dd->pclk);
dev_err(dev, "failed to get peripheral clock: %d\n", ret);
return ret;
}
dd->gclk = devm_clk_get(dev, "gclk");
if (IS_ERR(dd->gclk)) {
ret = PTR_ERR(dd->gclk);
dev_err(dev, "failed to get GCK: %d\n", ret);
return ret;
}
/* The gclk clock frequency must always be tree times
* lower than the pclk clock frequency
*/
ret = clk_set_rate(dd->gclk, clk_get_rate(dd->pclk)/3);
if (ret) {
dev_err(dev, "failed to set GCK clock rate: %d\n", ret);
return ret;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "no memory resource\n");
return -ENXIO;
}
io_base = devm_ioremap_resource(dev, res);
if (IS_ERR(io_base)) {
ret = PTR_ERR(io_base);
dev_err(dev, "failed to remap register memory: %d\n", ret);
return ret;
}
dd->phy_base = res->start;
dd->regmap = devm_regmap_init_mmio(dev, io_base,
&atmel_pdmic_regmap_config);
if (IS_ERR(dd->regmap)) {
ret = PTR_ERR(dd->regmap);
dev_err(dev, "failed to init register map: %d\n", ret);
return ret;
}
ret = devm_request_irq(dev, dd->irq, atmel_pdmic_interrupt, 0,
"PDMIC", (void *)dd);
if (ret < 0) {
dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
dd->irq, ret);
return ret;
}
/* Get the minimal and maximal sample rate that micphone supports */
atmel_pdmic_get_sample_rate(dd, &rate_min, &rate_max);
/* register cpu dai */
atmel_pdmic_cpu_dai.capture.rate_min = rate_min;
atmel_pdmic_cpu_dai.capture.rate_max = rate_max;
ret = devm_snd_soc_register_component(dev,
&atmel_pdmic_cpu_dai_component,
&atmel_pdmic_cpu_dai, 1);
if (ret) {
dev_err(dev, "could not register CPU DAI: %d\n", ret);
return ret;
}
/* register platform */
ret = devm_snd_dmaengine_pcm_register(dev,
&atmel_pdmic_dmaengine_pcm_config,
0);
if (ret) {
dev_err(dev, "could not register platform: %d\n", ret);
return ret;
}
/* register codec and codec dai */
atmel_pdmic_codec_dai.capture.rate_min = rate_min;
atmel_pdmic_codec_dai.capture.rate_max = rate_max;
ret = snd_soc_register_codec(dev, &soc_codec_dev_pdmic,
&atmel_pdmic_codec_dai, 1);
if (ret) {
dev_err(dev, "could not register codec: %d\n", ret);
return ret;
}
/* register sound card */
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card) {
ret = -ENOMEM;
goto unregister_codec;
}
snd_soc_card_set_drvdata(card, dd);
platform_set_drvdata(pdev, card);
ret = atmel_pdmic_asoc_card_init(dev, card);
if (ret) {
dev_err(dev, "failed to init sound card: %d\n", ret);
goto unregister_codec;
}
ret = devm_snd_soc_register_card(dev, card);
if (ret) {
dev_err(dev, "failed to register sound card: %d\n", ret);
goto unregister_codec;
}
return 0;
unregister_codec:
snd_soc_unregister_codec(dev);
return ret;
}
static int atmel_pdmic_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver atmel_pdmic_driver = {
.driver = {
.name = "atmel-pdmic",
.of_match_table = of_match_ptr(atmel_pdmic_of_match),
.pm = &snd_soc_pm_ops,
},
.probe = atmel_pdmic_probe,
.remove = atmel_pdmic_remove,
};
module_platform_driver(atmel_pdmic_driver);
MODULE_DESCRIPTION("Atmel PDMIC driver under ALSA SoC architecture");
MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,80 @@
#ifndef __ATMEL_PDMIC_H_
#define __ATMEL_PDMIC_H_
#include <linux/bitops.h>
#define PDMIC_CR 0x00000000
#define PDMIC_CR_SWRST 0x1
#define PDMIC_CR_SWRST_MASK BIT(0)
#define PDMIC_CR_SWRST_SHIFT (0)
#define PDMIC_CR_ENPDM_DIS 0x0
#define PDMIC_CR_ENPDM_EN 0x1
#define PDMIC_CR_ENPDM_MASK BIT(4)
#define PDMIC_CR_ENPDM_SHIFT (4)
#define PDMIC_MR 0x00000004
#define PDMIC_MR_CLKS_PCK 0x0
#define PDMIC_MR_CLKS_GCK 0x1
#define PDMIC_MR_CLKS_MASK BIT(4)
#define PDMIC_MR_CLKS_SHIFT (4)
#define PDMIC_MR_PRESCAL_MASK GENMASK(14, 8)
#define PDMIC_MR_PRESCAL_SHIFT (8)
#define PDMIC_CDR 0x00000014
#define PDMIC_IER 0x00000018
#define PDMIC_IER_OVRE BIT(25)
#define PDMIC_IDR 0x0000001c
#define PDMIC_IDR_OVRE BIT(25)
#define PDMIC_IMR 0x00000020
#define PDMIC_ISR 0x00000024
#define PDMIC_ISR_OVRE BIT(25)
#define PDMIC_DSPR0 0x00000058
#define PDMIC_DSPR0_HPFBYP_DIS 0x1
#define PDMIC_DSPR0_HPFBYP_EN 0x0
#define PDMIC_DSPR0_HPFBYP_MASK BIT(1)
#define PDMIC_DSPR0_HPFBYP_SHIFT (1)
#define PDMIC_DSPR0_SINBYP_DIS 0x1
#define PDMIC_DSPR0_SINBYP_EN 0x0
#define PDMIC_DSPR0_SINBYP_MASK BIT(2)
#define PDMIC_DSPR0_SINBYP_SHIFT (2)
#define PDMIC_DSPR0_SIZE_16_BITS 0x0
#define PDMIC_DSPR0_SIZE_32_BITS 0x1
#define PDMIC_DSPR0_SIZE_MASK BIT(3)
#define PDMIC_DSPR0_SIZE_SHIFT (3)
#define PDMIC_DSPR0_OSR_128 0x0
#define PDMIC_DSPR0_OSR_64 0x1
#define PDMIC_DSPR0_OSR_MASK GENMASK(6, 4)
#define PDMIC_DSPR0_OSR_SHIFT (4)
#define PDMIC_DSPR0_SCALE_MASK GENMASK(11, 8)
#define PDMIC_DSPR0_SCALE_SHIFT (8)
#define PDMIC_DSPR0_SHIFT_MASK GENMASK(15, 12)
#define PDMIC_DSPR0_SHIFT_SHIFT (12)
#define PDMIC_DSPR1 0x0000005c
#define PDMIC_DSPR1_DGAIN_MASK GENMASK(14, 0)
#define PDMIC_DSPR1_DGAIN_SHIFT (0)
#define PDMIC_DSPR1_OFFSET_MASK GENMASK(31, 16)
#define PDMIC_DSPR1_OFFSET_SHIFT (16)
#define PDMIC_WPMR 0x000000e4
#define PDMIC_WPSR 0x000000e8
#endif

View File

@ -183,6 +183,7 @@ static struct platform_driver atmel_asoc_wm8904_driver = {
.driver = {
.name = "atmel-wm8904-audio",
.of_match_table = of_match_ptr(atmel_asoc_wm8904_dt_ids),
.pm = &snd_soc_pm_ops,
},
.probe = atmel_asoc_wm8904_probe,
.remove = atmel_asoc_wm8904_remove,

View File

@ -55,9 +55,11 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS4271_SPI if SPI_MASTER
select SND_SOC_CS42XX8_I2C if I2C
select SND_SOC_CS4349 if I2C
select SND_SOC_CS47L24 if MFD_CS47L24
select SND_SOC_CX20442 if TTY
select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
select SND_SOC_DA7213 if I2C
select SND_SOC_DA7218 if I2C
select SND_SOC_DA7219 if I2C
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
@ -68,6 +70,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_GTM601
select SND_SOC_HDAC_HDMI
select SND_SOC_ICS43432
select SND_SOC_INNO_RK3036
select SND_SOC_ISABELLE if I2C
select SND_SOC_JZ4740_CODEC
select SND_SOC_LM4857 if I2C
@ -86,14 +89,18 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM1792A if SPI_MASTER
select SND_SOC_PCM3008
select SND_SOC_PCM3168A_I2C if I2C
select SND_SOC_PCM3168A_SPI if SPI_MASTER
select SND_SOC_PCM512x_I2C if I2C
select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT286 if I2C
select SND_SOC_RT298 if I2C
select SND_SOC_RT5616 if I2C
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
select SND_SOC_RT5645 if I2C
select SND_SOC_RT5651 if I2C
select SND_SOC_RT5659 if I2C
select SND_SOC_RT5670 if I2C
select SND_SOC_RT5677 if I2C && SPI_MASTER
select SND_SOC_SGTL5000 if I2C
@ -196,10 +203,12 @@ config SND_SOC_88PM860X
config SND_SOC_ARIZONA
tristate
default y if SND_SOC_CS47L24=y
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
default y if SND_SOC_WM8997=y
default y if SND_SOC_WM8998=y
default m if SND_SOC_CS47L24=m
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
default m if SND_SOC_WM8997=m
@ -212,9 +221,12 @@ config SND_SOC_WM_HUBS
config SND_SOC_WM_ADSP
tristate
select SND_SOC_COMPRESS
default y if SND_SOC_CS47L24=y
default y if SND_SOC_WM5102=y
default y if SND_SOC_WM5110=y
default y if SND_SOC_WM2200=y
default m if SND_SOC_CS47L24=m
default m if SND_SOC_WM5102=m
default m if SND_SOC_WM5110=m
default m if SND_SOC_WM2200=m
@ -423,6 +435,9 @@ config SND_SOC_CS4349
tristate "Cirrus Logic CS4349 CODEC"
depends on I2C
config SND_SOC_CS47L24
tristate
config SND_SOC_CX20442
tristate
depends on TTY
@ -440,6 +455,9 @@ config SND_SOC_DA7210
config SND_SOC_DA7213
tristate
config SND_SOC_DA7218
tristate
config SND_SOC_DA7219
tristate
@ -477,6 +495,9 @@ config SND_SOC_HDAC_HDMI
config SND_SOC_ICS43432
tristate
config SND_SOC_INNO_RK3036
tristate "Inno codec driver for RK3036 SoC"
config SND_SOC_ISABELLE
tristate
@ -512,6 +533,21 @@ config SND_SOC_PCM1792A
config SND_SOC_PCM3008
tristate
config SND_SOC_PCM3168A
tristate
config SND_SOC_PCM3168A_I2C
tristate "Texas Instruments PCM3168A CODEC - I2C"
depends on I2C
select SND_SOC_PCM3168A
select REGMAP_I2C
config SND_SOC_PCM3168A_SPI
tristate "Texas Instruments PCM3168A CODEC - SPI"
depends on SPI_MASTER
select SND_SOC_PCM3168A
select REGMAP_SPI
config SND_SOC_PCM512x
tristate
@ -529,14 +565,18 @@ config SND_SOC_PCM512x_SPI
config SND_SOC_RL6231
tristate
default y if SND_SOC_RT5616=y
default y if SND_SOC_RT5640=y
default y if SND_SOC_RT5645=y
default y if SND_SOC_RT5651=y
default y if SND_SOC_RT5659=y
default y if SND_SOC_RT5670=y
default y if SND_SOC_RT5677=y
default m if SND_SOC_RT5616=m
default m if SND_SOC_RT5640=m
default m if SND_SOC_RT5645=m
default m if SND_SOC_RT5651=m
default m if SND_SOC_RT5659=m
default m if SND_SOC_RT5670=m
default m if SND_SOC_RT5677=m
@ -555,6 +595,9 @@ config SND_SOC_RT298
tristate
depends on I2C
config SND_SOC_RT5616
tristate
config SND_SOC_RT5631
tristate "Realtek ALC5631/RT5631 CODEC"
depends on I2C
@ -568,6 +611,9 @@ config SND_SOC_RT5645
config SND_SOC_RT5651
tristate
config SND_SOC_RT5659
tristate
config SND_SOC_RT5670
tristate
@ -844,7 +890,8 @@ config SND_SOC_WM8971
tristate
config SND_SOC_WM8974
tristate
tristate "Wolfson Microelectronics WM8974 codec"
depends on I2C
config SND_SOC_WM8978
tristate "Wolfson Microelectronics WM8978 codec"
@ -897,6 +944,7 @@ config SND_SOC_WM9712
config SND_SOC_WM9713
tristate
select REGMAP_AC97
# Amp
config SND_SOC_LM4857

View File

@ -47,9 +47,11 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o
snd-soc-cs42xx8-objs := cs42xx8.o
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
snd-soc-cs4349-objs := cs4349.o
snd-soc-cs47l24-objs := cs47l24.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
snd-soc-da7218-objs := da7218.o
snd-soc-da7219-objs := da7219.o da7219-aad.o
snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
@ -61,6 +63,7 @@ snd-soc-es8328-spi-objs := es8328-spi.o
snd-soc-gtm601-objs := gtm601.o
snd-soc-hdac-hdmi-objs := hdac_hdmi.o
snd-soc-ics43432-objs := ics43432.o
snd-soc-inno-rk3036-objs := inno_rk3036.o
snd-soc-isabelle-objs := isabelle.o
snd-soc-jz4740-codec-objs := jz4740.o
snd-soc-l3-objs := l3.o
@ -79,6 +82,9 @@ snd-soc-nau8825-objs := nau8825.o
snd-soc-pcm1681-objs := pcm1681.o
snd-soc-pcm1792a-codec-objs := pcm1792a.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-pcm3168a-objs := pcm3168a.o
snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
@ -86,10 +92,12 @@ snd-soc-rl6231-objs := rl6231.o
snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt286-objs := rt286.o
snd-soc-rt298-objs := rt298.o
snd-soc-rt5616-objs := rt5616.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
snd-soc-rt5645-objs := rt5645.o
snd-soc-rt5651-objs := rt5651.o
snd-soc-rt5659-objs := rt5659.o
snd-soc-rt5670-objs := rt5670.o
snd-soc-rt5677-objs := rt5677.o
snd-soc-rt5677-spi-objs := rt5677-spi.o
@ -243,9 +251,11 @@ obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o
obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
@ -257,6 +267,7 @@ obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
@ -275,6 +286,9 @@ obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
@ -282,10 +296,12 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o
obj-$(CONFIG_SND_SOC_RT5659) += snd-soc-rt5659.o
obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o
obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o

View File

@ -70,18 +70,11 @@
#define FMT_MASK (0xf8)
/* CTRL2 */
#define DFS_MASK (3 << 2)
#define DFS_NORMAL_SPEED (0 << 2)
#define DFS_DOUBLE_SPEED (1 << 2)
#define DFS_QUAD_SPEED (2 << 2)
struct ak4613_priv {
struct mutex lock;
unsigned int fmt;
u8 fmt_ctrl;
int cnt;
};
struct ak4613_formats {
unsigned int width;
unsigned int fmt;
@ -92,6 +85,16 @@ struct ak4613_interface {
struct ak4613_formats playback;
};
struct ak4613_priv {
struct mutex lock;
const struct ak4613_interface *iface;
unsigned int fmt;
u8 oc;
u8 ic;
int cnt;
};
/*
* Playback Volume
*
@ -126,7 +129,7 @@ static const struct reg_default ak4613_reg[] = {
{ 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
};
#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3)
#define AUDIO_IFACE_TO_VAL(fmts) ((fmts - ak4613_iface) << 3)
#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
static const struct ak4613_interface ak4613_iface[] = {
/* capture */ /* playback */
@ -240,7 +243,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
priv->cnt = 0;
}
if (!priv->cnt)
priv->fmt_ctrl = NO_FMT;
priv->iface = NULL;
mutex_unlock(&priv->lock);
}
@ -265,13 +268,35 @@ static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface,
int is_play,
unsigned int fmt, unsigned int width)
{
const struct ak4613_formats *fmts;
fmts = (is_play) ? &iface->playback : &iface->capture;
if (fmts->fmt != fmt)
return false;
if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
if (fmts->width != width)
return false;
} else {
if (fmts->width < width)
return false;
}
return true;
}
static int ak4613_dai_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 ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
const struct ak4613_formats *fmts;
const struct ak4613_interface *iface;
struct device *dev = codec->dev;
unsigned int width = params_width(params);
unsigned int fmt = priv->fmt;
@ -305,33 +330,27 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
* It doesn't support TDM at this point
*/
fmt_ctrl = NO_FMT;
for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) {
fmts = (is_play) ? &ak4613_iface[i].playback :
&ak4613_iface[i].capture;
if (fmts->fmt != fmt)
continue;
if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
if (fmts->width != width)
continue;
} else {
if (fmts->width < width)
continue;
}
fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i);
break;
}
ret = -EINVAL;
if (fmt_ctrl == NO_FMT)
goto hw_params_end;
iface = NULL;
mutex_lock(&priv->lock);
if ((priv->fmt_ctrl == NO_FMT) ||
(priv->fmt_ctrl == fmt_ctrl)) {
priv->fmt_ctrl = fmt_ctrl;
if (priv->iface) {
if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width))
iface = priv->iface;
} else {
for (i = ARRAY_SIZE(ak4613_iface); i >= 0; i--) {
if (!ak4613_dai_fmt_matching(ak4613_iface + i,
is_play,
fmt, width))
continue;
iface = ak4613_iface + i;
break;
}
}
if ((priv->iface == NULL) ||
(priv->iface == iface)) {
priv->iface = iface;
priv->cnt++;
ret = 0;
}
@ -340,8 +359,13 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
goto hw_params_end;
fmt_ctrl = AUDIO_IFACE_TO_VAL(iface);
snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
snd_soc_write(codec, CTRL2, ctrl2);
snd_soc_update_bits(codec, CTRL2, DFS_MASK, ctrl2);
snd_soc_write(codec, ICTRL, priv->ic);
snd_soc_write(codec, OCTRL, priv->oc);
hw_params_end:
if (ret < 0)
@ -431,6 +455,28 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
.num_dapm_routes = ARRAY_SIZE(ak4613_intercon),
};
static void ak4613_parse_of(struct ak4613_priv *priv,
struct device *dev)
{
struct device_node *np = dev->of_node;
char prop[32];
int i;
/* Input 1 - 2 */
for (i = 0; i < 2; i++) {
snprintf(prop, sizeof(prop), "asahi-kasei,in%d-single-end", i + 1);
if (!of_get_property(np, prop, NULL))
priv->ic |= 1 << i;
}
/* Output 1 - 6 */
for (i = 0; i < 6; i++) {
snprintf(prop, sizeof(prop), "asahi-kasei,out%d-single-end", i + 1);
if (!of_get_property(np, prop, NULL))
priv->oc |= 1 << i;
}
}
static int ak4613_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@ -458,7 +504,9 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
if (!priv)
return -ENOMEM;
priv->fmt_ctrl = NO_FMT;
ak4613_parse_of(priv, dev);
priv->iface = NULL;
priv->cnt = 0;
mutex_init(&priv->lock);

View File

@ -310,7 +310,7 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_gpio);
const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None",
"Tone Generator 1",
"Tone Generator 2",
@ -418,7 +418,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
};
EXPORT_SYMBOL_GPL(arizona_mixer_texts);
int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
0x00, /* None */
0x04, /* Tone */
0x05,
@ -555,12 +555,12 @@ const char *arizona_sample_rate_val_to_name(unsigned int rate_val)
}
EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name);
const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
"SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate",
};
EXPORT_SYMBOL_GPL(arizona_rate_text);
const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
0, 1, 2, 8,
};
EXPORT_SYMBOL_GPL(arizona_rate_val);
@ -702,6 +702,100 @@ const struct soc_enum arizona_in_dmic_osr[] = {
};
EXPORT_SYMBOL_GPL(arizona_in_dmic_osr);
static const char * const arizona_anc_input_src_text[] = {
"None", "IN1", "IN2", "IN3", "IN4",
};
static const char * const arizona_anc_channel_src_text[] = {
"None", "Left", "Right", "Combine",
};
const struct soc_enum arizona_anc_input_src[] = {
SOC_ENUM_SINGLE(ARIZONA_ANC_SRC,
ARIZONA_IN_RXANCL_SEL_SHIFT,
ARRAY_SIZE(arizona_anc_input_src_text),
arizona_anc_input_src_text),
SOC_ENUM_SINGLE(ARIZONA_FCL_ADC_REFORMATTER_CONTROL,
ARIZONA_FCL_MIC_MODE_SEL,
ARRAY_SIZE(arizona_anc_channel_src_text),
arizona_anc_channel_src_text),
SOC_ENUM_SINGLE(ARIZONA_ANC_SRC,
ARIZONA_IN_RXANCR_SEL_SHIFT,
ARRAY_SIZE(arizona_anc_input_src_text),
arizona_anc_input_src_text),
SOC_ENUM_SINGLE(ARIZONA_FCR_ADC_REFORMATTER_CONTROL,
ARIZONA_FCR_MIC_MODE_SEL,
ARRAY_SIZE(arizona_anc_channel_src_text),
arizona_anc_channel_src_text),
};
EXPORT_SYMBOL_GPL(arizona_anc_input_src);
static const char * const arizona_anc_ng_texts[] = {
"None",
"Internal",
"External",
};
SOC_ENUM_SINGLE_DECL(arizona_anc_ng_enum, SND_SOC_NOPM, 0,
arizona_anc_ng_texts);
EXPORT_SYMBOL_GPL(arizona_anc_ng_enum);
static const char * const arizona_output_anc_src_text[] = {
"None", "RXANCL", "RXANCR",
};
const struct soc_enum arizona_output_anc_src[] = {
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
ARIZONA_OUT1L_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1R,
ARIZONA_OUT1R_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L,
ARIZONA_OUT2L_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2R,
ARIZONA_OUT2R_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
ARIZONA_OUT3L_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
SOC_ENUM_SINGLE(ARIZONA_DAC_VOLUME_LIMIT_3R,
ARIZONA_OUT3R_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4L,
ARIZONA_OUT4L_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4R,
ARIZONA_OUT4R_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5L,
ARIZONA_OUT5L_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5R,
ARIZONA_OUT5R_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6L,
ARIZONA_OUT6L_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6R,
ARIZONA_OUT6R_ANC_SRC_SHIFT,
ARRAY_SIZE(arizona_output_anc_src_text),
arizona_output_anc_src_text),
};
EXPORT_SYMBOL_GPL(arizona_output_anc_src);
static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
@ -1023,6 +1117,31 @@ void arizona_init_dvfs(struct arizona_priv *priv)
}
EXPORT_SYMBOL_GPL(arizona_init_dvfs);
int arizona_anc_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
unsigned int mask = 0x3 << w->shift;
unsigned int val;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
val = 1 << w->shift;
break;
case SND_SOC_DAPM_PRE_PMD:
val = 1 << (w->shift + 1);
break;
default:
return 0;
}
snd_soc_update_bits(codec, ARIZONA_CLOCK_CONTROL, mask, val);
return 0;
}
EXPORT_SYMBOL_GPL(arizona_anc_ev);
static unsigned int arizona_opclk_ref_48k_rates[] = {
6144000,
12288000,
@ -1095,7 +1214,7 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
unsigned int reg;
unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK;
unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT;
unsigned int *clk;
int *clk;
switch (clk_id) {
case ARIZONA_CLK_SYSCLK:
@ -1901,18 +2020,18 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
}
switch (fll->arizona->type) {
case WM5102:
case WM8997:
return init_ratio;
case WM5110:
case WM8280:
if (fll->arizona->rev < 3 || sync)
return init_ratio;
break;
case WM8998:
case WM1814:
default:
if (sync)
return init_ratio;
break;
default:
return init_ratio;
}
cfg->fratio = init_ratio - 1;
@ -2093,9 +2212,9 @@ static int arizona_enable_fll(struct arizona_fll *fll)
/* Facilitate smooth refclk across the transition */
regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
ARIZONA_FLL1_GAIN_MASK, 0);
regmap_update_bits_async(fll->arizona->regmap, fll->base + 1,
ARIZONA_FLL1_FREERUN,
ARIZONA_FLL1_FREERUN);
regmap_update_bits(fll->arizona->regmap, fll->base + 1,
ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
udelay(32);
}
/*

View File

@ -57,7 +57,7 @@
#define ARIZONA_CLK_98MHZ 5
#define ARIZONA_CLK_147MHZ 6
#define ARIZONA_MAX_DAI 6
#define ARIZONA_MAX_DAI 8
#define ARIZONA_MAX_ADSP 4
#define ARIZONA_DVFS_SR1_RQ 0x001
@ -96,8 +96,8 @@ struct arizona_priv {
#define ARIZONA_NUM_MIXER_INPUTS 104
extern const unsigned int arizona_mixer_tlv[];
extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
extern const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
#define ARIZONA_GAINMUX_CONTROLS(name, base) \
SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \
@ -216,8 +216,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
#define ARIZONA_RATE_ENUM_SIZE 4
#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
extern const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
extern const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
@ -242,6 +242,10 @@ extern const struct soc_enum arizona_in_dmic_osr[];
extern const struct snd_kcontrol_new arizona_adsp2_rate_controls[];
extern const struct soc_enum arizona_anc_input_src[];
extern const struct soc_enum arizona_anc_ng_enum;
extern const struct soc_enum arizona_output_anc_src[];
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
@ -251,6 +255,9 @@ extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
extern int arizona_anc_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);

1148
sound/soc/codecs/cs47l24.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
/*
* cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24
*
* Copyright 2015 Cirrus Logic Inc.
*
* Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _CS47L24_H
#define _CS47L24_H
#include "arizona.h"
#define CS47L24_FLL1 1
#define CS47L24_FLL2 2
#define CS47L24_FLL1_REFCLK 3
#define CS47L24_FLL2_REFCLK 4
#endif

3314
sound/soc/codecs/da7218.c Normal file

File diff suppressed because it is too large Load Diff

1414
sound/soc/codecs/da7218.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -968,10 +968,11 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = {
{"Mixin PGA", NULL, "Mic PGA"},
{"ADC", NULL, "Mixin PGA"},
{"Sidetone Filter", NULL, "ADC"},
{"Mixer In", NULL, "Mixer In Supply"},
{"Mixer In", "Mic Switch", "ADC"},
{"Sidetone Filter", NULL, "Mixer In"},
{"Tone Generator", NULL, "TONE"},
DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"),
@ -1073,11 +1074,8 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
u32 freq_ref;
u64 frac_div;
/* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */
if (da7219->mclk_rate == 32768) {
indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
} else if (da7219->mclk_rate < 2000000) {
/* Verify 2MHz - 54MHz MCLK provided, and set input divider */
if (da7219->mclk_rate < 2000000) {
dev_err(codec->dev, "PLL input clock %d below valid range\n",
da7219->mclk_rate);
return -EINVAL;
@ -1118,9 +1116,6 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
case DA7219_SYSCLK_PLL_SRM:
pll_ctrl |= DA7219_PLL_MODE_SRM;
break;
case DA7219_SYSCLK_PLL_32KHZ:
pll_ctrl |= DA7219_PLL_MODE_32KHZ;
break;
default:
dev_err(codec->dev, "Invalid PLL config\n");
return -EINVAL;
@ -1306,7 +1301,7 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
}
channels = params_channels(params);
if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) {
if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
dev_err(codec->dev,
"Invalid number of channels, only 1 to %d supported\n",
DA7219_DAI_CH_NUM_MAX);
@ -1405,28 +1400,12 @@ static const struct of_device_id da7219_of_match[] = {
};
MODULE_DEVICE_TABLE(of, da7219_of_match);
static enum da7219_ldo_lvl_sel da7219_of_ldo_lvl(struct snd_soc_codec *codec,
u32 val)
{
switch (val) {
case 1050:
return DA7219_LDO_LVL_SEL_1_05V;
case 1100:
return DA7219_LDO_LVL_SEL_1_10V;
case 1200:
return DA7219_LDO_LVL_SEL_1_20V;
case 1400:
return DA7219_LDO_LVL_SEL_1_40V;
default:
dev_warn(codec->dev, "Invalid LDO level");
return DA7219_LDO_LVL_SEL_1_05V;
}
}
static enum da7219_micbias_voltage
da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
{
switch (val) {
case 1600:
return DA7219_MICBIAS_1_6V;
case 1800:
return DA7219_MICBIAS_1_8V;
case 2000:
@ -1469,9 +1448,6 @@ static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
if (!pdata)
return NULL;
if (of_property_read_u32(np, "dlg,ldo-lvl", &of_val32) >= 0)
pdata->ldo_lvl_sel = da7219_of_ldo_lvl(codec, of_val32);
if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0)
pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32);
else
@ -1516,24 +1492,13 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, DA7219_REFERENCES,
DA7219_BIAS_EN_MASK,
DA7219_BIAS_EN_MASK);
/* Enable Internal Digital LDO */
snd_soc_update_bits(codec, DA7219_LDO_CTRL,
DA7219_LDO_EN_MASK,
DA7219_LDO_EN_MASK);
}
break;
case SND_SOC_BIAS_OFF:
/* Only disable if jack detection not active */
if (!da7219->aad->jack) {
/* Bypass Internal Digital LDO */
snd_soc_update_bits(codec, DA7219_LDO_CTRL,
DA7219_LDO_EN_MASK, 0);
/* Master bias */
/* Only disable master bias if jack detection not active */
if (!da7219->aad->jack)
snd_soc_update_bits(codec, DA7219_REFERENCES,
DA7219_BIAS_EN_MASK, 0);
}
/* MCLK */
if (da7219->mclk)
@ -1600,21 +1565,9 @@ static void da7219_handle_pdata(struct snd_soc_codec *codec)
if (pdata) {
u8 micbias_lvl = 0;
/* Internal LDO */
switch (pdata->ldo_lvl_sel) {
case DA7219_LDO_LVL_SEL_1_05V:
case DA7219_LDO_LVL_SEL_1_10V:
case DA7219_LDO_LVL_SEL_1_20V:
case DA7219_LDO_LVL_SEL_1_40V:
snd_soc_update_bits(codec, DA7219_LDO_CTRL,
DA7219_LDO_LEVEL_SELECT_MASK,
(pdata->ldo_lvl_sel <<
DA7219_LDO_LEVEL_SELECT_SHIFT));
break;
}
/* Mic Bias voltages */
switch (pdata->micbias_lvl) {
case DA7219_MICBIAS_1_6V:
case DA7219_MICBIAS_1_8V:
case DA7219_MICBIAS_2_0V:
case DA7219_MICBIAS_2_2V:
@ -1662,10 +1615,12 @@ static int da7219_probe(struct snd_soc_codec *codec)
/* Check if MCLK provided */
da7219->mclk = devm_clk_get(codec->dev, "mclk");
if (IS_ERR(da7219->mclk)) {
if (PTR_ERR(da7219->mclk) != -ENOENT)
return PTR_ERR(da7219->mclk);
else
if (PTR_ERR(da7219->mclk) != -ENOENT) {
ret = PTR_ERR(da7219->mclk);
goto err_disable_reg;
} else {
da7219->mclk = NULL;
}
}
/* Default PC counter to free-running */
@ -1693,7 +1648,16 @@ static int da7219_probe(struct snd_soc_codec *codec)
snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
/* Initialise AAD block */
return da7219_aad_init(codec);
ret = da7219_aad_init(codec);
if (ret)
goto err_disable_reg;
return 0;
err_disable_reg:
regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
return ret;
}
static int da7219_remove(struct snd_soc_codec *codec)
@ -1776,7 +1740,7 @@ static struct reg_default da7219_reg_defaults[] = {
{ DA7219_DIG_ROUTING_DAC, 0x32 },
{ DA7219_DAI_OFFSET_LOWER, 0x00 },
{ DA7219_DAI_OFFSET_UPPER, 0x00 },
{ DA7219_REFERENCES, 0x00 },
{ DA7219_REFERENCES, 0x08 },
{ DA7219_MIXIN_L_SELECT, 0x00 },
{ DA7219_MIXIN_L_GAIN, 0x03 },
{ DA7219_ADC_L_GAIN, 0x6F },
@ -1811,7 +1775,6 @@ static struct reg_default da7219_reg_defaults[] = {
{ DA7219_CHIP_ID1, 0x23 },
{ DA7219_CHIP_ID2, 0x93 },
{ DA7219_CHIP_REVISION, 0x00 },
{ DA7219_LDO_CTRL, 0x00 },
{ DA7219_IO_CTRL, 0x00 },
{ DA7219_GAIN_RAMP_CTRL, 0x00 },
{ DA7219_PC_COUNT, 0x02 },

View File

@ -85,7 +85,6 @@
#define DA7219_CHIP_ID1 0x81
#define DA7219_CHIP_ID2 0x82
#define DA7219_CHIP_REVISION 0x83
#define DA7219_LDO_CTRL 0x90
#define DA7219_IO_CTRL 0x91
#define DA7219_GAIN_RAMP_CTRL 0x92
#define DA7219_PC_COUNT 0x94
@ -207,7 +206,6 @@
#define DA7219_PLL_MODE_BYPASS (0x0 << 6)
#define DA7219_PLL_MODE_NORMAL (0x1 << 6)
#define DA7219_PLL_MODE_SRM (0x2 << 6)
#define DA7219_PLL_MODE_32KHZ (0x3 << 6)
/* DA7219_PLL_FRAC_TOP = 0x22 */
#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0
@ -569,12 +567,6 @@
#define DA7219_CHIP_MAJOR_SHIFT 4
#define DA7219_CHIP_MAJOR_MASK (0xF << 4)
/* DA7219_LDO_CTRL = 0x90 */
#define DA7219_LDO_LEVEL_SELECT_SHIFT 4
#define DA7219_LDO_LEVEL_SELECT_MASK (0x3 << 4)
#define DA7219_LDO_EN_SHIFT 7
#define DA7219_LDO_EN_MASK (0x1 << 7)
/* DA7219_IO_CTRL = 0x91 */
#define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0
#define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0)
@ -787,7 +779,6 @@ enum da7219_sys_clk {
DA7219_SYSCLK_MCLK = 0,
DA7219_SYSCLK_PLL,
DA7219_SYSCLK_PLL_SRM,
DA7219_SYSCLK_PLL_32KHZ
};
/* Regulators */

View File

@ -0,0 +1,490 @@
/*
* Driver of Inno codec for rk3036 by Rockchip Inc.
*
* Author: Rockchip Inc.
* Author: Zheng ShunQian<zhengsq@rock-chips.com>
*/
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/soc-dapm.h>
#include <sound/soc-dai.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/device.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/io.h>
#include "inno_rk3036.h"
struct rk3036_codec_priv {
void __iomem *base;
struct clk *pclk;
struct regmap *regmap;
struct device *dev;
};
static const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0);
static int rk3036_codec_antipop_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int val, ret, regval;
ret = snd_soc_component_read(component, INNO_R09, &regval);
if (ret)
return ret;
val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) &
INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
ucontrol->value.integer.value[0] = val;
val = ((regval >> INNO_R09_HPR_ANITPOP_SHIFT) &
INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
ucontrol->value.integer.value[1] = val;
return 0;
}
static int rk3036_codec_antipop_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int val, ret, regmsk;
val = (ucontrol->value.integer.value[0] ?
INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
INNO_R09_HPL_ANITPOP_SHIFT;
val |= (ucontrol->value.integer.value[1] ?
INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
INNO_R09_HPR_ANITPOP_SHIFT;
regmsk = INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPL_ANITPOP_SHIFT |
INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPR_ANITPOP_SHIFT;
ret = snd_soc_component_update_bits(component, INNO_R09,
regmsk, val);
if (ret < 0)
return ret;
return 0;
}
#define SOC_RK3036_CODEC_ANTIPOP_DECL(xname) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = rk3036_codec_antipop_info, .get = rk3036_codec_antipop_get, \
.put = rk3036_codec_antipop_put, }
static const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = {
SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08,
INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB,
INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv),
SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT,
INNO_R06_VOUTR_CZ_SHIFT, 1, 0),
SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT,
INNO_R09_HPR_MUTE_SHIFT, 1, 0),
SOC_RK3036_CODEC_ANTIPOP_DECL("Anti-pop Switch"),
};
static const struct snd_kcontrol_new rk3036_codec_hpl_mixer_controls[] = {
SOC_DAPM_SINGLE("DAC Left Out Switch", INNO_R09,
INNO_R09_DACL_SWITCH_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new rk3036_codec_hpr_mixer_controls[] = {
SOC_DAPM_SINGLE("DAC Right Out Switch", INNO_R09,
INNO_R09_DACR_SWITCH_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new rk3036_codec_hpl_switch_controls[] = {
SOC_DAPM_SINGLE("HP Left Out Switch", INNO_R05,
INNO_R05_HPL_WORK_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new rk3036_codec_hpr_switch_controls[] = {
SOC_DAPM_SINGLE("HP Right Out Switch", INNO_R05,
INNO_R05_HPR_WORK_SHIFT, 1, 0),
};
static const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("DAC PWR", 1, INNO_R06,
INNO_R06_DAC_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACL VREF", 2, INNO_R04,
INNO_R04_DACL_VREF_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACR VREF", 2, INNO_R04,
INNO_R04_DACR_VREF_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 3, INNO_R06,
INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 3, INNO_R06,
INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACR CLK", 3, INNO_R04,
INNO_R04_DACR_CLK_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACL CLK", 3, INNO_R04,
INNO_R04_DACL_CLK_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_DAC("DACL", "Left Playback", INNO_R04,
INNO_R04_DACL_SW_SHIFT, 0),
SND_SOC_DAPM_DAC("DACR", "Right Playback", INNO_R04,
INNO_R04_DACR_SW_SHIFT, 0),
SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
rk3036_codec_hpl_mixer_controls,
ARRAY_SIZE(rk3036_codec_hpl_mixer_controls)),
SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
rk3036_codec_hpr_mixer_controls,
ARRAY_SIZE(rk3036_codec_hpr_mixer_controls)),
SND_SOC_DAPM_PGA("HP Left Out", INNO_R05,
INNO_R05_HPL_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("HP Right Out", INNO_R05,
INNO_R05_HPR_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_MIXER("HP Left Switch", SND_SOC_NOPM, 0, 0,
rk3036_codec_hpl_switch_controls,
ARRAY_SIZE(rk3036_codec_hpl_switch_controls)),
SND_SOC_DAPM_MIXER("HP Right Switch", SND_SOC_NOPM, 0, 0,
rk3036_codec_hpr_switch_controls,
ARRAY_SIZE(rk3036_codec_hpr_switch_controls)),
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
};
static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {
{"DACL VREF", NULL, "DAC PWR"},
{"DACR VREF", NULL, "DAC PWR"},
{"DACL HiLo VREF", NULL, "DAC PWR"},
{"DACR HiLo VREF", NULL, "DAC PWR"},
{"DACL CLK", NULL, "DAC PWR"},
{"DACR CLK", NULL, "DAC PWR"},
{"DACL", NULL, "DACL VREF"},
{"DACL", NULL, "DACL HiLo VREF"},
{"DACL", NULL, "DACL CLK"},
{"DACR", NULL, "DACR VREF"},
{"DACR", NULL, "DACR HiLo VREF"},
{"DACR", NULL, "DACR CLK"},
{"Left Headphone Mixer", "DAC Left Out Switch", "DACL"},
{"Right Headphone Mixer", "DAC Right Out Switch", "DACR"},
{"HP Left Out", NULL, "Left Headphone Mixer"},
{"HP Right Out", NULL, "Right Headphone Mixer"},
{"HP Left Switch", "HP Left Out Switch", "HP Left Out"},
{"HP Right Switch", "HP Right Out Switch", "HP Right Out"},
{"HPL", NULL, "HP Left Switch"},
{"HPR", NULL, "HP Right Switch"},
};
static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
unsigned int reg01_val = 0, reg02_val = 0, reg03_val = 0;
dev_dbg(codec->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
reg01_val |= INNO_R01_PINDIR_IN_SLAVE |
INNO_R01_I2SMODE_SLAVE;
break;
case SND_SOC_DAIFMT_CBM_CFM:
reg01_val |= INNO_R01_PINDIR_OUT_MASTER |
INNO_R01_I2SMODE_MASTER;
break;
default:
dev_err(codec->dev, "invalid fmt\n");
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
reg02_val |= INNO_R02_DACM_PCM;
break;
case SND_SOC_DAIFMT_I2S:
reg02_val |= INNO_R02_DACM_I2S;
break;
case SND_SOC_DAIFMT_RIGHT_J:
reg02_val |= INNO_R02_DACM_RJM;
break;
case SND_SOC_DAIFMT_LEFT_J:
reg02_val |= INNO_R02_DACM_LJM;
break;
default:
dev_err(codec->dev, "set dai format failed\n");
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
reg02_val |= INNO_R02_LRCP_NORMAL;
reg03_val |= INNO_R03_BCP_NORMAL;
break;
case SND_SOC_DAIFMT_IB_IF:
reg02_val |= INNO_R02_LRCP_REVERSAL;
reg03_val |= INNO_R03_BCP_REVERSAL;
break;
case SND_SOC_DAIFMT_IB_NF:
reg02_val |= INNO_R02_LRCP_REVERSAL;
reg03_val |= INNO_R03_BCP_NORMAL;
break;
case SND_SOC_DAIFMT_NB_IF:
reg02_val |= INNO_R02_LRCP_NORMAL;
reg03_val |= INNO_R03_BCP_REVERSAL;
break;
default:
dev_err(codec->dev, "set dai format failed\n");
return -EINVAL;
}
snd_soc_update_bits(codec, INNO_R01, INNO_R01_I2SMODE_MSK |
INNO_R01_PINDIR_MSK, reg01_val);
snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
INNO_R02_DACM_MSK, reg02_val);
snd_soc_update_bits(codec, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
return 0;
}
static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
unsigned int reg02_val = 0, reg03_val = 0;
switch (params_format(hw_params)) {
case SNDRV_PCM_FORMAT_S16_LE:
reg02_val |= INNO_R02_VWL_16BIT;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
reg02_val |= INNO_R02_VWL_20BIT;
break;
case SNDRV_PCM_FORMAT_S24_LE:
reg02_val |= INNO_R02_VWL_24BIT;
break;
case SNDRV_PCM_FORMAT_S32_LE:
reg02_val |= INNO_R02_VWL_32BIT;
break;
default:
return -EINVAL;
}
reg02_val |= INNO_R02_LRCP_NORMAL;
reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;
snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
INNO_R02_VWL_MSK, reg02_val);
snd_soc_update_bits(codec, INNO_R03, INNO_R03_DACR_MSK |
INNO_R03_FWL_MSK, reg03_val);
return 0;
}
#define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000 | \
SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_96000)
#define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops rk3036_codec_dai_ops = {
.set_fmt = rk3036_codec_dai_set_fmt,
.hw_params = rk3036_codec_dai_hw_params,
};
static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
{
.name = "rk3036-codec-dai",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = RK3036_CODEC_RATES,
.formats = RK3036_CODEC_FMTS,
},
.ops = &rk3036_codec_dai_ops,
.symmetric_rates = 1,
},
};
static void rk3036_codec_reset(struct snd_soc_codec *codec)
{
snd_soc_write(codec, INNO_R00,
INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);
snd_soc_write(codec, INNO_R00,
INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);
}
static int rk3036_codec_probe(struct snd_soc_codec *codec)
{
rk3036_codec_reset(codec);
return 0;
}
static int rk3036_codec_remove(struct snd_soc_codec *codec)
{
rk3036_codec_reset(codec);
return 0;
}
static int rk3036_codec_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_STANDBY:
/* set a big current for capacitor charging. */
snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
/* start precharge */
snd_soc_write(codec, INNO_R06, INNO_R06_DAC_PRECHARGE);
break;
case SND_SOC_BIAS_OFF:
/* set a big current for capacitor discharging. */
snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
/* start discharge. */
snd_soc_write(codec, INNO_R06, INNO_R06_DAC_DISCHARGE);
break;
default:
break;
}
return 0;
}
static struct snd_soc_codec_driver rk3036_codec_driver = {
.probe = rk3036_codec_probe,
.remove = rk3036_codec_remove,
.set_bias_level = rk3036_codec_set_bias_level,
.controls = rk3036_codec_dapm_controls,
.num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls),
.dapm_routes = rk3036_codec_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes),
.dapm_widgets = rk3036_codec_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets),
};
static const struct regmap_config rk3036_codec_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
};
#define GRF_SOC_CON0 0x00140
#define GRF_ACODEC_SEL (BIT(10) | BIT(16 + 10))
static int rk3036_codec_platform_probe(struct platform_device *pdev)
{
struct rk3036_codec_priv *priv;
struct device_node *of_node = pdev->dev.of_node;
struct resource *res;
void __iomem *base;
struct regmap *grf;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
priv->base = base;
priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
&rk3036_codec_regmap_config);
if (IS_ERR(priv->regmap)) {
dev_err(&pdev->dev, "init regmap failed\n");
return PTR_ERR(priv->regmap);
}
grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf");
if (IS_ERR(grf)) {
dev_err(&pdev->dev, "needs 'rockchip,grf' property\n");
return PTR_ERR(grf);
}
ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL);
if (ret) {
dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret);
return ret;
}
priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk");
if (IS_ERR(priv->pclk))
return PTR_ERR(priv->pclk);
ret = clk_prepare_enable(priv->pclk);
if (ret < 0) {
dev_err(&pdev->dev, "failed to enable clk\n");
return ret;
}
priv->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, priv);
ret = snd_soc_register_codec(&pdev->dev, &rk3036_codec_driver,
rk3036_codec_dai_driver,
ARRAY_SIZE(rk3036_codec_dai_driver));
if (ret) {
clk_disable_unprepare(priv->pclk);
dev_set_drvdata(&pdev->dev, NULL);
}
return ret;
}
static int rk3036_codec_platform_remove(struct platform_device *pdev)
{
struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev);
clk_disable_unprepare(priv->pclk);
return 0;
}
static const struct of_device_id rk3036_codec_of_match[] = {
{ .compatible = "rockchip,rk3036-codec", },
{}
};
MODULE_DEVICE_TABLE(of, rk3036_codec_of_match);
static struct platform_driver rk3036_codec_platform_driver = {
.driver = {
.name = "rk3036-codec-platform",
.of_match_table = of_match_ptr(rk3036_codec_of_match),
},
.probe = rk3036_codec_platform_probe,
.remove = rk3036_codec_platform_remove,
};
module_platform_driver(rk3036_codec_platform_driver);
MODULE_AUTHOR("Rockchip Inc.");
MODULE_DESCRIPTION("Rockchip rk3036 codec driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,123 @@
/*
* Driver of Inno Codec for rk3036 by Rockchip Inc.
*
* Author: Zheng ShunQian<zhengsq@rock-chips.com>
*/
#ifndef _INNO_RK3036_CODEC_H
#define _INNO_RK3036_CODEC_H
/* codec registers */
#define INNO_R00 0x00
#define INNO_R01 0x0c
#define INNO_R02 0x10
#define INNO_R03 0x14
#define INNO_R04 0x88
#define INNO_R05 0x8c
#define INNO_R06 0x90
#define INNO_R07 0x94
#define INNO_R08 0x98
#define INNO_R09 0x9c
#define INNO_R10 0xa0
/* register bit filed */
#define INNO_R00_CSR_RESET (0x0 << 0) /*codec system reset*/
#define INNO_R00_CSR_WORK (0x1 << 0)
#define INNO_R00_CDCR_RESET (0x0 << 1) /*codec digital core reset*/
#define INNO_R00_CDCR_WORK (0x1 << 1)
#define INNO_R00_PRB_DISABLE (0x0 << 6) /*power reset bypass*/
#define INNO_R00_PRB_ENABLE (0x1 << 6)
#define INNO_R01_I2SMODE_MSK (0x1 << 4)
#define INNO_R01_I2SMODE_SLAVE (0x0 << 4)
#define INNO_R01_I2SMODE_MASTER (0x1 << 4)
#define INNO_R01_PINDIR_MSK (0x1 << 5)
#define INNO_R01_PINDIR_IN_SLAVE (0x0 << 5) /*direction of pin*/
#define INNO_R01_PINDIR_OUT_MASTER (0x1 << 5)
#define INNO_R02_LRS_MSK (0x1 << 2)
#define INNO_R02_LRS_NORMAL (0x0 << 2) /*DAC Left Right Swap*/
#define INNO_R02_LRS_SWAP (0x1 << 2)
#define INNO_R02_DACM_MSK (0x3 << 3)
#define INNO_R02_DACM_PCM (0x3 << 3) /*DAC Mode*/
#define INNO_R02_DACM_I2S (0x2 << 3)
#define INNO_R02_DACM_LJM (0x1 << 3)
#define INNO_R02_DACM_RJM (0x0 << 3)
#define INNO_R02_VWL_MSK (0x3 << 5)
#define INNO_R02_VWL_32BIT (0x3 << 5) /*1/2Frame Valid Word Len*/
#define INNO_R02_VWL_24BIT (0x2 << 5)
#define INNO_R02_VWL_20BIT (0x1 << 5)
#define INNO_R02_VWL_16BIT (0x0 << 5)
#define INNO_R02_LRCP_MSK (0x1 << 7)
#define INNO_R02_LRCP_NORMAL (0x0 << 7) /*Left Right Polarity*/
#define INNO_R02_LRCP_REVERSAL (0x1 << 7)
#define INNO_R03_BCP_MSK (0x1 << 0)
#define INNO_R03_BCP_NORMAL (0x0 << 0) /*DAC bit clock polarity*/
#define INNO_R03_BCP_REVERSAL (0x1 << 0)
#define INNO_R03_DACR_MSK (0x1 << 1)
#define INNO_R03_DACR_RESET (0x0 << 1) /*DAC Reset*/
#define INNO_R03_DACR_WORK (0x1 << 1)
#define INNO_R03_FWL_MSK (0x3 << 2)
#define INNO_R03_FWL_32BIT (0x3 << 2) /*1/2Frame Word Length*/
#define INNO_R03_FWL_24BIT (0x2 << 2)
#define INNO_R03_FWL_20BIT (0x1 << 2)
#define INNO_R03_FWL_16BIT (0x0 << 2)
#define INNO_R04_DACR_SW_SHIFT 0
#define INNO_R04_DACL_SW_SHIFT 1
#define INNO_R04_DACR_CLK_SHIFT 2
#define INNO_R04_DACL_CLK_SHIFT 3
#define INNO_R04_DACR_VREF_SHIFT 4
#define INNO_R04_DACL_VREF_SHIFT 5
#define INNO_R05_HPR_EN_SHIFT 0
#define INNO_R05_HPL_EN_SHIFT 1
#define INNO_R05_HPR_WORK_SHIFT 2
#define INNO_R05_HPL_WORK_SHIFT 3
#define INNO_R06_VOUTR_CZ_SHIFT 0
#define INNO_R06_VOUTL_CZ_SHIFT 1
#define INNO_R06_DACR_HILO_VREF_SHIFT 2
#define INNO_R06_DACL_HILO_VREF_SHIFT 3
#define INNO_R06_DAC_EN_SHIFT 5
#define INNO_R06_DAC_PRECHARGE (0x0 << 4) /*PreCharge control for DAC*/
#define INNO_R06_DAC_DISCHARGE (0x1 << 4)
#define INNO_HP_GAIN_SHIFT 0
/* Gain of output, 1.5db step: -39db(0x0) ~ 0db(0x1a) ~ 6db(0x1f) */
#define INNO_HP_GAIN_0DB 0x1a
#define INNO_HP_GAIN_N39DB 0x0
#define INNO_R09_HP_ANTIPOP_MSK 0x3
#define INNO_R09_HP_ANTIPOP_OFF 0x1
#define INNO_R09_HP_ANTIPOP_ON 0x2
#define INNO_R09_HPR_ANITPOP_SHIFT 0
#define INNO_R09_HPL_ANITPOP_SHIFT 2
#define INNO_R09_HPR_MUTE_SHIFT 4
#define INNO_R09_HPL_MUTE_SHIFT 5
#define INNO_R09_DACR_SWITCH_SHIFT 6
#define INNO_R09_DACL_SWITCH_SHIFT 7
#define INNO_R10_CHARGE_SEL_CUR_400I_YES (0x0 << 0)
#define INNO_R10_CHARGE_SEL_CUR_400I_NO (0x1 << 0)
#define INNO_R10_CHARGE_SEL_CUR_260I_YES (0x0 << 1)
#define INNO_R10_CHARGE_SEL_CUR_260I_NO (0x1 << 1)
#define INNO_R10_CHARGE_SEL_CUR_130I_YES (0x0 << 2)
#define INNO_R10_CHARGE_SEL_CUR_130I_NO (0x1 << 2)
#define INNO_R10_CHARGE_SEL_CUR_100I_YES (0x0 << 3)
#define INNO_R10_CHARGE_SEL_CUR_100I_NO (0x1 << 3)
#define INNO_R10_CHARGE_SEL_CUR_050I_YES (0x0 << 4)
#define INNO_R10_CHARGE_SEL_CUR_050I_NO (0x1 << 4)
#define INNO_R10_CHARGE_SEL_CUR_027I_YES (0x0 << 5)
#define INNO_R10_CHARGE_SEL_CUR_027I_NO (0x1 << 5)
#define INNO_R10_MAX_CUR (INNO_R10_CHARGE_SEL_CUR_400I_YES | \
INNO_R10_CHARGE_SEL_CUR_260I_YES | \
INNO_R10_CHARGE_SEL_CUR_130I_YES | \
INNO_R10_CHARGE_SEL_CUR_100I_YES | \
INNO_R10_CHARGE_SEL_CUR_050I_YES | \
INNO_R10_CHARGE_SEL_CUR_027I_YES)
#endif

View File

@ -12,6 +12,7 @@
* max98357a.c -- MAX98357A ALSA SoC Codec driver
*/
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio.h>
@ -123,10 +124,19 @@ static const struct of_device_id max98357a_device_id[] = {
MODULE_DEVICE_TABLE(of, max98357a_device_id);
#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id max98357a_acpi_match[] = {
{ "MX98357A", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, max98357a_acpi_match);
#endif
static struct platform_driver max98357a_platform_driver = {
.driver = {
.name = "max98357a",
.of_match_table = of_match_ptr(max98357a_device_id),
.acpi_match_table = ACPI_PTR(max98357a_acpi_match),
},
.probe = max98357a_platform_probe,
.remove = max98357a_platform_remove,

View File

@ -0,0 +1,66 @@
/*
* PCM3168A codec i2c driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <sound/soc.h>
#include "pcm3168a.h"
static int pcm3168a_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct regmap *regmap;
regmap = devm_regmap_init_i2c(i2c, &pcm3168a_regmap);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return pcm3168a_probe(&i2c->dev, regmap);
}
static int pcm3168a_i2c_remove(struct i2c_client *i2c)
{
pcm3168a_remove(&i2c->dev);
return 0;
}
static const struct i2c_device_id pcm3168a_i2c_id[] = {
{ "pcm3168a", },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
static const struct of_device_id pcm3168a_of_match[] = {
{ .compatible = "ti,pcm3168a", },
{ }
};
MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
static struct i2c_driver pcm3168a_i2c_driver = {
.probe = pcm3168a_i2c_probe,
.remove = pcm3168a_i2c_remove,
.id_table = pcm3168a_i2c_id,
.driver = {
.name = "pcm3168a",
.of_match_table = pcm3168a_of_match,
.pm = &pcm3168a_pm_ops,
},
};
module_i2c_driver(pcm3168a_i2c_driver);
MODULE_DESCRIPTION("PCM3168A I2C codec driver");
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,65 @@
/*
* PCM3168A codec spi driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include "pcm3168a.h"
static int pcm3168a_spi_probe(struct spi_device *spi)
{
struct regmap *regmap;
regmap = devm_regmap_init_spi(spi, &pcm3168a_regmap);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return pcm3168a_probe(&spi->dev, regmap);
}
static int pcm3168a_spi_remove(struct spi_device *spi)
{
pcm3168a_remove(&spi->dev);
return 0;
}
static const struct spi_device_id pcm3168a_spi_id[] = {
{ "pcm3168a", },
{ },
};
MODULE_DEVICE_TABLE(spi, pcm3168a_spi_id);
static const struct of_device_id pcm3168a_of_match[] = {
{ .compatible = "ti,pcm3168a", },
{ }
};
MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
static struct spi_driver pcm3168a_spi_driver = {
.probe = pcm3168a_spi_probe,
.remove = pcm3168a_spi_remove,
.id_table = pcm3168a_spi_id,
.driver = {
.name = "pcm3168a",
.of_match_table = pcm3168a_of_match,
.pm = &pcm3168a_pm_ops,
},
};
module_spi_driver(pcm3168a_spi_driver);
MODULE_DESCRIPTION("PCM3168A SPI codec driver");
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
MODULE_LICENSE("GPL v2");

767
sound/soc/codecs/pcm3168a.c Normal file
View File

@ -0,0 +1,767 @@
/*
* PCM3168A codec driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "pcm3168a.h"
#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
#define PCM3168A_FMT_I2S 0x0
#define PCM3168A_FMT_LEFT_J 0x1
#define PCM3168A_FMT_RIGHT_J 0x2
#define PCM3168A_FMT_RIGHT_J_16 0x3
#define PCM3168A_FMT_DSP_A 0x4
#define PCM3168A_FMT_DSP_B 0x5
#define PCM3168A_FMT_DSP_MASK 0x4
#define PCM3168A_NUM_SUPPLIES 6
static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
"VDD1",
"VDD2",
"VCCAD1",
"VCCAD2",
"VCCDA1",
"VCCDA2"
};
struct pcm3168a_priv {
struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
struct regmap *regmap;
struct clk *scki;
bool adc_master_mode;
bool dac_master_mode;
unsigned long sysclk;
unsigned int adc_fmt;
unsigned int dac_fmt;
};
static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
static SOC_ENUM_SINGLE_DECL(pcm3168a_d1_roll_off, PCM3168A_DAC_OP_FLT,
PCM3168A_DAC_FLT_SHIFT, pcm3168a_roll_off);
static SOC_ENUM_SINGLE_DECL(pcm3168a_d2_roll_off, PCM3168A_DAC_OP_FLT,
PCM3168A_DAC_FLT_SHIFT + 1, pcm3168a_roll_off);
static SOC_ENUM_SINGLE_DECL(pcm3168a_d3_roll_off, PCM3168A_DAC_OP_FLT,
PCM3168A_DAC_FLT_SHIFT + 2, pcm3168a_roll_off);
static SOC_ENUM_SINGLE_DECL(pcm3168a_d4_roll_off, PCM3168A_DAC_OP_FLT,
PCM3168A_DAC_FLT_SHIFT + 3, pcm3168a_roll_off);
static const char *const pcm3168a_volume_type[] = {
"Individual", "Master + Individual" };
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_volume_type, PCM3168A_DAC_ATT_DEMP_ZF,
PCM3168A_DAC_ATMDDA_SHIFT, pcm3168a_volume_type);
static const char *const pcm3168a_att_speed_mult[] = { "2048", "4096" };
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_att_mult, PCM3168A_DAC_ATT_DEMP_ZF,
PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_att_speed_mult);
static const char *const pcm3168a_demp[] = {
"Disabled", "48khz", "44.1khz", "32khz" };
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_demp, PCM3168A_DAC_ATT_DEMP_ZF,
PCM3168A_DAC_DEMP_SHIFT, pcm3168a_demp);
static const char *const pcm3168a_zf_func[] = {
"DAC 1/2/3/4 AND", "DAC 1/2/3/4 OR", "DAC 1/2/3 AND",
"DAC 1/2/3 OR", "DAC 4 AND", "DAC 4 OR" };
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF,
PCM3168A_DAC_AZRO_SHIFT, pcm3168a_zf_func);
static const char *const pcm3168a_pol[] = { "Active High", "Active Low" };
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF,
PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol);
static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" };
static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc1_con, PCM3168A_ADC_SEAD,
0, 1, pcm3168a_con);
static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc2_con, PCM3168A_ADC_SEAD,
2, 3, pcm3168a_con);
static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc3_con, PCM3168A_ADC_SEAD,
4, 5, pcm3168a_con);
static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_volume_type, PCM3168A_ADC_ATT_OVF,
PCM3168A_ADC_ATMDAD_SHIFT, pcm3168a_volume_type);
static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_att_mult, PCM3168A_ADC_ATT_OVF,
PCM3168A_ADC_ATSPAD_SHIFT, pcm3168a_att_speed_mult);
static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_ov_pol, PCM3168A_ADC_ATT_OVF,
PCM3168A_ADC_OVFP_SHIFT, pcm3168a_pol);
/* -100db to 0db, register values 0-54 cause mute */
static const DECLARE_TLV_DB_SCALE(pcm3168a_dac_tlv, -10050, 50, 1);
/* -100db to 20db, register values 0-14 cause mute */
static const DECLARE_TLV_DB_SCALE(pcm3168a_adc_tlv, -10050, 50, 1);
static const struct snd_kcontrol_new pcm3168a_snd_controls[] = {
SOC_SINGLE("DAC Power-Save Switch", PCM3168A_DAC_PWR_MST_FMT,
PCM3168A_DAC_PSMDA_SHIFT, 1, 1),
SOC_ENUM("DAC1 Digital Filter roll-off", pcm3168a_d1_roll_off),
SOC_ENUM("DAC2 Digital Filter roll-off", pcm3168a_d2_roll_off),
SOC_ENUM("DAC3 Digital Filter roll-off", pcm3168a_d3_roll_off),
SOC_ENUM("DAC4 Digital Filter roll-off", pcm3168a_d4_roll_off),
SOC_DOUBLE("DAC1 Invert Switch", PCM3168A_DAC_INV, 0, 1, 1, 0),
SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0),
SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0),
SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0),
SOC_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0),
SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0),
SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0),
SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 6, 7, 1, 0),
SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type),
SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult),
SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp),
SOC_ENUM("DAC Zero Flag Function", pcm3168a_dac_zf_func),
SOC_ENUM("DAC Zero Flag Polarity", pcm3168a_dac_zf_pol),
SOC_SINGLE_RANGE_TLV("Master Playback Volume",
PCM3168A_DAC_VOL_MASTER, 0, 54, 255, 0,
pcm3168a_dac_tlv),
SOC_DOUBLE_R_RANGE_TLV("DAC1 Playback Volume",
PCM3168A_DAC_VOL_CHAN_START,
PCM3168A_DAC_VOL_CHAN_START + 1,
0, 54, 255, 0, pcm3168a_dac_tlv),
SOC_DOUBLE_R_RANGE_TLV("DAC2 Playback Volume",
PCM3168A_DAC_VOL_CHAN_START + 2,
PCM3168A_DAC_VOL_CHAN_START + 3,
0, 54, 255, 0, pcm3168a_dac_tlv),
SOC_DOUBLE_R_RANGE_TLV("DAC3 Playback Volume",
PCM3168A_DAC_VOL_CHAN_START + 4,
PCM3168A_DAC_VOL_CHAN_START + 5,
0, 54, 255, 0, pcm3168a_dac_tlv),
SOC_DOUBLE_R_RANGE_TLV("DAC4 Playback Volume",
PCM3168A_DAC_VOL_CHAN_START + 6,
PCM3168A_DAC_VOL_CHAN_START + 7,
0, 54, 255, 0, pcm3168a_dac_tlv),
SOC_SINGLE("ADC1 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
PCM3168A_ADC_BYP_SHIFT, 1, 1),
SOC_SINGLE("ADC2 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
PCM3168A_ADC_BYP_SHIFT + 1, 1, 1),
SOC_SINGLE("ADC3 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
PCM3168A_ADC_BYP_SHIFT + 2, 1, 1),
SOC_ENUM("ADC1 Connection Type", pcm3168a_adc1_con),
SOC_ENUM("ADC2 Connection Type", pcm3168a_adc2_con),
SOC_ENUM("ADC3 Connection Type", pcm3168a_adc3_con),
SOC_DOUBLE("ADC1 Invert Switch", PCM3168A_ADC_INV, 0, 1, 1, 0),
SOC_DOUBLE("ADC2 Invert Switch", PCM3168A_ADC_INV, 2, 3, 1, 0),
SOC_DOUBLE("ADC3 Invert Switch", PCM3168A_ADC_INV, 4, 5, 1, 0),
SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0),
SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0),
SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0),
SOC_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0),
SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0),
SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 4, 5, 1, 0),
SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type),
SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult),
SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol),
SOC_SINGLE_RANGE_TLV("Master Capture Volume",
PCM3168A_ADC_VOL_MASTER, 0, 14, 255, 0,
pcm3168a_adc_tlv),
SOC_DOUBLE_R_RANGE_TLV("ADC1 Capture Volume",
PCM3168A_ADC_VOL_CHAN_START,
PCM3168A_ADC_VOL_CHAN_START + 1,
0, 14, 255, 0, pcm3168a_adc_tlv),
SOC_DOUBLE_R_RANGE_TLV("ADC2 Capture Volume",
PCM3168A_ADC_VOL_CHAN_START + 2,
PCM3168A_ADC_VOL_CHAN_START + 3,
0, 14, 255, 0, pcm3168a_adc_tlv),
SOC_DOUBLE_R_RANGE_TLV("ADC3 Capture Volume",
PCM3168A_ADC_VOL_CHAN_START + 4,
PCM3168A_ADC_VOL_CHAN_START + 5,
0, 14, 255, 0, pcm3168a_adc_tlv)
};
static const struct snd_soc_dapm_widget pcm3168a_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC1", "Playback", PCM3168A_DAC_OP_FLT,
PCM3168A_DAC_OPEDA_SHIFT, 1),
SND_SOC_DAPM_DAC("DAC2", "Playback", PCM3168A_DAC_OP_FLT,
PCM3168A_DAC_OPEDA_SHIFT + 1, 1),
SND_SOC_DAPM_DAC("DAC3", "Playback", PCM3168A_DAC_OP_FLT,
PCM3168A_DAC_OPEDA_SHIFT + 2, 1),
SND_SOC_DAPM_DAC("DAC4", "Playback", PCM3168A_DAC_OP_FLT,
PCM3168A_DAC_OPEDA_SHIFT + 3, 1),
SND_SOC_DAPM_OUTPUT("AOUT1L"),
SND_SOC_DAPM_OUTPUT("AOUT1R"),
SND_SOC_DAPM_OUTPUT("AOUT2L"),
SND_SOC_DAPM_OUTPUT("AOUT2R"),
SND_SOC_DAPM_OUTPUT("AOUT3L"),
SND_SOC_DAPM_OUTPUT("AOUT3R"),
SND_SOC_DAPM_OUTPUT("AOUT4L"),
SND_SOC_DAPM_OUTPUT("AOUT4R"),
SND_SOC_DAPM_ADC("ADC1", "Capture", PCM3168A_ADC_PWR_HPFB,
PCM3168A_ADC_PSVAD_SHIFT, 1),
SND_SOC_DAPM_ADC("ADC2", "Capture", PCM3168A_ADC_PWR_HPFB,
PCM3168A_ADC_PSVAD_SHIFT + 1, 1),
SND_SOC_DAPM_ADC("ADC3", "Capture", PCM3168A_ADC_PWR_HPFB,
PCM3168A_ADC_PSVAD_SHIFT + 2, 1),
SND_SOC_DAPM_INPUT("AIN1L"),
SND_SOC_DAPM_INPUT("AIN1R"),
SND_SOC_DAPM_INPUT("AIN2L"),
SND_SOC_DAPM_INPUT("AIN2R"),
SND_SOC_DAPM_INPUT("AIN3L"),
SND_SOC_DAPM_INPUT("AIN3R")
};
static const struct snd_soc_dapm_route pcm3168a_dapm_routes[] = {
/* Playback */
{ "AOUT1L", NULL, "DAC1" },
{ "AOUT1R", NULL, "DAC1" },
{ "AOUT2L", NULL, "DAC2" },
{ "AOUT2R", NULL, "DAC2" },
{ "AOUT3L", NULL, "DAC3" },
{ "AOUT3R", NULL, "DAC3" },
{ "AOUT4L", NULL, "DAC4" },
{ "AOUT4R", NULL, "DAC4" },
/* Capture */
{ "ADC1", NULL, "AIN1L" },
{ "ADC1", NULL, "AIN1R" },
{ "ADC2", NULL, "AIN2L" },
{ "ADC2", NULL, "AIN2R" },
{ "ADC3", NULL, "AIN3L" },
{ "ADC3", NULL, "AIN3R" }
};
static unsigned int pcm3168a_scki_ratios[] = {
768,
512,
384,
256,
192,
128
};
#define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios)
#define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2)
#define PCM1368A_MAX_SYSCLK 36864000
static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
{
int ret;
ret = regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, 0);
if (ret)
return ret;
/* Internal reset is de-asserted after 3846 SCKI cycles */
msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
return regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE,
PCM3168A_MRST_MASK | PCM3168A_SRST_MASK);
}
static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
regmap_write(pcm3168a->regmap, PCM3168A_DAC_MUTE, mute ? 0xff : 0);
return 0;
}
static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec);
if (freq > PCM1368A_MAX_SYSCLK)
return -EINVAL;
pcm3168a->sysclk = freq;
return 0;
}
static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int format, bool dac)
{
struct snd_soc_codec *codec = dai->codec;
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
u32 fmt, reg, mask, shift;
bool master_mode;
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_LEFT_J:
fmt = PCM3168A_FMT_LEFT_J;
break;
case SND_SOC_DAIFMT_I2S:
fmt = PCM3168A_FMT_I2S;
break;
case SND_SOC_DAIFMT_RIGHT_J:
fmt = PCM3168A_FMT_RIGHT_J;
break;
case SND_SOC_DAIFMT_DSP_A:
fmt = PCM3168A_FMT_DSP_A;
break;
case SND_SOC_DAIFMT_DSP_B:
fmt = PCM3168A_FMT_DSP_B;
break;
default:
dev_err(codec->dev, "unsupported dai format\n");
return -EINVAL;
}
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
master_mode = false;
break;
case SND_SOC_DAIFMT_CBM_CFM:
master_mode = true;
break;
default:
dev_err(codec->dev, "unsupported master/slave mode\n");
return -EINVAL;
}
switch (format & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
default:
return -EINVAL;
}
if (dac) {
reg = PCM3168A_DAC_PWR_MST_FMT;
mask = PCM3168A_DAC_FMT_MASK;
shift = PCM3168A_DAC_FMT_SHIFT;
pcm3168a->dac_master_mode = master_mode;
pcm3168a->dac_fmt = fmt;
} else {
reg = PCM3168A_ADC_MST_FMT;
mask = PCM3168A_ADC_FMTAD_MASK;
shift = PCM3168A_ADC_FMTAD_SHIFT;
pcm3168a->adc_master_mode = master_mode;
pcm3168a->adc_fmt = fmt;
}
regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
return 0;
}
static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai,
unsigned int format)
{
return pcm3168a_set_dai_fmt(dai, format, true);
}
static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai,
unsigned int format)
{
return pcm3168a_set_dai_fmt(dai, format, false);
}
static int pcm3168a_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 pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
bool tx, master_mode;
u32 val, mask, shift, reg;
unsigned int rate, channels, fmt, ratio, max_ratio;
int i, min_frame_size;
snd_pcm_format_t format;
rate = params_rate(params);
format = params_format(params);
channels = params_channels(params);
ratio = pcm3168a->sysclk / rate;
tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
if (tx) {
max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC;
reg = PCM3168A_DAC_PWR_MST_FMT;
mask = PCM3168A_DAC_MSDA_MASK;
shift = PCM3168A_DAC_MSDA_SHIFT;
master_mode = pcm3168a->dac_master_mode;
fmt = pcm3168a->dac_fmt;
} else {
max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC;
reg = PCM3168A_ADC_MST_FMT;
mask = PCM3168A_ADC_MSAD_MASK;
shift = PCM3168A_ADC_MSAD_SHIFT;
master_mode = pcm3168a->adc_master_mode;
fmt = pcm3168a->adc_fmt;
}
for (i = 0; i < max_ratio; i++) {
if (pcm3168a_scki_ratios[i] == ratio)
break;
}
if (i == max_ratio) {
dev_err(codec->dev, "unsupported sysclk ratio\n");
return -EINVAL;
}
min_frame_size = params_width(params) * 2;
switch (min_frame_size) {
case 32:
if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
return -EINVAL;
}
fmt = PCM3168A_FMT_RIGHT_J_16;
break;
case 48:
if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) {
dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
return -EINVAL;
}
break;
case 64:
break;
default:
dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
return -EINVAL;
}
if (master_mode)
val = ((i + 1) << shift);
else
val = 0;
regmap_update_bits(pcm3168a->regmap, reg, mask, val);
if (tx) {
mask = PCM3168A_DAC_FMT_MASK;
shift = PCM3168A_DAC_FMT_SHIFT;
} else {
mask = PCM3168A_ADC_FMTAD_MASK;
shift = PCM3168A_ADC_FMTAD_SHIFT;
}
regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
return 0;
}
static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
.set_fmt = pcm3168a_set_dai_fmt_dac,
.set_sysclk = pcm3168a_set_dai_sysclk,
.hw_params = pcm3168a_hw_params,
.digital_mute = pcm3168a_digital_mute
};
static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
.set_fmt = pcm3168a_set_dai_fmt_adc,
.set_sysclk = pcm3168a_set_dai_sysclk,
.hw_params = pcm3168a_hw_params
};
static struct snd_soc_dai_driver pcm3168a_dais[] = {
{
.name = "pcm3168a-dac",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = PCM3168A_FORMATS
},
.ops = &pcm3168a_dac_dai_ops
},
{
.name = "pcm3168a-adc",
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 6,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = PCM3168A_FORMATS
},
.ops = &pcm3168a_adc_dai_ops
},
};
static const struct reg_default pcm3168a_reg_default[] = {
{ PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK },
{ PCM3168A_DAC_PWR_MST_FMT, 0x00 },
{ PCM3168A_DAC_OP_FLT, 0x00 },
{ PCM3168A_DAC_INV, 0x00 },
{ PCM3168A_DAC_MUTE, 0x00 },
{ PCM3168A_DAC_ZERO, 0x00 },
{ PCM3168A_DAC_ATT_DEMP_ZF, 0x00 },
{ PCM3168A_DAC_VOL_MASTER, 0xff },
{ PCM3168A_DAC_VOL_CHAN_START, 0xff },
{ PCM3168A_DAC_VOL_CHAN_START + 1, 0xff },
{ PCM3168A_DAC_VOL_CHAN_START + 2, 0xff },
{ PCM3168A_DAC_VOL_CHAN_START + 3, 0xff },
{ PCM3168A_DAC_VOL_CHAN_START + 4, 0xff },
{ PCM3168A_DAC_VOL_CHAN_START + 5, 0xff },
{ PCM3168A_DAC_VOL_CHAN_START + 6, 0xff },
{ PCM3168A_DAC_VOL_CHAN_START + 7, 0xff },
{ PCM3168A_ADC_SMODE, 0x00 },
{ PCM3168A_ADC_MST_FMT, 0x00 },
{ PCM3168A_ADC_PWR_HPFB, 0x00 },
{ PCM3168A_ADC_SEAD, 0x00 },
{ PCM3168A_ADC_INV, 0x00 },
{ PCM3168A_ADC_MUTE, 0x00 },
{ PCM3168A_ADC_OV, 0x00 },
{ PCM3168A_ADC_ATT_OVF, 0x00 },
{ PCM3168A_ADC_VOL_MASTER, 0xd3 },
{ PCM3168A_ADC_VOL_CHAN_START, 0xd3 },
{ PCM3168A_ADC_VOL_CHAN_START + 1, 0xd3 },
{ PCM3168A_ADC_VOL_CHAN_START + 2, 0xd3 },
{ PCM3168A_ADC_VOL_CHAN_START + 3, 0xd3 },
{ PCM3168A_ADC_VOL_CHAN_START + 4, 0xd3 },
{ PCM3168A_ADC_VOL_CHAN_START + 5, 0xd3 }
};
static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
{
if (reg >= PCM3168A_RST_SMODE)
return true;
else
return false;
}
static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case PCM3168A_DAC_ZERO:
case PCM3168A_ADC_OV:
return true;
default:
return false;
}
}
static bool pcm3168a_writeable_register(struct device *dev, unsigned int reg)
{
if (reg < PCM3168A_RST_SMODE)
return false;
switch (reg) {
case PCM3168A_DAC_ZERO:
case PCM3168A_ADC_OV:
return false;
default:
return true;
}
}
const struct regmap_config pcm3168a_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = PCM3168A_ADC_VOL_CHAN_START + 5,
.reg_defaults = pcm3168a_reg_default,
.num_reg_defaults = ARRAY_SIZE(pcm3168a_reg_default),
.readable_reg = pcm3168a_readable_register,
.volatile_reg = pcm3168a_volatile_register,
.writeable_reg = pcm3168a_writeable_register,
.cache_type = REGCACHE_FLAT
};
EXPORT_SYMBOL_GPL(pcm3168a_regmap);
static const struct snd_soc_codec_driver pcm3168a_driver = {
.idle_bias_off = true,
.controls = pcm3168a_snd_controls,
.num_controls = ARRAY_SIZE(pcm3168a_snd_controls),
.dapm_widgets = pcm3168a_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets),
.dapm_routes = pcm3168a_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes)
};
int pcm3168a_probe(struct device *dev, struct regmap *regmap)
{
struct pcm3168a_priv *pcm3168a;
int ret, i;
pcm3168a = devm_kzalloc(dev, sizeof(*pcm3168a), GFP_KERNEL);
if (pcm3168a == NULL)
return -ENOMEM;
dev_set_drvdata(dev, pcm3168a);
pcm3168a->scki = devm_clk_get(dev, "scki");
if (IS_ERR(pcm3168a->scki)) {
ret = PTR_ERR(pcm3168a->scki);
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to acquire clock 'scki': %d\n", ret);
return ret;
}
ret = clk_prepare_enable(pcm3168a->scki);
if (ret) {
dev_err(dev, "Failed to enable mclk: %d\n", ret);
return ret;
}
pcm3168a->sysclk = clk_get_rate(pcm3168a->scki);
for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++)
pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
ret = devm_regulator_bulk_get(dev,
ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to request supplies: %d\n", ret);
goto err_clk;
}
ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
pcm3168a->supplies);
if (ret) {
dev_err(dev, "failed to enable supplies: %d\n", ret);
goto err_clk;
}
pcm3168a->regmap = regmap;
if (IS_ERR(pcm3168a->regmap)) {
ret = PTR_ERR(pcm3168a->regmap);
dev_err(dev, "failed to allocate regmap: %d\n", ret);
goto err_regulator;
}
ret = pcm3168a_reset(pcm3168a);
if (ret) {
dev_err(dev, "Failed to reset device: %d\n", ret);
goto err_regulator;
}
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_idle(dev);
ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais,
ARRAY_SIZE(pcm3168a_dais));
if (ret) {
dev_err(dev, "failed to register codec: %d\n", ret);
goto err_regulator;
}
return 0;
err_regulator:
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
pcm3168a->supplies);
err_clk:
clk_disable_unprepare(pcm3168a->scki);
return ret;
}
EXPORT_SYMBOL_GPL(pcm3168a_probe);
void pcm3168a_remove(struct device *dev)
{
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
snd_soc_unregister_codec(dev);
pm_runtime_disable(dev);
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
pcm3168a->supplies);
clk_disable_unprepare(pcm3168a->scki);
}
EXPORT_SYMBOL_GPL(pcm3168a_remove);
#ifdef CONFIG_PM
static int pcm3168a_rt_resume(struct device *dev)
{
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(pcm3168a->scki);
if (ret) {
dev_err(dev, "Failed to enable mclk: %d\n", ret);
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
pcm3168a->supplies);
if (ret) {
dev_err(dev, "Failed to enable supplies: %d\n", ret);
goto err_clk;
}
ret = pcm3168a_reset(pcm3168a);
if (ret) {
dev_err(dev, "Failed to reset device: %d\n", ret);
goto err_regulator;
}
regcache_cache_only(pcm3168a->regmap, false);
regcache_mark_dirty(pcm3168a->regmap);
ret = regcache_sync(pcm3168a->regmap);
if (ret) {
dev_err(dev, "Failed to sync regmap: %d\n", ret);
goto err_regulator;
}
return 0;
err_regulator:
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
pcm3168a->supplies);
err_clk:
clk_disable_unprepare(pcm3168a->scki);
return ret;
}
static int pcm3168a_rt_suspend(struct device *dev)
{
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
regcache_cache_only(pcm3168a->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
pcm3168a->supplies);
clk_disable_unprepare(pcm3168a->scki);
return 0;
}
#endif
const struct dev_pm_ops pcm3168a_pm_ops = {
SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL)
};
EXPORT_SYMBOL_GPL(pcm3168a_pm_ops);
MODULE_DESCRIPTION("PCM3168A codec driver");
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
MODULE_LICENSE("GPL v2");

100
sound/soc/codecs/pcm3168a.h Normal file
View File

@ -0,0 +1,100 @@
/*
* PCM3168A codec driver header
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#ifndef __PCM3168A_H__
#define __PCM3168A_H__
extern const struct dev_pm_ops pcm3168a_pm_ops;
extern const struct regmap_config pcm3168a_regmap;
extern int pcm3168a_probe(struct device *dev, struct regmap *regmap);
extern void pcm3168a_remove(struct device *dev);
#define PCM3168A_RST_SMODE 0x40
#define PCM3168A_MRST_MASK 0x80
#define PCM3168A_SRST_MASK 0x40
#define PCM3168A_DAC_SRDA_SHIFT 0
#define PCM3168A_DAC_SRDA_MASK 0x3
#define PCM3168A_DAC_PWR_MST_FMT 0x41
#define PCM3168A_DAC_PSMDA_SHIFT 7
#define PCM3168A_DAC_PSMDA_MASK 0x80
#define PCM3168A_DAC_MSDA_SHIFT 4
#define PCM3168A_DAC_MSDA_MASK 0x70
#define PCM3168A_DAC_FMT_SHIFT 0
#define PCM3168A_DAC_FMT_MASK 0xf
#define PCM3168A_DAC_OP_FLT 0x42
#define PCM3168A_DAC_OPEDA_SHIFT 4
#define PCM3168A_DAC_OPEDA_MASK 0xf0
#define PCM3168A_DAC_FLT_SHIFT 0
#define PCM3168A_DAC_FLT_MASK 0xf
#define PCM3168A_DAC_INV 0x43
#define PCM3168A_DAC_MUTE 0x44
#define PCM3168A_DAC_ZERO 0x45
#define PCM3168A_DAC_ATT_DEMP_ZF 0x46
#define PCM3168A_DAC_ATMDDA_MASK 0x80
#define PCM3168A_DAC_ATMDDA_SHIFT 7
#define PCM3168A_DAC_ATSPDA_MASK 0x40
#define PCM3168A_DAC_ATSPDA_SHIFT 6
#define PCM3168A_DAC_DEMP_SHIFT 4
#define PCM3168A_DAC_DEMP_MASK 0x30
#define PCM3168A_DAC_AZRO_SHIFT 1
#define PCM3168A_DAC_AZRO_MASK 0xe
#define PCM3168A_DAC_ZREV_MASK 0x1
#define PCM3168A_DAC_ZREV_SHIFT 0
#define PCM3168A_DAC_VOL_MASTER 0x47
#define PCM3168A_DAC_VOL_CHAN_START 0x48
#define PCM3168A_ADC_SMODE 0x50
#define PCM3168A_ADC_SRAD_SHIFT 0
#define PCM3168A_ADC_SRAD_MASK 0x3
#define PCM3168A_ADC_MST_FMT 0x51
#define PCM3168A_ADC_MSAD_SHIFT 4
#define PCM3168A_ADC_MSAD_MASK 0x70
#define PCM3168A_ADC_FMTAD_SHIFT 0
#define PCM3168A_ADC_FMTAD_MASK 0x7
#define PCM3168A_ADC_PWR_HPFB 0x52
#define PCM3168A_ADC_PSVAD_SHIFT 4
#define PCM3168A_ADC_PSVAD_MASK 0x70
#define PCM3168A_ADC_BYP_SHIFT 0
#define PCM3168A_ADC_BYP_MASK 0x7
#define PCM3168A_ADC_SEAD 0x53
#define PCM3168A_ADC_INV 0x54
#define PCM3168A_ADC_MUTE 0x55
#define PCM3168A_ADC_OV 0x56
#define PCM3168A_ADC_ATT_OVF 0x57
#define PCM3168A_ADC_ATMDAD_MASK 0x80
#define PCM3168A_ADC_ATMDAD_SHIFT 7
#define PCM3168A_ADC_ATSPAD_MASK 0x40
#define PCM3168A_ADC_ATSPAD_SHIFT 6
#define PCM3168A_ADC_OVFP_MASK 0x1
#define PCM3168A_ADC_OVFP_SHIFT 0
#define PCM3168A_ADC_VOL_MASTER 0x58
#define PCM3168A_ADC_VOL_CHAN_START 0x59
#endif

View File

@ -1114,6 +1114,12 @@ static const struct dmi_system_id force_combo_jack_table[] = {
DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS")
}
},
{
.ident = "Intel Skylake RVP",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Skylake Client platform")
}
},
{ }
};

View File

@ -854,8 +854,6 @@ static int rt298_set_dai_sysclk(struct snd_soc_dai *dai,
} else {
snd_soc_update_bits(codec,
RT298_I2S_CTRL2, 0x0100, 0x0100);
snd_soc_update_bits(codec,
RT298_PLL_CTRL, 0x4, 0x4);
snd_soc_update_bits(codec,
RT298_PLL_CTRL1, 0x20, 0x0);
}

1381
sound/soc/codecs/rt5616.c Normal file

File diff suppressed because it is too large Load Diff

1819
sound/soc/codecs/rt5616.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -226,6 +226,163 @@ static const struct reg_default rt5645_reg[] = {
{ 0xff, 0x6308 },
};
static const struct reg_default rt5650_reg[] = {
{ 0x00, 0x0000 },
{ 0x01, 0xc8c8 },
{ 0x02, 0xc8c8 },
{ 0x03, 0xc8c8 },
{ 0x0a, 0x0002 },
{ 0x0b, 0x2827 },
{ 0x0c, 0xe000 },
{ 0x0d, 0x0000 },
{ 0x0e, 0x0000 },
{ 0x0f, 0x0808 },
{ 0x14, 0x3333 },
{ 0x16, 0x4b00 },
{ 0x18, 0x018b },
{ 0x19, 0xafaf },
{ 0x1a, 0xafaf },
{ 0x1b, 0x0001 },
{ 0x1c, 0x2f2f },
{ 0x1d, 0x2f2f },
{ 0x1e, 0x0000 },
{ 0x20, 0x0000 },
{ 0x27, 0x7060 },
{ 0x28, 0x7070 },
{ 0x29, 0x8080 },
{ 0x2a, 0x5656 },
{ 0x2b, 0x5454 },
{ 0x2c, 0xaaa0 },
{ 0x2d, 0x0000 },
{ 0x2f, 0x1002 },
{ 0x31, 0x5000 },
{ 0x32, 0x0000 },
{ 0x33, 0x0000 },
{ 0x34, 0x0000 },
{ 0x35, 0x0000 },
{ 0x3b, 0x0000 },
{ 0x3c, 0x007f },
{ 0x3d, 0x0000 },
{ 0x3e, 0x007f },
{ 0x3f, 0x0000 },
{ 0x40, 0x001f },
{ 0x41, 0x0000 },
{ 0x42, 0x001f },
{ 0x45, 0x6000 },
{ 0x46, 0x003e },
{ 0x47, 0x003e },
{ 0x48, 0xf807 },
{ 0x4a, 0x0004 },
{ 0x4d, 0x0000 },
{ 0x4e, 0x0000 },
{ 0x4f, 0x01ff },
{ 0x50, 0x0000 },
{ 0x51, 0x0000 },
{ 0x52, 0x01ff },
{ 0x53, 0xf000 },
{ 0x56, 0x0111 },
{ 0x57, 0x0064 },
{ 0x58, 0xef0e },
{ 0x59, 0xf0f0 },
{ 0x5a, 0xef0e },
{ 0x5b, 0xf0f0 },
{ 0x5c, 0xef0e },
{ 0x5d, 0xf0f0 },
{ 0x5e, 0xf000 },
{ 0x5f, 0x0000 },
{ 0x61, 0x0300 },
{ 0x62, 0x0000 },
{ 0x63, 0x00c2 },
{ 0x64, 0x0000 },
{ 0x65, 0x0000 },
{ 0x66, 0x0000 },
{ 0x6a, 0x0000 },
{ 0x6c, 0x0aaa },
{ 0x70, 0x8000 },
{ 0x71, 0x8000 },
{ 0x72, 0x8000 },
{ 0x73, 0x7770 },
{ 0x74, 0x3e00 },
{ 0x75, 0x2409 },
{ 0x76, 0x000a },
{ 0x77, 0x0c00 },
{ 0x78, 0x0000 },
{ 0x79, 0x0123 },
{ 0x7a, 0x0123 },
{ 0x80, 0x0000 },
{ 0x81, 0x0000 },
{ 0x82, 0x0000 },
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
{ 0x8a, 0x0000 },
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0646 },
{ 0x91, 0x0c06 },
{ 0x93, 0x0000 },
{ 0x94, 0x0200 },
{ 0x95, 0x0000 },
{ 0x9a, 0x2184 },
{ 0x9b, 0x010a },
{ 0x9c, 0x0aea },
{ 0x9d, 0x000c },
{ 0x9e, 0x0400 },
{ 0xa0, 0xa0a8 },
{ 0xa1, 0x0059 },
{ 0xa2, 0x0001 },
{ 0xae, 0x6000 },
{ 0xaf, 0x0000 },
{ 0xb0, 0x6000 },
{ 0xb1, 0x0000 },
{ 0xb2, 0x0000 },
{ 0xb3, 0x001f },
{ 0xb4, 0x020c },
{ 0xb5, 0x1f00 },
{ 0xb6, 0x0000 },
{ 0xbb, 0x0000 },
{ 0xbc, 0x0000 },
{ 0xbd, 0x0000 },
{ 0xbe, 0x0000 },
{ 0xbf, 0x3100 },
{ 0xc0, 0x0000 },
{ 0xc1, 0x0000 },
{ 0xc2, 0x0000 },
{ 0xc3, 0x2000 },
{ 0xcd, 0x0000 },
{ 0xce, 0x0000 },
{ 0xcf, 0x1813 },
{ 0xd0, 0x0690 },
{ 0xd1, 0x1c17 },
{ 0xd3, 0xb320 },
{ 0xd4, 0x0000 },
{ 0xd6, 0x0400 },
{ 0xd9, 0x0809 },
{ 0xda, 0x0000 },
{ 0xdb, 0x0003 },
{ 0xdc, 0x0049 },
{ 0xdd, 0x001b },
{ 0xdf, 0x0008 },
{ 0xe0, 0x4000 },
{ 0xe6, 0x8000 },
{ 0xe7, 0x0200 },
{ 0xec, 0xb300 },
{ 0xed, 0x0000 },
{ 0xf0, 0x001f },
{ 0xf1, 0x020c },
{ 0xf2, 0x1f00 },
{ 0xf3, 0x0000 },
{ 0xf4, 0x4000 },
{ 0xf8, 0x0000 },
{ 0xf9, 0x0000 },
{ 0xfa, 0x2060 },
{ 0xfb, 0x4040 },
{ 0xfc, 0x0000 },
{ 0xfd, 0x0002 },
{ 0xfe, 0x10ec },
{ 0xff, 0x6308 },
};
struct rt5645_eq_param_s {
unsigned short reg;
unsigned short val;
@ -572,14 +729,12 @@ static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol,
struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
int ret;
cancel_delayed_work_sync(&rt5645->rcclock_work);
regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PU);
ret = snd_soc_put_volsw(kcontrol, ucontrol);
queue_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work,
mod_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work,
msecs_to_jiffies(200));
return ret;
@ -3322,6 +3477,31 @@ static const struct regmap_config rt5645_regmap = {
.num_ranges = ARRAY_SIZE(rt5645_ranges),
};
static const struct regmap_config rt5650_regmap = {
.reg_bits = 8,
.val_bits = 16,
.use_single_rw = true,
.max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) *
RT5645_PR_SPACING),
.volatile_reg = rt5645_volatile_register,
.readable_reg = rt5645_readable_register,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = rt5650_reg,
.num_reg_defaults = ARRAY_SIZE(rt5650_reg),
.ranges = rt5645_ranges,
.num_ranges = ARRAY_SIZE(rt5645_ranges),
};
static const struct regmap_config temp_regmap = {
.name="nocache",
.reg_bits = 8,
.val_bits = 16,
.use_single_rw = true,
.max_register = RT5645_VENDOR_ID2 + 1,
.cache_type = REGCACHE_NONE,
};
static const struct i2c_device_id rt5645_i2c_id[] = {
{ "rt5645", 0 },
{ "rt5650", 0 },
@ -3338,69 +3518,23 @@ static struct acpi_device_id rt5645_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
#endif
static struct rt5645_platform_data *rt5645_pdata;
static struct rt5645_platform_data strago_platform_data = {
static struct rt5645_platform_data general_platform_data = {
.dmic1_data_pin = RT5645_DMIC1_DISABLE,
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
.jd_mode = 3,
};
static int strago_quirk_cb(const struct dmi_system_id *id)
{
rt5645_pdata = &strago_platform_data;
return 1;
}
static const struct dmi_system_id dmi_platform_intel_braswell[] = {
{
.ident = "Intel Strago",
.callback = strago_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Strago"),
},
},
{
.ident = "Google Celes",
.callback = strago_quirk_cb,
.ident = "Google Chrome",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Celes"),
},
},
{
.ident = "Google Ultima",
.callback = strago_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"),
},
},
{
.ident = "Google Reks",
.callback = strago_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Reks"),
},
},
{
.ident = "Google Edgar",
.callback = strago_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Edgar"),
},
},
{
.ident = "Google Wizpig",
.callback = strago_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Wizpig"),
},
},
{
.ident = "Google Terra",
.callback = strago_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Terra"),
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
},
},
{ }
@ -3413,17 +3547,9 @@ static struct rt5645_platform_data buddy_platform_data = {
.jd_invert = true,
};
static int buddy_quirk_cb(const struct dmi_system_id *id)
{
rt5645_pdata = &buddy_platform_data;
return 1;
}
static struct dmi_system_id dmi_platform_intel_broadwell[] = {
{
.ident = "Chrome Buddy",
.callback = buddy_quirk_cb,
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"),
},
@ -3431,6 +3557,16 @@ static struct dmi_system_id dmi_platform_intel_broadwell[] = {
{ }
};
static bool rt5645_check_dp(struct device *dev)
{
if (device_property_present(dev, "realtek,in2-differential") ||
device_property_present(dev, "realtek,dmic1-data-pin") ||
device_property_present(dev, "realtek,dmic2-data-pin") ||
device_property_present(dev, "realtek,jd-mode"))
return true;
return false;
}
static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
{
@ -3453,6 +3589,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
struct rt5645_priv *rt5645;
int ret, i;
unsigned int val;
struct regmap *regmap;
rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv),
GFP_KERNEL);
@ -3464,11 +3601,12 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt5645->pdata = *pdata;
else if (dmi_check_system(dmi_platform_intel_braswell) ||
dmi_check_system(dmi_platform_intel_broadwell))
rt5645->pdata = *rt5645_pdata;
else
else if (dmi_check_system(dmi_platform_intel_broadwell))
rt5645->pdata = buddy_platform_data;
else if (rt5645_check_dp(&i2c->dev))
rt5645_parse_dt(rt5645, &i2c->dev);
else if (dmi_check_system(dmi_platform_intel_braswell))
rt5645->pdata = general_platform_data;
rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect",
GPIOD_IN);
@ -3478,14 +3616,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
return PTR_ERR(rt5645->gpiod_hp_det);
}
rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
if (IS_ERR(rt5645->regmap)) {
ret = PTR_ERR(rt5645->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++)
rt5645->supplies[i].supply = rt5645_supply_names[i];
@ -3504,13 +3634,22 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
return ret;
}
regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val);
regmap = devm_regmap_init_i2c(i2c, &temp_regmap);
if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap);
dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n",
ret);
return ret;
}
regmap_read(regmap, RT5645_VENDOR_ID2, &val);
switch (val) {
case RT5645_DEVICE_ID:
rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
rt5645->codec_type = CODEC_TYPE_RT5645;
break;
case RT5650_DEVICE_ID:
rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5650_regmap);
rt5645->codec_type = CODEC_TYPE_RT5650;
break;
default:
@ -3521,6 +3660,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
goto err_enable;
}
if (IS_ERR(rt5645->regmap)) {
ret = PTR_ERR(rt5645->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
regmap_write(rt5645->regmap, RT5645_RESET, 0);
ret = regmap_register_patch(rt5645->regmap, init_list,

4223
sound/soc/codecs/rt5659.c Normal file

File diff suppressed because it is too large Load Diff

1819
sound/soc/codecs/rt5659.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -297,8 +297,6 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_HAP_GENE_CTRL2:
case RT5677_PWR_DSP_ST:
case RT5677_PRIV_DATA:
case RT5677_PLL1_CTRL2:
case RT5677_PLL2_CTRL2:
case RT5677_ASRC_22:
case RT5677_ASRC_23:
case RT5677_VAD_CTRL5:
@ -4788,7 +4786,7 @@ static int rt5677_remove(struct snd_soc_codec *codec)
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
gpiod_set_value_cansleep(rt5677->reset_pin, 0);
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
return 0;
}
@ -4803,7 +4801,7 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
regcache_mark_dirty(rt5677->regmap);
gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
gpiod_set_value_cansleep(rt5677->reset_pin, 0);
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
}
return 0;
@ -4814,8 +4812,11 @@ static int rt5677_resume(struct snd_soc_codec *codec)
struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
if (!rt5677->dsp_vad_en) {
rt5677->pll_src = 0;
rt5677->pll_in = 0;
rt5677->pll_out = 0;
gpiod_set_value_cansleep(rt5677->pow_ldo2, 1);
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
gpiod_set_value_cansleep(rt5677->reset_pin, 0);
if (rt5677->pow_ldo2 || rt5677->reset_pin)
msleep(10);
@ -5160,7 +5161,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
return ret;
}
rt5677->reset_pin = devm_gpiod_get_optional(&i2c->dev,
"realtek,reset", GPIOD_OUT_HIGH);
"realtek,reset", GPIOD_OUT_LOW);
if (IS_ERR(rt5677->reset_pin)) {
ret = PTR_ERR(rt5677->reset_pin);
dev_err(&i2c->dev, "Failed to request RESET: %d\n", ret);

View File

@ -309,7 +309,7 @@ static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = {
.count = ARRAY_SIZE(ssm2518_rates_12288000),
};
static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
static int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
unsigned int rate)
{
const unsigned int *sysclks = NULL;

View File

@ -573,6 +573,33 @@ static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \
SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
#define WM5110_RXANC_INPUT_ROUTES(widget, name) \
{ widget, NULL, name " NG Mux" }, \
{ name " NG Internal", NULL, "RXANC NG Clock" }, \
{ name " NG Internal", NULL, name " Channel" }, \
{ name " NG External", NULL, "RXANC NG External Clock" }, \
{ name " NG External", NULL, name " Channel" }, \
{ name " NG Mux", "None", name " Channel" }, \
{ name " NG Mux", "Internal", name " NG Internal" }, \
{ name " NG Mux", "External", name " NG External" }, \
{ name " Channel", "Left", name " Left Input" }, \
{ name " Channel", "Combine", name " Left Input" }, \
{ name " Channel", "Right", name " Right Input" }, \
{ name " Channel", "Combine", name " Right Input" }, \
{ name " Left Input", "IN1", "IN1L PGA" }, \
{ name " Right Input", "IN1", "IN1R PGA" }, \
{ name " Left Input", "IN2", "IN2L PGA" }, \
{ name " Right Input", "IN2", "IN2R PGA" }, \
{ name " Left Input", "IN3", "IN3L PGA" }, \
{ name " Right Input", "IN3", "IN3R PGA" }, \
{ name " Left Input", "IN4", "IN4L PGA" }, \
{ name " Right Input", "IN4", "IN4R PGA" }
#define WM5110_RXANC_OUTPUT_ROUTES(widget, name) \
{ widget, NULL, name " ANC Source" }, \
{ name " ANC Source", "RXANCL", "RXANCL" }, \
{ name " ANC Source", "RXANCR", "RXANCR" }
static const struct snd_kcontrol_new wm5110_snd_controls[] = {
SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
@ -637,6 +664,15 @@ SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R,
SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
SND_SOC_BYTES("RXANC Coefficients", ARIZONA_ANC_COEFF_START,
ARIZONA_ANC_COEFF_END - ARIZONA_ANC_COEFF_START + 1),
SND_SOC_BYTES("RXANCL Config", ARIZONA_FCL_FILTER_CONTROL, 1),
SND_SOC_BYTES("RXANCL Coefficients", ARIZONA_FCL_COEFF_START,
ARIZONA_FCL_COEFF_END - ARIZONA_FCL_COEFF_START + 1),
SND_SOC_BYTES("RXANCR Config", ARIZONA_FCR_FILTER_CONTROL, 1),
SND_SOC_BYTES("RXANCR Coefficients", ARIZONA_FCR_COEFF_START,
ARIZONA_FCR_COEFF_END - ARIZONA_FCR_COEFF_START + 1),
ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
@ -993,6 +1029,31 @@ static const struct soc_enum wm5110_aec_loopback =
static const struct snd_kcontrol_new wm5110_aec_loopback_mux =
SOC_DAPM_ENUM("AEC Loopback", wm5110_aec_loopback);
static const struct snd_kcontrol_new wm5110_anc_input_mux[] = {
SOC_DAPM_ENUM("RXANCL Input", arizona_anc_input_src[0]),
SOC_DAPM_ENUM("RXANCL Channel", arizona_anc_input_src[1]),
SOC_DAPM_ENUM("RXANCR Input", arizona_anc_input_src[2]),
SOC_DAPM_ENUM("RXANCR Channel", arizona_anc_input_src[3]),
};
static const struct snd_kcontrol_new wm5110_anc_ng_mux =
SOC_DAPM_ENUM("RXANC NG Source", arizona_anc_ng_enum);
static const struct snd_kcontrol_new wm5110_output_anc_src[] = {
SOC_DAPM_ENUM("HPOUT1L ANC Source", arizona_output_anc_src[0]),
SOC_DAPM_ENUM("HPOUT1R ANC Source", arizona_output_anc_src[1]),
SOC_DAPM_ENUM("HPOUT2L ANC Source", arizona_output_anc_src[2]),
SOC_DAPM_ENUM("HPOUT2R ANC Source", arizona_output_anc_src[3]),
SOC_DAPM_ENUM("HPOUT3L ANC Source", arizona_output_anc_src[4]),
SOC_DAPM_ENUM("HPOUT3R ANC Source", arizona_output_anc_src[5]),
SOC_DAPM_ENUM("SPKOUTL ANC Source", arizona_output_anc_src[6]),
SOC_DAPM_ENUM("SPKOUTR ANC Source", arizona_output_anc_src[7]),
SOC_DAPM_ENUM("SPKDAT1L ANC Source", arizona_output_anc_src[8]),
SOC_DAPM_ENUM("SPKDAT1R ANC Source", arizona_output_anc_src[9]),
SOC_DAPM_ENUM("SPKDAT2L ANC Source", arizona_output_anc_src[10]),
SOC_DAPM_ENUM("SPKDAT2R ANC Source", arizona_output_anc_src[11]),
};
static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU),
@ -1183,6 +1244,65 @@ SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
&wm5110_aec_loopback_mux),
SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM,
ARIZONA_EXT_NG_SEL_SET_SHIFT, 0, arizona_anc_ev,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM,
ARIZONA_CLK_NG_ENA_SET_SHIFT, 0, arizona_anc_ev,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0,
&wm5110_anc_input_mux[0]),
SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0,
&wm5110_anc_input_mux[0]),
SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0,
&wm5110_anc_input_mux[1]),
SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux),
SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0,
&wm5110_anc_input_mux[2]),
SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0,
&wm5110_anc_input_mux[2]),
SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0,
&wm5110_anc_input_mux[3]),
SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux),
SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, ARIZONA_CLK_L_ENA_SET_SHIFT,
0, NULL, 0, arizona_anc_ev,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, ARIZONA_CLK_R_ENA_SET_SHIFT,
0, NULL, 0, arizona_anc_ev,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[0]),
SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[1]),
SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[2]),
SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[3]),
SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[4]),
SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[5]),
SND_SOC_DAPM_MUX("SPKOUTL ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[6]),
SND_SOC_DAPM_MUX("SPKOUTR ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[7]),
SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[8]),
SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[9]),
SND_SOC_DAPM_MUX("SPKDAT2L ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[10]),
SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 0,
&wm5110_output_anc_src[11]),
SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
@ -1688,6 +1808,9 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "Slim2 Capture", NULL, "SYSCLK" },
{ "Slim3 Capture", NULL, "SYSCLK" },
{ "Voice Control DSP", NULL, "DSP3" },
{ "Voice Control DSP", NULL, "SYSCLK" },
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@ -1836,6 +1959,22 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "SPKDAT2L", NULL, "OUT6L" },
{ "SPKDAT2R", NULL, "OUT6R" },
WM5110_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"),
WM5110_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"),
WM5110_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"),
WM5110_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"),
WM5110_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"),
WM5110_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"),
WM5110_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"),
WM5110_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"),
WM5110_RXANC_OUTPUT_ROUTES("OUT4L", "SPKOUTL"),
WM5110_RXANC_OUTPUT_ROUTES("OUT4R", "SPKOUTR"),
WM5110_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"),
WM5110_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"),
WM5110_RXANC_OUTPUT_ROUTES("OUT6L", "SPKDAT2L"),
WM5110_RXANC_OUTPUT_ROUTES("OUT6R", "SPKDAT2R"),
{ "MICSUPP", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
@ -1994,8 +2133,48 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
},
.ops = &arizona_simple_dai_ops,
},
{
.name = "wm5110-cpu-voicectrl",
.capture = {
.stream_name = "Voice Control CPU",
.channels_min = 1,
.channels_max = 1,
.rates = WM5110_RATES,
.formats = WM5110_FORMATS,
},
.compress_new = snd_soc_new_compress,
},
{
.name = "wm5110-dsp-voicectrl",
.capture = {
.stream_name = "Voice Control DSP",
.channels_min = 1,
.channels_max = 1,
.rates = WM5110_RATES,
.formats = WM5110_FORMATS,
},
},
};
static int wm5110_open(struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
struct arizona *arizona = priv->core.arizona;
int n_adsp;
if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) {
n_adsp = 2;
} else {
dev_err(arizona->dev,
"No suitable compressed stream for DAI '%s'\n",
rtd->codec_dai->name);
return -EINVAL;
}
return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
}
static int wm5110_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
@ -2086,6 +2265,18 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
.num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes),
};
static struct snd_compr_ops wm5110_compr_ops = {
.open = wm5110_open,
.free = wm_adsp_compr_free,
.set_params = wm_adsp_compr_set_params,
.get_caps = wm_adsp_compr_get_caps,
.trigger = wm_adsp_compr_trigger,
};
static struct snd_soc_platform_driver wm5110_compr_platform = {
.compr_ops = &wm5110_compr_ops,
};
static int wm5110_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@ -2146,8 +2337,21 @@ static int wm5110_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
goto error;
}
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
wm5110_dai, ARRAY_SIZE(wm5110_dai));
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
snd_soc_unregister_platform(&pdev->dev);
}
error:
return ret;
}
static int wm5110_remove(struct platform_device *pdev)

View File

@ -1804,7 +1804,7 @@ static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
regmap_read(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, &reg);
return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT;
return !!((reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT);
}
static int wm8903_gpio_direction_out(struct gpio_chip *chip,

View File

@ -312,7 +312,7 @@ static bool wm8904_readable_register(struct device *dev, unsigned int reg)
case WM8904_FLL_NCO_TEST_1:
return true;
default:
return true;
return false;
}
}

View File

@ -131,7 +131,7 @@ static const struct reg_default wm8962_reg[] = {
{ 15, 0x6243 }, /* R15 - Software Reset */
{ 17, 0x007B }, /* R17 - ALC1 */
{ 18, 0x0000 }, /* R18 - ALC2 */
{ 19, 0x1C32 }, /* R19 - ALC3 */
{ 20, 0x3200 }, /* R20 - Noise Gate */
{ 21, 0x00C0 }, /* R21 - Left ADC volume */
@ -794,7 +794,6 @@ static bool wm8962_volatile_register(struct device *dev, unsigned int reg)
case WM8962_CLOCKING1:
case WM8962_CLOCKING2:
case WM8962_SOFTWARE_RESET:
case WM8962_ALC2:
case WM8962_THERMAL_SHUTDOWN_STATUS:
case WM8962_ADDITIONAL_CONTROL_4:
case WM8962_DC_SERVO_6:

View File

@ -632,9 +632,16 @@ static const struct i2c_device_id wm8974_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
static const struct of_device_id wm8974_of_match[] = {
{ .compatible = "wlf,wm8974", },
{ }
};
MODULE_DEVICE_TABLE(of, wm8974_of_match);
static struct i2c_driver wm8974_i2c_driver = {
.driver = {
.name = "wm8974",
.of_match_table = wm8974_of_match,
},
.probe = wm8974_i2c_probe,
.remove = wm8974_i2c_remove,

View File

@ -199,20 +199,20 @@ static const char * const wm8998_inmux_texts[] = {
"B",
};
static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum,
ARIZONA_ADC_DIGITAL_VOLUME_1L,
ARIZONA_IN1L_SRC_SHIFT,
wm8998_inmux_texts);
static SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum,
ARIZONA_ADC_DIGITAL_VOLUME_1L,
ARIZONA_IN1L_SRC_SHIFT,
wm8998_inmux_texts);
static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum,
ARIZONA_ADC_DIGITAL_VOLUME_1R,
ARIZONA_IN1R_SRC_SHIFT,
wm8998_inmux_texts);
static SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum,
ARIZONA_ADC_DIGITAL_VOLUME_1R,
ARIZONA_IN1R_SRC_SHIFT,
wm8998_inmux_texts);
static const SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum,
ARIZONA_ADC_DIGITAL_VOLUME_2L,
ARIZONA_IN2L_SRC_SHIFT,
wm8998_inmux_texts);
static SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum,
ARIZONA_ADC_DIGITAL_VOLUME_2L,
ARIZONA_IN2L_SRC_SHIFT,
wm8998_inmux_texts);
static const struct snd_kcontrol_new wm8998_in1mux[2] = {
SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum,
@ -522,17 +522,17 @@ static const unsigned int wm8998_aec_loopback_values[] = {
0, 1, 2, 3, 4, 6, 7, 8, 9,
};
static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback,
ARIZONA_DAC_AEC_CONTROL_1,
ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
wm8998_aec_loopback_texts,
wm8998_aec_loopback_values);
static SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback,
ARIZONA_DAC_AEC_CONTROL_1,
ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
wm8998_aec_loopback_texts,
wm8998_aec_loopback_values);
static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback,
ARIZONA_DAC_AEC_CONTROL_2,
ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
wm8998_aec_loopback_texts,
wm8998_aec_loopback_values);
static SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback,
ARIZONA_DAC_AEC_CONTROL_2,
ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
wm8998_aec_loopback_texts,
wm8998_aec_loopback_values);
static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = {
SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback),

View File

@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
@ -39,34 +40,6 @@ struct wm9713_priv {
struct mutex lock;
};
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg);
static int ac97_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int val);
/*
* WM9713 register cache
* Reg 0x3c bit 15 is used by touch driver.
*/
static const u16 wm9713_reg[] = {
0x6174, 0x8080, 0x8080, 0x8080,
0xc880, 0xe808, 0xe808, 0x0808,
0x00da, 0x8000, 0xd600, 0xaaa0,
0xaaa0, 0xaaa0, 0x0000, 0x0000,
0x0f0f, 0x0040, 0x0000, 0x7f00,
0x0405, 0x0410, 0xbb80, 0xbb80,
0x0000, 0xbb80, 0x0000, 0x4523,
0x0000, 0x2000, 0x7eff, 0xffff,
0x0000, 0x0000, 0x0080, 0x0000,
0x0000, 0x0000, 0xfffe, 0xffff,
0x0000, 0x0000, 0x0000, 0xfffe,
0x4000, 0x0000, 0x0000, 0x0000,
0xb032, 0x3e00, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0006,
0x0001, 0x0000, 0x574d, 0x4c13,
};
#define HPL_MIXER 0
#define HPR_MIXER 1
@ -220,18 +193,15 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
u16 status, rate;
if (WARN_ON(event != SND_SOC_DAPM_PRE_PMD))
return -EINVAL;
/* Gracefully shut down the voice interface. */
status = ac97_read(codec, AC97_EXTENDED_MID) | 0x1000;
rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0200);
schedule_timeout_interruptible(msecs_to_jiffies(1));
ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
ac97_write(codec, AC97_EXTENDED_MID, status);
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0f00);
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x1000, 0x1000);
return 0;
}
@ -674,40 +644,98 @@ static const struct snd_soc_dapm_route wm9713_audio_map[] = {
{"Capture Mono Mux", "Right", "Right Capture Source"},
};
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
static bool wm9713_readable_reg(struct device *dev, unsigned int reg)
{
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_CD)
return soc_ac97_ops->read(wm9713->ac97, reg);
else {
reg = reg >> 1;
if (reg >= (ARRAY_SIZE(wm9713_reg)))
return -EIO;
return cache[reg];
switch (reg) {
case AC97_RESET ... AC97_PCM_SURR_DAC_RATE:
case AC97_PCM_LR_ADC_RATE:
case AC97_CENTER_LFE_MASTER:
case AC97_SPDIF ... AC97_LINE1_LEVEL:
case AC97_GPIO_CFG ... 0x5c:
case AC97_CODEC_CLASS_REV ... AC97_PCI_SID:
case 0x74 ... AC97_VENDOR_ID2:
return true;
default:
return false;
}
}
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
static bool wm9713_writeable_reg(struct device *dev, unsigned int reg)
{
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
soc_ac97_ops->write(wm9713->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9713_reg)))
cache[reg] = val;
return 0;
switch (reg) {
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
return false;
default:
return wm9713_readable_reg(dev, reg);
}
}
static const struct reg_default wm9713_reg_defaults[] = {
{ 0x02, 0x8080 }, /* Speaker Output Volume */
{ 0x04, 0x8080 }, /* Headphone Output Volume */
{ 0x06, 0x8080 }, /* Out3/OUT4 Volume */
{ 0x08, 0xc880 }, /* Mono Volume */
{ 0x0a, 0xe808 }, /* LINEIN Volume */
{ 0x0c, 0xe808 }, /* DAC PGA Volume */
{ 0x0e, 0x0808 }, /* MIC PGA Volume */
{ 0x10, 0x00da }, /* MIC Routing Control */
{ 0x12, 0x8000 }, /* Record PGA Volume */
{ 0x14, 0xd600 }, /* Record Routing */
{ 0x16, 0xaaa0 }, /* PCBEEP Volume */
{ 0x18, 0xaaa0 }, /* VxDAC Volume */
{ 0x1a, 0xaaa0 }, /* AUXDAC Volume */
{ 0x1c, 0x0000 }, /* Output PGA Mux */
{ 0x1e, 0x0000 }, /* DAC 3D control */
{ 0x20, 0x0f0f }, /* DAC Tone Control*/
{ 0x22, 0x0040 }, /* MIC Input Select & Bias */
{ 0x24, 0x0000 }, /* Output Volume Mapping & Jack */
{ 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/
{ 0x28, 0x0405 }, /* Extended Audio ID */
{ 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */
{ 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */
{ 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */
{ 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */
{ 0x36, 0x4523 }, /* PCM codec control */
{ 0x3a, 0x2000 }, /* SPDIF control */
{ 0x3c, 0xfdff }, /* Powerdown 1 */
{ 0x3e, 0xffff }, /* Powerdown 2 */
{ 0x40, 0x0000 }, /* General Purpose */
{ 0x42, 0x0000 }, /* Fast Power-Up Control */
{ 0x44, 0x0080 }, /* MCLK/PLL Control */
{ 0x46, 0x0000 }, /* MCLK/PLL Control */
{ 0x4c, 0xfffe }, /* GPIO Pin Configuration */
{ 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */
{ 0x50, 0x0000 }, /* GPIO Pin Sticky */
{ 0x52, 0x0000 }, /* GPIO Pin Wake-Up */
/* GPIO Pin Status */
{ 0x56, 0xfffe }, /* GPIO Pin Sharing */
{ 0x58, 0x4000 }, /* GPIO PullUp/PullDown */
{ 0x5a, 0x0000 }, /* Additional Functions 1 */
{ 0x5c, 0x0000 }, /* Additional Functions 2 */
{ 0x60, 0xb032 }, /* ALC Control */
{ 0x62, 0x3e00 }, /* ALC / Noise Gate Control */
{ 0x64, 0x0000 }, /* AUXDAC input control */
{ 0x74, 0x0000 }, /* Digitiser Reg 1 */
{ 0x76, 0x0006 }, /* Digitiser Reg 2 */
{ 0x78, 0x0001 }, /* Digitiser Reg 3 */
{ 0x7a, 0x0000 }, /* Digitiser Read Back */
};
static const struct regmap_config wm9713_regmap_config = {
.reg_bits = 16,
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = wm9713_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults),
.volatile_reg = regmap_ac97_default_volatile,
.readable_reg = wm9713_readable_reg,
.writeable_reg = wm9713_writeable_reg,
};
/* PLL divisors */
struct _pll_div {
u32 divsel:1;
@ -793,10 +821,8 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
/* turn PLL off ? */
if (freq_in == 0) {
/* disable PLL power and select ext source */
reg = ac97_read(codec, AC97_HANDSET_RATE);
ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080);
reg = ac97_read(codec, AC97_EXTENDED_MID);
ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200);
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0080);
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0200);
wm9713->pll_in = 0;
return 0;
}
@ -806,7 +832,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
if (pll_div.k == 0) {
reg = (pll_div.n << 12) | (pll_div.lf << 11) |
(pll_div.divsel << 9) | (pll_div.divctl << 8);
ac97_write(codec, AC97_LINE1_LEVEL, reg);
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
} else {
/* write the fractional k to the reg 0x46 pages */
reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) |
@ -814,33 +840,31 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
/* K [21:20] */
reg = reg2 | (0x5 << 4) | (pll_div.k >> 20);
ac97_write(codec, AC97_LINE1_LEVEL, reg);
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
/* K [19:16] */
reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf);
ac97_write(codec, AC97_LINE1_LEVEL, reg);
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
/* K [15:12] */
reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf);
ac97_write(codec, AC97_LINE1_LEVEL, reg);
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
/* K [11:8] */
reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf);
ac97_write(codec, AC97_LINE1_LEVEL, reg);
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
/* K [7:4] */
reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf);
ac97_write(codec, AC97_LINE1_LEVEL, reg);
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */
ac97_write(codec, AC97_LINE1_LEVEL, reg);
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
}
/* turn PLL on and select as source */
reg = ac97_read(codec, AC97_EXTENDED_MID);
ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff);
reg = ac97_read(codec, AC97_HANDSET_RATE);
ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f);
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0000);
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0000);
wm9713->pll_in = freq_in;
/* wait 10ms AC97 link frames for the link to stabilise */
@ -863,10 +887,10 @@ static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai,
int tristate)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff;
if (tristate)
ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
0x6000, 0x0000);
return 0;
}
@ -879,36 +903,30 @@ static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
switch (div_id) {
case WM9713_PCMCLK_DIV:
reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff;
ac97_write(codec, AC97_HANDSET_RATE, reg | div);
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, div);
break;
case WM9713_CLKA_MULT:
reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd;
ac97_write(codec, AC97_HANDSET_RATE, reg | div);
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0002, div);
break;
case WM9713_CLKB_MULT:
reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb;
ac97_write(codec, AC97_HANDSET_RATE, reg | div);
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0004, div);
break;
case WM9713_HIFI_DIV:
reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff;
ac97_write(codec, AC97_HANDSET_RATE, reg | div);
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x7000, div);
break;
case WM9713_PCMBCLK_DIV:
reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff;
ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div);
snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, 0x0e00, div);
break;
case WM9713_PCMCLK_PLL_DIV:
reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80;
ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x60 | div);
snd_soc_update_bits(codec, AC97_LINE1_LEVEL,
0x007f, div | 0x60);
break;
case WM9713_HIFI_PLL_DIV:
reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80;
ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x70 | div);
snd_soc_update_bits(codec, AC97_LINE1_LEVEL,
0x007f, div | 0x70);
break;
default:
return -EINVAL;
@ -921,7 +939,7 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffc5;
u16 gpio = snd_soc_read(codec, AC97_GPIO_CFG) & 0xffc5;
u16 reg = 0x8000;
/* clock masters */
@ -974,8 +992,8 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
break;
}
ac97_write(codec, AC97_GPIO_CFG, gpio);
ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
snd_soc_write(codec, AC97_GPIO_CFG, gpio);
snd_soc_write(codec, AC97_CENTER_LFE_MASTER, reg);
return 0;
}
@ -984,24 +1002,24 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
/* enable PCM interface in master mode */
switch (params_width(params)) {
case 16:
break;
case 20:
reg |= 0x0004;
snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
0x000c, 0x0004);
break;
case 24:
reg |= 0x0008;
snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
0x000c, 0x0008);
break;
case 32:
reg |= 0x000c;
snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
0x000c, 0x000c);
break;
}
/* enable PCM interface in master mode */
ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
return 0;
}
@ -1011,17 +1029,15 @@ static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
struct snd_pcm_runtime *runtime = substream->runtime;
int reg;
u16 vra;
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = AC97_PCM_FRONT_DAC_RATE;
else
reg = AC97_PCM_LR_ADC_RATE;
return ac97_write(codec, reg, runtime->rate);
return snd_soc_write(codec, reg, runtime->rate);
}
static int ac97_aux_prepare(struct snd_pcm_substream *substream,
@ -1029,17 +1045,14 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream,
{
struct snd_soc_codec *codec = dai->codec;
struct snd_pcm_runtime *runtime = substream->runtime;
u16 vra, xsle;
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
xsle = ac97_read(codec, AC97_PCI_SID);
ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001);
snd_soc_update_bits(codec, AC97_PCI_SID, 0x8000, 0x8000);
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
return snd_soc_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
}
#define WM9713_RATES (SNDRV_PCM_RATE_8000 | \
@ -1128,27 +1141,23 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
static int wm9713_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
u16 reg;
switch (level) {
case SND_SOC_BIAS_ON:
/* enable thermal shutdown */
reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
ac97_write(codec, AC97_EXTENDED_MID, reg);
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0xe400, 0x0000);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* enable master bias and vmid */
reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
ac97_write(codec, AC97_EXTENDED_MID, reg);
ac97_write(codec, AC97_POWERDOWN, 0x0000);
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0xc400, 0x0000);
snd_soc_write(codec, AC97_POWERDOWN, 0x0000);
break;
case SND_SOC_BIAS_OFF:
/* disable everything including AC link */
ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
ac97_write(codec, AC97_POWERDOWN, 0xffff);
snd_soc_write(codec, AC97_EXTENDED_MID, 0xffff);
snd_soc_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
snd_soc_write(codec, AC97_POWERDOWN, 0xffff);
break;
}
return 0;
@ -1156,16 +1165,14 @@ static int wm9713_set_bias_level(struct snd_soc_codec *codec,
static int wm9713_soc_suspend(struct snd_soc_codec *codec)
{
u16 reg;
/* Disable everything except touchpanel - that will be handled
* by the touch driver and left disabled if touch is not in
* use. */
reg = ac97_read(codec, AC97_EXTENDED_MID);
ac97_write(codec, AC97_EXTENDED_MID, reg | 0x7fff);
ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
ac97_write(codec, AC97_POWERDOWN, 0x6f00);
ac97_write(codec, AC97_POWERDOWN, 0xffff);
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x7fff,
0x7fff);
snd_soc_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
snd_soc_write(codec, AC97_POWERDOWN, 0x6f00);
snd_soc_write(codec, AC97_POWERDOWN, 0xffff);
return 0;
}
@ -1173,8 +1180,7 @@ static int wm9713_soc_suspend(struct snd_soc_codec *codec)
static int wm9713_soc_resume(struct snd_soc_codec *codec)
{
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
int i, ret;
u16 *cache = codec->reg_cache;
int ret;
ret = snd_ac97_reset(wm9713->ac97, true, WM9713_VENDOR_ID,
WM9713_VENDOR_ID_MASK);
@ -1189,12 +1195,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
/* only synchronise the codec if warm reset failed */
if (ret == 0) {
for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i += 2) {
if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
i == AC97_EXTENDED_MSTATUS || i > 0x66)
continue;
soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]);
}
regcache_mark_dirty(codec->component.regmap);
snd_soc_cache_sync(codec);
}
return ret;
@ -1203,16 +1205,23 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
static int wm9713_soc_probe(struct snd_soc_codec *codec)
{
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
int reg;
struct regmap *regmap;
wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID,
WM9713_VENDOR_ID_MASK);
if (IS_ERR(wm9713->ac97))
return PTR_ERR(wm9713->ac97);
regmap = devm_regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config);
if (IS_ERR(regmap)) {
snd_soc_free_ac97_codec(wm9713->ac97);
return PTR_ERR(regmap);
}
snd_soc_codec_init_regmap(codec, regmap);
/* unmute the adc - move to kcontrol */
reg = ac97_read(codec, AC97_CD) & 0x7fff;
ac97_write(codec, AC97_CD, reg);
snd_soc_update_bits(codec, AC97_CD, 0x7fff, 0x0000);
return 0;
}
@ -1221,6 +1230,7 @@ static int wm9713_soc_remove(struct snd_soc_codec *codec)
{
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
snd_soc_codec_exit_regmap(codec);
snd_soc_free_ac97_codec(wm9713->ac97);
return 0;
}
@ -1230,13 +1240,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
.remove = wm9713_soc_remove,
.suspend = wm9713_soc_suspend,
.resume = wm9713_soc_resume,
.read = ac97_read,
.write = ac97_write,
.set_bias_level = wm9713_set_bias_level,
.reg_cache_size = ARRAY_SIZE(wm9713_reg),
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9713_reg,
.controls = wm9713_snd_ac97_controls,
.num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/compress_driver.h>
#include "wmfw.h"
@ -30,6 +31,9 @@ struct wm_adsp_alg_region {
unsigned int base;
};
struct wm_adsp_compr;
struct wm_adsp_compr_buf;
struct wm_adsp {
const char *part;
int num;
@ -45,8 +49,8 @@ struct wm_adsp {
struct list_head alg_regions;
int fw_id;
int fw_id_version;
unsigned int fw_id;
unsigned int fw_id_version;
const struct wm_adsp_region *mem;
int num_mems;
@ -59,9 +63,13 @@ struct wm_adsp {
struct work_struct boot_work;
struct wm_adsp_compr *compr;
struct wm_adsp_compr_buf *buffer;
struct mutex pwr_lock;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
struct mutex debugfs_lock;
char *wmfw_file_name;
char *bin_file_name;
#endif
@ -96,4 +104,13 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
extern int wm_adsp_compr_open(struct wm_adsp *dsp,
struct snd_compr_stream *stream);
extern int wm_adsp_compr_free(struct snd_compr_stream *stream);
extern int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
struct snd_compr_params *params);
extern int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
struct snd_compr_caps *caps);
extern int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
#endif

View File

@ -18,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <sound/designware_i2s.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@ -93,7 +94,12 @@ struct dw_i2s_dev {
struct clk *clk;
int active;
unsigned int capability;
unsigned int quirks;
unsigned int i2s_reg_comp1;
unsigned int i2s_reg_comp2;
struct device *dev;
u32 ccr;
u32 xfer_resolution;
/* data related to DMA transfers b/w i2s and DMAC */
union dw_i2s_snd_dma_data play_dma_data;
@ -213,31 +219,58 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream,
return 0;
}
static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
{
u32 ch_reg, irq;
struct i2s_clk_config_data *config = &dev->config;
i2s_disable_channels(dev, stream);
for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
i2s_write_reg(dev->i2s_base, TCR(ch_reg),
dev->xfer_resolution);
i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
} else {
i2s_write_reg(dev->i2s_base, RCR(ch_reg),
dev->xfer_resolution);
i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
}
}
}
static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
struct i2s_clk_config_data *config = &dev->config;
u32 ccr, xfer_resolution, ch_reg, irq;
int ret;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
config->data_width = 16;
ccr = 0x00;
xfer_resolution = 0x02;
dev->ccr = 0x00;
dev->xfer_resolution = 0x02;
break;
case SNDRV_PCM_FORMAT_S24_LE:
config->data_width = 24;
ccr = 0x08;
xfer_resolution = 0x04;
dev->ccr = 0x08;
dev->xfer_resolution = 0x04;
break;
case SNDRV_PCM_FORMAT_S32_LE:
config->data_width = 32;
ccr = 0x10;
xfer_resolution = 0x05;
dev->ccr = 0x10;
dev->xfer_resolution = 0x05;
break;
default:
@ -258,27 +291,9 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
i2s_disable_channels(dev, substream->stream);
dw_i2s_config(dev, substream->stream);
for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
i2s_write_reg(dev->i2s_base, TCR(ch_reg),
xfer_resolution);
i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
} else {
i2s_write_reg(dev->i2s_base, RCR(ch_reg),
xfer_resolution);
i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
}
}
i2s_write_reg(dev->i2s_base, CCR, ccr);
i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
config->sample_rate = params_rate(params);
@ -394,6 +409,23 @@ static const struct snd_soc_component_driver dw_i2s_component = {
};
#ifdef CONFIG_PM
static int dw_i2s_runtime_suspend(struct device *dev)
{
struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
if (dw_dev->capability & DW_I2S_MASTER)
clk_disable(dw_dev->clk);
return 0;
}
static int dw_i2s_runtime_resume(struct device *dev)
{
struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
if (dw_dev->capability & DW_I2S_MASTER)
clk_enable(dw_dev->clk);
return 0;
}
static int dw_i2s_suspend(struct snd_soc_dai *dai)
{
@ -410,6 +442,11 @@ static int dw_i2s_resume(struct snd_soc_dai *dai)
if (dev->capability & DW_I2S_MASTER)
clk_enable(dev->clk);
if (dai->playback_active)
dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK);
if (dai->capture_active)
dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE);
return 0;
}
@ -459,8 +496,8 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
* Read component parameter registers to extract
* the I2S block's configuration.
*/
u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2);
u32 idx;
if (COMP1_TX_ENABLED(comp1)) {
@ -503,7 +540,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev,
struct resource *res,
const struct i2s_platform_data *pdata)
{
u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
u32 idx = COMP1_APB_DATA_WIDTH(comp1);
int ret;
@ -607,6 +644,14 @@ static int dw_i2s_probe(struct platform_device *pdev)
if (pdata) {
dev->capability = pdata->cap;
clk_id = NULL;
dev->quirks = pdata->quirks;
if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) {
dev->i2s_reg_comp1 = pdata->i2s_reg_comp1;
dev->i2s_reg_comp2 = pdata->i2s_reg_comp2;
} else {
dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
}
ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
} else {
clk_id = "i2sclk";
@ -649,7 +694,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
goto err_clk_disable;
}
}
pm_runtime_enable(&pdev->dev);
return 0;
err_clk_disable:
@ -665,6 +710,7 @@ static int dw_i2s_remove(struct platform_device *pdev)
if (dev->capability & DW_I2S_MASTER)
clk_disable_unprepare(dev->clk);
pm_runtime_disable(&pdev->dev);
return 0;
}
@ -677,12 +723,17 @@ static const struct of_device_id dw_i2s_of_match[] = {
MODULE_DEVICE_TABLE(of, dw_i2s_of_match);
#endif
static const struct dev_pm_ops dwc_pm_ops = {
SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL)
};
static struct platform_driver dw_i2s_driver = {
.probe = dw_i2s_probe,
.remove = dw_i2s_remove,
.driver = {
.name = "designware-i2s",
.of_match_table = of_match_ptr(dw_i2s_of_match),
.pm = &dwc_pm_ops,
},
};

View File

@ -107,6 +107,13 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"CPU-Capture", NULL, "Capture"},
};
static const struct snd_soc_dapm_route audio_map_ac97[] = {
{"AC97 Playback", NULL, "ASRC-Playback"},
{"Playback", NULL, "AC97 Playback"},
{"ASRC-Capture", NULL, "AC97 Capture"},
{"AC97 Capture", NULL, "Capture"},
};
/* Add all possible widgets into here without being redundant */
static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
SND_SOC_DAPM_LINE("Line Out Jack", NULL),
@ -417,14 +424,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
{
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
struct snd_soc_pcm_runtime *rtd = list_first_entry(
&card->rtd_list, struct snd_soc_pcm_runtime, list);
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct codec_priv *codec_priv = &priv->codec_priv;
struct device *dev = card->dev;
int ret;
if (fsl_asoc_card_is_ac97(priv)) {
#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
struct snd_soc_codec *codec = card->rtd[0].codec;
struct snd_soc_codec *codec = rtd->codec;
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
/*
@ -577,7 +586,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->card.dev = &pdev->dev;
priv->card.name = priv->name;
priv->card.dai_link = priv->dai_link;
priv->card.dapm_routes = audio_map;
priv->card.dapm_routes = fsl_asoc_card_is_ac97(priv) ?
audio_map_ac97 : audio_map;
priv->card.late_probe = fsl_asoc_card_late_probe;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;

View File

@ -31,21 +31,21 @@
dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
/* Sample rates are aligned with that defined in pcm.h file */
static const u8 process_option[][8][2] = {
/* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
{{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */
{{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */
{{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */
{{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */
{{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */
{{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */
{{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */
{{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */
{{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */
{{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */
{{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */
{{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */
{{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */
static const u8 process_option[][12][2] = {
/* 8kHz 11.025kHz 16kHz 22.05kHz 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
{{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */
{{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */
{{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */
{{1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */
{{1, 2}, {1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */
{{1, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */
{{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */
{{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */
{{2, 2}, {2, 2}, {2, 2}, {2, 1}, {1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */
{{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */
{{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */
{{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */
{{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */
};
/* Corresponding to process_option */
@ -55,7 +55,7 @@ static int supported_input_rate[] = {
};
static int supported_asrc_rate[] = {
32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
};
/**
@ -286,6 +286,13 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
return -EINVAL;
}
if ((outrate > 8000 && outrate < 30000) &&
(outrate/inrate > 24 || inrate/outrate > 8)) {
pair_err("exceed supported ratio range [1/24, 8] for \
inrate/outrate: %d/%d\n", inrate, outrate);
return -EINVAL;
}
/* Validate input and output clock sources */
clk_index[IN] = clk_map[IN][config->inclk];
clk_index[OUT] = clk_map[OUT][config->outclk];
@ -447,7 +454,7 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
int width = snd_pcm_format_width(params_format(params));
int width = params_width(params);
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
unsigned int channels = params_channels(params);
@ -859,6 +866,10 @@ static int fsl_asrc_probe(struct platform_device *pdev)
return PTR_ERR(asrc_priv->ipg_clk);
}
asrc_priv->spba_clk = devm_clk_get(&pdev->dev, "spba");
if (IS_ERR(asrc_priv->spba_clk))
dev_warn(&pdev->dev, "failed to get spba clock\n");
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
sprintf(tmp, "asrck_%x", i);
asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
@ -939,6 +950,11 @@ static int fsl_asrc_runtime_resume(struct device *dev)
ret = clk_prepare_enable(asrc_priv->ipg_clk);
if (ret)
goto disable_mem_clk;
if (!IS_ERR(asrc_priv->spba_clk)) {
ret = clk_prepare_enable(asrc_priv->spba_clk);
if (ret)
goto disable_ipg_clk;
}
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
ret = clk_prepare_enable(asrc_priv->asrck_clk[i]);
if (ret)
@ -950,6 +966,9 @@ static int fsl_asrc_runtime_resume(struct device *dev)
disable_asrck_clk:
for (i--; i >= 0; i--)
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
if (!IS_ERR(asrc_priv->spba_clk))
clk_disable_unprepare(asrc_priv->spba_clk);
disable_ipg_clk:
clk_disable_unprepare(asrc_priv->ipg_clk);
disable_mem_clk:
clk_disable_unprepare(asrc_priv->mem_clk);
@ -963,6 +982,8 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
if (!IS_ERR(asrc_priv->spba_clk))
clk_disable_unprepare(asrc_priv->spba_clk);
clk_disable_unprepare(asrc_priv->ipg_clk);
clk_disable_unprepare(asrc_priv->mem_clk);

View File

@ -426,6 +426,7 @@ struct fsl_asrc_pair {
* @paddr: physical address to the base address of registers
* @mem_clk: clock source to access register
* @ipg_clk: clock source to drive peripheral
* @spba_clk: SPBA clock (optional, depending on SoC design)
* @asrck_clk: clock sources to driver ASRC internal logic
* @lock: spin lock for resource protection
* @pair: pair pointers
@ -442,6 +443,7 @@ struct fsl_asrc {
unsigned long paddr;
struct clk *mem_clk;
struct clk *ipg_clk;
struct clk *spba_clk;
struct clk *asrck_clk[ASRC_CLK_MAX_NUM];
spinlock_t lock;

View File

@ -35,6 +35,7 @@
* @coreclk: clock source to access register
* @extalclk: esai clock source to derive HCK, SCK and FS
* @fsysclk: system clock source to derive HCK, SCK and FS
* @spbaclk: SPBA clock (optional, depending on SoC design)
* @fifo_depth: depth of tx/rx FIFO
* @slot_width: width of each DAI slot
* @slots: number of slots
@ -54,6 +55,7 @@ struct fsl_esai {
struct clk *coreclk;
struct clk *extalclk;
struct clk *fsysclk;
struct clk *spbaclk;
u32 fifo_depth;
u32 slot_width;
u32 slots;
@ -469,6 +471,11 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream,
ret = clk_prepare_enable(esai_priv->coreclk);
if (ret)
return ret;
if (!IS_ERR(esai_priv->spbaclk)) {
ret = clk_prepare_enable(esai_priv->spbaclk);
if (ret)
goto err_spbaclk;
}
if (!IS_ERR(esai_priv->extalclk)) {
ret = clk_prepare_enable(esai_priv->extalclk);
if (ret)
@ -499,6 +506,9 @@ err_fsysclk:
if (!IS_ERR(esai_priv->extalclk))
clk_disable_unprepare(esai_priv->extalclk);
err_extalck:
if (!IS_ERR(esai_priv->spbaclk))
clk_disable_unprepare(esai_priv->spbaclk);
err_spbaclk:
clk_disable_unprepare(esai_priv->coreclk);
return ret;
@ -510,7 +520,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
{
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
u32 width = snd_pcm_format_width(params_format(params));
u32 width = params_width(params);
u32 channels = params_channels(params);
u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
u32 slot_width = width;
@ -564,6 +574,8 @@ static void fsl_esai_shutdown(struct snd_pcm_substream *substream,
clk_disable_unprepare(esai_priv->fsysclk);
if (!IS_ERR(esai_priv->extalclk))
clk_disable_unprepare(esai_priv->extalclk);
if (!IS_ERR(esai_priv->spbaclk))
clk_disable_unprepare(esai_priv->spbaclk);
clk_disable_unprepare(esai_priv->coreclk);
}
@ -653,21 +665,28 @@ static const struct snd_soc_component_driver fsl_esai_component = {
};
static const struct reg_default fsl_esai_reg_defaults[] = {
{0x8, 0x00000000},
{0x10, 0x00000000},
{0x18, 0x00000000},
{0x98, 0x00000000},
{0xd0, 0x00000000},
{0xd4, 0x00000000},
{0xd8, 0x00000000},
{0xdc, 0x00000000},
{0xe0, 0x00000000},
{0xe4, 0x0000ffff},
{0xe8, 0x0000ffff},
{0xec, 0x0000ffff},
{0xf0, 0x0000ffff},
{0xf8, 0x00000000},
{0xfc, 0x00000000},
{REG_ESAI_ETDR, 0x00000000},
{REG_ESAI_ECR, 0x00000000},
{REG_ESAI_TFCR, 0x00000000},
{REG_ESAI_RFCR, 0x00000000},
{REG_ESAI_TX0, 0x00000000},
{REG_ESAI_TX1, 0x00000000},
{REG_ESAI_TX2, 0x00000000},
{REG_ESAI_TX3, 0x00000000},
{REG_ESAI_TX4, 0x00000000},
{REG_ESAI_TX5, 0x00000000},
{REG_ESAI_TSR, 0x00000000},
{REG_ESAI_SAICR, 0x00000000},
{REG_ESAI_TCR, 0x00000000},
{REG_ESAI_TCCR, 0x00000000},
{REG_ESAI_RCR, 0x00000000},
{REG_ESAI_RCCR, 0x00000000},
{REG_ESAI_TSMA, 0x0000ffff},
{REG_ESAI_TSMB, 0x0000ffff},
{REG_ESAI_RSMA, 0x0000ffff},
{REG_ESAI_RSMB, 0x0000ffff},
{REG_ESAI_PRRC, 0x00000000},
{REG_ESAI_PCRC, 0x00000000},
};
static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
@ -705,17 +724,10 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case REG_ESAI_ETDR:
case REG_ESAI_ERDR:
case REG_ESAI_ESR:
case REG_ESAI_TFSR:
case REG_ESAI_RFSR:
case REG_ESAI_TX0:
case REG_ESAI_TX1:
case REG_ESAI_TX2:
case REG_ESAI_TX3:
case REG_ESAI_TX4:
case REG_ESAI_TX5:
case REG_ESAI_RX0:
case REG_ESAI_RX1:
case REG_ESAI_RX2:
@ -819,6 +831,11 @@ static int fsl_esai_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n",
PTR_ERR(esai_priv->fsysclk));
esai_priv->spbaclk = devm_clk_get(&pdev->dev, "spba");
if (IS_ERR(esai_priv->spbaclk))
dev_warn(&pdev->dev, "failed to get spba clock: %ld\n",
PTR_ERR(esai_priv->spbaclk));
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);

View File

@ -126,6 +126,17 @@ out:
return IRQ_HANDLED;
}
static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
u32 rx_mask, int slots, int slot_width)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
sai->slots = slots;
sai->slot_width = slot_width;
return 0;
}
static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int fsl_dir)
{
@ -354,13 +365,25 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
return -EINVAL;
}
if ((tx && sai->synchronous[TX]) || (!tx && !sai->synchronous[RX])) {
/*
* 1) For Asynchronous mode, we must set RCR2 register for capture, and
* set TCR2 register for playback.
* 2) For Tx sync with Rx clock, we must set RCR2 register for playback
* and capture.
* 3) For Rx sync with Tx clock, we must set TCR2 register for playback
* and capture.
* 4) For Tx and Rx are both Synchronous with another SAI, we just
* ignore it.
*/
if ((sai->synchronous[TX] && !sai->synchronous[RX]) ||
(!tx && !sai->synchronous[RX])) {
regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
FSL_SAI_CR2_MSEL_MASK,
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
FSL_SAI_CR2_DIV_MASK, savediv - 1);
} else {
} else if ((sai->synchronous[RX] && !sai->synchronous[TX]) ||
(tx && !sai->synchronous[TX])) {
regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
FSL_SAI_CR2_MSEL_MASK,
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
@ -381,13 +404,21 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
unsigned int channels = params_channels(params);
u32 word_width = snd_pcm_format_width(params_format(params));
u32 word_width = params_width(params);
u32 val_cr4 = 0, val_cr5 = 0;
u32 slots = (channels == 1) ? 2 : channels;
u32 slot_width = word_width;
int ret;
if (sai->slots)
slots = sai->slots;
if (sai->slot_width)
slot_width = sai->slot_width;
if (!sai->is_slave_mode) {
ret = fsl_sai_set_bclk(cpu_dai, tx,
2 * word_width * params_rate(params));
slots * slot_width * params_rate(params));
if (ret)
return ret;
@ -399,21 +430,49 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
sai->mclk_streams |= BIT(substream->stream);
}
}
if (!sai->is_dsp_mode)
val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
val_cr4 |= FSL_SAI_CR4_SYWD(slot_width);
val_cr5 |= FSL_SAI_CR5_WNW(word_width);
val_cr5 |= FSL_SAI_CR5_W0W(word_width);
val_cr5 |= FSL_SAI_CR5_WNW(slot_width);
val_cr5 |= FSL_SAI_CR5_W0W(slot_width);
if (sai->is_lsb_first)
val_cr5 |= FSL_SAI_CR5_FBT(0);
else
val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
val_cr4 |= FSL_SAI_CR4_FRSZ(slots);
/*
* For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will
* generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4),
* RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync
* error.
*/
if (!sai->is_slave_mode) {
if (!sai->synchronous[TX] && sai->synchronous[RX] && !tx) {
regmap_update_bits(sai->regmap, FSL_SAI_TCR4,
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
val_cr4);
regmap_update_bits(sai->regmap, FSL_SAI_TCR5,
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
FSL_SAI_CR5_FBT_MASK, val_cr5);
regmap_write(sai->regmap, FSL_SAI_TMR,
~0UL - ((1 << channels) - 1));
} else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) {
regmap_update_bits(sai->regmap, FSL_SAI_RCR4,
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
val_cr4);
regmap_update_bits(sai->regmap, FSL_SAI_RCR5,
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
FSL_SAI_CR5_FBT_MASK, val_cr5);
regmap_write(sai->regmap, FSL_SAI_RMR,
~0UL - ((1 << channels) - 1));
}
}
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
@ -569,6 +628,7 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
.set_sysclk = fsl_sai_set_dai_sysclk,
.set_fmt = fsl_sai_set_dai_fmt,
.set_tdm_slot = fsl_sai_set_dai_tdm_slot,
.hw_params = fsl_sai_hw_params,
.hw_free = fsl_sai_hw_free,
.trigger = fsl_sai_trigger,
@ -627,6 +687,22 @@ static const struct snd_soc_component_driver fsl_component = {
.name = "fsl-sai",
};
static struct reg_default fsl_sai_reg_defaults[] = {
{FSL_SAI_TCR1, 0},
{FSL_SAI_TCR2, 0},
{FSL_SAI_TCR3, 0},
{FSL_SAI_TCR4, 0},
{FSL_SAI_TCR5, 0},
{FSL_SAI_TDR, 0},
{FSL_SAI_TMR, 0},
{FSL_SAI_RCR1, 0},
{FSL_SAI_RCR2, 0},
{FSL_SAI_RCR3, 0},
{FSL_SAI_RCR4, 0},
{FSL_SAI_RCR5, 0},
{FSL_SAI_RMR, 0},
};
static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@ -660,13 +736,11 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
case FSL_SAI_RCSR:
case FSL_SAI_TFR:
case FSL_SAI_RFR:
case FSL_SAI_TDR:
case FSL_SAI_RDR:
return true;
default:
return false;
}
}
static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
@ -699,6 +773,8 @@ static const struct regmap_config fsl_sai_regmap_config = {
.val_bits = 32,
.max_register = FSL_SAI_RMR,
.reg_defaults = fsl_sai_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults),
.readable_reg = fsl_sai_readable_reg,
.volatile_reg = fsl_sai_volatile_reg,
.writeable_reg = fsl_sai_writeable_reg,

View File

@ -143,6 +143,9 @@ struct fsl_sai {
unsigned int mclk_id[2];
unsigned int mclk_streams;
unsigned int slots;
unsigned int slot_width;
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct snd_dmaengine_dai_dma_data dma_params_tx;
};

View File

@ -88,6 +88,7 @@ struct spdif_mixer_control {
* @rxclk: rx clock sources for capture
* @coreclk: core clock for register access via DMA
* @sysclk: system clock for rx clock rate measurement
* @spbaclk: SPBA clock (optional, depending on SoC design)
* @dma_params_tx: DMA parameters for transmit channel
* @dma_params_rx: DMA parameters for receive channel
*/
@ -106,6 +107,7 @@ struct fsl_spdif_priv {
struct clk *rxclk;
struct clk *coreclk;
struct clk *sysclk;
struct clk *spbaclk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
/* regcache for SRPC */
@ -474,6 +476,14 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
return ret;
}
if (!IS_ERR(spdif_priv->spbaclk)) {
ret = clk_prepare_enable(spdif_priv->spbaclk);
if (ret) {
dev_err(&pdev->dev, "failed to enable spba clock\n");
goto err_spbaclk;
}
}
ret = spdif_softreset(spdif_priv);
if (ret) {
dev_err(&pdev->dev, "failed to soft reset\n");
@ -515,6 +525,9 @@ disable_txclk:
for (i--; i >= 0; i--)
clk_disable_unprepare(spdif_priv->txclk[i]);
err:
if (!IS_ERR(spdif_priv->spbaclk))
clk_disable_unprepare(spdif_priv->spbaclk);
err_spbaclk:
clk_disable_unprepare(spdif_priv->coreclk);
return ret;
@ -548,6 +561,8 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
spdif_intr_status_clear(spdif_priv);
regmap_update_bits(regmap, REG_SPDIF_SCR,
SCR_LOW_POWER, SCR_LOW_POWER);
if (!IS_ERR(spdif_priv->spbaclk))
clk_disable_unprepare(spdif_priv->spbaclk);
clk_disable_unprepare(spdif_priv->coreclk);
}
}
@ -1006,12 +1021,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = {
/* FSL SPDIF REGMAP */
static const struct reg_default fsl_spdif_reg_defaults[] = {
{0x0, 0x00000400},
{0x4, 0x00000000},
{0xc, 0x00000000},
{0x34, 0x00000000},
{0x38, 0x00000000},
{0x50, 0x00020f00},
{REG_SPDIF_SCR, 0x00000400},
{REG_SPDIF_SRCD, 0x00000000},
{REG_SPDIF_SIE, 0x00000000},
{REG_SPDIF_STL, 0x00000000},
{REG_SPDIF_STR, 0x00000000},
{REG_SPDIF_STCSCH, 0x00000000},
{REG_SPDIF_STCSCL, 0x00000000},
{REG_SPDIF_STC, 0x00020f00},
};
static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
@ -1049,8 +1066,6 @@ static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg)
case REG_SPDIF_SRCSL:
case REG_SPDIF_SRU:
case REG_SPDIF_SRQ:
case REG_SPDIF_STL:
case REG_SPDIF_STR:
case REG_SPDIF_SRFM:
return true;
default:
@ -1261,6 +1276,10 @@ static int fsl_spdif_probe(struct platform_device *pdev)
return PTR_ERR(spdif_priv->coreclk);
}
spdif_priv->spbaclk = devm_clk_get(&pdev->dev, "spba");
if (IS_ERR(spdif_priv->spbaclk))
dev_warn(&pdev->dev, "no spba clock in devicetree\n");
/* Select clock source for rx/tx clock */
spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
if (IS_ERR(spdif_priv->rxclk)) {

View File

@ -113,17 +113,17 @@ struct fsl_ssi_rxtx_reg_val {
};
static const struct reg_default fsl_ssi_reg_defaults[] = {
{0x10, 0x00000000},
{0x18, 0x00003003},
{0x1c, 0x00000200},
{0x20, 0x00000200},
{0x24, 0x00040000},
{0x28, 0x00040000},
{0x38, 0x00000000},
{0x48, 0x00000000},
{0x4c, 0x00000000},
{0x54, 0x00000000},
{0x58, 0x00000000},
{CCSR_SSI_SCR, 0x00000000},
{CCSR_SSI_SIER, 0x00003003},
{CCSR_SSI_STCR, 0x00000200},
{CCSR_SSI_SRCR, 0x00000200},
{CCSR_SSI_STCCR, 0x00040000},
{CCSR_SSI_SRCCR, 0x00040000},
{CCSR_SSI_SACNT, 0x00000000},
{CCSR_SSI_STMSK, 0x00000000},
{CCSR_SSI_SRMSK, 0x00000000},
{CCSR_SSI_SACCEN, 0x00000000},
{CCSR_SSI_SACCDIS, 0x00000000},
};
static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
@ -767,8 +767,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
struct regmap *regs = ssi_private->regs;
unsigned int channels = params_channels(hw_params);
unsigned int sample_size =
snd_pcm_format_width(params_format(hw_params));
unsigned int sample_size = params_width(hw_params);
u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
int ret;
u32 scr_val;

View File

@ -62,6 +62,8 @@ int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
config = devm_kzalloc(&pdev->dev,
sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL);
if (!config)
return -ENOMEM;
*config = imx_dmaengine_pcm_config;
if (size)
config->prealloc_buffer_size = size;

View File

@ -220,9 +220,9 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
ret = dma_mmap_writecombine(substream->pcm->card->dev, vma,
runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret,
pr_debug("%s: ret: %d %p %pad 0x%08x\n", __func__, ret,
runtime->dma_area,
runtime->dma_addr,
&runtime->dma_addr,
runtime->dma_bytes);
return ret;
}

52
sound/soc/img/Kconfig Normal file
View File

@ -0,0 +1,52 @@
config SND_SOC_IMG
bool "Audio support for Imagination Technologies designs"
help
Audio support for Imagination Technologies audio hardware
config SND_SOC_IMG_I2S_IN
tristate "Imagination I2S Input Device Driver"
depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2S in driver for
Imagination Technologies I2S in device.
config SND_SOC_IMG_I2S_OUT
tristate "Imagination I2S Output Device Driver"
depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2S out driver for
Imagination Technologies I2S out device.
config SND_SOC_IMG_PARALLEL_OUT
tristate "Imagination Parallel Output Device Driver"
depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for parallel out driver for
Imagination Technologies parallel out device.
config SND_SOC_IMG_SPDIF_IN
tristate "Imagination SPDIF Input Device Driver"
depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for SPDIF input driver for
Imagination Technologies SPDIF input device.
config SND_SOC_IMG_SPDIF_OUT
tristate "Imagination SPDIF Output Device Driver"
depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for SPDIF out driver for
Imagination Technologies SPDIF out device.
config SND_SOC_IMG_PISTACHIO_INTERNAL_DAC
tristate "Support for Pistachio SoC Internal DAC Driver"
depends on SND_SOC_IMG
help
Say Y or M if you want to add support for Pistachio internal DAC
driver for Imagination Technologies Pistachio internal DAC device.

7
sound/soc/img/Makefile Normal file
View File

@ -0,0 +1,7 @@
obj-$(CONFIG_SND_SOC_IMG_I2S_IN) += img-i2s-in.o
obj-$(CONFIG_SND_SOC_IMG_I2S_OUT) += img-i2s-out.o
obj-$(CONFIG_SND_SOC_IMG_PARALLEL_OUT) += img-parallel-out.o
obj-$(CONFIG_SND_SOC_IMG_SPDIF_IN) += img-spdif-in.o
obj-$(CONFIG_SND_SOC_IMG_SPDIF_OUT) += img-spdif-out.o
obj-$(CONFIG_SND_SOC_IMG_PISTACHIO_INTERNAL_DAC) += pistachio-internal-dac.o

516
sound/soc/img/img-i2s-in.c Normal file
View File

@ -0,0 +1,516 @@
/*
* IMG I2S input controller driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#define IMG_I2S_IN_RX_FIFO 0x0
#define IMG_I2S_IN_CTL 0x4
#define IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK 0xfffffffc
#define IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT 2
#define IMG_I2S_IN_CTL_16PACK_MASK BIT(1)
#define IMG_I2S_IN_CTL_ME_MASK BIT(0)
#define IMG_I2S_IN_CH_CTL 0x4
#define IMG_I2S_IN_CH_CTL_CCDEL_MASK 0x38000
#define IMG_I2S_IN_CH_CTL_CCDEL_SHIFT 15
#define IMG_I2S_IN_CH_CTL_FEN_MASK BIT(14)
#define IMG_I2S_IN_CH_CTL_FMODE_MASK BIT(13)
#define IMG_I2S_IN_CH_CTL_16PACK_MASK BIT(12)
#define IMG_I2S_IN_CH_CTL_JUST_MASK BIT(10)
#define IMG_I2S_IN_CH_CTL_PACKH_MASK BIT(9)
#define IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK BIT(8)
#define IMG_I2S_IN_CH_CTL_BLKP_MASK BIT(7)
#define IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK BIT(6)
#define IMG_I2S_IN_CH_CTL_LRD_MASK BIT(3)
#define IMG_I2S_IN_CH_CTL_FW_MASK BIT(2)
#define IMG_I2S_IN_CH_CTL_SW_MASK BIT(1)
#define IMG_I2S_IN_CH_CTL_ME_MASK BIT(0)
#define IMG_I2S_IN_CH_STRIDE 0x20
struct img_i2s_in {
void __iomem *base;
struct clk *clk_sys;
struct snd_dmaengine_dai_dma_data dma_data;
struct device *dev;
unsigned int max_i2s_chan;
void __iomem *channel_base;
unsigned int active_channels;
struct snd_soc_dai_driver dai_driver;
};
static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg)
{
writel(val, i2s->base + reg);
}
static inline u32 img_i2s_in_readl(struct img_i2s_in *i2s, u32 reg)
{
return readl(i2s->base + reg);
}
static inline void img_i2s_in_ch_writel(struct img_i2s_in *i2s, u32 chan,
u32 val, u32 reg)
{
writel(val, i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
}
static inline u32 img_i2s_in_ch_readl(struct img_i2s_in *i2s, u32 chan,
u32 reg)
{
return readl(i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
}
static inline void img_i2s_in_ch_disable(struct img_i2s_in *i2s, u32 chan)
{
u32 reg;
reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
reg &= ~IMG_I2S_IN_CH_CTL_ME_MASK;
img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
}
static inline void img_i2s_in_ch_enable(struct img_i2s_in *i2s, u32 chan)
{
u32 reg;
reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
reg |= IMG_I2S_IN_CH_CTL_ME_MASK;
img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
}
static inline void img_i2s_in_disable(struct img_i2s_in *i2s)
{
u32 reg;
reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
reg &= ~IMG_I2S_IN_CTL_ME_MASK;
img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
}
static inline void img_i2s_in_enable(struct img_i2s_in *i2s)
{
u32 reg;
reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
reg |= IMG_I2S_IN_CTL_ME_MASK;
img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
}
static inline void img_i2s_in_flush(struct img_i2s_in *i2s)
{
int i;
u32 reg;
for (i = 0; i < i2s->active_channels; i++) {
reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
reg |= IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
reg &= ~IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
}
}
static int img_i2s_in_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
img_i2s_in_enable(i2s);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
img_i2s_in_disable(i2s);
break;
default:
return -EINVAL;
}
return 0;
}
static int img_i2s_in_check_rate(struct img_i2s_in *i2s,
unsigned int sample_rate, unsigned int frame_size,
unsigned int *bclk_filter_enable,
unsigned int *bclk_filter_value)
{
unsigned int bclk_freq, cur_freq;
bclk_freq = sample_rate * frame_size;
cur_freq = clk_get_rate(i2s->clk_sys);
if (cur_freq >= bclk_freq * 8) {
*bclk_filter_enable = 1;
*bclk_filter_value = 0;
} else if (cur_freq >= bclk_freq * 7) {
*bclk_filter_enable = 1;
*bclk_filter_value = 1;
} else if (cur_freq >= bclk_freq * 6) {
*bclk_filter_enable = 0;
*bclk_filter_value = 0;
} else {
dev_err(i2s->dev,
"Sys clock rate %u insufficient for sample rate %u\n",
cur_freq, sample_rate);
return -EINVAL;
}
return 0;
}
static int img_i2s_in_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
unsigned int rate, channels, i2s_channels, frame_size;
unsigned int bclk_filter_enable, bclk_filter_value;
int i, ret = 0;
u32 reg, control_mask, chan_control_mask;
u32 control_set = 0, chan_control_set = 0;
snd_pcm_format_t format;
rate = params_rate(params);
format = params_format(params);
channels = params_channels(params);
i2s_channels = channels / 2;
switch (format) {
case SNDRV_PCM_FORMAT_S32_LE:
frame_size = 64;
chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
chan_control_set |= IMG_I2S_IN_CH_CTL_PACKH_MASK;
break;
case SNDRV_PCM_FORMAT_S24_LE:
frame_size = 64;
chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
break;
case SNDRV_PCM_FORMAT_S16_LE:
frame_size = 32;
control_set |= IMG_I2S_IN_CTL_16PACK_MASK;
chan_control_set |= IMG_I2S_IN_CH_CTL_16PACK_MASK;
break;
default:
return -EINVAL;
}
if ((channels < 2) ||
(channels > (i2s->max_i2s_chan * 2)) ||
(channels % 2))
return -EINVAL;
control_set |= ((i2s_channels - 1) << IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT);
ret = img_i2s_in_check_rate(i2s, rate, frame_size,
&bclk_filter_enable, &bclk_filter_value);
if (ret < 0)
return ret;
if (bclk_filter_enable)
chan_control_set |= IMG_I2S_IN_CH_CTL_FEN_MASK;
if (bclk_filter_value)
chan_control_set |= IMG_I2S_IN_CH_CTL_FMODE_MASK;
control_mask = IMG_I2S_IN_CTL_16PACK_MASK |
IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK;
chan_control_mask = IMG_I2S_IN_CH_CTL_16PACK_MASK |
IMG_I2S_IN_CH_CTL_FEN_MASK |
IMG_I2S_IN_CH_CTL_FMODE_MASK |
IMG_I2S_IN_CH_CTL_SW_MASK |
IMG_I2S_IN_CH_CTL_FW_MASK |
IMG_I2S_IN_CH_CTL_PACKH_MASK;
reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
reg = (reg & ~control_mask) | control_set;
img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
for (i = 0; i < i2s->active_channels; i++)
img_i2s_in_ch_disable(i2s, i);
for (i = 0; i < i2s->max_i2s_chan; i++) {
reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
reg = (reg & ~chan_control_mask) | chan_control_set;
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
}
i2s->active_channels = i2s_channels;
img_i2s_in_flush(i2s);
for (i = 0; i < i2s->active_channels; i++)
img_i2s_in_ch_enable(i2s, i);
return 0;
}
static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
int i;
u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0;
u32 reg;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
break;
case SND_SOC_DAIFMT_NB_IF:
break;
case SND_SOC_DAIFMT_IB_NF:
lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
break;
case SND_SOC_DAIFMT_IB_IF:
blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
chan_control_set |= IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
break;
case SND_SOC_DAIFMT_LEFT_J:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
break;
default:
return -EINVAL;
}
chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
for (i = 0; i < i2s->active_channels; i++)
img_i2s_in_ch_disable(i2s, i);
/*
* BLKP and LRD must be set during separate register writes
*/
for (i = 0; i < i2s->max_i2s_chan; i++) {
reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
reg = (reg & ~chan_control_mask) | chan_control_set;
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
reg = (reg & ~IMG_I2S_IN_CH_CTL_BLKP_MASK) | blkp_set;
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
reg = (reg & ~IMG_I2S_IN_CH_CTL_LRD_MASK) | lrd_set;
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
}
for (i = 0; i < i2s->active_channels; i++)
img_i2s_in_ch_enable(i2s, i);
return 0;
}
static const struct snd_soc_dai_ops img_i2s_in_dai_ops = {
.trigger = img_i2s_in_trigger,
.hw_params = img_i2s_in_hw_params,
.set_fmt = img_i2s_in_set_fmt
};
static int img_i2s_in_dai_probe(struct snd_soc_dai *dai)
{
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_init_dma_data(dai, NULL, &i2s->dma_data);
return 0;
}
static const struct snd_soc_component_driver img_i2s_in_component = {
.name = "img-i2s-in"
};
static int img_i2s_in_dma_prepare_slave_config(struct snd_pcm_substream *st,
struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
{
unsigned int i2s_channels = params_channels(params) / 2;
struct snd_soc_pcm_runtime *rtd = st->private_data;
struct snd_dmaengine_dai_dma_data *dma_data;
int ret;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st);
ret = snd_hwparams_to_dma_slave_config(st, params, sc);
if (ret)
return ret;
sc->src_addr = dma_data->addr;
sc->src_addr_width = dma_data->addr_width;
sc->src_maxburst = 4 * i2s_channels;
return 0;
}
static const struct snd_dmaengine_pcm_config img_i2s_in_dma_config = {
.prepare_slave_config = img_i2s_in_dma_prepare_slave_config
};
static int img_i2s_in_probe(struct platform_device *pdev)
{
struct img_i2s_in *i2s;
struct resource *res;
void __iomem *base;
int ret, i;
struct reset_control *rst;
unsigned int max_i2s_chan_pow_2;
struct device *dev = &pdev->dev;
i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
platform_set_drvdata(pdev, i2s);
i2s->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
i2s->base = base;
if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
&i2s->max_i2s_chan)) {
dev_err(dev, "No img,i2s-channels property\n");
return -EINVAL;
}
max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
i2s->clk_sys = devm_clk_get(dev, "sys");
if (IS_ERR(i2s->clk_sys)) {
if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
dev_err(dev, "Failed to acquire clock 'sys'\n");
return PTR_ERR(i2s->clk_sys);
}
ret = clk_prepare_enable(i2s->clk_sys);
if (ret)
return ret;
i2s->active_channels = 1;
i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO;
i2s->dma_data.addr_width = 4;
i2s->dai_driver.probe = img_i2s_in_dai_probe;
i2s->dai_driver.capture.channels_min = 2;
i2s->dai_driver.capture.channels_max = i2s->max_i2s_chan * 2;
i2s->dai_driver.capture.rates = SNDRV_PCM_RATE_8000_192000;
i2s->dai_driver.capture.formats = SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE;
i2s->dai_driver.ops = &img_i2s_in_dai_ops;
rst = devm_reset_control_get(dev, "rst");
if (IS_ERR(rst)) {
if (PTR_ERR(rst) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto err_clk_disable;
}
dev_dbg(dev, "No top level reset found\n");
img_i2s_in_disable(i2s);
for (i = 0; i < i2s->max_i2s_chan; i++)
img_i2s_in_ch_disable(i2s, i);
} else {
reset_control_assert(rst);
reset_control_deassert(rst);
}
img_i2s_in_writel(i2s, 0, IMG_I2S_IN_CTL);
for (i = 0; i < i2s->max_i2s_chan; i++)
img_i2s_in_ch_writel(i2s, i,
(4 << IMG_I2S_IN_CH_CTL_CCDEL_SHIFT) |
IMG_I2S_IN_CH_CTL_JUST_MASK |
IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL);
ret = devm_snd_soc_register_component(dev, &img_i2s_in_component,
&i2s->dai_driver, 1);
if (ret)
goto err_clk_disable;
ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0);
if (ret)
goto err_clk_disable;
return 0;
err_clk_disable:
clk_disable_unprepare(i2s->clk_sys);
return ret;
}
static int img_i2s_in_dev_remove(struct platform_device *pdev)
{
struct img_i2s_in *i2s = platform_get_drvdata(pdev);
clk_disable_unprepare(i2s->clk_sys);
return 0;
}
static const struct of_device_id img_i2s_in_of_match[] = {
{ .compatible = "img,i2s-in" },
{}
};
MODULE_DEVICE_TABLE(of, img_i2s_in_of_match);
static struct platform_driver img_i2s_in_driver = {
.driver = {
.name = "img-i2s-in",
.of_match_table = img_i2s_in_of_match
},
.probe = img_i2s_in_probe,
.remove = img_i2s_in_dev_remove
};
module_platform_driver(img_i2s_in_driver);
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
MODULE_DESCRIPTION("IMG I2S Input Driver");
MODULE_LICENSE("GPL v2");

565
sound/soc/img/img-i2s-out.c Normal file
View File

@ -0,0 +1,565 @@
/*
* IMG I2S output controller driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#define IMG_I2S_OUT_TX_FIFO 0x0
#define IMG_I2S_OUT_CTL 0x4
#define IMG_I2S_OUT_CTL_DATA_EN_MASK BIT(24)
#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK 0xffe000
#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT 13
#define IMG_I2S_OUT_CTL_FRM_SIZE_MASK BIT(8)
#define IMG_I2S_OUT_CTL_MASTER_MASK BIT(6)
#define IMG_I2S_OUT_CTL_CLK_MASK BIT(5)
#define IMG_I2S_OUT_CTL_CLK_EN_MASK BIT(4)
#define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK BIT(3)
#define IMG_I2S_OUT_CTL_BCLK_POL_MASK BIT(2)
#define IMG_I2S_OUT_CTL_ME_MASK BIT(0)
#define IMG_I2S_OUT_CH_CTL 0x4
#define IMG_I2S_OUT_CHAN_CTL_CH_MASK BIT(11)
#define IMG_I2S_OUT_CHAN_CTL_LT_MASK BIT(10)
#define IMG_I2S_OUT_CHAN_CTL_FMT_MASK 0xf0
#define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT 4
#define IMG_I2S_OUT_CHAN_CTL_JUST_MASK BIT(3)
#define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK BIT(1)
#define IMG_I2S_OUT_CHAN_CTL_ME_MASK BIT(0)
#define IMG_I2S_OUT_CH_STRIDE 0x20
struct img_i2s_out {
void __iomem *base;
struct clk *clk_sys;
struct clk *clk_ref;
struct snd_dmaengine_dai_dma_data dma_data;
struct device *dev;
unsigned int max_i2s_chan;
void __iomem *channel_base;
bool force_clk_active;
unsigned int active_channels;
struct reset_control *rst;
struct snd_soc_dai_driver dai_driver;
};
static int img_i2s_out_suspend(struct device *dev)
{
struct img_i2s_out *i2s = dev_get_drvdata(dev);
if (!i2s->force_clk_active)
clk_disable_unprepare(i2s->clk_ref);
return 0;
}
static int img_i2s_out_resume(struct device *dev)
{
struct img_i2s_out *i2s = dev_get_drvdata(dev);
int ret;
if (!i2s->force_clk_active) {
ret = clk_prepare_enable(i2s->clk_ref);
if (ret) {
dev_err(dev, "clk_enable failed: %d\n", ret);
return ret;
}
}
return 0;
}
static inline void img_i2s_out_writel(struct img_i2s_out *i2s, u32 val,
u32 reg)
{
writel(val, i2s->base + reg);
}
static inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg)
{
return readl(i2s->base + reg);
}
static inline void img_i2s_out_ch_writel(struct img_i2s_out *i2s,
u32 chan, u32 val, u32 reg)
{
writel(val, i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
}
static inline u32 img_i2s_out_ch_readl(struct img_i2s_out *i2s, u32 chan,
u32 reg)
{
return readl(i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
}
static inline void img_i2s_out_ch_disable(struct img_i2s_out *i2s, u32 chan)
{
u32 reg;
reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
reg &= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
}
static inline void img_i2s_out_ch_enable(struct img_i2s_out *i2s, u32 chan)
{
u32 reg;
reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
reg |= IMG_I2S_OUT_CHAN_CTL_ME_MASK;
img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
}
static inline void img_i2s_out_disable(struct img_i2s_out *i2s)
{
u32 reg;
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
reg &= ~IMG_I2S_OUT_CTL_ME_MASK;
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
}
static inline void img_i2s_out_enable(struct img_i2s_out *i2s)
{
u32 reg;
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
reg |= IMG_I2S_OUT_CTL_ME_MASK;
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
}
static void img_i2s_out_reset(struct img_i2s_out *i2s)
{
int i;
u32 core_ctl, chan_ctl;
core_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL) &
~IMG_I2S_OUT_CTL_ME_MASK &
~IMG_I2S_OUT_CTL_DATA_EN_MASK;
if (!i2s->force_clk_active)
core_ctl &= ~IMG_I2S_OUT_CTL_CLK_EN_MASK;
chan_ctl = img_i2s_out_ch_readl(i2s, 0, IMG_I2S_OUT_CH_CTL) &
~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
reset_control_assert(i2s->rst);
reset_control_deassert(i2s->rst);
for (i = 0; i < i2s->max_i2s_chan; i++)
img_i2s_out_ch_writel(i2s, i, chan_ctl, IMG_I2S_OUT_CH_CTL);
for (i = 0; i < i2s->active_channels; i++)
img_i2s_out_ch_enable(i2s, i);
img_i2s_out_writel(i2s, core_ctl, IMG_I2S_OUT_CTL);
img_i2s_out_enable(i2s);
}
static int img_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
u32 reg;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
if (!i2s->force_clk_active)
reg |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
reg |= IMG_I2S_OUT_CTL_DATA_EN_MASK;
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
img_i2s_out_reset(i2s);
break;
default:
return -EINVAL;
}
return 0;
}
static int img_i2s_out_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
unsigned int channels, i2s_channels;
long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate;
int i;
u32 reg, control_mask, control_set = 0;
snd_pcm_format_t format;
rate = params_rate(params);
format = params_format(params);
channels = params_channels(params);
i2s_channels = channels / 2;
if (format != SNDRV_PCM_FORMAT_S32_LE)
return -EINVAL;
if ((channels < 2) ||
(channels > (i2s->max_i2s_chan * 2)) ||
(channels % 2))
return -EINVAL;
pre_div_a = clk_round_rate(i2s->clk_ref, rate * 256);
if (pre_div_a < 0)
return pre_div_a;
pre_div_b = clk_round_rate(i2s->clk_ref, rate * 384);
if (pre_div_b < 0)
return pre_div_b;
diff_a = abs((pre_div_a / 256) - rate);
diff_b = abs((pre_div_b / 384) - rate);
/* If diffs are equal, use lower clock rate */
if (diff_a > diff_b)
clk_set_rate(i2s->clk_ref, pre_div_b);
else
clk_set_rate(i2s->clk_ref, pre_div_a);
/*
* Another driver (eg alsa machine driver) may have rejected the above
* change. Get the current rate and set the register bit according to
* the new minimum diff
*/
clk_rate = clk_get_rate(i2s->clk_ref);
diff_a = abs((clk_rate / 256) - rate);
diff_b = abs((clk_rate / 384) - rate);
if (diff_a > diff_b)
control_set |= IMG_I2S_OUT_CTL_CLK_MASK;
control_set |= ((i2s_channels - 1) <<
IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT) &
IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
control_mask = IMG_I2S_OUT_CTL_CLK_MASK |
IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
img_i2s_out_disable(i2s);
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
reg = (reg & ~control_mask) | control_set;
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
for (i = 0; i < i2s_channels; i++)
img_i2s_out_ch_enable(i2s, i);
for (; i < i2s->max_i2s_chan; i++)
img_i2s_out_ch_disable(i2s, i);
img_i2s_out_enable(i2s);
i2s->active_channels = i2s_channels;
return 0;
}
static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
int i;
bool force_clk_active;
u32 chan_control_mask, control_mask, chan_control_set = 0;
u32 reg, control_set = 0;
force_clk_active = ((fmt & SND_SOC_DAIFMT_CLOCK_MASK) ==
SND_SOC_DAIFMT_CONT);
if (force_clk_active)
control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
break;
case SND_SOC_DAIFMT_CBS_CFS:
control_set |= IMG_I2S_OUT_CTL_MASTER_MASK;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
break;
case SND_SOC_DAIFMT_NB_IF:
control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
break;
case SND_SOC_DAIFMT_IB_NF:
break;
case SND_SOC_DAIFMT_IB_IF:
control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
chan_control_set |= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
break;
case SND_SOC_DAIFMT_LEFT_J:
break;
default:
return -EINVAL;
}
control_mask = IMG_I2S_OUT_CTL_CLK_EN_MASK |
IMG_I2S_OUT_CTL_MASTER_MASK |
IMG_I2S_OUT_CTL_BCLK_POL_MASK |
IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
img_i2s_out_disable(i2s);
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
reg = (reg & ~control_mask) | control_set;
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
for (i = 0; i < i2s->active_channels; i++)
img_i2s_out_ch_disable(i2s, i);
for (i = 0; i < i2s->max_i2s_chan; i++) {
reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
reg = (reg & ~chan_control_mask) | chan_control_set;
img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
}
for (i = 0; i < i2s->active_channels; i++)
img_i2s_out_ch_enable(i2s, i);
img_i2s_out_enable(i2s);
i2s->force_clk_active = force_clk_active;
return 0;
}
static const struct snd_soc_dai_ops img_i2s_out_dai_ops = {
.trigger = img_i2s_out_trigger,
.hw_params = img_i2s_out_hw_params,
.set_fmt = img_i2s_out_set_fmt
};
static int img_i2s_out_dai_probe(struct snd_soc_dai *dai)
{
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL);
return 0;
}
static const struct snd_soc_component_driver img_i2s_out_component = {
.name = "img-i2s-out"
};
static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st,
struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
{
unsigned int i2s_channels = params_channels(params) / 2;
struct snd_soc_pcm_runtime *rtd = st->private_data;
struct snd_dmaengine_dai_dma_data *dma_data;
int ret;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st);
ret = snd_hwparams_to_dma_slave_config(st, params, sc);
if (ret)
return ret;
sc->dst_addr = dma_data->addr;
sc->dst_addr_width = dma_data->addr_width;
sc->dst_maxburst = 4 * i2s_channels;
return 0;
}
static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = {
.prepare_slave_config = img_i2s_out_dma_prepare_slave_config
};
static int img_i2s_out_probe(struct platform_device *pdev)
{
struct img_i2s_out *i2s;
struct resource *res;
void __iomem *base;
int i, ret;
unsigned int max_i2s_chan_pow_2;
u32 reg;
struct device *dev = &pdev->dev;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
platform_set_drvdata(pdev, i2s);
i2s->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
i2s->base = base;
if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
&i2s->max_i2s_chan)) {
dev_err(&pdev->dev, "No img,i2s-channels property\n");
return -EINVAL;
}
max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
i2s->rst = devm_reset_control_get(&pdev->dev, "rst");
if (IS_ERR(i2s->rst)) {
if (PTR_ERR(i2s->rst) != -EPROBE_DEFER)
dev_err(&pdev->dev, "No top level reset found\n");
return PTR_ERR(i2s->rst);
}
i2s->clk_sys = devm_clk_get(&pdev->dev, "sys");
if (IS_ERR(i2s->clk_sys)) {
if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
dev_err(dev, "Failed to acquire clock 'sys'\n");
return PTR_ERR(i2s->clk_sys);
}
i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
if (IS_ERR(i2s->clk_ref)) {
if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER)
dev_err(dev, "Failed to acquire clock 'ref'\n");
return PTR_ERR(i2s->clk_ref);
}
ret = clk_prepare_enable(i2s->clk_sys);
if (ret)
return ret;
reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK;
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK |
IMG_I2S_OUT_CHAN_CTL_LT_MASK |
IMG_I2S_OUT_CHAN_CTL_CH_MASK |
(8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT);
for (i = 0; i < i2s->max_i2s_chan; i++)
img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
img_i2s_out_reset(i2s);
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = img_i2s_out_resume(&pdev->dev);
if (ret)
goto err_pm_disable;
}
i2s->active_channels = 1;
i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO;
i2s->dma_data.addr_width = 4;
i2s->dma_data.maxburst = 4;
i2s->dai_driver.probe = img_i2s_out_dai_probe;
i2s->dai_driver.playback.channels_min = 2;
i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2;
i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000;
i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE;
i2s->dai_driver.ops = &img_i2s_out_dai_ops;
ret = devm_snd_soc_register_component(&pdev->dev,
&img_i2s_out_component, &i2s->dai_driver, 1);
if (ret)
goto err_suspend;
ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
&img_i2s_out_dma_config, 0);
if (ret)
goto err_suspend;
return 0;
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
img_i2s_out_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(i2s->clk_sys);
return ret;
}
static int img_i2s_out_dev_remove(struct platform_device *pdev)
{
struct img_i2s_out *i2s = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
img_i2s_out_suspend(&pdev->dev);
clk_disable_unprepare(i2s->clk_sys);
return 0;
}
static const struct of_device_id img_i2s_out_of_match[] = {
{ .compatible = "img,i2s-out" },
{}
};
MODULE_DEVICE_TABLE(of, img_i2s_out_of_match);
static const struct dev_pm_ops img_i2s_out_pm_ops = {
SET_RUNTIME_PM_OPS(img_i2s_out_suspend,
img_i2s_out_resume, NULL)
};
static struct platform_driver img_i2s_out_driver = {
.driver = {
.name = "img-i2s-out",
.of_match_table = img_i2s_out_of_match,
.pm = &img_i2s_out_pm_ops
},
.probe = img_i2s_out_probe,
.remove = img_i2s_out_dev_remove
};
module_platform_driver(img_i2s_out_driver);
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
MODULE_DESCRIPTION("IMG I2S Output Driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,327 @@
/*
* IMG parallel output controller driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#define IMG_PRL_OUT_TX_FIFO 0
#define IMG_PRL_OUT_CTL 0x4
#define IMG_PRL_OUT_CTL_CH_MASK BIT(4)
#define IMG_PRL_OUT_CTL_PACKH_MASK BIT(3)
#define IMG_PRL_OUT_CTL_EDGE_MASK BIT(2)
#define IMG_PRL_OUT_CTL_ME_MASK BIT(1)
#define IMG_PRL_OUT_CTL_SRST_MASK BIT(0)
struct img_prl_out {
void __iomem *base;
struct clk *clk_sys;
struct clk *clk_ref;
struct snd_dmaengine_dai_dma_data dma_data;
struct device *dev;
struct reset_control *rst;
};
static int img_prl_out_suspend(struct device *dev)
{
struct img_prl_out *prl = dev_get_drvdata(dev);
clk_disable_unprepare(prl->clk_ref);
return 0;
}
static int img_prl_out_resume(struct device *dev)
{
struct img_prl_out *prl = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(prl->clk_ref);
if (ret) {
dev_err(dev, "clk_enable failed: %d\n", ret);
return ret;
}
return 0;
}
static inline void img_prl_out_writel(struct img_prl_out *prl,
u32 val, u32 reg)
{
writel(val, prl->base + reg);
}
static inline u32 img_prl_out_readl(struct img_prl_out *prl, u32 reg)
{
return readl(prl->base + reg);
}
static void img_prl_out_reset(struct img_prl_out *prl)
{
u32 ctl;
ctl = img_prl_out_readl(prl, IMG_PRL_OUT_CTL) &
~IMG_PRL_OUT_CTL_ME_MASK;
reset_control_assert(prl->rst);
reset_control_deassert(prl->rst);
img_prl_out_writel(prl, ctl, IMG_PRL_OUT_CTL);
}
static int img_prl_out_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
u32 reg;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
reg |= IMG_PRL_OUT_CTL_ME_MASK;
img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
img_prl_out_reset(prl);
break;
default:
return -EINVAL;
}
return 0;
}
static int img_prl_out_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
unsigned int rate, channels;
u32 reg, control_set = 0;
snd_pcm_format_t format;
rate = params_rate(params);
format = params_format(params);
channels = params_channels(params);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S32_LE:
control_set |= IMG_PRL_OUT_CTL_PACKH_MASK;
break;
case SNDRV_PCM_FORMAT_S24_LE:
break;
default:
return -EINVAL;
}
if (channels != 2)
return -EINVAL;
clk_set_rate(prl->clk_ref, rate * 256);
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
reg = (reg & ~IMG_PRL_OUT_CTL_PACKH_MASK) | control_set;
img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
return 0;
}
static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
u32 reg, control_set = 0;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
control_set |= IMG_PRL_OUT_CTL_EDGE_MASK;
break;
default:
return -EINVAL;
}
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set;
img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
return 0;
}
static const struct snd_soc_dai_ops img_prl_out_dai_ops = {
.trigger = img_prl_out_trigger,
.hw_params = img_prl_out_hw_params,
.set_fmt = img_prl_out_set_fmt
};
static int img_prl_out_dai_probe(struct snd_soc_dai *dai)
{
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_init_dma_data(dai, &prl->dma_data, NULL);
return 0;
}
static struct snd_soc_dai_driver img_prl_out_dai = {
.probe = img_prl_out_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE
},
.ops = &img_prl_out_dai_ops
};
static const struct snd_soc_component_driver img_prl_out_component = {
.name = "img-prl-out"
};
static int img_prl_out_probe(struct platform_device *pdev)
{
struct img_prl_out *prl;
struct resource *res;
void __iomem *base;
int ret;
struct device *dev = &pdev->dev;
prl = devm_kzalloc(&pdev->dev, sizeof(*prl), GFP_KERNEL);
if (!prl)
return -ENOMEM;
platform_set_drvdata(pdev, prl);
prl->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
prl->base = base;
prl->rst = devm_reset_control_get(&pdev->dev, "rst");
if (IS_ERR(prl->rst)) {
if (PTR_ERR(prl->rst) != -EPROBE_DEFER)
dev_err(&pdev->dev, "No top level reset found\n");
return PTR_ERR(prl->rst);
}
prl->clk_sys = devm_clk_get(&pdev->dev, "sys");
if (IS_ERR(prl->clk_sys)) {
if (PTR_ERR(prl->clk_sys) != -EPROBE_DEFER)
dev_err(dev, "Failed to acquire clock 'sys'\n");
return PTR_ERR(prl->clk_sys);
}
prl->clk_ref = devm_clk_get(&pdev->dev, "ref");
if (IS_ERR(prl->clk_ref)) {
if (PTR_ERR(prl->clk_ref) != -EPROBE_DEFER)
dev_err(dev, "Failed to acquire clock 'ref'\n");
return PTR_ERR(prl->clk_ref);
}
ret = clk_prepare_enable(prl->clk_sys);
if (ret)
return ret;
img_prl_out_writel(prl, IMG_PRL_OUT_CTL_EDGE_MASK, IMG_PRL_OUT_CTL);
img_prl_out_reset(prl);
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = img_prl_out_resume(&pdev->dev);
if (ret)
goto err_pm_disable;
}
prl->dma_data.addr = res->start + IMG_PRL_OUT_TX_FIFO;
prl->dma_data.addr_width = 4;
prl->dma_data.maxburst = 4;
ret = devm_snd_soc_register_component(&pdev->dev,
&img_prl_out_component,
&img_prl_out_dai, 1);
if (ret)
goto err_suspend;
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret)
goto err_suspend;
return 0;
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
img_prl_out_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(prl->clk_sys);
return ret;
}
static int img_prl_out_dev_remove(struct platform_device *pdev)
{
struct img_prl_out *prl = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
img_prl_out_suspend(&pdev->dev);
clk_disable_unprepare(prl->clk_sys);
return 0;
}
static const struct of_device_id img_prl_out_of_match[] = {
{ .compatible = "img,parallel-out" },
{}
};
MODULE_DEVICE_TABLE(of, img_prl_out_of_match);
static const struct dev_pm_ops img_prl_out_pm_ops = {
SET_RUNTIME_PM_OPS(img_prl_out_suspend,
img_prl_out_resume, NULL)
};
static struct platform_driver img_prl_out_driver = {
.driver = {
.name = "img-parallel-out",
.of_match_table = img_prl_out_of_match,
.pm = &img_prl_out_pm_ops
},
.probe = img_prl_out_probe,
.remove = img_prl_out_dev_remove
};
module_platform_driver(img_prl_out_driver);
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
MODULE_DESCRIPTION("IMG Parallel Output Driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,806 @@
/*
* IMG SPDIF input controller driver
*
* Copyright (C) 2015 Imagination Technologies Ltd.
*
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#define IMG_SPDIF_IN_RX_FIFO_OFFSET 0
#define IMG_SPDIF_IN_CTL 0x4
#define IMG_SPDIF_IN_CTL_LOCKLO_MASK 0xff
#define IMG_SPDIF_IN_CTL_LOCKLO_SHIFT 0
#define IMG_SPDIF_IN_CTL_LOCKHI_MASK 0xff00
#define IMG_SPDIF_IN_CTL_LOCKHI_SHIFT 8
#define IMG_SPDIF_IN_CTL_TRK_MASK 0xff0000
#define IMG_SPDIF_IN_CTL_TRK_SHIFT 16
#define IMG_SPDIF_IN_CTL_SRD_MASK 0x70000000
#define IMG_SPDIF_IN_CTL_SRD_SHIFT 28
#define IMG_SPDIF_IN_CTL_SRT_MASK BIT(31)
#define IMG_SPDIF_IN_STATUS 0x8
#define IMG_SPDIF_IN_STATUS_SAM_MASK 0x7000
#define IMG_SPDIF_IN_STATUS_SAM_SHIFT 12
#define IMG_SPDIF_IN_STATUS_LOCK_MASK BIT(15)
#define IMG_SPDIF_IN_STATUS_LOCK_SHIFT 15
#define IMG_SPDIF_IN_CLKGEN 0x1c
#define IMG_SPDIF_IN_CLKGEN_NOM_MASK 0x3ff
#define IMG_SPDIF_IN_CLKGEN_NOM_SHIFT 0
#define IMG_SPDIF_IN_CLKGEN_HLD_MASK 0x3ff0000
#define IMG_SPDIF_IN_CLKGEN_HLD_SHIFT 16
#define IMG_SPDIF_IN_CSL 0x20
#define IMG_SPDIF_IN_CSH 0x24
#define IMG_SPDIF_IN_CSH_MASK 0xff
#define IMG_SPDIF_IN_CSH_SHIFT 0
#define IMG_SPDIF_IN_SOFT_RESET 0x28
#define IMG_SPDIF_IN_SOFT_RESET_MASK BIT(0)
#define IMG_SPDIF_IN_ACLKGEN_START 0x2c
#define IMG_SPDIF_IN_ACLKGEN_NOM_MASK 0x3ff
#define IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT 0
#define IMG_SPDIF_IN_ACLKGEN_HLD_MASK 0xffc00
#define IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT 10
#define IMG_SPDIF_IN_ACLKGEN_TRK_MASK 0xff00000
#define IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT 20
#define IMG_SPDIF_IN_NUM_ACLKGEN 4
struct img_spdif_in {
spinlock_t lock;
void __iomem *base;
struct clk *clk_sys;
struct snd_dmaengine_dai_dma_data dma_data;
struct device *dev;
unsigned int trk;
bool multi_freq;
int lock_acquire;
int lock_release;
unsigned int single_freq;
unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN];
bool active;
/* Write-only registers */
unsigned int aclkgen_regs[IMG_SPDIF_IN_NUM_ACLKGEN];
};
static inline void img_spdif_in_writel(struct img_spdif_in *spdif,
u32 val, u32 reg)
{
writel(val, spdif->base + reg);
}
static inline u32 img_spdif_in_readl(struct img_spdif_in *spdif, u32 reg)
{
return readl(spdif->base + reg);
}
static inline void img_spdif_in_aclkgen_writel(struct img_spdif_in *spdif,
u32 index)
{
img_spdif_in_writel(spdif, spdif->aclkgen_regs[index],
IMG_SPDIF_IN_ACLKGEN_START + (index * 0x4));
}
static int img_spdif_in_check_max_rate(struct img_spdif_in *spdif,
unsigned int sample_rate, unsigned long *actual_freq)
{
unsigned long min_freq, freq_t;
/* Clock rate must be at least 24x the bit rate */
min_freq = sample_rate * 2 * 32 * 24;
freq_t = clk_get_rate(spdif->clk_sys);
if (freq_t < min_freq)
return -EINVAL;
*actual_freq = freq_t;
return 0;
}
static int img_spdif_in_do_clkgen_calc(unsigned int rate, unsigned int *pnom,
unsigned int *phld, unsigned long clk_rate)
{
unsigned int ori, nom, hld;
/*
* Calculate oversampling ratio, nominal phase increment and hold
* increment for the given rate / frequency
*/
if (!rate)
return -EINVAL;
ori = clk_rate / (rate * 64);
if (!ori)
return -EINVAL;
nom = (4096 / ori) + 1;
do
hld = 4096 - (--nom * (ori - 1));
while (hld < 120);
*pnom = nom;
*phld = hld;
return 0;
}
static int img_spdif_in_do_clkgen_single(struct img_spdif_in *spdif,
unsigned int rate)
{
unsigned int nom, hld;
unsigned long flags, clk_rate;
int ret = 0;
u32 reg;
ret = img_spdif_in_check_max_rate(spdif, rate, &clk_rate);
if (ret)
return ret;
ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate);
if (ret)
return ret;
reg = (nom << IMG_SPDIF_IN_CLKGEN_NOM_SHIFT) &
IMG_SPDIF_IN_CLKGEN_NOM_MASK;
reg |= (hld << IMG_SPDIF_IN_CLKGEN_HLD_SHIFT) &
IMG_SPDIF_IN_CLKGEN_HLD_MASK;
spin_lock_irqsave(&spdif->lock, flags);
if (spdif->active) {
spin_unlock_irqrestore(&spdif->lock, flags);
return -EBUSY;
}
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CLKGEN);
spdif->single_freq = rate;
spin_unlock_irqrestore(&spdif->lock, flags);
return 0;
}
static int img_spdif_in_do_clkgen_multi(struct img_spdif_in *spdif,
unsigned int multi_freqs[])
{
unsigned int nom, hld, rate, max_rate = 0;
unsigned long flags, clk_rate;
int i, ret = 0;
u32 reg, trk_reg, temp_regs[IMG_SPDIF_IN_NUM_ACLKGEN];
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++)
if (multi_freqs[i] > max_rate)
max_rate = multi_freqs[i];
ret = img_spdif_in_check_max_rate(spdif, max_rate, &clk_rate);
if (ret)
return ret;
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
rate = multi_freqs[i];
ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate);
if (ret)
return ret;
reg = (nom << IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT) &
IMG_SPDIF_IN_ACLKGEN_NOM_MASK;
reg |= (hld << IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT) &
IMG_SPDIF_IN_ACLKGEN_HLD_MASK;
temp_regs[i] = reg;
}
spin_lock_irqsave(&spdif->lock, flags);
if (spdif->active) {
spin_unlock_irqrestore(&spdif->lock, flags);
return -EBUSY;
}
trk_reg = spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT;
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
spdif->aclkgen_regs[i] = temp_regs[i] | trk_reg;
img_spdif_in_aclkgen_writel(spdif, i);
}
spdif->multi_freq = true;
spdif->multi_freqs[0] = multi_freqs[0];
spdif->multi_freqs[1] = multi_freqs[1];
spdif->multi_freqs[2] = multi_freqs[2];
spdif->multi_freqs[3] = multi_freqs[3];
spin_unlock_irqrestore(&spdif->lock, flags);
return 0;
}
static int img_spdif_in_iec958_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
static int img_spdif_in_get_status_mask(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.iec958.status[0] = 0xff;
ucontrol->value.iec958.status[1] = 0xff;
ucontrol->value.iec958.status[2] = 0xff;
ucontrol->value.iec958.status[3] = 0xff;
ucontrol->value.iec958.status[4] = 0xff;
return 0;
}
static int img_spdif_in_get_status(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
u32 reg;
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSL);
ucontrol->value.iec958.status[0] = reg & 0xff;
ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff;
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSH);
ucontrol->value.iec958.status[4] = (reg & IMG_SPDIF_IN_CSH_MASK)
>> IMG_SPDIF_IN_CSH_SHIFT;
return 0;
}
static int img_spdif_in_info_multi_freq(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = IMG_SPDIF_IN_NUM_ACLKGEN;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = LONG_MAX;
return 0;
}
static int img_spdif_in_get_multi_freq(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
unsigned long flags;
spin_lock_irqsave(&spdif->lock, flags);
if (spdif->multi_freq) {
ucontrol->value.integer.value[0] = spdif->multi_freqs[0];
ucontrol->value.integer.value[1] = spdif->multi_freqs[1];
ucontrol->value.integer.value[2] = spdif->multi_freqs[2];
ucontrol->value.integer.value[3] = spdif->multi_freqs[3];
} else {
ucontrol->value.integer.value[0] = 0;
ucontrol->value.integer.value[1] = 0;
ucontrol->value.integer.value[2] = 0;
ucontrol->value.integer.value[3] = 0;
}
spin_unlock_irqrestore(&spdif->lock, flags);
return 0;
}
static int img_spdif_in_set_multi_freq(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN];
bool multi_freq;
unsigned long flags;
if ((ucontrol->value.integer.value[0] == 0) &&
(ucontrol->value.integer.value[1] == 0) &&
(ucontrol->value.integer.value[2] == 0) &&
(ucontrol->value.integer.value[3] == 0)) {
multi_freq = false;
} else {
multi_freqs[0] = ucontrol->value.integer.value[0];
multi_freqs[1] = ucontrol->value.integer.value[1];
multi_freqs[2] = ucontrol->value.integer.value[2];
multi_freqs[3] = ucontrol->value.integer.value[3];
multi_freq = true;
}
if (multi_freq)
return img_spdif_in_do_clkgen_multi(spdif, multi_freqs);
spin_lock_irqsave(&spdif->lock, flags);
if (spdif->active) {
spin_unlock_irqrestore(&spdif->lock, flags);
return -EBUSY;
}
spdif->multi_freq = false;
spin_unlock_irqrestore(&spdif->lock, flags);
return 0;
}
static int img_spdif_in_info_lock_freq(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = LONG_MAX;
return 0;
}
static int img_spdif_in_get_lock_freq(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uc)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
u32 reg;
int i;
unsigned long flags;
spin_lock_irqsave(&spdif->lock, flags);
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_STATUS);
if (reg & IMG_SPDIF_IN_STATUS_LOCK_MASK) {
if (spdif->multi_freq) {
i = ((reg & IMG_SPDIF_IN_STATUS_SAM_MASK) >>
IMG_SPDIF_IN_STATUS_SAM_SHIFT) - 1;
uc->value.integer.value[0] = spdif->multi_freqs[i];
} else {
uc->value.integer.value[0] = spdif->single_freq;
}
} else {
uc->value.integer.value[0] = 0;
}
spin_unlock_irqrestore(&spdif->lock, flags);
return 0;
}
static int img_spdif_in_info_trk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 255;
return 0;
}
static int img_spdif_in_get_trk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
ucontrol->value.integer.value[0] = spdif->trk;
return 0;
}
static int img_spdif_in_set_trk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
unsigned long flags;
int i;
u32 reg;
spin_lock_irqsave(&spdif->lock, flags);
if (spdif->active) {
spin_unlock_irqrestore(&spdif->lock, flags);
return -EBUSY;
}
spdif->trk = ucontrol->value.integer.value[0];
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
reg &= ~IMG_SPDIF_IN_CTL_TRK_MASK;
reg |= spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT;
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
spdif->aclkgen_regs[i] = (spdif->aclkgen_regs[i] &
~IMG_SPDIF_IN_ACLKGEN_TRK_MASK) |
(spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT);
img_spdif_in_aclkgen_writel(spdif, i);
}
spin_unlock_irqrestore(&spdif->lock, flags);
return 0;
}
static int img_spdif_in_info_lock(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = -128;
uinfo->value.integer.max = 127;
return 0;
}
static int img_spdif_in_get_lock_acquire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
ucontrol->value.integer.value[0] = spdif->lock_acquire;
return 0;
}
static int img_spdif_in_set_lock_acquire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&spdif->lock, flags);
if (spdif->active) {
spin_unlock_irqrestore(&spdif->lock, flags);
return -EBUSY;
}
spdif->lock_acquire = ucontrol->value.integer.value[0];
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
reg &= ~IMG_SPDIF_IN_CTL_LOCKHI_MASK;
reg |= (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) &
IMG_SPDIF_IN_CTL_LOCKHI_MASK;
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
spin_unlock_irqrestore(&spdif->lock, flags);
return 0;
}
static int img_spdif_in_get_lock_release(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
ucontrol->value.integer.value[0] = spdif->lock_release;
return 0;
}
static int img_spdif_in_set_lock_release(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&spdif->lock, flags);
if (spdif->active) {
spin_unlock_irqrestore(&spdif->lock, flags);
return -EBUSY;
}
spdif->lock_release = ucontrol->value.integer.value[0];
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
reg &= ~IMG_SPDIF_IN_CTL_LOCKLO_MASK;
reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) &
IMG_SPDIF_IN_CTL_LOCKLO_MASK;
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
spin_unlock_irqrestore(&spdif->lock, flags);
return 0;
}
static struct snd_kcontrol_new img_spdif_in_controls[] = {
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
.info = img_spdif_in_iec958_info,
.get = img_spdif_in_get_status_mask
},
{
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
.info = img_spdif_in_iec958_info,
.get = img_spdif_in_get_status
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "SPDIF In Multi Frequency Acquire",
.info = img_spdif_in_info_multi_freq,
.get = img_spdif_in_get_multi_freq,
.put = img_spdif_in_set_multi_freq
},
{
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "SPDIF In Lock Frequency",
.info = img_spdif_in_info_lock_freq,
.get = img_spdif_in_get_lock_freq
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "SPDIF In Lock TRK",
.info = img_spdif_in_info_trk,
.get = img_spdif_in_get_trk,
.put = img_spdif_in_set_trk
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "SPDIF In Lock Acquire Threshold",
.info = img_spdif_in_info_lock,
.get = img_spdif_in_get_lock_acquire,
.put = img_spdif_in_set_lock_acquire
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "SPDIF In Lock Release Threshold",
.info = img_spdif_in_info_lock,
.get = img_spdif_in_get_lock_release,
.put = img_spdif_in_set_lock_release
}
};
static int img_spdif_in_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
unsigned long flags;
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
int ret = 0;
u32 reg;
spin_lock_irqsave(&spdif->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
if (spdif->multi_freq)
reg &= ~IMG_SPDIF_IN_CTL_SRD_MASK;
else
reg |= (1UL << IMG_SPDIF_IN_CTL_SRD_SHIFT);
reg |= IMG_SPDIF_IN_CTL_SRT_MASK;
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
spdif->active = true;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
reg &= ~IMG_SPDIF_IN_CTL_SRT_MASK;
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
spdif->active = false;
break;
default:
ret = -EINVAL;
}
spin_unlock_irqrestore(&spdif->lock, flags);
return ret;
}
static int img_spdif_in_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
unsigned int rate, channels;
snd_pcm_format_t format;
rate = params_rate(params);
channels = params_channels(params);
format = params_format(params);
if (format != SNDRV_PCM_FORMAT_S32_LE)
return -EINVAL;
if (channels != 2)
return -EINVAL;
return img_spdif_in_do_clkgen_single(spdif, rate);
}
static const struct snd_soc_dai_ops img_spdif_in_dai_ops = {
.trigger = img_spdif_in_trigger,
.hw_params = img_spdif_in_hw_params
};
static int img_spdif_in_dai_probe(struct snd_soc_dai *dai)
{
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_init_dma_data(dai, NULL, &spdif->dma_data);
snd_soc_add_dai_controls(dai, img_spdif_in_controls,
ARRAY_SIZE(img_spdif_in_controls));
return 0;
}
static struct snd_soc_dai_driver img_spdif_in_dai = {
.probe = img_spdif_in_dai_probe,
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S32_LE
},
.ops = &img_spdif_in_dai_ops
};
static const struct snd_soc_component_driver img_spdif_in_component = {
.name = "img-spdif-in"
};
static int img_spdif_in_probe(struct platform_device *pdev)
{
struct img_spdif_in *spdif;
struct resource *res;
void __iomem *base;
int ret;
struct reset_control *rst;
u32 reg;
struct device *dev = &pdev->dev;
spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
if (!spdif)
return -ENOMEM;
platform_set_drvdata(pdev, spdif);
spdif->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
spdif->base = base;
spdif->clk_sys = devm_clk_get(dev, "sys");
if (IS_ERR(spdif->clk_sys)) {
if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER)
dev_err(dev, "Failed to acquire clock 'sys'\n");
return PTR_ERR(spdif->clk_sys);
}
ret = clk_prepare_enable(spdif->clk_sys);
if (ret)
return ret;
rst = devm_reset_control_get(&pdev->dev, "rst");
if (IS_ERR(rst)) {
if (PTR_ERR(rst) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto err_clk_disable;
}
dev_dbg(dev, "No top level reset found\n");
img_spdif_in_writel(spdif, IMG_SPDIF_IN_SOFT_RESET_MASK,
IMG_SPDIF_IN_SOFT_RESET);
img_spdif_in_writel(spdif, 0, IMG_SPDIF_IN_SOFT_RESET);
} else {
reset_control_assert(rst);
reset_control_deassert(rst);
}
spin_lock_init(&spdif->lock);
spdif->dma_data.addr = res->start + IMG_SPDIF_IN_RX_FIFO_OFFSET;
spdif->dma_data.addr_width = 4;
spdif->dma_data.maxburst = 4;
spdif->trk = 0x80;
spdif->lock_acquire = 4;
spdif->lock_release = -128;
reg = (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) &
IMG_SPDIF_IN_CTL_LOCKHI_MASK;
reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) &
IMG_SPDIF_IN_CTL_LOCKLO_MASK;
reg |= (spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT) &
IMG_SPDIF_IN_CTL_TRK_MASK;
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
ret = devm_snd_soc_register_component(&pdev->dev,
&img_spdif_in_component, &img_spdif_in_dai, 1);
if (ret)
goto err_clk_disable;
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret)
goto err_clk_disable;
return 0;
err_clk_disable:
clk_disable_unprepare(spdif->clk_sys);
return ret;
}
static int img_spdif_in_dev_remove(struct platform_device *pdev)
{
struct img_spdif_in *spdif = platform_get_drvdata(pdev);
clk_disable_unprepare(spdif->clk_sys);
return 0;
}
static const struct of_device_id img_spdif_in_of_match[] = {
{ .compatible = "img,spdif-in" },
{}
};
MODULE_DEVICE_TABLE(of, img_spdif_in_of_match);
static struct platform_driver img_spdif_in_driver = {
.driver = {
.name = "img-spdif-in",
.of_match_table = img_spdif_in_of_match
},
.probe = img_spdif_in_probe,
.remove = img_spdif_in_dev_remove
};
module_platform_driver(img_spdif_in_driver);
MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
MODULE_DESCRIPTION("IMG SPDIF Input driver");
MODULE_LICENSE("GPL v2");

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