mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 14:12:06 +00:00
Merge branch 'asoc-5.2' into asoc-next
This commit is contained in:
commit
378d590c49
@ -1,5 +1,8 @@
|
||||
ADI AXI-I2S controller
|
||||
|
||||
The core can be generated with transmit (playback), only receive
|
||||
(capture) or both directions enabled.
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "adi,axi-i2s-1.00.a"
|
||||
- reg : Must contain I2S core's registers location and length
|
||||
@ -9,8 +12,8 @@ Required properties:
|
||||
- clock-names : "axi" for the clock to the AXI interface, "ref" for the sample
|
||||
rate reference clock.
|
||||
- dmas: Pairs of phandle and specifier for the DMA channels that are used by
|
||||
the core. The core expects two dma channels, one for transmit and one for
|
||||
receive.
|
||||
the core. The core expects two dma channels if both transmit and receive are
|
||||
enabled, one channel otherwise.
|
||||
- dma-names : "tx" for the transmit channel, "rx" for the receive channel.
|
||||
|
||||
For more details on the 'dma', 'dma-names', 'clock' and 'clock-names' properties
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-toddr' or
|
||||
'amlogic,axg-frddr'
|
||||
'amlogic,axg-toddr' or
|
||||
'amlogic,g12a-frddr' or
|
||||
'amlogic,g12a-toddr'
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- interrupts: interrupt specifier for the fifo.
|
||||
|
@ -1,7 +1,8 @@
|
||||
* Amlogic Audio PDM input
|
||||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-pdm'
|
||||
- compatible: 'amlogic,axg-pdm' or
|
||||
'amlogic,g12a-pdm'
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
|
@ -1,7 +1,8 @@
|
||||
* Amlogic Audio SPDIF Input
|
||||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-spdifin'
|
||||
- compatible: 'amlogic,axg-spdifin' or
|
||||
'amlogic,g12a-spdifin'
|
||||
- interrupts: interrupt specifier for the spdif input.
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
- clock-names: should contain the following:
|
||||
|
@ -1,7 +1,8 @@
|
||||
* Amlogic Audio SPDIF Output
|
||||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-spdifout'
|
||||
- compatible: 'amlogic,axg-spdifout' or
|
||||
'amlogic,g12a-spdifout'
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
- clock-names: should contain the following:
|
||||
* "pclk" : peripheral clock.
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
Required properties:
|
||||
- compatible: 'amlogic,axg-tdmin' or
|
||||
'amlogic,axg-tdmout'
|
||||
'amlogic,axg-tdmout' or
|
||||
'amlogic,g12a-tdmin' or
|
||||
'amlogic,g12a-tdmout'
|
||||
- reg: physical base address of the controller and length of memory
|
||||
mapped region.
|
||||
- clocks: list of clock phandle, one for each entry clock-names.
|
||||
|
39
Documentation/devicetree/bindings/sound/cirrus,lochnagar.txt
Normal file
39
Documentation/devicetree/bindings/sound/cirrus,lochnagar.txt
Normal file
@ -0,0 +1,39 @@
|
||||
Cirrus Logic Lochnagar Audio Development Board
|
||||
|
||||
Lochnagar is an evaluation and development board for Cirrus Logic
|
||||
Smart CODEC and Amp devices. It allows the connection of most Cirrus
|
||||
Logic devices on mini-cards, as well as allowing connection of
|
||||
various application processor systems to provide a full evaluation
|
||||
platform. Audio system topology, clocking and power can all be
|
||||
controlled through the Lochnagar, allowing the device under test
|
||||
to be used in a variety of possible use cases.
|
||||
|
||||
This binding document describes the binding for the audio portion
|
||||
of the driver.
|
||||
|
||||
This binding must be part of the Lochnagar MFD binding:
|
||||
[4] ../mfd/cirrus,lochnagar.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : One of the following strings:
|
||||
"cirrus,lochnagar2-soundcard"
|
||||
|
||||
- #sound-dai-cells : Must be set to 1.
|
||||
|
||||
- clocks : Contains an entry for each entry in clock-names.
|
||||
- clock-names : Must include the following clocks:
|
||||
"mclk" Master clock source for the sound card, should normally
|
||||
be set to LOCHNAGAR_SOUNDCARD_MCLK provided by the Lochnagar
|
||||
clock driver.
|
||||
|
||||
Example:
|
||||
|
||||
lochnagar-sc {
|
||||
compatible = "cirrus,lochnagar2-soundcard";
|
||||
|
||||
#sound-dai-cells = <1>;
|
||||
|
||||
clocks = <&lochnagar_clk LOCHNAGAR_SOUNDCARD_MCLK>;
|
||||
clock-names = "mclk";
|
||||
};
|
@ -1,6 +1,17 @@
|
||||
CS42L51 audio CODEC
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "cirrus,cs42l51"
|
||||
|
||||
- reg : the I2C address of the device for I2C.
|
||||
|
||||
Optional properties:
|
||||
- VL-supply, VD-supply, VA-supply, VAHP-supply: power supplies for the device,
|
||||
as covered in Documentation/devicetree/bindings/regulator/regulator.txt.
|
||||
|
||||
- reset-gpios : GPIO specification for the reset pin. If specified, it will be
|
||||
deasserted before starting the communication with the codec.
|
||||
|
||||
- clocks : a list of phandles + clock-specifiers, one for each entry in
|
||||
clock-names
|
||||
@ -14,4 +25,9 @@ cs42l51: cs42l51@4a {
|
||||
reg = <0x4a>;
|
||||
clocks = <&mclk_prov>;
|
||||
clock-names = "MCLK";
|
||||
VL-supply = <®_audio>;
|
||||
VD-supply = <®_audio>;
|
||||
VA-supply = <®_audio>;
|
||||
VAHP-supply = <®_audio>;
|
||||
reset-gpios = <&gpiog 9 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
@ -23,8 +23,8 @@ Optional properties:
|
||||
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).
|
||||
|
||||
- #clock-cells : Should be set to '<0>', only one clock source provided;
|
||||
- clock-output-names : Name given for DAI clocks output;
|
||||
- #clock-cells : Should be set to '<1>', two clock sources provided;
|
||||
- clock-output-names : Names given for DAI clock outputs (WCLK & BCLK);
|
||||
|
||||
- clocks : phandle and clock specifier for codec MCLK.
|
||||
- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
|
||||
@ -84,8 +84,8 @@ Example:
|
||||
VDDMIC-supply = <®_audio>;
|
||||
VDDIO-supply = <®_audio>;
|
||||
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "dai-clks";
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "dai-wclk", "dai-bclk";
|
||||
|
||||
clocks = <&clks 201>;
|
||||
clock-names = "mclk";
|
||||
|
50
Documentation/devicetree/bindings/sound/fsl,audmix.txt
Normal file
50
Documentation/devicetree/bindings/sound/fsl,audmix.txt
Normal file
@ -0,0 +1,50 @@
|
||||
NXP Audio Mixer (AUDMIX).
|
||||
|
||||
The Audio Mixer is a on-chip functional module that allows mixing of two
|
||||
audio streams into a single audio stream. Audio Mixer has two input serial
|
||||
audio interfaces. These are driven by two Synchronous Audio interface
|
||||
modules (SAI). Each input serial interface carries 8 audio channels in its
|
||||
frame in TDM manner. Mixer mixes audio samples of corresponding channels
|
||||
from two interfaces into a single sample. Before mixing, audio samples of
|
||||
two inputs can be attenuated based on configuration. The output of the
|
||||
Audio Mixer is also a serial audio interface. Like input interfaces it has
|
||||
the same TDM frame format. This output is used to drive the serial DAC TDM
|
||||
interface of audio codec and also sent to the external pins along with the
|
||||
receive path of normal audio SAI module for readback by the CPU.
|
||||
|
||||
The output of Audio Mixer can be selected from any of the three streams
|
||||
- serial audio input 1
|
||||
- serial audio input 2
|
||||
- mixed audio
|
||||
|
||||
Mixing operation is independent of audio sample rate but the two audio
|
||||
input streams must have same audio sample rate with same number of channels
|
||||
in TDM frame to be eligible for mixing.
|
||||
|
||||
Device driver required properties:
|
||||
=================================
|
||||
- compatible : Compatible list, contains "fsl,imx8qm-audmix"
|
||||
|
||||
- reg : Offset and length of the register set for the device.
|
||||
|
||||
- clocks : Must contain an entry for each entry in clock-names.
|
||||
|
||||
- clock-names : Must include the "ipg" for register access.
|
||||
|
||||
- power-domains : Must contain the phandle to AUDMIX power domain node
|
||||
|
||||
- dais : Must contain a list of phandles to AUDMIX connected
|
||||
DAIs. The current implementation requires two phandles
|
||||
to SAI interfaces to be provided, the first SAI in the
|
||||
list being used to route the AUDMIX output.
|
||||
|
||||
Device driver configuration example:
|
||||
======================================
|
||||
audmix: audmix@59840000 {
|
||||
compatible = "fsl,imx8qm-audmix";
|
||||
reg = <0x0 0x59840000 0x0 0x10000>;
|
||||
clocks = <&clk IMX8QXP_AUD_AUDMIX_IPG>;
|
||||
clock-names = "ipg";
|
||||
power-domains = <&pd_audmix>;
|
||||
dais = <&sai4>, <&sai5>;
|
||||
};
|
43
Documentation/devicetree/bindings/sound/mchp-i2s-mcc.txt
Normal file
43
Documentation/devicetree/bindings/sound/mchp-i2s-mcc.txt
Normal file
@ -0,0 +1,43 @@
|
||||
* Microchip I2S Multi-Channel Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "microchip,sam9x60-i2smcc".
|
||||
- reg: Should be the physical base address of the controller and the
|
||||
length of memory mapped region.
|
||||
- interrupts: Should contain the interrupt for the controller.
|
||||
- dmas: Should be one per channel name listed in the dma-names property,
|
||||
as described in atmel-dma.txt and dma.txt files.
|
||||
- dma-names: Identifier string for each DMA request line in the dmas property.
|
||||
Two dmas have to be defined, "tx" and "rx".
|
||||
- clocks: Must contain an entry for each entry in clock-names.
|
||||
Please refer to clock-bindings.txt.
|
||||
- clock-names: Should be one of each entry matching the clocks phandles list:
|
||||
- "pclk" (peripheral clock) Required.
|
||||
- "gclk" (generated clock) Optional (1).
|
||||
|
||||
Optional properties:
|
||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
||||
- princtrl-names: Should contain only one value - "default".
|
||||
|
||||
|
||||
(1) : Only the peripheral clock is required. The generated clock is optional
|
||||
and should be set mostly when Master Mode is required.
|
||||
|
||||
Example:
|
||||
|
||||
i2s@f001c000 {
|
||||
compatible = "microchip,sam9x60-i2smcc";
|
||||
reg = <0xf001c000 0x100>;
|
||||
interrupts = <34 IRQ_TYPE_LEVEL_HIGH 7>;
|
||||
dmas = <&dma0
|
||||
(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
|
||||
AT91_XDMAC_DT_PERID(36))>,
|
||||
<&dma0
|
||||
(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
|
||||
AT91_XDMAC_DT_PERID(37))>;
|
||||
dma-names = "tx", "rx";
|
||||
clocks = <&i2s_clk>, <&i2s_gclk>;
|
||||
clock-names = "pclk", "gclk";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_i2s_default>;
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
MT8183 with MT6358, DA7219 and MAX98357 CODECS
|
||||
|
||||
Required properties:
|
||||
- compatible : "mediatek,mt8183_da7219_max98357"
|
||||
- mediatek,headset-codec: the phandles of da7219 codecs
|
||||
- mediatek,platform: the phandle of MT8183 ASoC platform
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "mediatek,mt8183_da7219_max98357";
|
||||
mediatek,headset-codec = <&da7219>;
|
||||
mediatek,platform = <&afe>;
|
||||
};
|
||||
|
@ -0,0 +1,15 @@
|
||||
MT8183 with MT6358, TS3A227 and MAX98357 CODECS
|
||||
|
||||
Required properties:
|
||||
- compatible : "mediatek,mt8183_mt6358_ts3a227_max98357"
|
||||
- mediatek,headset-codec: the phandles of ts3a227 codecs
|
||||
- mediatek,platform: the phandle of MT8183 ASoC platform
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "mediatek,mt8183_mt6358_ts3a227_max98357";
|
||||
mediatek,headset-codec = <&ts3a227>;
|
||||
mediatek,platform = <&afe>;
|
||||
};
|
||||
|
@ -266,6 +266,7 @@ Required properties:
|
||||
- "renesas,rcar_sound-r8a7743" (RZ/G1M)
|
||||
- "renesas,rcar_sound-r8a7744" (RZ/G1N)
|
||||
- "renesas,rcar_sound-r8a7745" (RZ/G1E)
|
||||
- "renesas,rcar_sound-r8a77470" (RZ/G1C)
|
||||
- "renesas,rcar_sound-r8a774a1" (RZ/G2M)
|
||||
- "renesas,rcar_sound-r8a774c0" (RZ/G2E)
|
||||
- "renesas,rcar_sound-r8a7778" (R-Car M1A)
|
||||
@ -282,7 +283,12 @@ Required properties:
|
||||
- reg : Should contain the register physical address.
|
||||
required register is
|
||||
SRU/ADG/SSI if generation1
|
||||
SRU/ADG/SSIU/SSI if generation2
|
||||
SRU/ADG/SSIU/SSI/AUDIO-DMAC-periperi if generation2/generation3
|
||||
Select extended AUDIO-DMAC-periperi address if SoC has it,
|
||||
otherwise select normal AUDIO-DMAC-periperi address.
|
||||
- reg-names : Should contain the register names.
|
||||
scu/adg/ssi if generation1
|
||||
scu/adg/ssiu/ssi/audmapp if generation2/generation3
|
||||
- rcar_sound,ssi : Should contain SSI feature.
|
||||
The number of SSI subnode should be same as HW.
|
||||
see below for detail.
|
||||
|
@ -3,6 +3,9 @@
|
||||
Required properties:
|
||||
|
||||
- compatible: "rockchip,pdm"
|
||||
- "rockchip,px30-pdm"
|
||||
- "rockchip,rk1808-pdm"
|
||||
- "rockchip,rk3308-pdm"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- dmas: DMA specifiers for rx dma. See the DMA client binding,
|
||||
@ -12,6 +15,8 @@ Required properties:
|
||||
- clock-names: should contain following:
|
||||
- "pdm_hclk": clock for PDM BUS
|
||||
- "pdm_clk" : clock for PDM controller
|
||||
- resets: a list of phandle + reset-specifer paris, one for each entry in reset-names.
|
||||
- reset-names: reset names, should include "pdm-m".
|
||||
- pinctrl-names: Must contain a "default" entry.
|
||||
- pinctrl-N: One property must exist for each entry in
|
||||
pinctrl-names. See ../pinctrl/pinctrl-bindings.txt
|
||||
|
@ -22,6 +22,11 @@ Optional properties:
|
||||
2: Use JD1_2 pin for jack-detect
|
||||
3: Use JD2 pin for jack-detect
|
||||
|
||||
- realtek,jack-detect-not-inverted
|
||||
bool. Normal jack-detect switches give an inverted (active-low) signal,
|
||||
set this bool in the rare case you've a jack-detect switch which is not
|
||||
inverted.
|
||||
|
||||
- realtek,over-current-threshold-microamp
|
||||
u32, micbias over-current detection threshold in µA, valid values are
|
||||
600, 1500 and 2000µA.
|
||||
|
@ -2,9 +2,9 @@ Simple Amplifier Audio Driver
|
||||
|
||||
Required properties:
|
||||
- compatible : "dioo,dio2125" or "simple-audio-amplifier"
|
||||
- enable-gpios : the gpio connected to the enable pin of the simple amplifier
|
||||
|
||||
Optional properties:
|
||||
- enable-gpios : the gpio connected to the enable pin of the simple amplifier
|
||||
- VCC-supply : power supply for the device, as covered
|
||||
in Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
|
||||
|
@ -24,6 +24,8 @@ Optional properties:
|
||||
a microphone is attached.
|
||||
- simple-audio-card,aux-devs : List of phandles pointing to auxiliary devices, such
|
||||
as amplifiers, to be added to the sound card.
|
||||
- simple-audio-card,pin-switches : List of strings containing the widget names for
|
||||
which pin switches must be created.
|
||||
|
||||
Optional subnodes:
|
||||
|
||||
|
19
Documentation/devicetree/bindings/sound/sprd-mcdt.txt
Normal file
19
Documentation/devicetree/bindings/sound/sprd-mcdt.txt
Normal file
@ -0,0 +1,19 @@
|
||||
Spreadtrum Multi-Channel Data Transfer Binding
|
||||
|
||||
The Multi-channel data transfer controller is used for sound stream
|
||||
transmission between audio subsystem and other AP/CP subsystem. It
|
||||
supports 10 DAC channel and 10 ADC channel, and each channel can be
|
||||
configured with DMA mode or interrupt mode.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "sprd,sc9860-mcdt".
|
||||
- reg: Should contain registers address and length.
|
||||
- interrupts: Should contain one interrupt shared by all channel.
|
||||
|
||||
Example:
|
||||
|
||||
mcdt@41490000 {
|
||||
compatible = "sprd,sc9860-mcdt";
|
||||
reg = <0 0x41490000 0 0x170>;
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
@ -3801,6 +3801,7 @@ F: drivers/clk/clk-lochnagar.c
|
||||
F: drivers/mfd/lochnagar-i2c.c
|
||||
F: drivers/pinctrl/cirrus/pinctrl-lochnagar.c
|
||||
F: drivers/regulator/lochnagar-regulator.c
|
||||
F: sound/soc/codecs/lochnagar-sc.c
|
||||
F: include/dt-bindings/clk/lochnagar.h
|
||||
F: include/dt-bindings/pinctrl/lochnagar.h
|
||||
F: include/linux/mfd/lochnagar*
|
||||
@ -3808,6 +3809,7 @@ F: Documentation/devicetree/bindings/mfd/cirrus,lochnagar.txt
|
||||
F: Documentation/devicetree/bindings/clock/cirrus,lochnagar.txt
|
||||
F: Documentation/devicetree/bindings/pinctrl/cirrus,lochnagar.txt
|
||||
F: Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt
|
||||
F: Documentation/devicetree/bindings/sound/cirrus,lochnagar.txt
|
||||
|
||||
CISCO FCOE HBA DRIVER
|
||||
M: Satish Kharat <satishkh@cisco.com>
|
||||
|
@ -739,6 +739,7 @@ EXPORT_SYMBOL(acpi_dev_found);
|
||||
|
||||
struct acpi_dev_match_info {
|
||||
const char *dev_name;
|
||||
struct acpi_device *adev;
|
||||
struct acpi_device_id hid[2];
|
||||
const char *uid;
|
||||
s64 hrv;
|
||||
@ -759,6 +760,7 @@ static int acpi_dev_match_cb(struct device *dev, void *data)
|
||||
return 0;
|
||||
|
||||
match->dev_name = acpi_dev_name(adev);
|
||||
match->adev = adev;
|
||||
|
||||
if (match->hrv == -1)
|
||||
return 1;
|
||||
@ -806,18 +808,20 @@ bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
|
||||
EXPORT_SYMBOL(acpi_dev_present);
|
||||
|
||||
/**
|
||||
* acpi_dev_get_first_match_name - Return name of first match of ACPI device
|
||||
* acpi_dev_get_first_match_dev - Return the first match of ACPI device
|
||||
* @hid: Hardware ID of the device.
|
||||
* @uid: Unique ID of the device, pass NULL to not check _UID
|
||||
* @hrv: Hardware Revision of the device, pass -1 to not check _HRV
|
||||
*
|
||||
* Return device name if a matching device was present
|
||||
* Return the first match of ACPI device if a matching device was present
|
||||
* at the moment of invocation, or NULL otherwise.
|
||||
*
|
||||
* The caller is responsible to call put_device() on the returned device.
|
||||
*
|
||||
* See additional information in acpi_dev_present() as well.
|
||||
*/
|
||||
const char *
|
||||
acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv)
|
||||
struct acpi_device *
|
||||
acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
|
||||
{
|
||||
struct acpi_dev_match_info match = {};
|
||||
struct device *dev;
|
||||
@ -827,9 +831,9 @@ acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv)
|
||||
match.hrv = hrv;
|
||||
|
||||
dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
|
||||
return dev ? match.dev_name : NULL;
|
||||
return dev ? match.adev : NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_dev_get_first_match_name);
|
||||
EXPORT_SYMBOL(acpi_dev_get_first_match_dev);
|
||||
|
||||
/*
|
||||
* acpi_backlight= handling, this is done here rather then in video_detect.c
|
||||
|
@ -333,7 +333,7 @@ static int axp288_extcon_probe(struct platform_device *pdev)
|
||||
struct axp288_extcon_info *info;
|
||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
const char *name;
|
||||
struct acpi_device *adev;
|
||||
int ret, i, pirq;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
@ -357,9 +357,10 @@ static int axp288_extcon_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
name = acpi_dev_get_first_match_name("INT3496", NULL, -1);
|
||||
if (name) {
|
||||
info->id_extcon = extcon_get_extcon_dev(name);
|
||||
adev = acpi_dev_get_first_match_dev("INT3496", NULL, -1);
|
||||
if (adev) {
|
||||
info->id_extcon = extcon_get_extcon_dev(acpi_dev_name(adev));
|
||||
put_device(&adev->dev);
|
||||
if (!info->id_extcon)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
|
@ -377,10 +377,20 @@ static void mrfld_irq_init_hw(struct mrfld_gpio *priv)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *mrfld_gpio_get_pinctrl_dev_name(void)
|
||||
static const char *mrfld_gpio_get_pinctrl_dev_name(struct mrfld_gpio *priv)
|
||||
{
|
||||
const char *dev_name = acpi_dev_get_first_match_name("INTC1002", NULL, -1);
|
||||
return dev_name ? dev_name : "pinctrl-merrifield";
|
||||
struct acpi_device *adev;
|
||||
const char *name;
|
||||
|
||||
adev = acpi_dev_get_first_match_dev("INTC1002", NULL, -1);
|
||||
if (adev) {
|
||||
name = devm_kstrdup(priv->dev, acpi_dev_name(adev), GFP_KERNEL);
|
||||
put_device(&adev->dev);
|
||||
} else {
|
||||
name = "pinctrl-merrifield";
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
@ -441,7 +451,7 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
|
||||
return retval;
|
||||
}
|
||||
|
||||
pinctrl_dev_name = mrfld_gpio_get_pinctrl_dev_name();
|
||||
pinctrl_dev_name = mrfld_gpio_get_pinctrl_dev_name(priv);
|
||||
for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) {
|
||||
range = &mrfld_gpio_ranges[i];
|
||||
retval = gpiochip_add_pin_range(&priv->chip,
|
||||
|
@ -91,8 +91,8 @@ acpi_evaluate_dsm_typed(acpi_handle handle, const guid_t *guid, u64 rev,
|
||||
bool acpi_dev_found(const char *hid);
|
||||
bool acpi_dev_present(const char *hid, const char *uid, s64 hrv);
|
||||
|
||||
const char *
|
||||
acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv);
|
||||
struct acpi_device *
|
||||
acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
|
@ -669,8 +669,8 @@ static inline bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline const char *
|
||||
acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv)
|
||||
static inline struct acpi_device *
|
||||
acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -33,10 +33,16 @@ enum da7219_mic_amp_in_sel {
|
||||
|
||||
struct da7219_aad_pdata;
|
||||
|
||||
enum da7219_dai_clks {
|
||||
DA7219_DAI_WCLK_IDX = 0,
|
||||
DA7219_DAI_BCLK_IDX,
|
||||
DA7219_DAI_NUM_CLKS,
|
||||
};
|
||||
|
||||
struct da7219_pdata {
|
||||
bool wakeup_source;
|
||||
|
||||
const char *dai_clks_name;
|
||||
const char *dai_clk_names[DA7219_DAI_NUM_CLKS];
|
||||
|
||||
/* Mic */
|
||||
enum da7219_micbias_voltage micbias_lvl;
|
||||
|
@ -10,10 +10,10 @@
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define asoc_simple_card_init_hp(card, sjack, prefix) \
|
||||
asoc_simple_card_init_jack(card, sjack, 1, prefix)
|
||||
#define asoc_simple_card_init_mic(card, sjack, prefix) \
|
||||
asoc_simple_card_init_jack(card, sjack, 0, prefix)
|
||||
#define asoc_simple_init_hp(card, sjack, prefix) \
|
||||
asoc_simple_init_jack(card, sjack, 1, prefix)
|
||||
#define asoc_simple_init_mic(card, sjack, prefix) \
|
||||
asoc_simple_init_jack(card, sjack, 0, prefix)
|
||||
|
||||
struct asoc_simple_dai {
|
||||
const char *name;
|
||||
@ -26,7 +26,7 @@ struct asoc_simple_dai {
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
struct asoc_simple_card_data {
|
||||
struct asoc_simple_data {
|
||||
u32 convert_rate;
|
||||
u32 convert_channels;
|
||||
};
|
||||
@ -37,96 +37,180 @@ struct asoc_simple_jack {
|
||||
struct snd_soc_jack_gpio gpio;
|
||||
};
|
||||
|
||||
int asoc_simple_card_parse_daifmt(struct device *dev,
|
||||
struct device_node *node,
|
||||
struct device_node *codec,
|
||||
char *prefix,
|
||||
unsigned int *retfmt);
|
||||
struct asoc_simple_priv {
|
||||
struct snd_soc_card snd_card;
|
||||
struct simple_dai_props {
|
||||
struct asoc_simple_dai *cpu_dai;
|
||||
struct asoc_simple_dai *codec_dai;
|
||||
struct snd_soc_dai_link_component codecs; /* single codec */
|
||||
struct snd_soc_dai_link_component platforms;
|
||||
struct asoc_simple_data adata;
|
||||
struct snd_soc_codec_conf *codec_conf;
|
||||
unsigned int mclk_fs;
|
||||
} *dai_props;
|
||||
struct asoc_simple_jack hp_jack;
|
||||
struct asoc_simple_jack mic_jack;
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
struct asoc_simple_dai *dais;
|
||||
struct snd_soc_codec_conf *codec_conf;
|
||||
struct gpio_desc *pa_gpio;
|
||||
};
|
||||
#define simple_priv_to_card(priv) (&(priv)->snd_card)
|
||||
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
|
||||
#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
|
||||
#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
|
||||
|
||||
struct link_info {
|
||||
int dais; /* number of dai */
|
||||
int link; /* number of link */
|
||||
int conf; /* number of codec_conf */
|
||||
int cpu; /* turn for CPU / Codec */
|
||||
};
|
||||
|
||||
int asoc_simple_parse_daifmt(struct device *dev,
|
||||
struct device_node *node,
|
||||
struct device_node *codec,
|
||||
char *prefix,
|
||||
unsigned int *retfmt);
|
||||
__printf(3, 4)
|
||||
int asoc_simple_card_set_dailink_name(struct device *dev,
|
||||
struct snd_soc_dai_link *dai_link,
|
||||
const char *fmt, ...);
|
||||
int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
|
||||
char *prefix);
|
||||
int asoc_simple_set_dailink_name(struct device *dev,
|
||||
struct snd_soc_dai_link *dai_link,
|
||||
const char *fmt, ...);
|
||||
int asoc_simple_parse_card_name(struct snd_soc_card *card,
|
||||
char *prefix);
|
||||
|
||||
#define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \
|
||||
#define asoc_simple_parse_clk_cpu(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \
|
||||
dai_link->cpu_dai_name, NULL)
|
||||
#define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\
|
||||
#define asoc_simple_parse_clk_codec(dev, node, dai_link, simple_dai) \
|
||||
asoc_simple_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\
|
||||
dai_link->codec_dai_name, dai_link->codecs)
|
||||
int asoc_simple_card_parse_clk(struct device *dev,
|
||||
struct device_node *node,
|
||||
struct device_node *dai_of_node,
|
||||
struct asoc_simple_dai *simple_dai,
|
||||
const char *dai_name,
|
||||
struct snd_soc_dai_link_component *dlc);
|
||||
int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai);
|
||||
void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai);
|
||||
int asoc_simple_parse_clk(struct device *dev,
|
||||
struct device_node *node,
|
||||
struct device_node *dai_of_node,
|
||||
struct asoc_simple_dai *simple_dai,
|
||||
const char *dai_name,
|
||||
struct snd_soc_dai_link_component *dlc);
|
||||
int asoc_simple_startup(struct snd_pcm_substream *substream);
|
||||
void asoc_simple_shutdown(struct snd_pcm_substream *substream);
|
||||
int asoc_simple_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params);
|
||||
int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd);
|
||||
int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params);
|
||||
|
||||
#define asoc_simple_card_parse_cpu(node, dai_link, \
|
||||
list_name, cells_name, is_single_link) \
|
||||
asoc_simple_card_parse_dai(node, NULL, \
|
||||
&dai_link->cpu_of_node, \
|
||||
&dai_link->cpu_dai_name, list_name, cells_name, is_single_link)
|
||||
#define asoc_simple_card_parse_codec(node, dai_link, list_name, cells_name) \
|
||||
asoc_simple_card_parse_dai(node, dai_link->codecs, \
|
||||
#define asoc_simple_parse_cpu(node, dai_link, is_single_link) \
|
||||
asoc_simple_parse_dai(node, NULL, \
|
||||
&dai_link->cpu_of_node, \
|
||||
&dai_link->cpu_dai_name, is_single_link)
|
||||
#define asoc_simple_parse_codec(node, dai_link) \
|
||||
asoc_simple_parse_dai(node, dai_link->codecs, \
|
||||
&dai_link->codec_of_node, \
|
||||
&dai_link->codec_dai_name, \
|
||||
list_name, cells_name, NULL)
|
||||
#define asoc_simple_card_parse_platform(node, dai_link, list_name, cells_name) \
|
||||
asoc_simple_card_parse_dai(node, dai_link->platforms, \
|
||||
&dai_link->platform_of_node, \
|
||||
NULL, list_name, cells_name, NULL)
|
||||
int asoc_simple_card_parse_dai(struct device_node *node,
|
||||
struct snd_soc_dai_link_component *dlc,
|
||||
struct device_node **endpoint_np,
|
||||
const char **dai_name,
|
||||
const char *list_name,
|
||||
const char *cells_name,
|
||||
int *is_single_links);
|
||||
&dai_link->codec_dai_name, NULL)
|
||||
#define asoc_simple_parse_platform(node, dai_link) \
|
||||
asoc_simple_parse_dai(node, dai_link->platforms, \
|
||||
&dai_link->platform_of_node, NULL, NULL)
|
||||
|
||||
#define asoc_simple_card_parse_graph_cpu(ep, dai_link) \
|
||||
asoc_simple_card_parse_graph_dai(ep, NULL, \
|
||||
&dai_link->cpu_of_node, \
|
||||
&dai_link->cpu_dai_name)
|
||||
#define asoc_simple_card_parse_graph_codec(ep, dai_link) \
|
||||
asoc_simple_card_parse_graph_dai(ep, dai_link->codecs, \
|
||||
&dai_link->codec_of_node, \
|
||||
&dai_link->codec_dai_name)
|
||||
int asoc_simple_card_parse_graph_dai(struct device_node *ep,
|
||||
struct snd_soc_dai_link_component *dlc,
|
||||
struct device_node **endpoint_np,
|
||||
const char **dai_name);
|
||||
|
||||
#define asoc_simple_card_of_parse_tdm(np, dai) \
|
||||
#define asoc_simple_parse_tdm(np, dai) \
|
||||
snd_soc_of_parse_tdm_slot(np, &(dai)->tx_slot_mask, \
|
||||
&(dai)->rx_slot_mask, \
|
||||
&(dai)->slots, \
|
||||
&(dai)->slot_width);
|
||||
|
||||
int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
|
||||
struct asoc_simple_dai *simple_dai);
|
||||
|
||||
void asoc_simple_card_canonicalize_platform(struct snd_soc_dai_link *dai_link);
|
||||
void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
|
||||
void asoc_simple_canonicalize_platform(struct snd_soc_dai_link *dai_link);
|
||||
void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
|
||||
int is_single_links);
|
||||
|
||||
int asoc_simple_card_clean_reference(struct snd_soc_card *card);
|
||||
int asoc_simple_clean_reference(struct snd_soc_card *card);
|
||||
|
||||
void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
|
||||
void asoc_simple_convert_fixup(struct asoc_simple_data *data,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void asoc_simple_card_parse_convert(struct device *dev,
|
||||
struct device_node *np, char *prefix,
|
||||
struct asoc_simple_card_data *data);
|
||||
void asoc_simple_parse_convert(struct device *dev,
|
||||
struct device_node *np, char *prefix,
|
||||
struct asoc_simple_data *data);
|
||||
|
||||
int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
|
||||
int asoc_simple_parse_routing(struct snd_soc_card *card,
|
||||
char *prefix);
|
||||
int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
|
||||
int asoc_simple_parse_widgets(struct snd_soc_card *card,
|
||||
char *prefix);
|
||||
int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
|
||||
char *prefix);
|
||||
|
||||
int asoc_simple_card_init_jack(struct snd_soc_card *card,
|
||||
int asoc_simple_init_jack(struct snd_soc_card *card,
|
||||
struct asoc_simple_jack *sjack,
|
||||
int is_hp, char *prefix);
|
||||
int asoc_simple_init_priv(struct asoc_simple_priv *priv,
|
||||
struct link_info *li);
|
||||
|
||||
#ifdef DEBUG
|
||||
inline void asoc_simple_debug_dai(struct asoc_simple_priv *priv,
|
||||
char *name,
|
||||
struct asoc_simple_dai *dai)
|
||||
{
|
||||
struct device *dev = simple_priv_to_dev(priv);
|
||||
|
||||
if (dai->name)
|
||||
dev_dbg(dev, "%s dai name = %s\n",
|
||||
name, dai->name);
|
||||
if (dai->sysclk)
|
||||
dev_dbg(dev, "%s sysclk = %d\n",
|
||||
name, dai->sysclk);
|
||||
|
||||
dev_dbg(dev, "%s direction = %s\n",
|
||||
name, dai->clk_direction ? "OUT" : "IN");
|
||||
|
||||
if (dai->slots)
|
||||
dev_dbg(dev, "%s slots = %d\n", name, dai->slots);
|
||||
if (dai->slot_width)
|
||||
dev_dbg(dev, "%s slot width = %d\n", name, dai->slot_width);
|
||||
if (dai->tx_slot_mask)
|
||||
dev_dbg(dev, "%s tx slot mask = %d\n", name, dai->tx_slot_mask);
|
||||
if (dai->rx_slot_mask)
|
||||
dev_dbg(dev, "%s rx slot mask = %d\n", name, dai->rx_slot_mask);
|
||||
if (dai->clk)
|
||||
dev_dbg(dev, "%s clk %luHz\n", name, clk_get_rate(dai->clk));
|
||||
}
|
||||
|
||||
inline void asoc_simple_debug_info(struct asoc_simple_priv *priv)
|
||||
{
|
||||
struct snd_soc_card *card = simple_priv_to_card(priv);
|
||||
struct device *dev = simple_priv_to_dev(priv);
|
||||
|
||||
int i;
|
||||
|
||||
if (card->name)
|
||||
dev_dbg(dev, "Card Name: %s\n", card->name);
|
||||
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
struct simple_dai_props *props = simple_priv_to_props(priv, i);
|
||||
struct snd_soc_dai_link *link = simple_priv_to_link(priv, i);
|
||||
|
||||
dev_dbg(dev, "DAI%d\n", i);
|
||||
|
||||
asoc_simple_debug_dai(priv, "cpu", props->cpu_dai);
|
||||
asoc_simple_debug_dai(priv, "codec", props->codec_dai);
|
||||
|
||||
if (link->name)
|
||||
dev_dbg(dev, "dai name = %s\n", link->name);
|
||||
|
||||
dev_dbg(dev, "dai format = %04x\n", link->dai_fmt);
|
||||
|
||||
if (props->adata.convert_rate)
|
||||
dev_dbg(dev, "convert_rate = %d\n",
|
||||
props->adata.convert_rate);
|
||||
if (props->adata.convert_channels)
|
||||
dev_dbg(dev, "convert_channels = %d\n",
|
||||
props->adata.convert_channels);
|
||||
if (props->codec_conf && props->codec_conf->name_prefix)
|
||||
dev_dbg(dev, "name prefix = %s\n",
|
||||
props->codec_conf->name_prefix);
|
||||
if (props->mclk_fs)
|
||||
dev_dbg(dev, "mclk-fs = %d\n",
|
||||
props->mclk_fs);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define asoc_simple_debug_info(priv)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* __SIMPLE_CARD_UTILS_H */
|
||||
|
100
include/sound/sof.h
Normal file
100
include/sound/sof.h
Normal file
@ -0,0 +1,100 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_H
|
||||
#define __INCLUDE_SOUND_SOF_H
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-acpi.h>
|
||||
|
||||
struct snd_sof_dsp_ops;
|
||||
|
||||
/*
|
||||
* SOF Platform data.
|
||||
*/
|
||||
struct snd_sof_pdata {
|
||||
const struct firmware *fw;
|
||||
const char *drv_name;
|
||||
const char *name;
|
||||
const char *platform;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
/*
|
||||
* notification callback used if the hardware initialization
|
||||
* can take time or is handled in a workqueue. This callback
|
||||
* can be used by the caller to e.g. enable runtime_pm
|
||||
* or limit functionality until all low-level inits are
|
||||
* complete.
|
||||
*/
|
||||
void (*sof_probe_complete)(struct device *dev);
|
||||
|
||||
/* descriptor */
|
||||
const struct sof_dev_desc *desc;
|
||||
|
||||
/* firmware and topology filenames */
|
||||
const char *fw_filename_prefix;
|
||||
const char *fw_filename;
|
||||
const char *tplg_filename_prefix;
|
||||
const char *tplg_filename;
|
||||
|
||||
/* machine */
|
||||
struct platform_device *pdev_mach;
|
||||
const struct snd_soc_acpi_mach *machine;
|
||||
|
||||
void *hw_pdata;
|
||||
};
|
||||
|
||||
/*
|
||||
* Descriptor used for setting up SOF platform data. This is used when
|
||||
* ACPI/PCI data is missing or mapped differently.
|
||||
*/
|
||||
struct sof_dev_desc {
|
||||
/* list of machines using this configuration */
|
||||
struct snd_soc_acpi_mach *machines;
|
||||
|
||||
/* Platform resource indexes in BAR / ACPI resources. */
|
||||
/* Must set to -1 if not used - add new items to end */
|
||||
int resindex_lpe_base;
|
||||
int resindex_pcicfg_base;
|
||||
int resindex_imr_base;
|
||||
int irqindex_host_ipc;
|
||||
int resindex_dma_base;
|
||||
|
||||
/* DMA only valid when resindex_dma_base != -1*/
|
||||
int dma_engine;
|
||||
int dma_size;
|
||||
|
||||
/* IPC timeouts in ms */
|
||||
int ipc_timeout;
|
||||
int boot_timeout;
|
||||
|
||||
/* chip information for dsp */
|
||||
const void *chip_info;
|
||||
|
||||
/* defaults for no codec mode */
|
||||
const char *nocodec_fw_filename;
|
||||
const char *nocodec_tplg_filename;
|
||||
|
||||
/* defaults paths for firmware and topology files */
|
||||
const char *default_fw_path;
|
||||
const char *default_tplg_path;
|
||||
|
||||
const struct snd_sof_dsp_ops *ops;
|
||||
const struct sof_arch_ops *arch_ops;
|
||||
};
|
||||
|
||||
int sof_nocodec_setup(struct device *dev,
|
||||
struct snd_sof_pdata *sof_pdata,
|
||||
struct snd_soc_acpi_mach *mach,
|
||||
const struct sof_dev_desc *desc,
|
||||
const struct snd_sof_dsp_ops *ops);
|
||||
#endif
|
158
include/sound/sof/control.h
Normal file
158
include/sound/sof/control.h
Normal file
@ -0,0 +1,158 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_CONTROL_H__
|
||||
#define __INCLUDE_SOUND_SOF_CONTROL_H__
|
||||
|
||||
#include <uapi/sound/sof/header.h>
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/*
|
||||
* Component Mixers and Controls
|
||||
*/
|
||||
|
||||
/* channel positions - uses same values as ALSA */
|
||||
enum sof_ipc_chmap {
|
||||
SOF_CHMAP_UNKNOWN = 0,
|
||||
SOF_CHMAP_NA, /**< N/A, silent */
|
||||
SOF_CHMAP_MONO, /**< mono stream */
|
||||
SOF_CHMAP_FL, /**< front left */
|
||||
SOF_CHMAP_FR, /**< front right */
|
||||
SOF_CHMAP_RL, /**< rear left */
|
||||
SOF_CHMAP_RR, /**< rear right */
|
||||
SOF_CHMAP_FC, /**< front centre */
|
||||
SOF_CHMAP_LFE, /**< LFE */
|
||||
SOF_CHMAP_SL, /**< side left */
|
||||
SOF_CHMAP_SR, /**< side right */
|
||||
SOF_CHMAP_RC, /**< rear centre */
|
||||
SOF_CHMAP_FLC, /**< front left centre */
|
||||
SOF_CHMAP_FRC, /**< front right centre */
|
||||
SOF_CHMAP_RLC, /**< rear left centre */
|
||||
SOF_CHMAP_RRC, /**< rear right centre */
|
||||
SOF_CHMAP_FLW, /**< front left wide */
|
||||
SOF_CHMAP_FRW, /**< front right wide */
|
||||
SOF_CHMAP_FLH, /**< front left high */
|
||||
SOF_CHMAP_FCH, /**< front centre high */
|
||||
SOF_CHMAP_FRH, /**< front right high */
|
||||
SOF_CHMAP_TC, /**< top centre */
|
||||
SOF_CHMAP_TFL, /**< top front left */
|
||||
SOF_CHMAP_TFR, /**< top front right */
|
||||
SOF_CHMAP_TFC, /**< top front centre */
|
||||
SOF_CHMAP_TRL, /**< top rear left */
|
||||
SOF_CHMAP_TRR, /**< top rear right */
|
||||
SOF_CHMAP_TRC, /**< top rear centre */
|
||||
SOF_CHMAP_TFLC, /**< top front left centre */
|
||||
SOF_CHMAP_TFRC, /**< top front right centre */
|
||||
SOF_CHMAP_TSL, /**< top side left */
|
||||
SOF_CHMAP_TSR, /**< top side right */
|
||||
SOF_CHMAP_LLFE, /**< left LFE */
|
||||
SOF_CHMAP_RLFE, /**< right LFE */
|
||||
SOF_CHMAP_BC, /**< bottom centre */
|
||||
SOF_CHMAP_BLC, /**< bottom left centre */
|
||||
SOF_CHMAP_BRC, /**< bottom right centre */
|
||||
SOF_CHMAP_LAST = SOF_CHMAP_BRC,
|
||||
};
|
||||
|
||||
/* control data type and direction */
|
||||
enum sof_ipc_ctrl_type {
|
||||
/* per channel data - uses struct sof_ipc_ctrl_value_chan */
|
||||
SOF_CTRL_TYPE_VALUE_CHAN_GET = 0,
|
||||
SOF_CTRL_TYPE_VALUE_CHAN_SET,
|
||||
/* component data - uses struct sof_ipc_ctrl_value_comp */
|
||||
SOF_CTRL_TYPE_VALUE_COMP_GET,
|
||||
SOF_CTRL_TYPE_VALUE_COMP_SET,
|
||||
/* bespoke data - uses struct sof_abi_hdr */
|
||||
SOF_CTRL_TYPE_DATA_GET,
|
||||
SOF_CTRL_TYPE_DATA_SET,
|
||||
};
|
||||
|
||||
/* control command type */
|
||||
enum sof_ipc_ctrl_cmd {
|
||||
SOF_CTRL_CMD_VOLUME = 0, /**< maps to ALSA volume style controls */
|
||||
SOF_CTRL_CMD_ENUM, /**< maps to ALSA enum style controls */
|
||||
SOF_CTRL_CMD_SWITCH, /**< maps to ALSA switch style controls */
|
||||
SOF_CTRL_CMD_BINARY, /**< maps to ALSA binary style controls */
|
||||
};
|
||||
|
||||
/* generic channel mapped value data */
|
||||
struct sof_ipc_ctrl_value_chan {
|
||||
uint32_t channel; /**< channel map - enum sof_ipc_chmap */
|
||||
uint32_t value;
|
||||
} __packed;
|
||||
|
||||
/* generic component mapped value data */
|
||||
struct sof_ipc_ctrl_value_comp {
|
||||
uint32_t index; /**< component source/sink/control index in control */
|
||||
union {
|
||||
uint32_t uvalue;
|
||||
int32_t svalue;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* generic control data */
|
||||
struct sof_ipc_ctrl_data {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint32_t comp_id;
|
||||
|
||||
/* control access and data type */
|
||||
uint32_t type; /**< enum sof_ipc_ctrl_type */
|
||||
uint32_t cmd; /**< enum sof_ipc_ctrl_cmd */
|
||||
uint32_t index; /**< control index for comps > 1 control */
|
||||
|
||||
/* control data - can either be appended or DMAed from host */
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
uint32_t num_elems; /**< in array elems or bytes for data type */
|
||||
uint32_t elems_remaining; /**< elems remaining if sent in parts */
|
||||
|
||||
uint32_t msg_index; /**< for large messages sent in parts */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[6];
|
||||
|
||||
/* control data - add new types if needed */
|
||||
union {
|
||||
/* channel values can be used by volume type controls */
|
||||
struct sof_ipc_ctrl_value_chan chanv[0];
|
||||
/* component values used by routing controls like mux, mixer */
|
||||
struct sof_ipc_ctrl_value_comp compv[0];
|
||||
/* data can be used by binary controls */
|
||||
struct sof_abi_hdr data[0];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/** Event type */
|
||||
enum sof_ipc_ctrl_event_type {
|
||||
SOF_CTRL_EVENT_GENERIC = 0, /**< generic event */
|
||||
SOF_CTRL_EVENT_GENERIC_METADATA, /**< generic event with metadata */
|
||||
SOF_CTRL_EVENT_KD, /**< keyword detection event */
|
||||
SOF_CTRL_EVENT_VAD, /**< voice activity detection event */
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic notification data.
|
||||
*/
|
||||
struct sof_ipc_comp_event {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint16_t src_comp_type; /**< COMP_TYPE_ */
|
||||
uint32_t src_comp_id; /**< source component id */
|
||||
uint32_t event_type; /**< event type - SOF_CTRL_EVENT_* */
|
||||
uint32_t num_elems; /**< in array elems or bytes for data type */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[8];
|
||||
|
||||
/* control data - add new types if needed */
|
||||
union {
|
||||
/* data can be used by binary controls */
|
||||
struct sof_abi_hdr data[0];
|
||||
/* event specific values */
|
||||
uint32_t event_value;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#endif
|
178
include/sound/sof/dai-intel.h
Normal file
178
include/sound/sof/dai-intel.h
Normal file
@ -0,0 +1,178 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_DAI_INTEL_H__
|
||||
#define __INCLUDE_SOUND_SOF_DAI_INTEL_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/* ssc1: TINTE */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_TINTE (1 << 0)
|
||||
/* ssc1: PINTE */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_PINTE (1 << 1)
|
||||
/* ssc2: SMTATF */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_SMTATF (1 << 2)
|
||||
/* ssc2: MMRATF */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_MMRATF (1 << 3)
|
||||
/* ssc2: PSPSTWFDFD */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_PSPSTWFDFD (1 << 4)
|
||||
/* ssc2: PSPSRWFDFD */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_PSPSRWFDFD (1 << 5)
|
||||
/* ssc1: LBM */
|
||||
#define SOF_DAI_INTEL_SSP_QUIRK_LBM (1 << 6)
|
||||
|
||||
/* here is the possibility to define others aux macros */
|
||||
|
||||
#define SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX 38
|
||||
#define SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX 31
|
||||
|
||||
/* SSP clocks control settings
|
||||
*
|
||||
* Macros for clks_control field in sof_ipc_dai_ssp_params struct.
|
||||
*/
|
||||
|
||||
/* mclk 0 disable */
|
||||
#define SOF_DAI_INTEL_SSP_MCLK_0_DISABLE BIT(0)
|
||||
/* mclk 1 disable */
|
||||
#define SOF_DAI_INTEL_SSP_MCLK_1_DISABLE BIT(1)
|
||||
/* mclk keep active */
|
||||
#define SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_KA BIT(2)
|
||||
/* bclk keep active */
|
||||
#define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_KA BIT(3)
|
||||
/* fs keep active */
|
||||
#define SOF_DAI_INTEL_SSP_CLKCTRL_FS_KA BIT(4)
|
||||
/* bclk idle */
|
||||
#define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_IDLE_HIGH BIT(5)
|
||||
|
||||
/* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */
|
||||
struct sof_ipc_dai_ssp_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint16_t reserved1;
|
||||
uint16_t mclk_id;
|
||||
|
||||
uint32_t mclk_rate; /* mclk frequency in Hz */
|
||||
uint32_t fsync_rate; /* fsync frequency in Hz */
|
||||
uint32_t bclk_rate; /* bclk frequency in Hz */
|
||||
|
||||
/* TDM */
|
||||
uint32_t tdm_slots;
|
||||
uint32_t rx_slots;
|
||||
uint32_t tx_slots;
|
||||
|
||||
/* data */
|
||||
uint32_t sample_valid_bits;
|
||||
uint16_t tdm_slot_width;
|
||||
uint16_t reserved2; /* alignment */
|
||||
|
||||
/* MCLK */
|
||||
uint32_t mclk_direction;
|
||||
|
||||
uint16_t frame_pulse_width;
|
||||
uint16_t tdm_per_slot_padding_flag;
|
||||
uint32_t clks_control;
|
||||
uint32_t quirks;
|
||||
} __packed;
|
||||
|
||||
/* HDA Configuration Request - SOF_IPC_DAI_HDA_CONFIG */
|
||||
struct sof_ipc_dai_hda_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t link_dma_ch;
|
||||
} __packed;
|
||||
|
||||
/* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */
|
||||
|
||||
/* This struct is defined per 2ch PDM controller available in the platform.
|
||||
* Normally it is sufficient to set the used microphone specific enables to 1
|
||||
* and keep other parameters as zero. The customizations are:
|
||||
*
|
||||
* 1. If a device mixes different microphones types with different polarity
|
||||
* and/or the absolute polarity matters the PCM signal from a microphone
|
||||
* can be inverted with the controls.
|
||||
*
|
||||
* 2. If the microphones in a stereo pair do not appear in captured stream
|
||||
* in desired order due to board schematics choises they can be swapped with
|
||||
* the clk_edge parameter.
|
||||
*
|
||||
* 3. If PDM bit errors are seen in capture (poor quality) the skew parameter
|
||||
* that delays the sampling time of data by half cycles of DMIC source clock
|
||||
* can be tried for improvement. However there is no guarantee for this to fix
|
||||
* data integrity problems.
|
||||
*/
|
||||
struct sof_ipc_dai_dmic_pdm_ctrl {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint16_t id; /**< PDM controller ID */
|
||||
|
||||
uint16_t enable_mic_a; /**< Use A (left) channel mic (0 or 1)*/
|
||||
uint16_t enable_mic_b; /**< Use B (right) channel mic (0 or 1)*/
|
||||
|
||||
uint16_t polarity_mic_a; /**< Optionally invert mic A signal (0 or 1) */
|
||||
uint16_t polarity_mic_b; /**< Optionally invert mic B signal (0 or 1) */
|
||||
|
||||
uint16_t clk_edge; /**< Optionally swap data clock edge (0 or 1) */
|
||||
uint16_t skew; /**< Adjust PDM data sampling vs. clock (0..15) */
|
||||
|
||||
uint16_t reserved[3]; /**< Make sure the total size is 4 bytes aligned */
|
||||
} __packed;
|
||||
|
||||
/* This struct contains the global settings for all 2ch PDM controllers. The
|
||||
* version number used in configuration data is checked vs. version used by
|
||||
* device driver src/drivers/dmic.c need to match. It is incremented from
|
||||
* initial value 1 if updates done for the to driver would alter the operation
|
||||
* of the microhone.
|
||||
*
|
||||
* Note: The microphone clock (pdmclk_min, pdmclk_max, duty_min, duty_max)
|
||||
* parameters need to be set as defined in microphone data sheet. E.g. clock
|
||||
* range 1.0 - 3.2 MHz is usually supported microphones. Some microphones are
|
||||
* multi-mode capable and there may be denied mic clock frequencies between
|
||||
* the modes. In such case set the clock range limits of the desired mode to
|
||||
* avoid the driver to set clock to an illegal rate.
|
||||
*
|
||||
* The duty cycle could be set to 48-52% if not known. Generally these
|
||||
* parameters can be altered within data sheet specified limits to match
|
||||
* required audio application performance power.
|
||||
*
|
||||
* The microphone clock needs to be usually about 50-80 times the used audio
|
||||
* sample rate. With highest sample rates above 48 kHz this can relaxed
|
||||
* somewhat.
|
||||
*
|
||||
* The parameter wake_up_time describes how long time the microphone needs
|
||||
* for the data line to produce valid output from mic clock start. The driver
|
||||
* will mute the captured audio for the given time. The min_clock_on_time
|
||||
* parameter is used to prevent too short clock bursts to happen. The driver
|
||||
* will keep the clock active after capture stop if this time is not yet
|
||||
* met. The unit for both is microseconds (us). Exceed of 100 ms will be
|
||||
* treated as an error.
|
||||
*/
|
||||
struct sof_ipc_dai_dmic_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t driver_ipc_version; /**< Version (1..N) */
|
||||
|
||||
uint32_t pdmclk_min; /**< Minimum microphone clock in Hz (100000..N) */
|
||||
uint32_t pdmclk_max; /**< Maximum microphone clock in Hz (min...N) */
|
||||
|
||||
uint32_t fifo_fs; /**< FIFO sample rate in Hz (8000..96000) */
|
||||
uint32_t reserved_1; /**< Reserved */
|
||||
uint16_t fifo_bits; /**< FIFO word length (16 or 32) */
|
||||
uint16_t reserved_2; /**< Reserved */
|
||||
|
||||
uint16_t duty_min; /**< Min. mic clock duty cycle in % (20..80) */
|
||||
uint16_t duty_max; /**< Max. mic clock duty cycle in % (min..80) */
|
||||
|
||||
uint32_t num_pdm_active; /**< Number of active pdm controllers */
|
||||
|
||||
uint32_t wake_up_time; /**< Time from clock start to data (us) */
|
||||
uint32_t min_clock_on_time; /**< Min. time that clk is kept on (us) */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[6];
|
||||
|
||||
/**< variable number of pdm controller config */
|
||||
struct sof_ipc_dai_dmic_pdm_ctrl pdm[0];
|
||||
} __packed;
|
||||
|
||||
#endif
|
75
include/sound/sof/dai.h
Normal file
75
include/sound/sof/dai.h
Normal file
@ -0,0 +1,75 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_DAI_H__
|
||||
#define __INCLUDE_SOUND_SOF_DAI_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
#include <sound/sof/dai-intel.h>
|
||||
|
||||
/*
|
||||
* DAI Configuration.
|
||||
*
|
||||
* Each different DAI type will have it's own structure and IPC cmd.
|
||||
*/
|
||||
|
||||
#define SOF_DAI_FMT_I2S 1 /**< I2S mode */
|
||||
#define SOF_DAI_FMT_RIGHT_J 2 /**< Right Justified mode */
|
||||
#define SOF_DAI_FMT_LEFT_J 3 /**< Left Justified mode */
|
||||
#define SOF_DAI_FMT_DSP_A 4 /**< L data MSB after FRM LRC */
|
||||
#define SOF_DAI_FMT_DSP_B 5 /**< L data MSB during FRM LRC */
|
||||
#define SOF_DAI_FMT_PDM 6 /**< Pulse density modulation */
|
||||
|
||||
#define SOF_DAI_FMT_CONT (1 << 4) /**< continuous clock */
|
||||
#define SOF_DAI_FMT_GATED (0 << 4) /**< clock is gated */
|
||||
|
||||
#define SOF_DAI_FMT_NB_NF (0 << 8) /**< normal bit clock + frame */
|
||||
#define SOF_DAI_FMT_NB_IF (2 << 8) /**< normal BCLK + inv FRM */
|
||||
#define SOF_DAI_FMT_IB_NF (3 << 8) /**< invert BCLK + nor FRM */
|
||||
#define SOF_DAI_FMT_IB_IF (4 << 8) /**< invert BCLK + FRM */
|
||||
|
||||
#define SOF_DAI_FMT_CBM_CFM (0 << 12) /**< codec clk & FRM master */
|
||||
#define SOF_DAI_FMT_CBS_CFM (2 << 12) /**< codec clk slave & FRM master */
|
||||
#define SOF_DAI_FMT_CBM_CFS (3 << 12) /**< codec clk master & frame slave */
|
||||
#define SOF_DAI_FMT_CBS_CFS (4 << 12) /**< codec clk & FRM slave */
|
||||
|
||||
#define SOF_DAI_FMT_FORMAT_MASK 0x000f
|
||||
#define SOF_DAI_FMT_CLOCK_MASK 0x00f0
|
||||
#define SOF_DAI_FMT_INV_MASK 0x0f00
|
||||
#define SOF_DAI_FMT_MASTER_MASK 0xf000
|
||||
|
||||
/** \brief Types of DAI */
|
||||
enum sof_ipc_dai_type {
|
||||
SOF_DAI_INTEL_NONE = 0, /**< None */
|
||||
SOF_DAI_INTEL_SSP, /**< Intel SSP */
|
||||
SOF_DAI_INTEL_DMIC, /**< Intel DMIC */
|
||||
SOF_DAI_INTEL_HDA, /**< Intel HD/A */
|
||||
};
|
||||
|
||||
/* general purpose DAI configuration */
|
||||
struct sof_ipc_dai_config {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t type; /**< DAI type - enum sof_ipc_dai_type */
|
||||
uint32_t dai_index; /**< index of this type dai */
|
||||
|
||||
/* physical protocol and clocking */
|
||||
uint16_t format; /**< SOF_DAI_FMT_ */
|
||||
uint16_t reserved16; /**< alignment */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[8];
|
||||
|
||||
/* HW specific data */
|
||||
union {
|
||||
struct sof_ipc_dai_ssp_params ssp;
|
||||
struct sof_ipc_dai_dmic_params dmic;
|
||||
struct sof_ipc_dai_hda_params hda;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#endif
|
158
include/sound/sof/header.h
Normal file
158
include/sound/sof/header.h
Normal file
@ -0,0 +1,158 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_HEADER_H__
|
||||
#define __INCLUDE_SOUND_SOF_HEADER_H__
|
||||
|
||||
#include <uapi/sound/sof/abi.h>
|
||||
|
||||
/** \addtogroup sof_uapi uAPI
|
||||
* SOF uAPI specification.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*
|
||||
* IPC messages have a prefixed 32 bit identifier made up as follows :-
|
||||
*
|
||||
* 0xGCCCNNNN where
|
||||
* G is global cmd type (4 bits)
|
||||
* C is command type (12 bits)
|
||||
* I is the ID number (16 bits) - monotonic and overflows
|
||||
*
|
||||
* This is sent at the start of the IPM message in the mailbox. Messages should
|
||||
* not be sent in the doorbell (special exceptions for firmware .
|
||||
*/
|
||||
|
||||
/* Global Message - Generic */
|
||||
#define SOF_GLB_TYPE_SHIFT 28
|
||||
#define SOF_GLB_TYPE_MASK (0xf << SOF_GLB_TYPE_SHIFT)
|
||||
#define SOF_GLB_TYPE(x) ((x) << SOF_GLB_TYPE_SHIFT)
|
||||
|
||||
/* Command Message - Generic */
|
||||
#define SOF_CMD_TYPE_SHIFT 16
|
||||
#define SOF_CMD_TYPE_MASK (0xfff << SOF_CMD_TYPE_SHIFT)
|
||||
#define SOF_CMD_TYPE(x) ((x) << SOF_CMD_TYPE_SHIFT)
|
||||
|
||||
/* Global Message Types */
|
||||
#define SOF_IPC_GLB_REPLY SOF_GLB_TYPE(0x1U)
|
||||
#define SOF_IPC_GLB_COMPOUND SOF_GLB_TYPE(0x2U)
|
||||
#define SOF_IPC_GLB_TPLG_MSG SOF_GLB_TYPE(0x3U)
|
||||
#define SOF_IPC_GLB_PM_MSG SOF_GLB_TYPE(0x4U)
|
||||
#define SOF_IPC_GLB_COMP_MSG SOF_GLB_TYPE(0x5U)
|
||||
#define SOF_IPC_GLB_STREAM_MSG SOF_GLB_TYPE(0x6U)
|
||||
#define SOF_IPC_FW_READY SOF_GLB_TYPE(0x7U)
|
||||
#define SOF_IPC_GLB_DAI_MSG SOF_GLB_TYPE(0x8U)
|
||||
#define SOF_IPC_GLB_TRACE_MSG SOF_GLB_TYPE(0x9U)
|
||||
|
||||
/*
|
||||
* DSP Command Message Types
|
||||
*/
|
||||
|
||||
/* topology */
|
||||
#define SOF_IPC_TPLG_COMP_NEW SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_TPLG_COMP_FREE SOF_CMD_TYPE(0x002)
|
||||
#define SOF_IPC_TPLG_COMP_CONNECT SOF_CMD_TYPE(0x003)
|
||||
#define SOF_IPC_TPLG_PIPE_NEW SOF_CMD_TYPE(0x010)
|
||||
#define SOF_IPC_TPLG_PIPE_FREE SOF_CMD_TYPE(0x011)
|
||||
#define SOF_IPC_TPLG_PIPE_CONNECT SOF_CMD_TYPE(0x012)
|
||||
#define SOF_IPC_TPLG_PIPE_COMPLETE SOF_CMD_TYPE(0x013)
|
||||
#define SOF_IPC_TPLG_BUFFER_NEW SOF_CMD_TYPE(0x020)
|
||||
#define SOF_IPC_TPLG_BUFFER_FREE SOF_CMD_TYPE(0x021)
|
||||
|
||||
/* PM */
|
||||
#define SOF_IPC_PM_CTX_SAVE SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_PM_CTX_RESTORE SOF_CMD_TYPE(0x002)
|
||||
#define SOF_IPC_PM_CTX_SIZE SOF_CMD_TYPE(0x003)
|
||||
#define SOF_IPC_PM_CLK_SET SOF_CMD_TYPE(0x004)
|
||||
#define SOF_IPC_PM_CLK_GET SOF_CMD_TYPE(0x005)
|
||||
#define SOF_IPC_PM_CLK_REQ SOF_CMD_TYPE(0x006)
|
||||
#define SOF_IPC_PM_CORE_ENABLE SOF_CMD_TYPE(0x007)
|
||||
|
||||
/* component runtime config - multiple different types */
|
||||
#define SOF_IPC_COMP_SET_VALUE SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_COMP_GET_VALUE SOF_CMD_TYPE(0x002)
|
||||
#define SOF_IPC_COMP_SET_DATA SOF_CMD_TYPE(0x003)
|
||||
#define SOF_IPC_COMP_GET_DATA SOF_CMD_TYPE(0x004)
|
||||
|
||||
/* DAI messages */
|
||||
#define SOF_IPC_DAI_CONFIG SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_DAI_LOOPBACK SOF_CMD_TYPE(0x002)
|
||||
|
||||
/* stream */
|
||||
#define SOF_IPC_STREAM_PCM_PARAMS SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_STREAM_PCM_PARAMS_REPLY SOF_CMD_TYPE(0x002)
|
||||
#define SOF_IPC_STREAM_PCM_FREE SOF_CMD_TYPE(0x003)
|
||||
#define SOF_IPC_STREAM_TRIG_START SOF_CMD_TYPE(0x004)
|
||||
#define SOF_IPC_STREAM_TRIG_STOP SOF_CMD_TYPE(0x005)
|
||||
#define SOF_IPC_STREAM_TRIG_PAUSE SOF_CMD_TYPE(0x006)
|
||||
#define SOF_IPC_STREAM_TRIG_RELEASE SOF_CMD_TYPE(0x007)
|
||||
#define SOF_IPC_STREAM_TRIG_DRAIN SOF_CMD_TYPE(0x008)
|
||||
#define SOF_IPC_STREAM_TRIG_XRUN SOF_CMD_TYPE(0x009)
|
||||
#define SOF_IPC_STREAM_POSITION SOF_CMD_TYPE(0x00a)
|
||||
#define SOF_IPC_STREAM_VORBIS_PARAMS SOF_CMD_TYPE(0x010)
|
||||
#define SOF_IPC_STREAM_VORBIS_FREE SOF_CMD_TYPE(0x011)
|
||||
|
||||
/* trace and debug */
|
||||
#define SOF_IPC_TRACE_DMA_PARAMS SOF_CMD_TYPE(0x001)
|
||||
#define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002)
|
||||
|
||||
/* Get message component id */
|
||||
#define SOF_IPC_MESSAGE_ID(x) ((x) & 0xffff)
|
||||
|
||||
/* maximum message size for mailbox Tx/Rx */
|
||||
#define SOF_IPC_MSG_MAX_SIZE 384
|
||||
|
||||
/*
|
||||
* Structure Header - Header for all IPC structures except command structs.
|
||||
* The size can be greater than the structure size and that means there is
|
||||
* extended bespoke data beyond the end of the structure including variable
|
||||
* arrays.
|
||||
*/
|
||||
|
||||
struct sof_ipc_hdr {
|
||||
uint32_t size; /**< size of structure */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Command Header - Header for all IPC commands. Identifies IPC message.
|
||||
* The size can be greater than the structure size and that means there is
|
||||
* extended bespoke data beyond the end of the structure including variable
|
||||
* arrays.
|
||||
*/
|
||||
|
||||
struct sof_ipc_cmd_hdr {
|
||||
uint32_t size; /**< size of structure */
|
||||
uint32_t cmd; /**< SOF_IPC_GLB_ + cmd */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Generic reply message. Some commands override this with their own reply
|
||||
* types that must include this at start.
|
||||
*/
|
||||
struct sof_ipc_reply {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
int32_t error; /**< negative error numbers */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Compound commands - SOF_IPC_GLB_COMPOUND.
|
||||
*
|
||||
* Compound commands are sent to the DSP as a single IPC operation. The
|
||||
* commands are split into blocks and each block has a header. This header
|
||||
* identifies the command type and the number of commands before the next
|
||||
* header.
|
||||
*/
|
||||
|
||||
struct sof_ipc_compound_hdr {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t count; /**< count of 0 means end of compound sequence */
|
||||
} __packed;
|
||||
|
||||
/** @}*/
|
||||
|
||||
#endif
|
118
include/sound/sof/info.h
Normal file
118
include/sound/sof/info.h
Normal file
@ -0,0 +1,118 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_INFO_H__
|
||||
#define __INCLUDE_SOUND_SOF_INFO_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
#include <sound/sof/stream.h>
|
||||
|
||||
/*
|
||||
* Firmware boot and version
|
||||
*/
|
||||
|
||||
#define SOF_IPC_MAX_ELEMS 16
|
||||
|
||||
/* extended data types that can be appended onto end of sof_ipc_fw_ready */
|
||||
enum sof_ipc_ext_data {
|
||||
SOF_IPC_EXT_DMA_BUFFER = 0,
|
||||
SOF_IPC_EXT_WINDOW,
|
||||
};
|
||||
|
||||
/* FW version - SOF_IPC_GLB_VERSION */
|
||||
struct sof_ipc_fw_version {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint16_t micro;
|
||||
uint16_t build;
|
||||
uint8_t date[12];
|
||||
uint8_t time[10];
|
||||
uint8_t tag[6];
|
||||
uint32_t abi_version;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[4];
|
||||
} __packed;
|
||||
|
||||
/* FW ready Message - sent by firmware when boot has completed */
|
||||
struct sof_ipc_fw_ready {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t dspbox_offset; /* dsp initiated IPC mailbox */
|
||||
uint32_t hostbox_offset; /* host initiated IPC mailbox */
|
||||
uint32_t dspbox_size;
|
||||
uint32_t hostbox_size;
|
||||
struct sof_ipc_fw_version version;
|
||||
|
||||
/* Miscellaneous debug flags showing build/debug features enabled */
|
||||
union {
|
||||
uint64_t reserved;
|
||||
struct {
|
||||
uint64_t build:1;
|
||||
uint64_t locks:1;
|
||||
uint64_t locks_verbose:1;
|
||||
uint64_t gdb:1;
|
||||
} bits;
|
||||
} debug;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[4];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Extended Firmware data. All optional, depends on platform/arch.
|
||||
*/
|
||||
enum sof_ipc_region {
|
||||
SOF_IPC_REGION_DOWNBOX = 0,
|
||||
SOF_IPC_REGION_UPBOX,
|
||||
SOF_IPC_REGION_TRACE,
|
||||
SOF_IPC_REGION_DEBUG,
|
||||
SOF_IPC_REGION_STREAM,
|
||||
SOF_IPC_REGION_REGS,
|
||||
SOF_IPC_REGION_EXCEPTION,
|
||||
};
|
||||
|
||||
struct sof_ipc_ext_data_hdr {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t type; /**< SOF_IPC_EXT_ */
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_dma_buffer_elem {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t type; /**< SOF_IPC_REGION_ */
|
||||
uint32_t id; /**< platform specific - used to map to host memory */
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
} __packed;
|
||||
|
||||
/* extended data DMA buffers for IPC, trace and debug */
|
||||
struct sof_ipc_dma_buffer_data {
|
||||
struct sof_ipc_ext_data_hdr ext_hdr;
|
||||
uint32_t num_buffers;
|
||||
|
||||
/* host files in buffer[n].buffer */
|
||||
struct sof_ipc_dma_buffer_elem buffer[];
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_window_elem {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t type; /**< SOF_IPC_REGION_ */
|
||||
uint32_t id; /**< platform specific - used to map to host memory */
|
||||
uint32_t flags; /**< R, W, RW, etc - to define */
|
||||
uint32_t size; /**< size of region in bytes */
|
||||
/* offset in window region as windows can be partitioned */
|
||||
uint32_t offset;
|
||||
} __packed;
|
||||
|
||||
/* extended data memory windows for IPC, trace and debug */
|
||||
struct sof_ipc_window {
|
||||
struct sof_ipc_ext_data_hdr ext_hdr;
|
||||
uint32_t num_windows;
|
||||
struct sof_ipc_window_elem window[];
|
||||
} __packed;
|
||||
|
||||
#endif
|
48
include/sound/sof/pm.h
Normal file
48
include/sound/sof/pm.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_PM_H__
|
||||
#define __INCLUDE_SOUND_SOF_PM_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/*
|
||||
* PM
|
||||
*/
|
||||
|
||||
/* PM context element */
|
||||
struct sof_ipc_pm_ctx_elem {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t type;
|
||||
uint32_t size;
|
||||
uint64_t addr;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* PM context - SOF_IPC_PM_CTX_SAVE, SOF_IPC_PM_CTX_RESTORE,
|
||||
* SOF_IPC_PM_CTX_SIZE
|
||||
*/
|
||||
struct sof_ipc_pm_ctx {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
uint32_t num_elems;
|
||||
uint32_t size;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[8];
|
||||
|
||||
struct sof_ipc_pm_ctx_elem elems[];
|
||||
} __packed;
|
||||
|
||||
/* enable or disable cores - SOF_IPC_PM_CORE_ENABLE */
|
||||
struct sof_ipc_pm_core_config {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t enable_mask;
|
||||
} __packed;
|
||||
|
||||
#endif
|
148
include/sound/sof/stream.h
Normal file
148
include/sound/sof/stream.h
Normal file
@ -0,0 +1,148 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_STREAM_H__
|
||||
#define __INCLUDE_SOUND_SOF_STREAM_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/*
|
||||
* Stream configuration.
|
||||
*/
|
||||
|
||||
#define SOF_IPC_MAX_CHANNELS 8
|
||||
|
||||
/* common sample rates for use in masks */
|
||||
#define SOF_RATE_8000 (1 << 0) /**< 8000Hz */
|
||||
#define SOF_RATE_11025 (1 << 1) /**< 11025Hz */
|
||||
#define SOF_RATE_12000 (1 << 2) /**< 12000Hz */
|
||||
#define SOF_RATE_16000 (1 << 3) /**< 16000Hz */
|
||||
#define SOF_RATE_22050 (1 << 4) /**< 22050Hz */
|
||||
#define SOF_RATE_24000 (1 << 5) /**< 24000Hz */
|
||||
#define SOF_RATE_32000 (1 << 6) /**< 32000Hz */
|
||||
#define SOF_RATE_44100 (1 << 7) /**< 44100Hz */
|
||||
#define SOF_RATE_48000 (1 << 8) /**< 48000Hz */
|
||||
#define SOF_RATE_64000 (1 << 9) /**< 64000Hz */
|
||||
#define SOF_RATE_88200 (1 << 10) /**< 88200Hz */
|
||||
#define SOF_RATE_96000 (1 << 11) /**< 96000Hz */
|
||||
#define SOF_RATE_176400 (1 << 12) /**< 176400Hz */
|
||||
#define SOF_RATE_192000 (1 << 13) /**< 192000Hz */
|
||||
|
||||
/* continuous and non-standard rates for flexibility */
|
||||
#define SOF_RATE_CONTINUOUS (1 << 30) /**< range */
|
||||
#define SOF_RATE_KNOT (1 << 31) /**< non-continuous */
|
||||
|
||||
/* generic PCM flags for runtime settings */
|
||||
#define SOF_PCM_FLAG_XRUN_STOP (1 << 0) /**< Stop on any XRUN */
|
||||
|
||||
/* stream PCM frame format */
|
||||
enum sof_ipc_frame {
|
||||
SOF_IPC_FRAME_S16_LE = 0,
|
||||
SOF_IPC_FRAME_S24_4LE,
|
||||
SOF_IPC_FRAME_S32_LE,
|
||||
SOF_IPC_FRAME_FLOAT,
|
||||
/* other formats here */
|
||||
};
|
||||
|
||||
/* stream buffer format */
|
||||
enum sof_ipc_buffer_format {
|
||||
SOF_IPC_BUFFER_INTERLEAVED,
|
||||
SOF_IPC_BUFFER_NONINTERLEAVED,
|
||||
/* other formats here */
|
||||
};
|
||||
|
||||
/* stream direction */
|
||||
enum sof_ipc_stream_direction {
|
||||
SOF_IPC_STREAM_PLAYBACK = 0,
|
||||
SOF_IPC_STREAM_CAPTURE,
|
||||
};
|
||||
|
||||
/* stream ring info */
|
||||
struct sof_ipc_host_buffer {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t phy_addr;
|
||||
uint32_t pages;
|
||||
uint32_t size;
|
||||
uint32_t reserved[3];
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_stream_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
uint32_t direction; /**< enum sof_ipc_stream_direction */
|
||||
uint32_t frame_fmt; /**< enum sof_ipc_frame */
|
||||
uint32_t buffer_fmt; /**< enum sof_ipc_buffer_format */
|
||||
uint32_t rate;
|
||||
uint16_t stream_tag;
|
||||
uint16_t channels;
|
||||
uint16_t sample_valid_bytes;
|
||||
uint16_t sample_container_bytes;
|
||||
|
||||
/* for notifying host period has completed - 0 means no period IRQ */
|
||||
uint32_t host_period_bytes;
|
||||
|
||||
uint32_t reserved[2];
|
||||
uint16_t chmap[SOF_IPC_MAX_CHANNELS]; /**< channel map - SOF_CHMAP_ */
|
||||
} __packed;
|
||||
|
||||
/* PCM params info - SOF_IPC_STREAM_PCM_PARAMS */
|
||||
struct sof_ipc_pcm_params {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t comp_id;
|
||||
uint32_t flags; /**< generic PCM flags - SOF_PCM_FLAG_ */
|
||||
uint32_t reserved[2];
|
||||
struct sof_ipc_stream_params params;
|
||||
} __packed;
|
||||
|
||||
/* PCM params info reply - SOF_IPC_STREAM_PCM_PARAMS_REPLY */
|
||||
struct sof_ipc_pcm_params_reply {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint32_t comp_id;
|
||||
uint32_t posn_offset;
|
||||
} __packed;
|
||||
|
||||
/* free stream - SOF_IPC_STREAM_PCM_PARAMS */
|
||||
struct sof_ipc_stream {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t comp_id;
|
||||
} __packed;
|
||||
|
||||
/* flags indicating which time stamps are in sync with each other */
|
||||
#define SOF_TIME_HOST_SYNC (1 << 0)
|
||||
#define SOF_TIME_DAI_SYNC (1 << 1)
|
||||
#define SOF_TIME_WALL_SYNC (1 << 2)
|
||||
#define SOF_TIME_STAMP_SYNC (1 << 3)
|
||||
|
||||
/* flags indicating which time stamps are valid */
|
||||
#define SOF_TIME_HOST_VALID (1 << 8)
|
||||
#define SOF_TIME_DAI_VALID (1 << 9)
|
||||
#define SOF_TIME_WALL_VALID (1 << 10)
|
||||
#define SOF_TIME_STAMP_VALID (1 << 11)
|
||||
|
||||
/* flags indicating time stamps are 64bit else 3use low 32bit */
|
||||
#define SOF_TIME_HOST_64 (1 << 16)
|
||||
#define SOF_TIME_DAI_64 (1 << 17)
|
||||
#define SOF_TIME_WALL_64 (1 << 18)
|
||||
#define SOF_TIME_STAMP_64 (1 << 19)
|
||||
|
||||
struct sof_ipc_stream_posn {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint32_t comp_id; /**< host component ID */
|
||||
uint32_t flags; /**< SOF_TIME_ */
|
||||
uint32_t wallclock_hz; /**< frequency of wallclock in Hz */
|
||||
uint32_t timestamp_ns; /**< resolution of timestamp in ns */
|
||||
uint64_t host_posn; /**< host DMA position in bytes */
|
||||
uint64_t dai_posn; /**< DAI DMA position in bytes */
|
||||
uint64_t comp_posn; /**< comp position in bytes */
|
||||
uint64_t wallclock; /**< audio wall clock */
|
||||
uint64_t timestamp; /**< system time stamp */
|
||||
uint32_t xrun_comp_id; /**< comp ID of XRUN component */
|
||||
int32_t xrun_size; /**< XRUN size in bytes */
|
||||
} __packed;
|
||||
|
||||
#endif
|
256
include/sound/sof/topology.h
Normal file
256
include/sound/sof/topology.h
Normal file
@ -0,0 +1,256 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_TOPOLOGY_H__
|
||||
#define __INCLUDE_SOUND_SOF_TOPOLOGY_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/*
|
||||
* Component
|
||||
*/
|
||||
|
||||
/* types of component */
|
||||
enum sof_comp_type {
|
||||
SOF_COMP_NONE = 0,
|
||||
SOF_COMP_HOST,
|
||||
SOF_COMP_DAI,
|
||||
SOF_COMP_SG_HOST, /**< scatter gather variant */
|
||||
SOF_COMP_SG_DAI, /**< scatter gather variant */
|
||||
SOF_COMP_VOLUME,
|
||||
SOF_COMP_MIXER,
|
||||
SOF_COMP_MUX,
|
||||
SOF_COMP_SRC,
|
||||
SOF_COMP_SPLITTER,
|
||||
SOF_COMP_TONE,
|
||||
SOF_COMP_SWITCH,
|
||||
SOF_COMP_BUFFER,
|
||||
SOF_COMP_EQ_IIR,
|
||||
SOF_COMP_EQ_FIR,
|
||||
SOF_COMP_KEYWORD_DETECT,
|
||||
SOF_COMP_KPB, /* A key phrase buffer component */
|
||||
SOF_COMP_SELECTOR, /**< channel selector component */
|
||||
/* keep FILEREAD/FILEWRITE as the last ones */
|
||||
SOF_COMP_FILEREAD = 10000, /**< host test based file IO */
|
||||
SOF_COMP_FILEWRITE = 10001, /**< host test based file IO */
|
||||
};
|
||||
|
||||
/* XRUN action for component */
|
||||
#define SOF_XRUN_STOP 1 /**< stop stream */
|
||||
#define SOF_XRUN_UNDER_ZERO 2 /**< send 0s to sink */
|
||||
#define SOF_XRUN_OVER_NULL 4 /**< send data to NULL */
|
||||
|
||||
/* create new generic component - SOF_IPC_TPLG_COMP_NEW */
|
||||
struct sof_ipc_comp {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t id;
|
||||
enum sof_comp_type type;
|
||||
uint32_t pipeline_id;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[2];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Component Buffers
|
||||
*/
|
||||
|
||||
/*
|
||||
* SOF memory capabilities, add new ones at the end
|
||||
*/
|
||||
#define SOF_MEM_CAPS_RAM (1 << 0)
|
||||
#define SOF_MEM_CAPS_ROM (1 << 1)
|
||||
#define SOF_MEM_CAPS_EXT (1 << 2) /**< external */
|
||||
#define SOF_MEM_CAPS_LP (1 << 3) /**< low power */
|
||||
#define SOF_MEM_CAPS_HP (1 << 4) /**< high performance */
|
||||
#define SOF_MEM_CAPS_DMA (1 << 5) /**< DMA'able */
|
||||
#define SOF_MEM_CAPS_CACHE (1 << 6) /**< cacheable */
|
||||
#define SOF_MEM_CAPS_EXEC (1 << 7) /**< executable */
|
||||
|
||||
/* create new component buffer - SOF_IPC_TPLG_BUFFER_NEW */
|
||||
struct sof_ipc_buffer {
|
||||
struct sof_ipc_comp comp;
|
||||
uint32_t size; /**< buffer size in bytes */
|
||||
uint32_t caps; /**< SOF_MEM_CAPS_ */
|
||||
} __packed;
|
||||
|
||||
/* generic component config data - must always be after struct sof_ipc_comp */
|
||||
struct sof_ipc_comp_config {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t periods_sink; /**< 0 means variable */
|
||||
uint32_t periods_source; /**< 0 means variable */
|
||||
uint32_t reserved1; /**< reserved */
|
||||
uint32_t frame_fmt; /**< SOF_IPC_FRAME_ */
|
||||
uint32_t xrun_action;
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[2];
|
||||
} __packed;
|
||||
|
||||
/* generic host component */
|
||||
struct sof_ipc_comp_host {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
uint32_t direction; /**< SOF_IPC_STREAM_ */
|
||||
uint32_t no_irq; /**< don't send periodic IRQ to host/DSP */
|
||||
uint32_t dmac_config; /**< DMA engine specific */
|
||||
} __packed;
|
||||
|
||||
/* generic DAI component */
|
||||
struct sof_ipc_comp_dai {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
uint32_t direction; /**< SOF_IPC_STREAM_ */
|
||||
uint32_t dai_index; /**< index of this type dai */
|
||||
uint32_t type; /**< DAI type - SOF_DAI_ */
|
||||
uint32_t reserved; /**< reserved */
|
||||
} __packed;
|
||||
|
||||
/* generic mixer component */
|
||||
struct sof_ipc_comp_mixer {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
} __packed;
|
||||
|
||||
/* volume ramping types */
|
||||
enum sof_volume_ramp {
|
||||
SOF_VOLUME_LINEAR = 0,
|
||||
SOF_VOLUME_LOG,
|
||||
SOF_VOLUME_LINEAR_ZC,
|
||||
SOF_VOLUME_LOG_ZC,
|
||||
};
|
||||
|
||||
/* generic volume component */
|
||||
struct sof_ipc_comp_volume {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
uint32_t channels;
|
||||
uint32_t min_value;
|
||||
uint32_t max_value;
|
||||
uint32_t ramp; /**< SOF_VOLUME_ */
|
||||
uint32_t initial_ramp; /**< ramp space in ms */
|
||||
} __packed;
|
||||
|
||||
/* generic SRC component */
|
||||
struct sof_ipc_comp_src {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
/* either source or sink rate must be non zero */
|
||||
uint32_t source_rate; /**< source rate or 0 for variable */
|
||||
uint32_t sink_rate; /**< sink rate or 0 for variable */
|
||||
uint32_t rate_mask; /**< SOF_RATE_ supported rates */
|
||||
} __packed;
|
||||
|
||||
/* generic MUX component */
|
||||
struct sof_ipc_comp_mux {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
} __packed;
|
||||
|
||||
/* generic tone generator component */
|
||||
struct sof_ipc_comp_tone {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
int32_t sample_rate;
|
||||
int32_t frequency;
|
||||
int32_t amplitude;
|
||||
int32_t freq_mult;
|
||||
int32_t ampl_mult;
|
||||
int32_t length;
|
||||
int32_t period;
|
||||
int32_t repeats;
|
||||
int32_t ramp_step;
|
||||
} __packed;
|
||||
|
||||
/** \brief Types of processing components */
|
||||
enum sof_ipc_process_type {
|
||||
SOF_PROCESS_NONE = 0, /**< None */
|
||||
SOF_PROCESS_EQFIR, /**< Intel FIR */
|
||||
SOF_PROCESS_EQIIR, /**< Intel IIR */
|
||||
SOF_PROCESS_KEYWORD_DETECT, /**< Keyword Detection */
|
||||
SOF_PROCESS_KPB, /**< KeyPhrase Buffer Manager */
|
||||
SOF_PROCESS_CHAN_SELECTOR, /**< Channel Selector */
|
||||
};
|
||||
|
||||
/* generic "effect", "codec" or proprietary processing component */
|
||||
struct sof_ipc_comp_process {
|
||||
struct sof_ipc_comp comp;
|
||||
struct sof_ipc_comp_config config;
|
||||
uint32_t size; /**< size of bespoke data section in bytes */
|
||||
uint32_t type; /**< sof_ipc_process_type */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[7];
|
||||
|
||||
unsigned char data[0];
|
||||
} __packed;
|
||||
|
||||
/* frees components, buffers and pipelines
|
||||
* SOF_IPC_TPLG_COMP_FREE, SOF_IPC_TPLG_PIPE_FREE, SOF_IPC_TPLG_BUFFER_FREE
|
||||
*/
|
||||
struct sof_ipc_free {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t id;
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_comp_reply {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint32_t id;
|
||||
uint32_t offset;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Pipeline
|
||||
*/
|
||||
|
||||
/** \brief Types of pipeline scheduling time domains */
|
||||
enum sof_ipc_pipe_sched_time_domain {
|
||||
SOF_TIME_DOMAIN_DMA = 0, /**< DMA interrupt */
|
||||
SOF_TIME_DOMAIN_TIMER, /**< Timer interrupt */
|
||||
};
|
||||
|
||||
/* new pipeline - SOF_IPC_TPLG_PIPE_NEW */
|
||||
struct sof_ipc_pipe_new {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t comp_id; /**< component id for pipeline */
|
||||
uint32_t pipeline_id; /**< pipeline id */
|
||||
uint32_t sched_id; /**< Scheduling component id */
|
||||
uint32_t core; /**< core we run on */
|
||||
uint32_t period; /**< execution period in us*/
|
||||
uint32_t priority; /**< priority level 0 (low) to 10 (max) */
|
||||
uint32_t period_mips; /**< worst case instruction count per period */
|
||||
uint32_t frames_per_sched;/**< output frames of pipeline, 0 is variable */
|
||||
uint32_t xrun_limit_usecs; /**< report xruns greater than limit */
|
||||
uint32_t time_domain; /**< scheduling time domain */
|
||||
} __packed;
|
||||
|
||||
/* pipeline construction complete - SOF_IPC_TPLG_PIPE_COMPLETE */
|
||||
struct sof_ipc_pipe_ready {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t comp_id;
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_pipe_free {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t comp_id;
|
||||
} __packed;
|
||||
|
||||
/* connect two components in pipeline - SOF_IPC_TPLG_COMP_CONNECT */
|
||||
struct sof_ipc_pipe_comp_connect {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t source_id;
|
||||
uint32_t sink_id;
|
||||
} __packed;
|
||||
|
||||
/* external events */
|
||||
enum sof_event_types {
|
||||
SOF_EVENT_NONE = 0,
|
||||
SOF_KEYWORD_DETECT_DAPM_EVENT,
|
||||
};
|
||||
|
||||
#endif
|
67
include/sound/sof/trace.h
Normal file
67
include/sound/sof/trace.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_TRACE_H__
|
||||
#define __INCLUDE_SOUND_SOF_TRACE_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
#include <sound/sof/stream.h>
|
||||
|
||||
/*
|
||||
* DMA for Trace
|
||||
*/
|
||||
|
||||
#define SOF_TRACE_FILENAME_SIZE 32
|
||||
|
||||
/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */
|
||||
struct sof_ipc_dma_trace_params {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
struct sof_ipc_host_buffer buffer;
|
||||
uint32_t stream_tag;
|
||||
} __packed;
|
||||
|
||||
/* DMA for Trace params info - SOF_IPC_DEBUG_DMA_PARAMS */
|
||||
struct sof_ipc_dma_trace_posn {
|
||||
struct sof_ipc_reply rhdr;
|
||||
uint32_t host_offset; /* Offset of DMA host buffer */
|
||||
uint32_t overflow; /* overflow bytes if any */
|
||||
uint32_t messages; /* total trace messages */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Commom debug
|
||||
*/
|
||||
|
||||
/*
|
||||
* SOF panic codes
|
||||
*/
|
||||
#define SOF_IPC_PANIC_MAGIC 0x0dead000
|
||||
#define SOF_IPC_PANIC_MAGIC_MASK 0x0ffff000
|
||||
#define SOF_IPC_PANIC_CODE_MASK 0x00000fff
|
||||
#define SOF_IPC_PANIC_MEM (SOF_IPC_PANIC_MAGIC | 0x0)
|
||||
#define SOF_IPC_PANIC_WORK (SOF_IPC_PANIC_MAGIC | 0x1)
|
||||
#define SOF_IPC_PANIC_IPC (SOF_IPC_PANIC_MAGIC | 0x2)
|
||||
#define SOF_IPC_PANIC_ARCH (SOF_IPC_PANIC_MAGIC | 0x3)
|
||||
#define SOF_IPC_PANIC_PLATFORM (SOF_IPC_PANIC_MAGIC | 0x4)
|
||||
#define SOF_IPC_PANIC_TASK (SOF_IPC_PANIC_MAGIC | 0x5)
|
||||
#define SOF_IPC_PANIC_EXCEPTION (SOF_IPC_PANIC_MAGIC | 0x6)
|
||||
#define SOF_IPC_PANIC_DEADLOCK (SOF_IPC_PANIC_MAGIC | 0x7)
|
||||
#define SOF_IPC_PANIC_STACK (SOF_IPC_PANIC_MAGIC | 0x8)
|
||||
#define SOF_IPC_PANIC_IDLE (SOF_IPC_PANIC_MAGIC | 0x9)
|
||||
#define SOF_IPC_PANIC_WFI (SOF_IPC_PANIC_MAGIC | 0xa)
|
||||
#define SOF_IPC_PANIC_ASSERT (SOF_IPC_PANIC_MAGIC | 0xb)
|
||||
|
||||
/* panic info include filename and line number */
|
||||
struct sof_ipc_panic_info {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t code; /* SOF_IPC_PANIC_ */
|
||||
char filename[SOF_TRACE_FILENAME_SIZE];
|
||||
uint32_t linenum;
|
||||
} __packed;
|
||||
|
||||
#endif
|
44
include/sound/sof/xtensa.h
Normal file
44
include/sound/sof/xtensa.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_XTENSA_H__
|
||||
#define __INCLUDE_SOUND_SOF_XTENSA_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/*
|
||||
* Architecture specific debug
|
||||
*/
|
||||
|
||||
/* Xtensa Firmware Oops data */
|
||||
struct sof_ipc_dsp_oops_xtensa {
|
||||
struct sof_ipc_hdr hdr;
|
||||
uint32_t exccause;
|
||||
uint32_t excvaddr;
|
||||
uint32_t ps;
|
||||
uint32_t epc1;
|
||||
uint32_t epc2;
|
||||
uint32_t epc3;
|
||||
uint32_t epc4;
|
||||
uint32_t epc5;
|
||||
uint32_t epc6;
|
||||
uint32_t epc7;
|
||||
uint32_t eps2;
|
||||
uint32_t eps3;
|
||||
uint32_t eps4;
|
||||
uint32_t eps5;
|
||||
uint32_t eps6;
|
||||
uint32_t eps7;
|
||||
uint32_t depc;
|
||||
uint32_t intenable;
|
||||
uint32_t interrupt;
|
||||
uint32_t sar;
|
||||
uint32_t stack;
|
||||
} __packed;
|
||||
|
||||
#endif
|
62
include/uapi/sound/sof/abi.h
Normal file
62
include/uapi/sound/sof/abi.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SOF ABI versioning is based on Semantic Versioning where we have a given
|
||||
* MAJOR.MINOR.PATCH version number. See https://semver.org/
|
||||
*
|
||||
* Rules for incrementing or changing version :-
|
||||
*
|
||||
* 1) Increment MAJOR version if you make incompatible API changes. MINOR and
|
||||
* PATCH should be reset to 0.
|
||||
*
|
||||
* 2) Increment MINOR version if you add backwards compatible features or
|
||||
* changes. PATCH should be reset to 0.
|
||||
*
|
||||
* 3) Increment PATCH version if you add backwards compatible bug fixes.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_ABI_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_ABI_H__
|
||||
|
||||
/* SOF ABI version major, minor and patch numbers */
|
||||
#define SOF_ABI_MAJOR 3
|
||||
#define SOF_ABI_MINOR 4
|
||||
#define SOF_ABI_PATCH 0
|
||||
|
||||
/* SOF ABI version number. Format within 32bit word is MMmmmppp */
|
||||
#define SOF_ABI_MAJOR_SHIFT 24
|
||||
#define SOF_ABI_MAJOR_MASK 0xff
|
||||
#define SOF_ABI_MINOR_SHIFT 12
|
||||
#define SOF_ABI_MINOR_MASK 0xfff
|
||||
#define SOF_ABI_PATCH_SHIFT 0
|
||||
#define SOF_ABI_PATCH_MASK 0xfff
|
||||
|
||||
#define SOF_ABI_VER(major, minor, patch) \
|
||||
(((major) << SOF_ABI_MAJOR_SHIFT) | \
|
||||
((minor) << SOF_ABI_MINOR_SHIFT) | \
|
||||
((patch) << SOF_ABI_PATCH_SHIFT))
|
||||
|
||||
#define SOF_ABI_VERSION_MAJOR(version) \
|
||||
(((version) >> SOF_ABI_MAJOR_SHIFT) & SOF_ABI_MAJOR_MASK)
|
||||
#define SOF_ABI_VERSION_MINOR(version) \
|
||||
(((version) >> SOF_ABI_MINOR_SHIFT) & SOF_ABI_MINOR_MASK)
|
||||
#define SOF_ABI_VERSION_PATCH(version) \
|
||||
(((version) >> SOF_ABI_PATCH_SHIFT) & SOF_ABI_PATCH_MASK)
|
||||
|
||||
#define SOF_ABI_VERSION_INCOMPATIBLE(sof_ver, client_ver) \
|
||||
(SOF_ABI_VERSION_MAJOR((sof_ver)) != \
|
||||
SOF_ABI_VERSION_MAJOR((client_ver)) \
|
||||
)
|
||||
|
||||
#define SOF_ABI_VERSION SOF_ABI_VER(SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH)
|
||||
|
||||
/* SOF ABI magic number "SOF\0". */
|
||||
#define SOF_ABI_MAGIC 0x00464F53
|
||||
|
||||
#endif
|
172
include/uapi/sound/sof/eq.h
Normal file
172
include/uapi/sound/sof/eq.h
Normal file
@ -0,0 +1,172 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_EQ_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_EQ_H__
|
||||
|
||||
/* FIR EQ type */
|
||||
|
||||
#define SOF_EQ_FIR_IDX_SWITCH 0
|
||||
|
||||
#define SOF_EQ_FIR_MAX_SIZE 4096 /* Max size allowed for coef data in bytes */
|
||||
|
||||
#define SOF_EQ_FIR_MAX_LENGTH 192 /* Max length for individual filter */
|
||||
|
||||
#define SOF_EQ_FIR_MAX_RESPONSES 8 /* A blob can define max 8 FIR EQs */
|
||||
|
||||
/*
|
||||
* eq_fir_configuration data structure contains this information
|
||||
* uint32_t size
|
||||
* This is the number of bytes need to store the received EQ
|
||||
* configuration.
|
||||
* uint16_t channels_in_config
|
||||
* This describes the number of channels in this EQ config data. It
|
||||
* can be different from PLATFORM_MAX_CHANNELS.
|
||||
* uint16_t number_of_responses
|
||||
* 0=no responses, 1=one response defined, 2=two responses defined, etc.
|
||||
* int16_t data[]
|
||||
* assign_response[channels_in_config]
|
||||
* 0 = use first response, 1 = use 2nd response, etc.
|
||||
* E.g. {0, 0, 0, 0, 1, 1, 1, 1} would apply to channels 0-3 the
|
||||
* same first defined response and for to channels 4-7 the second.
|
||||
* coef_data[]
|
||||
* Repeated data
|
||||
* { filter_length, output_shift, h[] }
|
||||
* for every EQ response defined where vector h has filter_length
|
||||
* number of coefficients. Coefficients in h[] are in Q1.15 format.
|
||||
* E.g. 16384 (Q1.15) = 0.5. The shifts are number of right shifts.
|
||||
*
|
||||
* NOTE: The channels_in_config must be even to have coef_data aligned to
|
||||
* 32 bit word in RAM. Therefore a mono EQ assign must be duplicated to 2ch
|
||||
* even if it would never used. Similarly a 5ch EQ assign must be increased
|
||||
* to 6ch. EQ init will return an error if this is not met.
|
||||
*
|
||||
* NOTE: The filter_length must be multiple of four. Therefore the filter must
|
||||
* be padded from the end with zeros have this condition met.
|
||||
*/
|
||||
|
||||
struct sof_eq_fir_config {
|
||||
uint32_t size;
|
||||
uint16_t channels_in_config;
|
||||
uint16_t number_of_responses;
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int16_t data[];
|
||||
} __packed;
|
||||
|
||||
struct sof_eq_fir_coef_data {
|
||||
int16_t length; /* Number of FIR taps */
|
||||
int16_t out_shift; /* Amount of right shifts at output */
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int16_t coef[]; /* FIR coefficients */
|
||||
} __packed;
|
||||
|
||||
/* In the struct above there's two 16 bit words (length, shift) and four
|
||||
* reserved 32 bit words before the actual FIR coefficients. This information
|
||||
* is used in parsing of the configuration blob.
|
||||
*/
|
||||
#define SOF_EQ_FIR_COEF_NHEADER \
|
||||
(sizeof(struct sof_eq_fir_coef_data) / sizeof(int16_t))
|
||||
|
||||
/* IIR EQ type */
|
||||
|
||||
#define SOF_EQ_IIR_IDX_SWITCH 0
|
||||
|
||||
#define SOF_EQ_IIR_MAX_SIZE 1024 /* Max size allowed for coef data in bytes */
|
||||
|
||||
#define SOF_EQ_IIR_MAX_RESPONSES 8 /* A blob can define max 8 IIR EQs */
|
||||
|
||||
/* eq_iir_configuration
|
||||
* uint32_t channels_in_config
|
||||
* This describes the number of channels in this EQ config data. It
|
||||
* can be different from PLATFORM_MAX_CHANNELS.
|
||||
* uint32_t number_of_responses_defined
|
||||
* 0=no responses, 1=one response defined, 2=two responses defined, etc.
|
||||
* int32_t data[]
|
||||
* Data consist of two parts. First is the response assign vector that
|
||||
* has length of channels_in_config. The latter part is coefficient
|
||||
* data.
|
||||
* uint32_t assign_response[channels_in_config]
|
||||
* -1 = not defined, 0 = use first response, 1 = use 2nd, etc.
|
||||
* E.g. {0, 0, 0, 0, -1, -1, -1, -1} would apply to channels 0-3 the
|
||||
* same first defined response and leave channels 4-7 unequalized.
|
||||
* coefficient_data[]
|
||||
* <1st EQ>
|
||||
* uint32_t num_biquads
|
||||
* uint32_t num_biquads_in_series
|
||||
* <1st biquad>
|
||||
* int32_t coef_a2 Q2.30 format
|
||||
* int32_t coef_a1 Q2.30 format
|
||||
* int32_t coef_b2 Q2.30 format
|
||||
* int32_t coef_b1 Q2.30 format
|
||||
* int32_t coef_b0 Q2.30 format
|
||||
* int32_t output_shift number of shifts right, shift left is negative
|
||||
* int32_t output_gain Q2.14 format
|
||||
* <2nd biquad>
|
||||
* ...
|
||||
* <2nd EQ>
|
||||
*
|
||||
* Note: A flat response biquad can be made with a section set to
|
||||
* b0 = 1.0, gain = 1.0, and other parameters set to 0
|
||||
* {0, 0, 0, 0, 1073741824, 0, 16484}
|
||||
*/
|
||||
|
||||
struct sof_eq_iir_config {
|
||||
uint32_t size;
|
||||
uint32_t channels_in_config;
|
||||
uint32_t number_of_responses;
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int32_t data[]; /* eq_assign[channels], eq 0, eq 1, ... */
|
||||
} __packed;
|
||||
|
||||
struct sof_eq_iir_header_df2t {
|
||||
uint32_t num_sections;
|
||||
uint32_t num_sections_in_series;
|
||||
|
||||
/* reserved */
|
||||
uint32_t reserved[4];
|
||||
|
||||
int32_t biquads[]; /* Repeated biquad coefficients */
|
||||
} __packed;
|
||||
|
||||
struct sof_eq_iir_biquad_df2t {
|
||||
int32_t a2; /* Q2.30 */
|
||||
int32_t a1; /* Q2.30 */
|
||||
int32_t b2; /* Q2.30 */
|
||||
int32_t b1; /* Q2.30 */
|
||||
int32_t b0; /* Q2.30 */
|
||||
int32_t output_shift; /* Number of right shifts */
|
||||
int32_t output_gain; /* Q2.14 */
|
||||
} __packed;
|
||||
|
||||
/* A full 22th order equalizer with 11 biquads cover octave bands 1-11 in
|
||||
* in the 0 - 20 kHz bandwidth.
|
||||
*/
|
||||
#define SOF_EQ_IIR_DF2T_BIQUADS_MAX 11
|
||||
|
||||
/* The number of int32_t words in sof_eq_iir_header_df2t:
|
||||
* num_sections, num_sections_in_series, reserved[4]
|
||||
*/
|
||||
#define SOF_EQ_IIR_NHEADER_DF2T \
|
||||
(sizeof(struct sof_eq_iir_header_df2t) / sizeof(int32_t))
|
||||
|
||||
/* The number of int32_t words in sof_eq_iir_biquad_df2t:
|
||||
* a2, a1, b2, b1, b0, output_shift, output_gain
|
||||
*/
|
||||
#define SOF_EQ_IIR_NBIQUAD_DF2T \
|
||||
(sizeof(struct sof_eq_iir_biquad_df2t) / sizeof(int32_t))
|
||||
|
||||
#endif
|
78
include/uapi/sound/sof/fw.h
Normal file
78
include/uapi/sound/sof/fw.h
Normal file
@ -0,0 +1,78 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Firmware file format .
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOF_FW_H__
|
||||
#define __INCLUDE_UAPI_SOF_FW_H__
|
||||
|
||||
#define SND_SOF_FW_SIG_SIZE 4
|
||||
#define SND_SOF_FW_ABI 1
|
||||
#define SND_SOF_FW_SIG "Reef"
|
||||
|
||||
/*
|
||||
* Firmware module is made up of 1 . N blocks of different types. The
|
||||
* Block header is used to determine where and how block is to be copied in the
|
||||
* DSP/host memory space.
|
||||
*/
|
||||
enum snd_sof_fw_blk_type {
|
||||
SOF_FW_BLK_TYPE_INVALID = -1,
|
||||
SOF_FW_BLK_TYPE_START = 0,
|
||||
SOF_FW_BLK_TYPE_RSRVD0 = SOF_FW_BLK_TYPE_START,
|
||||
SOF_FW_BLK_TYPE_IRAM = 1, /* local instruction RAM */
|
||||
SOF_FW_BLK_TYPE_DRAM = 2, /* local data RAM */
|
||||
SOF_FW_BLK_TYPE_SRAM = 3, /* system RAM */
|
||||
SOF_FW_BLK_TYPE_ROM = 4,
|
||||
SOF_FW_BLK_TYPE_IMR = 5,
|
||||
SOF_FW_BLK_TYPE_RSRVD6 = 6,
|
||||
SOF_FW_BLK_TYPE_RSRVD7 = 7,
|
||||
SOF_FW_BLK_TYPE_RSRVD8 = 8,
|
||||
SOF_FW_BLK_TYPE_RSRVD9 = 9,
|
||||
SOF_FW_BLK_TYPE_RSRVD10 = 10,
|
||||
SOF_FW_BLK_TYPE_RSRVD11 = 11,
|
||||
SOF_FW_BLK_TYPE_RSRVD12 = 12,
|
||||
SOF_FW_BLK_TYPE_RSRVD13 = 13,
|
||||
SOF_FW_BLK_TYPE_RSRVD14 = 14,
|
||||
/* use SOF_FW_BLK_TYPE_RSVRDX for new block types */
|
||||
SOF_FW_BLK_TYPE_NUM
|
||||
};
|
||||
|
||||
struct snd_sof_blk_hdr {
|
||||
enum snd_sof_fw_blk_type type;
|
||||
uint32_t size; /* bytes minus this header */
|
||||
uint32_t offset; /* offset from base */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Firmware file is made up of 1 .. N different modules types. The module
|
||||
* type is used to determine how to load and parse the module.
|
||||
*/
|
||||
enum snd_sof_fw_mod_type {
|
||||
SOF_FW_BASE = 0, /* base firmware image */
|
||||
SOF_FW_MODULE = 1, /* firmware module */
|
||||
};
|
||||
|
||||
struct snd_sof_mod_hdr {
|
||||
enum snd_sof_fw_mod_type type;
|
||||
uint32_t size; /* bytes minus this header */
|
||||
uint32_t num_blocks; /* number of blocks */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Firmware file header.
|
||||
*/
|
||||
struct snd_sof_fw_header {
|
||||
unsigned char sig[SND_SOF_FW_SIG_SIZE]; /* "Reef" */
|
||||
uint32_t file_size; /* size of file minus this header */
|
||||
uint32_t num_modules; /* number of modules */
|
||||
uint32_t abi; /* version of header format */
|
||||
} __packed;
|
||||
|
||||
#endif
|
27
include/uapi/sound/sof/header.h
Normal file
27
include/uapi/sound/sof/header.h
Normal file
@ -0,0 +1,27 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_HEADER_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_HEADER_H__
|
||||
|
||||
/*
|
||||
* Header for all non IPC ABI data.
|
||||
*
|
||||
* Identifies data type, size and ABI.
|
||||
* Used by any bespoke component data structures or binary blobs.
|
||||
*/
|
||||
struct sof_abi_hdr {
|
||||
uint32_t magic; /**< 'S', 'O', 'F', '\0' */
|
||||
uint32_t type; /**< component specific type */
|
||||
uint32_t size; /**< size in bytes of data excl. this struct */
|
||||
uint32_t abi; /**< SOF ABI version */
|
||||
uint32_t reserved[4]; /**< reserved for future use */
|
||||
uint32_t data[0]; /**< Component data - opaque to core */
|
||||
} __packed;
|
||||
|
||||
#endif
|
188
include/uapi/sound/sof/manifest.h
Normal file
188
include/uapi/sound/sof/manifest.h
Normal file
@ -0,0 +1,188 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_MANIFEST_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_MANIFEST_H__
|
||||
|
||||
/* start offset for base FW module */
|
||||
#define SOF_MAN_ELF_TEXT_OFFSET 0x2000
|
||||
|
||||
/* FW Extended Manifest Header id = $AE1 */
|
||||
#define SOF_MAN_EXT_HEADER_MAGIC 0x31454124
|
||||
|
||||
/* module type load type */
|
||||
#define SOF_MAN_MOD_TYPE_BUILTIN 0
|
||||
#define SOF_MAN_MOD_TYPE_MODULE 1
|
||||
|
||||
struct sof_man_module_type {
|
||||
uint32_t load_type:4; /* SOF_MAN_MOD_TYPE_ */
|
||||
uint32_t auto_start:1;
|
||||
uint32_t domain_ll:1;
|
||||
uint32_t domain_dp:1;
|
||||
uint32_t rsvd_:25;
|
||||
};
|
||||
|
||||
/* segment flags.type */
|
||||
#define SOF_MAN_SEGMENT_TEXT 0
|
||||
#define SOF_MAN_SEGMENT_RODATA 1
|
||||
#define SOF_MAN_SEGMENT_DATA 1
|
||||
#define SOF_MAN_SEGMENT_BSS 2
|
||||
#define SOF_MAN_SEGMENT_EMPTY 15
|
||||
|
||||
union sof_man_segment_flags {
|
||||
uint32_t ul;
|
||||
struct {
|
||||
uint32_t contents:1;
|
||||
uint32_t alloc:1;
|
||||
uint32_t load:1;
|
||||
uint32_t readonly:1;
|
||||
uint32_t code:1;
|
||||
uint32_t data:1;
|
||||
uint32_t _rsvd0:2;
|
||||
uint32_t type:4; /* MAN_SEGMENT_ */
|
||||
uint32_t _rsvd1:4;
|
||||
uint32_t length:16; /* of segment in pages */
|
||||
} r;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Module segment descriptor. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_segment_desc {
|
||||
union sof_man_segment_flags flags;
|
||||
uint32_t v_base_addr;
|
||||
uint32_t file_offset;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* The firmware binary can be split into several modules.
|
||||
*/
|
||||
|
||||
#define SOF_MAN_MOD_ID_LEN 4
|
||||
#define SOF_MAN_MOD_NAME_LEN 8
|
||||
#define SOF_MAN_MOD_SHA256_LEN 32
|
||||
#define SOF_MAN_MOD_ID {'$', 'A', 'M', 'E'}
|
||||
|
||||
/*
|
||||
* Each module has an entry in the FW header. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_module {
|
||||
uint8_t struct_id[SOF_MAN_MOD_ID_LEN]; /* SOF_MAN_MOD_ID */
|
||||
uint8_t name[SOF_MAN_MOD_NAME_LEN];
|
||||
uint8_t uuid[16];
|
||||
struct sof_man_module_type type;
|
||||
uint8_t hash[SOF_MAN_MOD_SHA256_LEN];
|
||||
uint32_t entry_point;
|
||||
uint16_t cfg_offset;
|
||||
uint16_t cfg_count;
|
||||
uint32_t affinity_mask;
|
||||
uint16_t instance_max_count; /* max number of instances */
|
||||
uint16_t instance_bss_size; /* instance (pages) */
|
||||
struct sof_man_segment_desc segment[3];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Each module has a configuration in the FW header. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_mod_config {
|
||||
uint32_t par[4]; /* module parameters */
|
||||
uint32_t is_pages; /* actual size of instance .bss (pages) */
|
||||
uint32_t cps; /* cycles per second */
|
||||
uint32_t ibs; /* input buffer size (bytes) */
|
||||
uint32_t obs; /* output buffer size (bytes) */
|
||||
uint32_t module_flags; /* flags, reserved for future use */
|
||||
uint32_t cpc; /* cycles per single run */
|
||||
uint32_t obls; /* output block size, reserved for future use */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* FW Manifest Header
|
||||
*/
|
||||
|
||||
#define SOF_MAN_FW_HDR_FW_NAME_LEN 8
|
||||
#define SOF_MAN_FW_HDR_ID {'$', 'A', 'M', '1'}
|
||||
#define SOF_MAN_FW_HDR_NAME "ADSPFW"
|
||||
#define SOF_MAN_FW_HDR_FLAGS 0x0
|
||||
#define SOF_MAN_FW_HDR_FEATURES 0xff
|
||||
|
||||
/*
|
||||
* The firmware has a standard header that is checked by the ROM on firmware
|
||||
* loading. preload_page_count is used by DMA code loader and is entire
|
||||
* image size on CNL. i.e. CNL: total size of the binary’s .text and .rodata
|
||||
* Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_fw_header {
|
||||
uint8_t header_id[4];
|
||||
uint32_t header_len;
|
||||
uint8_t name[SOF_MAN_FW_HDR_FW_NAME_LEN];
|
||||
/* number of pages of preloaded image loaded by driver */
|
||||
uint32_t preload_page_count;
|
||||
uint32_t fw_image_flags;
|
||||
uint32_t feature_mask;
|
||||
uint16_t major_version;
|
||||
uint16_t minor_version;
|
||||
uint16_t hotfix_version;
|
||||
uint16_t build_version;
|
||||
uint32_t num_module_entries;
|
||||
uint32_t hw_buf_base_addr;
|
||||
uint32_t hw_buf_length;
|
||||
/* target address for binary loading as offset in IMR - must be == base offset */
|
||||
uint32_t load_offset;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Firmware manifest descriptor. This can contain N modules and N module
|
||||
* configs. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_fw_desc {
|
||||
struct sof_man_fw_header header;
|
||||
|
||||
/* Warning - hack for module arrays. For some unknown reason the we
|
||||
* have a variable size array of struct man_module followed by a
|
||||
* variable size array of struct mod_config. These should have been
|
||||
* merged into a variable array of a parent structure. We have to hack
|
||||
* around this in many places....
|
||||
*
|
||||
* struct sof_man_module man_module[];
|
||||
* struct sof_man_mod_config mod_config[];
|
||||
*/
|
||||
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Component Descriptor. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_component_desc {
|
||||
uint32_t reserved[2]; /* all 0 */
|
||||
uint32_t version;
|
||||
uint8_t hash[SOF_MAN_MOD_SHA256_LEN];
|
||||
uint32_t base_offset;
|
||||
uint32_t limit_offset;
|
||||
uint32_t attributes[4];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Audio DSP extended metadata. Used by ROM - Immutable.
|
||||
*/
|
||||
struct sof_man_adsp_meta_file_ext {
|
||||
uint32_t ext_type; /* always 17 for ADSP extension */
|
||||
uint32_t ext_len;
|
||||
uint32_t imr_type;
|
||||
uint8_t reserved[16]; /* all 0 */
|
||||
struct sof_man_component_desc comp_desc[1];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Module Manifest for rimage module metadata. Not used by ROM.
|
||||
*/
|
||||
struct sof_man_module_manifest {
|
||||
struct sof_man_module module;
|
||||
uint32_t text_size;
|
||||
} __packed;
|
||||
|
||||
#endif
|
107
include/uapi/sound/sof/tokens.h
Normal file
107
include/uapi/sound/sof/tokens.h
Normal file
@ -0,0 +1,107 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
* Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
|
||||
* Keyon Jie <yang.jie@linux.intel.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Topology IDs and tokens.
|
||||
*
|
||||
* ** MUST BE ALIGNED WITH TOPOLOGY CONFIGURATION TOKEN VALUES **
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOF_TOPOLOGY_H__
|
||||
#define __INCLUDE_UAPI_SOF_TOPOLOGY_H__
|
||||
|
||||
/*
|
||||
* Kcontrol IDs
|
||||
*/
|
||||
#define SOF_TPLG_KCTL_VOL_ID 256
|
||||
#define SOF_TPLG_KCTL_ENUM_ID 257
|
||||
#define SOF_TPLG_KCTL_BYTES_ID 258
|
||||
#define SOF_TPLG_KCTL_SWITCH_ID 259
|
||||
|
||||
/*
|
||||
* Tokens - must match values in topology configurations
|
||||
*/
|
||||
|
||||
/* buffers */
|
||||
#define SOF_TKN_BUF_SIZE 100
|
||||
#define SOF_TKN_BUF_CAPS 101
|
||||
|
||||
/* DAI */
|
||||
/* Token retired with ABI 3.2, do not use for new capabilities
|
||||
* #define SOF_TKN_DAI_DMAC_CONFIG 153
|
||||
*/
|
||||
#define SOF_TKN_DAI_TYPE 154
|
||||
#define SOF_TKN_DAI_INDEX 155
|
||||
#define SOF_TKN_DAI_DIRECTION 156
|
||||
|
||||
/* scheduling */
|
||||
#define SOF_TKN_SCHED_PERIOD 200
|
||||
#define SOF_TKN_SCHED_PRIORITY 201
|
||||
#define SOF_TKN_SCHED_MIPS 202
|
||||
#define SOF_TKN_SCHED_CORE 203
|
||||
#define SOF_TKN_SCHED_FRAMES 204
|
||||
#define SOF_TKN_SCHED_TIME_DOMAIN 205
|
||||
|
||||
/* volume */
|
||||
#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250
|
||||
#define SOF_TKN_VOLUME_RAMP_STEP_MS 251
|
||||
|
||||
/* SRC */
|
||||
#define SOF_TKN_SRC_RATE_IN 300
|
||||
#define SOF_TKN_SRC_RATE_OUT 301
|
||||
|
||||
/* PCM */
|
||||
#define SOF_TKN_PCM_DMAC_CONFIG 353
|
||||
|
||||
/* Generic components */
|
||||
#define SOF_TKN_COMP_PERIOD_SINK_COUNT 400
|
||||
#define SOF_TKN_COMP_PERIOD_SOURCE_COUNT 401
|
||||
#define SOF_TKN_COMP_FORMAT 402
|
||||
/* Token retired with ABI 3.2, do not use for new capabilities
|
||||
* #define SOF_TKN_COMP_PRELOAD_COUNT 403
|
||||
*/
|
||||
|
||||
/* SSP */
|
||||
#define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500
|
||||
#define SOF_TKN_INTEL_SSP_MCLK_ID 501
|
||||
#define SOF_TKN_INTEL_SSP_SAMPLE_BITS 502
|
||||
#define SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH 503
|
||||
#define SOF_TKN_INTEL_SSP_QUIRKS 504
|
||||
#define SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT 505
|
||||
|
||||
/* DMIC */
|
||||
#define SOF_TKN_INTEL_DMIC_DRIVER_VERSION 600
|
||||
#define SOF_TKN_INTEL_DMIC_CLK_MIN 601
|
||||
#define SOF_TKN_INTEL_DMIC_CLK_MAX 602
|
||||
#define SOF_TKN_INTEL_DMIC_DUTY_MIN 603
|
||||
#define SOF_TKN_INTEL_DMIC_DUTY_MAX 604
|
||||
#define SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE 605
|
||||
#define SOF_TKN_INTEL_DMIC_SAMPLE_RATE 608
|
||||
#define SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH 609
|
||||
|
||||
/* DMIC PDM */
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_CTRL_ID 700
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable 701
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable 702
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_POLARITY_A 703
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_POLARITY_B 704
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE 705
|
||||
#define SOF_TKN_INTEL_DMIC_PDM_SKEW 706
|
||||
|
||||
/* Tone */
|
||||
#define SOF_TKN_TONE_SAMPLE_RATE 800
|
||||
|
||||
/* Processing Components */
|
||||
#define SOF_TKN_PROCESS_TYPE 900
|
||||
|
||||
/* for backward compatibility */
|
||||
#define SOF_TKN_EFFECT_TYPE SOF_TKN_PROCESS_TYPE
|
||||
|
||||
#endif
|
21
include/uapi/sound/sof/tone.h
Normal file
21
include/uapi/sound/sof/tone.h
Normal file
@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_TONE_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_TONE_H__
|
||||
|
||||
#define SOF_TONE_IDX_FREQUENCY 0
|
||||
#define SOF_TONE_IDX_AMPLITUDE 1
|
||||
#define SOF_TONE_IDX_FREQ_MULT 2
|
||||
#define SOF_TONE_IDX_AMPL_MULT 3
|
||||
#define SOF_TONE_IDX_LENGTH 4
|
||||
#define SOF_TONE_IDX_PERIOD 5
|
||||
#define SOF_TONE_IDX_REPEATS 6
|
||||
#define SOF_TONE_IDX_LIN_RAMP_STEP 7
|
||||
|
||||
#endif
|
66
include/uapi/sound/sof/trace.h
Normal file
66
include/uapi/sound/sof/trace.h
Normal file
@ -0,0 +1,66 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_UAPI_SOUND_SOF_USER_TRACE_H__
|
||||
#define __INCLUDE_UAPI_SOUND_SOF_USER_TRACE_H__
|
||||
|
||||
/*
|
||||
* Host system time.
|
||||
*
|
||||
* This property is used by the driver to pass down information about
|
||||
* current system time. It is expressed in us.
|
||||
* FW translates timestamps (in log entries, probe pockets) to this time
|
||||
* domain.
|
||||
*
|
||||
* (cavs: SystemTime).
|
||||
*/
|
||||
struct system_time {
|
||||
uint32_t val_l; /* Lower dword of current host time value */
|
||||
uint32_t val_u; /* Upper dword of current host time value */
|
||||
} __packed;
|
||||
|
||||
#define LOG_ENABLE 1 /* Enable logging */
|
||||
#define LOG_DISABLE 0 /* Disable logging */
|
||||
|
||||
#define LOG_LEVEL_CRITICAL 1 /* (FDK fatal) */
|
||||
#define LOG_LEVEL_VERBOSE 2
|
||||
|
||||
/*
|
||||
* Layout of a log fifo.
|
||||
*/
|
||||
struct log_buffer_layout {
|
||||
uint32_t read_ptr; /*read pointer */
|
||||
uint32_t write_ptr; /* write pointer */
|
||||
uint32_t buffer[0]; /* buffer */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Log buffer status reported by FW.
|
||||
*/
|
||||
struct log_buffer_status {
|
||||
uint32_t core_id; /* ID of core that logged to other half */
|
||||
} __packed;
|
||||
|
||||
#define TRACE_ID_LENGTH 12
|
||||
|
||||
/*
|
||||
* Log entry header.
|
||||
*
|
||||
* The header is followed by an array of arguments (uint32_t[]).
|
||||
* Number of arguments is specified by the params_num field of log_entry
|
||||
*/
|
||||
struct log_entry_header {
|
||||
uint32_t id_0 : TRACE_ID_LENGTH; /* e.g. Pipeline ID */
|
||||
uint32_t id_1 : TRACE_ID_LENGTH; /* e.g. Component ID */
|
||||
uint32_t core_id : 8; /* Reporting core's id */
|
||||
|
||||
uint64_t timestamp; /* Timestamp (in dsp ticks) */
|
||||
uint32_t log_entry_address; /* Address of log entry in ELF */
|
||||
} __packed;
|
||||
|
||||
#endif
|
@ -63,6 +63,7 @@ source "sound/soc/rockchip/Kconfig"
|
||||
source "sound/soc/samsung/Kconfig"
|
||||
source "sound/soc/sh/Kconfig"
|
||||
source "sound/soc/sirf/Kconfig"
|
||||
source "sound/soc/sof/Kconfig"
|
||||
source "sound/soc/spear/Kconfig"
|
||||
source "sound/soc/sprd/Kconfig"
|
||||
source "sound/soc/sti/Kconfig"
|
||||
|
@ -47,6 +47,7 @@ obj-$(CONFIG_SND_SOC) += rockchip/
|
||||
obj-$(CONFIG_SND_SOC) += samsung/
|
||||
obj-$(CONFIG_SND_SOC) += sh/
|
||||
obj-$(CONFIG_SND_SOC) += sirf/
|
||||
obj-$(CONFIG_SND_SOC) += sof/
|
||||
obj-$(CONFIG_SND_SOC) += spear/
|
||||
obj-$(CONFIG_SND_SOC) += sprd/
|
||||
obj-$(CONFIG_SND_SOC) += sti/
|
||||
|
@ -43,6 +43,9 @@ struct axi_i2s {
|
||||
struct clk *clk;
|
||||
struct clk *clk_ref;
|
||||
|
||||
bool has_capture;
|
||||
bool has_playback;
|
||||
|
||||
struct snd_soc_dai_driver dai_driver;
|
||||
|
||||
struct snd_dmaengine_dai_dma_data capture_dma_data;
|
||||
@ -136,8 +139,10 @@ static int axi_i2s_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
|
||||
&i2s->capture_dma_data);
|
||||
snd_soc_dai_init_dma_data(
|
||||
dai,
|
||||
i2s->has_playback ? &i2s->playback_dma_data : NULL,
|
||||
i2s->has_capture ? &i2s->capture_dma_data : NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -151,18 +156,6 @@ static const struct snd_soc_dai_ops axi_i2s_dai_ops = {
|
||||
|
||||
static struct snd_soc_dai_driver axi_i2s_dai = {
|
||||
.probe = axi_i2s_dai_probe,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
|
||||
},
|
||||
.capture = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
|
||||
},
|
||||
.ops = &axi_i2s_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
};
|
||||
@ -178,6 +171,19 @@ static const struct regmap_config axi_i2s_regmap_config = {
|
||||
.max_register = AXI_I2S_REG_STATUS,
|
||||
};
|
||||
|
||||
static void axi_i2s_parse_of(struct axi_i2s *i2s, const struct device_node *np)
|
||||
{
|
||||
struct property *dma_names;
|
||||
const char *dma_name;
|
||||
|
||||
of_property_for_each_string(np, "dma-names", dma_names, dma_name) {
|
||||
if (strcmp(dma_name, "rx") == 0)
|
||||
i2s->has_capture = true;
|
||||
if (strcmp(dma_name, "tx") == 0)
|
||||
i2s->has_playback = true;
|
||||
}
|
||||
}
|
||||
|
||||
static int axi_i2s_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
@ -191,6 +197,8 @@ static int axi_i2s_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, i2s);
|
||||
|
||||
axi_i2s_parse_of(i2s, pdev->dev.of_node);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
@ -213,13 +221,29 @@ static int axi_i2s_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
|
||||
i2s->playback_dma_data.addr_width = 4;
|
||||
i2s->playback_dma_data.maxburst = 1;
|
||||
if (i2s->has_playback) {
|
||||
axi_i2s_dai.playback.channels_min = 2;
|
||||
axi_i2s_dai.playback.channels_max = 2;
|
||||
axi_i2s_dai.playback.rates = SNDRV_PCM_RATE_KNOT;
|
||||
axi_i2s_dai.playback.formats =
|
||||
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
|
||||
|
||||
i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
|
||||
i2s->capture_dma_data.addr_width = 4;
|
||||
i2s->capture_dma_data.maxburst = 1;
|
||||
i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
|
||||
i2s->playback_dma_data.addr_width = 4;
|
||||
i2s->playback_dma_data.maxburst = 1;
|
||||
}
|
||||
|
||||
if (i2s->has_capture) {
|
||||
axi_i2s_dai.capture.channels_min = 2;
|
||||
axi_i2s_dai.capture.channels_max = 2;
|
||||
axi_i2s_dai.capture.rates = SNDRV_PCM_RATE_KNOT;
|
||||
axi_i2s_dai.capture.formats =
|
||||
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
|
||||
|
||||
i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
|
||||
i2s->capture_dma_data.addr_width = 4;
|
||||
i2s->capture_dma_data.maxburst = 1;
|
||||
}
|
||||
|
||||
i2s->ratnum.num = clk_get_rate(i2s->clk_ref) / 2 / AXI_I2S_BITS_PER_FRAME;
|
||||
i2s->ratnum.den_step = 1;
|
||||
@ -240,6 +264,10 @@ static int axi_i2s_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
dev_info(&pdev->dev, "probed, capture %s, playback %s\n",
|
||||
i2s->has_capture ? "enabled" : "disabled",
|
||||
i2s->has_playback ? "enabled" : "disabled");
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_disable:
|
||||
|
@ -46,8 +46,9 @@
|
||||
#define DUAL_CHANNEL 2
|
||||
|
||||
static struct snd_soc_jack cz_jack;
|
||||
static struct clk *da7219_dai_clk;
|
||||
extern int bt_uart_enable;
|
||||
static struct clk *da7219_dai_wclk;
|
||||
static struct clk *da7219_dai_bclk;
|
||||
extern bool bt_uart_enable;
|
||||
|
||||
static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
@ -72,7 +73,8 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
da7219_dai_clk = clk_get(component->dev, "da7219-dai-clks");
|
||||
da7219_dai_wclk = clk_get(component->dev, "da7219-dai-wclk");
|
||||
da7219_dai_bclk = clk_get(component->dev, "da7219-dai-bclk");
|
||||
|
||||
ret = snd_soc_card_jack_new(card, "Headset Jack",
|
||||
SND_JACK_HEADSET | SND_JACK_LINEOUT |
|
||||
@ -94,12 +96,15 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da7219_clk_enable(struct snd_pcm_substream *substream)
|
||||
static int da7219_clk_enable(struct snd_pcm_substream *substream,
|
||||
int wclk_rate, int bclk_rate)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
||||
ret = clk_prepare_enable(da7219_dai_clk);
|
||||
clk_set_rate(da7219_dai_wclk, wclk_rate);
|
||||
clk_set_rate(da7219_dai_bclk, bclk_rate);
|
||||
ret = clk_prepare_enable(da7219_dai_bclk);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't enable master clock %d\n", ret);
|
||||
return ret;
|
||||
@ -110,7 +115,7 @@ static int da7219_clk_enable(struct snd_pcm_substream *substream)
|
||||
|
||||
static void da7219_clk_disable(void)
|
||||
{
|
||||
clk_disable_unprepare(da7219_dai_clk);
|
||||
clk_disable_unprepare(da7219_dai_bclk);
|
||||
}
|
||||
|
||||
static const unsigned int channels[] = {
|
||||
@ -151,7 +156,7 @@ static int cz_da7219_play_startup(struct snd_pcm_substream *substream)
|
||||
&constraints_rates);
|
||||
|
||||
machine->play_i2s_instance = I2S_SP_INSTANCE;
|
||||
return da7219_clk_enable(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cz_da7219_cap_startup(struct snd_pcm_substream *substream)
|
||||
@ -173,12 +178,7 @@ static int cz_da7219_cap_startup(struct snd_pcm_substream *substream)
|
||||
|
||||
machine->cap_i2s_instance = I2S_SP_INSTANCE;
|
||||
machine->capture_channel = CAP_CHANNEL1;
|
||||
return da7219_clk_enable(substream);
|
||||
}
|
||||
|
||||
static void cz_da7219_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
da7219_clk_disable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cz_max_startup(struct snd_pcm_substream *substream)
|
||||
@ -199,12 +199,7 @@ static int cz_max_startup(struct snd_pcm_substream *substream)
|
||||
&constraints_rates);
|
||||
|
||||
machine->play_i2s_instance = I2S_BT_INSTANCE;
|
||||
return da7219_clk_enable(substream);
|
||||
}
|
||||
|
||||
static void cz_max_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
da7219_clk_disable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cz_dmic0_startup(struct snd_pcm_substream *substream)
|
||||
@ -225,7 +220,7 @@ static int cz_dmic0_startup(struct snd_pcm_substream *substream)
|
||||
&constraints_rates);
|
||||
|
||||
machine->cap_i2s_instance = I2S_BT_INSTANCE;
|
||||
return da7219_clk_enable(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cz_dmic1_startup(struct snd_pcm_substream *substream)
|
||||
@ -247,10 +242,28 @@ static int cz_dmic1_startup(struct snd_pcm_substream *substream)
|
||||
|
||||
machine->cap_i2s_instance = I2S_SP_INSTANCE;
|
||||
machine->capture_channel = CAP_CHANNEL0;
|
||||
return da7219_clk_enable(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cz_dmic_shutdown(struct snd_pcm_substream *substream)
|
||||
static int cz_da7219_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
int wclk, bclk;
|
||||
|
||||
wclk = params_rate(params);
|
||||
bclk = wclk * params_channels(params) *
|
||||
snd_pcm_format_width(params_format(params));
|
||||
/* ADAU7002 spec: "The ADAU7002 requires a BCLK rate
|
||||
* that is minimum of 64x the LRCLK sample rate."
|
||||
* DA7219 is the only clk source so for all codecs
|
||||
* we have to limit bclk to 64X lrclk.
|
||||
*/
|
||||
if (bclk < (wclk * 64))
|
||||
bclk = wclk * 64;
|
||||
return da7219_clk_enable(substream, wclk, bclk);
|
||||
}
|
||||
|
||||
static void cz_da7219_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
da7219_clk_disable();
|
||||
}
|
||||
@ -258,26 +271,31 @@ static void cz_dmic_shutdown(struct snd_pcm_substream *substream)
|
||||
static const struct snd_soc_ops cz_da7219_play_ops = {
|
||||
.startup = cz_da7219_play_startup,
|
||||
.shutdown = cz_da7219_shutdown,
|
||||
.hw_params = cz_da7219_params,
|
||||
};
|
||||
|
||||
static const struct snd_soc_ops cz_da7219_cap_ops = {
|
||||
.startup = cz_da7219_cap_startup,
|
||||
.shutdown = cz_da7219_shutdown,
|
||||
.hw_params = cz_da7219_params,
|
||||
};
|
||||
|
||||
static const struct snd_soc_ops cz_max_play_ops = {
|
||||
.startup = cz_max_startup,
|
||||
.shutdown = cz_max_shutdown,
|
||||
.shutdown = cz_da7219_shutdown,
|
||||
.hw_params = cz_da7219_params,
|
||||
};
|
||||
|
||||
static const struct snd_soc_ops cz_dmic0_cap_ops = {
|
||||
.startup = cz_dmic0_startup,
|
||||
.shutdown = cz_dmic_shutdown,
|
||||
.shutdown = cz_da7219_shutdown,
|
||||
.hw_params = cz_da7219_params,
|
||||
};
|
||||
|
||||
static const struct snd_soc_ops cz_dmic1_cap_ops = {
|
||||
.startup = cz_dmic1_startup,
|
||||
.shutdown = cz_dmic_shutdown,
|
||||
.shutdown = cz_da7219_shutdown,
|
||||
.hw_params = cz_da7219_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link cz_dai_7219_98357[] = {
|
||||
|
@ -558,7 +558,7 @@ static int acp3x_dai_i2s_trigger(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct snd_soc_dai_ops acp3x_dai_i2s_ops = {
|
||||
static struct snd_soc_dai_ops acp3x_dai_i2s_ops = {
|
||||
.hw_params = acp3x_dai_i2s_hwparams,
|
||||
.trigger = acp3x_dai_i2s_trigger,
|
||||
.set_fmt = acp3x_dai_i2s_set_fmt,
|
||||
|
@ -109,4 +109,18 @@ config SND_SOC_MIKROE_PROTO
|
||||
using I2C over SDA (MPU Data Input) and SCL (MPU Clock Input) pins.
|
||||
Both playback and capture are supported.
|
||||
|
||||
config SND_MCHP_SOC_I2S_MCC
|
||||
tristate "Microchip ASoC driver for boards using I2S MCC"
|
||||
depends on OF && (ARCH_AT91 || COMPILE_TEST)
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Say Y or M if you want to add support for I2S Multi-Channel ASoC
|
||||
driver on the following Microchip platforms:
|
||||
- sam9x60
|
||||
|
||||
The I2SMCC complies with the Inter-IC Sound (I2S) bus specification
|
||||
and supports a Time Division Multiplexed (TDM) interface with
|
||||
external multi-channel audio codecs.
|
||||
|
||||
endif
|
||||
|
@ -4,11 +4,13 @@ snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
|
||||
snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
|
||||
snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
|
||||
snd-soc-atmel-i2s-objs := atmel-i2s.o
|
||||
snd-soc-mchp-i2s-mcc-objs := mchp-i2s-mcc.o
|
||||
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o
|
||||
obj-$(CONFIG_SND_MCHP_SOC_I2S_MCC) += snd-soc-mchp-i2s-mcc.o
|
||||
|
||||
# AT91 Machine Support
|
||||
snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
|
||||
|
974
sound/soc/atmel/mchp-i2s-mcc.c
Normal file
974
sound/soc/atmel/mchp-i2s-mcc.c
Normal file
@ -0,0 +1,974 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Driver for Microchip I2S Multi-channel controller
|
||||
//
|
||||
// Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
|
||||
//
|
||||
// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/lcm.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
/*
|
||||
* ---- I2S Controller Register map ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_CR 0x0000 /* Control Register */
|
||||
#define MCHP_I2SMCC_MRA 0x0004 /* Mode Register A */
|
||||
#define MCHP_I2SMCC_MRB 0x0008 /* Mode Register B */
|
||||
#define MCHP_I2SMCC_SR 0x000C /* Status Register */
|
||||
#define MCHP_I2SMCC_IERA 0x0010 /* Interrupt Enable Register A */
|
||||
#define MCHP_I2SMCC_IDRA 0x0014 /* Interrupt Disable Register A */
|
||||
#define MCHP_I2SMCC_IMRA 0x0018 /* Interrupt Mask Register A */
|
||||
#define MCHP_I2SMCC_ISRA 0X001C /* Interrupt Status Register A */
|
||||
|
||||
#define MCHP_I2SMCC_IERB 0x0020 /* Interrupt Enable Register B */
|
||||
#define MCHP_I2SMCC_IDRB 0x0024 /* Interrupt Disable Register B */
|
||||
#define MCHP_I2SMCC_IMRB 0x0028 /* Interrupt Mask Register B */
|
||||
#define MCHP_I2SMCC_ISRB 0X002C /* Interrupt Status Register B */
|
||||
|
||||
#define MCHP_I2SMCC_RHR 0x0030 /* Receiver Holding Register */
|
||||
#define MCHP_I2SMCC_THR 0x0034 /* Transmitter Holding Register */
|
||||
|
||||
#define MCHP_I2SMCC_RHL0R 0x0040 /* Receiver Holding Left 0 Register */
|
||||
#define MCHP_I2SMCC_RHR0R 0x0044 /* Receiver Holding Right 0 Register */
|
||||
|
||||
#define MCHP_I2SMCC_RHL1R 0x0048 /* Receiver Holding Left 1 Register */
|
||||
#define MCHP_I2SMCC_RHR1R 0x004C /* Receiver Holding Right 1 Register */
|
||||
|
||||
#define MCHP_I2SMCC_RHL2R 0x0050 /* Receiver Holding Left 2 Register */
|
||||
#define MCHP_I2SMCC_RHR2R 0x0054 /* Receiver Holding Right 2 Register */
|
||||
|
||||
#define MCHP_I2SMCC_RHL3R 0x0058 /* Receiver Holding Left 3 Register */
|
||||
#define MCHP_I2SMCC_RHR3R 0x005C /* Receiver Holding Right 3 Register */
|
||||
|
||||
#define MCHP_I2SMCC_THL0R 0x0060 /* Transmitter Holding Left 0 Register */
|
||||
#define MCHP_I2SMCC_THR0R 0x0064 /* Transmitter Holding Right 0 Register */
|
||||
|
||||
#define MCHP_I2SMCC_THL1R 0x0068 /* Transmitter Holding Left 1 Register */
|
||||
#define MCHP_I2SMCC_THR1R 0x006C /* Transmitter Holding Right 1 Register */
|
||||
|
||||
#define MCHP_I2SMCC_THL2R 0x0070 /* Transmitter Holding Left 2 Register */
|
||||
#define MCHP_I2SMCC_THR2R 0x0074 /* Transmitter Holding Right 2 Register */
|
||||
|
||||
#define MCHP_I2SMCC_THL3R 0x0078 /* Transmitter Holding Left 3 Register */
|
||||
#define MCHP_I2SMCC_THR3R 0x007C /* Transmitter Holding Right 3 Register */
|
||||
|
||||
#define MCHP_I2SMCC_VERSION 0x00FC /* Version Register */
|
||||
|
||||
/*
|
||||
* ---- Control Register (Write-only) ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_CR_RXEN BIT(0) /* Receiver Enable */
|
||||
#define MCHP_I2SMCC_CR_RXDIS BIT(1) /* Receiver Disable */
|
||||
#define MCHP_I2SMCC_CR_CKEN BIT(2) /* Clock Enable */
|
||||
#define MCHP_I2SMCC_CR_CKDIS BIT(3) /* Clock Disable */
|
||||
#define MCHP_I2SMCC_CR_TXEN BIT(4) /* Transmitter Enable */
|
||||
#define MCHP_I2SMCC_CR_TXDIS BIT(5) /* Transmitter Disable */
|
||||
#define MCHP_I2SMCC_CR_SWRST BIT(7) /* Software Reset */
|
||||
|
||||
/*
|
||||
* ---- Mode Register A (Read/Write) ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_MRA_MODE_MASK GENMASK(0, 0)
|
||||
#define MCHP_I2SMCC_MRA_MODE_SLAVE (0 << 0)
|
||||
#define MCHP_I2SMCC_MRA_MODE_MASTER (1 << 0)
|
||||
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_MASK GENMASK(3, 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_32_BITS (0 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_24_BITS (1 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_20_BITS (2 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_18_BITS (3 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS (4 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS_COMPACT (5 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS (6 << 1)
|
||||
#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS_COMPACT (7 << 1)
|
||||
|
||||
#define MCHP_I2SMCC_MRA_WIRECFG_MASK GENMASK(5, 4)
|
||||
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_1_TDM_0 (0 << 4)
|
||||
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_2_TDM_1 (1 << 4)
|
||||
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_4_TDM_2 (2 << 4)
|
||||
#define MCHP_I2SMCC_MRA_WIRECFG_TDM_3 (3 << 4)
|
||||
|
||||
#define MCHP_I2SMCC_MRA_FORMAT_MASK GENMASK(7, 6)
|
||||
#define MCHP_I2SMCC_MRA_FORMAT_I2S (0 << 6)
|
||||
#define MCHP_I2SMCC_MRA_FORMAT_LJ (1 << 6) /* Left Justified */
|
||||
#define MCHP_I2SMCC_MRA_FORMAT_TDM (2 << 6)
|
||||
#define MCHP_I2SMCC_MRA_FORMAT_TDMLJ (3 << 6)
|
||||
|
||||
/* Transmitter uses one DMA channel ... */
|
||||
/* Left audio samples duplicated to right audio channel */
|
||||
#define MCHP_I2SMCC_MRA_RXMONO BIT(8)
|
||||
|
||||
/* I2SDO output of I2SC is internally connected to I2SDI input */
|
||||
#define MCHP_I2SMCC_MRA_RXLOOP BIT(9)
|
||||
|
||||
/* Receiver uses one DMA channel ... */
|
||||
/* Left audio samples duplicated to right audio channel */
|
||||
#define MCHP_I2SMCC_MRA_TXMONO BIT(10)
|
||||
|
||||
/* x sample transmitted when underrun */
|
||||
#define MCHP_I2SMCC_MRA_TXSAME_ZERO (0 << 11) /* Zero sample */
|
||||
#define MCHP_I2SMCC_MRA_TXSAME_PREVIOUS (1 << 11) /* Previous sample */
|
||||
|
||||
/* select between peripheral clock and generated clock */
|
||||
#define MCHP_I2SMCC_MRA_SRCCLK_PCLK (0 << 12)
|
||||
#define MCHP_I2SMCC_MRA_SRCCLK_GCLK (1 << 12)
|
||||
|
||||
/* Number of TDM Channels - 1 */
|
||||
#define MCHP_I2SMCC_MRA_NBCHAN_MASK GENMASK(15, 13)
|
||||
#define MCHP_I2SMCC_MRA_NBCHAN(ch) \
|
||||
((((ch) - 1) << 13) & MCHP_I2SMCC_MRA_NBCHAN_MASK)
|
||||
|
||||
/* Selected Clock to I2SMCC Master Clock ratio */
|
||||
#define MCHP_I2SMCC_MRA_IMCKDIV_MASK GENMASK(21, 16)
|
||||
#define MCHP_I2SMCC_MRA_IMCKDIV(div) \
|
||||
(((div) << 16) & MCHP_I2SMCC_MRA_IMCKDIV_MASK)
|
||||
|
||||
/* TDM Frame Synchronization */
|
||||
#define MCHP_I2SMCC_MRA_TDMFS_MASK GENMASK(23, 22)
|
||||
#define MCHP_I2SMCC_MRA_TDMFS_SLOT (0 << 22)
|
||||
#define MCHP_I2SMCC_MRA_TDMFS_HALF (1 << 22)
|
||||
#define MCHP_I2SMCC_MRA_TDMFS_BIT (2 << 22)
|
||||
|
||||
/* Selected Clock to I2SMC Serial Clock ratio */
|
||||
#define MCHP_I2SMCC_MRA_ISCKDIV_MASK GENMASK(29, 24)
|
||||
#define MCHP_I2SMCC_MRA_ISCKDIV(div) \
|
||||
(((div) << 24) & MCHP_I2SMCC_MRA_ISCKDIV_MASK)
|
||||
|
||||
/* Master Clock mode */
|
||||
#define MCHP_I2SMCC_MRA_IMCKMODE_MASK GENMASK(30, 30)
|
||||
/* 0: No master clock generated*/
|
||||
#define MCHP_I2SMCC_MRA_IMCKMODE_NONE (0 << 30)
|
||||
/* 1: master clock generated (internally generated clock drives I2SMCK pin) */
|
||||
#define MCHP_I2SMCC_MRA_IMCKMODE_GEN (1 << 30)
|
||||
|
||||
/* Slot Width */
|
||||
/* 0: slot is 32 bits wide for DATALENGTH = 18/20/24 bits. */
|
||||
/* 1: slot is 24 bits wide for DATALENGTH = 18/20/24 bits. */
|
||||
#define MCHP_I2SMCC_MRA_IWS BIT(31)
|
||||
|
||||
/*
|
||||
* ---- Mode Register B (Read/Write) ----
|
||||
*/
|
||||
/* all enabled I2S left channels are filled first, then I2S right channels */
|
||||
#define MCHP_I2SMCC_MRB_CRAMODE_LEFT_FIRST (0 << 0)
|
||||
/*
|
||||
* an enabled I2S left channel is filled, then the corresponding right
|
||||
* channel, until all channels are filled
|
||||
*/
|
||||
#define MCHP_I2SMCC_MRB_CRAMODE_REGULAR (1 << 0)
|
||||
|
||||
#define MCHP_I2SMCC_MRB_FIFOEN BIT(1)
|
||||
|
||||
#define MCHP_I2SMCC_MRB_DMACHUNK_MASK GENMASK(9, 8)
|
||||
#define MCHP_I2SMCC_MRB_DMACHUNK(no_words) \
|
||||
(((fls(no_words) - 1) << 8) & MCHP_I2SMCC_MRB_DMACHUNK_MASK)
|
||||
|
||||
#define MCHP_I2SMCC_MRB_CLKSEL_MASK GENMASK(16, 16)
|
||||
#define MCHP_I2SMCC_MRB_CLKSEL_EXT (0 << 16)
|
||||
#define MCHP_I2SMCC_MRB_CLKSEL_INT (1 << 16)
|
||||
|
||||
/*
|
||||
* ---- Status Registers (Read-only) ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_SR_RXEN BIT(0) /* Receiver Enabled */
|
||||
#define MCHP_I2SMCC_SR_TXEN BIT(4) /* Transmitter Enabled */
|
||||
|
||||
/*
|
||||
* ---- Interrupt Enable/Disable/Mask/Status Registers A ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_INT_TXRDY_MASK(ch) GENMASK((ch) - 1, 0)
|
||||
#define MCHP_I2SMCC_INT_TXRDYCH(ch) BIT(ch)
|
||||
#define MCHP_I2SMCC_INT_TXUNF_MASK(ch) GENMASK((ch) + 7, 8)
|
||||
#define MCHP_I2SMCC_INT_TXUNFCH(ch) BIT((ch) + 8)
|
||||
#define MCHP_I2SMCC_INT_RXRDY_MASK(ch) GENMASK((ch) + 15, 16)
|
||||
#define MCHP_I2SMCC_INT_RXRDYCH(ch) BIT((ch) + 16)
|
||||
#define MCHP_I2SMCC_INT_RXOVF_MASK(ch) GENMASK((ch) + 23, 24)
|
||||
#define MCHP_I2SMCC_INT_RXOVFCH(ch) BIT((ch) + 24)
|
||||
|
||||
/*
|
||||
* ---- Interrupt Enable/Disable/Mask/Status Registers B ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_INT_WERR BIT(0)
|
||||
#define MCHP_I2SMCC_INT_TXFFRDY BIT(8)
|
||||
#define MCHP_I2SMCC_INT_TXFFEMP BIT(9)
|
||||
#define MCHP_I2SMCC_INT_RXFFRDY BIT(12)
|
||||
#define MCHP_I2SMCC_INT_RXFFFUL BIT(13)
|
||||
|
||||
/*
|
||||
* ---- Version Register (Read-only) ----
|
||||
*/
|
||||
#define MCHP_I2SMCC_VERSION_MASK GENMASK(11, 0)
|
||||
|
||||
#define MCHP_I2SMCC_MAX_CHANNELS 8
|
||||
#define MCHP_I2MCC_TDM_SLOT_WIDTH 32
|
||||
|
||||
static const struct regmap_config mchp_i2s_mcc_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = MCHP_I2SMCC_VERSION,
|
||||
};
|
||||
|
||||
struct mchp_i2s_mcc_dev {
|
||||
struct wait_queue_head wq_txrdy;
|
||||
struct wait_queue_head wq_rxrdy;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *pclk;
|
||||
struct clk *gclk;
|
||||
struct snd_dmaengine_dai_dma_data playback;
|
||||
struct snd_dmaengine_dai_dma_data capture;
|
||||
unsigned int fmt;
|
||||
unsigned int sysclk;
|
||||
unsigned int frame_length;
|
||||
int tdm_slots;
|
||||
int channels;
|
||||
int gclk_use:1;
|
||||
int gclk_running:1;
|
||||
int tx_rdy:1;
|
||||
int rx_rdy:1;
|
||||
};
|
||||
|
||||
static irqreturn_t mchp_i2s_mcc_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = dev_id;
|
||||
u32 sra, imra, srb, imrb, pendinga, pendingb, idra = 0;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_IMRA, &imra);
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_ISRA, &sra);
|
||||
pendinga = imra & sra;
|
||||
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_IMRB, &imrb);
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_ISRB, &srb);
|
||||
pendingb = imrb & srb;
|
||||
|
||||
if (!pendinga && !pendingb)
|
||||
return IRQ_NONE;
|
||||
|
||||
/*
|
||||
* Tx/Rx ready interrupts are enabled when stopping only, to assure
|
||||
* availability and to disable clocks if necessary
|
||||
*/
|
||||
idra |= pendinga & (MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels) |
|
||||
MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
|
||||
if (idra)
|
||||
ret = IRQ_HANDLED;
|
||||
|
||||
if ((imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) &&
|
||||
(imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) ==
|
||||
(idra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels))) {
|
||||
dev->tx_rdy = 1;
|
||||
wake_up_interruptible(&dev->wq_txrdy);
|
||||
}
|
||||
if ((imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) &&
|
||||
(imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) ==
|
||||
(idra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels))) {
|
||||
dev->rx_rdy = 1;
|
||||
wake_up_interruptible(&dev->wq_rxrdy);
|
||||
}
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_set_sysclk(struct snd_soc_dai *dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(dev->dev, "%s() clk_id=%d freq=%u dir=%d\n",
|
||||
__func__, clk_id, freq, dir);
|
||||
|
||||
/* We do not need SYSCLK */
|
||||
if (dir == SND_SOC_CLOCK_IN)
|
||||
return 0;
|
||||
|
||||
dev->sysclk = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_set_bclk_ratio(struct snd_soc_dai *dai,
|
||||
unsigned int ratio)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(dev->dev, "%s() ratio=%u\n", __func__, ratio);
|
||||
|
||||
dev->frame_length = ratio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(dev->dev, "%s() fmt=%#x\n", __func__, fmt);
|
||||
|
||||
/* We don't support any kind of clock inversion */
|
||||
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
|
||||
return -EINVAL;
|
||||
|
||||
/* We can't generate only FSYNC */
|
||||
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFS)
|
||||
return -EINVAL;
|
||||
|
||||
/* We can only reconfigure the IP when it's stopped */
|
||||
if (fmt & SND_SOC_DAIFMT_CONT)
|
||||
return -EINVAL;
|
||||
|
||||
dev->fmt = fmt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int tx_mask,
|
||||
unsigned int rx_mask,
|
||||
int slots, int slot_width)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(dev->dev,
|
||||
"%s() tx_mask=0x%08x rx_mask=0x%08x slots=%d width=%d\n",
|
||||
__func__, tx_mask, rx_mask, slots, slot_width);
|
||||
|
||||
if (slots < 0 || slots > MCHP_I2SMCC_MAX_CHANNELS ||
|
||||
slot_width != MCHP_I2MCC_TDM_SLOT_WIDTH)
|
||||
return -EINVAL;
|
||||
|
||||
if (slots) {
|
||||
/* We do not support daisy chain */
|
||||
if (rx_mask != GENMASK(slots - 1, 0) ||
|
||||
rx_mask != tx_mask)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev->tdm_slots = slots;
|
||||
dev->frame_length = slots * MCHP_I2MCC_TDM_SLOT_WIDTH;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_clk_get_rate_diff(struct clk *clk,
|
||||
unsigned long rate,
|
||||
struct clk **best_clk,
|
||||
unsigned long *best_rate,
|
||||
unsigned long *best_diff_rate)
|
||||
{
|
||||
long round_rate;
|
||||
unsigned int diff_rate;
|
||||
|
||||
round_rate = clk_round_rate(clk, rate);
|
||||
if (round_rate < 0)
|
||||
return (int)round_rate;
|
||||
|
||||
diff_rate = abs(rate - round_rate);
|
||||
if (diff_rate < *best_diff_rate) {
|
||||
*best_clk = clk;
|
||||
*best_diff_rate = diff_rate;
|
||||
*best_rate = rate;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
|
||||
unsigned int bclk, unsigned int *mra)
|
||||
{
|
||||
unsigned long clk_rate;
|
||||
unsigned long lcm_rate;
|
||||
unsigned long best_rate = 0;
|
||||
unsigned long best_diff_rate = ~0;
|
||||
unsigned int sysclk;
|
||||
struct clk *best_clk = NULL;
|
||||
int ret;
|
||||
|
||||
/* For code simplification */
|
||||
if (!dev->sysclk)
|
||||
sysclk = bclk;
|
||||
else
|
||||
sysclk = dev->sysclk;
|
||||
|
||||
/*
|
||||
* MCLK is Selected CLK / (2 * IMCKDIV),
|
||||
* BCLK is Selected CLK / (2 * ISCKDIV);
|
||||
* if IMCKDIV or ISCKDIV are 0, MCLK or BCLK = Selected CLK
|
||||
*/
|
||||
lcm_rate = lcm(sysclk, bclk);
|
||||
if ((lcm_rate / sysclk % 2 == 1 && lcm_rate / sysclk > 2) ||
|
||||
(lcm_rate / bclk % 2 == 1 && lcm_rate / bclk > 2))
|
||||
lcm_rate *= 2;
|
||||
|
||||
for (clk_rate = lcm_rate;
|
||||
(clk_rate == sysclk || clk_rate / (sysclk * 2) <= GENMASK(5, 0)) &&
|
||||
(clk_rate == bclk || clk_rate / (bclk * 2) <= GENMASK(5, 0));
|
||||
clk_rate += lcm_rate) {
|
||||
ret = mchp_i2s_mcc_clk_get_rate_diff(dev->gclk, clk_rate,
|
||||
&best_clk, &best_rate,
|
||||
&best_diff_rate);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "gclk error for rate %lu: %d",
|
||||
clk_rate, ret);
|
||||
} else {
|
||||
if (!best_diff_rate) {
|
||||
dev_dbg(dev->dev, "found perfect rate on gclk: %lu\n",
|
||||
clk_rate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mchp_i2s_mcc_clk_get_rate_diff(dev->pclk, clk_rate,
|
||||
&best_clk, &best_rate,
|
||||
&best_diff_rate);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "pclk error for rate %lu: %d",
|
||||
clk_rate, ret);
|
||||
} else {
|
||||
if (!best_diff_rate) {
|
||||
dev_dbg(dev->dev, "found perfect rate on pclk: %lu\n",
|
||||
clk_rate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check if clocks returned only errors */
|
||||
if (!best_clk) {
|
||||
dev_err(dev->dev, "unable to change rate to clocks\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev->dev, "source CLK is %s with rate %lu, diff %lu\n",
|
||||
best_clk == dev->pclk ? "pclk" : "gclk",
|
||||
best_rate, best_diff_rate);
|
||||
|
||||
/* set the rate */
|
||||
ret = clk_set_rate(best_clk, best_rate);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "unable to set rate %lu to %s: %d\n",
|
||||
best_rate, best_clk == dev->pclk ? "PCLK" : "GCLK",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure divisors */
|
||||
if (dev->sysclk)
|
||||
*mra |= MCHP_I2SMCC_MRA_IMCKDIV(best_rate / (2 * sysclk));
|
||||
*mra |= MCHP_I2SMCC_MRA_ISCKDIV(best_rate / (2 * bclk));
|
||||
|
||||
if (best_clk == dev->gclk) {
|
||||
*mra |= MCHP_I2SMCC_MRA_SRCCLK_GCLK;
|
||||
ret = clk_prepare(dev->gclk);
|
||||
if (ret < 0)
|
||||
dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
|
||||
else
|
||||
dev->gclk_use = 1;
|
||||
} else {
|
||||
*mra |= MCHP_I2SMCC_MRA_SRCCLK_PCLK;
|
||||
dev->gclk_use = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_is_running(struct mchp_i2s_mcc_dev *dev)
|
||||
{
|
||||
u32 sr;
|
||||
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
|
||||
return !!(sr & (MCHP_I2SMCC_SR_TXEN | MCHP_I2SMCC_SR_RXEN));
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
u32 mra = 0;
|
||||
u32 mrb = 0;
|
||||
unsigned int channels = params_channels(params);
|
||||
unsigned int frame_length = dev->frame_length;
|
||||
unsigned int bclk_rate;
|
||||
int set_divs = 0;
|
||||
int ret;
|
||||
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
|
||||
|
||||
dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
|
||||
__func__, params_rate(params), params_format(params),
|
||||
params_width(params), params_channels(params));
|
||||
|
||||
switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
if (dev->tdm_slots) {
|
||||
dev_err(dev->dev, "I2S with TDM is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mra |= MCHP_I2SMCC_MRA_FORMAT_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
if (dev->tdm_slots) {
|
||||
dev_err(dev->dev, "Left-Justified with TDM is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mra |= MCHP_I2SMCC_MRA_FORMAT_LJ;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
mra |= MCHP_I2SMCC_MRA_FORMAT_TDM;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "unsupported bus format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
/* cpu is BCLK and LRC master */
|
||||
mra |= MCHP_I2SMCC_MRA_MODE_MASTER;
|
||||
if (dev->sysclk)
|
||||
mra |= MCHP_I2SMCC_MRA_IMCKMODE_GEN;
|
||||
set_divs = 1;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
/* cpu is BCLK master */
|
||||
mrb |= MCHP_I2SMCC_MRB_CLKSEL_INT;
|
||||
set_divs = 1;
|
||||
/* fall through */
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
/* cpu is slave */
|
||||
mra |= MCHP_I2SMCC_MRA_MODE_SLAVE;
|
||||
if (dev->sysclk)
|
||||
dev_warn(dev->dev, "Unable to generate MCLK in Slave mode\n");
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "unsupported master/slave mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dev->fmt & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) {
|
||||
switch (channels) {
|
||||
case 1:
|
||||
if (is_playback)
|
||||
mra |= MCHP_I2SMCC_MRA_TXMONO;
|
||||
else
|
||||
mra |= MCHP_I2SMCC_MRA_RXMONO;
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "unsupported number of audio channels\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!frame_length)
|
||||
frame_length = 2 * params_physical_width(params);
|
||||
} else if (dev->fmt & SND_SOC_DAIFMT_DSP_A) {
|
||||
if (dev->tdm_slots) {
|
||||
if (channels % 2 && channels * 2 <= dev->tdm_slots) {
|
||||
/*
|
||||
* Duplicate data for even-numbered channels
|
||||
* to odd-numbered channels
|
||||
*/
|
||||
if (is_playback)
|
||||
mra |= MCHP_I2SMCC_MRA_TXMONO;
|
||||
else
|
||||
mra |= MCHP_I2SMCC_MRA_RXMONO;
|
||||
}
|
||||
channels = dev->tdm_slots;
|
||||
}
|
||||
|
||||
mra |= MCHP_I2SMCC_MRA_NBCHAN(channels);
|
||||
if (!frame_length)
|
||||
frame_length = channels * MCHP_I2MCC_TDM_SLOT_WIDTH;
|
||||
}
|
||||
|
||||
/*
|
||||
* We must have the same burst size configured
|
||||
* in the DMA transfer and in out IP
|
||||
*/
|
||||
mrb |= MCHP_I2SMCC_MRB_DMACHUNK(channels);
|
||||
if (is_playback)
|
||||
dev->playback.maxburst = 1 << (fls(channels) - 1);
|
||||
else
|
||||
dev->capture.maxburst = 1 << (fls(channels) - 1);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_8_BITS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_16_BITS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_18_BITS |
|
||||
MCHP_I2SMCC_MRA_IWS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_20_BITS |
|
||||
MCHP_I2SMCC_MRA_IWS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_3LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS |
|
||||
MCHP_I2SMCC_MRA_IWS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
mra |= MCHP_I2SMCC_MRA_DATALENGTH_32_BITS;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "unsupported size/endianness for audio samples\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are already running, the wanted setup must be
|
||||
* the same with the one that's currently ongoing
|
||||
*/
|
||||
if (mchp_i2s_mcc_is_running(dev)) {
|
||||
u32 mra_cur;
|
||||
u32 mrb_cur;
|
||||
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_MRA, &mra_cur);
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_MRB, &mrb_cur);
|
||||
if (mra != mra_cur || mrb != mrb_cur)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save the number of channels to know what interrupts to enable */
|
||||
dev->channels = channels;
|
||||
|
||||
if (set_divs) {
|
||||
bclk_rate = frame_length * params_rate(params);
|
||||
ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "unable to configure the divisors: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_write(dev->regmap, MCHP_I2SMCC_MRA, mra);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return regmap_write(dev->regmap, MCHP_I2SMCC_MRB, mrb);
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_hw_free(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
|
||||
long err;
|
||||
|
||||
if (is_playback) {
|
||||
err = wait_event_interruptible_timeout(dev->wq_txrdy,
|
||||
dev->tx_rdy,
|
||||
msecs_to_jiffies(500));
|
||||
} else {
|
||||
err = wait_event_interruptible_timeout(dev->wq_rxrdy,
|
||||
dev->rx_rdy,
|
||||
msecs_to_jiffies(500));
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
u32 idra;
|
||||
|
||||
dev_warn_once(dev->dev, "Timeout waiting for %s\n",
|
||||
is_playback ? "Tx ready" : "Rx ready");
|
||||
if (is_playback)
|
||||
idra = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
|
||||
else
|
||||
idra = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
|
||||
}
|
||||
|
||||
if (!mchp_i2s_mcc_is_running(dev)) {
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_CR, MCHP_I2SMCC_CR_CKDIS);
|
||||
|
||||
if (dev->gclk_running) {
|
||||
clk_disable_unprepare(dev->gclk);
|
||||
dev->gclk_running = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
|
||||
u32 cr = 0;
|
||||
u32 iera = 0;
|
||||
u32 sr;
|
||||
int err;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (is_playback)
|
||||
cr = MCHP_I2SMCC_CR_TXEN | MCHP_I2SMCC_CR_CKEN;
|
||||
else
|
||||
cr = MCHP_I2SMCC_CR_RXEN | MCHP_I2SMCC_CR_CKEN;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
|
||||
if (is_playback && (sr & MCHP_I2SMCC_SR_TXEN)) {
|
||||
cr = MCHP_I2SMCC_CR_TXDIS;
|
||||
dev->tx_rdy = 0;
|
||||
/*
|
||||
* Enable Tx Ready interrupts on all channels
|
||||
* to assure all data is sent
|
||||
*/
|
||||
iera = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
|
||||
} else if (!is_playback && (sr & MCHP_I2SMCC_SR_RXEN)) {
|
||||
cr = MCHP_I2SMCC_CR_RXDIS;
|
||||
dev->rx_rdy = 0;
|
||||
/*
|
||||
* Enable Rx Ready interrupts on all channels
|
||||
* to assure all data is received
|
||||
*/
|
||||
iera = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((cr & MCHP_I2SMCC_CR_CKEN) && dev->gclk_use &&
|
||||
!dev->gclk_running) {
|
||||
err = clk_enable(dev->gclk);
|
||||
if (err) {
|
||||
dev_err_once(dev->dev, "failed to enable GCLK: %d\n",
|
||||
err);
|
||||
} else {
|
||||
dev->gclk_running = 1;
|
||||
}
|
||||
}
|
||||
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_IERA, iera);
|
||||
regmap_write(dev->regmap, MCHP_I2SMCC_CR, cr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
/* Software reset the IP if it's not running */
|
||||
if (!mchp_i2s_mcc_is_running(dev)) {
|
||||
return regmap_write(dev->regmap, MCHP_I2SMCC_CR,
|
||||
MCHP_I2SMCC_CR_SWRST);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = {
|
||||
.set_sysclk = mchp_i2s_mcc_set_sysclk,
|
||||
.set_bclk_ratio = mchp_i2s_mcc_set_bclk_ratio,
|
||||
.startup = mchp_i2s_mcc_startup,
|
||||
.trigger = mchp_i2s_mcc_trigger,
|
||||
.hw_params = mchp_i2s_mcc_hw_params,
|
||||
.hw_free = mchp_i2s_mcc_hw_free,
|
||||
.set_fmt = mchp_i2s_mcc_set_dai_fmt,
|
||||
.set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot,
|
||||
};
|
||||
|
||||
static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
init_waitqueue_head(&dev->wq_txrdy);
|
||||
init_waitqueue_head(&dev->wq_rxrdy);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000
|
||||
|
||||
#define MCHP_I2SMCC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
|
||||
SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S18_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver mchp_i2s_mcc_dai = {
|
||||
.probe = mchp_i2s_mcc_dai_probe,
|
||||
.playback = {
|
||||
.stream_name = "I2SMCC-Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = MCHP_I2SMCC_RATES,
|
||||
.formats = MCHP_I2SMCC_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "I2SMCC-Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = MCHP_I2SMCC_RATES,
|
||||
.formats = MCHP_I2SMCC_FORMATS,
|
||||
},
|
||||
.ops = &mchp_i2s_mcc_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
.symmetric_samplebits = 1,
|
||||
.symmetric_channels = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver mchp_i2s_mcc_component = {
|
||||
.name = "mchp-i2s-mcc",
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id mchp_i2s_mcc_dt_ids[] = {
|
||||
{
|
||||
.compatible = "microchip,sam9x60-i2smcc",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mchp_i2s_mcc_dt_ids);
|
||||
#endif
|
||||
|
||||
static int mchp_i2s_mcc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev;
|
||||
struct resource *mem;
|
||||
struct regmap *regmap;
|
||||
void __iomem *base;
|
||||
u32 version;
|
||||
int irq;
|
||||
int err;
|
||||
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&mchp_i2s_mcc_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = devm_request_irq(&pdev->dev, irq, mchp_i2s_mcc_interrupt, 0,
|
||||
dev_name(&pdev->dev), dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dev->pclk = devm_clk_get(&pdev->dev, "pclk");
|
||||
if (IS_ERR(dev->pclk)) {
|
||||
err = PTR_ERR(dev->pclk);
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get the peripheral clock: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Get the optional generated clock */
|
||||
dev->gclk = devm_clk_get(&pdev->dev, "gclk");
|
||||
if (IS_ERR(dev->gclk)) {
|
||||
if (PTR_ERR(dev->gclk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_warn(&pdev->dev,
|
||||
"generated clock not found: %d\n", err);
|
||||
dev->gclk = NULL;
|
||||
}
|
||||
|
||||
dev->dev = &pdev->dev;
|
||||
dev->regmap = regmap;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
err = clk_prepare_enable(dev->pclk);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to enable the peripheral clock: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_snd_soc_register_component(&pdev->dev,
|
||||
&mchp_i2s_mcc_component,
|
||||
&mchp_i2s_mcc_dai, 1);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to register DAI: %d\n", err);
|
||||
clk_disable_unprepare(dev->pclk);
|
||||
return err;
|
||||
}
|
||||
|
||||
dev->playback.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_THR;
|
||||
dev->capture.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_RHR;
|
||||
|
||||
err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
|
||||
clk_disable_unprepare(dev->pclk);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Get IP version. */
|
||||
regmap_read(dev->regmap, MCHP_I2SMCC_VERSION, &version);
|
||||
dev_info(&pdev->dev, "hw version: %#lx\n",
|
||||
version & MCHP_I2SMCC_VERSION_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_i2s_mcc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mchp_i2s_mcc_dev *dev = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(dev->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mchp_i2s_mcc_driver = {
|
||||
.driver = {
|
||||
.name = "mchp_i2s_mcc",
|
||||
.of_match_table = of_match_ptr(mchp_i2s_mcc_dt_ids),
|
||||
},
|
||||
.probe = mchp_i2s_mcc_probe,
|
||||
.remove = mchp_i2s_mcc_remove,
|
||||
};
|
||||
module_platform_driver(mchp_i2s_mcc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Microchip I2S Multi-Channel Controller driver");
|
||||
MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -117,8 +117,8 @@ static int tse850_put_mux2(struct snd_kcontrol *kctrl,
|
||||
return snd_soc_dapm_put_enum_double(kctrl, ucontrol);
|
||||
}
|
||||
|
||||
int tse850_get_mix(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static int tse850_get_mix(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
@ -129,8 +129,8 @@ int tse850_get_mix(struct snd_kcontrol *kctrl,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tse850_put_mix(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static int tse850_put_mix(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
@ -151,8 +151,8 @@ int tse850_put_mix(struct snd_kcontrol *kctrl,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int tse850_get_ana(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static int tse850_get_ana(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
@ -184,8 +184,8 @@ int tse850_get_ana(struct snd_kcontrol *kctrl,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tse850_put_ana(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static int tse850_put_ana(struct snd_kcontrol *kctrl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kctrl);
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
|
@ -94,6 +94,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_JZ4725B_CODEC
|
||||
select SND_SOC_LM4857 if I2C
|
||||
select SND_SOC_LM49453 if I2C
|
||||
select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR
|
||||
select SND_SOC_MAX98088 if I2C
|
||||
select SND_SOC_MAX98090 if I2C
|
||||
select SND_SOC_MAX98095 if I2C
|
||||
@ -179,8 +180,8 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
|
||||
select SND_SOC_TLV320AIC26 if SPI_MASTER
|
||||
select SND_SOC_TLV320AIC31XX if I2C
|
||||
select SND_SOC_TLV320AIC32X4_I2C if I2C
|
||||
select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER
|
||||
select SND_SOC_TLV320AIC32X4_I2C if I2C && COMMON_CLK
|
||||
select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER && COMMON_CLK
|
||||
select SND_SOC_TLV320AIC3X if I2C
|
||||
select SND_SOC_TPA6130A2 if I2C
|
||||
select SND_SOC_TLV320DAC33 if I2C
|
||||
@ -688,6 +689,13 @@ config SND_SOC_ISABELLE
|
||||
config SND_SOC_LM49453
|
||||
tristate
|
||||
|
||||
config SND_SOC_LOCHNAGAR_SC
|
||||
tristate "Lochnagar Sound Card"
|
||||
depends on MFD_LOCHNAGAR
|
||||
help
|
||||
This driver support the sound card functionality of the Cirrus
|
||||
Logic Lochnagar audio development board.
|
||||
|
||||
config SND_SOC_MAX98088
|
||||
tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec"
|
||||
depends on I2C
|
||||
@ -1097,15 +1105,18 @@ config SND_SOC_TLV320AIC31XX
|
||||
|
||||
config SND_SOC_TLV320AIC32X4
|
||||
tristate
|
||||
depends on COMMON_CLK
|
||||
|
||||
config SND_SOC_TLV320AIC32X4_I2C
|
||||
tristate "Texas Instruments TLV320AIC32x4 audio CODECs - I2C"
|
||||
depends on I2C
|
||||
depends on COMMON_CLK
|
||||
select SND_SOC_TLV320AIC32X4
|
||||
|
||||
config SND_SOC_TLV320AIC32X4_SPI
|
||||
tristate "Texas Instruments TLV320AIC32x4 audio CODECs - SPI"
|
||||
depends on SPI_MASTER
|
||||
depends on COMMON_CLK
|
||||
select SND_SOC_TLV320AIC32X4
|
||||
|
||||
config SND_SOC_TLV320AIC3X
|
||||
|
@ -91,6 +91,7 @@ snd-soc-jz4725b-codec-objs := jz4725b.o
|
||||
snd-soc-l3-objs := l3.o
|
||||
snd-soc-lm4857-objs := lm4857.o
|
||||
snd-soc-lm49453-objs := lm49453.o
|
||||
snd-soc-lochnagar-sc-objs := lochnagar-sc.o
|
||||
snd-soc-max9759-objs := max9759.o
|
||||
snd-soc-max9768-objs := max9768.o
|
||||
snd-soc-max98088-objs := max98088.o
|
||||
@ -192,7 +193,7 @@ snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
|
||||
snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
|
||||
snd-soc-tlv320aic26-objs := tlv320aic26.o
|
||||
snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o
|
||||
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
|
||||
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o tlv320aic32x4-clk.o
|
||||
snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o
|
||||
snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o
|
||||
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
|
||||
@ -364,6 +365,7 @@ obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o
|
||||
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
|
||||
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
|
||||
obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
|
||||
obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o
|
||||
obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o
|
||||
obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
|
||||
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
|
||||
|
@ -29,18 +29,27 @@ static int cs42l51_i2c_probe(struct i2c_client *i2c,
|
||||
struct regmap_config config;
|
||||
|
||||
config = cs42l51_regmap;
|
||||
config.val_bits = 8;
|
||||
config.reg_bits = 8;
|
||||
|
||||
return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config));
|
||||
}
|
||||
|
||||
static int cs42l51_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
return cs42l51_remove(&i2c->dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cs42l51_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cs42l51_suspend, cs42l51_resume)
|
||||
};
|
||||
|
||||
static struct i2c_driver cs42l51_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "cs42l51",
|
||||
.of_match_table = cs42l51_of_match,
|
||||
.pm = &cs42l51_pm_ops,
|
||||
},
|
||||
.probe = cs42l51_i2c_probe,
|
||||
.remove = cs42l51_i2c_remove,
|
||||
.id_table = cs42l51_i2c_id,
|
||||
};
|
||||
|
||||
|
@ -30,7 +30,9 @@
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "cs42l51.h"
|
||||
|
||||
@ -40,11 +42,21 @@ enum master_slave_mode {
|
||||
MODE_MASTER,
|
||||
};
|
||||
|
||||
static const char * const cs42l51_supply_names[] = {
|
||||
"VL",
|
||||
"VD",
|
||||
"VA",
|
||||
"VAHP",
|
||||
};
|
||||
|
||||
struct cs42l51_private {
|
||||
unsigned int mclk;
|
||||
struct clk *mclk_handle;
|
||||
unsigned int audio_mode; /* The mode (I2S or left-justified) */
|
||||
enum master_slave_mode func;
|
||||
struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)];
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
#define CS42L51_FORMATS ( \
|
||||
@ -111,6 +123,7 @@ static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0);
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(adc_boost_tlv, 2000, 2000, 0);
|
||||
static const char *chan_mix[] = {
|
||||
"L R",
|
||||
"L+R",
|
||||
@ -139,6 +152,8 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
|
||||
SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
|
||||
SOC_DOUBLE_TLV("Mic Boost Volume",
|
||||
CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
|
||||
SOC_DOUBLE_TLV("ADC Boost Volume",
|
||||
CS42L51_MIC_CTL, 5, 6, 1, 0, adc_boost_tlv),
|
||||
SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
|
||||
SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
|
||||
SOC_ENUM_EXT("PCM channel mixer",
|
||||
@ -195,7 +210,8 @@ static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
|
||||
SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
|
||||
|
||||
static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
|
||||
SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1, NULL,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
|
||||
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
|
||||
SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
|
||||
@ -329,6 +345,19 @@ static struct cs42l51_ratios slave_auto_ratios[] = {
|
||||
{ 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 },
|
||||
};
|
||||
|
||||
/*
|
||||
* Master mode mclk/fs ratios.
|
||||
* Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges
|
||||
* The table below provides support of following ratios:
|
||||
* 128: SSM (%128) with div2 disabled
|
||||
* 256: SSM (%128) with div2 enabled
|
||||
* In both cases, if sampling rate is above 50kHz, SSM is overridden
|
||||
* with DSM (%128) configuration
|
||||
*/
|
||||
static struct cs42l51_ratios master_ratios[] = {
|
||||
{ 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 },
|
||||
};
|
||||
|
||||
static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
@ -351,11 +380,13 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
|
||||
unsigned int ratio;
|
||||
struct cs42l51_ratios *ratios = NULL;
|
||||
int nr_ratios = 0;
|
||||
int intf_ctl, power_ctl, fmt;
|
||||
int intf_ctl, power_ctl, fmt, mode;
|
||||
|
||||
switch (cs42l51->func) {
|
||||
case MODE_MASTER:
|
||||
return -EINVAL;
|
||||
ratios = master_ratios;
|
||||
nr_ratios = ARRAY_SIZE(master_ratios);
|
||||
break;
|
||||
case MODE_SLAVE:
|
||||
ratios = slave_ratios;
|
||||
nr_ratios = ARRAY_SIZE(slave_ratios);
|
||||
@ -391,7 +422,16 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
|
||||
switch (cs42l51->func) {
|
||||
case MODE_MASTER:
|
||||
intf_ctl |= CS42L51_INTF_CTL_MASTER;
|
||||
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
|
||||
mode = ratios[i].speed_mode;
|
||||
/* Force DSM mode if sampling rate is above 50kHz */
|
||||
if (rate > 50000)
|
||||
mode = CS42L51_DSM_MODE;
|
||||
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode);
|
||||
/*
|
||||
* Auto detect mode is not applicable for master mode and has to
|
||||
* be disabled. Otherwise SPEED[1:0] bits will be ignored.
|
||||
*/
|
||||
power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO;
|
||||
break;
|
||||
case MODE_SLAVE:
|
||||
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
|
||||
@ -464,6 +504,13 @@ static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
|
||||
return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg);
|
||||
}
|
||||
|
||||
static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *endpoint)
|
||||
{
|
||||
/* return dai id 0, whatever the endpoint index */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops cs42l51_dai_ops = {
|
||||
.hw_params = cs42l51_hw_params,
|
||||
.set_sysclk = cs42l51_set_dai_sysclk,
|
||||
@ -526,13 +573,113 @@ static const struct snd_soc_component_driver soc_component_device_cs42l51 = {
|
||||
.num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets),
|
||||
.dapm_routes = cs42l51_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
|
||||
.of_xlate_dai_id = cs42l51_of_xlate_dai_id,
|
||||
.idle_bias_on = 1,
|
||||
.use_pmdown_time = 1,
|
||||
.endianness = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static bool cs42l51_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS42L51_POWER_CTL1:
|
||||
case CS42L51_MIC_POWER_CTL:
|
||||
case CS42L51_INTF_CTL:
|
||||
case CS42L51_MIC_CTL:
|
||||
case CS42L51_ADC_CTL:
|
||||
case CS42L51_ADC_INPUT:
|
||||
case CS42L51_DAC_OUT_CTL:
|
||||
case CS42L51_DAC_CTL:
|
||||
case CS42L51_ALC_PGA_CTL:
|
||||
case CS42L51_ALC_PGB_CTL:
|
||||
case CS42L51_ADCA_ATT:
|
||||
case CS42L51_ADCB_ATT:
|
||||
case CS42L51_ADCA_VOL:
|
||||
case CS42L51_ADCB_VOL:
|
||||
case CS42L51_PCMA_VOL:
|
||||
case CS42L51_PCMB_VOL:
|
||||
case CS42L51_BEEP_FREQ:
|
||||
case CS42L51_BEEP_VOL:
|
||||
case CS42L51_BEEP_CONF:
|
||||
case CS42L51_TONE_CTL:
|
||||
case CS42L51_AOUTA_VOL:
|
||||
case CS42L51_AOUTB_VOL:
|
||||
case CS42L51_PCM_MIXER:
|
||||
case CS42L51_LIMIT_THRES_DIS:
|
||||
case CS42L51_LIMIT_REL:
|
||||
case CS42L51_LIMIT_ATT:
|
||||
case CS42L51_ALC_EN:
|
||||
case CS42L51_ALC_REL:
|
||||
case CS42L51_ALC_THRES:
|
||||
case CS42L51_NOISE_CONF:
|
||||
case CS42L51_CHARGE_FREQ:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool cs42l51_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS42L51_STATUS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool cs42l51_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CS42L51_CHIP_REV_ID:
|
||||
case CS42L51_POWER_CTL1:
|
||||
case CS42L51_MIC_POWER_CTL:
|
||||
case CS42L51_INTF_CTL:
|
||||
case CS42L51_MIC_CTL:
|
||||
case CS42L51_ADC_CTL:
|
||||
case CS42L51_ADC_INPUT:
|
||||
case CS42L51_DAC_OUT_CTL:
|
||||
case CS42L51_DAC_CTL:
|
||||
case CS42L51_ALC_PGA_CTL:
|
||||
case CS42L51_ALC_PGB_CTL:
|
||||
case CS42L51_ADCA_ATT:
|
||||
case CS42L51_ADCB_ATT:
|
||||
case CS42L51_ADCA_VOL:
|
||||
case CS42L51_ADCB_VOL:
|
||||
case CS42L51_PCMA_VOL:
|
||||
case CS42L51_PCMB_VOL:
|
||||
case CS42L51_BEEP_FREQ:
|
||||
case CS42L51_BEEP_VOL:
|
||||
case CS42L51_BEEP_CONF:
|
||||
case CS42L51_TONE_CTL:
|
||||
case CS42L51_AOUTA_VOL:
|
||||
case CS42L51_AOUTB_VOL:
|
||||
case CS42L51_PCM_MIXER:
|
||||
case CS42L51_LIMIT_THRES_DIS:
|
||||
case CS42L51_LIMIT_REL:
|
||||
case CS42L51_LIMIT_ATT:
|
||||
case CS42L51_ALC_EN:
|
||||
case CS42L51_ALC_REL:
|
||||
case CS42L51_ALC_THRES:
|
||||
case CS42L51_NOISE_CONF:
|
||||
case CS42L51_STATUS:
|
||||
case CS42L51_CHARGE_FREQ:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const struct regmap_config cs42l51_regmap = {
|
||||
.reg_bits = 8,
|
||||
.reg_stride = 1,
|
||||
.val_bits = 8,
|
||||
.use_single_write = true,
|
||||
.readable_reg = cs42l51_readable_reg,
|
||||
.volatile_reg = cs42l51_volatile_reg,
|
||||
.writeable_reg = cs42l51_writeable_reg,
|
||||
.max_register = CS42L51_CHARGE_FREQ,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
@ -542,7 +689,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
|
||||
{
|
||||
struct cs42l51_private *cs42l51;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
@ -553,6 +700,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, cs42l51);
|
||||
cs42l51->regmap = regmap;
|
||||
|
||||
cs42l51->mclk_handle = devm_clk_get(dev, "MCLK");
|
||||
if (IS_ERR(cs42l51->mclk_handle)) {
|
||||
@ -561,6 +709,34 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
|
||||
cs42l51->mclk_handle = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++)
|
||||
cs42l51->supplies[i].supply = cs42l51_supply_names[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies),
|
||||
cs42l51->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to request supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies),
|
||||
cs42l51->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to enable supplies: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(cs42l51->reset_gpio))
|
||||
return PTR_ERR(cs42l51->reset_gpio);
|
||||
|
||||
if (cs42l51->reset_gpio) {
|
||||
dev_dbg(dev, "Release reset gpio\n");
|
||||
gpiod_set_value_cansleep(cs42l51->reset_gpio, 0);
|
||||
mdelay(2);
|
||||
}
|
||||
|
||||
/* Verify that we have a CS42L51 */
|
||||
ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
|
||||
if (ret < 0) {
|
||||
@ -579,11 +755,50 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
|
||||
|
||||
ret = devm_snd_soc_register_component(dev,
|
||||
&soc_component_device_cs42l51, &cs42l51_dai, 1);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
|
||||
cs42l51->supplies);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cs42l51_probe);
|
||||
|
||||
int cs42l51_remove(struct device *dev)
|
||||
{
|
||||
struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
|
||||
|
||||
gpiod_set_value_cansleep(cs42l51->reset_gpio, 1);
|
||||
|
||||
return regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
|
||||
cs42l51->supplies);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cs42l51_remove);
|
||||
|
||||
int __maybe_unused cs42l51_suspend(struct device *dev)
|
||||
{
|
||||
struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
|
||||
|
||||
regcache_cache_only(cs42l51->regmap, true);
|
||||
regcache_mark_dirty(cs42l51->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cs42l51_suspend);
|
||||
|
||||
int __maybe_unused cs42l51_resume(struct device *dev)
|
||||
{
|
||||
struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
|
||||
|
||||
regcache_cache_only(cs42l51->regmap, false);
|
||||
|
||||
return regcache_sync(cs42l51->regmap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cs42l51_resume);
|
||||
|
||||
const struct of_device_id cs42l51_of_match[] = {
|
||||
{ .compatible = "cirrus,cs42l51", },
|
||||
{ }
|
||||
|
@ -22,6 +22,9 @@ struct device;
|
||||
|
||||
extern const struct regmap_config cs42l51_regmap;
|
||||
int cs42l51_probe(struct device *dev, struct regmap *regmap);
|
||||
int cs42l51_remove(struct device *dev);
|
||||
int __maybe_unused cs42l51_suspend(struct device *dev);
|
||||
int __maybe_unused cs42l51_resume(struct device *dev);
|
||||
extern const struct of_device_id cs42l51_of_match[];
|
||||
|
||||
#define CS42L51_CHIP_ID 0x1B
|
||||
|
@ -2322,6 +2322,8 @@ static int cs43130_probe(struct snd_soc_component *component)
|
||||
return ret;
|
||||
|
||||
cs43130->wq = create_singlethread_workqueue("cs43130_hp");
|
||||
if (!cs43130->wq)
|
||||
return -ENOMEM;
|
||||
INIT_WORK(&cs43130->work, cs43130_imp_meas);
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,9 @@ static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w,
|
||||
|
||||
v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
|
||||
|
||||
return wm_adsp2_early_event(w, kcontrol, event, v);
|
||||
wm_adsp2_set_dspclk(w, v);
|
||||
|
||||
return wm_adsp_early_event(w, kcontrol, event);
|
||||
}
|
||||
|
||||
static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
|
||||
|
@ -797,6 +797,7 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
|
||||
u8 pll_ctrl, pll_status;
|
||||
int i = 0, ret;
|
||||
bool srm_lock = false;
|
||||
@ -805,11 +806,11 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
if (da7219->master) {
|
||||
/* Enable DAI clks for master mode */
|
||||
if (da7219->dai_clks) {
|
||||
ret = clk_prepare_enable(da7219->dai_clks);
|
||||
if (bclk) {
|
||||
ret = clk_prepare_enable(bclk);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to enable dai_clks\n");
|
||||
"Failed to enable DAI clks\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
@ -852,8 +853,8 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
|
||||
|
||||
/* Disable DAI clks if in master mode */
|
||||
if (da7219->master) {
|
||||
if (da7219->dai_clks)
|
||||
clk_disable_unprepare(da7219->dai_clks);
|
||||
if (bclk)
|
||||
clk_disable_unprepare(bclk);
|
||||
else
|
||||
snd_soc_component_update_bits(component,
|
||||
DA7219_DAI_CLK_MODE,
|
||||
@ -1385,17 +1386,50 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da7219_set_bclks_per_wclk(struct snd_soc_component *component,
|
||||
unsigned long factor)
|
||||
{
|
||||
u8 bclks_per_wclk;
|
||||
|
||||
switch (factor) {
|
||||
case 32:
|
||||
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
|
||||
break;
|
||||
case 64:
|
||||
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
|
||||
break;
|
||||
case 128:
|
||||
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
|
||||
break;
|
||||
case 256:
|
||||
bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
|
||||
DA7219_DAI_BCLKS_PER_WCLK_MASK,
|
||||
bclks_per_wclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int tx_mask, unsigned int rx_mask,
|
||||
int slots, int slot_width)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
|
||||
struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
|
||||
unsigned int ch_mask;
|
||||
u8 dai_bclks_per_wclk, slot_offset;
|
||||
unsigned long sr, bclk_rate;
|
||||
u8 slot_offset;
|
||||
u16 offset;
|
||||
__le16 dai_offset;
|
||||
u32 frame_size;
|
||||
int ret;
|
||||
|
||||
/* No channels enabled so disable TDM */
|
||||
if (!tx_mask) {
|
||||
@ -1432,28 +1466,26 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
||||
*/
|
||||
if (da7219->master) {
|
||||
frame_size = slots * slot_width;
|
||||
switch (frame_size) {
|
||||
case 32:
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
|
||||
break;
|
||||
case 64:
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
|
||||
break;
|
||||
case 128:
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
|
||||
break;
|
||||
case 256:
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "Invalid frame size %d\n",
|
||||
frame_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
|
||||
DA7219_DAI_BCLKS_PER_WCLK_MASK,
|
||||
dai_bclks_per_wclk);
|
||||
if (bclk) {
|
||||
sr = clk_get_rate(wclk);
|
||||
bclk_rate = sr * frame_size;
|
||||
ret = clk_set_rate(bclk, bclk_rate);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set TDM BCLK rate %lu: %d\n",
|
||||
bclk_rate, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = da7219_set_bclks_per_wclk(component, frame_size);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set TDM BCLKs per WCLK %d: %d\n",
|
||||
frame_size, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dai_offset = cpu_to_le16(offset);
|
||||
@ -1471,44 +1503,12 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da7219_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
static int da7219_set_sr(struct snd_soc_component *component,
|
||||
unsigned long rate)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
u8 dai_ctrl = 0, dai_bclks_per_wclk = 0, fs;
|
||||
unsigned int channels;
|
||||
int word_len = params_width(params);
|
||||
int frame_size;
|
||||
u8 fs;
|
||||
|
||||
switch (word_len) {
|
||||
case 16:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
|
||||
break;
|
||||
case 20:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
|
||||
break;
|
||||
case 24:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
|
||||
break;
|
||||
case 32:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
channels = params_channels(params);
|
||||
if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
|
||||
dev_err(component->dev,
|
||||
"Invalid number of channels, only 1 to %d supported\n",
|
||||
DA7219_DAI_CH_NUM_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
switch (rate) {
|
||||
case 8000:
|
||||
fs = DA7219_SR_8000;
|
||||
break;
|
||||
@ -1546,28 +1546,118 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_component_write(component, DA7219_SR, fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da7219_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
|
||||
struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
|
||||
u8 dai_ctrl = 0;
|
||||
unsigned int channels;
|
||||
unsigned long sr, bclk_rate;
|
||||
int word_len = params_width(params);
|
||||
int frame_size, ret;
|
||||
|
||||
switch (word_len) {
|
||||
case 16:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
|
||||
break;
|
||||
case 20:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
|
||||
break;
|
||||
case 24:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
|
||||
break;
|
||||
case 32:
|
||||
dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
channels = params_channels(params);
|
||||
if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
|
||||
dev_err(component->dev,
|
||||
"Invalid number of channels, only 1 to %d supported\n",
|
||||
DA7219_DAI_CH_NUM_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
|
||||
|
||||
sr = params_rate(params);
|
||||
if (da7219->master && wclk) {
|
||||
ret = clk_set_rate(wclk, sr);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set WCLK SR %lu: %d\n", sr, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = da7219_set_sr(component, sr);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set SR %lu: %d\n", sr, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're master, then we have a limited set of BCLK rates we
|
||||
* support. For slave mode this isn't the case and the codec can detect
|
||||
* the BCLK rate automatically.
|
||||
*/
|
||||
if (da7219->master && !da7219->tdm_en) {
|
||||
frame_size = word_len * 2;
|
||||
if (frame_size <= 32)
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
|
||||
if ((word_len * DA7219_DAI_CH_NUM_MAX) <= 32)
|
||||
frame_size = 32;
|
||||
else
|
||||
dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
|
||||
frame_size = 64;
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
|
||||
DA7219_DAI_BCLKS_PER_WCLK_MASK,
|
||||
dai_bclks_per_wclk);
|
||||
if (bclk) {
|
||||
bclk_rate = frame_size * sr;
|
||||
/*
|
||||
* Rounding the rate here avoids failure trying to set a
|
||||
* new rate on an already enabled bclk. In that
|
||||
* instance this will just set the same rate as is
|
||||
* currently in use, and so should continue without
|
||||
* problem, as long as the BCLK rate is suitable for the
|
||||
* desired frame size.
|
||||
*/
|
||||
bclk_rate = clk_round_rate(bclk, bclk_rate);
|
||||
if ((bclk_rate / sr) < frame_size) {
|
||||
dev_err(component->dev,
|
||||
"BCLK rate mismatch against frame size");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(bclk, bclk_rate);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set BCLK rate %lu: %d\n",
|
||||
bclk_rate, ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = da7219_set_bclks_per_wclk(component, frame_size);
|
||||
if (ret) {
|
||||
dev_err(component->dev,
|
||||
"Failed to set BCLKs per WCLK %d: %d\n",
|
||||
frame_size, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CTRL,
|
||||
DA7219_DAI_WORD_LENGTH_MASK |
|
||||
DA7219_DAI_CH_NUM_MASK,
|
||||
dai_ctrl);
|
||||
snd_soc_component_write(component, DA7219_SR, fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1678,11 +1768,14 @@ static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_component *compone
|
||||
|
||||
pdata->wakeup_source = device_property_read_bool(dev, "wakeup-source");
|
||||
|
||||
pdata->dai_clks_name = "da7219-dai-clks";
|
||||
if (device_property_read_string(dev, "clock-output-names",
|
||||
&pdata->dai_clks_name))
|
||||
dev_warn(dev, "Using default clk name: %s\n",
|
||||
pdata->dai_clks_name);
|
||||
pdata->dai_clk_names[DA7219_DAI_WCLK_IDX] = "da7219-dai-wclk";
|
||||
pdata->dai_clk_names[DA7219_DAI_BCLK_IDX] = "da7219-dai-bclk";
|
||||
if (device_property_read_string_array(dev, "clock-output-names",
|
||||
pdata->dai_clk_names,
|
||||
DA7219_DAI_NUM_CLKS) < 0)
|
||||
dev_warn(dev, "Using default DAI clk names: %s, %s\n",
|
||||
pdata->dai_clk_names[DA7219_DAI_WCLK_IDX],
|
||||
pdata->dai_clk_names[DA7219_DAI_BCLK_IDX]);
|
||||
|
||||
if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0)
|
||||
pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32);
|
||||
@ -1799,12 +1892,16 @@ static int da7219_handle_supplies(struct snd_soc_component *component)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
static int da7219_dai_clks_prepare(struct clk_hw *hw)
|
||||
static int da7219_wclk_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv, dai_clks_hw);
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
|
||||
if (!da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
|
||||
DA7219_DAI_CLK_EN_MASK,
|
||||
DA7219_DAI_CLK_EN_MASK);
|
||||
@ -1812,33 +1909,42 @@ static int da7219_dai_clks_prepare(struct clk_hw *hw)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void da7219_dai_clks_unprepare(struct clk_hw *hw)
|
||||
static void da7219_wclk_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv, dai_clks_hw);
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
|
||||
if (!da7219->master)
|
||||
return;
|
||||
|
||||
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
|
||||
DA7219_DAI_CLK_EN_MASK, 0);
|
||||
}
|
||||
|
||||
static int da7219_dai_clks_is_prepared(struct clk_hw *hw)
|
||||
static int da7219_wclk_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv, dai_clks_hw);
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
u8 clk_reg;
|
||||
|
||||
if (!da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
clk_reg = snd_soc_component_read32(component, DA7219_DAI_CLK_MODE);
|
||||
|
||||
return !!(clk_reg & DA7219_DAI_CLK_EN_MASK);
|
||||
}
|
||||
|
||||
static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv, dai_clks_hw);
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
u8 fs = snd_soc_component_read32(component, DA7219_SR);
|
||||
|
||||
@ -1870,11 +1976,148 @@ static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw,
|
||||
}
|
||||
}
|
||||
|
||||
static const struct clk_ops da7219_dai_clks_ops = {
|
||||
.prepare = da7219_dai_clks_prepare,
|
||||
.unprepare = da7219_dai_clks_unprepare,
|
||||
.is_prepared = da7219_dai_clks_is_prepared,
|
||||
.recalc_rate = da7219_dai_clks_recalc_rate,
|
||||
static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
|
||||
if (!da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
if (rate < 11025)
|
||||
return 8000;
|
||||
else if (rate < 12000)
|
||||
return 11025;
|
||||
else if (rate < 16000)
|
||||
return 12000;
|
||||
else if (rate < 22050)
|
||||
return 16000;
|
||||
else if (rate < 24000)
|
||||
return 22050;
|
||||
else if (rate < 32000)
|
||||
return 24000;
|
||||
else if (rate < 44100)
|
||||
return 32000;
|
||||
else if (rate < 48000)
|
||||
return 44100;
|
||||
else if (rate < 88200)
|
||||
return 48000;
|
||||
else if (rate < 96000)
|
||||
return 88200;
|
||||
else
|
||||
return 96000;
|
||||
}
|
||||
|
||||
static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_WCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
|
||||
if (!da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
return da7219_set_sr(component, rate);
|
||||
}
|
||||
|
||||
static unsigned long da7219_bclk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_BCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
u8 bclks_per_wclk = snd_soc_component_read32(component,
|
||||
DA7219_DAI_CLK_MODE);
|
||||
|
||||
switch (bclks_per_wclk & DA7219_DAI_BCLKS_PER_WCLK_MASK) {
|
||||
case DA7219_DAI_BCLKS_PER_WCLK_32:
|
||||
return parent_rate * 32;
|
||||
case DA7219_DAI_BCLKS_PER_WCLK_64:
|
||||
return parent_rate * 64;
|
||||
case DA7219_DAI_BCLKS_PER_WCLK_128:
|
||||
return parent_rate * 128;
|
||||
case DA7219_DAI_BCLKS_PER_WCLK_256:
|
||||
return parent_rate * 256;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long da7219_bclk_get_factor(unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned long factor;
|
||||
|
||||
factor = rate / parent_rate;
|
||||
if (factor < 64)
|
||||
return 32;
|
||||
else if (factor < 128)
|
||||
return 64;
|
||||
else if (factor < 256)
|
||||
return 128;
|
||||
else
|
||||
return 256;
|
||||
}
|
||||
|
||||
static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_BCLK_IDX]);
|
||||
unsigned long factor;
|
||||
|
||||
if (!*parent_rate || !da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* We don't allow changing the parent rate as some BCLK rates can be
|
||||
* derived from multiple parent WCLK rates (BCLK rates are set as a
|
||||
* multiplier of WCLK in HW). We just do some rounding down based on the
|
||||
* parent WCLK rate set and find the appropriate multiplier of BCLK to
|
||||
* get the rounded down BCLK value.
|
||||
*/
|
||||
factor = da7219_bclk_get_factor(rate, *parent_rate);
|
||||
|
||||
return *parent_rate * factor;
|
||||
}
|
||||
|
||||
static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct da7219_priv *da7219 =
|
||||
container_of(hw, struct da7219_priv,
|
||||
dai_clks_hw[DA7219_DAI_BCLK_IDX]);
|
||||
struct snd_soc_component *component = da7219->component;
|
||||
unsigned long factor;
|
||||
|
||||
if (!da7219->master)
|
||||
return -EINVAL;
|
||||
|
||||
factor = da7219_bclk_get_factor(rate, parent_rate);
|
||||
|
||||
return da7219_set_bclks_per_wclk(component, factor);
|
||||
}
|
||||
|
||||
static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = {
|
||||
[DA7219_DAI_WCLK_IDX] = {
|
||||
.prepare = da7219_wclk_prepare,
|
||||
.unprepare = da7219_wclk_unprepare,
|
||||
.is_prepared = da7219_wclk_is_prepared,
|
||||
.recalc_rate = da7219_wclk_recalc_rate,
|
||||
.round_rate = da7219_wclk_round_rate,
|
||||
.set_rate = da7219_wclk_set_rate,
|
||||
},
|
||||
[DA7219_DAI_BCLK_IDX] = {
|
||||
.recalc_rate = da7219_bclk_recalc_rate,
|
||||
.round_rate = da7219_bclk_round_rate,
|
||||
.set_rate = da7219_bclk_set_rate,
|
||||
},
|
||||
};
|
||||
|
||||
static int da7219_register_dai_clks(struct snd_soc_component *component)
|
||||
@ -1882,47 +2125,81 @@ static int da7219_register_dai_clks(struct snd_soc_component *component)
|
||||
struct device *dev = component->dev;
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
struct da7219_pdata *pdata = da7219->pdata;
|
||||
struct clk_init_data init = {};
|
||||
struct clk *dai_clks;
|
||||
struct clk_lookup *dai_clks_lookup;
|
||||
const char *parent_name;
|
||||
int i, ret;
|
||||
|
||||
if (da7219->mclk) {
|
||||
parent_name = __clk_get_name(da7219->mclk);
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
} else {
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
}
|
||||
for (i = 0; i < DA7219_DAI_NUM_CLKS; ++i) {
|
||||
struct clk_init_data init = {};
|
||||
struct clk *dai_clk;
|
||||
struct clk_lookup *dai_clk_lookup;
|
||||
struct clk_hw *dai_clk_hw = &da7219->dai_clks_hw[i];
|
||||
|
||||
init.name = pdata->dai_clks_name;
|
||||
init.ops = &da7219_dai_clks_ops;
|
||||
init.flags = CLK_GET_RATE_NOCACHE;
|
||||
da7219->dai_clks_hw.init = &init;
|
||||
switch (i) {
|
||||
case DA7219_DAI_WCLK_IDX:
|
||||
/*
|
||||
* If we can, make MCLK the parent of WCLK to ensure
|
||||
* it's enabled as required.
|
||||
*/
|
||||
if (da7219->mclk) {
|
||||
parent_name = __clk_get_name(da7219->mclk);
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
} else {
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
}
|
||||
break;
|
||||
case DA7219_DAI_BCLK_IDX:
|
||||
/* Make WCLK the parent of BCLK */
|
||||
parent_name = __clk_get_name(da7219->dai_clks[DA7219_DAI_WCLK_IDX]);
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid clock index\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dai_clks = devm_clk_register(dev, &da7219->dai_clks_hw);
|
||||
if (IS_ERR(dai_clks)) {
|
||||
dev_warn(dev, "Failed to register DAI clocks: %ld\n",
|
||||
PTR_ERR(dai_clks));
|
||||
return PTR_ERR(dai_clks);
|
||||
}
|
||||
da7219->dai_clks = dai_clks;
|
||||
init.name = pdata->dai_clk_names[i];
|
||||
init.ops = &da7219_dai_clk_ops[i];
|
||||
init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
|
||||
dai_clk_hw->init = &init;
|
||||
|
||||
/* If we're using DT, then register as provider accordingly */
|
||||
if (dev->of_node) {
|
||||
devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
|
||||
&da7219->dai_clks_hw);
|
||||
} else {
|
||||
dai_clks_lookup = clkdev_create(dai_clks, pdata->dai_clks_name,
|
||||
"%s", dev_name(dev));
|
||||
if (!dai_clks_lookup)
|
||||
return -ENOMEM;
|
||||
else
|
||||
da7219->dai_clks_lookup = dai_clks_lookup;
|
||||
dai_clk = devm_clk_register(dev, dai_clk_hw);
|
||||
if (IS_ERR(dai_clk)) {
|
||||
dev_warn(dev, "Failed to register %s: %ld\n",
|
||||
init.name, PTR_ERR(dai_clk));
|
||||
ret = PTR_ERR(dai_clk);
|
||||
goto err;
|
||||
}
|
||||
da7219->dai_clks[i] = dai_clk;
|
||||
|
||||
/* If we're using DT, then register as provider accordingly */
|
||||
if (dev->of_node) {
|
||||
devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
|
||||
dai_clk_hw);
|
||||
} else {
|
||||
dai_clk_lookup = clkdev_create(dai_clk, init.name,
|
||||
"%s", dev_name(dev));
|
||||
if (!dai_clk_lookup) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
} else {
|
||||
da7219->dai_clks_lookup[i] = dai_clk_lookup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
do {
|
||||
if (da7219->dai_clks_lookup[i])
|
||||
clkdev_drop(da7219->dai_clks_lookup[i]);
|
||||
} while (i-- > 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static inline int da7219_register_dai_clks(struct snd_soc_component *component)
|
||||
@ -2086,12 +2363,15 @@ err_disable_reg:
|
||||
static void da7219_remove(struct snd_soc_component *component)
|
||||
{
|
||||
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
|
||||
int i;
|
||||
|
||||
da7219_aad_exit(component);
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
if (da7219->dai_clks_lookup)
|
||||
clkdev_drop(da7219->dai_clks_lookup);
|
||||
for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) {
|
||||
if (da7219->dai_clks_lookup[i])
|
||||
clkdev_drop(da7219->dai_clks_lookup[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Supplies */
|
||||
|
@ -820,10 +820,10 @@ struct da7219_priv {
|
||||
struct mutex pll_lock;
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
struct clk_hw dai_clks_hw;
|
||||
struct clk_hw dai_clks_hw[DA7219_DAI_NUM_CLKS];
|
||||
#endif
|
||||
struct clk_lookup *dai_clks_lookup;
|
||||
struct clk *dai_clks;
|
||||
struct clk_lookup *dai_clks_lookup[DA7219_DAI_NUM_CLKS];
|
||||
struct clk *dai_clks[DA7219_DAI_NUM_CLKS];
|
||||
|
||||
struct clk *mclk;
|
||||
unsigned int mclk_rate;
|
||||
|
@ -43,6 +43,7 @@ struct es8316_priv {
|
||||
unsigned int sysclk;
|
||||
unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS];
|
||||
struct snd_pcm_hw_constraint_list sysclk_constraints;
|
||||
bool jd_inverted;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -577,6 +578,9 @@ static irqreturn_t es8316_irq(int irq, void *data)
|
||||
if (!es8316->jack)
|
||||
goto out;
|
||||
|
||||
if (es8316->jd_inverted)
|
||||
flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
|
||||
|
||||
dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
|
||||
if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
|
||||
/* Jack removed, or spurious IRQ? */
|
||||
@ -592,6 +596,8 @@ static irqreturn_t es8316_irq(int irq, void *data)
|
||||
/* Jack inserted, determine type */
|
||||
es8316_enable_micbias_for_mic_gnd_short_detect(comp);
|
||||
regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
|
||||
if (es8316->jd_inverted)
|
||||
flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
|
||||
dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
|
||||
if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
|
||||
/* Jack unplugged underneath us */
|
||||
@ -633,6 +639,14 @@ static void es8316_enable_jack_detect(struct snd_soc_component *component,
|
||||
{
|
||||
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
/*
|
||||
* Init es8316->jd_inverted here and not in the probe, as we cannot
|
||||
* guarantee that the bytchr-es8316 driver, which might set this
|
||||
* property, will probe before us.
|
||||
*/
|
||||
es8316->jd_inverted = device_property_read_bool(component->dev,
|
||||
"everest,jack-detect-inverted");
|
||||
|
||||
mutex_lock(&es8316->lock);
|
||||
|
||||
es8316->jack = jack;
|
||||
|
@ -496,10 +496,6 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hdmi_codec_new_stream(substream, dai);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hdmi_audio_infoframe_init(&hp.cea);
|
||||
hp.cea.channels = params_channels(params);
|
||||
hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
|
||||
@ -761,7 +757,7 @@ static int hdmi_codec_probe(struct platform_device *pdev)
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
if (!hcd) {
|
||||
dev_err(dev, "%s: No plalform data\n", __func__);
|
||||
dev_err(dev, "%s: No platform data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
266
sound/soc/codecs/lochnagar-sc.c
Normal file
266
sound/soc/codecs/lochnagar-sc.c
Normal file
@ -0,0 +1,266 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Lochnagar sound card driver
|
||||
//
|
||||
// Copyright (c) 2017-2019 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
//
|
||||
// Author: Charles Keepax <ckeepax@opensource.cirrus.com>
|
||||
// Piotr Stankiewicz <piotrs@opensource.cirrus.com>
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <linux/mfd/lochnagar.h>
|
||||
#include <linux/mfd/lochnagar1_regs.h>
|
||||
#include <linux/mfd/lochnagar2_regs.h>
|
||||
|
||||
struct lochnagar_sc_priv {
|
||||
struct clk *mclk;
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget lochnagar_sc_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("Line Jack", NULL),
|
||||
SND_SOC_DAPM_LINE("USB Audio", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route lochnagar_sc_routes[] = {
|
||||
{ "Line Jack", NULL, "AIF1 Playback" },
|
||||
{ "AIF1 Capture", NULL, "Line Jack" },
|
||||
|
||||
{ "USB Audio", NULL, "USB1 Playback" },
|
||||
{ "USB Audio", NULL, "USB2 Playback" },
|
||||
{ "USB1 Capture", NULL, "USB Audio" },
|
||||
{ "USB2 Capture", NULL, "USB Audio" },
|
||||
};
|
||||
|
||||
static const unsigned int lochnagar_sc_chan_vals[] = {
|
||||
4, 8,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list lochnagar_sc_chan_constraint = {
|
||||
.count = ARRAY_SIZE(lochnagar_sc_chan_vals),
|
||||
.list = lochnagar_sc_chan_vals,
|
||||
};
|
||||
|
||||
static const unsigned int lochnagar_sc_rate_vals[] = {
|
||||
8000, 16000, 24000, 32000, 48000, 96000, 192000,
|
||||
22050, 44100, 88200, 176400,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list lochnagar_sc_rate_constraint = {
|
||||
.count = ARRAY_SIZE(lochnagar_sc_rate_vals),
|
||||
.list = lochnagar_sc_rate_vals,
|
||||
};
|
||||
|
||||
static int lochnagar_sc_hw_rule_rate(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_interval range = {
|
||||
.min = 8000,
|
||||
.max = 24576000 / hw_param_interval(params, rule->deps[0])->max,
|
||||
};
|
||||
|
||||
return snd_interval_refine(hw_param_interval(params, rule->var),
|
||||
&range);
|
||||
}
|
||||
|
||||
static int lochnagar_sc_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *comp = dai->component;
|
||||
struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
|
||||
int ret;
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&lochnagar_sc_rate_constraint);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return snd_pcm_hw_rule_add(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
lochnagar_sc_hw_rule_rate, priv,
|
||||
SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
|
||||
}
|
||||
|
||||
static int lochnagar_sc_line_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *comp = dai->component;
|
||||
struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->mclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Failed to enable MCLK: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = lochnagar_sc_startup(substream, dai);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&lochnagar_sc_chan_constraint);
|
||||
}
|
||||
|
||||
static void lochnagar_sc_line_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *comp = dai->component;
|
||||
struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
|
||||
|
||||
clk_disable_unprepare(priv->mclk);
|
||||
}
|
||||
|
||||
static int lochnagar_sc_check_fmt(struct snd_soc_dai *dai, unsigned int fmt,
|
||||
unsigned int tar)
|
||||
{
|
||||
tar |= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF;
|
||||
|
||||
if ((fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) != tar)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lochnagar_sc_set_line_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBS_CFS);
|
||||
}
|
||||
|
||||
static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBM_CFM);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops lochnagar_sc_line_ops = {
|
||||
.startup = lochnagar_sc_line_startup,
|
||||
.shutdown = lochnagar_sc_line_shutdown,
|
||||
.set_fmt = lochnagar_sc_set_line_fmt,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = {
|
||||
.startup = lochnagar_sc_startup,
|
||||
.set_fmt = lochnagar_sc_set_usb_fmt,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver lochnagar_sc_dai[] = {
|
||||
{
|
||||
.name = "lochnagar-line",
|
||||
.playback = {
|
||||
.stream_name = "AIF1 Playback",
|
||||
.channels_min = 4,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "AIF1 Capture",
|
||||
.channels_min = 4,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
||||
},
|
||||
.ops = &lochnagar_sc_line_ops,
|
||||
.symmetric_rates = true,
|
||||
.symmetric_samplebits = true,
|
||||
},
|
||||
{
|
||||
.name = "lochnagar-usb1",
|
||||
.playback = {
|
||||
.stream_name = "USB1 Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "USB1 Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
||||
},
|
||||
.ops = &lochnagar_sc_usb_ops,
|
||||
.symmetric_rates = true,
|
||||
.symmetric_samplebits = true,
|
||||
},
|
||||
{
|
||||
.name = "lochnagar-usb2",
|
||||
.playback = {
|
||||
.stream_name = "USB2 Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "USB2 Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
||||
},
|
||||
.ops = &lochnagar_sc_usb_ops,
|
||||
.symmetric_rates = true,
|
||||
.symmetric_samplebits = true,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver lochnagar_sc_driver = {
|
||||
.non_legacy_dai_naming = 1,
|
||||
|
||||
.dapm_widgets = lochnagar_sc_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(lochnagar_sc_widgets),
|
||||
.dapm_routes = lochnagar_sc_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(lochnagar_sc_routes),
|
||||
};
|
||||
|
||||
static int lochnagar_sc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lochnagar_sc_priv *priv;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->mclk = devm_clk_get(&pdev->dev, "mclk");
|
||||
if (IS_ERR(priv->mclk)) {
|
||||
ret = PTR_ERR(priv->mclk);
|
||||
dev_err(&pdev->dev, "Failed to get MCLK: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return devm_snd_soc_register_component(&pdev->dev,
|
||||
&lochnagar_sc_driver,
|
||||
lochnagar_sc_dai,
|
||||
ARRAY_SIZE(lochnagar_sc_dai));
|
||||
}
|
||||
|
||||
static const struct of_device_id lochnagar_of_match[] = {
|
||||
{ .compatible = "cirrus,lochnagar2-soundcard" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lochnagar_of_match);
|
||||
|
||||
static struct platform_driver lochnagar_sc_codec_driver = {
|
||||
.driver = {
|
||||
.name = "lochnagar-soundcard",
|
||||
.of_match_table = of_match_ptr(lochnagar_of_match),
|
||||
},
|
||||
|
||||
.probe = lochnagar_sc_probe,
|
||||
};
|
||||
module_platform_driver(lochnagar_sc_codec_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC Lochnagar Sound Card Driver");
|
||||
MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:lochnagar-soundcard");
|
@ -97,7 +97,10 @@ static struct snd_soc_dai_driver max98357a_dai_driver = {
|
||||
SNDRV_PCM_FMTBIT_S32,
|
||||
.rates = SNDRV_PCM_RATE_8000 |
|
||||
SNDRV_PCM_RATE_16000 |
|
||||
SNDRV_PCM_RATE_32000 |
|
||||
SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_88200 |
|
||||
SNDRV_PCM_RATE_96000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
|
@ -493,7 +493,7 @@ static int nau8810_set_sysclk(struct snd_soc_dai *dai,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nau88l0_calc_pll(unsigned int pll_in,
|
||||
static int nau8810_calc_pll(unsigned int pll_in,
|
||||
unsigned int fs, struct nau8810_pll *pll_param)
|
||||
{
|
||||
u64 f2, f2_max, pll_ratio;
|
||||
@ -505,7 +505,8 @@ static int nau88l0_calc_pll(unsigned int pll_in,
|
||||
f2_max = 0;
|
||||
scal_sel = ARRAY_SIZE(nau8810_mclk_scaler);
|
||||
for (i = 0; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) {
|
||||
f2 = 256 * fs * 4 * nau8810_mclk_scaler[i] / 10;
|
||||
f2 = 256ULL * fs * 4 * nau8810_mclk_scaler[i];
|
||||
f2 = div_u64(f2, 10);
|
||||
if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX &&
|
||||
f2_max < f2) {
|
||||
f2_max = f2;
|
||||
@ -542,7 +543,7 @@ static int nau8810_set_pll(struct snd_soc_dai *codec_dai, int pll_id,
|
||||
int ret, fs;
|
||||
|
||||
fs = freq_out / 256;
|
||||
ret = nau88l0_calc_pll(freq_in, fs, pll_param);
|
||||
ret = nau8810_calc_pll(freq_in, fs, pll_param);
|
||||
if (ret < 0) {
|
||||
dev_err(nau8810->dev, "Unsupported input clock %d\n", freq_in);
|
||||
return ret;
|
||||
@ -667,6 +668,24 @@ static int nau8810_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
|
||||
int val_len = 0, val_rate = 0, ret = 0;
|
||||
unsigned int ctrl_val, bclk_fs, bclk_div;
|
||||
|
||||
/* Select BCLK configuration if the codec as master. */
|
||||
regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &ctrl_val);
|
||||
if (ctrl_val & NAU8810_CLKIO_MASTER) {
|
||||
/* get the bclk and fs ratio */
|
||||
bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params);
|
||||
if (bclk_fs <= 32)
|
||||
bclk_div = NAU8810_BCLKDIV_8;
|
||||
else if (bclk_fs <= 64)
|
||||
bclk_div = NAU8810_BCLKDIV_4;
|
||||
else if (bclk_fs <= 128)
|
||||
bclk_div = NAU8810_BCLKDIV_2;
|
||||
else
|
||||
return -EINVAL;
|
||||
regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
|
||||
NAU8810_BCLKSEL_MASK, bclk_div);
|
||||
}
|
||||
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
|
@ -457,13 +457,16 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
|
||||
if (chan > 2) {
|
||||
switch (fmt) {
|
||||
case PCM3168A_FMT_I2S:
|
||||
case PCM3168A_FMT_DSP_A:
|
||||
fmt = PCM3168A_FMT_I2S_TDM;
|
||||
break;
|
||||
case PCM3168A_FMT_LEFT_J:
|
||||
case PCM3168A_FMT_DSP_B:
|
||||
fmt = PCM3168A_FMT_LEFT_J_TDM;
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "TDM is supported under I2S/Left_J only\n");
|
||||
dev_err(component->dev,
|
||||
"TDM is supported under DSP/I2S/Left_J only\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@ -526,6 +529,8 @@ static int pcm3168a_startup(struct snd_pcm_substream *substream,
|
||||
break;
|
||||
case PCM3168A_FMT_LEFT_J:
|
||||
case PCM3168A_FMT_I2S:
|
||||
case PCM3168A_FMT_DSP_A:
|
||||
case PCM3168A_FMT_DSP_B:
|
||||
sample_min = 24;
|
||||
channel_max = channel_maxs[tx];
|
||||
break;
|
||||
|
@ -3419,6 +3419,9 @@ static int rt5645_probe(struct snd_soc_component *component)
|
||||
RT5645_HWEQ_NUM, sizeof(struct rt5645_eq_param_s),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!rt5645->eq_param)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3631,6 +3634,11 @@ static const struct rt5645_platform_data jd_mode3_platform_data = {
|
||||
.jd_mode = 3,
|
||||
};
|
||||
|
||||
static const struct rt5645_platform_data lattepanda_board_platform_data = {
|
||||
.jd_mode = 2,
|
||||
.inv_jd1_1 = true
|
||||
};
|
||||
|
||||
static const struct dmi_system_id dmi_platform_data[] = {
|
||||
{
|
||||
.ident = "Chrome Buddy",
|
||||
@ -3728,6 +3736,15 @@ static const struct dmi_system_id dmi_platform_data[] = {
|
||||
},
|
||||
.driver_data = (void *)&intel_braswell_platform_data,
|
||||
},
|
||||
{
|
||||
.ident = "LattePanda board",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VERSION, "Default string"),
|
||||
},
|
||||
.driver_data = (void *)&lattepanda_board_platform_data,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -1645,7 +1645,10 @@ static bool rt5651_jack_inserted(struct snd_soc_component *component)
|
||||
break;
|
||||
}
|
||||
|
||||
return val == 0;
|
||||
if (rt5651->jd_active_high)
|
||||
return val != 0;
|
||||
else
|
||||
return val == 0;
|
||||
}
|
||||
|
||||
/* Jack detect and button-press timings */
|
||||
@ -1868,20 +1871,47 @@ static void rt5651_enable_jack_detect(struct snd_soc_component *component,
|
||||
case RT5651_JD1_1:
|
||||
snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
|
||||
RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_1);
|
||||
snd_soc_component_update_bits(component, RT5651_IRQ_CTRL1,
|
||||
RT5651_JD1_1_IRQ_EN, RT5651_JD1_1_IRQ_EN);
|
||||
/* active-low is normal, set inv flag for active-high */
|
||||
if (rt5651->jd_active_high)
|
||||
snd_soc_component_update_bits(component,
|
||||
RT5651_IRQ_CTRL1,
|
||||
RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV,
|
||||
RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV);
|
||||
else
|
||||
snd_soc_component_update_bits(component,
|
||||
RT5651_IRQ_CTRL1,
|
||||
RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV,
|
||||
RT5651_JD1_1_IRQ_EN);
|
||||
break;
|
||||
case RT5651_JD1_2:
|
||||
snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
|
||||
RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_2);
|
||||
snd_soc_component_update_bits(component, RT5651_IRQ_CTRL1,
|
||||
RT5651_JD1_2_IRQ_EN, RT5651_JD1_2_IRQ_EN);
|
||||
/* active-low is normal, set inv flag for active-high */
|
||||
if (rt5651->jd_active_high)
|
||||
snd_soc_component_update_bits(component,
|
||||
RT5651_IRQ_CTRL1,
|
||||
RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV,
|
||||
RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV);
|
||||
else
|
||||
snd_soc_component_update_bits(component,
|
||||
RT5651_IRQ_CTRL1,
|
||||
RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV,
|
||||
RT5651_JD1_2_IRQ_EN);
|
||||
break;
|
||||
case RT5651_JD2:
|
||||
snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
|
||||
RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD2);
|
||||
snd_soc_component_update_bits(component, RT5651_IRQ_CTRL1,
|
||||
RT5651_JD2_IRQ_EN, RT5651_JD2_IRQ_EN);
|
||||
/* active-low is normal, set inv flag for active-high */
|
||||
if (rt5651->jd_active_high)
|
||||
snd_soc_component_update_bits(component,
|
||||
RT5651_IRQ_CTRL1,
|
||||
RT5651_JD2_IRQ_EN | RT5651_JD2_INV,
|
||||
RT5651_JD2_IRQ_EN | RT5651_JD2_INV);
|
||||
else
|
||||
snd_soc_component_update_bits(component,
|
||||
RT5651_IRQ_CTRL1,
|
||||
RT5651_JD2_IRQ_EN | RT5651_JD2_INV,
|
||||
RT5651_JD2_IRQ_EN);
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n");
|
||||
@ -1986,6 +2016,9 @@ static void rt5651_apply_properties(struct snd_soc_component *component)
|
||||
"realtek,jack-detect-source", &val) == 0)
|
||||
rt5651->jd_src = val;
|
||||
|
||||
if (device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted"))
|
||||
rt5651->jd_active_high = true;
|
||||
|
||||
/*
|
||||
* Testing on various boards has shown that good defaults for the OVCD
|
||||
* threshold and scale-factor are 2000µA and 0.75. For an effective
|
||||
|
@ -2083,6 +2083,7 @@ struct rt5651_priv {
|
||||
int release_count;
|
||||
int poll_count;
|
||||
unsigned int jd_src;
|
||||
bool jd_active_high;
|
||||
unsigned int ovcd_th;
|
||||
unsigned int ovcd_sf;
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "rt5677-spi.h"
|
||||
|
||||
@ -226,9 +227,16 @@ static int rt5677_spi_probe(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id rt5677_spi_acpi_id[] = {
|
||||
{ "RT5677AA", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, rt5677_spi_acpi_id);
|
||||
|
||||
static struct spi_driver rt5677_spi_driver = {
|
||||
.driver = {
|
||||
.name = "rt5677",
|
||||
.acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id),
|
||||
},
|
||||
.probe = rt5677_spi_probe,
|
||||
};
|
||||
|
@ -89,7 +89,8 @@ static int simple_amp_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
priv->gpiod_enable = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
|
||||
priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(priv->gpiod_enable)) {
|
||||
err = PTR_ERR(priv->gpiod_enable);
|
||||
if (err != -EPROBE_DEFER)
|
||||
|
@ -461,9 +461,6 @@ static int sirf_audio_codec_driver_probe(struct platform_device *pdev)
|
||||
struct sirf_audio_codec *sirf_audio_codec;
|
||||
void __iomem *base;
|
||||
struct resource *mem_res;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(sirf_audio_codec_of_match, pdev->dev.of_node);
|
||||
|
||||
sirf_audio_codec = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct sirf_audio_codec), GFP_KERNEL);
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
@ -89,6 +90,7 @@ static bool aic31xx_volatile(struct device *dev, unsigned int reg)
|
||||
case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */
|
||||
case AIC31XX_INTRDACFLAG2:
|
||||
case AIC31XX_INTRADCFLAG2:
|
||||
case AIC31XX_HSDETECT:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -163,6 +165,7 @@ struct aic31xx_priv {
|
||||
struct aic31xx_pdata pdata;
|
||||
struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
|
||||
struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
|
||||
struct snd_soc_jack *jack;
|
||||
unsigned int sysclk;
|
||||
u8 p_div;
|
||||
int rate_div_line;
|
||||
@ -1261,6 +1264,20 @@ static int aic31xx_set_bias_level(struct snd_soc_component *component,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aic31xx_set_jack(struct snd_soc_component *component,
|
||||
struct snd_soc_jack *jack, void *data)
|
||||
{
|
||||
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
||||
|
||||
aic31xx->jack = jack;
|
||||
|
||||
/* Enable/Disable jack detection */
|
||||
regmap_write(aic31xx->regmap, AIC31XX_HSDETECT,
|
||||
jack ? AIC31XX_HSD_ENABLE : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aic31xx_codec_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
|
||||
@ -1301,6 +1318,7 @@ static int aic31xx_codec_probe(struct snd_soc_component *component)
|
||||
|
||||
static const struct snd_soc_component_driver soc_codec_driver_aic31xx = {
|
||||
.probe = aic31xx_codec_probe,
|
||||
.set_jack = aic31xx_set_jack,
|
||||
.set_bias_level = aic31xx_set_bias_level,
|
||||
.controls = common31xx_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(common31xx_snd_controls),
|
||||
@ -1405,8 +1423,47 @@ static irqreturn_t aic31xx_irq(int irq, void *data)
|
||||
dev_err(dev, "Short circuit on Left output is detected\n");
|
||||
if (value & AIC31XX_HPRSCDETECT)
|
||||
dev_err(dev, "Short circuit on Right output is detected\n");
|
||||
if (value & (AIC31XX_HSPLUG | AIC31XX_BUTTONPRESS)) {
|
||||
unsigned int val;
|
||||
int status = 0;
|
||||
|
||||
ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG2,
|
||||
&val);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to read interrupt mask: %d\n",
|
||||
ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (val & AIC31XX_BUTTONPRESS)
|
||||
status |= SND_JACK_BTN_0;
|
||||
|
||||
ret = regmap_read(aic31xx->regmap, AIC31XX_HSDETECT, &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to read headset type: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
switch ((val & AIC31XX_HSD_TYPE_MASK) >>
|
||||
AIC31XX_HSD_TYPE_SHIFT) {
|
||||
case AIC31XX_HSD_HP:
|
||||
status |= SND_JACK_HEADPHONE;
|
||||
break;
|
||||
case AIC31XX_HSD_HS:
|
||||
status |= SND_JACK_HEADSET;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (aic31xx->jack)
|
||||
snd_soc_jack_report(aic31xx->jack, status,
|
||||
AIC31XX_JACK_MASK);
|
||||
}
|
||||
if (value & ~(AIC31XX_HPLSCDETECT |
|
||||
AIC31XX_HPRSCDETECT))
|
||||
AIC31XX_HPRSCDETECT |
|
||||
AIC31XX_HSPLUG |
|
||||
AIC31XX_BUTTONPRESS))
|
||||
dev_err(dev, "Unknown DAC interrupt flags: 0x%08x\n", value);
|
||||
|
||||
read_overflow:
|
||||
@ -1518,6 +1575,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
|
||||
AIC31XX_GPIO1_FUNC_SHIFT);
|
||||
|
||||
regmap_write(aic31xx->regmap, AIC31XX_INT1CTRL,
|
||||
AIC31XX_HSPLUGDET |
|
||||
AIC31XX_BUTTONPRESSDET |
|
||||
AIC31XX_SC |
|
||||
AIC31XX_ENGINE);
|
||||
|
||||
|
@ -20,6 +20,10 @@
|
||||
#define AIC31XX_MINIDSP_BIT BIT(2)
|
||||
#define DAC31XX_BIT BIT(3)
|
||||
|
||||
#define AIC31XX_JACK_MASK (SND_JACK_HEADPHONE | \
|
||||
SND_JACK_HEADSET | \
|
||||
SND_JACK_BTN_0)
|
||||
|
||||
enum aic31xx_type {
|
||||
AIC3100 = 0,
|
||||
AIC3110 = AIC31XX_STEREO_CLASS_D_BIT,
|
||||
@ -220,6 +224,14 @@ struct aic31xx_pdata {
|
||||
/* AIC31XX_DACMUTE */
|
||||
#define AIC31XX_DACMUTE_MASK GENMASK(3, 2)
|
||||
|
||||
/* AIC31XX_HSDETECT */
|
||||
#define AIC31XX_HSD_ENABLE BIT(7)
|
||||
#define AIC31XX_HSD_TYPE_MASK GENMASK(6, 5)
|
||||
#define AIC31XX_HSD_TYPE_SHIFT 5
|
||||
#define AIC31XX_HSD_NONE 0x00
|
||||
#define AIC31XX_HSD_HP 0x01
|
||||
#define AIC31XX_HSD_HS 0x03
|
||||
|
||||
/* AIC31XX_MICBIAS */
|
||||
#define AIC31XX_MICBIAS_MASK GENMASK(1, 0)
|
||||
#define AIC31XX_MICBIAS_SHIFT 0
|
||||
|
483
sound/soc/codecs/tlv320aic32x4-clk.c
Normal file
483
sound/soc/codecs/tlv320aic32x4-clk.c
Normal file
@ -0,0 +1,483 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Clock Tree for the Texas Instruments TLV320AIC32x4
|
||||
*
|
||||
* Copyright 2019 Annaliese McDermond
|
||||
*
|
||||
* Author: Annaliese McDermond <nh6z@nh6z.net>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "tlv320aic32x4.h"
|
||||
|
||||
#define to_clk_aic32x4(_hw) container_of(_hw, struct clk_aic32x4, hw)
|
||||
struct clk_aic32x4 {
|
||||
struct clk_hw hw;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
unsigned int reg;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct clk_aic32x4_pll_muldiv - Multiplier/divider settings
|
||||
* @p: Divider
|
||||
* @r: first multiplier
|
||||
* @j: integer part of second multiplier
|
||||
* @d: decimal part of second multiplier
|
||||
*/
|
||||
struct clk_aic32x4_pll_muldiv {
|
||||
u8 p;
|
||||
u16 r;
|
||||
u8 j;
|
||||
u16 d;
|
||||
};
|
||||
|
||||
struct aic32x4_clkdesc {
|
||||
const char *name;
|
||||
const char * const *parent_names;
|
||||
unsigned int num_parents;
|
||||
const struct clk_ops *ops;
|
||||
unsigned int reg;
|
||||
};
|
||||
|
||||
static int clk_aic32x4_pll_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
|
||||
|
||||
return regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
|
||||
AIC32X4_PLLEN, AIC32X4_PLLEN);
|
||||
}
|
||||
|
||||
static void clk_aic32x4_pll_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
|
||||
|
||||
regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
|
||||
AIC32X4_PLLEN, 0);
|
||||
}
|
||||
|
||||
static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
|
||||
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(val & AIC32X4_PLLEN);
|
||||
}
|
||||
|
||||
static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll,
|
||||
struct clk_aic32x4_pll_muldiv *settings)
|
||||
{
|
||||
/* Change to use regmap_bulk_read? */
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
settings->r = val & AIC32X4_PLL_R_MASK;
|
||||
settings->p = (val & AIC32X4_PLL_P_MASK) >> AIC32X4_PLL_P_SHIFT;
|
||||
|
||||
ret = regmap_read(pll->regmap, AIC32X4_PLLJ, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
settings->j = val;
|
||||
|
||||
ret = regmap_read(pll->regmap, AIC32X4_PLLDMSB, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
settings->d = val << 8;
|
||||
|
||||
ret = regmap_read(pll->regmap, AIC32X4_PLLDLSB, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
settings->d |= val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll,
|
||||
struct clk_aic32x4_pll_muldiv *settings)
|
||||
{
|
||||
int ret;
|
||||
/* Change to use regmap_bulk_write for some if not all? */
|
||||
|
||||
ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
|
||||
AIC32X4_PLL_R_MASK, settings->r);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
|
||||
AIC32X4_PLL_P_MASK,
|
||||
settings->p << AIC32X4_PLL_P_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(pll->regmap, AIC32X4_PLLJ, settings->j);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(pll->regmap, AIC32X4_PLLDMSB, (settings->d >> 8));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_write(pll->regmap, AIC32X4_PLLDLSB, (settings->d & 0xff));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clk_aic32x4_pll_calc_rate(
|
||||
struct clk_aic32x4_pll_muldiv *settings,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u64 rate;
|
||||
/*
|
||||
* We scale j by 10000 to account for the decimal part of P and divide
|
||||
* it back out later.
|
||||
*/
|
||||
rate = (u64) parent_rate * settings->r *
|
||||
((settings->j * 10000) + settings->d);
|
||||
|
||||
return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000);
|
||||
}
|
||||
|
||||
static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings,
|
||||
unsigned long rate, unsigned long parent_rate)
|
||||
{
|
||||
u64 multiplier;
|
||||
|
||||
settings->p = parent_rate / AIC32X4_MAX_PLL_CLKIN + 1;
|
||||
if (settings->p > 8)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* We scale this figure by 10000 so that we can get the decimal part
|
||||
* of the multiplier. This is because we can't do floating point
|
||||
* math in the kernel.
|
||||
*/
|
||||
multiplier = (u64) rate * settings->p * 10000;
|
||||
do_div(multiplier, parent_rate);
|
||||
|
||||
/*
|
||||
* J can't be over 64, so R can scale this.
|
||||
* R can't be greater than 4.
|
||||
*/
|
||||
settings->r = ((u32) multiplier / 640000) + 1;
|
||||
if (settings->r > 4)
|
||||
return -1;
|
||||
do_div(multiplier, settings->r);
|
||||
|
||||
/*
|
||||
* J can't be < 1.
|
||||
*/
|
||||
if (multiplier < 10000)
|
||||
return -1;
|
||||
|
||||
/* Figure out the integer part, J, and the fractional part, D. */
|
||||
settings->j = (u32) multiplier / 10000;
|
||||
settings->d = (u32) multiplier % 10000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
|
||||
struct clk_aic32x4_pll_muldiv settings;
|
||||
int ret;
|
||||
|
||||
ret = clk_aic32x4_pll_get_muldiv(pll, &settings);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
return clk_aic32x4_pll_calc_rate(&settings, parent_rate);
|
||||
}
|
||||
|
||||
static long clk_aic32x4_pll_round_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct clk_aic32x4_pll_muldiv settings;
|
||||
int ret;
|
||||
|
||||
ret = clk_aic32x4_pll_calc_muldiv(&settings, rate, *parent_rate);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
return clk_aic32x4_pll_calc_rate(&settings, *parent_rate);
|
||||
}
|
||||
|
||||
static int clk_aic32x4_pll_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
|
||||
struct clk_aic32x4_pll_muldiv settings;
|
||||
int ret;
|
||||
|
||||
ret = clk_aic32x4_pll_calc_muldiv(&settings, rate, parent_rate);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return clk_aic32x4_pll_set_muldiv(pll, &settings);
|
||||
}
|
||||
|
||||
static int clk_aic32x4_pll_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
|
||||
|
||||
return regmap_update_bits(pll->regmap,
|
||||
AIC32X4_CLKMUX,
|
||||
AIC32X4_PLL_CLKIN_MASK,
|
||||
index << AIC32X4_PLL_CLKIN_SHIFT);
|
||||
}
|
||||
|
||||
static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
|
||||
unsigned int val;
|
||||
|
||||
regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
|
||||
|
||||
return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT;
|
||||
}
|
||||
|
||||
|
||||
static const struct clk_ops aic32x4_pll_ops = {
|
||||
.prepare = clk_aic32x4_pll_prepare,
|
||||
.unprepare = clk_aic32x4_pll_unprepare,
|
||||
.is_prepared = clk_aic32x4_pll_is_prepared,
|
||||
.recalc_rate = clk_aic32x4_pll_recalc_rate,
|
||||
.round_rate = clk_aic32x4_pll_round_rate,
|
||||
.set_rate = clk_aic32x4_pll_set_rate,
|
||||
.set_parent = clk_aic32x4_pll_set_parent,
|
||||
.get_parent = clk_aic32x4_pll_get_parent,
|
||||
};
|
||||
|
||||
static int clk_aic32x4_codec_clkin_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
|
||||
|
||||
return regmap_update_bits(mux->regmap,
|
||||
AIC32X4_CLKMUX,
|
||||
AIC32X4_CODEC_CLKIN_MASK, index << AIC32X4_CODEC_CLKIN_SHIFT);
|
||||
}
|
||||
|
||||
static u8 clk_aic32x4_codec_clkin_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
|
||||
unsigned int val;
|
||||
|
||||
regmap_read(mux->regmap, AIC32X4_CLKMUX, &val);
|
||||
|
||||
return (val & AIC32X4_CODEC_CLKIN_MASK) >> AIC32X4_CODEC_CLKIN_SHIFT;
|
||||
}
|
||||
|
||||
static const struct clk_ops aic32x4_codec_clkin_ops = {
|
||||
.set_parent = clk_aic32x4_codec_clkin_set_parent,
|
||||
.get_parent = clk_aic32x4_codec_clkin_get_parent,
|
||||
};
|
||||
|
||||
static int clk_aic32x4_div_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_aic32x4 *div = to_clk_aic32x4(hw);
|
||||
|
||||
return regmap_update_bits(div->regmap, div->reg,
|
||||
AIC32X4_DIVEN, AIC32X4_DIVEN);
|
||||
}
|
||||
|
||||
static void clk_aic32x4_div_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_aic32x4 *div = to_clk_aic32x4(hw);
|
||||
|
||||
regmap_update_bits(div->regmap, div->reg,
|
||||
AIC32X4_DIVEN, 0);
|
||||
}
|
||||
|
||||
static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_aic32x4 *div = to_clk_aic32x4(hw);
|
||||
u8 divisor;
|
||||
|
||||
divisor = DIV_ROUND_UP(parent_rate, rate);
|
||||
if (divisor > 128)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_update_bits(div->regmap, div->reg,
|
||||
AIC32X4_DIV_MASK, divisor);
|
||||
}
|
||||
|
||||
static long clk_aic32x4_div_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long divisor;
|
||||
|
||||
divisor = DIV_ROUND_UP(*parent_rate, rate);
|
||||
if (divisor > 128)
|
||||
return -EINVAL;
|
||||
|
||||
return DIV_ROUND_UP(*parent_rate, divisor);
|
||||
}
|
||||
|
||||
static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_aic32x4 *div = to_clk_aic32x4(hw);
|
||||
|
||||
unsigned int val;
|
||||
|
||||
regmap_read(div->regmap, div->reg, &val);
|
||||
|
||||
return DIV_ROUND_UP(parent_rate, val & AIC32X4_DIV_MASK);
|
||||
}
|
||||
|
||||
static const struct clk_ops aic32x4_div_ops = {
|
||||
.prepare = clk_aic32x4_div_prepare,
|
||||
.unprepare = clk_aic32x4_div_unprepare,
|
||||
.set_rate = clk_aic32x4_div_set_rate,
|
||||
.round_rate = clk_aic32x4_div_round_rate,
|
||||
.recalc_rate = clk_aic32x4_div_recalc_rate,
|
||||
};
|
||||
|
||||
static int clk_aic32x4_bdiv_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
|
||||
|
||||
return regmap_update_bits(mux->regmap, AIC32X4_IFACE3,
|
||||
AIC32X4_BDIVCLK_MASK, index);
|
||||
}
|
||||
|
||||
static u8 clk_aic32x4_bdiv_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
|
||||
unsigned int val;
|
||||
|
||||
regmap_read(mux->regmap, AIC32X4_IFACE3, &val);
|
||||
|
||||
return val & AIC32X4_BDIVCLK_MASK;
|
||||
}
|
||||
|
||||
static const struct clk_ops aic32x4_bdiv_ops = {
|
||||
.prepare = clk_aic32x4_div_prepare,
|
||||
.unprepare = clk_aic32x4_div_unprepare,
|
||||
.set_parent = clk_aic32x4_bdiv_set_parent,
|
||||
.get_parent = clk_aic32x4_bdiv_get_parent,
|
||||
.set_rate = clk_aic32x4_div_set_rate,
|
||||
.round_rate = clk_aic32x4_div_round_rate,
|
||||
.recalc_rate = clk_aic32x4_div_recalc_rate,
|
||||
};
|
||||
|
||||
static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = {
|
||||
{
|
||||
.name = "pll",
|
||||
.parent_names =
|
||||
(const char* []) { "mclk", "bclk", "gpio", "din" },
|
||||
.num_parents = 4,
|
||||
.ops = &aic32x4_pll_ops,
|
||||
.reg = 0,
|
||||
},
|
||||
{
|
||||
.name = "codec_clkin",
|
||||
.parent_names =
|
||||
(const char *[]) { "mclk", "bclk", "gpio", "pll" },
|
||||
.num_parents = 4,
|
||||
.ops = &aic32x4_codec_clkin_ops,
|
||||
.reg = 0,
|
||||
},
|
||||
{
|
||||
.name = "ndac",
|
||||
.parent_names = (const char * []) { "codec_clkin" },
|
||||
.num_parents = 1,
|
||||
.ops = &aic32x4_div_ops,
|
||||
.reg = AIC32X4_NDAC,
|
||||
},
|
||||
{
|
||||
.name = "mdac",
|
||||
.parent_names = (const char * []) { "ndac" },
|
||||
.num_parents = 1,
|
||||
.ops = &aic32x4_div_ops,
|
||||
.reg = AIC32X4_MDAC,
|
||||
},
|
||||
{
|
||||
.name = "nadc",
|
||||
.parent_names = (const char * []) { "codec_clkin" },
|
||||
.num_parents = 1,
|
||||
.ops = &aic32x4_div_ops,
|
||||
.reg = AIC32X4_NADC,
|
||||
},
|
||||
{
|
||||
.name = "madc",
|
||||
.parent_names = (const char * []) { "nadc" },
|
||||
.num_parents = 1,
|
||||
.ops = &aic32x4_div_ops,
|
||||
.reg = AIC32X4_MADC,
|
||||
},
|
||||
{
|
||||
.name = "bdiv",
|
||||
.parent_names =
|
||||
(const char *[]) { "ndac", "mdac", "nadc", "madc" },
|
||||
.num_parents = 4,
|
||||
.ops = &aic32x4_bdiv_ops,
|
||||
.reg = AIC32X4_BCLKN,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk *aic32x4_register_clk(struct device *dev,
|
||||
struct aic32x4_clkdesc *desc)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct clk_aic32x4 *priv;
|
||||
const char *devname = dev_name(dev);
|
||||
|
||||
init.ops = desc->ops;
|
||||
init.name = desc->name;
|
||||
init.parent_names = desc->parent_names;
|
||||
init.num_parents = desc->num_parents;
|
||||
init.flags = 0;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return (struct clk *) -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
priv->hw.init = &init;
|
||||
priv->regmap = dev_get_regmap(dev, NULL);
|
||||
priv->reg = desc->reg;
|
||||
|
||||
clk_hw_register_clkdev(&priv->hw, desc->name, devname);
|
||||
return devm_clk_register(dev, &priv->hw);
|
||||
}
|
||||
|
||||
int aic32x4_register_clocks(struct device *dev, const char *mclk_name)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* These lines are here to preserve the current functionality of
|
||||
* the driver with regard to the DT. These should eventually be set
|
||||
* by DT nodes so that the connections can be set up in configuration
|
||||
* rather than code.
|
||||
*/
|
||||
aic32x4_clkdesc_array[0].parent_names =
|
||||
(const char* []) { mclk_name, "bclk", "gpio", "din" };
|
||||
aic32x4_clkdesc_array[1].parent_names =
|
||||
(const char *[]) { mclk_name, "bclk", "gpio", "pll" };
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(aic32x4_clkdesc_array); ++i)
|
||||
aic32x4_register_clk(dev, &aic32x4_clkdesc_array[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aic32x4_register_clocks);
|
@ -1,21 +1,11 @@
|
||||
/*
|
||||
* linux/sound/soc/codecs/tlv320aic32x4-i2c.c
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright 2011 NW Digital Radio
|
||||
* Copyright 2011-2019 NW Digital Radio
|
||||
*
|
||||
* Author: Annaliese McDermond <nh6z@nh6z.net>
|
||||
*
|
||||
* Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
|
@ -1,21 +1,11 @@
|
||||
/*
|
||||
* linux/sound/soc/codecs/tlv320aic32x4-spi.c
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright 2011 NW Digital Radio
|
||||
* Copyright 2011-2019 NW Digital Radio
|
||||
*
|
||||
* Author: Annaliese McDermond <nh6z@nh6z.net>
|
||||
*
|
||||
* Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
|
@ -14,7 +14,7 @@
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
@ -33,6 +33,7 @@
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of_clk.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <sound/tlv320aic32x4.h>
|
||||
@ -46,29 +47,13 @@
|
||||
|
||||
#include "tlv320aic32x4.h"
|
||||
|
||||
struct aic32x4_rate_divs {
|
||||
u32 mclk;
|
||||
u32 rate;
|
||||
u8 p_val;
|
||||
u8 pll_j;
|
||||
u16 pll_d;
|
||||
u16 dosr;
|
||||
u8 ndac;
|
||||
u8 mdac;
|
||||
u8 aosr;
|
||||
u8 nadc;
|
||||
u8 madc;
|
||||
u8 blck_N;
|
||||
};
|
||||
|
||||
struct aic32x4_priv {
|
||||
struct regmap *regmap;
|
||||
u32 sysclk;
|
||||
u32 power_cfg;
|
||||
u32 micpga_routing;
|
||||
bool swapdacs;
|
||||
int rstn_gpio;
|
||||
struct clk *mclk;
|
||||
const char *mclk_name;
|
||||
|
||||
struct regulator *supply_ldo;
|
||||
struct regulator *supply_iov;
|
||||
@ -257,9 +242,24 @@ static DECLARE_TLV_DB_SCALE(tlv_driver_gain, -600, 100, 0);
|
||||
/* -12dB min, 0.5dB steps */
|
||||
static DECLARE_TLV_DB_SCALE(tlv_adc_vol, -1200, 50, 0);
|
||||
|
||||
static const char * const lo_cm_text[] = {
|
||||
"Full Chip", "1.65V",
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(lo_cm_enum, AIC32X4_CMMODE, 3, lo_cm_text);
|
||||
|
||||
static const char * const ptm_text[] = {
|
||||
"P3", "P2", "P1",
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(l_ptm_enum, AIC32X4_LPLAYBACK, 2, ptm_text);
|
||||
static SOC_ENUM_SINGLE_DECL(r_ptm_enum, AIC32X4_RPLAYBACK, 2, ptm_text);
|
||||
|
||||
static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
|
||||
SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
|
||||
AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm),
|
||||
SOC_ENUM("DAC Left Playback PowerTune Switch", l_ptm_enum),
|
||||
SOC_ENUM("DAC Right Playback PowerTune Switch", r_ptm_enum),
|
||||
SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN,
|
||||
AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0,
|
||||
tlv_driver_gain),
|
||||
@ -270,6 +270,7 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
|
||||
AIC32X4_HPRGAIN, 6, 0x01, 1),
|
||||
SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN,
|
||||
AIC32X4_LORGAIN, 6, 0x01, 1),
|
||||
SOC_ENUM("LO Playback Common Mode Switch", lo_cm_enum),
|
||||
SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL,
|
||||
AIC32X4_RMICPGAVOL, 7, 0x01, 1),
|
||||
|
||||
@ -305,38 +306,6 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
|
||||
0, 0x0F, 0),
|
||||
};
|
||||
|
||||
static const struct aic32x4_rate_divs aic32x4_divs[] = {
|
||||
/* 8k rate */
|
||||
{12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24},
|
||||
{24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24},
|
||||
{25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24},
|
||||
/* 11.025k rate */
|
||||
{12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16},
|
||||
{24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16},
|
||||
/* 16k rate */
|
||||
{12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12},
|
||||
{24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12},
|
||||
{25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12},
|
||||
/* 22.05k rate */
|
||||
{12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8},
|
||||
{24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8},
|
||||
{25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8},
|
||||
/* 32k rate */
|
||||
{12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6},
|
||||
{24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6},
|
||||
/* 44.1k rate */
|
||||
{12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4},
|
||||
{24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4},
|
||||
{25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4},
|
||||
/* 48k rate */
|
||||
{12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4},
|
||||
{24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4},
|
||||
{25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4},
|
||||
|
||||
/* 96k rate */
|
||||
{25000000, 96000, 2, 7, 8643, 64, 4, 4, 64, 4, 4, 1},
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0),
|
||||
SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0),
|
||||
@ -391,7 +360,7 @@ static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = {
|
||||
SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum),
|
||||
};
|
||||
|
||||
/* Right mixer pins */
|
||||
/* Right mixer pins */
|
||||
static SOC_ENUM_SINGLE_DECL(in1r_rpga_p_enum, AIC32X4_RMICPGAPIN, 6, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in2r_rpga_p_enum, AIC32X4_RMICPGAPIN, 4, resistor_text);
|
||||
static SOC_ENUM_SINGLE_DECL(in3r_rpga_p_enum, AIC32X4_RMICPGAPIN, 2, resistor_text);
|
||||
@ -595,7 +564,7 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
|
||||
static const struct regmap_range_cfg aic32x4_regmap_pages[] = {
|
||||
{
|
||||
.selector_reg = 0,
|
||||
.selector_mask = 0xff,
|
||||
.selector_mask = 0xff,
|
||||
.window_start = 0,
|
||||
.window_len = 128,
|
||||
.range_min = 0,
|
||||
@ -610,35 +579,17 @@ const struct regmap_config aic32x4_regmap_config = {
|
||||
};
|
||||
EXPORT_SYMBOL(aic32x4_regmap_config);
|
||||
|
||||
static inline int aic32x4_get_divs(int mclk, int rate)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) {
|
||||
if ((aic32x4_divs[i].rate == rate)
|
||||
&& (aic32x4_divs[i].mclk == mclk)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct snd_soc_component *component = codec_dai->component;
|
||||
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
|
||||
struct clk *mclk;
|
||||
struct clk *pll;
|
||||
|
||||
switch (freq) {
|
||||
case 12000000:
|
||||
case 24000000:
|
||||
case 25000000:
|
||||
aic32x4->sysclk = freq;
|
||||
return 0;
|
||||
}
|
||||
printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n");
|
||||
return -EINVAL;
|
||||
pll = devm_clk_get(component->dev, "pll");
|
||||
mclk = clk_get_parent(pll);
|
||||
|
||||
return clk_set_rate(mclk, freq);
|
||||
}
|
||||
|
||||
static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
||||
@ -688,103 +639,175 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, AIC32X4_IFACE1,
|
||||
AIC32X4_IFACE1_DATATYPE_MASK |
|
||||
AIC32X4_IFACE1_MASTER_MASK, iface_reg_1);
|
||||
AIC32X4_IFACE1_DATATYPE_MASK |
|
||||
AIC32X4_IFACE1_MASTER_MASK, iface_reg_1);
|
||||
snd_soc_component_update_bits(component, AIC32X4_IFACE2,
|
||||
AIC32X4_DATA_OFFSET_MASK, iface_reg_2);
|
||||
AIC32X4_DATA_OFFSET_MASK, iface_reg_2);
|
||||
snd_soc_component_update_bits(component, AIC32X4_IFACE3,
|
||||
AIC32X4_BCLKINV_MASK, iface_reg_3);
|
||||
AIC32X4_BCLKINV_MASK, iface_reg_3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr)
|
||||
{
|
||||
return snd_soc_component_write(component, AIC32X4_AOSR, aosr);
|
||||
}
|
||||
|
||||
static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr)
|
||||
{
|
||||
snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8);
|
||||
snd_soc_component_write(component, AIC32X4_DOSRLSB,
|
||||
(dosr & 0xff));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aic32x4_set_processing_blocks(struct snd_soc_component *component,
|
||||
u8 r_block, u8 p_block)
|
||||
{
|
||||
if (r_block > 18 || p_block > 25)
|
||||
return -EINVAL;
|
||||
|
||||
snd_soc_component_write(component, AIC32X4_ADCSPB, r_block);
|
||||
snd_soc_component_write(component, AIC32X4_DACSPB, p_block);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aic32x4_setup_clocks(struct snd_soc_component *component,
|
||||
unsigned int sample_rate)
|
||||
{
|
||||
u8 aosr;
|
||||
u16 dosr;
|
||||
u8 adc_resource_class, dac_resource_class;
|
||||
u8 madc, nadc, mdac, ndac, max_nadc, min_mdac, max_ndac;
|
||||
u8 dosr_increment;
|
||||
u16 max_dosr, min_dosr;
|
||||
unsigned long adc_clock_rate, dac_clock_rate;
|
||||
int ret;
|
||||
|
||||
struct clk_bulk_data clocks[] = {
|
||||
{ .id = "pll" },
|
||||
{ .id = "nadc" },
|
||||
{ .id = "madc" },
|
||||
{ .id = "ndac" },
|
||||
{ .id = "mdac" },
|
||||
{ .id = "bdiv" },
|
||||
};
|
||||
ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (sample_rate <= 48000) {
|
||||
aosr = 128;
|
||||
adc_resource_class = 6;
|
||||
dac_resource_class = 8;
|
||||
dosr_increment = 8;
|
||||
aic32x4_set_processing_blocks(component, 1, 1);
|
||||
} else if (sample_rate <= 96000) {
|
||||
aosr = 64;
|
||||
adc_resource_class = 6;
|
||||
dac_resource_class = 8;
|
||||
dosr_increment = 4;
|
||||
aic32x4_set_processing_blocks(component, 1, 9);
|
||||
} else if (sample_rate == 192000) {
|
||||
aosr = 32;
|
||||
adc_resource_class = 3;
|
||||
dac_resource_class = 4;
|
||||
dosr_increment = 2;
|
||||
aic32x4_set_processing_blocks(component, 13, 19);
|
||||
} else {
|
||||
dev_err(component->dev, "Sampling rate not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
madc = DIV_ROUND_UP((32 * adc_resource_class), aosr);
|
||||
max_dosr = (AIC32X4_MAX_DOSR_FREQ / sample_rate / dosr_increment) *
|
||||
dosr_increment;
|
||||
min_dosr = (AIC32X4_MIN_DOSR_FREQ / sample_rate / dosr_increment) *
|
||||
dosr_increment;
|
||||
max_nadc = AIC32X4_MAX_CODEC_CLKIN_FREQ / (madc * aosr * sample_rate);
|
||||
|
||||
for (nadc = max_nadc; nadc > 0; --nadc) {
|
||||
adc_clock_rate = nadc * madc * aosr * sample_rate;
|
||||
for (dosr = max_dosr; dosr >= min_dosr;
|
||||
dosr -= dosr_increment) {
|
||||
min_mdac = DIV_ROUND_UP((32 * dac_resource_class), dosr);
|
||||
max_ndac = AIC32X4_MAX_CODEC_CLKIN_FREQ /
|
||||
(min_mdac * dosr * sample_rate);
|
||||
for (mdac = min_mdac; mdac <= 128; ++mdac) {
|
||||
for (ndac = max_ndac; ndac > 0; --ndac) {
|
||||
dac_clock_rate = ndac * mdac * dosr *
|
||||
sample_rate;
|
||||
if (dac_clock_rate == adc_clock_rate) {
|
||||
if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0)
|
||||
continue;
|
||||
|
||||
clk_set_rate(clocks[0].clk,
|
||||
dac_clock_rate);
|
||||
|
||||
clk_set_rate(clocks[1].clk,
|
||||
sample_rate * aosr *
|
||||
madc);
|
||||
clk_set_rate(clocks[2].clk,
|
||||
sample_rate * aosr);
|
||||
aic32x4_set_aosr(component,
|
||||
aosr);
|
||||
|
||||
clk_set_rate(clocks[3].clk,
|
||||
sample_rate * dosr *
|
||||
mdac);
|
||||
clk_set_rate(clocks[4].clk,
|
||||
sample_rate * dosr);
|
||||
aic32x4_set_dosr(component,
|
||||
dosr);
|
||||
|
||||
clk_set_rate(clocks[5].clk,
|
||||
sample_rate * 32);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev_err(component->dev,
|
||||
"Could not set clocks to support sample rate.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int aic32x4_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
|
||||
u8 iface1_reg = 0;
|
||||
u8 dacsetup_reg = 0;
|
||||
int i;
|
||||
|
||||
i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params));
|
||||
if (i < 0) {
|
||||
printk(KERN_ERR "aic32x4: sampling rate not supported\n");
|
||||
return i;
|
||||
}
|
||||
|
||||
/* MCLK as PLL_CLKIN */
|
||||
snd_soc_component_update_bits(component, AIC32X4_CLKMUX, AIC32X4_PLL_CLKIN_MASK,
|
||||
AIC32X4_PLL_CLKIN_MCLK << AIC32X4_PLL_CLKIN_SHIFT);
|
||||
/* PLL as CODEC_CLKIN */
|
||||
snd_soc_component_update_bits(component, AIC32X4_CLKMUX, AIC32X4_CODEC_CLKIN_MASK,
|
||||
AIC32X4_CODEC_CLKIN_PLL << AIC32X4_CODEC_CLKIN_SHIFT);
|
||||
/* DAC_MOD_CLK as BDIV_CLKIN */
|
||||
snd_soc_component_update_bits(component, AIC32X4_IFACE3, AIC32X4_BDIVCLK_MASK,
|
||||
AIC32X4_DACMOD2BCLK << AIC32X4_BDIVCLK_SHIFT);
|
||||
|
||||
/* We will fix R value to 1 and will make P & J=K.D as variable */
|
||||
snd_soc_component_update_bits(component, AIC32X4_PLLPR, AIC32X4_PLL_R_MASK, 0x01);
|
||||
|
||||
/* PLL P value */
|
||||
snd_soc_component_update_bits(component, AIC32X4_PLLPR, AIC32X4_PLL_P_MASK,
|
||||
aic32x4_divs[i].p_val << AIC32X4_PLL_P_SHIFT);
|
||||
|
||||
/* PLL J value */
|
||||
snd_soc_component_write(component, AIC32X4_PLLJ, aic32x4_divs[i].pll_j);
|
||||
|
||||
/* PLL D value */
|
||||
snd_soc_component_write(component, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8));
|
||||
snd_soc_component_write(component, AIC32X4_PLLDLSB, (aic32x4_divs[i].pll_d & 0xff));
|
||||
|
||||
/* NDAC divider value */
|
||||
snd_soc_component_update_bits(component, AIC32X4_NDAC,
|
||||
AIC32X4_NDAC_MASK, aic32x4_divs[i].ndac);
|
||||
|
||||
/* MDAC divider value */
|
||||
snd_soc_component_update_bits(component, AIC32X4_MDAC,
|
||||
AIC32X4_MDAC_MASK, aic32x4_divs[i].mdac);
|
||||
|
||||
/* DOSR MSB & LSB values */
|
||||
snd_soc_component_write(component, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8);
|
||||
snd_soc_component_write(component, AIC32X4_DOSRLSB, (aic32x4_divs[i].dosr & 0xff));
|
||||
|
||||
/* NADC divider value */
|
||||
snd_soc_component_update_bits(component, AIC32X4_NADC,
|
||||
AIC32X4_NADC_MASK, aic32x4_divs[i].nadc);
|
||||
|
||||
/* MADC divider value */
|
||||
snd_soc_component_update_bits(component, AIC32X4_MADC,
|
||||
AIC32X4_MADC_MASK, aic32x4_divs[i].madc);
|
||||
|
||||
/* AOSR value */
|
||||
snd_soc_component_write(component, AIC32X4_AOSR, aic32x4_divs[i].aosr);
|
||||
|
||||
/* BCLK N divider */
|
||||
snd_soc_component_update_bits(component, AIC32X4_BCLKN,
|
||||
AIC32X4_BCLK_MASK, aic32x4_divs[i].blck_N);
|
||||
aic32x4_setup_clocks(component, params_rate(params));
|
||||
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
iface1_reg |= (AIC32X4_WORD_LEN_16BITS <<
|
||||
AIC32X4_IFACE1_DATALEN_SHIFT);
|
||||
AIC32X4_IFACE1_DATALEN_SHIFT);
|
||||
break;
|
||||
case 20:
|
||||
iface1_reg |= (AIC32X4_WORD_LEN_20BITS <<
|
||||
AIC32X4_IFACE1_DATALEN_SHIFT);
|
||||
AIC32X4_IFACE1_DATALEN_SHIFT);
|
||||
break;
|
||||
case 24:
|
||||
iface1_reg |= (AIC32X4_WORD_LEN_24BITS <<
|
||||
AIC32X4_IFACE1_DATALEN_SHIFT);
|
||||
AIC32X4_IFACE1_DATALEN_SHIFT);
|
||||
break;
|
||||
case 32:
|
||||
iface1_reg |= (AIC32X4_WORD_LEN_32BITS <<
|
||||
AIC32X4_IFACE1_DATALEN_SHIFT);
|
||||
AIC32X4_IFACE1_DATALEN_SHIFT);
|
||||
break;
|
||||
}
|
||||
snd_soc_component_update_bits(component, AIC32X4_IFACE1,
|
||||
AIC32X4_IFACE1_DATALEN_MASK, iface1_reg);
|
||||
AIC32X4_IFACE1_DATALEN_MASK, iface1_reg);
|
||||
|
||||
if (params_channels(params) == 1) {
|
||||
dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN;
|
||||
@ -795,7 +818,7 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream,
|
||||
dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN;
|
||||
}
|
||||
snd_soc_component_update_bits(component, AIC32X4_DACSETUP,
|
||||
AIC32X4_DAC_CHAN_MASK, dacsetup_reg);
|
||||
AIC32X4_DAC_CHAN_MASK, dacsetup_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -805,7 +828,7 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
|
||||
struct snd_soc_component *component = dai->component;
|
||||
|
||||
snd_soc_component_update_bits(component, AIC32X4_DACMUTE,
|
||||
AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0);
|
||||
AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -813,41 +836,25 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
|
||||
static int aic32x4_set_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
struct clk_bulk_data clocks[] = {
|
||||
{ .id = "madc" },
|
||||
{ .id = "mdac" },
|
||||
{ .id = "bdiv" },
|
||||
};
|
||||
|
||||
ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
/* Switch on master clock */
|
||||
ret = clk_prepare_enable(aic32x4->mclk);
|
||||
ret = clk_bulk_prepare_enable(ARRAY_SIZE(clocks), clocks);
|
||||
if (ret) {
|
||||
dev_err(component->dev, "Failed to enable master clock\n");
|
||||
dev_err(component->dev, "Failed to enable clocks\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Switch on PLL */
|
||||
snd_soc_component_update_bits(component, AIC32X4_PLLPR,
|
||||
AIC32X4_PLLEN, AIC32X4_PLLEN);
|
||||
|
||||
/* Switch on NDAC Divider */
|
||||
snd_soc_component_update_bits(component, AIC32X4_NDAC,
|
||||
AIC32X4_NDACEN, AIC32X4_NDACEN);
|
||||
|
||||
/* Switch on MDAC Divider */
|
||||
snd_soc_component_update_bits(component, AIC32X4_MDAC,
|
||||
AIC32X4_MDACEN, AIC32X4_MDACEN);
|
||||
|
||||
/* Switch on NADC Divider */
|
||||
snd_soc_component_update_bits(component, AIC32X4_NADC,
|
||||
AIC32X4_NADCEN, AIC32X4_NADCEN);
|
||||
|
||||
/* Switch on MADC Divider */
|
||||
snd_soc_component_update_bits(component, AIC32X4_MADC,
|
||||
AIC32X4_MADCEN, AIC32X4_MADCEN);
|
||||
|
||||
/* Switch on BCLK_N Divider */
|
||||
snd_soc_component_update_bits(component, AIC32X4_BCLKN,
|
||||
AIC32X4_BCLKEN, AIC32X4_BCLKEN);
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
@ -856,32 +863,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component,
|
||||
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
|
||||
break;
|
||||
|
||||
/* Switch off BCLK_N Divider */
|
||||
snd_soc_component_update_bits(component, AIC32X4_BCLKN,
|
||||
AIC32X4_BCLKEN, 0);
|
||||
|
||||
/* Switch off MADC Divider */
|
||||
snd_soc_component_update_bits(component, AIC32X4_MADC,
|
||||
AIC32X4_MADCEN, 0);
|
||||
|
||||
/* Switch off NADC Divider */
|
||||
snd_soc_component_update_bits(component, AIC32X4_NADC,
|
||||
AIC32X4_NADCEN, 0);
|
||||
|
||||
/* Switch off MDAC Divider */
|
||||
snd_soc_component_update_bits(component, AIC32X4_MDAC,
|
||||
AIC32X4_MDACEN, 0);
|
||||
|
||||
/* Switch off NDAC Divider */
|
||||
snd_soc_component_update_bits(component, AIC32X4_NDAC,
|
||||
AIC32X4_NDACEN, 0);
|
||||
|
||||
/* Switch off PLL */
|
||||
snd_soc_component_update_bits(component, AIC32X4_PLLPR,
|
||||
AIC32X4_PLLEN, 0);
|
||||
|
||||
/* Switch off master clock */
|
||||
clk_disable_unprepare(aic32x4->mclk);
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(clocks), clocks);
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
break;
|
||||
@ -889,8 +871,8 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define AIC32X4_RATES SNDRV_PCM_RATE_8000_96000
|
||||
#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
|
||||
#define AIC32X4_RATES SNDRV_PCM_RATE_8000_192000
|
||||
#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
|
||||
| SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static const struct snd_soc_dai_ops aic32x4_ops = {
|
||||
@ -903,17 +885,17 @@ static const struct snd_soc_dai_ops aic32x4_ops = {
|
||||
static struct snd_soc_dai_driver aic32x4_dai = {
|
||||
.name = "tlv320aic32x4-hifi",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AIC32X4_RATES,
|
||||
.formats = AIC32X4_FORMATS,},
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AIC32X4_RATES,
|
||||
.formats = AIC32X4_FORMATS,},
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AIC32X4_RATES,
|
||||
.formats = AIC32X4_FORMATS,},
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = AIC32X4_RATES,
|
||||
.formats = AIC32X4_FORMATS,},
|
||||
.ops = &aic32x4_ops,
|
||||
.symmetric_rates = 1,
|
||||
};
|
||||
@ -926,7 +908,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
|
||||
/* MFP1 */
|
||||
if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) {
|
||||
snd_soc_component_write(component, AIC32X4_DINCTL,
|
||||
aic32x4->setup->gpio_func[0]);
|
||||
aic32x4->setup->gpio_func[0]);
|
||||
snd_soc_add_component_controls(component, aic32x4_mfp1,
|
||||
ARRAY_SIZE(aic32x4_mfp1));
|
||||
}
|
||||
@ -934,7 +916,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
|
||||
/* MFP2 */
|
||||
if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) {
|
||||
snd_soc_component_write(component, AIC32X4_DOUTCTL,
|
||||
aic32x4->setup->gpio_func[1]);
|
||||
aic32x4->setup->gpio_func[1]);
|
||||
snd_soc_add_component_controls(component, aic32x4_mfp2,
|
||||
ARRAY_SIZE(aic32x4_mfp2));
|
||||
}
|
||||
@ -942,7 +924,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
|
||||
/* MFP3 */
|
||||
if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) {
|
||||
snd_soc_component_write(component, AIC32X4_SCLKCTL,
|
||||
aic32x4->setup->gpio_func[2]);
|
||||
aic32x4->setup->gpio_func[2]);
|
||||
snd_soc_add_component_controls(component, aic32x4_mfp3,
|
||||
ARRAY_SIZE(aic32x4_mfp3));
|
||||
}
|
||||
@ -950,7 +932,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
|
||||
/* MFP4 */
|
||||
if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) {
|
||||
snd_soc_component_write(component, AIC32X4_MISOCTL,
|
||||
aic32x4->setup->gpio_func[3]);
|
||||
aic32x4->setup->gpio_func[3]);
|
||||
snd_soc_add_component_controls(component, aic32x4_mfp4,
|
||||
ARRAY_SIZE(aic32x4_mfp4));
|
||||
}
|
||||
@ -958,7 +940,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
|
||||
/* MFP5 */
|
||||
if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) {
|
||||
snd_soc_component_write(component, AIC32X4_GPIOCTL,
|
||||
aic32x4->setup->gpio_func[4]);
|
||||
aic32x4->setup->gpio_func[4]);
|
||||
snd_soc_add_component_controls(component, aic32x4_mfp5,
|
||||
ARRAY_SIZE(aic32x4_mfp5));
|
||||
}
|
||||
@ -968,6 +950,18 @@ static int aic32x4_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
|
||||
u32 tmp_reg;
|
||||
int ret;
|
||||
|
||||
struct clk_bulk_data clocks[] = {
|
||||
{ .id = "codec_clkin" },
|
||||
{ .id = "pll" },
|
||||
{ .id = "bdiv" },
|
||||
{ .id = "mdac" },
|
||||
};
|
||||
|
||||
ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (gpio_is_valid(aic32x4->rstn_gpio)) {
|
||||
ndelay(10);
|
||||
@ -980,10 +974,13 @@ static int aic32x4_component_probe(struct snd_soc_component *component)
|
||||
if (aic32x4->setup)
|
||||
aic32x4_setup_gpios(component);
|
||||
|
||||
clk_set_parent(clocks[0].clk, clocks[1].clk);
|
||||
clk_set_parent(clocks[2].clk, clocks[3].clk);
|
||||
|
||||
/* Power platform configuration */
|
||||
if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
|
||||
snd_soc_component_write(component, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN |
|
||||
AIC32X4_MICBIAS_2075V);
|
||||
snd_soc_component_write(component, AIC32X4_MICBIAS,
|
||||
AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V);
|
||||
}
|
||||
if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE)
|
||||
snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE);
|
||||
@ -1046,12 +1043,18 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct aic32x4_setup_data *aic32x4_setup;
|
||||
int ret;
|
||||
|
||||
aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup),
|
||||
GFP_KERNEL);
|
||||
if (!aic32x4_setup)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_property_match_string(np, "clock-names", "mclk");
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
aic32x4->mclk_name = of_clk_get_parent_name(np, ret);
|
||||
|
||||
aic32x4->swapdacs = false;
|
||||
aic32x4->micpga_routing = 0;
|
||||
aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
|
||||
@ -1173,7 +1176,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv),
|
||||
GFP_KERNEL);
|
||||
GFP_KERNEL);
|
||||
if (aic32x4 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1185,6 +1188,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
|
||||
aic32x4->swapdacs = pdata->swapdacs;
|
||||
aic32x4->micpga_routing = pdata->micpga_routing;
|
||||
aic32x4->rstn_gpio = pdata->rstn_gpio;
|
||||
aic32x4->mclk_name = "mclk";
|
||||
} else if (np) {
|
||||
ret = aic32x4_parse_dt(aic32x4, np);
|
||||
if (ret) {
|
||||
@ -1196,13 +1200,12 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
|
||||
aic32x4->swapdacs = false;
|
||||
aic32x4->micpga_routing = 0;
|
||||
aic32x4->rstn_gpio = -1;
|
||||
aic32x4->mclk_name = "mclk";
|
||||
}
|
||||
|
||||
aic32x4->mclk = devm_clk_get(dev, "mclk");
|
||||
if (IS_ERR(aic32x4->mclk)) {
|
||||
dev_err(dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n");
|
||||
return PTR_ERR(aic32x4->mclk);
|
||||
}
|
||||
ret = aic32x4_register_clocks(dev, aic32x4->mclk_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (gpio_is_valid(aic32x4->rstn_gpio)) {
|
||||
ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio,
|
||||
|
@ -16,6 +16,7 @@ struct regmap_config;
|
||||
extern const struct regmap_config aic32x4_regmap_config;
|
||||
int aic32x4_probe(struct device *dev, struct regmap *regmap);
|
||||
int aic32x4_remove(struct device *dev);
|
||||
int aic32x4_register_clocks(struct device *dev, const char *mclk_name);
|
||||
|
||||
/* tlv320aic32x4 register space (in decimal to match datasheet) */
|
||||
|
||||
@ -77,6 +78,8 @@ int aic32x4_remove(struct device *dev);
|
||||
|
||||
#define AIC32X4_PWRCFG AIC32X4_REG(1, 1)
|
||||
#define AIC32X4_LDOCTL AIC32X4_REG(1, 2)
|
||||
#define AIC32X4_LPLAYBACK AIC32X4_REG(1, 3)
|
||||
#define AIC32X4_RPLAYBACK AIC32X4_REG(1, 4)
|
||||
#define AIC32X4_OUTPWRCTL AIC32X4_REG(1, 9)
|
||||
#define AIC32X4_CMMODE AIC32X4_REG(1, 10)
|
||||
#define AIC32X4_HPLROUTE AIC32X4_REG(1, 12)
|
||||
@ -205,4 +208,14 @@ int aic32x4_remove(struct device *dev);
|
||||
#define AIC32X4_RMICPGANIN_IN1L_10K 0x10
|
||||
#define AIC32X4_RMICPGANIN_CM1R_10K 0x40
|
||||
|
||||
/* Common mask and enable for all of the dividers */
|
||||
#define AIC32X4_DIVEN BIT(7)
|
||||
#define AIC32X4_DIV_MASK GENMASK(6, 0)
|
||||
|
||||
/* Clock Limits */
|
||||
#define AIC32X4_MAX_DOSR_FREQ 6200000
|
||||
#define AIC32X4_MIN_DOSR_FREQ 2800000
|
||||
#define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000
|
||||
#define AIC32X4_MAX_PLL_CLKIN 20000000
|
||||
|
||||
#endif /* _TLV320AIC32X4_H */
|
||||
|
@ -5188,6 +5188,7 @@ static int wcd9335_slim_status(struct slim_device *sdev,
|
||||
|
||||
wcd->slim = sdev;
|
||||
wcd->slim_ifc_dev = of_slim_get_device(sdev->ctrl, ifc_dev_np);
|
||||
of_node_put(ifc_dev_np);
|
||||
if (!wcd->slim_ifc_dev) {
|
||||
dev_err(dev, "Unable to get SLIM Interface device\n");
|
||||
return -EINVAL;
|
||||
|
@ -646,6 +646,8 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
wm_adsp2_set_dspclk(w, v);
|
||||
break;
|
||||
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
@ -659,7 +661,7 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
|
||||
break;
|
||||
}
|
||||
|
||||
return wm_adsp2_early_event(w, kcontrol, event, v);
|
||||
return wm_adsp_early_event(w, kcontrol, event);
|
||||
}
|
||||
|
||||
static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
|
||||
|
@ -211,7 +211,9 @@ static int wm5110_adsp_power_ev(struct snd_soc_dapm_widget *w,
|
||||
|
||||
v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
|
||||
|
||||
return wm_adsp2_early_event(w, kcontrol, event, v);
|
||||
wm_adsp2_set_dspclk(w, v);
|
||||
|
||||
return wm_adsp_early_event(w, kcontrol, event);
|
||||
}
|
||||
|
||||
static const struct reg_sequence wm5110_no_dre_left_enable[] = {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -54,6 +54,7 @@ struct wm_adsp_alg_region {
|
||||
|
||||
struct wm_adsp_compr;
|
||||
struct wm_adsp_compr_buf;
|
||||
struct wm_adsp_ops;
|
||||
|
||||
struct wm_adsp {
|
||||
const char *part;
|
||||
@ -66,7 +67,10 @@ struct wm_adsp {
|
||||
struct regmap *regmap;
|
||||
struct snd_soc_component *component;
|
||||
|
||||
struct wm_adsp_ops *ops;
|
||||
|
||||
unsigned int base;
|
||||
unsigned int base_sysinfo;
|
||||
unsigned int sysclk_reg;
|
||||
unsigned int sysclk_mask;
|
||||
unsigned int sysclk_shift;
|
||||
@ -75,6 +79,7 @@ struct wm_adsp {
|
||||
|
||||
unsigned int fw_id;
|
||||
unsigned int fw_id_version;
|
||||
unsigned int fw_vendor_id;
|
||||
|
||||
const struct wm_adsp_region *mem;
|
||||
int num_mems;
|
||||
@ -106,6 +111,32 @@ struct wm_adsp {
|
||||
|
||||
};
|
||||
|
||||
struct wm_adsp_ops {
|
||||
unsigned int sys_config_size;
|
||||
|
||||
bool (*validate_version)(struct wm_adsp *dsp, unsigned int version);
|
||||
unsigned int (*parse_sizes)(struct wm_adsp *dsp,
|
||||
const char * const file,
|
||||
unsigned int pos,
|
||||
const struct firmware *firmware);
|
||||
int (*setup_algs)(struct wm_adsp *dsp);
|
||||
unsigned int (*region_to_reg)(struct wm_adsp_region const *mem,
|
||||
unsigned int offset);
|
||||
|
||||
void (*show_fw_status)(struct wm_adsp *dsp);
|
||||
void (*stop_watchdog)(struct wm_adsp *dsp);
|
||||
|
||||
int (*enable_memory)(struct wm_adsp *dsp);
|
||||
void (*disable_memory)(struct wm_adsp *dsp);
|
||||
int (*lock_memory)(struct wm_adsp *dsp, unsigned int lock_regions);
|
||||
|
||||
int (*enable_core)(struct wm_adsp *dsp);
|
||||
void (*disable_core)(struct wm_adsp *dsp);
|
||||
|
||||
int (*start_core)(struct wm_adsp *dsp);
|
||||
void (*stop_core)(struct wm_adsp *dsp);
|
||||
};
|
||||
|
||||
#define WM_ADSP1(wname, num) \
|
||||
SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
|
||||
wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
|
||||
@ -121,7 +152,7 @@ struct wm_adsp {
|
||||
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD, \
|
||||
.subseq = 100, /* Ensure we run after SYSCLK supply widget */ }, \
|
||||
{ .id = snd_soc_dapm_out_drv, .name = wname, \
|
||||
.reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \
|
||||
.reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp_event, \
|
||||
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
|
||||
|
||||
#define WM_ADSP_FW_CONTROL(dspname, num) \
|
||||
@ -135,17 +166,22 @@ int wm_adsp2_init(struct wm_adsp *dsp);
|
||||
void wm_adsp2_remove(struct wm_adsp *dsp);
|
||||
int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component);
|
||||
int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component);
|
||||
int wm_halo_init(struct wm_adsp *dsp);
|
||||
|
||||
int wm_adsp1_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event,
|
||||
unsigned int freq);
|
||||
|
||||
int wm_adsp2_lock(struct wm_adsp *adsp, unsigned int regions);
|
||||
int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
|
||||
irqreturn_t wm_adsp2_bus_error(struct wm_adsp *adsp);
|
||||
irqreturn_t wm_halo_bus_error(struct wm_adsp *dsp);
|
||||
irqreturn_t wm_halo_wdt_expire(int irq, void *data);
|
||||
|
||||
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
int wm_adsp_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
|
||||
int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq);
|
||||
|
||||
int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
|
@ -73,6 +73,14 @@ struct wmfw_id_hdr {
|
||||
__be32 ver;
|
||||
} __packed;
|
||||
|
||||
struct wmfw_v3_id_hdr {
|
||||
__be32 core_id;
|
||||
__be32 block_rev;
|
||||
__be32 vendor_id;
|
||||
__be32 id;
|
||||
__be32 ver;
|
||||
} __packed;
|
||||
|
||||
struct wmfw_adsp1_id_hdr {
|
||||
struct wmfw_id_hdr fw;
|
||||
__be32 zm;
|
||||
@ -88,6 +96,15 @@ struct wmfw_adsp2_id_hdr {
|
||||
__be32 n_algs;
|
||||
} __packed;
|
||||
|
||||
struct wmfw_halo_id_hdr {
|
||||
struct wmfw_v3_id_hdr fw;
|
||||
__be32 xm_base;
|
||||
__be32 xm_size;
|
||||
__be32 ym_base;
|
||||
__be32 ym_size;
|
||||
__be32 n_algs;
|
||||
} __packed;
|
||||
|
||||
struct wmfw_alg_hdr {
|
||||
__be32 id;
|
||||
__be32 ver;
|
||||
@ -106,6 +123,14 @@ struct wmfw_adsp2_alg_hdr {
|
||||
__be32 ym;
|
||||
} __packed;
|
||||
|
||||
struct wmfw_halo_alg_hdr {
|
||||
struct wmfw_alg_hdr alg;
|
||||
__be32 xm_base;
|
||||
__be32 xm_size;
|
||||
__be32 ym_base;
|
||||
__be32 ym_size;
|
||||
} __packed;
|
||||
|
||||
struct wmfw_adsp_alg_data {
|
||||
__le32 id;
|
||||
u8 name[WMFW_MAX_ALG_NAME];
|
||||
@ -154,6 +179,7 @@ struct wmfw_coeff_item {
|
||||
|
||||
#define WMFW_ADSP1 1
|
||||
#define WMFW_ADSP2 2
|
||||
#define WMFW_HALO 4
|
||||
|
||||
#define WMFW_ABSOLUTE 0xf0
|
||||
#define WMFW_ALGORITHM_DATA 0xf2
|
||||
@ -169,4 +195,8 @@ struct wmfw_coeff_item {
|
||||
#define WMFW_ADSP2_XM 5
|
||||
#define WMFW_ADSP2_YM 6
|
||||
|
||||
#define WMFW_HALO_PM_PACKED 0x10
|
||||
#define WMFW_HALO_XM_PACKED 0x11
|
||||
#define WMFW_HALO_YM_PACKED 0x12
|
||||
|
||||
#endif
|
||||
|
@ -24,6 +24,13 @@ config SND_SOC_FSL_SAI
|
||||
This option is only useful for out-of-tree drivers since
|
||||
in-tree drivers select it automatically.
|
||||
|
||||
config SND_SOC_FSL_AUDMIX
|
||||
tristate "Audio Mixer (AUDMIX) module support"
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Say Y if you want to add Audio Mixer (AUDMIX)
|
||||
support for the NXP iMX CPUs.
|
||||
|
||||
config SND_SOC_FSL_SSI
|
||||
tristate "Synchronous Serial Interface module (SSI) support"
|
||||
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
|
||||
@ -182,16 +189,17 @@ config SND_MPC52xx_SOC_EFIKA
|
||||
|
||||
endif # SND_POWERPC_SOC
|
||||
|
||||
config SND_SOC_IMX_PCM_FIQ
|
||||
tristate
|
||||
default y if SND_SOC_IMX_SSI=y && (SND_SOC_FSL_SSI=m || SND_SOC_FSL_SPDIF=m) && (MXC_TZIC || MXC_AVIC)
|
||||
select FIQ
|
||||
|
||||
if SND_IMX_SOC
|
||||
|
||||
config SND_SOC_IMX_SSI
|
||||
tristate
|
||||
select SND_SOC_FSL_UTILS
|
||||
|
||||
config SND_SOC_IMX_PCM_FIQ
|
||||
tristate
|
||||
select FIQ
|
||||
|
||||
comment "SoC Audio support for Freescale i.MX boards:"
|
||||
|
||||
config SND_MXC_SOC_WM1133_EV1
|
||||
@ -296,6 +304,15 @@ config SND_SOC_FSL_ASOC_CARD
|
||||
CS4271, CS4272 and SGTL5000.
|
||||
Say Y if you want to add support for Freescale Generic ASoC Sound Card.
|
||||
|
||||
config SND_SOC_IMX_AUDMIX
|
||||
tristate "SoC Audio support for i.MX boards with AUDMIX"
|
||||
select SND_SOC_FSL_AUDMIX
|
||||
select SND_SOC_FSL_SAI
|
||||
help
|
||||
SoC Audio support for i.MX boards with Audio Mixer
|
||||
Say Y if you want to add support for SoC audio on an i.MX board with
|
||||
an Audio Mixer.
|
||||
|
||||
endif # SND_IMX_SOC
|
||||
|
||||
endmenu
|
||||
|
@ -12,6 +12,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o
|
||||
obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
|
||||
|
||||
# Freescale SSI/DMA/SAI/SPDIF Support
|
||||
snd-soc-fsl-audmix-objs := fsl_audmix.o
|
||||
snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
|
||||
snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
|
||||
snd-soc-fsl-sai-objs := fsl_sai.o
|
||||
@ -22,6 +23,8 @@ snd-soc-fsl-esai-objs := fsl_esai.o
|
||||
snd-soc-fsl-micfil-objs := fsl_micfil.o
|
||||
snd-soc-fsl-utils-objs := fsl_utils.o
|
||||
snd-soc-fsl-dma-objs := fsl_dma.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
|
||||
@ -59,6 +62,7 @@ snd-soc-imx-es8328-objs := imx-es8328.o
|
||||
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
|
||||
snd-soc-imx-spdif-objs := imx-spdif.o
|
||||
snd-soc-imx-mc13783-objs := imx-mc13783.o
|
||||
snd-soc-imx-audmix-objs := imx-audmix.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
|
||||
obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
|
||||
@ -68,3 +72,4 @@ obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
|
||||
obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o
|
||||
|
@ -1,19 +1,13 @@
|
||||
/*
|
||||
* eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode
|
||||
*
|
||||
* Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com>
|
||||
*
|
||||
* based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
|
||||
* which is Copyright 2009 Simtec Electronics
|
||||
* and on sound/soc/imx/phycore-ac97.c which is
|
||||
* Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode
|
||||
//
|
||||
// Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com>
|
||||
//
|
||||
// based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
|
||||
// which is Copyright 2009 Simtec Electronics
|
||||
// and on sound/soc/imx/phycore-ac97.c which is
|
||||
// Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
@ -118,13 +112,13 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"fsl,mux-int-port node missing or invalid.\n");
|
||||
return ret;
|
||||
goto err;
|
||||
}
|
||||
ret = of_property_read_u32(np, "fsl,mux-ext-port", &ext_port);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"fsl,mux-ext-port node missing or invalid.\n");
|
||||
return ret;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
578
sound/soc/fsl/fsl_audmix.c
Normal file
578
sound/soc/fsl/fsl_audmix.c
Normal file
@ -0,0 +1,578 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
|
||||
*
|
||||
* Copyright 2017 NXP
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "fsl_audmix.h"
|
||||
|
||||
#define SOC_ENUM_SINGLE_S(xreg, xshift, xtexts) \
|
||||
SOC_ENUM_SINGLE(xreg, xshift, ARRAY_SIZE(xtexts), xtexts)
|
||||
|
||||
static const char
|
||||
*tdm_sel[] = { "TDM1", "TDM2", },
|
||||
*mode_sel[] = { "Disabled", "TDM1", "TDM2", "Mixed", },
|
||||
*width_sel[] = { "16b", "18b", "20b", "24b", "32b", },
|
||||
*endis_sel[] = { "Disabled", "Enabled", },
|
||||
*updn_sel[] = { "Downward", "Upward", },
|
||||
*mask_sel[] = { "Unmask", "Mask", };
|
||||
|
||||
static const struct soc_enum fsl_audmix_enum[] = {
|
||||
/* FSL_AUDMIX_CTR enums */
|
||||
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MIXCLK_SHIFT, tdm_sel),
|
||||
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTSRC_SHIFT, mode_sel),
|
||||
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTWIDTH_SHIFT, width_sel),
|
||||
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKRTDF_SHIFT, mask_sel),
|
||||
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKCKDF_SHIFT, mask_sel),
|
||||
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCMODE_SHIFT, endis_sel),
|
||||
SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCSRC_SHIFT, tdm_sel),
|
||||
/* FSL_AUDMIX_ATCR0 enums */
|
||||
SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 0, endis_sel),
|
||||
SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 1, updn_sel),
|
||||
/* FSL_AUDMIX_ATCR1 enums */
|
||||
SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 0, endis_sel),
|
||||
SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 1, updn_sel),
|
||||
};
|
||||
|
||||
struct fsl_audmix_state {
|
||||
u8 tdms;
|
||||
u8 clk;
|
||||
char msg[64];
|
||||
};
|
||||
|
||||
static const struct fsl_audmix_state prms[4][4] = {{
|
||||
/* DIS->DIS, do nothing */
|
||||
{ .tdms = 0, .clk = 0, .msg = "" },
|
||||
/* DIS->TDM1*/
|
||||
{ .tdms = 1, .clk = 1, .msg = "DIS->TDM1: TDM1 not started!\n" },
|
||||
/* DIS->TDM2*/
|
||||
{ .tdms = 2, .clk = 2, .msg = "DIS->TDM2: TDM2 not started!\n" },
|
||||
/* DIS->MIX */
|
||||
{ .tdms = 3, .clk = 0, .msg = "DIS->MIX: Please start both TDMs!\n" }
|
||||
}, { /* TDM1->DIS */
|
||||
{ .tdms = 1, .clk = 0, .msg = "TDM1->DIS: TDM1 not started!\n" },
|
||||
/* TDM1->TDM1, do nothing */
|
||||
{ .tdms = 0, .clk = 0, .msg = "" },
|
||||
/* TDM1->TDM2 */
|
||||
{ .tdms = 3, .clk = 2, .msg = "TDM1->TDM2: Please start both TDMs!\n" },
|
||||
/* TDM1->MIX */
|
||||
{ .tdms = 3, .clk = 0, .msg = "TDM1->MIX: Please start both TDMs!\n" }
|
||||
}, { /* TDM2->DIS */
|
||||
{ .tdms = 2, .clk = 0, .msg = "TDM2->DIS: TDM2 not started!\n" },
|
||||
/* TDM2->TDM1 */
|
||||
{ .tdms = 3, .clk = 1, .msg = "TDM2->TDM1: Please start both TDMs!\n" },
|
||||
/* TDM2->TDM2, do nothing */
|
||||
{ .tdms = 0, .clk = 0, .msg = "" },
|
||||
/* TDM2->MIX */
|
||||
{ .tdms = 3, .clk = 0, .msg = "TDM2->MIX: Please start both TDMs!\n" }
|
||||
}, { /* MIX->DIS */
|
||||
{ .tdms = 3, .clk = 0, .msg = "MIX->DIS: Please start both TDMs!\n" },
|
||||
/* MIX->TDM1 */
|
||||
{ .tdms = 3, .clk = 1, .msg = "MIX->TDM1: Please start both TDMs!\n" },
|
||||
/* MIX->TDM2 */
|
||||
{ .tdms = 3, .clk = 2, .msg = "MIX->TDM2: Please start both TDMs!\n" },
|
||||
/* MIX->MIX, do nothing */
|
||||
{ .tdms = 0, .clk = 0, .msg = "" }
|
||||
}, };
|
||||
|
||||
static int fsl_audmix_state_trans(struct snd_soc_component *comp,
|
||||
unsigned int *mask, unsigned int *ctr,
|
||||
const struct fsl_audmix_state prm)
|
||||
{
|
||||
struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
|
||||
/* Enforce all required TDMs are started */
|
||||
if ((priv->tdms & prm.tdms) != prm.tdms) {
|
||||
dev_dbg(comp->dev, "%s", prm.msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (prm.clk) {
|
||||
case 1:
|
||||
case 2:
|
||||
/* Set mix clock */
|
||||
(*mask) |= FSL_AUDMIX_CTR_MIXCLK_MASK;
|
||||
(*ctr) |= FSL_AUDMIX_CTR_MIXCLK(prm.clk - 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
|
||||
struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
unsigned int *item = ucontrol->value.enumerated.item;
|
||||
unsigned int reg_val, val, mix_clk;
|
||||
int ret = 0;
|
||||
|
||||
/* Get current state */
|
||||
ret = snd_soc_component_read(comp, FSL_AUDMIX_CTR, ®_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
|
||||
>> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
|
||||
val = snd_soc_enum_item_to_val(e, item[0]);
|
||||
|
||||
dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
|
||||
|
||||
/**
|
||||
* Ensure the current selected mixer clock is available
|
||||
* for configuration propagation
|
||||
*/
|
||||
if (!(priv->tdms & BIT(mix_clk))) {
|
||||
dev_err(comp->dev,
|
||||
"Started TDM%d needed for config propagation!\n",
|
||||
mix_clk + 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!(priv->tdms & BIT(val))) {
|
||||
dev_err(comp->dev,
|
||||
"The selected clock source has no TDM%d enabled!\n",
|
||||
val + 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snd_soc_put_enum_double(kcontrol, ucontrol);
|
||||
}
|
||||
|
||||
static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
|
||||
struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
unsigned int *item = ucontrol->value.enumerated.item;
|
||||
u32 out_src, mix_clk;
|
||||
unsigned int reg_val, val, mask = 0, ctr = 0;
|
||||
int ret = 0;
|
||||
|
||||
/* Get current state */
|
||||
ret = snd_soc_component_read(comp, FSL_AUDMIX_CTR, ®_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* "From" state */
|
||||
out_src = ((reg_val & FSL_AUDMIX_CTR_OUTSRC_MASK)
|
||||
>> FSL_AUDMIX_CTR_OUTSRC_SHIFT);
|
||||
mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
|
||||
>> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
|
||||
|
||||
/* "To" state */
|
||||
val = snd_soc_enum_item_to_val(e, item[0]);
|
||||
|
||||
dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
|
||||
|
||||
/* Check if state is changing ... */
|
||||
if (out_src == val)
|
||||
return 0;
|
||||
/**
|
||||
* Ensure the current selected mixer clock is available
|
||||
* for configuration propagation
|
||||
*/
|
||||
if (!(priv->tdms & BIT(mix_clk))) {
|
||||
dev_err(comp->dev,
|
||||
"Started TDM%d needed for config propagation!\n",
|
||||
mix_clk + 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check state transition constraints */
|
||||
ret = fsl_audmix_state_trans(comp, &mask, &ctr, prms[out_src][val]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Complete transition to new state */
|
||||
mask |= FSL_AUDMIX_CTR_OUTSRC_MASK;
|
||||
ctr |= FSL_AUDMIX_CTR_OUTSRC(val);
|
||||
|
||||
return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new fsl_audmix_snd_controls[] = {
|
||||
/* FSL_AUDMIX_CTR controls */
|
||||
SOC_ENUM_EXT("Mixing Clock Source", fsl_audmix_enum[0],
|
||||
snd_soc_get_enum_double, fsl_audmix_put_mix_clk_src),
|
||||
SOC_ENUM_EXT("Output Source", fsl_audmix_enum[1],
|
||||
snd_soc_get_enum_double, fsl_audmix_put_out_src),
|
||||
SOC_ENUM("Output Width", fsl_audmix_enum[2]),
|
||||
SOC_ENUM("Frame Rate Diff Error", fsl_audmix_enum[3]),
|
||||
SOC_ENUM("Clock Freq Diff Error", fsl_audmix_enum[4]),
|
||||
SOC_ENUM("Sync Mode Config", fsl_audmix_enum[5]),
|
||||
SOC_ENUM("Sync Mode Clk Source", fsl_audmix_enum[6]),
|
||||
/* TDM1 Attenuation controls */
|
||||
SOC_ENUM("TDM1 Attenuation", fsl_audmix_enum[7]),
|
||||
SOC_ENUM("TDM1 Attenuation Direction", fsl_audmix_enum[8]),
|
||||
SOC_SINGLE("TDM1 Attenuation Step Divider", FSL_AUDMIX_ATCR0,
|
||||
2, 0x00fff, 0),
|
||||
SOC_SINGLE("TDM1 Attenuation Initial Value", FSL_AUDMIX_ATIVAL0,
|
||||
0, 0x3ffff, 0),
|
||||
SOC_SINGLE("TDM1 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP0,
|
||||
0, 0x3ffff, 0),
|
||||
SOC_SINGLE("TDM1 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN0,
|
||||
0, 0x3ffff, 0),
|
||||
SOC_SINGLE("TDM1 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT0,
|
||||
0, 0x3ffff, 0),
|
||||
/* TDM2 Attenuation controls */
|
||||
SOC_ENUM("TDM2 Attenuation", fsl_audmix_enum[9]),
|
||||
SOC_ENUM("TDM2 Attenuation Direction", fsl_audmix_enum[10]),
|
||||
SOC_SINGLE("TDM2 Attenuation Step Divider", FSL_AUDMIX_ATCR1,
|
||||
2, 0x00fff, 0),
|
||||
SOC_SINGLE("TDM2 Attenuation Initial Value", FSL_AUDMIX_ATIVAL1,
|
||||
0, 0x3ffff, 0),
|
||||
SOC_SINGLE("TDM2 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP1,
|
||||
0, 0x3ffff, 0),
|
||||
SOC_SINGLE("TDM2 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN1,
|
||||
0, 0x3ffff, 0),
|
||||
SOC_SINGLE("TDM2 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT1,
|
||||
0, 0x3ffff, 0),
|
||||
};
|
||||
|
||||
static int fsl_audmix_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_component *comp = dai->component;
|
||||
u32 mask = 0, ctr = 0;
|
||||
|
||||
/* AUDMIX is working in DSP_A format only */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* For playback the AUDMIX is slave, and for record is master */
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
/* Output data will be written on positive edge of the clock */
|
||||
ctr |= FSL_AUDMIX_CTR_OUTCKPOL(0);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
/* Output data will be written on negative edge of the clock */
|
||||
ctr |= FSL_AUDMIX_CTR_OUTCKPOL(1);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mask |= FSL_AUDMIX_CTR_OUTCKPOL_MASK;
|
||||
|
||||
return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
|
||||
}
|
||||
|
||||
static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
/* Capture stream shall not be handled */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
return 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
priv->tdms |= BIT(dai->driver->id);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
priv->tdms &= ~BIT(dai->driver->id);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops fsl_audmix_dai_ops = {
|
||||
.set_fmt = fsl_audmix_dai_set_fmt,
|
||||
.trigger = fsl_audmix_dai_trigger,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver fsl_audmix_dai[] = {
|
||||
{
|
||||
.id = 0,
|
||||
.name = "audmix-0",
|
||||
.playback = {
|
||||
.stream_name = "AUDMIX-Playback-0",
|
||||
.channels_min = 8,
|
||||
.channels_max = 8,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = FSL_AUDMIX_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "AUDMIX-Capture-0",
|
||||
.channels_min = 8,
|
||||
.channels_max = 8,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = FSL_AUDMIX_FORMATS,
|
||||
},
|
||||
.ops = &fsl_audmix_dai_ops,
|
||||
},
|
||||
{
|
||||
.id = 1,
|
||||
.name = "audmix-1",
|
||||
.playback = {
|
||||
.stream_name = "AUDMIX-Playback-1",
|
||||
.channels_min = 8,
|
||||
.channels_max = 8,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = FSL_AUDMIX_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "AUDMIX-Capture-1",
|
||||
.channels_min = 8,
|
||||
.channels_max = 8,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = FSL_AUDMIX_FORMATS,
|
||||
},
|
||||
.ops = &fsl_audmix_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver fsl_audmix_component = {
|
||||
.name = "fsl-audmix-dai",
|
||||
.controls = fsl_audmix_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(fsl_audmix_snd_controls),
|
||||
};
|
||||
|
||||
static bool fsl_audmix_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case FSL_AUDMIX_CTR:
|
||||
case FSL_AUDMIX_STR:
|
||||
case FSL_AUDMIX_ATCR0:
|
||||
case FSL_AUDMIX_ATIVAL0:
|
||||
case FSL_AUDMIX_ATSTPUP0:
|
||||
case FSL_AUDMIX_ATSTPDN0:
|
||||
case FSL_AUDMIX_ATSTPTGT0:
|
||||
case FSL_AUDMIX_ATTNVAL0:
|
||||
case FSL_AUDMIX_ATSTP0:
|
||||
case FSL_AUDMIX_ATCR1:
|
||||
case FSL_AUDMIX_ATIVAL1:
|
||||
case FSL_AUDMIX_ATSTPUP1:
|
||||
case FSL_AUDMIX_ATSTPDN1:
|
||||
case FSL_AUDMIX_ATSTPTGT1:
|
||||
case FSL_AUDMIX_ATTNVAL1:
|
||||
case FSL_AUDMIX_ATSTP1:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool fsl_audmix_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case FSL_AUDMIX_CTR:
|
||||
case FSL_AUDMIX_ATCR0:
|
||||
case FSL_AUDMIX_ATIVAL0:
|
||||
case FSL_AUDMIX_ATSTPUP0:
|
||||
case FSL_AUDMIX_ATSTPDN0:
|
||||
case FSL_AUDMIX_ATSTPTGT0:
|
||||
case FSL_AUDMIX_ATCR1:
|
||||
case FSL_AUDMIX_ATIVAL1:
|
||||
case FSL_AUDMIX_ATSTPUP1:
|
||||
case FSL_AUDMIX_ATSTPDN1:
|
||||
case FSL_AUDMIX_ATSTPTGT1:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct reg_default fsl_audmix_reg[] = {
|
||||
{ FSL_AUDMIX_CTR, 0x00060 },
|
||||
{ FSL_AUDMIX_STR, 0x00003 },
|
||||
{ FSL_AUDMIX_ATCR0, 0x00000 },
|
||||
{ FSL_AUDMIX_ATIVAL0, 0x3FFFF },
|
||||
{ FSL_AUDMIX_ATSTPUP0, 0x2AAAA },
|
||||
{ FSL_AUDMIX_ATSTPDN0, 0x30000 },
|
||||
{ FSL_AUDMIX_ATSTPTGT0, 0x00010 },
|
||||
{ FSL_AUDMIX_ATTNVAL0, 0x00000 },
|
||||
{ FSL_AUDMIX_ATSTP0, 0x00000 },
|
||||
{ FSL_AUDMIX_ATCR1, 0x00000 },
|
||||
{ FSL_AUDMIX_ATIVAL1, 0x3FFFF },
|
||||
{ FSL_AUDMIX_ATSTPUP1, 0x2AAAA },
|
||||
{ FSL_AUDMIX_ATSTPDN1, 0x30000 },
|
||||
{ FSL_AUDMIX_ATSTPTGT1, 0x00010 },
|
||||
{ FSL_AUDMIX_ATTNVAL1, 0x00000 },
|
||||
{ FSL_AUDMIX_ATSTP1, 0x00000 },
|
||||
};
|
||||
|
||||
static const struct regmap_config fsl_audmix_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = FSL_AUDMIX_ATSTP1,
|
||||
.reg_defaults = fsl_audmix_reg,
|
||||
.num_reg_defaults = ARRAY_SIZE(fsl_audmix_reg),
|
||||
.readable_reg = fsl_audmix_readable_reg,
|
||||
.writeable_reg = fsl_audmix_writeable_reg,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static const struct of_device_id fsl_audmix_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,imx8qm-audmix",
|
||||
.data = "imx-audmix",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_audmix_ids);
|
||||
|
||||
static int fsl_audmix_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct fsl_audmix *priv;
|
||||
struct resource *res;
|
||||
const char *mdrv;
|
||||
const struct of_device_id *of_id;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
|
||||
of_id = of_match_device(fsl_audmix_ids, dev);
|
||||
if (!of_id || !of_id->data)
|
||||
return -EINVAL;
|
||||
|
||||
mdrv = of_id->data;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get the addresses */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio_clk(dev, "ipg", regs,
|
||||
&fsl_audmix_regmap_config);
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
dev_err(dev, "failed to init regmap\n");
|
||||
return PTR_ERR(priv->regmap);
|
||||
}
|
||||
|
||||
priv->ipg_clk = devm_clk_get(dev, "ipg");
|
||||
if (IS_ERR(priv->ipg_clk)) {
|
||||
dev_err(dev, "failed to get ipg clock\n");
|
||||
return PTR_ERR(priv->ipg_clk);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
ret = devm_snd_soc_register_component(dev, &fsl_audmix_component,
|
||||
fsl_audmix_dai,
|
||||
ARRAY_SIZE(fsl_audmix_dai));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register ASoC DAI\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->pdev = platform_device_register_data(dev, mdrv, 0, NULL, 0);
|
||||
if (IS_ERR(priv->pdev)) {
|
||||
ret = PTR_ERR(priv->pdev);
|
||||
dev_err(dev, "failed to register platform %s: %d\n", mdrv, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_audmix_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (priv->pdev)
|
||||
platform_device_unregister(priv->pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_audmix_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_audmix *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->ipg_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable IPG clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regcache_cache_only(priv->regmap, false);
|
||||
regcache_mark_dirty(priv->regmap);
|
||||
|
||||
return regcache_sync(priv->regmap);
|
||||
}
|
||||
|
||||
static int fsl_audmix_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_audmix *priv = dev_get_drvdata(dev);
|
||||
|
||||
regcache_cache_only(priv->regmap, true);
|
||||
|
||||
clk_disable_unprepare(priv->ipg_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops fsl_audmix_pm = {
|
||||
SET_RUNTIME_PM_OPS(fsl_audmix_runtime_suspend,
|
||||
fsl_audmix_runtime_resume,
|
||||
NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver fsl_audmix_driver = {
|
||||
.probe = fsl_audmix_probe,
|
||||
.remove = fsl_audmix_remove,
|
||||
.driver = {
|
||||
.name = "fsl-audmix",
|
||||
.of_match_table = fsl_audmix_ids,
|
||||
.pm = &fsl_audmix_pm,
|
||||
},
|
||||
};
|
||||
module_platform_driver(fsl_audmix_driver);
|
||||
|
||||
MODULE_DESCRIPTION("NXP AUDMIX ASoC DAI driver");
|
||||
MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
|
||||
MODULE_ALIAS("platform:fsl-audmix");
|
||||
MODULE_LICENSE("GPL v2");
|
102
sound/soc/fsl/fsl_audmix.h
Normal file
102
sound/soc/fsl/fsl_audmix.h
Normal file
@ -0,0 +1,102 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
|
||||
*
|
||||
* Copyright 2017 NXP
|
||||
*/
|
||||
|
||||
#ifndef __FSL_AUDMIX_H
|
||||
#define __FSL_AUDMIX_H
|
||||
|
||||
#define FSL_AUDMIX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
/* AUDMIX Registers */
|
||||
#define FSL_AUDMIX_CTR 0x200 /* Control */
|
||||
#define FSL_AUDMIX_STR 0x204 /* Status */
|
||||
|
||||
#define FSL_AUDMIX_ATCR0 0x208 /* Attenuation Control */
|
||||
#define FSL_AUDMIX_ATIVAL0 0x20c /* Attenuation Initial Value */
|
||||
#define FSL_AUDMIX_ATSTPUP0 0x210 /* Attenuation step up factor */
|
||||
#define FSL_AUDMIX_ATSTPDN0 0x214 /* Attenuation step down factor */
|
||||
#define FSL_AUDMIX_ATSTPTGT0 0x218 /* Attenuation step target */
|
||||
#define FSL_AUDMIX_ATTNVAL0 0x21c /* Attenuation Value */
|
||||
#define FSL_AUDMIX_ATSTP0 0x220 /* Attenuation step number */
|
||||
|
||||
#define FSL_AUDMIX_ATCR1 0x228 /* Attenuation Control */
|
||||
#define FSL_AUDMIX_ATIVAL1 0x22c /* Attenuation Initial Value */
|
||||
#define FSL_AUDMIX_ATSTPUP1 0x230 /* Attenuation step up factor */
|
||||
#define FSL_AUDMIX_ATSTPDN1 0x234 /* Attenuation step down factor */
|
||||
#define FSL_AUDMIX_ATSTPTGT1 0x238 /* Attenuation step target */
|
||||
#define FSL_AUDMIX_ATTNVAL1 0x23c /* Attenuation Value */
|
||||
#define FSL_AUDMIX_ATSTP1 0x240 /* Attenuation step number */
|
||||
|
||||
/* AUDMIX Control Register */
|
||||
#define FSL_AUDMIX_CTR_MIXCLK_SHIFT 0
|
||||
#define FSL_AUDMIX_CTR_MIXCLK_MASK BIT(FSL_AUDMIX_CTR_MIXCLK_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_MIXCLK(i) ((i) << FSL_AUDMIX_CTR_MIXCLK_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_OUTSRC_SHIFT 1
|
||||
#define FSL_AUDMIX_CTR_OUTSRC_MASK (0x3 << FSL_AUDMIX_CTR_OUTSRC_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_OUTSRC(i) (((i) << FSL_AUDMIX_CTR_OUTSRC_SHIFT)\
|
||||
& FSL_AUDMIX_CTR_OUTSRC_MASK)
|
||||
#define FSL_AUDMIX_CTR_OUTWIDTH_SHIFT 3
|
||||
#define FSL_AUDMIX_CTR_OUTWIDTH_MASK (0x7 << FSL_AUDMIX_CTR_OUTWIDTH_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_OUTWIDTH(i) (((i) << FSL_AUDMIX_CTR_OUTWIDTH_SHIFT)\
|
||||
& FSL_AUDMIX_CTR_OUTWIDTH_MASK)
|
||||
#define FSL_AUDMIX_CTR_OUTCKPOL_SHIFT 6
|
||||
#define FSL_AUDMIX_CTR_OUTCKPOL_MASK BIT(FSL_AUDMIX_CTR_OUTCKPOL_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_OUTCKPOL(i) ((i) << FSL_AUDMIX_CTR_OUTCKPOL_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_MASKRTDF_SHIFT 7
|
||||
#define FSL_AUDMIX_CTR_MASKRTDF_MASK BIT(FSL_AUDMIX_CTR_MASKRTDF_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_MASKRTDF(i) ((i) << FSL_AUDMIX_CTR_MASKRTDF_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_MASKCKDF_SHIFT 8
|
||||
#define FSL_AUDMIX_CTR_MASKCKDF_MASK BIT(FSL_AUDMIX_CTR_MASKCKDF_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_MASKCKDF(i) ((i) << FSL_AUDMIX_CTR_MASKCKDF_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_SYNCMODE_SHIFT 9
|
||||
#define FSL_AUDMIX_CTR_SYNCMODE_MASK BIT(FSL_AUDMIX_CTR_SYNCMODE_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_SYNCMODE(i) ((i) << FSL_AUDMIX_CTR_SYNCMODE_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_SYNCSRC_SHIFT 10
|
||||
#define FSL_AUDMIX_CTR_SYNCSRC_MASK BIT(FSL_AUDMIX_CTR_SYNCSRC_SHIFT)
|
||||
#define FSL_AUDMIX_CTR_SYNCSRC(i) ((i) << FSL_AUDMIX_CTR_SYNCSRC_SHIFT)
|
||||
|
||||
/* AUDMIX Status Register */
|
||||
#define FSL_AUDMIX_STR_RATEDIFF BIT(0)
|
||||
#define FSL_AUDMIX_STR_CLKDIFF BIT(1)
|
||||
#define FSL_AUDMIX_STR_MIXSTAT_SHIFT 2
|
||||
#define FSL_AUDMIX_STR_MIXSTAT_MASK (0x3 << FSL_AUDMIX_STR_MIXSTAT_SHIFT)
|
||||
#define FSL_AUDMIX_STR_MIXSTAT(i) (((i) & FSL_AUDMIX_STR_MIXSTAT_MASK) \
|
||||
>> FSL_AUDMIX_STR_MIXSTAT_SHIFT)
|
||||
/* AUDMIX Attenuation Control Register */
|
||||
#define FSL_AUDMIX_ATCR_AT_EN BIT(0)
|
||||
#define FSL_AUDMIX_ATCR_AT_UPDN BIT(1)
|
||||
#define FSL_AUDMIX_ATCR_ATSTPDIF_SHIFT 2
|
||||
#define FSL_AUDMIX_ATCR_ATSTPDFI_MASK \
|
||||
(0xfff << FSL_AUDMIX_ATCR_ATSTPDIF_SHIFT)
|
||||
|
||||
/* AUDMIX Attenuation Initial Value Register */
|
||||
#define FSL_AUDMIX_ATIVAL_ATINVAL_MASK 0x3FFFF
|
||||
|
||||
/* AUDMIX Attenuation Step Up Factor Register */
|
||||
#define FSL_AUDMIX_ATSTPUP_ATSTEPUP_MASK 0x3FFFF
|
||||
|
||||
/* AUDMIX Attenuation Step Down Factor Register */
|
||||
#define FSL_AUDMIX_ATSTPDN_ATSTEPDN_MASK 0x3FFFF
|
||||
|
||||
/* AUDMIX Attenuation Step Target Register */
|
||||
#define FSL_AUDMIX_ATSTPTGT_ATSTPTG_MASK 0x3FFFF
|
||||
|
||||
/* AUDMIX Attenuation Value Register */
|
||||
#define FSL_AUDMIX_ATTNVAL_ATCURVAL_MASK 0x3FFFF
|
||||
|
||||
/* AUDMIX Attenuation Step Number Register */
|
||||
#define FSL_AUDMIX_ATSTP_STPCTR_MASK 0x3FFFF
|
||||
|
||||
#define FSL_AUDMIX_MAX_DAIS 2
|
||||
struct fsl_audmix {
|
||||
struct platform_device *pdev;
|
||||
struct regmap *regmap;
|
||||
struct clk *ipg_clk;
|
||||
u8 tdms;
|
||||
};
|
||||
|
||||
#endif /* __FSL_AUDMIX_H */
|
@ -1,18 +1,14 @@
|
||||
/*
|
||||
* Freescale DMA ALSA SoC PCM driver
|
||||
*
|
||||
* Author: Timur Tabi <timur@freescale.com>
|
||||
*
|
||||
* Copyright 2007-2010 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*
|
||||
* This driver implements ASoC support for the Elo DMA controller, which is
|
||||
* the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms,
|
||||
* the PCM driver is what handles the DMA buffer.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Freescale DMA ALSA SoC PCM driver
|
||||
//
|
||||
// Author: Timur Tabi <timur@freescale.com>
|
||||
//
|
||||
// Copyright 2007-2010 Freescale Semiconductor, Inc.
|
||||
//
|
||||
// This driver implements ASoC support for the Elo DMA controller, which is
|
||||
// the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms,
|
||||
// the PCM driver is what handles the DMA buffer.
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
@ -1,9 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* mpc8610-pcm.h - ALSA PCM interface for the Freescale MPC8610 SoC
|
||||
*
|
||||
* 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 _MPC8610_PCM_H
|
||||
|
@ -218,7 +218,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
{
|
||||
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||
struct clk *clksrc = esai_priv->extalclk;
|
||||
bool tx = clk_id <= ESAI_HCKT_EXTAL;
|
||||
bool tx = (clk_id <= ESAI_HCKT_EXTAL || esai_priv->synchronous);
|
||||
bool in = dir == SND_SOC_CLOCK_IN;
|
||||
u32 ratio, ecr = 0;
|
||||
unsigned long clk_rate;
|
||||
@ -251,9 +251,9 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
break;
|
||||
case ESAI_HCKT_EXTAL:
|
||||
ecr |= ESAI_ECR_ETI;
|
||||
/* fall through */
|
||||
break;
|
||||
case ESAI_HCKR_EXTAL:
|
||||
ecr |= ESAI_ECR_ERI;
|
||||
ecr |= esai_priv->synchronous ? ESAI_ECR_ETI : ESAI_ECR_ERI;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -537,10 +537,18 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
bclk = params_rate(params) * slot_width * esai_priv->slots;
|
||||
|
||||
ret = fsl_esai_set_bclk(dai, tx, bclk);
|
||||
ret = fsl_esai_set_bclk(dai, esai_priv->synchronous || tx, bclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mask = ESAI_xCR_xSWS_MASK;
|
||||
val = ESAI_xCR_xSWS(slot_width, width);
|
||||
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
|
||||
/* Recording in synchronous mode needs to set TCR also */
|
||||
if (!tx && esai_priv->synchronous)
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, val);
|
||||
|
||||
/* Use Normal mode to support monaural audio */
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
|
||||
ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ?
|
||||
@ -556,10 +564,9 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
|
||||
|
||||
mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0);
|
||||
val = ESAI_xCR_xSWS(slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
|
||||
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
|
||||
if (tx)
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
|
||||
ESAI_xCR_PADC, ESAI_xCR_PADC);
|
||||
|
||||
/* Remove ESAI personal reset by configuring ESAI_PCRC and ESAI_PRRC */
|
||||
regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
|
||||
|
@ -151,12 +151,9 @@ static inline int get_clk_div(struct fsl_micfil *micfil,
|
||||
{
|
||||
u32 ctrl2_reg;
|
||||
long mclk_rate;
|
||||
int osr;
|
||||
int clk_div;
|
||||
|
||||
regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg);
|
||||
osr = 16 - ((ctrl2_reg & MICFIL_CTRL2_CICOSR_MASK)
|
||||
>> MICFIL_CTRL2_CICOSR_SHIFT);
|
||||
|
||||
mclk_rate = clk_get_rate(micfil->mclk);
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
@ -268,12 +269,14 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
|
||||
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
|
||||
sai->is_slave_mode = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
sai->is_slave_mode = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
|
||||
sai->is_slave_mode = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
|
||||
@ -899,6 +902,8 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, sai);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
|
||||
&fsl_sai_dai, 1);
|
||||
if (ret)
|
||||
@ -910,6 +915,13 @@ static int fsl_sai_probe(struct platform_device *pdev)
|
||||
return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||
}
|
||||
|
||||
static int fsl_sai_remove(struct platform_device *pdev)
|
||||
{
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id fsl_sai_ids[] = {
|
||||
{ .compatible = "fsl,vf610-sai", },
|
||||
{ .compatible = "fsl,imx6sx-sai", },
|
||||
@ -918,8 +930,8 @@ static const struct of_device_id fsl_sai_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_sai_ids);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int fsl_sai_suspend(struct device *dev)
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_sai_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_sai *sai = dev_get_drvdata(dev);
|
||||
|
||||
@ -929,7 +941,7 @@ static int fsl_sai_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_sai_resume(struct device *dev)
|
||||
static int fsl_sai_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_sai *sai = dev_get_drvdata(dev);
|
||||
|
||||
@ -941,14 +953,18 @@ static int fsl_sai_resume(struct device *dev)
|
||||
regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
|
||||
return regcache_sync(sai->regmap);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops fsl_sai_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume)
|
||||
SET_RUNTIME_PM_OPS(fsl_sai_runtime_suspend,
|
||||
fsl_sai_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver fsl_sai_driver = {
|
||||
.probe = fsl_sai_probe,
|
||||
.remove = fsl_sai_remove,
|
||||
.driver = {
|
||||
.name = "fsl-sai",
|
||||
.pm = &fsl_sai_pm_ops,
|
||||
|
@ -71,6 +71,7 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
|
||||
iprop = of_get_property(dma_np, "cell-index", NULL);
|
||||
if (!iprop) {
|
||||
of_node_put(dma_np);
|
||||
of_node_put(dma_channel_np);
|
||||
return -EINVAL;
|
||||
}
|
||||
*dma_id = be32_to_cpup(iprop);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user