ASoC: More updates for v3.14
A few more updates for v3.14 since the last set, highlights include: - Lots of DMA updates from Lars-Peter - Improvements to the constraints handling code from Lars-Peter - A very helpful conversion of the TWL4030 driver to regmap from Peter - A new driver for the Freescale ESAI controller from Nicolin Chen - Conversion of some of the drivers to use params_width() -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJS19X7AAoJELSic+t+oim9/psQAIKo9sqkQEd6jg/jhSLw5mu5 jVOvIGnSkmfhmnHtgHxFpJtnYiMCpTnUb1Irb16djO7ODpQ1yP3buv6Go5enUmVU zZgsFF+RudM16iXz6ZyAWW8BZJlMDAQTKkepAs7VLCt+FH/PaxOH7apZJi4dGCqj URt3NkvfJigPZciYcPGoFtSvSJnNb31ymdymmWxV6rTureyk9/KEKXiQ1r7QJWKY fQFyreSXaPfrAcBw8ALIHzdxJalW3M6hKQo9PhS5Z+yoqZRwc1XQ90I0LnB6xruo MZp+fQobxbioJDQjt7QluVOKpeUNojvG9dzeZaC7XS9qoejYFewwZNoscg0wljc6 DveEmRgU7AcHo7PYzBqih8gQGBw0Rhl08b0demyq/XHfaJ8Uf8Whfmwmv9MMXpju 6rR9yU2lsne/w+ND8Jj0FPvzARQrLjIXOJEWP9q58ZpDeiUBhcZN9qMtnTyAmh6P mauPd+mmBRLgojfD4+CDSMIFItGKc3rPivCIK7QRMps+aHcitiJO5AOhhBR18Z6Q W3LGrr1oGkc9gZQzaN5dOiPJ9ywJWReuca7EVp7eAK27HGMXfSvTC162uVM7Rv/v N2OtH+6ar2By5Q5HS8if7fU/n6E0QWU++WbTcfny/VHAJXz2p6uIFbymBH8H+5ws JC2F3pHVrCFF9rdgPrKy =9Nvq -----END PGP SIGNATURE----- Merge tag 'asoc-v3.14-2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next ASoC: More updates for v3.14 A few more updates for v3.14 since the last set, highlights include: - Lots of DMA updates from Lars-Peter - Improvements to the constraints handling code from Lars-Peter - A very helpful conversion of the TWL4030 driver to regmap from Peter - A new driver for the Freescale ESAI controller from Nicolin Chen - Conversion of some of the drivers to use params_width()
This commit is contained in:
commit
2aff4c9ce8
|
@ -0,0 +1,50 @@
|
||||||
|
Freescale Enhanced Serial Audio Interface (ESAI) Controller
|
||||||
|
|
||||||
|
The Enhanced Serial Audio Interface (ESAI) provides a full-duplex serial port
|
||||||
|
for serial communication with a variety of serial devices, including industry
|
||||||
|
standard codecs, Sony/Phillips Digital Interface (S/PDIF) transceivers, and
|
||||||
|
other DSPs. It has up to six transmitters and four receivers.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible : Compatible list, must contain "fsl,imx35-esai".
|
||||||
|
|
||||||
|
- reg : Offset and length of the register set for the device.
|
||||||
|
|
||||||
|
- interrupts : Contains the spdif interrupt.
|
||||||
|
|
||||||
|
- dmas : Generic dma devicetree binding as described in
|
||||||
|
Documentation/devicetree/bindings/dma/dma.txt.
|
||||||
|
|
||||||
|
- dma-names : Two dmas have to be defined, "tx" and "rx".
|
||||||
|
|
||||||
|
- clocks: Contains an entry for each entry in clock-names.
|
||||||
|
|
||||||
|
- clock-names : Includes the following entries:
|
||||||
|
"core" The core clock used to access registers
|
||||||
|
"extal" The esai baud clock for esai controller used to derive
|
||||||
|
HCK, SCK and FS.
|
||||||
|
"fsys" The system clock derived from ahb clock used to derive
|
||||||
|
HCK, SCK and FS.
|
||||||
|
|
||||||
|
- fsl,fifo-depth: The number of elements in the transmit and receive FIFOs.
|
||||||
|
This number is the maximum allowed value for TFCR[TFWM] or RFCR[RFWM].
|
||||||
|
|
||||||
|
- fsl,esai-synchronous: This is a boolean property. If present, indicating
|
||||||
|
that ESAI would work in the synchronous mode, which means all the settings
|
||||||
|
for Receiving would be duplicated from Transmition related registers.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
esai: esai@02024000 {
|
||||||
|
compatible = "fsl,imx35-esai";
|
||||||
|
reg = <0x02024000 0x4000>;
|
||||||
|
interrupts = <0 51 0x04>;
|
||||||
|
clocks = <&clks 208>, <&clks 118>, <&clks 208>;
|
||||||
|
clock-names = "core", "extal", "fsys";
|
||||||
|
dmas = <&sdma 23 21 0>, <&sdma 24 21 0>;
|
||||||
|
dma-names = "rx", "tx";
|
||||||
|
fsl,fifo-depth = <128>;
|
||||||
|
fsl,esai-synchronous;
|
||||||
|
status = "disabled";
|
||||||
|
};
|
|
@ -4,7 +4,12 @@ The SSI is a serial device that communicates with audio codecs. It can
|
||||||
be programmed in AC97, I2S, left-justified, or right-justified modes.
|
be programmed in AC97, I2S, left-justified, or right-justified modes.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Compatible list, contains "fsl,ssi".
|
- compatible: Compatible list, should contain one of the following
|
||||||
|
compatibles:
|
||||||
|
fsl,mpc8610-ssi
|
||||||
|
fsl,imx51-ssi
|
||||||
|
fsl,imx35-ssi
|
||||||
|
fsl,imx21-ssi
|
||||||
- cell-index: The SSI, <0> = SSI1, <1> = SSI2, and so on.
|
- cell-index: The SSI, <0> = SSI1, <1> = SSI2, and so on.
|
||||||
- reg: Offset and length of the register set for the device.
|
- reg: Offset and length of the register set for the device.
|
||||||
- interrupts: <a b> where a is the interrupt number and b is a
|
- interrupts: <a b> where a is the interrupt number and b is a
|
||||||
|
|
|
@ -11,7 +11,7 @@ Optional properties:
|
||||||
- simple-audio-card,format : CPU/CODEC common audio format.
|
- simple-audio-card,format : CPU/CODEC common audio format.
|
||||||
"i2s", "right_j", "left_j" , "dsp_a"
|
"i2s", "right_j", "left_j" , "dsp_a"
|
||||||
"dsp_b", "ac97", "pdm", "msb", "lsb"
|
"dsp_b", "ac97", "pdm", "msb", "lsb"
|
||||||
- simple-audio-routing : A list of the connections between audio components.
|
- simple-audio-card,routing : A list of the connections between audio components.
|
||||||
Each entry is a pair of strings, the first being the
|
Each entry is a pair of strings, the first being the
|
||||||
connection's sink, the second being the connection's
|
connection's sink, the second being the connection's
|
||||||
source.
|
source.
|
||||||
|
|
|
@ -6,6 +6,7 @@ Required properties:
|
||||||
|
|
||||||
- compatible - "string" - One of:
|
- compatible - "string" - One of:
|
||||||
"ti,tlv320aic3x" - Generic TLV320AIC3x device
|
"ti,tlv320aic3x" - Generic TLV320AIC3x device
|
||||||
|
"ti,tlv320aic32x4" - TLV320AIC32x4
|
||||||
"ti,tlv320aic33" - TLV320AIC33
|
"ti,tlv320aic33" - TLV320AIC33
|
||||||
"ti,tlv320aic3007" - TLV320AIC3007
|
"ti,tlv320aic3007" - TLV320AIC3007
|
||||||
"ti,tlv320aic3106" - TLV320AIC3106
|
"ti,tlv320aic3106" - TLV320AIC3106
|
||||||
|
|
|
@ -49,18 +49,23 @@ features :-
|
||||||
* Machine specific controls: Allow machines to add controls to the sound card
|
* Machine specific controls: Allow machines to add controls to the sound card
|
||||||
(e.g. volume control for speaker amplifier).
|
(e.g. volume control for speaker amplifier).
|
||||||
|
|
||||||
To achieve all this, ASoC basically splits an embedded audio system into 3
|
To achieve all this, ASoC basically splits an embedded audio system into
|
||||||
components :-
|
multiple re-usable component drivers :-
|
||||||
|
|
||||||
* Codec driver: The codec driver is platform independent and contains audio
|
* Codec class drivers: The codec class driver is platform independent and
|
||||||
controls, audio interface capabilities, codec DAPM definition and codec IO
|
contains audio controls, audio interface capabilities, codec DAPM
|
||||||
functions.
|
definition and codec IO functions. This class extends to BT, FM and MODEM
|
||||||
|
ICs if required. Codec class drivers should be generic code that can run
|
||||||
|
on any architecture and machine.
|
||||||
|
|
||||||
* Platform driver: The platform driver contains the audio DMA engine and audio
|
* Platform class drivers: The platform class driver includes the audio DMA
|
||||||
interface drivers (e.g. I2S, AC97, PCM) for that platform.
|
engine driver, digital audio interface (DAI) drivers (e.g. I2S, AC97, PCM)
|
||||||
|
and any audio DSP drivers for that platform.
|
||||||
|
|
||||||
* Machine driver: The machine driver handles any machine specific controls and
|
* Machine class driver: The machine driver class acts as the glue that
|
||||||
audio events (e.g. turning on an amp at start of playback).
|
decribes and binds the other component drivers together to form an ALSA
|
||||||
|
"sound card device". It handles any machine specific controls and
|
||||||
|
machine level audio events (e.g. turning on an amp at start of playback).
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
|
@ -84,3 +89,7 @@ machine.txt: Machine driver internals.
|
||||||
pop_clicks.txt: How to minimise audio artifacts.
|
pop_clicks.txt: How to minimise audio artifacts.
|
||||||
|
|
||||||
clocking.txt: ASoC clocking for best power performance.
|
clocking.txt: ASoC clocking for best power performance.
|
||||||
|
|
||||||
|
jack.txt: ASoC jack detection.
|
||||||
|
|
||||||
|
DPCM.txt: Dynamic PCM - Describes DPCM with DSP examples.
|
||||||
|
|
|
@ -31,7 +31,7 @@ static struct stedma40_chan_cfg msp0_dma_tx = {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct msp_i2s_platform_data msp0_platform_data = {
|
struct msp_i2s_platform_data msp0_platform_data = {
|
||||||
.id = MSP_I2S_0,
|
.id = 0,
|
||||||
.msp_i2s_dma_rx = &msp0_dma_rx,
|
.msp_i2s_dma_rx = &msp0_dma_rx,
|
||||||
.msp_i2s_dma_tx = &msp0_dma_tx,
|
.msp_i2s_dma_tx = &msp0_dma_tx,
|
||||||
};
|
};
|
||||||
|
@ -49,7 +49,7 @@ static struct stedma40_chan_cfg msp1_dma_tx = {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct msp_i2s_platform_data msp1_platform_data = {
|
struct msp_i2s_platform_data msp1_platform_data = {
|
||||||
.id = MSP_I2S_1,
|
.id = 1,
|
||||||
.msp_i2s_dma_rx = NULL,
|
.msp_i2s_dma_rx = NULL,
|
||||||
.msp_i2s_dma_tx = &msp1_dma_tx,
|
.msp_i2s_dma_tx = &msp1_dma_tx,
|
||||||
};
|
};
|
||||||
|
@ -69,13 +69,13 @@ static struct stedma40_chan_cfg msp2_dma_tx = {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct msp_i2s_platform_data msp2_platform_data = {
|
struct msp_i2s_platform_data msp2_platform_data = {
|
||||||
.id = MSP_I2S_2,
|
.id = 2,
|
||||||
.msp_i2s_dma_rx = &msp2_dma_rx,
|
.msp_i2s_dma_rx = &msp2_dma_rx,
|
||||||
.msp_i2s_dma_tx = &msp2_dma_tx,
|
.msp_i2s_dma_tx = &msp2_dma_tx,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct msp_i2s_platform_data msp3_platform_data = {
|
struct msp_i2s_platform_data msp3_platform_data = {
|
||||||
.id = MSP_I2S_3,
|
.id = 3,
|
||||||
.msp_i2s_dma_rx = &msp1_dma_rx,
|
.msp_i2s_dma_rx = &msp1_dma_rx,
|
||||||
.msp_i2s_dma_tx = NULL,
|
.msp_i2s_dma_tx = NULL,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2884,6 +2884,7 @@ static int pl330_dma_device_slave_caps(struct dma_chan *dchan,
|
||||||
caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
|
caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
|
||||||
caps->cmd_pause = false;
|
caps->cmd_pause = false;
|
||||||
caps->cmd_terminate = true;
|
caps->cmd_terminate = true;
|
||||||
|
caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,9 @@
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c/twl.h>
|
#include <linux/i2c/twl.h>
|
||||||
|
|
||||||
|
/* Register descriptions for audio */
|
||||||
|
#include <linux/mfd/twl4030-audio.h>
|
||||||
|
|
||||||
#include "twl-core.h"
|
#include "twl-core.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -200,6 +203,105 @@ static struct twl_mapping twl4030_map[] = {
|
||||||
{ 2, TWL5031_BASEADD_INTERRUPTS },
|
{ 2, TWL5031_BASEADD_INTERRUPTS },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct reg_default twl4030_49_defaults[] = {
|
||||||
|
/* Audio Registers */
|
||||||
|
{ 0x01, 0x00}, /* CODEC_MODE */
|
||||||
|
{ 0x02, 0x00}, /* OPTION */
|
||||||
|
/* 0x03 Unused */
|
||||||
|
{ 0x04, 0x00}, /* MICBIAS_CTL */
|
||||||
|
{ 0x05, 0x00}, /* ANAMICL */
|
||||||
|
{ 0x06, 0x00}, /* ANAMICR */
|
||||||
|
{ 0x07, 0x00}, /* AVADC_CTL */
|
||||||
|
{ 0x08, 0x00}, /* ADCMICSEL */
|
||||||
|
{ 0x09, 0x00}, /* DIGMIXING */
|
||||||
|
{ 0x0a, 0x0f}, /* ATXL1PGA */
|
||||||
|
{ 0x0b, 0x0f}, /* ATXR1PGA */
|
||||||
|
{ 0x0c, 0x0f}, /* AVTXL2PGA */
|
||||||
|
{ 0x0d, 0x0f}, /* AVTXR2PGA */
|
||||||
|
{ 0x0e, 0x00}, /* AUDIO_IF */
|
||||||
|
{ 0x0f, 0x00}, /* VOICE_IF */
|
||||||
|
{ 0x10, 0x3f}, /* ARXR1PGA */
|
||||||
|
{ 0x11, 0x3f}, /* ARXL1PGA */
|
||||||
|
{ 0x12, 0x3f}, /* ARXR2PGA */
|
||||||
|
{ 0x13, 0x3f}, /* ARXL2PGA */
|
||||||
|
{ 0x14, 0x25}, /* VRXPGA */
|
||||||
|
{ 0x15, 0x00}, /* VSTPGA */
|
||||||
|
{ 0x16, 0x00}, /* VRX2ARXPGA */
|
||||||
|
{ 0x17, 0x00}, /* AVDAC_CTL */
|
||||||
|
{ 0x18, 0x00}, /* ARX2VTXPGA */
|
||||||
|
{ 0x19, 0x32}, /* ARXL1_APGA_CTL*/
|
||||||
|
{ 0x1a, 0x32}, /* ARXR1_APGA_CTL*/
|
||||||
|
{ 0x1b, 0x32}, /* ARXL2_APGA_CTL*/
|
||||||
|
{ 0x1c, 0x32}, /* ARXR2_APGA_CTL*/
|
||||||
|
{ 0x1d, 0x00}, /* ATX2ARXPGA */
|
||||||
|
{ 0x1e, 0x00}, /* BT_IF */
|
||||||
|
{ 0x1f, 0x55}, /* BTPGA */
|
||||||
|
{ 0x20, 0x00}, /* BTSTPGA */
|
||||||
|
{ 0x21, 0x00}, /* EAR_CTL */
|
||||||
|
{ 0x22, 0x00}, /* HS_SEL */
|
||||||
|
{ 0x23, 0x00}, /* HS_GAIN_SET */
|
||||||
|
{ 0x24, 0x00}, /* HS_POPN_SET */
|
||||||
|
{ 0x25, 0x00}, /* PREDL_CTL */
|
||||||
|
{ 0x26, 0x00}, /* PREDR_CTL */
|
||||||
|
{ 0x27, 0x00}, /* PRECKL_CTL */
|
||||||
|
{ 0x28, 0x00}, /* PRECKR_CTL */
|
||||||
|
{ 0x29, 0x00}, /* HFL_CTL */
|
||||||
|
{ 0x2a, 0x00}, /* HFR_CTL */
|
||||||
|
{ 0x2b, 0x05}, /* ALC_CTL */
|
||||||
|
{ 0x2c, 0x00}, /* ALC_SET1 */
|
||||||
|
{ 0x2d, 0x00}, /* ALC_SET2 */
|
||||||
|
{ 0x2e, 0x00}, /* BOOST_CTL */
|
||||||
|
{ 0x2f, 0x00}, /* SOFTVOL_CTL */
|
||||||
|
{ 0x30, 0x13}, /* DTMF_FREQSEL */
|
||||||
|
{ 0x31, 0x00}, /* DTMF_TONEXT1H */
|
||||||
|
{ 0x32, 0x00}, /* DTMF_TONEXT1L */
|
||||||
|
{ 0x33, 0x00}, /* DTMF_TONEXT2H */
|
||||||
|
{ 0x34, 0x00}, /* DTMF_TONEXT2L */
|
||||||
|
{ 0x35, 0x79}, /* DTMF_TONOFF */
|
||||||
|
{ 0x36, 0x11}, /* DTMF_WANONOFF */
|
||||||
|
{ 0x37, 0x00}, /* I2S_RX_SCRAMBLE_H */
|
||||||
|
{ 0x38, 0x00}, /* I2S_RX_SCRAMBLE_M */
|
||||||
|
{ 0x39, 0x00}, /* I2S_RX_SCRAMBLE_L */
|
||||||
|
{ 0x3a, 0x06}, /* APLL_CTL */
|
||||||
|
{ 0x3b, 0x00}, /* DTMF_CTL */
|
||||||
|
{ 0x3c, 0x44}, /* DTMF_PGA_CTL2 (0x3C) */
|
||||||
|
{ 0x3d, 0x69}, /* DTMF_PGA_CTL1 (0x3D) */
|
||||||
|
{ 0x3e, 0x00}, /* MISC_SET_1 */
|
||||||
|
{ 0x3f, 0x00}, /* PCMBTMUX */
|
||||||
|
/* 0x40 - 0x42 Unused */
|
||||||
|
{ 0x43, 0x00}, /* RX_PATH_SEL */
|
||||||
|
{ 0x44, 0x32}, /* VDL_APGA_CTL */
|
||||||
|
{ 0x45, 0x00}, /* VIBRA_CTL */
|
||||||
|
{ 0x46, 0x00}, /* VIBRA_SET */
|
||||||
|
{ 0x47, 0x00}, /* VIBRA_PWM_SET */
|
||||||
|
{ 0x48, 0x00}, /* ANAMIC_GAIN */
|
||||||
|
{ 0x49, 0x00}, /* MISC_SET_2 */
|
||||||
|
/* End of Audio Registers */
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool twl4030_49_nop_reg(struct device *dev, unsigned int reg)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case 0:
|
||||||
|
case 3:
|
||||||
|
case 40:
|
||||||
|
case 41:
|
||||||
|
case 42:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct regmap_range twl4030_49_volatile_ranges[] = {
|
||||||
|
regmap_reg_range(TWL4030_BASEADD_TEST, 0xff),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_access_table twl4030_49_volatile_table = {
|
||||||
|
.yes_ranges = twl4030_49_volatile_ranges,
|
||||||
|
.n_yes_ranges = ARRAY_SIZE(twl4030_49_volatile_ranges),
|
||||||
|
};
|
||||||
|
|
||||||
static struct regmap_config twl4030_regmap_config[4] = {
|
static struct regmap_config twl4030_regmap_config[4] = {
|
||||||
{
|
{
|
||||||
/* Address 0x48 */
|
/* Address 0x48 */
|
||||||
|
@ -212,6 +314,15 @@ static struct regmap_config twl4030_regmap_config[4] = {
|
||||||
.reg_bits = 8,
|
.reg_bits = 8,
|
||||||
.val_bits = 8,
|
.val_bits = 8,
|
||||||
.max_register = 0xff,
|
.max_register = 0xff,
|
||||||
|
|
||||||
|
.readable_reg = twl4030_49_nop_reg,
|
||||||
|
.writeable_reg = twl4030_49_nop_reg,
|
||||||
|
|
||||||
|
.volatile_table = &twl4030_49_volatile_table,
|
||||||
|
|
||||||
|
.reg_defaults = twl4030_49_defaults,
|
||||||
|
.num_reg_defaults = ARRAY_SIZE(twl4030_49_defaults),
|
||||||
|
.cache_type = REGCACHE_RBTREE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
/* Address 0x4a */
|
/* Address 0x4a */
|
||||||
|
@ -301,6 +412,32 @@ unsigned int twl_rev(void)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(twl_rev);
|
EXPORT_SYMBOL(twl_rev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* twl_get_regmap - Get the regmap associated with the given module
|
||||||
|
* @mod_no: module number
|
||||||
|
*
|
||||||
|
* Returns the regmap pointer or NULL in case of failure.
|
||||||
|
*/
|
||||||
|
static struct regmap *twl_get_regmap(u8 mod_no)
|
||||||
|
{
|
||||||
|
int sid;
|
||||||
|
struct twl_client *twl;
|
||||||
|
|
||||||
|
if (unlikely(!twl_priv || !twl_priv->ready)) {
|
||||||
|
pr_err("%s: not initialized\n", DRIVER_NAME);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (unlikely(mod_no >= twl_get_last_module())) {
|
||||||
|
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sid = twl_priv->twl_map[mod_no].sid;
|
||||||
|
twl = &twl_priv->twl_modules[sid];
|
||||||
|
|
||||||
|
return twl->regmap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0
|
* twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0
|
||||||
* @mod_no: module number
|
* @mod_no: module number
|
||||||
|
@ -312,25 +449,14 @@ EXPORT_SYMBOL(twl_rev);
|
||||||
*/
|
*/
|
||||||
int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
|
int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
|
||||||
{
|
{
|
||||||
|
struct regmap *regmap = twl_get_regmap(mod_no);
|
||||||
int ret;
|
int ret;
|
||||||
int sid;
|
|
||||||
struct twl_client *twl;
|
|
||||||
|
|
||||||
if (unlikely(!twl_priv || !twl_priv->ready)) {
|
if (!regmap)
|
||||||
pr_err("%s: not initialized\n", DRIVER_NAME);
|
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
|
||||||
if (unlikely(mod_no >= twl_get_last_module())) {
|
|
||||||
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
sid = twl_priv->twl_map[mod_no].sid;
|
ret = regmap_bulk_write(regmap, twl_priv->twl_map[mod_no].base + reg,
|
||||||
twl = &twl_priv->twl_modules[sid];
|
value, num_bytes);
|
||||||
|
|
||||||
ret = regmap_bulk_write(twl->regmap,
|
|
||||||
twl_priv->twl_map[mod_no].base + reg, value,
|
|
||||||
num_bytes);
|
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n",
|
pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n",
|
||||||
|
@ -351,25 +477,14 @@ EXPORT_SYMBOL(twl_i2c_write);
|
||||||
*/
|
*/
|
||||||
int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
|
int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
|
||||||
{
|
{
|
||||||
|
struct regmap *regmap = twl_get_regmap(mod_no);
|
||||||
int ret;
|
int ret;
|
||||||
int sid;
|
|
||||||
struct twl_client *twl;
|
|
||||||
|
|
||||||
if (unlikely(!twl_priv || !twl_priv->ready)) {
|
if (!regmap)
|
||||||
pr_err("%s: not initialized\n", DRIVER_NAME);
|
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
|
||||||
if (unlikely(mod_no >= twl_get_last_module())) {
|
|
||||||
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
sid = twl_priv->twl_map[mod_no].sid;
|
ret = regmap_bulk_read(regmap, twl_priv->twl_map[mod_no].base + reg,
|
||||||
twl = &twl_priv->twl_modules[sid];
|
value, num_bytes);
|
||||||
|
|
||||||
ret = regmap_bulk_read(twl->regmap,
|
|
||||||
twl_priv->twl_map[mod_no].base + reg, value,
|
|
||||||
num_bytes);
|
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n",
|
pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n",
|
||||||
|
@ -379,6 +494,27 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(twl_i2c_read);
|
EXPORT_SYMBOL(twl_i2c_read);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* twl_regcache_bypass - Configure the regcache bypass for the regmap associated
|
||||||
|
* with the module
|
||||||
|
* @mod_no: module number
|
||||||
|
* @enable: Regcache bypass state
|
||||||
|
*
|
||||||
|
* Returns 0 else failure.
|
||||||
|
*/
|
||||||
|
int twl_set_regcache_bypass(u8 mod_no, bool enable)
|
||||||
|
{
|
||||||
|
struct regmap *regmap = twl_get_regmap(mod_no);
|
||||||
|
|
||||||
|
if (!regmap)
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
regcache_cache_bypass(regmap, enable);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(twl_set_regcache_bypass);
|
||||||
|
|
||||||
/*----------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -610,6 +610,9 @@ static const struct reg_default wm5110_reg_default[] = {
|
||||||
{ 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */
|
{ 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */
|
||||||
{ 0x00000492, 0x0069 }, /* R1170 - PDM SPK2 CTRL 1 */
|
{ 0x00000492, 0x0069 }, /* R1170 - PDM SPK2 CTRL 1 */
|
||||||
{ 0x00000493, 0x0000 }, /* R1171 - PDM SPK2 CTRL 2 */
|
{ 0x00000493, 0x0000 }, /* R1171 - PDM SPK2 CTRL 2 */
|
||||||
|
{ 0x000004A0, 0x3480 }, /* R1184 - HP1 Short Circuit Ctrl */
|
||||||
|
{ 0x000004A1, 0x3480 }, /* R1185 - HP2 Short Circuit Ctrl */
|
||||||
|
{ 0x000004A2, 0x3480 }, /* R1186 - HP3 Short Circuit Ctrl */
|
||||||
{ 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */
|
{ 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */
|
||||||
{ 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */
|
{ 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */
|
||||||
{ 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */
|
{ 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */
|
||||||
|
@ -1639,6 +1642,9 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
|
||||||
case ARIZONA_PDM_SPK1_CTRL_2:
|
case ARIZONA_PDM_SPK1_CTRL_2:
|
||||||
case ARIZONA_PDM_SPK2_CTRL_1:
|
case ARIZONA_PDM_SPK2_CTRL_1:
|
||||||
case ARIZONA_PDM_SPK2_CTRL_2:
|
case ARIZONA_PDM_SPK2_CTRL_2:
|
||||||
|
case ARIZONA_HP1_SHORT_CIRCUIT_CTRL:
|
||||||
|
case ARIZONA_HP2_SHORT_CIRCUIT_CTRL:
|
||||||
|
case ARIZONA_HP3_SHORT_CIRCUIT_CTRL:
|
||||||
case ARIZONA_AIF1_BCLK_CTRL:
|
case ARIZONA_AIF1_BCLK_CTRL:
|
||||||
case ARIZONA_AIF1_TX_PIN_CTRL:
|
case ARIZONA_AIF1_TX_PIN_CTRL:
|
||||||
case ARIZONA_AIF1_RX_PIN_CTRL:
|
case ARIZONA_AIF1_RX_PIN_CTRL:
|
||||||
|
|
|
@ -364,6 +364,32 @@ struct dma_slave_config {
|
||||||
unsigned int slave_id;
|
unsigned int slave_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum dma_residue_granularity - Granularity of the reported transfer residue
|
||||||
|
* @DMA_RESIDUE_GRANULARITY_DESCRIPTOR: Residue reporting is not support. The
|
||||||
|
* DMA channel is only able to tell whether a descriptor has been completed or
|
||||||
|
* not, which means residue reporting is not supported by this channel. The
|
||||||
|
* residue field of the dma_tx_state field will always be 0.
|
||||||
|
* @DMA_RESIDUE_GRANULARITY_SEGMENT: Residue is updated after each successfully
|
||||||
|
* completed segment of the transfer (For cyclic transfers this is after each
|
||||||
|
* period). This is typically implemented by having the hardware generate an
|
||||||
|
* interrupt after each transferred segment and then the drivers updates the
|
||||||
|
* outstanding residue by the size of the segment. Another possibility is if
|
||||||
|
* the hardware supports scatter-gather and the segment descriptor has a field
|
||||||
|
* which gets set after the segment has been completed. The driver then counts
|
||||||
|
* the number of segments without the flag set to compute the residue.
|
||||||
|
* @DMA_RESIDUE_GRANULARITY_BURST: Residue is updated after each transferred
|
||||||
|
* burst. This is typically only supported if the hardware has a progress
|
||||||
|
* register of some sort (E.g. a register with the current read/write address
|
||||||
|
* or a register with the amount of bursts/beats/bytes that have been
|
||||||
|
* transferred or still need to be transferred).
|
||||||
|
*/
|
||||||
|
enum dma_residue_granularity {
|
||||||
|
DMA_RESIDUE_GRANULARITY_DESCRIPTOR = 0,
|
||||||
|
DMA_RESIDUE_GRANULARITY_SEGMENT = 1,
|
||||||
|
DMA_RESIDUE_GRANULARITY_BURST = 2,
|
||||||
|
};
|
||||||
|
|
||||||
/* struct dma_slave_caps - expose capabilities of a slave channel only
|
/* struct dma_slave_caps - expose capabilities of a slave channel only
|
||||||
*
|
*
|
||||||
* @src_addr_widths: bit mask of src addr widths the channel supports
|
* @src_addr_widths: bit mask of src addr widths the channel supports
|
||||||
|
@ -374,6 +400,7 @@ struct dma_slave_config {
|
||||||
* should be checked by controller as well
|
* should be checked by controller as well
|
||||||
* @cmd_pause: true, if pause and thereby resume is supported
|
* @cmd_pause: true, if pause and thereby resume is supported
|
||||||
* @cmd_terminate: true, if terminate cmd is supported
|
* @cmd_terminate: true, if terminate cmd is supported
|
||||||
|
* @residue_granularity: granularity of the reported transfer residue
|
||||||
*/
|
*/
|
||||||
struct dma_slave_caps {
|
struct dma_slave_caps {
|
||||||
u32 src_addr_widths;
|
u32 src_addr_widths;
|
||||||
|
@ -381,6 +408,7 @@ struct dma_slave_caps {
|
||||||
u32 directions;
|
u32 directions;
|
||||||
bool cmd_pause;
|
bool cmd_pause;
|
||||||
bool cmd_terminate;
|
bool cmd_terminate;
|
||||||
|
enum dma_residue_granularity residue_granularity;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline const char *dma_chan_name(struct dma_chan *chan)
|
static inline const char *dma_chan_name(struct dma_chan *chan)
|
||||||
|
|
|
@ -175,6 +175,9 @@ static inline int twl_class_is_ ##class(void) \
|
||||||
TWL_CLASS_IS(4030, TWL4030_CLASS_ID)
|
TWL_CLASS_IS(4030, TWL4030_CLASS_ID)
|
||||||
TWL_CLASS_IS(6030, TWL6030_CLASS_ID)
|
TWL_CLASS_IS(6030, TWL6030_CLASS_ID)
|
||||||
|
|
||||||
|
/* Set the regcache bypass for the regmap associated with the nodule */
|
||||||
|
int twl_set_regcache_bypass(u8 mod_no, bool enable);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read and write several 8-bit registers at once.
|
* Read and write several 8-bit registers at once.
|
||||||
*/
|
*/
|
||||||
|
@ -667,8 +670,6 @@ struct twl4030_codec_data {
|
||||||
unsigned int digimic_delay; /* in ms */
|
unsigned int digimic_delay; /* in ms */
|
||||||
unsigned int ramp_delay_value;
|
unsigned int ramp_delay_value;
|
||||||
unsigned int offset_cncl_path;
|
unsigned int offset_cncl_path;
|
||||||
unsigned int check_defaults:1;
|
|
||||||
unsigned int reset_registers:1;
|
|
||||||
unsigned int hs_extmute:1;
|
unsigned int hs_extmute:1;
|
||||||
int hs_extmute_gpio;
|
int hs_extmute_gpio;
|
||||||
};
|
};
|
||||||
|
|
|
@ -226,6 +226,9 @@
|
||||||
#define ARIZONA_PDM_SPK1_CTRL_2 0x491
|
#define ARIZONA_PDM_SPK1_CTRL_2 0x491
|
||||||
#define ARIZONA_PDM_SPK2_CTRL_1 0x492
|
#define ARIZONA_PDM_SPK2_CTRL_1 0x492
|
||||||
#define ARIZONA_PDM_SPK2_CTRL_2 0x493
|
#define ARIZONA_PDM_SPK2_CTRL_2 0x493
|
||||||
|
#define ARIZONA_HP1_SHORT_CIRCUIT_CTRL 0x4A0
|
||||||
|
#define ARIZONA_HP2_SHORT_CIRCUIT_CTRL 0x4A1
|
||||||
|
#define ARIZONA_HP3_SHORT_CIRCUIT_CTRL 0x4A2
|
||||||
#define ARIZONA_SPK_CTRL_2 0x4B5
|
#define ARIZONA_SPK_CTRL_2 0x4B5
|
||||||
#define ARIZONA_SPK_CTRL_3 0x4B6
|
#define ARIZONA_SPK_CTRL_3 0x4B6
|
||||||
#define ARIZONA_DAC_COMP_1 0x4DC
|
#define ARIZONA_DAC_COMP_1 0x4DC
|
||||||
|
@ -3332,6 +3335,30 @@
|
||||||
#define ARIZONA_SPK2_FMT_SHIFT 0 /* SPK2_FMT */
|
#define ARIZONA_SPK2_FMT_SHIFT 0 /* SPK2_FMT */
|
||||||
#define ARIZONA_SPK2_FMT_WIDTH 1 /* SPK2_FMT */
|
#define ARIZONA_SPK2_FMT_WIDTH 1 /* SPK2_FMT */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* R1184 (0x4A0) - HP1 Short Circuit Ctrl
|
||||||
|
*/
|
||||||
|
#define ARIZONA_HP1_SC_ENA 0x1000 /* HP1_SC_ENA */
|
||||||
|
#define ARIZONA_HP1_SC_ENA_MASK 0x1000 /* HP1_SC_ENA */
|
||||||
|
#define ARIZONA_HP1_SC_ENA_SHIFT 12 /* HP1_SC_ENA */
|
||||||
|
#define ARIZONA_HP1_SC_ENA_WIDTH 1 /* HP1_SC_ENA */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* R1185 (0x4A1) - HP2 Short Circuit Ctrl
|
||||||
|
*/
|
||||||
|
#define ARIZONA_HP2_SC_ENA 0x1000 /* HP2_SC_ENA */
|
||||||
|
#define ARIZONA_HP2_SC_ENA_MASK 0x1000 /* HP2_SC_ENA */
|
||||||
|
#define ARIZONA_HP2_SC_ENA_SHIFT 12 /* HP2_SC_ENA */
|
||||||
|
#define ARIZONA_HP2_SC_ENA_WIDTH 1 /* HP2_SC_ENA */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* R1186 (0x4A2) - HP3 Short Circuit Ctrl
|
||||||
|
*/
|
||||||
|
#define ARIZONA_HP3_SC_ENA 0x1000 /* HP3_SC_ENA */
|
||||||
|
#define ARIZONA_HP3_SC_ENA_MASK 0x1000 /* HP3_SC_ENA */
|
||||||
|
#define ARIZONA_HP3_SC_ENA_SHIFT 12 /* HP3_SC_ENA */
|
||||||
|
#define ARIZONA_HP3_SC_ENA_WIDTH 1 /* HP3_SC_ENA */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* R1244 (0x4DC) - DAC comp 1
|
* R1244 (0x4DC) - DAC comp 1
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,16 +10,9 @@
|
||||||
|
|
||||||
#include <linux/platform_data/dma-ste-dma40.h>
|
#include <linux/platform_data/dma-ste-dma40.h>
|
||||||
|
|
||||||
enum msp_i2s_id {
|
|
||||||
MSP_I2S_0 = 0,
|
|
||||||
MSP_I2S_1,
|
|
||||||
MSP_I2S_2,
|
|
||||||
MSP_I2S_3,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Platform data structure for a MSP I2S-device */
|
/* Platform data structure for a MSP I2S-device */
|
||||||
struct msp_i2s_platform_data {
|
struct msp_i2s_platform_data {
|
||||||
enum msp_i2s_id id;
|
int id;
|
||||||
struct stedma40_chan_cfg *msp_i2s_dma_rx;
|
struct stedma40_chan_cfg *msp_i2s_dma_rx;
|
||||||
struct stedma40_chan_cfg *msp_i2s_dma_tx;
|
struct stedma40_chan_cfg *msp_i2s_dma_tx;
|
||||||
};
|
};
|
||||||
|
|
|
@ -900,6 +900,8 @@ extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates;
|
||||||
int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime);
|
int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime);
|
||||||
unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate);
|
unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate);
|
||||||
unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit);
|
unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit);
|
||||||
|
unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
|
||||||
|
unsigned int rates_b);
|
||||||
|
|
||||||
static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substream,
|
static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substream,
|
||||||
struct snd_dma_buffer *bufp)
|
struct snd_dma_buffer *bufp)
|
||||||
|
|
|
@ -123,6 +123,8 @@ int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
|
||||||
int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
|
int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
|
||||||
int direction);
|
int direction);
|
||||||
|
|
||||||
|
int snd_soc_dai_is_dummy(struct snd_soc_dai *dai);
|
||||||
|
|
||||||
struct snd_soc_dai_ops {
|
struct snd_soc_dai_ops {
|
||||||
/*
|
/*
|
||||||
* DAI clocking configuration, all optional.
|
* DAI clocking configuration, all optional.
|
||||||
|
|
|
@ -412,6 +412,7 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
|
||||||
int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
|
int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
|
||||||
struct snd_soc_dai *dai);
|
struct snd_soc_dai *dai);
|
||||||
int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
|
int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
|
||||||
|
void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card);
|
||||||
int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
|
int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
|
||||||
const struct snd_soc_pcm_stream *params,
|
const struct snd_soc_pcm_stream *params,
|
||||||
struct snd_soc_dapm_widget *source,
|
struct snd_soc_dapm_widget *source,
|
||||||
|
|
|
@ -895,6 +895,10 @@ struct snd_soc_dai_link {
|
||||||
/* This DAI link can route to other DAI links at runtime (Frontend)*/
|
/* This DAI link can route to other DAI links at runtime (Frontend)*/
|
||||||
unsigned int dynamic:1;
|
unsigned int dynamic:1;
|
||||||
|
|
||||||
|
/* DPCM capture and Playback support */
|
||||||
|
unsigned int dpcm_capture:1;
|
||||||
|
unsigned int dpcm_playback:1;
|
||||||
|
|
||||||
/* pmdown_time is ignored at stop */
|
/* pmdown_time is ignored at stop */
|
||||||
unsigned int ignore_pmdown_time:1;
|
unsigned int ignore_pmdown_time:1;
|
||||||
|
|
||||||
|
|
|
@ -514,3 +514,42 @@ unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(snd_pcm_rate_bit_to_rate);
|
EXPORT_SYMBOL(snd_pcm_rate_bit_to_rate);
|
||||||
|
|
||||||
|
static unsigned int snd_pcm_rate_mask_sanitize(unsigned int rates)
|
||||||
|
{
|
||||||
|
if (rates & SNDRV_PCM_RATE_CONTINUOUS)
|
||||||
|
return SNDRV_PCM_RATE_CONTINUOUS;
|
||||||
|
else if (rates & SNDRV_PCM_RATE_KNOT)
|
||||||
|
return SNDRV_PCM_RATE_KNOT;
|
||||||
|
return rates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* snd_pcm_rate_mask_intersect - computes the intersection between two rate masks
|
||||||
|
* @rates_a: The first rate mask
|
||||||
|
* @rates_b: The second rate mask
|
||||||
|
*
|
||||||
|
* This function computes the rates that are supported by both rate masks passed
|
||||||
|
* to the function. It will take care of the special handling of
|
||||||
|
* SNDRV_PCM_RATE_CONTINUOUS and SNDRV_PCM_RATE_KNOT.
|
||||||
|
*
|
||||||
|
* Return: A rate mask containing the rates that are supported by both rates_a
|
||||||
|
* and rates_b.
|
||||||
|
*/
|
||||||
|
unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
|
||||||
|
unsigned int rates_b)
|
||||||
|
{
|
||||||
|
rates_a = snd_pcm_rate_mask_sanitize(rates_a);
|
||||||
|
rates_b = snd_pcm_rate_mask_sanitize(rates_b);
|
||||||
|
|
||||||
|
if (rates_a & SNDRV_PCM_RATE_CONTINUOUS)
|
||||||
|
return rates_b;
|
||||||
|
else if (rates_b & SNDRV_PCM_RATE_CONTINUOUS)
|
||||||
|
return rates_a;
|
||||||
|
else if (rates_a & SNDRV_PCM_RATE_KNOT)
|
||||||
|
return rates_b;
|
||||||
|
else if (rates_b & SNDRV_PCM_RATE_KNOT)
|
||||||
|
return rates_a;
|
||||||
|
return rates_a & rates_b;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(snd_pcm_rate_mask_intersect);
|
||||||
|
|
|
@ -236,8 +236,7 @@ static int axi_i2s_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_clk_disable;
|
goto err_clk_disable;
|
||||||
|
|
||||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
|
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_clk_disable;
|
goto err_clk_disable;
|
||||||
|
|
||||||
|
|
|
@ -229,8 +229,7 @@ static int axi_spdif_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_clk_disable;
|
goto err_clk_disable;
|
||||||
|
|
||||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
|
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_clk_disable;
|
goto err_clk_disable;
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,6 @@ static const struct snd_pcm_hardware atmel_pcm_dma_hardware = {
|
||||||
SNDRV_PCM_INFO_INTERLEAVED |
|
SNDRV_PCM_INFO_INTERLEAVED |
|
||||||
SNDRV_PCM_INFO_RESUME |
|
SNDRV_PCM_INFO_RESUME |
|
||||||
SNDRV_PCM_INFO_PAUSE,
|
SNDRV_PCM_INFO_PAUSE,
|
||||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
|
||||||
.period_bytes_min = 256, /* lighting DMA overhead */
|
.period_bytes_min = 256, /* lighting DMA overhead */
|
||||||
.period_bytes_max = 2 * 0xffff, /* if 2 bytes format */
|
.period_bytes_max = 2 * 0xffff, /* if 2 bytes format */
|
||||||
.periods_min = 8,
|
.periods_min = 8,
|
||||||
|
|
|
@ -58,7 +58,6 @@ static const struct snd_pcm_hardware atmel_pcm_hardware = {
|
||||||
SNDRV_PCM_INFO_MMAP_VALID |
|
SNDRV_PCM_INFO_MMAP_VALID |
|
||||||
SNDRV_PCM_INFO_INTERLEAVED |
|
SNDRV_PCM_INFO_INTERLEAVED |
|
||||||
SNDRV_PCM_INFO_PAUSE,
|
SNDRV_PCM_INFO_PAUSE,
|
||||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
|
||||||
.period_bytes_min = 32,
|
.period_bytes_min = 32,
|
||||||
.period_bytes_max = 8192,
|
.period_bytes_max = 8192,
|
||||||
.periods_min = 2,
|
.periods_min = 2,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
config SND_BCM2835_SOC_I2S
|
config SND_BCM2835_SOC_I2S
|
||||||
tristate "SoC Audio support for the Broadcom BCM2835 I2S module"
|
tristate "SoC Audio support for the Broadcom BCM2835 I2S module"
|
||||||
depends on ARCH_BCM2835 || COMPILE_TEST
|
depends on ARCH_BCM2835 || COMPILE_TEST
|
||||||
select SND_SOC_DMAENGINE_PCM
|
|
||||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||||
select REGMAP_MMIO
|
select REGMAP_MMIO
|
||||||
help
|
help
|
||||||
|
|
|
@ -168,15 +168,15 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream,
|
||||||
int word_len = 0;
|
int word_len = 0;
|
||||||
|
|
||||||
/* bit size */
|
/* bit size */
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
word_len = AD1836_WORD_LEN_16;
|
word_len = AD1836_WORD_LEN_16;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
word_len = AD1836_WORD_LEN_20;
|
word_len = AD1836_WORD_LEN_20;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
case SNDRV_PCM_FORMAT_S32_LE:
|
case 32:
|
||||||
word_len = AD1836_WORD_LEN_24;
|
word_len = AD1836_WORD_LEN_24;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -249,15 +249,15 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
|
||||||
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
|
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
|
||||||
|
|
||||||
/* bit size */
|
/* bit size */
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
word_len = 3;
|
word_len = 3;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
word_len = 1;
|
word_len = 1;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
case SNDRV_PCM_FORMAT_S32_LE:
|
case 32:
|
||||||
word_len = 0;
|
word_len = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1078,17 +1078,17 @@ static int adau1373_hw_params(struct snd_pcm_substream *substream,
|
||||||
ADAU1373_BCLKDIV_SR_MASK | ADAU1373_BCLKDIV_BCLK_MASK,
|
ADAU1373_BCLKDIV_SR_MASK | ADAU1373_BCLKDIV_BCLK_MASK,
|
||||||
(div << 2) | ADAU1373_BCLKDIV_64);
|
(div << 2) | ADAU1373_BCLKDIV_64);
|
||||||
|
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
ctrl = ADAU1373_DAI_WLEN_16;
|
ctrl = ADAU1373_DAI_WLEN_16;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
ctrl = ADAU1373_DAI_WLEN_20;
|
ctrl = ADAU1373_DAI_WLEN_20;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
ctrl = ADAU1373_DAI_WLEN_24;
|
ctrl = ADAU1373_DAI_WLEN_24;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S32_LE:
|
case 32:
|
||||||
ctrl = ADAU1373_DAI_WLEN_32;
|
ctrl = ADAU1373_DAI_WLEN_32;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
|
|
||||||
#define ADAU1701_SEROCTL_WORD_LEN_24 0x0000
|
#define ADAU1701_SEROCTL_WORD_LEN_24 0x0000
|
||||||
#define ADAU1701_SEROCTL_WORD_LEN_20 0x0001
|
#define ADAU1701_SEROCTL_WORD_LEN_20 0x0001
|
||||||
#define ADAU1701_SEROCTL_WORD_LEN_16 0x0010
|
#define ADAU1701_SEROCTL_WORD_LEN_16 0x0002
|
||||||
#define ADAU1701_SEROCTL_WORD_LEN_MASK 0x0003
|
#define ADAU1701_SEROCTL_WORD_LEN_MASK 0x0003
|
||||||
|
|
||||||
#define ADAU1701_AUXNPOW_VBPD 0x40
|
#define ADAU1701_AUXNPOW_VBPD 0x40
|
||||||
|
@ -299,20 +299,20 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
|
static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
|
||||||
snd_pcm_format_t format)
|
struct snd_pcm_hw_params *params)
|
||||||
{
|
{
|
||||||
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
|
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
|
||||||
unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK;
|
unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK;
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
|
|
||||||
switch (format) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
val = ADAU1701_SEROCTL_WORD_LEN_16;
|
val = ADAU1701_SEROCTL_WORD_LEN_16;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
val = ADAU1701_SEROCTL_WORD_LEN_20;
|
val = ADAU1701_SEROCTL_WORD_LEN_20;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
val = ADAU1701_SEROCTL_WORD_LEN_24;
|
val = ADAU1701_SEROCTL_WORD_LEN_24;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -320,14 +320,14 @@ static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adau1701->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) {
|
if (adau1701->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) {
|
||||||
switch (format) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
val |= ADAU1701_SEROCTL_MSB_DEALY16;
|
val |= ADAU1701_SEROCTL_MSB_DEALY16;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
val |= ADAU1701_SEROCTL_MSB_DEALY12;
|
val |= ADAU1701_SEROCTL_MSB_DEALY12;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
val |= ADAU1701_SEROCTL_MSB_DEALY8;
|
val |= ADAU1701_SEROCTL_MSB_DEALY8;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -340,7 +340,7 @@ static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
|
static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
|
||||||
snd_pcm_format_t format)
|
struct snd_pcm_hw_params *params)
|
||||||
{
|
{
|
||||||
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
|
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
|
@ -348,14 +348,14 @@ static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
|
||||||
if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
|
if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
switch (format) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
val = ADAU1701_SERICTL_RIGHTJ_16;
|
val = ADAU1701_SERICTL_RIGHTJ_16;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
val = ADAU1701_SERICTL_RIGHTJ_20;
|
val = ADAU1701_SERICTL_RIGHTJ_20;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
val = ADAU1701_SERICTL_RIGHTJ_24;
|
val = ADAU1701_SERICTL_RIGHTJ_24;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -374,7 +374,6 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
|
||||||
struct snd_soc_codec *codec = dai->codec;
|
struct snd_soc_codec *codec = dai->codec;
|
||||||
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
|
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
|
||||||
unsigned int clkdiv = adau1701->sysclk / params_rate(params);
|
unsigned int clkdiv = adau1701->sysclk / params_rate(params);
|
||||||
snd_pcm_format_t format;
|
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -406,11 +405,10 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
|
||||||
regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
|
regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
|
||||||
ADAU1701_DSPCTRL_SR_MASK, val);
|
ADAU1701_DSPCTRL_SR_MASK, val);
|
||||||
|
|
||||||
format = params_format(params);
|
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
return adau1701_set_playback_pcm_format(codec, format);
|
return adau1701_set_playback_pcm_format(codec, params);
|
||||||
else
|
else
|
||||||
return adau1701_set_capture_pcm_format(codec, format);
|
return adau1701_set_capture_pcm_format(codec, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||||
|
|
|
@ -453,22 +453,22 @@ static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
|
static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
|
||||||
struct snd_soc_dai *dai, snd_pcm_format_t format)
|
struct snd_soc_dai *dai, struct snd_pcm_hw_params *params)
|
||||||
{
|
{
|
||||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
|
|
||||||
switch (format) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
val = ADAV80X_CAPTURE_WORD_LEN16;
|
val = ADAV80X_CAPTURE_WORD_LEN16;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
case 18:
|
||||||
val = ADAV80X_CAPTRUE_WORD_LEN18;
|
val = ADAV80X_CAPTRUE_WORD_LEN18;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
val = ADAV80X_CAPTURE_WORD_LEN20;
|
val = ADAV80X_CAPTURE_WORD_LEN20;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
val = ADAV80X_CAPTURE_WORD_LEN24;
|
val = ADAV80X_CAPTURE_WORD_LEN24;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -482,7 +482,7 @@ static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
|
static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
|
||||||
struct snd_soc_dai *dai, snd_pcm_format_t format)
|
struct snd_soc_dai *dai, struct snd_pcm_hw_params *params)
|
||||||
{
|
{
|
||||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
|
@ -490,17 +490,17 @@ static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
|
||||||
if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J)
|
if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
switch (format) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16;
|
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
case 18:
|
||||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18;
|
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20;
|
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
|
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -524,12 +524,10 @@ static int adav80x_hw_params(struct snd_pcm_substream *substream,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
adav80x_set_playback_pcm_format(codec, dai,
|
adav80x_set_playback_pcm_format(codec, dai, params);
|
||||||
params_format(params));
|
|
||||||
adav80x_set_dac_clock(codec, rate);
|
adav80x_set_dac_clock(codec, rate);
|
||||||
} else {
|
} else {
|
||||||
adav80x_set_capture_pcm_format(codec, dai,
|
adav80x_set_capture_pcm_format(codec, dai, params);
|
||||||
params_format(params));
|
|
||||||
adav80x_set_adc_clock(codec, rate);
|
adav80x_set_adc_clock(codec, rate);
|
||||||
}
|
}
|
||||||
adav80x->rate = rate;
|
adav80x->rate = rate;
|
||||||
|
|
|
@ -714,17 +714,17 @@ static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
iface &= ~ALC5623_DAI_I2S_DL_MASK;
|
iface &= ~ALC5623_DAI_I2S_DL_MASK;
|
||||||
|
|
||||||
/* bit size */
|
/* bit size */
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
iface |= ALC5623_DAI_I2S_DL_16;
|
iface |= ALC5623_DAI_I2S_DL_16;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
iface |= ALC5623_DAI_I2S_DL_20;
|
iface |= ALC5623_DAI_I2S_DL_20;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
iface |= ALC5623_DAI_I2S_DL_24;
|
iface |= ALC5623_DAI_I2S_DL_24;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S32_LE:
|
case 32:
|
||||||
iface |= ALC5623_DAI_I2S_DL_32;
|
iface |= ALC5623_DAI_I2S_DL_32;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -869,14 +869,14 @@ static int alc5632_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
iface &= ~ALC5632_DAI_I2S_DL_MASK;
|
iface &= ~ALC5632_DAI_I2S_DL_MASK;
|
||||||
|
|
||||||
/* bit size */
|
/* bit size */
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
iface |= ALC5632_DAI_I2S_DL_16;
|
iface |= ALC5632_DAI_I2S_DL_16;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
iface |= ALC5632_DAI_I2S_DL_20;
|
iface |= ALC5632_DAI_I2S_DL_20;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
iface |= ALC5632_DAI_I2S_DL_24;
|
iface |= ALC5632_DAI_I2S_DL_24;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -166,20 +166,21 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
|
||||||
ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
|
ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
|
||||||
|
|
||||||
#define ARIZONA_DSP_ROUTES(name) \
|
#define ARIZONA_DSP_ROUTES(name) \
|
||||||
{ name, NULL, name " Aux 1" }, \
|
{ name, NULL, name " Preloader"}, \
|
||||||
{ name, NULL, name " Aux 2" }, \
|
{ name " Preloader", NULL, name " Aux 1" }, \
|
||||||
{ name, NULL, name " Aux 3" }, \
|
{ name " Preloader", NULL, name " Aux 2" }, \
|
||||||
{ name, NULL, name " Aux 4" }, \
|
{ name " Preloader", NULL, name " Aux 3" }, \
|
||||||
{ name, NULL, name " Aux 5" }, \
|
{ name " Preloader", NULL, name " Aux 4" }, \
|
||||||
{ name, NULL, name " Aux 6" }, \
|
{ name " Preloader", NULL, name " Aux 5" }, \
|
||||||
|
{ name " Preloader", NULL, name " Aux 6" }, \
|
||||||
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \
|
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \
|
||||||
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \
|
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \
|
||||||
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \
|
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \
|
||||||
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \
|
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \
|
||||||
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \
|
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \
|
||||||
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \
|
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \
|
||||||
ARIZONA_MIXER_ROUTES(name, name "L"), \
|
ARIZONA_MIXER_ROUTES(name " Preloader", name "L"), \
|
||||||
ARIZONA_MIXER_ROUTES(name, name "R")
|
ARIZONA_MIXER_ROUTES(name " Preloader", name "R")
|
||||||
|
|
||||||
#define ARIZONA_RATE_ENUM_SIZE 4
|
#define ARIZONA_RATE_ENUM_SIZE 4
|
||||||
extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
|
extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
|
||||||
|
|
|
@ -423,21 +423,17 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
|
||||||
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
|
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
|
||||||
break;
|
break;
|
||||||
case SND_SOC_DAIFMT_RIGHT_J:
|
case SND_SOC_DAIFMT_RIGHT_J:
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
case SNDRV_PCM_FORMAT_S16_BE:
|
|
||||||
fmt = CS42L51_DAC_DIF_RJ16;
|
fmt = CS42L51_DAC_DIF_RJ16;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
case 18:
|
||||||
case SNDRV_PCM_FORMAT_S18_3BE:
|
|
||||||
fmt = CS42L51_DAC_DIF_RJ18;
|
fmt = CS42L51_DAC_DIF_RJ18;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
case SNDRV_PCM_FORMAT_S20_3BE:
|
|
||||||
fmt = CS42L51_DAC_DIF_RJ20;
|
fmt = CS42L51_DAC_DIF_RJ20;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
case SNDRV_PCM_FORMAT_S24_BE:
|
|
||||||
fmt = CS42L51_DAC_DIF_RJ24;
|
fmt = CS42L51_DAC_DIF_RJ24;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -778,17 +778,17 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
|
||||||
dai_cfg1 = 0xFC & snd_soc_read(codec, DA7210_DAI_CFG1);
|
dai_cfg1 = 0xFC & snd_soc_read(codec, DA7210_DAI_CFG1);
|
||||||
|
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
dai_cfg1 |= DA7210_DAI_WORD_S16_LE;
|
dai_cfg1 |= DA7210_DAI_WORD_S16_LE;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
dai_cfg1 |= DA7210_DAI_WORD_S20_3LE;
|
dai_cfg1 |= DA7210_DAI_WORD_S20_3LE;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
dai_cfg1 |= DA7210_DAI_WORD_S24_LE;
|
dai_cfg1 |= DA7210_DAI_WORD_S24_LE;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S32_LE:
|
case 32:
|
||||||
dai_cfg1 |= DA7210_DAI_WORD_S32_LE;
|
dai_cfg1 |= DA7210_DAI_WORD_S32_LE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1067,17 +1067,17 @@ static int da7213_hw_params(struct snd_pcm_substream *substream,
|
||||||
u8 fs;
|
u8 fs;
|
||||||
|
|
||||||
/* Set DAI format */
|
/* Set DAI format */
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S16_LE;
|
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S16_LE;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S20_LE;
|
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S20_LE;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S24_LE;
|
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S24_LE;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S32_LE:
|
case 32:
|
||||||
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S32_LE;
|
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S32_LE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -973,17 +973,17 @@ static int da732x_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
|
||||||
reg_aif = dai->driver->base;
|
reg_aif = dai->driver->base;
|
||||||
|
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
aif |= DA732X_AIF_WORD_16;
|
aif |= DA732X_AIF_WORD_16;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
aif |= DA732X_AIF_WORD_20;
|
aif |= DA732X_AIF_WORD_20;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
aif |= DA732X_AIF_WORD_24;
|
aif |= DA732X_AIF_WORD_24;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S32_LE:
|
case 32:
|
||||||
aif |= DA732X_AIF_WORD_32;
|
aif |= DA732X_AIF_WORD_32;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1058,17 +1058,17 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
|
||||||
u8 aif_ctrl, fs;
|
u8 aif_ctrl, fs;
|
||||||
u32 sysclk;
|
u32 sysclk;
|
||||||
|
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
aif_ctrl = DA9055_AIF_WORD_S16_LE;
|
aif_ctrl = DA9055_AIF_WORD_S16_LE;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
aif_ctrl = DA9055_AIF_WORD_S20_3LE;
|
aif_ctrl = DA9055_AIF_WORD_S20_3LE;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
aif_ctrl = DA9055_AIF_WORD_S24_LE;
|
aif_ctrl = DA9055_AIF_WORD_S24_LE;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S32_LE:
|
case 32:
|
||||||
aif_ctrl = DA9055_AIF_WORD_S32_LE;
|
aif_ctrl = DA9055_AIF_WORD_S32_LE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -951,11 +951,11 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
|
||||||
ISABELLE_FS_RATE_MASK, fs_val);
|
ISABELLE_FS_RATE_MASK, fs_val);
|
||||||
|
|
||||||
/* bit size */
|
/* bit size */
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
aif |= ISABELLE_AIF_LENGTH_20;
|
aif |= ISABELLE_AIF_LENGTH_20;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S32_LE:
|
case 32:
|
||||||
aif |= ISABELLE_AIF_LENGTH_32;
|
aif |= ISABELLE_AIF_LENGTH_32;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1233,12 +1233,12 @@ static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
|
||||||
rate = params_rate(params);
|
rate = params_rate(params);
|
||||||
|
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
|
snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
|
||||||
M98088_DAI_WS, 0);
|
M98088_DAI_WS, 0);
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
|
snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
|
||||||
M98088_DAI_WS, M98088_DAI_WS);
|
M98088_DAI_WS, M98088_DAI_WS);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1840,8 +1840,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
|
||||||
max98090->lrclk = params_rate(params);
|
max98090->lrclk = params_rate(params);
|
||||||
|
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
snd_soc_update_bits(codec, M98090_REG_INTERFACE_FORMAT,
|
snd_soc_update_bits(codec, M98090_REG_INTERFACE_FORMAT,
|
||||||
M98090_WS_MASK, 0);
|
M98090_WS_MASK, 0);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1213,12 +1213,12 @@ static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
|
||||||
rate = params_rate(params);
|
rate = params_rate(params);
|
||||||
|
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
|
snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
|
||||||
M98095_DAI_WS, 0);
|
M98095_DAI_WS, 0);
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
|
snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
|
||||||
M98095_DAI_WS, M98095_DAI_WS);
|
M98095_DAI_WS, M98095_DAI_WS);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -149,14 +149,14 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
|
||||||
snd_soc_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f);
|
snd_soc_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f);
|
||||||
snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff);
|
snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff);
|
||||||
|
|
||||||
switch (params_format(params)) {
|
switch (params_width(params)) {
|
||||||
case SNDRV_PCM_FORMAT_S16_LE:
|
case 16:
|
||||||
da = 0;
|
da = 0;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
case 20:
|
||||||
da = 0x2;
|
da = 0x2;
|
||||||
break;
|
break;
|
||||||
case SNDRV_PCM_FORMAT_S24_LE:
|
case 24:
|
||||||
da = 0x3;
|
da = 0x3;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -750,30 +750,26 @@ static struct snd_soc_codec_driver soc_codec_dev_mc13783 = {
|
||||||
.num_dapm_routes = ARRAY_SIZE(mc13783_routes),
|
.num_dapm_routes = ARRAY_SIZE(mc13783_routes),
|
||||||
};
|
};
|
||||||
|
|
||||||
static int mc13783_codec_probe(struct platform_device *pdev)
|
static int __init mc13783_codec_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct mc13xxx *mc13xxx;
|
|
||||||
struct mc13783_priv *priv;
|
struct mc13783_priv *priv;
|
||||||
struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
|
struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mc13xxx = dev_get_drvdata(pdev->dev.parent);
|
|
||||||
|
|
||||||
|
|
||||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
if (priv == NULL)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
dev_set_drvdata(&pdev->dev, priv);
|
|
||||||
priv->mc13xxx = mc13xxx;
|
|
||||||
if (pdata) {
|
if (pdata) {
|
||||||
priv->adc_ssi_port = pdata->adc_ssi_port;
|
priv->adc_ssi_port = pdata->adc_ssi_port;
|
||||||
priv->dac_ssi_port = pdata->dac_ssi_port;
|
priv->dac_ssi_port = pdata->dac_ssi_port;
|
||||||
} else {
|
} else {
|
||||||
priv->adc_ssi_port = MC13783_SSI1_PORT;
|
return -ENOSYS;
|
||||||
priv->dac_ssi_port = MC13783_SSI2_PORT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev_set_drvdata(&pdev->dev, priv);
|
||||||
|
priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
|
||||||
if (priv->adc_ssi_port == priv->dac_ssi_port)
|
if (priv->adc_ssi_port == priv->dac_ssi_port)
|
||||||
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
|
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
|
||||||
mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync));
|
mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync));
|
||||||
|
@ -781,14 +777,6 @@ static int mc13783_codec_probe(struct platform_device *pdev)
|
||||||
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
|
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
|
||||||
mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
|
mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
|
||||||
|
|
||||||
if (ret)
|
|
||||||
goto err_register_codec;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_register_codec:
|
|
||||||
dev_err(&pdev->dev, "register codec failed with %d\n", ret);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -804,11 +792,9 @@ static struct platform_driver mc13783_codec_driver = {
|
||||||
.name = "mc13783-codec",
|
.name = "mc13783-codec",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
},
|
},
|
||||||
.probe = mc13783_codec_probe,
|
|
||||||
.remove = mc13783_codec_remove,
|
.remove = mc13783_codec_remove,
|
||||||
};
|
};
|
||||||
|
module_platform_driver_probe(mc13783_codec_driver, mc13783_codec_probe);
|
||||||
module_platform_driver(mc13783_codec_driver);
|
|
||||||
|
|
||||||
MODULE_DESCRIPTION("ASoC MC13783 driver");
|
MODULE_DESCRIPTION("ASoC MC13783 driver");
|
||||||
MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>");
|
MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>");
|
||||||
|
|
|
@ -194,7 +194,7 @@ static const struct snd_soc_dapm_route ssm2604_routes[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const unsigned int ssm2602_rates_12288000[] = {
|
static const unsigned int ssm2602_rates_12288000[] = {
|
||||||
8000, 32000, 48000, 96000,
|
8000, 16000, 32000, 48000, 96000,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = {
|
static struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = {
|
||||||
|
@ -231,6 +231,11 @@ static const struct ssm2602_coeff ssm2602_coeff_table[] = {
|
||||||
{18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)},
|
{18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)},
|
||||||
{12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
|
{12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
|
||||||
|
|
||||||
|
/* 16k */
|
||||||
|
{12288000, 16000, SSM2602_COEFF_SRATE(0x5, 0x0, 0x0)},
|
||||||
|
{18432000, 16000, SSM2602_COEFF_SRATE(0x5, 0x1, 0x0)},
|
||||||
|
{12000000, 16000, SSM2602_COEFF_SRATE(0xa, 0x0, 0x1)},
|
||||||
|
|
||||||
/* 8k */
|
/* 8k */
|
||||||
{12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)},
|
{12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)},
|
||||||
{18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)},
|
{18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)},
|
||||||
|
@ -473,9 +478,10 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_32000 |\
|
#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
|
||||||
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
|
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
||||||
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
|
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
|
||||||
|
SNDRV_PCM_RATE_96000)
|
||||||
|
|
||||||
#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||||
|
|
|
@ -267,8 +267,8 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = {
|
||||||
.selector_mask = 0xff,
|
.selector_mask = 0xff,
|
||||||
.window_start = 0,
|
.window_start = 0,
|
||||||
.window_len = 128,
|
.window_len = 128,
|
||||||
.range_min = AIC32X4_PAGE1,
|
.range_min = 0,
|
||||||
.range_max = AIC32X4_PAGE1 + 127,
|
.range_max = AIC32X4_RMICPGAVOL,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -48,86 +48,6 @@
|
||||||
|
|
||||||
#define TWL4030_CACHEREGNUM (TWL4030_REG_MISC_SET_2 + 1)
|
#define TWL4030_CACHEREGNUM (TWL4030_REG_MISC_SET_2 + 1)
|
||||||
|
|
||||||
/*
|
|
||||||
* twl4030 register cache & default register settings
|
|
||||||
*/
|
|
||||||
static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
|
|
||||||
0x00, /* this register not used */
|
|
||||||
0x00, /* REG_CODEC_MODE (0x1) */
|
|
||||||
0x00, /* REG_OPTION (0x2) */
|
|
||||||
0x00, /* REG_UNKNOWN (0x3) */
|
|
||||||
0x00, /* REG_MICBIAS_CTL (0x4) */
|
|
||||||
0x00, /* REG_ANAMICL (0x5) */
|
|
||||||
0x00, /* REG_ANAMICR (0x6) */
|
|
||||||
0x00, /* REG_AVADC_CTL (0x7) */
|
|
||||||
0x00, /* REG_ADCMICSEL (0x8) */
|
|
||||||
0x00, /* REG_DIGMIXING (0x9) */
|
|
||||||
0x0f, /* REG_ATXL1PGA (0xA) */
|
|
||||||
0x0f, /* REG_ATXR1PGA (0xB) */
|
|
||||||
0x0f, /* REG_AVTXL2PGA (0xC) */
|
|
||||||
0x0f, /* REG_AVTXR2PGA (0xD) */
|
|
||||||
0x00, /* REG_AUDIO_IF (0xE) */
|
|
||||||
0x00, /* REG_VOICE_IF (0xF) */
|
|
||||||
0x3f, /* REG_ARXR1PGA (0x10) */
|
|
||||||
0x3f, /* REG_ARXL1PGA (0x11) */
|
|
||||||
0x3f, /* REG_ARXR2PGA (0x12) */
|
|
||||||
0x3f, /* REG_ARXL2PGA (0x13) */
|
|
||||||
0x25, /* REG_VRXPGA (0x14) */
|
|
||||||
0x00, /* REG_VSTPGA (0x15) */
|
|
||||||
0x00, /* REG_VRX2ARXPGA (0x16) */
|
|
||||||
0x00, /* REG_AVDAC_CTL (0x17) */
|
|
||||||
0x00, /* REG_ARX2VTXPGA (0x18) */
|
|
||||||
0x32, /* REG_ARXL1_APGA_CTL (0x19) */
|
|
||||||
0x32, /* REG_ARXR1_APGA_CTL (0x1A) */
|
|
||||||
0x32, /* REG_ARXL2_APGA_CTL (0x1B) */
|
|
||||||
0x32, /* REG_ARXR2_APGA_CTL (0x1C) */
|
|
||||||
0x00, /* REG_ATX2ARXPGA (0x1D) */
|
|
||||||
0x00, /* REG_BT_IF (0x1E) */
|
|
||||||
0x55, /* REG_BTPGA (0x1F) */
|
|
||||||
0x00, /* REG_BTSTPGA (0x20) */
|
|
||||||
0x00, /* REG_EAR_CTL (0x21) */
|
|
||||||
0x00, /* REG_HS_SEL (0x22) */
|
|
||||||
0x00, /* REG_HS_GAIN_SET (0x23) */
|
|
||||||
0x00, /* REG_HS_POPN_SET (0x24) */
|
|
||||||
0x00, /* REG_PREDL_CTL (0x25) */
|
|
||||||
0x00, /* REG_PREDR_CTL (0x26) */
|
|
||||||
0x00, /* REG_PRECKL_CTL (0x27) */
|
|
||||||
0x00, /* REG_PRECKR_CTL (0x28) */
|
|
||||||
0x00, /* REG_HFL_CTL (0x29) */
|
|
||||||
0x00, /* REG_HFR_CTL (0x2A) */
|
|
||||||
0x05, /* REG_ALC_CTL (0x2B) */
|
|
||||||
0x00, /* REG_ALC_SET1 (0x2C) */
|
|
||||||
0x00, /* REG_ALC_SET2 (0x2D) */
|
|
||||||
0x00, /* REG_BOOST_CTL (0x2E) */
|
|
||||||
0x00, /* REG_SOFTVOL_CTL (0x2F) */
|
|
||||||
0x13, /* REG_DTMF_FREQSEL (0x30) */
|
|
||||||
0x00, /* REG_DTMF_TONEXT1H (0x31) */
|
|
||||||
0x00, /* REG_DTMF_TONEXT1L (0x32) */
|
|
||||||
0x00, /* REG_DTMF_TONEXT2H (0x33) */
|
|
||||||
0x00, /* REG_DTMF_TONEXT2L (0x34) */
|
|
||||||
0x79, /* REG_DTMF_TONOFF (0x35) */
|
|
||||||
0x11, /* REG_DTMF_WANONOFF (0x36) */
|
|
||||||
0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */
|
|
||||||
0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */
|
|
||||||
0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */
|
|
||||||
0x06, /* REG_APLL_CTL (0x3A) */
|
|
||||||
0x00, /* REG_DTMF_CTL (0x3B) */
|
|
||||||
0x44, /* REG_DTMF_PGA_CTL2 (0x3C) */
|
|
||||||
0x69, /* REG_DTMF_PGA_CTL1 (0x3D) */
|
|
||||||
0x00, /* REG_MISC_SET_1 (0x3E) */
|
|
||||||
0x00, /* REG_PCMBTMUX (0x3F) */
|
|
||||||
0x00, /* not used (0x40) */
|
|
||||||
0x00, /* not used (0x41) */
|
|
||||||
0x00, /* not used (0x42) */
|
|
||||||
0x00, /* REG_RX_PATH_SEL (0x43) */
|
|
||||||
0x32, /* REG_VDL_APGA_CTL (0x44) */
|
|
||||||
0x00, /* REG_VIBRA_CTL (0x45) */
|
|
||||||
0x00, /* REG_VIBRA_SET (0x46) */
|
|
||||||
0x00, /* REG_VIBRA_PWM_SET (0x47) */
|
|
||||||
0x00, /* REG_ANAMIC_GAIN (0x48) */
|
|
||||||
0x00, /* REG_MISC_SET_2 (0x49) */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* codec private data */
|
/* codec private data */
|
||||||
struct twl4030_priv {
|
struct twl4030_priv {
|
||||||
unsigned int codec_powered;
|
unsigned int codec_powered;
|
||||||
|
@ -150,81 +70,108 @@ struct twl4030_priv {
|
||||||
u8 earpiece_enabled;
|
u8 earpiece_enabled;
|
||||||
u8 predrivel_enabled, predriver_enabled;
|
u8 predrivel_enabled, predriver_enabled;
|
||||||
u8 carkitl_enabled, carkitr_enabled;
|
u8 carkitl_enabled, carkitr_enabled;
|
||||||
|
u8 ctl_cache[TWL4030_REG_PRECKR_CTL - TWL4030_REG_EAR_CTL + 1];
|
||||||
|
|
||||||
struct twl4030_codec_data *pdata;
|
struct twl4030_codec_data *pdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
static void tw4030_init_ctl_cache(struct twl4030_priv *twl4030)
|
||||||
* read twl4030 register cache
|
|
||||||
*/
|
|
||||||
static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec,
|
|
||||||
unsigned int reg)
|
|
||||||
{
|
{
|
||||||
u8 *cache = codec->reg_cache;
|
int i;
|
||||||
|
u8 byte;
|
||||||
|
|
||||||
|
for (i = TWL4030_REG_EAR_CTL; i <= TWL4030_REG_PRECKR_CTL; i++) {
|
||||||
|
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, i);
|
||||||
|
twl4030->ctl_cache[i - TWL4030_REG_EAR_CTL] = byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int twl4030_read(struct snd_soc_codec *codec, unsigned int reg)
|
||||||
|
{
|
||||||
|
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||||
|
u8 value = 0;
|
||||||
|
|
||||||
if (reg >= TWL4030_CACHEREGNUM)
|
if (reg >= TWL4030_CACHEREGNUM)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
return cache[reg];
|
switch (reg) {
|
||||||
|
case TWL4030_REG_EAR_CTL:
|
||||||
|
case TWL4030_REG_PREDL_CTL:
|
||||||
|
case TWL4030_REG_PREDR_CTL:
|
||||||
|
case TWL4030_REG_PRECKL_CTL:
|
||||||
|
case TWL4030_REG_PRECKR_CTL:
|
||||||
|
case TWL4030_REG_HS_GAIN_SET:
|
||||||
|
value = twl4030->ctl_cache[reg - TWL4030_REG_EAR_CTL];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
return value;
|
||||||
* write twl4030 register cache
|
|
||||||
*/
|
|
||||||
static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec,
|
|
||||||
u8 reg, u8 value)
|
|
||||||
{
|
|
||||||
u8 *cache = codec->reg_cache;
|
|
||||||
|
|
||||||
if (reg >= TWL4030_CACHEREGNUM)
|
|
||||||
return;
|
|
||||||
cache[reg] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static bool twl4030_can_write_to_chip(struct twl4030_priv *twl4030,
|
||||||
* write to the twl4030 register space
|
unsigned int reg)
|
||||||
*/
|
|
||||||
static int twl4030_write(struct snd_soc_codec *codec,
|
|
||||||
unsigned int reg, unsigned int value)
|
|
||||||
{
|
{
|
||||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
bool write_to_reg = false;
|
||||||
int write_to_reg = 0;
|
|
||||||
|
|
||||||
twl4030_write_reg_cache(codec, reg, value);
|
|
||||||
/* Decide if the given register can be written */
|
/* Decide if the given register can be written */
|
||||||
switch (reg) {
|
switch (reg) {
|
||||||
case TWL4030_REG_EAR_CTL:
|
case TWL4030_REG_EAR_CTL:
|
||||||
if (twl4030->earpiece_enabled)
|
if (twl4030->earpiece_enabled)
|
||||||
write_to_reg = 1;
|
write_to_reg = true;
|
||||||
break;
|
break;
|
||||||
case TWL4030_REG_PREDL_CTL:
|
case TWL4030_REG_PREDL_CTL:
|
||||||
if (twl4030->predrivel_enabled)
|
if (twl4030->predrivel_enabled)
|
||||||
write_to_reg = 1;
|
write_to_reg = true;
|
||||||
break;
|
break;
|
||||||
case TWL4030_REG_PREDR_CTL:
|
case TWL4030_REG_PREDR_CTL:
|
||||||
if (twl4030->predriver_enabled)
|
if (twl4030->predriver_enabled)
|
||||||
write_to_reg = 1;
|
write_to_reg = true;
|
||||||
break;
|
break;
|
||||||
case TWL4030_REG_PRECKL_CTL:
|
case TWL4030_REG_PRECKL_CTL:
|
||||||
if (twl4030->carkitl_enabled)
|
if (twl4030->carkitl_enabled)
|
||||||
write_to_reg = 1;
|
write_to_reg = true;
|
||||||
break;
|
break;
|
||||||
case TWL4030_REG_PRECKR_CTL:
|
case TWL4030_REG_PRECKR_CTL:
|
||||||
if (twl4030->carkitr_enabled)
|
if (twl4030->carkitr_enabled)
|
||||||
write_to_reg = 1;
|
write_to_reg = true;
|
||||||
break;
|
break;
|
||||||
case TWL4030_REG_HS_GAIN_SET:
|
case TWL4030_REG_HS_GAIN_SET:
|
||||||
if (twl4030->hsl_enabled || twl4030->hsr_enabled)
|
if (twl4030->hsl_enabled || twl4030->hsr_enabled)
|
||||||
write_to_reg = 1;
|
write_to_reg = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* All other register can be written */
|
/* All other register can be written */
|
||||||
write_to_reg = 1;
|
write_to_reg = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (write_to_reg)
|
|
||||||
return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
|
return write_to_reg;
|
||||||
value, reg);
|
}
|
||||||
|
|
||||||
|
static int twl4030_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||||
|
unsigned int value)
|
||||||
|
{
|
||||||
|
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||||
|
|
||||||
|
/* Update the ctl cache */
|
||||||
|
switch (reg) {
|
||||||
|
case TWL4030_REG_EAR_CTL:
|
||||||
|
case TWL4030_REG_PREDL_CTL:
|
||||||
|
case TWL4030_REG_PREDR_CTL:
|
||||||
|
case TWL4030_REG_PRECKL_CTL:
|
||||||
|
case TWL4030_REG_PRECKR_CTL:
|
||||||
|
case TWL4030_REG_HS_GAIN_SET:
|
||||||
|
twl4030->ctl_cache[reg - TWL4030_REG_EAR_CTL] = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (twl4030_can_write_to_chip(twl4030, reg))
|
||||||
|
return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -252,46 +199,14 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
|
||||||
else
|
else
|
||||||
mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
|
mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
|
||||||
|
|
||||||
if (mode >= 0) {
|
if (mode >= 0)
|
||||||
twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode);
|
|
||||||
twl4030->codec_powered = enable;
|
twl4030->codec_powered = enable;
|
||||||
}
|
|
||||||
|
|
||||||
/* REVISIT: this delay is present in TI sample drivers */
|
/* REVISIT: this delay is present in TI sample drivers */
|
||||||
/* but there seems to be no TRM requirement for it */
|
/* but there seems to be no TRM requirement for it */
|
||||||
udelay(10);
|
udelay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void twl4030_check_defaults(struct snd_soc_codec *codec)
|
|
||||||
{
|
|
||||||
int i, difference = 0;
|
|
||||||
u8 val;
|
|
||||||
|
|
||||||
dev_dbg(codec->dev, "Checking TWL audio default configuration\n");
|
|
||||||
for (i = 1; i <= TWL4030_REG_MISC_SET_2; i++) {
|
|
||||||
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, i);
|
|
||||||
if (val != twl4030_reg[i]) {
|
|
||||||
difference++;
|
|
||||||
dev_dbg(codec->dev,
|
|
||||||
"Reg 0x%02x: chip: 0x%02x driver: 0x%02x\n",
|
|
||||||
i, val, twl4030_reg[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dev_dbg(codec->dev, "Found %d non-matching registers. %s\n",
|
|
||||||
difference, difference ? "Not OK" : "OK");
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void twl4030_reset_registers(struct snd_soc_codec *codec)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* set all audio section registers to reasonable defaults */
|
|
||||||
for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
|
|
||||||
if (i != TWL4030_REG_APLL_CTL)
|
|
||||||
twl4030_write(codec, i, twl4030_reg[i]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void twl4030_setup_pdata_of(struct twl4030_codec_data *pdata,
|
static void twl4030_setup_pdata_of(struct twl4030_codec_data *pdata,
|
||||||
struct device_node *node)
|
struct device_node *node)
|
||||||
{
|
{
|
||||||
|
@ -372,21 +287,11 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check defaults, if instructed before anything else */
|
/* Initialize the local ctl register cache */
|
||||||
if (pdata && pdata->check_defaults)
|
tw4030_init_ctl_cache(twl4030);
|
||||||
twl4030_check_defaults(codec);
|
|
||||||
|
|
||||||
/* Reset registers, if no setup data or if instructed to do so */
|
|
||||||
if (!pdata || (pdata && pdata->reset_registers))
|
|
||||||
twl4030_reset_registers(codec);
|
|
||||||
|
|
||||||
/* Refresh APLL_CTL register from HW */
|
|
||||||
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
|
|
||||||
TWL4030_REG_APLL_CTL);
|
|
||||||
twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, byte);
|
|
||||||
|
|
||||||
/* anti-pop when changing analog gain */
|
/* anti-pop when changing analog gain */
|
||||||
reg = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
|
reg = twl4030_read(codec, TWL4030_REG_MISC_SET_1);
|
||||||
twl4030_write(codec, TWL4030_REG_MISC_SET_1,
|
twl4030_write(codec, TWL4030_REG_MISC_SET_1,
|
||||||
reg | TWL4030_SMOOTH_ANAVOL_EN);
|
reg | TWL4030_SMOOTH_ANAVOL_EN);
|
||||||
|
|
||||||
|
@ -403,15 +308,15 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
|
||||||
|
|
||||||
twl4030->pdata = pdata;
|
twl4030->pdata = pdata;
|
||||||
|
|
||||||
reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
|
reg = twl4030_read(codec, TWL4030_REG_HS_POPN_SET);
|
||||||
reg &= ~TWL4030_RAMP_DELAY;
|
reg &= ~TWL4030_RAMP_DELAY;
|
||||||
reg |= (pdata->ramp_delay_value << 2);
|
reg |= (pdata->ramp_delay_value << 2);
|
||||||
twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg);
|
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, reg);
|
||||||
|
|
||||||
/* initiate offset cancellation */
|
/* initiate offset cancellation */
|
||||||
twl4030_codec_enable(codec, 1);
|
twl4030_codec_enable(codec, 1);
|
||||||
|
|
||||||
reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
|
reg = twl4030_read(codec, TWL4030_REG_ANAMICL);
|
||||||
reg &= ~TWL4030_OFFSET_CNCL_SEL;
|
reg &= ~TWL4030_OFFSET_CNCL_SEL;
|
||||||
reg |= pdata->offset_cncl_path;
|
reg |= pdata->offset_cncl_path;
|
||||||
twl4030_write(codec, TWL4030_REG_ANAMICL,
|
twl4030_write(codec, TWL4030_REG_ANAMICL,
|
||||||
|
@ -425,15 +330,14 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
|
||||||
msleep(20);
|
msleep(20);
|
||||||
do {
|
do {
|
||||||
usleep_range(1000, 2000);
|
usleep_range(1000, 2000);
|
||||||
|
twl_set_regcache_bypass(TWL4030_MODULE_AUDIO_VOICE, true);
|
||||||
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
|
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
|
||||||
TWL4030_REG_ANAMICL);
|
TWL4030_REG_ANAMICL);
|
||||||
|
twl_set_regcache_bypass(TWL4030_MODULE_AUDIO_VOICE, false);
|
||||||
} while ((i++ < 100) &&
|
} while ((i++ < 100) &&
|
||||||
((byte & TWL4030_CNCL_OFFSET_START) ==
|
((byte & TWL4030_CNCL_OFFSET_START) ==
|
||||||
TWL4030_CNCL_OFFSET_START));
|
TWL4030_CNCL_OFFSET_START));
|
||||||
|
|
||||||
/* Make sure that the reg_cache has the same value as the HW */
|
|
||||||
twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte);
|
|
||||||
|
|
||||||
twl4030_codec_enable(codec, 0);
|
twl4030_codec_enable(codec, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,9 +357,6 @@ static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
|
||||||
status = twl4030_audio_disable_resource(
|
status = twl4030_audio_disable_resource(
|
||||||
TWL4030_AUDIO_RES_APLL);
|
TWL4030_AUDIO_RES_APLL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status >= 0)
|
|
||||||
twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Earpiece */
|
/* Earpiece */
|
||||||
|
@ -678,13 +579,11 @@ static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \
|
||||||
switch (event) { \
|
switch (event) { \
|
||||||
case SND_SOC_DAPM_POST_PMU: \
|
case SND_SOC_DAPM_POST_PMU: \
|
||||||
twl4030->pin_name##_enabled = 1; \
|
twl4030->pin_name##_enabled = 1; \
|
||||||
twl4030_write(w->codec, reg, \
|
twl4030_write(w->codec, reg, twl4030_read(w->codec, reg)); \
|
||||||
twl4030_read_reg_cache(w->codec, reg)); \
|
|
||||||
break; \
|
break; \
|
||||||
case SND_SOC_DAPM_POST_PMD: \
|
case SND_SOC_DAPM_POST_PMD: \
|
||||||
twl4030->pin_name##_enabled = 0; \
|
twl4030->pin_name##_enabled = 0; \
|
||||||
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \
|
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 0, reg); \
|
||||||
0, reg); \
|
|
||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
return 0; \
|
return 0; \
|
||||||
|
@ -700,7 +599,7 @@ static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp)
|
||||||
{
|
{
|
||||||
unsigned char hs_ctl;
|
unsigned char hs_ctl;
|
||||||
|
|
||||||
hs_ctl = twl4030_read_reg_cache(codec, reg);
|
hs_ctl = twl4030_read(codec, reg);
|
||||||
|
|
||||||
if (ramp) {
|
if (ramp) {
|
||||||
/* HF ramp-up */
|
/* HF ramp-up */
|
||||||
|
@ -780,7 +679,7 @@ static int aif_event(struct snd_soc_dapm_widget *w,
|
||||||
{
|
{
|
||||||
u8 audio_if;
|
u8 audio_if;
|
||||||
|
|
||||||
audio_if = twl4030_read_reg_cache(w->codec, TWL4030_REG_AUDIO_IF);
|
audio_if = twl4030_read(w->codec, TWL4030_REG_AUDIO_IF);
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case SND_SOC_DAPM_PRE_PMU:
|
case SND_SOC_DAPM_PRE_PMU:
|
||||||
/* Enable AIF */
|
/* Enable AIF */
|
||||||
|
@ -810,8 +709,8 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
|
||||||
8388608, 16777216, 33554432, 67108864};
|
8388608, 16777216, 33554432, 67108864};
|
||||||
unsigned int delay;
|
unsigned int delay;
|
||||||
|
|
||||||
hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET);
|
hs_gain = twl4030_read(codec, TWL4030_REG_HS_GAIN_SET);
|
||||||
hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
|
hs_pop = twl4030_read(codec, TWL4030_REG_HS_POPN_SET);
|
||||||
delay = (ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
|
delay = (ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
|
||||||
twl4030->sysclk) + 1;
|
twl4030->sysclk) + 1;
|
||||||
|
|
||||||
|
@ -831,8 +730,7 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
|
||||||
hs_pop |= TWL4030_VMID_EN;
|
hs_pop |= TWL4030_VMID_EN;
|
||||||
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
|
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
|
||||||
/* Actually write to the register */
|
/* Actually write to the register */
|
||||||
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
|
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain,
|
||||||
hs_gain,
|
|
||||||
TWL4030_REG_HS_GAIN_SET);
|
TWL4030_REG_HS_GAIN_SET);
|
||||||
hs_pop |= TWL4030_RAMP_EN;
|
hs_pop |= TWL4030_RAMP_EN;
|
||||||
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
|
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
|
||||||
|
@ -846,8 +744,7 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
|
||||||
/* Wait ramp delay time + 1, so the VMID can settle */
|
/* Wait ramp delay time + 1, so the VMID can settle */
|
||||||
twl4030_wait_ms(delay);
|
twl4030_wait_ms(delay);
|
||||||
/* Bypass the reg_cache to mute the headset */
|
/* Bypass the reg_cache to mute the headset */
|
||||||
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
|
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain & (~0x0f),
|
||||||
hs_gain & (~0x0f),
|
|
||||||
TWL4030_REG_HS_GAIN_SET);
|
TWL4030_REG_HS_GAIN_SET);
|
||||||
|
|
||||||
hs_pop &= ~TWL4030_VMID_EN;
|
hs_pop &= ~TWL4030_VMID_EN;
|
||||||
|
@ -1755,7 +1652,7 @@ static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction,
|
||||||
{
|
{
|
||||||
u8 reg, mask;
|
u8 reg, mask;
|
||||||
|
|
||||||
reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION);
|
reg = twl4030_read(codec, TWL4030_REG_OPTION);
|
||||||
|
|
||||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN;
|
mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN;
|
||||||
|
@ -1784,7 +1681,7 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
|
||||||
if (twl4030->configured)
|
if (twl4030->configured)
|
||||||
twl4030_constraints(twl4030, twl4030->master_substream);
|
twl4030_constraints(twl4030, twl4030->master_substream);
|
||||||
} else {
|
} else {
|
||||||
if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) &
|
if (!(twl4030_read(codec, TWL4030_REG_CODEC_MODE) &
|
||||||
TWL4030_OPTION_1)) {
|
TWL4030_OPTION_1)) {
|
||||||
/* In option2 4 channel is not supported, set the
|
/* In option2 4 channel is not supported, set the
|
||||||
* constraint for the first stream for channels, the
|
* constraint for the first stream for channels, the
|
||||||
|
@ -1832,8 +1729,8 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
|
||||||
/* If the substream has 4 channel, do the necessary setup */
|
/* If the substream has 4 channel, do the necessary setup */
|
||||||
if (params_channels(params) == 4) {
|
if (params_channels(params) == 4) {
|
||||||
format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
|
format = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
|
||||||
mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
|
mode = twl4030_read(codec, TWL4030_REG_CODEC_MODE);
|
||||||
|
|
||||||
/* Safety check: are we in the correct operating mode and
|
/* Safety check: are we in the correct operating mode and
|
||||||
* the interface is in TDM mode? */
|
* the interface is in TDM mode? */
|
||||||
|
@ -1849,7 +1746,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* bit rate */
|
/* bit rate */
|
||||||
old_mode = twl4030_read_reg_cache(codec,
|
old_mode = twl4030_read(codec,
|
||||||
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
|
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
|
||||||
mode = old_mode & ~TWL4030_APLL_RATE;
|
mode = old_mode & ~TWL4030_APLL_RATE;
|
||||||
|
|
||||||
|
@ -1891,7 +1788,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sample size */
|
/* sample size */
|
||||||
old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
|
old_format = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
|
||||||
format = old_format;
|
format = old_format;
|
||||||
format &= ~TWL4030_DATA_WIDTH;
|
format &= ~TWL4030_DATA_WIDTH;
|
||||||
switch (params_format(params)) {
|
switch (params_format(params)) {
|
||||||
|
@ -1940,8 +1837,8 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
|
||||||
int clk_id, unsigned int freq, int dir)
|
unsigned int freq, int dir)
|
||||||
{
|
{
|
||||||
struct snd_soc_codec *codec = codec_dai->codec;
|
struct snd_soc_codec *codec = codec_dai->codec;
|
||||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||||
|
@ -1966,15 +1863,14 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
||||||
unsigned int fmt)
|
|
||||||
{
|
{
|
||||||
struct snd_soc_codec *codec = codec_dai->codec;
|
struct snd_soc_codec *codec = codec_dai->codec;
|
||||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||||
u8 old_format, format;
|
u8 old_format, format;
|
||||||
|
|
||||||
/* get format */
|
/* get format */
|
||||||
old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
|
old_format = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
|
||||||
format = old_format;
|
format = old_format;
|
||||||
|
|
||||||
/* set master/slave audio interface */
|
/* set master/slave audio interface */
|
||||||
|
@ -2024,7 +1920,7 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||||
static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate)
|
static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate)
|
||||||
{
|
{
|
||||||
struct snd_soc_codec *codec = dai->codec;
|
struct snd_soc_codec *codec = dai->codec;
|
||||||
u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
|
u8 reg = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
|
||||||
|
|
||||||
if (tristate)
|
if (tristate)
|
||||||
reg |= TWL4030_AIF_TRI_EN;
|
reg |= TWL4030_AIF_TRI_EN;
|
||||||
|
@ -2041,7 +1937,7 @@ static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction,
|
||||||
{
|
{
|
||||||
u8 reg, mask;
|
u8 reg, mask;
|
||||||
|
|
||||||
reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION);
|
reg = twl4030_read(codec, TWL4030_REG_OPTION);
|
||||||
|
|
||||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
mask = TWL4030_ARXL1_VRX_EN;
|
mask = TWL4030_ARXL1_VRX_EN;
|
||||||
|
@ -2076,7 +1972,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream,
|
||||||
/* If the codec mode is not option2, the voice PCM interface is not
|
/* If the codec mode is not option2, the voice PCM interface is not
|
||||||
* available.
|
* available.
|
||||||
*/
|
*/
|
||||||
mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
|
mode = twl4030_read(codec, TWL4030_REG_CODEC_MODE)
|
||||||
& TWL4030_OPT_MODE;
|
& TWL4030_OPT_MODE;
|
||||||
|
|
||||||
if (mode != TWL4030_OPTION_2) {
|
if (mode != TWL4030_OPTION_2) {
|
||||||
|
@ -2098,7 +1994,8 @@ static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
|
static int twl4030_voice_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_codec *codec = dai->codec;
|
struct snd_soc_codec *codec = dai->codec;
|
||||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||||
|
@ -2108,8 +2005,8 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
|
||||||
twl4030_voice_enable(codec, substream->stream, 1);
|
twl4030_voice_enable(codec, substream->stream, 1);
|
||||||
|
|
||||||
/* bit rate */
|
/* bit rate */
|
||||||
old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
|
old_mode = twl4030_read(codec,
|
||||||
& ~(TWL4030_CODECPDZ);
|
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
|
||||||
mode = old_mode;
|
mode = old_mode;
|
||||||
|
|
||||||
switch (params_rate(params)) {
|
switch (params_rate(params)) {
|
||||||
|
@ -2171,7 +2068,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||||
u8 old_format, format;
|
u8 old_format, format;
|
||||||
|
|
||||||
/* get format */
|
/* get format */
|
||||||
old_format = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF);
|
old_format = twl4030_read(codec, TWL4030_REG_VOICE_IF);
|
||||||
format = old_format;
|
format = old_format;
|
||||||
|
|
||||||
/* set master/slave audio interface */
|
/* set master/slave audio interface */
|
||||||
|
@ -2218,7 +2115,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||||
static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate)
|
static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate)
|
||||||
{
|
{
|
||||||
struct snd_soc_codec *codec = dai->codec;
|
struct snd_soc_codec *codec = dai->codec;
|
||||||
u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF);
|
u8 reg = twl4030_read(codec, TWL4030_REG_VOICE_IF);
|
||||||
|
|
||||||
if (tristate)
|
if (tristate)
|
||||||
reg |= TWL4030_VIF_TRI_EN;
|
reg |= TWL4030_VIF_TRI_EN;
|
||||||
|
@ -2310,8 +2207,6 @@ static int twl4030_soc_remove(struct snd_soc_codec *codec)
|
||||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||||
struct twl4030_codec_data *pdata = twl4030->pdata;
|
struct twl4030_codec_data *pdata = twl4030->pdata;
|
||||||
|
|
||||||
/* Reset registers to their chip default before leaving */
|
|
||||||
twl4030_reset_registers(codec);
|
|
||||||
twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||||
|
|
||||||
if (pdata && pdata->hs_extmute && gpio_is_valid(pdata->hs_extmute_gpio))
|
if (pdata && pdata->hs_extmute && gpio_is_valid(pdata->hs_extmute_gpio))
|
||||||
|
@ -2323,13 +2218,10 @@ static int twl4030_soc_remove(struct snd_soc_codec *codec)
|
||||||
static struct snd_soc_codec_driver soc_codec_dev_twl4030 = {
|
static struct snd_soc_codec_driver soc_codec_dev_twl4030 = {
|
||||||
.probe = twl4030_soc_probe,
|
.probe = twl4030_soc_probe,
|
||||||
.remove = twl4030_soc_remove,
|
.remove = twl4030_soc_remove,
|
||||||
.read = twl4030_read_reg_cache,
|
.read = twl4030_read,
|
||||||
.write = twl4030_write,
|
.write = twl4030_write,
|
||||||
.set_bias_level = twl4030_set_bias_level,
|
.set_bias_level = twl4030_set_bias_level,
|
||||||
.idle_bias_off = true,
|
.idle_bias_off = true,
|
||||||
.reg_cache_size = sizeof(twl4030_reg),
|
|
||||||
.reg_word_size = sizeof(u8),
|
|
||||||
.reg_cache_default = twl4030_reg,
|
|
||||||
|
|
||||||
.controls = twl4030_snd_controls,
|
.controls = twl4030_snd_controls,
|
||||||
.num_controls = ARRAY_SIZE(twl4030_snd_controls),
|
.num_controls = ARRAY_SIZE(twl4030_snd_controls),
|
||||||
|
|
|
@ -313,6 +313,13 @@ ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
|
||||||
ARIZONA_MIXER_CONTROLS("SPKDAT2L", ARIZONA_OUT6LMIX_INPUT_1_SOURCE),
|
ARIZONA_MIXER_CONTROLS("SPKDAT2L", ARIZONA_OUT6LMIX_INPUT_1_SOURCE),
|
||||||
ARIZONA_MIXER_CONTROLS("SPKDAT2R", ARIZONA_OUT6RMIX_INPUT_1_SOURCE),
|
ARIZONA_MIXER_CONTROLS("SPKDAT2R", ARIZONA_OUT6RMIX_INPUT_1_SOURCE),
|
||||||
|
|
||||||
|
SOC_SINGLE("HPOUT1 SC Protect Switch", ARIZONA_HP1_SHORT_CIRCUIT_CTRL,
|
||||||
|
ARIZONA_HP1_SC_ENA_SHIFT, 1, 0),
|
||||||
|
SOC_SINGLE("HPOUT2 SC Protect Switch", ARIZONA_HP2_SHORT_CIRCUIT_CTRL,
|
||||||
|
ARIZONA_HP2_SC_ENA_SHIFT, 1, 0),
|
||||||
|
SOC_SINGLE("HPOUT3 SC Protect Switch", ARIZONA_HP3_SHORT_CIRCUIT_CTRL,
|
||||||
|
ARIZONA_HP3_SC_ENA_SHIFT, 1, 0),
|
||||||
|
|
||||||
SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
|
SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
|
||||||
ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
|
ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
|
||||||
SOC_DOUBLE_R("HPOUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
|
SOC_DOUBLE_R("HPOUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
|
||||||
|
|
|
@ -1497,30 +1497,22 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
static void wm_adsp2_boot_work(struct work_struct *work)
|
||||||
struct snd_kcontrol *kcontrol, int event)
|
|
||||||
{
|
{
|
||||||
struct snd_soc_codec *codec = w->codec;
|
struct wm_adsp *dsp = container_of(work,
|
||||||
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
|
struct wm_adsp,
|
||||||
struct wm_adsp *dsp = &dsps[w->shift];
|
boot_work);
|
||||||
struct wm_adsp_alg_region *alg_region;
|
|
||||||
struct wm_coeff_ctl *ctl;
|
|
||||||
unsigned int val;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
dsp->card = codec->card;
|
|
||||||
|
|
||||||
switch (event) {
|
|
||||||
case SND_SOC_DAPM_POST_PMU:
|
|
||||||
/*
|
/*
|
||||||
* For simplicity set the DSP clock rate to be the
|
* For simplicity set the DSP clock rate to be the
|
||||||
* SYSCLK rate rather than making it configurable.
|
* SYSCLK rate rather than making it configurable.
|
||||||
*/
|
*/
|
||||||
ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
|
ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
|
adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
|
||||||
ret);
|
return;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
val = (val & ARIZONA_SYSCLK_FREQ_MASK)
|
val = (val & ARIZONA_SYSCLK_FREQ_MASK)
|
||||||
>> ARIZONA_SYSCLK_FREQ_SHIFT;
|
>> ARIZONA_SYSCLK_FREQ_SHIFT;
|
||||||
|
@ -1529,18 +1521,16 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||||
dsp->base + ADSP2_CLOCKING,
|
dsp->base + ADSP2_CLOCKING,
|
||||||
ADSP2_CLK_SEL_MASK, val);
|
ADSP2_CLK_SEL_MASK, val);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
adsp_err(dsp, "Failed to set clock rate: %d\n",
|
adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
|
||||||
ret);
|
return;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dsp->dvfs) {
|
if (dsp->dvfs) {
|
||||||
ret = regmap_read(dsp->regmap,
|
ret = regmap_read(dsp->regmap,
|
||||||
dsp->base + ADSP2_CLOCKING, &val);
|
dsp->base + ADSP2_CLOCKING, &val);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(dsp->dev,
|
dev_err(dsp->dev, "Failed to read clocking: %d\n", ret);
|
||||||
"Failed to read clocking: %d\n", ret);
|
return;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
|
if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
|
||||||
|
@ -1549,7 +1539,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||||
dev_err(dsp->dev,
|
dev_err(dsp->dev,
|
||||||
"Failed to enable supply: %d\n",
|
"Failed to enable supply: %d\n",
|
||||||
ret);
|
ret);
|
||||||
return ret;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = regulator_set_voltage(dsp->dvfs,
|
ret = regulator_set_voltage(dsp->dvfs,
|
||||||
|
@ -1559,14 +1549,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||||
dev_err(dsp->dev,
|
dev_err(dsp->dev,
|
||||||
"Failed to raise supply: %d\n",
|
"Failed to raise supply: %d\n",
|
||||||
ret);
|
ret);
|
||||||
return ret;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = wm_adsp2_ena(dsp);
|
ret = wm_adsp2_ena(dsp);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return;
|
||||||
|
|
||||||
ret = wm_adsp_load(dsp);
|
ret = wm_adsp_load(dsp);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
|
@ -1592,12 +1582,64 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||||
|
|
||||||
ret = regmap_update_bits_async(dsp->regmap,
|
ret = regmap_update_bits_async(dsp->regmap,
|
||||||
dsp->base + ADSP2_CONTROL,
|
dsp->base + ADSP2_CONTROL,
|
||||||
ADSP2_CORE_ENA | ADSP2_START,
|
ADSP2_CORE_ENA,
|
||||||
ADSP2_CORE_ENA | ADSP2_START);
|
ADSP2_CORE_ENA);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
dsp->running = true;
|
dsp->running = true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
|
||||||
|
ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
|
||||||
|
struct snd_kcontrol *kcontrol, int event)
|
||||||
|
{
|
||||||
|
struct snd_soc_codec *codec = w->codec;
|
||||||
|
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
|
||||||
|
struct wm_adsp *dsp = &dsps[w->shift];
|
||||||
|
|
||||||
|
dsp->card = codec->card;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case SND_SOC_DAPM_PRE_PMU:
|
||||||
|
queue_work(system_unbound_wq, &dsp->boot_work);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
|
||||||
|
|
||||||
|
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||||
|
struct snd_kcontrol *kcontrol, int event)
|
||||||
|
{
|
||||||
|
struct snd_soc_codec *codec = w->codec;
|
||||||
|
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
|
||||||
|
struct wm_adsp *dsp = &dsps[w->shift];
|
||||||
|
struct wm_adsp_alg_region *alg_region;
|
||||||
|
struct wm_coeff_ctl *ctl;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case SND_SOC_DAPM_POST_PMU:
|
||||||
|
flush_work(&dsp->boot_work);
|
||||||
|
|
||||||
|
if (!dsp->running)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
ret = regmap_update_bits(dsp->regmap,
|
||||||
|
dsp->base + ADSP2_CONTROL,
|
||||||
|
ADSP2_START,
|
||||||
|
ADSP2_START);
|
||||||
|
if (ret != 0)
|
||||||
|
goto err;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_SOC_DAPM_PRE_PMD:
|
case SND_SOC_DAPM_PRE_PMD:
|
||||||
|
@ -1668,6 +1710,7 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
|
||||||
|
|
||||||
INIT_LIST_HEAD(&adsp->alg_regions);
|
INIT_LIST_HEAD(&adsp->alg_regions);
|
||||||
INIT_LIST_HEAD(&adsp->ctl_list);
|
INIT_LIST_HEAD(&adsp->ctl_list);
|
||||||
|
INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work);
|
||||||
|
|
||||||
if (dvfs) {
|
if (dvfs) {
|
||||||
adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
|
adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
|
||||||
|
|
|
@ -59,6 +59,8 @@ struct wm_adsp {
|
||||||
struct regulator *dvfs;
|
struct regulator *dvfs;
|
||||||
|
|
||||||
struct list_head ctl_list;
|
struct list_head ctl_list;
|
||||||
|
|
||||||
|
struct work_struct boot_work;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define WM_ADSP1(wname, num) \
|
#define WM_ADSP1(wname, num) \
|
||||||
|
@ -66,8 +68,12 @@ struct wm_adsp {
|
||||||
wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
|
wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
|
||||||
|
|
||||||
#define WM_ADSP2(wname, num) \
|
#define WM_ADSP2(wname, num) \
|
||||||
SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
|
{ .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \
|
||||||
wm_adsp2_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
|
.reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_early_event, \
|
||||||
|
.event_flags = SND_SOC_DAPM_PRE_PMU }, \
|
||||||
|
{ .id = snd_soc_dapm_out_drv, .name = wname, \
|
||||||
|
.reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \
|
||||||
|
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
|
||||||
|
|
||||||
extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
|
extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
|
||||||
extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
|
extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
|
||||||
|
@ -76,6 +82,8 @@ int wm_adsp1_init(struct wm_adsp *adsp);
|
||||||
int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs);
|
int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs);
|
||||||
int wm_adsp1_event(struct snd_soc_dapm_widget *w,
|
int wm_adsp1_event(struct snd_soc_dapm_widget *w,
|
||||||
struct snd_kcontrol *kcontrol, int event);
|
struct snd_kcontrol *kcontrol, int event);
|
||||||
|
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
|
||||||
|
struct snd_kcontrol *kcontrol, int event);
|
||||||
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||||
struct snd_kcontrol *kcontrol, int event);
|
struct snd_kcontrol *kcontrol, int event);
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,9 @@ config SND_SOC_FSL_SSI
|
||||||
config SND_SOC_FSL_SPDIF
|
config SND_SOC_FSL_SPDIF
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
|
config SND_SOC_FSL_ESAI
|
||||||
|
tristate
|
||||||
|
|
||||||
config SND_SOC_FSL_UTILS
|
config SND_SOC_FSL_UTILS
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,13 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
|
||||||
snd-soc-fsl-sai-objs := fsl_sai.o
|
snd-soc-fsl-sai-objs := fsl_sai.o
|
||||||
snd-soc-fsl-ssi-objs := fsl_ssi.o
|
snd-soc-fsl-ssi-objs := fsl_ssi.o
|
||||||
snd-soc-fsl-spdif-objs := fsl_spdif.o
|
snd-soc-fsl-spdif-objs := fsl_spdif.o
|
||||||
|
snd-soc-fsl-esai-objs := fsl_esai.o
|
||||||
snd-soc-fsl-utils-objs := fsl_utils.o
|
snd-soc-fsl-utils-objs := fsl_utils.o
|
||||||
snd-soc-fsl-dma-objs := fsl_dma.o
|
snd-soc-fsl-dma-objs := fsl_dma.o
|
||||||
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
|
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
|
||||||
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
|
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
|
||||||
obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
|
obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
|
||||||
|
obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o
|
||||||
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
|
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
|
||||||
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
|
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
|
||||||
|
|
||||||
|
|
|
@ -55,10 +55,6 @@
|
||||||
SNDRV_PCM_FMTBIT_S32_BE | \
|
SNDRV_PCM_FMTBIT_S32_BE | \
|
||||||
SNDRV_PCM_FMTBIT_U32_LE | \
|
SNDRV_PCM_FMTBIT_U32_LE | \
|
||||||
SNDRV_PCM_FMTBIT_U32_BE)
|
SNDRV_PCM_FMTBIT_U32_BE)
|
||||||
|
|
||||||
#define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
|
|
||||||
SNDRV_PCM_RATE_CONTINUOUS)
|
|
||||||
|
|
||||||
struct dma_object {
|
struct dma_object {
|
||||||
struct snd_soc_platform_driver dai;
|
struct snd_soc_platform_driver dai;
|
||||||
dma_addr_t ssi_stx_phys;
|
dma_addr_t ssi_stx_phys;
|
||||||
|
@ -140,9 +136,6 @@ static const struct snd_pcm_hardware fsl_dma_hardware = {
|
||||||
SNDRV_PCM_INFO_JOINT_DUPLEX |
|
SNDRV_PCM_INFO_JOINT_DUPLEX |
|
||||||
SNDRV_PCM_INFO_PAUSE,
|
SNDRV_PCM_INFO_PAUSE,
|
||||||
.formats = FSLDMA_PCM_FORMATS,
|
.formats = FSLDMA_PCM_FORMATS,
|
||||||
.rates = FSLDMA_PCM_RATES,
|
|
||||||
.rate_min = 5512,
|
|
||||||
.rate_max = 192000,
|
|
||||||
.period_bytes_min = 512, /* A reasonable limit */
|
.period_bytes_min = 512, /* A reasonable limit */
|
||||||
.period_bytes_max = (u32) -1,
|
.period_bytes_max = (u32) -1,
|
||||||
.periods_min = NUM_DMA_LINKS,
|
.periods_min = NUM_DMA_LINKS,
|
||||||
|
|
|
@ -0,0 +1,815 @@
|
||||||
|
/*
|
||||||
|
* Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
#include <sound/dmaengine_pcm.h>
|
||||||
|
#include <sound/pcm_params.h>
|
||||||
|
|
||||||
|
#include "fsl_esai.h"
|
||||||
|
#include "imx-pcm.h"
|
||||||
|
|
||||||
|
#define FSL_ESAI_RATES SNDRV_PCM_RATE_8000_192000
|
||||||
|
#define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
|
||||||
|
SNDRV_PCM_FMTBIT_S16_LE | \
|
||||||
|
SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||||
|
SNDRV_PCM_FMTBIT_S24_LE)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fsl_esai: ESAI private data
|
||||||
|
*
|
||||||
|
* @dma_params_rx: DMA parameters for receive channel
|
||||||
|
* @dma_params_tx: DMA parameters for transmit channel
|
||||||
|
* @pdev: platform device pointer
|
||||||
|
* @regmap: regmap handler
|
||||||
|
* @coreclk: clock source to access register
|
||||||
|
* @extalclk: esai clock source to derive HCK, SCK and FS
|
||||||
|
* @fsysclk: system clock source to derive HCK, SCK and FS
|
||||||
|
* @fifo_depth: depth of tx/rx FIFO
|
||||||
|
* @slot_width: width of each DAI slot
|
||||||
|
* @hck_rate: clock rate of desired HCKx clock
|
||||||
|
* @sck_div: if using PSR/PM dividers for SCKx clock
|
||||||
|
* @slave_mode: if fully using DAI slave mode
|
||||||
|
* @synchronous: if using tx/rx synchronous mode
|
||||||
|
* @name: driver name
|
||||||
|
*/
|
||||||
|
struct fsl_esai {
|
||||||
|
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||||
|
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||||
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct clk *coreclk;
|
||||||
|
struct clk *extalclk;
|
||||||
|
struct clk *fsysclk;
|
||||||
|
u32 fifo_depth;
|
||||||
|
u32 slot_width;
|
||||||
|
u32 hck_rate[2];
|
||||||
|
bool sck_div[2];
|
||||||
|
bool slave_mode;
|
||||||
|
bool synchronous;
|
||||||
|
char name[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t esai_isr(int irq, void *devid)
|
||||||
|
{
|
||||||
|
struct fsl_esai *esai_priv = (struct fsl_esai *)devid;
|
||||||
|
struct platform_device *pdev = esai_priv->pdev;
|
||||||
|
u32 esr;
|
||||||
|
|
||||||
|
regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr);
|
||||||
|
|
||||||
|
if (esr & ESAI_ESR_TINIT_MASK)
|
||||||
|
dev_dbg(&pdev->dev, "isr: Transmition Initialized\n");
|
||||||
|
|
||||||
|
if (esr & ESAI_ESR_RFF_MASK)
|
||||||
|
dev_warn(&pdev->dev, "isr: Receiving overrun\n");
|
||||||
|
|
||||||
|
if (esr & ESAI_ESR_TFE_MASK)
|
||||||
|
dev_warn(&pdev->dev, "isr: Transmition underrun\n");
|
||||||
|
|
||||||
|
if (esr & ESAI_ESR_TLS_MASK)
|
||||||
|
dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n");
|
||||||
|
|
||||||
|
if (esr & ESAI_ESR_TDE_MASK)
|
||||||
|
dev_dbg(&pdev->dev, "isr: Transmition data exception\n");
|
||||||
|
|
||||||
|
if (esr & ESAI_ESR_TED_MASK)
|
||||||
|
dev_dbg(&pdev->dev, "isr: Transmitting even slots\n");
|
||||||
|
|
||||||
|
if (esr & ESAI_ESR_TD_MASK)
|
||||||
|
dev_dbg(&pdev->dev, "isr: Transmitting data\n");
|
||||||
|
|
||||||
|
if (esr & ESAI_ESR_RLS_MASK)
|
||||||
|
dev_dbg(&pdev->dev, "isr: Just received the last slot\n");
|
||||||
|
|
||||||
|
if (esr & ESAI_ESR_RDE_MASK)
|
||||||
|
dev_dbg(&pdev->dev, "isr: Receiving data exception\n");
|
||||||
|
|
||||||
|
if (esr & ESAI_ESR_RED_MASK)
|
||||||
|
dev_dbg(&pdev->dev, "isr: Receiving even slots\n");
|
||||||
|
|
||||||
|
if (esr & ESAI_ESR_RD_MASK)
|
||||||
|
dev_dbg(&pdev->dev, "isr: Receiving data\n");
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to calculate the divisors of psr, pm, fp and it is
|
||||||
|
* supposed to be called in set_dai_sysclk() and set_bclk().
|
||||||
|
*
|
||||||
|
* @ratio: desired overall ratio for the paticipating dividers
|
||||||
|
* @usefp: for HCK setting, there is no need to set fp divider
|
||||||
|
* @fp: bypass other dividers by setting fp directly if fp != 0
|
||||||
|
* @tx: current setting is for playback or capture
|
||||||
|
*/
|
||||||
|
static int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio,
|
||||||
|
bool usefp, u32 fp)
|
||||||
|
{
|
||||||
|
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||||
|
u32 psr, pm = 999, maxfp, prod, sub, savesub, i, j;
|
||||||
|
|
||||||
|
maxfp = usefp ? 16 : 1;
|
||||||
|
|
||||||
|
if (usefp && fp)
|
||||||
|
goto out_fp;
|
||||||
|
|
||||||
|
if (ratio > 2 * 8 * 256 * maxfp || ratio < 2) {
|
||||||
|
dev_err(dai->dev, "the ratio is out of range (2 ~ %d)\n",
|
||||||
|
2 * 8 * 256 * maxfp);
|
||||||
|
return -EINVAL;
|
||||||
|
} else if (ratio % 2) {
|
||||||
|
dev_err(dai->dev, "the raio must be even if using upper divider\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ratio /= 2;
|
||||||
|
|
||||||
|
psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8;
|
||||||
|
|
||||||
|
/* Set the max fluctuation -- 0.1% of the max devisor */
|
||||||
|
savesub = (psr ? 1 : 8) * 256 * maxfp / 1000;
|
||||||
|
|
||||||
|
/* Find the best value for PM */
|
||||||
|
for (i = 1; i <= 256; i++) {
|
||||||
|
for (j = 1; j <= maxfp; j++) {
|
||||||
|
/* PSR (1 or 8) * PM (1 ~ 256) * FP (1 ~ 16) */
|
||||||
|
prod = (psr ? 1 : 8) * i * j;
|
||||||
|
|
||||||
|
if (prod == ratio)
|
||||||
|
sub = 0;
|
||||||
|
else if (prod / ratio == 1)
|
||||||
|
sub = prod - ratio;
|
||||||
|
else if (ratio / prod == 1)
|
||||||
|
sub = ratio - prod;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Calculate the fraction */
|
||||||
|
sub = sub * 1000 / ratio;
|
||||||
|
if (sub < savesub) {
|
||||||
|
savesub = sub;
|
||||||
|
pm = i;
|
||||||
|
fp = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We are lucky */
|
||||||
|
if (savesub == 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pm == 999) {
|
||||||
|
dev_err(dai->dev, "failed to calculate proper divisors\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
|
||||||
|
ESAI_xCCR_xPSR_MASK | ESAI_xCCR_xPM_MASK,
|
||||||
|
psr | ESAI_xCCR_xPM(pm));
|
||||||
|
|
||||||
|
out_fp:
|
||||||
|
/* Bypass fp if not being required */
|
||||||
|
if (maxfp <= 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
|
||||||
|
ESAI_xCCR_xFP_MASK, ESAI_xCCR_xFP(fp));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function mainly configures the clock frequency of MCLK (HCKT/HCKR)
|
||||||
|
*
|
||||||
|
* @Parameters:
|
||||||
|
* clk_id: The clock source of HCKT/HCKR
|
||||||
|
* (Input from outside; output from inside, FSYS or EXTAL)
|
||||||
|
* freq: The required clock rate of HCKT/HCKR
|
||||||
|
* dir: The clock direction of HCKT/HCKR
|
||||||
|
*
|
||||||
|
* Note: If the direction is input, we do not care about clk_id.
|
||||||
|
*/
|
||||||
|
static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||||
|
unsigned int freq, int dir)
|
||||||
|
{
|
||||||
|
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 in = dir == SND_SOC_CLOCK_IN;
|
||||||
|
u32 ret, ratio, ecr = 0;
|
||||||
|
unsigned long clk_rate;
|
||||||
|
|
||||||
|
/* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */
|
||||||
|
esai_priv->sck_div[tx] = true;
|
||||||
|
|
||||||
|
/* Set the direction of HCKT/HCKR pins */
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
|
||||||
|
ESAI_xCCR_xHCKD, in ? 0 : ESAI_xCCR_xHCKD);
|
||||||
|
|
||||||
|
if (in)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
switch (clk_id) {
|
||||||
|
case ESAI_HCKT_FSYS:
|
||||||
|
case ESAI_HCKR_FSYS:
|
||||||
|
clksrc = esai_priv->fsysclk;
|
||||||
|
break;
|
||||||
|
case ESAI_HCKT_EXTAL:
|
||||||
|
ecr |= ESAI_ECR_ETI;
|
||||||
|
case ESAI_HCKR_EXTAL:
|
||||||
|
ecr |= ESAI_ECR_ERI;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ERR(clksrc)) {
|
||||||
|
dev_err(dai->dev, "no assigned %s clock\n",
|
||||||
|
clk_id % 2 ? "extal" : "fsys");
|
||||||
|
return PTR_ERR(clksrc);
|
||||||
|
}
|
||||||
|
clk_rate = clk_get_rate(clksrc);
|
||||||
|
|
||||||
|
ratio = clk_rate / freq;
|
||||||
|
if (ratio * freq > clk_rate)
|
||||||
|
ret = ratio * freq - clk_rate;
|
||||||
|
else if (ratio * freq < clk_rate)
|
||||||
|
ret = clk_rate - ratio * freq;
|
||||||
|
else
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
/* Block if clock source can not be divided into the required rate */
|
||||||
|
if (ret != 0 && clk_rate / ret < 1000) {
|
||||||
|
dev_err(dai->dev, "failed to derive required HCK%c rate\n",
|
||||||
|
tx ? 'T' : 'R');
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ratio == 1) {
|
||||||
|
/* Bypass all the dividers if not being needed */
|
||||||
|
ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
esai_priv->sck_div[tx] = false;
|
||||||
|
|
||||||
|
out:
|
||||||
|
esai_priv->hck_rate[tx] = freq;
|
||||||
|
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
|
||||||
|
tx ? ESAI_ECR_ETI | ESAI_ECR_ETO :
|
||||||
|
ESAI_ECR_ERI | ESAI_ECR_ERO, ecr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function configures the related dividers according to the bclk rate
|
||||||
|
*/
|
||||||
|
static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
|
||||||
|
{
|
||||||
|
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||||
|
u32 hck_rate = esai_priv->hck_rate[tx];
|
||||||
|
u32 sub, ratio = hck_rate / freq;
|
||||||
|
|
||||||
|
/* Don't apply for fully slave mode*/
|
||||||
|
if (esai_priv->slave_mode)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ratio * freq > hck_rate)
|
||||||
|
sub = ratio * freq - hck_rate;
|
||||||
|
else if (ratio * freq < hck_rate)
|
||||||
|
sub = hck_rate - ratio * freq;
|
||||||
|
else
|
||||||
|
sub = 0;
|
||||||
|
|
||||||
|
/* Block if clock source can not be divided into the required rate */
|
||||||
|
if (sub != 0 && hck_rate / sub < 1000) {
|
||||||
|
dev_err(dai->dev, "failed to derive required SCK%c rate\n",
|
||||||
|
tx ? 'T' : 'R');
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) {
|
||||||
|
dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fsl_esai_divisor_cal(dai, tx, ratio, true,
|
||||||
|
esai_priv->sck_div[tx] ? 0 : ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
|
||||||
|
u32 rx_mask, int slots, int slot_width)
|
||||||
|
{
|
||||||
|
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||||
|
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
|
||||||
|
ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
|
||||||
|
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMA,
|
||||||
|
ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(tx_mask));
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMB,
|
||||||
|
ESAI_xSMA_xS_MASK, ESAI_xSMB_xS(tx_mask));
|
||||||
|
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
|
||||||
|
ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
|
||||||
|
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMA,
|
||||||
|
ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(rx_mask));
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMB,
|
||||||
|
ESAI_xSMA_xS_MASK, ESAI_xSMB_xS(rx_mask));
|
||||||
|
|
||||||
|
esai_priv->slot_width = slot_width;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||||
|
{
|
||||||
|
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||||
|
u32 xcr = 0, xccr = 0, mask;
|
||||||
|
|
||||||
|
/* DAI mode */
|
||||||
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_I2S:
|
||||||
|
/* Data on rising edge of bclk, frame low, 1clk before data */
|
||||||
|
xcr |= ESAI_xCR_xFSR;
|
||||||
|
xccr |= ESAI_xCCR_xFSP | ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_LEFT_J:
|
||||||
|
/* Data on rising edge of bclk, frame high */
|
||||||
|
xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_RIGHT_J:
|
||||||
|
/* Data on rising edge of bclk, frame high, right aligned */
|
||||||
|
xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCR_xWA;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_DSP_A:
|
||||||
|
/* Data on rising edge of bclk, frame high, 1clk before data */
|
||||||
|
xcr |= ESAI_xCR_xFSL | ESAI_xCR_xFSR;
|
||||||
|
xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_DSP_B:
|
||||||
|
/* Data on rising edge of bclk, frame high */
|
||||||
|
xcr |= ESAI_xCR_xFSL;
|
||||||
|
xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DAI clock inversion */
|
||||||
|
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_NB_NF:
|
||||||
|
/* Nothing to do for both normal cases */
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_IB_NF:
|
||||||
|
/* Invert bit clock */
|
||||||
|
xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_NB_IF:
|
||||||
|
/* Invert frame clock */
|
||||||
|
xccr ^= ESAI_xCCR_xFSP;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_IB_IF:
|
||||||
|
/* Invert both clocks */
|
||||||
|
xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esai_priv->slave_mode = false;
|
||||||
|
|
||||||
|
/* DAI clock master masks */
|
||||||
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||||
|
case SND_SOC_DAIFMT_CBM_CFM:
|
||||||
|
esai_priv->slave_mode = true;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_CBS_CFM:
|
||||||
|
xccr |= ESAI_xCCR_xCKD;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_CBM_CFS:
|
||||||
|
xccr |= ESAI_xCCR_xFSD;
|
||||||
|
break;
|
||||||
|
case SND_SOC_DAIFMT_CBS_CFS:
|
||||||
|
xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR;
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, xcr);
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, mask, xcr);
|
||||||
|
|
||||||
|
mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP |
|
||||||
|
ESAI_xCCR_xFSD | ESAI_xCCR_xCKD | ESAI_xCR_xWA;
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, mask, xccr);
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, mask, xccr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsl_esai_startup(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some platforms might use the same bit to gate all three or two of
|
||||||
|
* clocks, so keep all clocks open/close at the same time for safety
|
||||||
|
*/
|
||||||
|
clk_prepare_enable(esai_priv->coreclk);
|
||||||
|
if (!IS_ERR(esai_priv->extalclk))
|
||||||
|
clk_prepare_enable(esai_priv->extalclk);
|
||||||
|
if (!IS_ERR(esai_priv->fsysclk))
|
||||||
|
clk_prepare_enable(esai_priv->fsysclk);
|
||||||
|
|
||||||
|
if (!dai->active) {
|
||||||
|
/* Reset Port C */
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
|
||||||
|
ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
|
||||||
|
ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
|
||||||
|
|
||||||
|
/* Set synchronous mode */
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR,
|
||||||
|
ESAI_SAICR_SYNC, esai_priv->synchronous ?
|
||||||
|
ESAI_SAICR_SYNC : 0);
|
||||||
|
|
||||||
|
/* Set a default slot number -- 2 */
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
|
||||||
|
ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2));
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
|
||||||
|
ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_pcm_hw_params *params,
|
||||||
|
struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||||
|
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||||
|
u32 width = snd_pcm_format_width(params_format(params));
|
||||||
|
u32 channels = params_channels(params);
|
||||||
|
u32 bclk, mask, val, ret;
|
||||||
|
|
||||||
|
bclk = params_rate(params) * esai_priv->slot_width * 2;
|
||||||
|
|
||||||
|
ret = fsl_esai_set_bclk(dai, tx, bclk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* 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 ?
|
||||||
|
ESAI_xCR_xMOD_NETWORK : 0);
|
||||||
|
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
|
||||||
|
ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR);
|
||||||
|
|
||||||
|
mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK |
|
||||||
|
(tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK);
|
||||||
|
val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) |
|
||||||
|
(tx ? ESAI_xFCR_TE(channels) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(channels));
|
||||||
|
|
||||||
|
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(esai_priv->slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
|
||||||
|
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fsl_esai_shutdown(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||||
|
|
||||||
|
if (!IS_ERR(esai_priv->fsysclk))
|
||||||
|
clk_disable_unprepare(esai_priv->fsysclk);
|
||||||
|
if (!IS_ERR(esai_priv->extalclk))
|
||||||
|
clk_disable_unprepare(esai_priv->extalclk);
|
||||||
|
clk_disable_unprepare(esai_priv->coreclk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||||
|
struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||||
|
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||||
|
u8 i, channels = substream->runtime->channels;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case SNDRV_PCM_TRIGGER_START:
|
||||||
|
case SNDRV_PCM_TRIGGER_RESUME:
|
||||||
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
|
||||||
|
ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN);
|
||||||
|
|
||||||
|
/* Write initial words reqiured by ESAI as normal procedure */
|
||||||
|
for (i = 0; tx && i < channels; i++)
|
||||||
|
regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0);
|
||||||
|
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
|
||||||
|
tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,
|
||||||
|
tx ? ESAI_xCR_TE(channels) : ESAI_xCR_RE(channels));
|
||||||
|
break;
|
||||||
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||||
|
case SNDRV_PCM_TRIGGER_STOP:
|
||||||
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
|
||||||
|
tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0);
|
||||||
|
|
||||||
|
/* Disable and reset FIFO */
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
|
||||||
|
ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR);
|
||||||
|
regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
|
||||||
|
ESAI_xFCR_xFR, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct snd_soc_dai_ops fsl_esai_dai_ops = {
|
||||||
|
.startup = fsl_esai_startup,
|
||||||
|
.shutdown = fsl_esai_shutdown,
|
||||||
|
.trigger = fsl_esai_trigger,
|
||||||
|
.hw_params = fsl_esai_hw_params,
|
||||||
|
.set_sysclk = fsl_esai_set_dai_sysclk,
|
||||||
|
.set_fmt = fsl_esai_set_dai_fmt,
|
||||||
|
.set_tdm_slot = fsl_esai_set_dai_tdm_slot,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fsl_esai_dai_probe(struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||||
|
|
||||||
|
snd_soc_dai_init_dma_data(dai, &esai_priv->dma_params_tx,
|
||||||
|
&esai_priv->dma_params_rx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct snd_soc_dai_driver fsl_esai_dai = {
|
||||||
|
.probe = fsl_esai_dai_probe,
|
||||||
|
.playback = {
|
||||||
|
.channels_min = 1,
|
||||||
|
.channels_max = 12,
|
||||||
|
.rates = FSL_ESAI_RATES,
|
||||||
|
.formats = FSL_ESAI_FORMATS,
|
||||||
|
},
|
||||||
|
.capture = {
|
||||||
|
.channels_min = 1,
|
||||||
|
.channels_max = 8,
|
||||||
|
.rates = FSL_ESAI_RATES,
|
||||||
|
.formats = FSL_ESAI_FORMATS,
|
||||||
|
},
|
||||||
|
.ops = &fsl_esai_dai_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct snd_soc_component_driver fsl_esai_component = {
|
||||||
|
.name = "fsl-esai",
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case REG_ESAI_ERDR:
|
||||||
|
case REG_ESAI_ECR:
|
||||||
|
case REG_ESAI_ESR:
|
||||||
|
case REG_ESAI_TFCR:
|
||||||
|
case REG_ESAI_TFSR:
|
||||||
|
case REG_ESAI_RFCR:
|
||||||
|
case REG_ESAI_RFSR:
|
||||||
|
case REG_ESAI_RX0:
|
||||||
|
case REG_ESAI_RX1:
|
||||||
|
case REG_ESAI_RX2:
|
||||||
|
case REG_ESAI_RX3:
|
||||||
|
case REG_ESAI_SAISR:
|
||||||
|
case REG_ESAI_SAICR:
|
||||||
|
case REG_ESAI_TCR:
|
||||||
|
case REG_ESAI_TCCR:
|
||||||
|
case REG_ESAI_RCR:
|
||||||
|
case REG_ESAI_RCCR:
|
||||||
|
case REG_ESAI_TSMA:
|
||||||
|
case REG_ESAI_TSMB:
|
||||||
|
case REG_ESAI_RSMA:
|
||||||
|
case REG_ESAI_RSMB:
|
||||||
|
case REG_ESAI_PRRC:
|
||||||
|
case REG_ESAI_PCRC:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case REG_ESAI_ETDR:
|
||||||
|
case REG_ESAI_ECR:
|
||||||
|
case REG_ESAI_TFCR:
|
||||||
|
case REG_ESAI_RFCR:
|
||||||
|
case REG_ESAI_TX0:
|
||||||
|
case REG_ESAI_TX1:
|
||||||
|
case REG_ESAI_TX2:
|
||||||
|
case REG_ESAI_TX3:
|
||||||
|
case REG_ESAI_TX4:
|
||||||
|
case REG_ESAI_TX5:
|
||||||
|
case REG_ESAI_TSR:
|
||||||
|
case REG_ESAI_SAICR:
|
||||||
|
case REG_ESAI_TCR:
|
||||||
|
case REG_ESAI_TCCR:
|
||||||
|
case REG_ESAI_RCR:
|
||||||
|
case REG_ESAI_RCCR:
|
||||||
|
case REG_ESAI_TSMA:
|
||||||
|
case REG_ESAI_TSMB:
|
||||||
|
case REG_ESAI_RSMA:
|
||||||
|
case REG_ESAI_RSMB:
|
||||||
|
case REG_ESAI_PRRC:
|
||||||
|
case REG_ESAI_PCRC:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct regmap_config fsl_esai_regmap_config = {
|
||||||
|
.reg_bits = 32,
|
||||||
|
.reg_stride = 4,
|
||||||
|
.val_bits = 32,
|
||||||
|
|
||||||
|
.max_register = REG_ESAI_PCRC,
|
||||||
|
.readable_reg = fsl_esai_readable_reg,
|
||||||
|
.writeable_reg = fsl_esai_writeable_reg,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fsl_esai_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct fsl_esai *esai_priv;
|
||||||
|
struct resource *res;
|
||||||
|
const uint32_t *iprop;
|
||||||
|
void __iomem *regs;
|
||||||
|
int irq, ret;
|
||||||
|
|
||||||
|
esai_priv = devm_kzalloc(&pdev->dev, sizeof(*esai_priv), GFP_KERNEL);
|
||||||
|
if (!esai_priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
esai_priv->pdev = pdev;
|
||||||
|
strcpy(esai_priv->name, np->name);
|
||||||
|
|
||||||
|
/* Get the addresses and IRQ */
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
regs = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(regs))
|
||||||
|
return PTR_ERR(regs);
|
||||||
|
|
||||||
|
esai_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
|
||||||
|
"core", regs, &fsl_esai_regmap_config);
|
||||||
|
if (IS_ERR(esai_priv->regmap)) {
|
||||||
|
dev_err(&pdev->dev, "failed to init regmap: %ld\n",
|
||||||
|
PTR_ERR(esai_priv->regmap));
|
||||||
|
return PTR_ERR(esai_priv->regmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
esai_priv->coreclk = devm_clk_get(&pdev->dev, "core");
|
||||||
|
if (IS_ERR(esai_priv->coreclk)) {
|
||||||
|
dev_err(&pdev->dev, "failed to get core clock: %ld\n",
|
||||||
|
PTR_ERR(esai_priv->coreclk));
|
||||||
|
return PTR_ERR(esai_priv->coreclk);
|
||||||
|
}
|
||||||
|
|
||||||
|
esai_priv->extalclk = devm_clk_get(&pdev->dev, "extal");
|
||||||
|
if (IS_ERR(esai_priv->extalclk))
|
||||||
|
dev_warn(&pdev->dev, "failed to get extal clock: %ld\n",
|
||||||
|
PTR_ERR(esai_priv->extalclk));
|
||||||
|
|
||||||
|
esai_priv->fsysclk = devm_clk_get(&pdev->dev, "fsys");
|
||||||
|
if (IS_ERR(esai_priv->fsysclk))
|
||||||
|
dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n",
|
||||||
|
PTR_ERR(esai_priv->fsysclk));
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
|
||||||
|
return irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, irq, esai_isr, 0,
|
||||||
|
esai_priv->name, esai_priv);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "failed to claim irq %u\n", irq);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set a default slot size */
|
||||||
|
esai_priv->slot_width = 32;
|
||||||
|
|
||||||
|
/* Set a default master/slave state */
|
||||||
|
esai_priv->slave_mode = true;
|
||||||
|
|
||||||
|
/* Determine the FIFO depth */
|
||||||
|
iprop = of_get_property(np, "fsl,fifo-depth", NULL);
|
||||||
|
if (iprop)
|
||||||
|
esai_priv->fifo_depth = be32_to_cpup(iprop);
|
||||||
|
else
|
||||||
|
esai_priv->fifo_depth = 64;
|
||||||
|
|
||||||
|
esai_priv->dma_params_tx.maxburst = 16;
|
||||||
|
esai_priv->dma_params_rx.maxburst = 16;
|
||||||
|
esai_priv->dma_params_tx.addr = res->start + REG_ESAI_ETDR;
|
||||||
|
esai_priv->dma_params_rx.addr = res->start + REG_ESAI_ERDR;
|
||||||
|
|
||||||
|
esai_priv->synchronous =
|
||||||
|
of_property_read_bool(np, "fsl,esai-synchronous");
|
||||||
|
|
||||||
|
/* Implement full symmetry for synchronous mode */
|
||||||
|
if (esai_priv->synchronous) {
|
||||||
|
fsl_esai_dai.symmetric_rates = 1;
|
||||||
|
fsl_esai_dai.symmetric_channels = 1;
|
||||||
|
fsl_esai_dai.symmetric_samplebits = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_set_drvdata(&pdev->dev, esai_priv);
|
||||||
|
|
||||||
|
/* Reset ESAI unit */
|
||||||
|
ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ERST);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to enable ESAI so as to access some of its registers.
|
||||||
|
* Otherwise, we would fail to dump regmap from user space.
|
||||||
|
*/
|
||||||
|
ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ESAIEN);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component,
|
||||||
|
&fsl_esai_dai, 1);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = imx_pcm_dma_init(pdev);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id fsl_esai_dt_ids[] = {
|
||||||
|
{ .compatible = "fsl,imx35-esai", },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
|
||||||
|
|
||||||
|
static struct platform_driver fsl_esai_driver = {
|
||||||
|
.probe = fsl_esai_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "fsl-esai-dai",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = fsl_esai_dt_ids,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(fsl_esai_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||||
|
MODULE_DESCRIPTION("Freescale ESAI CPU DAI driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_ALIAS("platform:fsl-esai-dai");
|
|
@ -0,0 +1,354 @@
|
||||||
|
/*
|
||||||
|
* fsl_esai.h - ALSA ESAI interface for the Freescale i.MX SoC
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||||
|
*
|
||||||
|
* Author: Nicolin Chen <Guangyu.Chen@freescale.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FSL_ESAI_DAI_H
|
||||||
|
#define _FSL_ESAI_DAI_H
|
||||||
|
|
||||||
|
/* ESAI Register Map */
|
||||||
|
#define REG_ESAI_ETDR 0x00
|
||||||
|
#define REG_ESAI_ERDR 0x04
|
||||||
|
#define REG_ESAI_ECR 0x08
|
||||||
|
#define REG_ESAI_ESR 0x0C
|
||||||
|
#define REG_ESAI_TFCR 0x10
|
||||||
|
#define REG_ESAI_TFSR 0x14
|
||||||
|
#define REG_ESAI_RFCR 0x18
|
||||||
|
#define REG_ESAI_RFSR 0x1C
|
||||||
|
#define REG_ESAI_xFCR(tx) (tx ? REG_ESAI_TFCR : REG_ESAI_RFCR)
|
||||||
|
#define REG_ESAI_xFSR(tx) (tx ? REG_ESAI_TFSR : REG_ESAI_RFSR)
|
||||||
|
#define REG_ESAI_TX0 0x80
|
||||||
|
#define REG_ESAI_TX1 0x84
|
||||||
|
#define REG_ESAI_TX2 0x88
|
||||||
|
#define REG_ESAI_TX3 0x8C
|
||||||
|
#define REG_ESAI_TX4 0x90
|
||||||
|
#define REG_ESAI_TX5 0x94
|
||||||
|
#define REG_ESAI_TSR 0x98
|
||||||
|
#define REG_ESAI_RX0 0xA0
|
||||||
|
#define REG_ESAI_RX1 0xA4
|
||||||
|
#define REG_ESAI_RX2 0xA8
|
||||||
|
#define REG_ESAI_RX3 0xAC
|
||||||
|
#define REG_ESAI_SAISR 0xCC
|
||||||
|
#define REG_ESAI_SAICR 0xD0
|
||||||
|
#define REG_ESAI_TCR 0xD4
|
||||||
|
#define REG_ESAI_TCCR 0xD8
|
||||||
|
#define REG_ESAI_RCR 0xDC
|
||||||
|
#define REG_ESAI_RCCR 0xE0
|
||||||
|
#define REG_ESAI_xCR(tx) (tx ? REG_ESAI_TCR : REG_ESAI_RCR)
|
||||||
|
#define REG_ESAI_xCCR(tx) (tx ? REG_ESAI_TCCR : REG_ESAI_RCCR)
|
||||||
|
#define REG_ESAI_TSMA 0xE4
|
||||||
|
#define REG_ESAI_TSMB 0xE8
|
||||||
|
#define REG_ESAI_RSMA 0xEC
|
||||||
|
#define REG_ESAI_RSMB 0xF0
|
||||||
|
#define REG_ESAI_xSMA(tx) (tx ? REG_ESAI_TSMA : REG_ESAI_RSMA)
|
||||||
|
#define REG_ESAI_xSMB(tx) (tx ? REG_ESAI_TSMB : REG_ESAI_RSMB)
|
||||||
|
#define REG_ESAI_PRRC 0xF8
|
||||||
|
#define REG_ESAI_PCRC 0xFC
|
||||||
|
|
||||||
|
/* ESAI Control Register -- REG_ESAI_ECR 0x8 */
|
||||||
|
#define ESAI_ECR_ETI_SHIFT 19
|
||||||
|
#define ESAI_ECR_ETI_MASK (1 << ESAI_ECR_ETI_SHIFT)
|
||||||
|
#define ESAI_ECR_ETI (1 << ESAI_ECR_ETI_SHIFT)
|
||||||
|
#define ESAI_ECR_ETO_SHIFT 18
|
||||||
|
#define ESAI_ECR_ETO_MASK (1 << ESAI_ECR_ETO_SHIFT)
|
||||||
|
#define ESAI_ECR_ETO (1 << ESAI_ECR_ETO_SHIFT)
|
||||||
|
#define ESAI_ECR_ERI_SHIFT 17
|
||||||
|
#define ESAI_ECR_ERI_MASK (1 << ESAI_ECR_ERI_SHIFT)
|
||||||
|
#define ESAI_ECR_ERI (1 << ESAI_ECR_ERI_SHIFT)
|
||||||
|
#define ESAI_ECR_ERO_SHIFT 16
|
||||||
|
#define ESAI_ECR_ERO_MASK (1 << ESAI_ECR_ERO_SHIFT)
|
||||||
|
#define ESAI_ECR_ERO (1 << ESAI_ECR_ERO_SHIFT)
|
||||||
|
#define ESAI_ECR_ERST_SHIFT 1
|
||||||
|
#define ESAI_ECR_ERST_MASK (1 << ESAI_ECR_ERST_SHIFT)
|
||||||
|
#define ESAI_ECR_ERST (1 << ESAI_ECR_ERST_SHIFT)
|
||||||
|
#define ESAI_ECR_ESAIEN_SHIFT 0
|
||||||
|
#define ESAI_ECR_ESAIEN_MASK (1 << ESAI_ECR_ESAIEN_SHIFT)
|
||||||
|
#define ESAI_ECR_ESAIEN (1 << ESAI_ECR_ESAIEN_SHIFT)
|
||||||
|
|
||||||
|
/* ESAI Status Register -- REG_ESAI_ESR 0xC */
|
||||||
|
#define ESAI_ESR_TINIT_SHIFT 10
|
||||||
|
#define ESAI_ESR_TINIT_MASK (1 << ESAI_ESR_TINIT_SHIFT)
|
||||||
|
#define ESAI_ESR_TINIT (1 << ESAI_ESR_TINIT_SHIFT)
|
||||||
|
#define ESAI_ESR_RFF_SHIFT 9
|
||||||
|
#define ESAI_ESR_RFF_MASK (1 << ESAI_ESR_RFF_SHIFT)
|
||||||
|
#define ESAI_ESR_RFF (1 << ESAI_ESR_RFF_SHIFT)
|
||||||
|
#define ESAI_ESR_TFE_SHIFT 8
|
||||||
|
#define ESAI_ESR_TFE_MASK (1 << ESAI_ESR_TFE_SHIFT)
|
||||||
|
#define ESAI_ESR_TFE (1 << ESAI_ESR_TFE_SHIFT)
|
||||||
|
#define ESAI_ESR_TLS_SHIFT 7
|
||||||
|
#define ESAI_ESR_TLS_MASK (1 << ESAI_ESR_TLS_SHIFT)
|
||||||
|
#define ESAI_ESR_TLS (1 << ESAI_ESR_TLS_SHIFT)
|
||||||
|
#define ESAI_ESR_TDE_SHIFT 6
|
||||||
|
#define ESAI_ESR_TDE_MASK (1 << ESAI_ESR_TDE_SHIFT)
|
||||||
|
#define ESAI_ESR_TDE (1 << ESAI_ESR_TDE_SHIFT)
|
||||||
|
#define ESAI_ESR_TED_SHIFT 5
|
||||||
|
#define ESAI_ESR_TED_MASK (1 << ESAI_ESR_TED_SHIFT)
|
||||||
|
#define ESAI_ESR_TED (1 << ESAI_ESR_TED_SHIFT)
|
||||||
|
#define ESAI_ESR_TD_SHIFT 4
|
||||||
|
#define ESAI_ESR_TD_MASK (1 << ESAI_ESR_TD_SHIFT)
|
||||||
|
#define ESAI_ESR_TD (1 << ESAI_ESR_TD_SHIFT)
|
||||||
|
#define ESAI_ESR_RLS_SHIFT 3
|
||||||
|
#define ESAI_ESR_RLS_MASK (1 << ESAI_ESR_RLS_SHIFT)
|
||||||
|
#define ESAI_ESR_RLS (1 << ESAI_ESR_RLS_SHIFT)
|
||||||
|
#define ESAI_ESR_RDE_SHIFT 2
|
||||||
|
#define ESAI_ESR_RDE_MASK (1 << ESAI_ESR_RDE_SHIFT)
|
||||||
|
#define ESAI_ESR_RDE (1 << ESAI_ESR_RDE_SHIFT)
|
||||||
|
#define ESAI_ESR_RED_SHIFT 1
|
||||||
|
#define ESAI_ESR_RED_MASK (1 << ESAI_ESR_RED_SHIFT)
|
||||||
|
#define ESAI_ESR_RED (1 << ESAI_ESR_RED_SHIFT)
|
||||||
|
#define ESAI_ESR_RD_SHIFT 0
|
||||||
|
#define ESAI_ESR_RD_MASK (1 << ESAI_ESR_RD_SHIFT)
|
||||||
|
#define ESAI_ESR_RD (1 << ESAI_ESR_RD_SHIFT)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmit FIFO Configuration Register -- REG_ESAI_TFCR 0x10
|
||||||
|
* Receive FIFO Configuration Register -- REG_ESAI_RFCR 0x18
|
||||||
|
*/
|
||||||
|
#define ESAI_xFCR_TIEN_SHIFT 19
|
||||||
|
#define ESAI_xFCR_TIEN_MASK (1 << ESAI_xFCR_TIEN_SHIFT)
|
||||||
|
#define ESAI_xFCR_TIEN (1 << ESAI_xFCR_TIEN_SHIFT)
|
||||||
|
#define ESAI_xFCR_REXT_SHIFT 19
|
||||||
|
#define ESAI_xFCR_REXT_MASK (1 << ESAI_xFCR_REXT_SHIFT)
|
||||||
|
#define ESAI_xFCR_REXT (1 << ESAI_xFCR_REXT_SHIFT)
|
||||||
|
#define ESAI_xFCR_xWA_SHIFT 16
|
||||||
|
#define ESAI_xFCR_xWA_WIDTH 3
|
||||||
|
#define ESAI_xFCR_xWA_MASK (((1 << ESAI_xFCR_xWA_WIDTH) - 1) << ESAI_xFCR_xWA_SHIFT)
|
||||||
|
#define ESAI_xFCR_xWA(v) (((8 - ((v) >> 2)) << ESAI_xFCR_xWA_SHIFT) & ESAI_xFCR_xWA_MASK)
|
||||||
|
#define ESAI_xFCR_xFWM_SHIFT 8
|
||||||
|
#define ESAI_xFCR_xFWM_WIDTH 8
|
||||||
|
#define ESAI_xFCR_xFWM_MASK (((1 << ESAI_xFCR_xFWM_WIDTH) - 1) << ESAI_xFCR_xFWM_SHIFT)
|
||||||
|
#define ESAI_xFCR_xFWM(v) ((((v) - 1) << ESAI_xFCR_xFWM_SHIFT) & ESAI_xFCR_xFWM_MASK)
|
||||||
|
#define ESAI_xFCR_xE_SHIFT 2
|
||||||
|
#define ESAI_xFCR_TE_WIDTH 6
|
||||||
|
#define ESAI_xFCR_RE_WIDTH 4
|
||||||
|
#define ESAI_xFCR_TE_MASK (((1 << ESAI_xFCR_TE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT)
|
||||||
|
#define ESAI_xFCR_RE_MASK (((1 << ESAI_xFCR_RE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT)
|
||||||
|
#define ESAI_xFCR_TE(x) ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_TE_MASK)
|
||||||
|
#define ESAI_xFCR_RE(x) ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_RE_MASK)
|
||||||
|
#define ESAI_xFCR_xFR_SHIFT 1
|
||||||
|
#define ESAI_xFCR_xFR_MASK (1 << ESAI_xFCR_xFR_SHIFT)
|
||||||
|
#define ESAI_xFCR_xFR (1 << ESAI_xFCR_xFR_SHIFT)
|
||||||
|
#define ESAI_xFCR_xFEN_SHIFT 0
|
||||||
|
#define ESAI_xFCR_xFEN_MASK (1 << ESAI_xFCR_xFEN_SHIFT)
|
||||||
|
#define ESAI_xFCR_xFEN (1 << ESAI_xFCR_xFEN_SHIFT)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmit FIFO Status Register -- REG_ESAI_TFSR 0x14
|
||||||
|
* Receive FIFO Status Register --REG_ESAI_RFSR 0x1C
|
||||||
|
*/
|
||||||
|
#define ESAI_xFSR_NTFO_SHIFT 12
|
||||||
|
#define ESAI_xFSR_NRFI_SHIFT 12
|
||||||
|
#define ESAI_xFSR_NTFI_SHIFT 8
|
||||||
|
#define ESAI_xFSR_NRFO_SHIFT 8
|
||||||
|
#define ESAI_xFSR_NTFx_WIDTH 3
|
||||||
|
#define ESAI_xFSR_NRFx_WIDTH 2
|
||||||
|
#define ESAI_xFSR_NTFO_MASK (((1 << ESAI_xFSR_NTFx_WIDTH) - 1) << ESAI_xFSR_NTFO_SHIFT)
|
||||||
|
#define ESAI_xFSR_NTFI_MASK (((1 << ESAI_xFSR_NTFx_WIDTH) - 1) << ESAI_xFSR_NTFI_SHIFT)
|
||||||
|
#define ESAI_xFSR_NRFO_MASK (((1 << ESAI_xFSR_NRFx_WIDTH) - 1) << ESAI_xFSR_NRFO_SHIFT)
|
||||||
|
#define ESAI_xFSR_NRFI_MASK (((1 << ESAI_xFSR_NRFx_WIDTH) - 1) << ESAI_xFSR_NRFI_SHIFT)
|
||||||
|
#define ESAI_xFSR_xFCNT_SHIFT 0
|
||||||
|
#define ESAI_xFSR_xFCNT_WIDTH 8
|
||||||
|
#define ESAI_xFSR_xFCNT_MASK (((1 << ESAI_xFSR_xFCNT_WIDTH) - 1) << ESAI_xFSR_xFCNT_SHIFT)
|
||||||
|
|
||||||
|
/* ESAI Transmit Slot Register -- REG_ESAI_TSR 0x98 */
|
||||||
|
#define ESAI_TSR_SHIFT 0
|
||||||
|
#define ESAI_TSR_WIDTH 24
|
||||||
|
#define ESAI_TSR_MASK (((1 << ESAI_TSR_WIDTH) - 1) << ESAI_TSR_SHIFT)
|
||||||
|
|
||||||
|
/* Serial Audio Interface Status Register -- REG_ESAI_SAISR 0xCC */
|
||||||
|
#define ESAI_SAISR_TODFE_SHIFT 17
|
||||||
|
#define ESAI_SAISR_TODFE_MASK (1 << ESAI_SAISR_TODFE_SHIFT)
|
||||||
|
#define ESAI_SAISR_TODFE (1 << ESAI_SAISR_TODFE_SHIFT)
|
||||||
|
#define ESAI_SAISR_TEDE_SHIFT 16
|
||||||
|
#define ESAI_SAISR_TEDE_MASK (1 << ESAI_SAISR_TEDE_SHIFT)
|
||||||
|
#define ESAI_SAISR_TEDE (1 << ESAI_SAISR_TEDE_SHIFT)
|
||||||
|
#define ESAI_SAISR_TDE_SHIFT 15
|
||||||
|
#define ESAI_SAISR_TDE_MASK (1 << ESAI_SAISR_TDE_SHIFT)
|
||||||
|
#define ESAI_SAISR_TDE (1 << ESAI_SAISR_TDE_SHIFT)
|
||||||
|
#define ESAI_SAISR_TUE_SHIFT 14
|
||||||
|
#define ESAI_SAISR_TUE_MASK (1 << ESAI_SAISR_TUE_SHIFT)
|
||||||
|
#define ESAI_SAISR_TUE (1 << ESAI_SAISR_TUE_SHIFT)
|
||||||
|
#define ESAI_SAISR_TFS_SHIFT 13
|
||||||
|
#define ESAI_SAISR_TFS_MASK (1 << ESAI_SAISR_TFS_SHIFT)
|
||||||
|
#define ESAI_SAISR_TFS (1 << ESAI_SAISR_TFS_SHIFT)
|
||||||
|
#define ESAI_SAISR_RODF_SHIFT 10
|
||||||
|
#define ESAI_SAISR_RODF_MASK (1 << ESAI_SAISR_RODF_SHIFT)
|
||||||
|
#define ESAI_SAISR_RODF (1 << ESAI_SAISR_RODF_SHIFT)
|
||||||
|
#define ESAI_SAISR_REDF_SHIFT 9
|
||||||
|
#define ESAI_SAISR_REDF_MASK (1 << ESAI_SAISR_REDF_SHIFT)
|
||||||
|
#define ESAI_SAISR_REDF (1 << ESAI_SAISR_REDF_SHIFT)
|
||||||
|
#define ESAI_SAISR_RDF_SHIFT 8
|
||||||
|
#define ESAI_SAISR_RDF_MASK (1 << ESAI_SAISR_RDF_SHIFT)
|
||||||
|
#define ESAI_SAISR_RDF (1 << ESAI_SAISR_RDF_SHIFT)
|
||||||
|
#define ESAI_SAISR_ROE_SHIFT 7
|
||||||
|
#define ESAI_SAISR_ROE_MASK (1 << ESAI_SAISR_ROE_SHIFT)
|
||||||
|
#define ESAI_SAISR_ROE (1 << ESAI_SAISR_ROE_SHIFT)
|
||||||
|
#define ESAI_SAISR_RFS_SHIFT 6
|
||||||
|
#define ESAI_SAISR_RFS_MASK (1 << ESAI_SAISR_RFS_SHIFT)
|
||||||
|
#define ESAI_SAISR_RFS (1 << ESAI_SAISR_RFS_SHIFT)
|
||||||
|
#define ESAI_SAISR_IF2_SHIFT 2
|
||||||
|
#define ESAI_SAISR_IF2_MASK (1 << ESAI_SAISR_IF2_SHIFT)
|
||||||
|
#define ESAI_SAISR_IF2 (1 << ESAI_SAISR_IF2_SHIFT)
|
||||||
|
#define ESAI_SAISR_IF1_SHIFT 1
|
||||||
|
#define ESAI_SAISR_IF1_MASK (1 << ESAI_SAISR_IF1_SHIFT)
|
||||||
|
#define ESAI_SAISR_IF1 (1 << ESAI_SAISR_IF1_SHIFT)
|
||||||
|
#define ESAI_SAISR_IF0_SHIFT 0
|
||||||
|
#define ESAI_SAISR_IF0_MASK (1 << ESAI_SAISR_IF0_SHIFT)
|
||||||
|
#define ESAI_SAISR_IF0 (1 << ESAI_SAISR_IF0_SHIFT)
|
||||||
|
|
||||||
|
/* Serial Audio Interface Control Register -- REG_ESAI_SAICR 0xD0 */
|
||||||
|
#define ESAI_SAICR_ALC_SHIFT 8
|
||||||
|
#define ESAI_SAICR_ALC_MASK (1 << ESAI_SAICR_ALC_SHIFT)
|
||||||
|
#define ESAI_SAICR_ALC (1 << ESAI_SAICR_ALC_SHIFT)
|
||||||
|
#define ESAI_SAICR_TEBE_SHIFT 7
|
||||||
|
#define ESAI_SAICR_TEBE_MASK (1 << ESAI_SAICR_TEBE_SHIFT)
|
||||||
|
#define ESAI_SAICR_TEBE (1 << ESAI_SAICR_TEBE_SHIFT)
|
||||||
|
#define ESAI_SAICR_SYNC_SHIFT 6
|
||||||
|
#define ESAI_SAICR_SYNC_MASK (1 << ESAI_SAICR_SYNC_SHIFT)
|
||||||
|
#define ESAI_SAICR_SYNC (1 << ESAI_SAICR_SYNC_SHIFT)
|
||||||
|
#define ESAI_SAICR_OF2_SHIFT 2
|
||||||
|
#define ESAI_SAICR_OF2_MASK (1 << ESAI_SAICR_OF2_SHIFT)
|
||||||
|
#define ESAI_SAICR_OF2 (1 << ESAI_SAICR_OF2_SHIFT)
|
||||||
|
#define ESAI_SAICR_OF1_SHIFT 1
|
||||||
|
#define ESAI_SAICR_OF1_MASK (1 << ESAI_SAICR_OF1_SHIFT)
|
||||||
|
#define ESAI_SAICR_OF1 (1 << ESAI_SAICR_OF1_SHIFT)
|
||||||
|
#define ESAI_SAICR_OF0_SHIFT 0
|
||||||
|
#define ESAI_SAICR_OF0_MASK (1 << ESAI_SAICR_OF0_SHIFT)
|
||||||
|
#define ESAI_SAICR_OF0 (1 << ESAI_SAICR_OF0_SHIFT)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmit Control Register -- REG_ESAI_TCR 0xD4
|
||||||
|
* Receive Control Register -- REG_ESAI_RCR 0xDC
|
||||||
|
*/
|
||||||
|
#define ESAI_xCR_xLIE_SHIFT 23
|
||||||
|
#define ESAI_xCR_xLIE_MASK (1 << ESAI_xCR_xLIE_SHIFT)
|
||||||
|
#define ESAI_xCR_xLIE (1 << ESAI_xCR_xLIE_SHIFT)
|
||||||
|
#define ESAI_xCR_xIE_SHIFT 22
|
||||||
|
#define ESAI_xCR_xIE_MASK (1 << ESAI_xCR_xIE_SHIFT)
|
||||||
|
#define ESAI_xCR_xIE (1 << ESAI_xCR_xIE_SHIFT)
|
||||||
|
#define ESAI_xCR_xEDIE_SHIFT 21
|
||||||
|
#define ESAI_xCR_xEDIE_MASK (1 << ESAI_xCR_xEDIE_SHIFT)
|
||||||
|
#define ESAI_xCR_xEDIE (1 << ESAI_xCR_xEDIE_SHIFT)
|
||||||
|
#define ESAI_xCR_xEIE_SHIFT 20
|
||||||
|
#define ESAI_xCR_xEIE_MASK (1 << ESAI_xCR_xEIE_SHIFT)
|
||||||
|
#define ESAI_xCR_xEIE (1 << ESAI_xCR_xEIE_SHIFT)
|
||||||
|
#define ESAI_xCR_xPR_SHIFT 19
|
||||||
|
#define ESAI_xCR_xPR_MASK (1 << ESAI_xCR_xPR_SHIFT)
|
||||||
|
#define ESAI_xCR_xPR (1 << ESAI_xCR_xPR_SHIFT)
|
||||||
|
#define ESAI_xCR_PADC_SHIFT 17
|
||||||
|
#define ESAI_xCR_PADC_MASK (1 << ESAI_xCR_PADC_SHIFT)
|
||||||
|
#define ESAI_xCR_PADC (1 << ESAI_xCR_PADC_SHIFT)
|
||||||
|
#define ESAI_xCR_xFSR_SHIFT 16
|
||||||
|
#define ESAI_xCR_xFSR_MASK (1 << ESAI_xCR_xFSR_SHIFT)
|
||||||
|
#define ESAI_xCR_xFSR (1 << ESAI_xCR_xFSR_SHIFT)
|
||||||
|
#define ESAI_xCR_xFSL_SHIFT 15
|
||||||
|
#define ESAI_xCR_xFSL_MASK (1 << ESAI_xCR_xFSL_SHIFT)
|
||||||
|
#define ESAI_xCR_xFSL (1 << ESAI_xCR_xFSL_SHIFT)
|
||||||
|
#define ESAI_xCR_xSWS_SHIFT 10
|
||||||
|
#define ESAI_xCR_xSWS_WIDTH 5
|
||||||
|
#define ESAI_xCR_xSWS_MASK (((1 << ESAI_xCR_xSWS_WIDTH) - 1) << ESAI_xCR_xSWS_SHIFT)
|
||||||
|
#define ESAI_xCR_xSWS(s, w) ((w < 24 ? (s - w + ((w - 8) >> 2)) : (s < 32 ? 0x1e : 0x1f)) << ESAI_xCR_xSWS_SHIFT)
|
||||||
|
#define ESAI_xCR_xMOD_SHIFT 8
|
||||||
|
#define ESAI_xCR_xMOD_WIDTH 2
|
||||||
|
#define ESAI_xCR_xMOD_MASK (((1 << ESAI_xCR_xMOD_WIDTH) - 1) << ESAI_xCR_xMOD_SHIFT)
|
||||||
|
#define ESAI_xCR_xMOD_ONDEMAND (0x1 << ESAI_xCR_xMOD_SHIFT)
|
||||||
|
#define ESAI_xCR_xMOD_NETWORK (0x1 << ESAI_xCR_xMOD_SHIFT)
|
||||||
|
#define ESAI_xCR_xMOD_AC97 (0x3 << ESAI_xCR_xMOD_SHIFT)
|
||||||
|
#define ESAI_xCR_xWA_SHIFT 7
|
||||||
|
#define ESAI_xCR_xWA_MASK (1 << ESAI_xCR_xWA_SHIFT)
|
||||||
|
#define ESAI_xCR_xWA (1 << ESAI_xCR_xWA_SHIFT)
|
||||||
|
#define ESAI_xCR_xSHFD_SHIFT 6
|
||||||
|
#define ESAI_xCR_xSHFD_MASK (1 << ESAI_xCR_xSHFD_SHIFT)
|
||||||
|
#define ESAI_xCR_xSHFD (1 << ESAI_xCR_xSHFD_SHIFT)
|
||||||
|
#define ESAI_xCR_xE_SHIFT 0
|
||||||
|
#define ESAI_xCR_TE_WIDTH 6
|
||||||
|
#define ESAI_xCR_RE_WIDTH 4
|
||||||
|
#define ESAI_xCR_TE_MASK (((1 << ESAI_xCR_TE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT)
|
||||||
|
#define ESAI_xCR_RE_MASK (((1 << ESAI_xCR_RE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT)
|
||||||
|
#define ESAI_xCR_TE(x) ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_TE_MASK)
|
||||||
|
#define ESAI_xCR_RE(x) ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_RE_MASK)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmit Clock Control Register -- REG_ESAI_TCCR 0xD8
|
||||||
|
* Receive Clock Control Register -- REG_ESAI_RCCR 0xE0
|
||||||
|
*/
|
||||||
|
#define ESAI_xCCR_xHCKD_SHIFT 23
|
||||||
|
#define ESAI_xCCR_xHCKD_MASK (1 << ESAI_xCCR_xHCKD_SHIFT)
|
||||||
|
#define ESAI_xCCR_xHCKD (1 << ESAI_xCCR_xHCKD_SHIFT)
|
||||||
|
#define ESAI_xCCR_xFSD_SHIFT 22
|
||||||
|
#define ESAI_xCCR_xFSD_MASK (1 << ESAI_xCCR_xFSD_SHIFT)
|
||||||
|
#define ESAI_xCCR_xFSD (1 << ESAI_xCCR_xFSD_SHIFT)
|
||||||
|
#define ESAI_xCCR_xCKD_SHIFT 21
|
||||||
|
#define ESAI_xCCR_xCKD_MASK (1 << ESAI_xCCR_xCKD_SHIFT)
|
||||||
|
#define ESAI_xCCR_xCKD (1 << ESAI_xCCR_xCKD_SHIFT)
|
||||||
|
#define ESAI_xCCR_xHCKP_SHIFT 20
|
||||||
|
#define ESAI_xCCR_xHCKP_MASK (1 << ESAI_xCCR_xHCKP_SHIFT)
|
||||||
|
#define ESAI_xCCR_xHCKP (1 << ESAI_xCCR_xHCKP_SHIFT)
|
||||||
|
#define ESAI_xCCR_xFSP_SHIFT 19
|
||||||
|
#define ESAI_xCCR_xFSP_MASK (1 << ESAI_xCCR_xFSP_SHIFT)
|
||||||
|
#define ESAI_xCCR_xFSP (1 << ESAI_xCCR_xFSP_SHIFT)
|
||||||
|
#define ESAI_xCCR_xCKP_SHIFT 18
|
||||||
|
#define ESAI_xCCR_xCKP_MASK (1 << ESAI_xCCR_xCKP_SHIFT)
|
||||||
|
#define ESAI_xCCR_xCKP (1 << ESAI_xCCR_xCKP_SHIFT)
|
||||||
|
#define ESAI_xCCR_xFP_SHIFT 14
|
||||||
|
#define ESAI_xCCR_xFP_WIDTH 4
|
||||||
|
#define ESAI_xCCR_xFP_MASK (((1 << ESAI_xCCR_xFP_WIDTH) - 1) << ESAI_xCCR_xFP_SHIFT)
|
||||||
|
#define ESAI_xCCR_xFP(v) ((((v) - 1) << ESAI_xCCR_xFP_SHIFT) & ESAI_xCCR_xFP_MASK)
|
||||||
|
#define ESAI_xCCR_xDC_SHIFT 9
|
||||||
|
#define ESAI_xCCR_xDC_WIDTH 4
|
||||||
|
#define ESAI_xCCR_xDC_MASK (((1 << ESAI_xCCR_xDC_WIDTH) - 1) << ESAI_xCCR_xDC_SHIFT)
|
||||||
|
#define ESAI_xCCR_xDC(v) ((((v) - 1) << ESAI_xCCR_xDC_SHIFT) & ESAI_xCCR_xDC_MASK)
|
||||||
|
#define ESAI_xCCR_xPSR_SHIFT 8
|
||||||
|
#define ESAI_xCCR_xPSR_MASK (1 << ESAI_xCCR_xPSR_SHIFT)
|
||||||
|
#define ESAI_xCCR_xPSR_BYPASS (1 << ESAI_xCCR_xPSR_SHIFT)
|
||||||
|
#define ESAI_xCCR_xPSR_DIV8 (0 << ESAI_xCCR_xPSR_SHIFT)
|
||||||
|
#define ESAI_xCCR_xPM_SHIFT 0
|
||||||
|
#define ESAI_xCCR_xPM_WIDTH 8
|
||||||
|
#define ESAI_xCCR_xPM_MASK (((1 << ESAI_xCCR_xPM_WIDTH) - 1) << ESAI_xCCR_xPM_SHIFT)
|
||||||
|
#define ESAI_xCCR_xPM(v) ((((v) - 1) << ESAI_xCCR_xPM_SHIFT) & ESAI_xCCR_xPM_MASK)
|
||||||
|
|
||||||
|
/* Transmit Slot Mask Register A/B -- REG_ESAI_TSMA/B 0xE4 ~ 0xF0 */
|
||||||
|
#define ESAI_xSMA_xS_SHIFT 0
|
||||||
|
#define ESAI_xSMA_xS_WIDTH 16
|
||||||
|
#define ESAI_xSMA_xS_MASK (((1 << ESAI_xSMA_xS_WIDTH) - 1) << ESAI_xSMA_xS_SHIFT)
|
||||||
|
#define ESAI_xSMA_xS(v) ((v) & ESAI_xSMA_xS_MASK)
|
||||||
|
#define ESAI_xSMB_xS_SHIFT 0
|
||||||
|
#define ESAI_xSMB_xS_WIDTH 16
|
||||||
|
#define ESAI_xSMB_xS_MASK (((1 << ESAI_xSMB_xS_WIDTH) - 1) << ESAI_xSMB_xS_SHIFT)
|
||||||
|
#define ESAI_xSMB_xS(v) (((v) >> ESAI_xSMA_xS_WIDTH) & ESAI_xSMA_xS_MASK)
|
||||||
|
|
||||||
|
/* Port C Direction Register -- REG_ESAI_PRRC 0xF8 */
|
||||||
|
#define ESAI_PRRC_PDC_SHIFT 0
|
||||||
|
#define ESAI_PRRC_PDC_WIDTH 12
|
||||||
|
#define ESAI_PRRC_PDC_MASK (((1 << ESAI_PRRC_PDC_WIDTH) - 1) << ESAI_PRRC_PDC_SHIFT)
|
||||||
|
#define ESAI_PRRC_PDC(v) ((v) & ESAI_PRRC_PDC_MASK)
|
||||||
|
|
||||||
|
/* Port C Control Register -- REG_ESAI_PCRC 0xFC */
|
||||||
|
#define ESAI_PCRC_PC_SHIFT 0
|
||||||
|
#define ESAI_PCRC_PC_WIDTH 12
|
||||||
|
#define ESAI_PCRC_PC_MASK (((1 << ESAI_PCRC_PC_WIDTH) - 1) << ESAI_PCRC_PC_SHIFT)
|
||||||
|
#define ESAI_PCRC_PC(v) ((v) & ESAI_PCRC_PC_MASK)
|
||||||
|
|
||||||
|
#define ESAI_GPIO 0xfff
|
||||||
|
|
||||||
|
/* ESAI clock source */
|
||||||
|
#define ESAI_HCKT_FSYS 0
|
||||||
|
#define ESAI_HCKT_EXTAL 1
|
||||||
|
#define ESAI_HCKR_FSYS 2
|
||||||
|
#define ESAI_HCKR_EXTAL 3
|
||||||
|
|
||||||
|
/* ESAI clock divider */
|
||||||
|
#define ESAI_TX_DIV_PSR 0
|
||||||
|
#define ESAI_TX_DIV_PM 1
|
||||||
|
#define ESAI_TX_DIV_FP 2
|
||||||
|
#define ESAI_RX_DIV_PSR 3
|
||||||
|
#define ESAI_RX_DIV_PM 4
|
||||||
|
#define ESAI_RX_DIV_FP 5
|
||||||
|
#endif /* _FSL_ESAI_DAI_H */
|
|
@ -62,26 +62,25 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
|
||||||
reg_cr2 = FSL_SAI_RCR2;
|
reg_cr2 = FSL_SAI_RCR2;
|
||||||
|
|
||||||
val_cr2 = sai_readl(sai, sai->base + reg_cr2);
|
val_cr2 = sai_readl(sai, sai->base + reg_cr2);
|
||||||
|
val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
|
||||||
|
|
||||||
switch (clk_id) {
|
switch (clk_id) {
|
||||||
case FSL_SAI_CLK_BUS:
|
case FSL_SAI_CLK_BUS:
|
||||||
val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
|
|
||||||
val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
|
val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
|
||||||
break;
|
break;
|
||||||
case FSL_SAI_CLK_MAST1:
|
case FSL_SAI_CLK_MAST1:
|
||||||
val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
|
|
||||||
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
|
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
|
||||||
break;
|
break;
|
||||||
case FSL_SAI_CLK_MAST2:
|
case FSL_SAI_CLK_MAST2:
|
||||||
val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
|
|
||||||
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
|
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
|
||||||
break;
|
break;
|
||||||
case FSL_SAI_CLK_MAST3:
|
case FSL_SAI_CLK_MAST3:
|
||||||
val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
|
|
||||||
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
|
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
sai_writel(sai, val_cr2, sai->base + reg_cr2);
|
sai_writel(sai, val_cr2, sai->base + reg_cr2);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/debugfs.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -80,8 +81,7 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
|
||||||
* ALSA that we support all rates and let the codec driver decide what rates
|
* ALSA that we support all rates and let the codec driver decide what rates
|
||||||
* are really supported.
|
* are really supported.
|
||||||
*/
|
*/
|
||||||
#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
|
#define FSLSSI_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
|
||||||
SNDRV_PCM_RATE_CONTINUOUS)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FSLSSI_I2S_FORMATS: audio formats supported by the SSI
|
* FSLSSI_I2S_FORMATS: audio formats supported by the SSI
|
||||||
|
@ -107,12 +107,33 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
|
||||||
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
|
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* SIER bitflag of interrupts to enable */
|
#define FSLSSI_SIER_DBG_RX_FLAGS (CCSR_SSI_SIER_RFF0_EN | \
|
||||||
#define SIER_FLAGS (CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | \
|
CCSR_SSI_SIER_RLS_EN | CCSR_SSI_SIER_RFS_EN | \
|
||||||
CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | \
|
CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_RFRC_EN)
|
||||||
CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | \
|
#define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \
|
||||||
CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \
|
CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \
|
||||||
CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN)
|
CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_EN)
|
||||||
|
#define FSLSSI_SISR_MASK (FSLSSI_SIER_DBG_RX_FLAGS | FSLSSI_SIER_DBG_TX_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
enum fsl_ssi_type {
|
||||||
|
FSL_SSI_MCP8610,
|
||||||
|
FSL_SSI_MX21,
|
||||||
|
FSL_SSI_MX35,
|
||||||
|
FSL_SSI_MX51,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fsl_ssi_reg_val {
|
||||||
|
u32 sier;
|
||||||
|
u32 srcr;
|
||||||
|
u32 stcr;
|
||||||
|
u32 scr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fsl_ssi_rxtx_reg_val {
|
||||||
|
struct fsl_ssi_reg_val rx;
|
||||||
|
struct fsl_ssi_reg_val tx;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fsl_ssi_private: per-SSI private data
|
* fsl_ssi_private: per-SSI private data
|
||||||
|
@ -133,14 +154,16 @@ struct fsl_ssi_private {
|
||||||
unsigned int irq;
|
unsigned int irq;
|
||||||
unsigned int fifo_depth;
|
unsigned int fifo_depth;
|
||||||
struct snd_soc_dai_driver cpu_dai_drv;
|
struct snd_soc_dai_driver cpu_dai_drv;
|
||||||
struct device_attribute dev_attr;
|
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
|
|
||||||
|
enum fsl_ssi_type hw_type;
|
||||||
bool new_binding;
|
bool new_binding;
|
||||||
bool ssi_on_imx;
|
bool ssi_on_imx;
|
||||||
bool imx_ac97;
|
bool imx_ac97;
|
||||||
bool use_dma;
|
bool use_dma;
|
||||||
bool baudclk_locked;
|
bool baudclk_locked;
|
||||||
|
bool irq_stats;
|
||||||
|
bool offline_config;
|
||||||
u8 i2s_mode;
|
u8 i2s_mode;
|
||||||
spinlock_t baudclk_lock;
|
spinlock_t baudclk_lock;
|
||||||
struct clk *baudclk;
|
struct clk *baudclk;
|
||||||
|
@ -150,6 +173,8 @@ struct fsl_ssi_private {
|
||||||
struct imx_dma_data filter_data_tx;
|
struct imx_dma_data filter_data_tx;
|
||||||
struct imx_dma_data filter_data_rx;
|
struct imx_dma_data filter_data_rx;
|
||||||
struct imx_pcm_fiq_params fiq_params;
|
struct imx_pcm_fiq_params fiq_params;
|
||||||
|
/* Register values for rx/tx configuration */
|
||||||
|
struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned int rfrc;
|
unsigned int rfrc;
|
||||||
|
@ -174,10 +199,21 @@ struct fsl_ssi_private {
|
||||||
unsigned int tfe1;
|
unsigned int tfe1;
|
||||||
unsigned int tfe0;
|
unsigned int tfe0;
|
||||||
} stats;
|
} stats;
|
||||||
|
struct dentry *dbg_dir;
|
||||||
|
struct dentry *dbg_stats;
|
||||||
|
|
||||||
char name[1];
|
char name[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id fsl_ssi_ids[] = {
|
||||||
|
{ .compatible = "fsl,mpc8610-ssi", .data = (void *) FSL_SSI_MCP8610},
|
||||||
|
{ .compatible = "fsl,imx51-ssi", .data = (void *) FSL_SSI_MX51},
|
||||||
|
{ .compatible = "fsl,imx35-ssi", .data = (void *) FSL_SSI_MX35},
|
||||||
|
{ .compatible = "fsl,imx21-ssi", .data = (void *) FSL_SSI_MX21},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fsl_ssi_isr: SSI interrupt handler
|
* fsl_ssi_isr: SSI interrupt handler
|
||||||
*
|
*
|
||||||
|
@ -196,23 +232,40 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
||||||
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
||||||
irqreturn_t ret = IRQ_NONE;
|
irqreturn_t ret = IRQ_NONE;
|
||||||
__be32 sisr;
|
__be32 sisr;
|
||||||
__be32 sisr2 = 0;
|
__be32 sisr2;
|
||||||
|
__be32 sisr_write_mask = 0;
|
||||||
|
|
||||||
|
switch (ssi_private->hw_type) {
|
||||||
|
case FSL_SSI_MX21:
|
||||||
|
sisr_write_mask = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FSL_SSI_MCP8610:
|
||||||
|
case FSL_SSI_MX35:
|
||||||
|
sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC |
|
||||||
|
CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
|
||||||
|
CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FSL_SSI_MX51:
|
||||||
|
sisr_write_mask = CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
|
||||||
|
CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* We got an interrupt, so read the status register to see what we
|
/* We got an interrupt, so read the status register to see what we
|
||||||
were interrupted for. We mask it with the Interrupt Enable register
|
were interrupted for. We mask it with the Interrupt Enable register
|
||||||
so that we only check for events that we're interested in.
|
so that we only check for events that we're interested in.
|
||||||
*/
|
*/
|
||||||
sisr = read_ssi(&ssi->sisr) & SIER_FLAGS;
|
sisr = read_ssi(&ssi->sisr) & FSLSSI_SISR_MASK;
|
||||||
|
|
||||||
if (sisr & CCSR_SSI_SISR_RFRC) {
|
if (sisr & CCSR_SSI_SISR_RFRC) {
|
||||||
ssi_private->stats.rfrc++;
|
ssi_private->stats.rfrc++;
|
||||||
sisr2 |= CCSR_SSI_SISR_RFRC;
|
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sisr & CCSR_SSI_SISR_TFRC) {
|
if (sisr & CCSR_SSI_SISR_TFRC) {
|
||||||
ssi_private->stats.tfrc++;
|
ssi_private->stats.tfrc++;
|
||||||
sisr2 |= CCSR_SSI_SISR_TFRC;
|
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,25 +306,21 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
||||||
|
|
||||||
if (sisr & CCSR_SSI_SISR_ROE1) {
|
if (sisr & CCSR_SSI_SISR_ROE1) {
|
||||||
ssi_private->stats.roe1++;
|
ssi_private->stats.roe1++;
|
||||||
sisr2 |= CCSR_SSI_SISR_ROE1;
|
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sisr & CCSR_SSI_SISR_ROE0) {
|
if (sisr & CCSR_SSI_SISR_ROE0) {
|
||||||
ssi_private->stats.roe0++;
|
ssi_private->stats.roe0++;
|
||||||
sisr2 |= CCSR_SSI_SISR_ROE0;
|
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sisr & CCSR_SSI_SISR_TUE1) {
|
if (sisr & CCSR_SSI_SISR_TUE1) {
|
||||||
ssi_private->stats.tue1++;
|
ssi_private->stats.tue1++;
|
||||||
sisr2 |= CCSR_SSI_SISR_TUE1;
|
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sisr & CCSR_SSI_SISR_TUE0) {
|
if (sisr & CCSR_SSI_SISR_TUE0) {
|
||||||
ssi_private->stats.tue0++;
|
ssi_private->stats.tue0++;
|
||||||
sisr2 |= CCSR_SSI_SISR_TUE0;
|
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,6 +364,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sisr2 = sisr & sisr_write_mask;
|
||||||
/* Clear the bits that we set */
|
/* Clear the bits that we set */
|
||||||
if (sisr2)
|
if (sisr2)
|
||||||
write_ssi(sisr2, &ssi->sisr);
|
write_ssi(sisr2, &ssi->sisr);
|
||||||
|
@ -322,6 +372,245 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||||
|
/* Show the statistics of a flag only if its interrupt is enabled. The
|
||||||
|
* compiler will optimze this code to a no-op if the interrupt is not
|
||||||
|
* enabled.
|
||||||
|
*/
|
||||||
|
#define SIER_SHOW(flag, name) \
|
||||||
|
do { \
|
||||||
|
if (FSLSSI_SISR_MASK & CCSR_SSI_SIER_##flag) \
|
||||||
|
seq_printf(s, #name "=%u\n", ssi_private->stats.name); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fsl_sysfs_ssi_show: display SSI statistics
|
||||||
|
*
|
||||||
|
* Display the statistics for the current SSI device. To avoid confusion,
|
||||||
|
* we only show those counts that are enabled.
|
||||||
|
*/
|
||||||
|
static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
|
||||||
|
{
|
||||||
|
struct fsl_ssi_private *ssi_private = s->private;
|
||||||
|
|
||||||
|
SIER_SHOW(RFRC_EN, rfrc);
|
||||||
|
SIER_SHOW(TFRC_EN, tfrc);
|
||||||
|
SIER_SHOW(CMDAU_EN, cmdau);
|
||||||
|
SIER_SHOW(CMDDU_EN, cmddu);
|
||||||
|
SIER_SHOW(RXT_EN, rxt);
|
||||||
|
SIER_SHOW(RDR1_EN, rdr1);
|
||||||
|
SIER_SHOW(RDR0_EN, rdr0);
|
||||||
|
SIER_SHOW(TDE1_EN, tde1);
|
||||||
|
SIER_SHOW(TDE0_EN, tde0);
|
||||||
|
SIER_SHOW(ROE1_EN, roe1);
|
||||||
|
SIER_SHOW(ROE0_EN, roe0);
|
||||||
|
SIER_SHOW(TUE1_EN, tue1);
|
||||||
|
SIER_SHOW(TUE0_EN, tue0);
|
||||||
|
SIER_SHOW(TFS_EN, tfs);
|
||||||
|
SIER_SHOW(RFS_EN, rfs);
|
||||||
|
SIER_SHOW(TLS_EN, tls);
|
||||||
|
SIER_SHOW(RLS_EN, rls);
|
||||||
|
SIER_SHOW(RFF1_EN, rff1);
|
||||||
|
SIER_SHOW(RFF0_EN, rff0);
|
||||||
|
SIER_SHOW(TFE1_EN, tfe1);
|
||||||
|
SIER_SHOW(TFE0_EN, tfe0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsl_ssi_stats_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
return single_open(file, fsl_ssi_stats_show, inode->i_private);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations fsl_ssi_stats_ops = {
|
||||||
|
.open = fsl_ssi_stats_open,
|
||||||
|
.read = seq_read,
|
||||||
|
.llseek = seq_lseek,
|
||||||
|
.release = single_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private,
|
||||||
|
struct device *dev)
|
||||||
|
{
|
||||||
|
ssi_private->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
|
||||||
|
if (!ssi_private->dbg_dir)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ssi_private->dbg_stats = debugfs_create_file("stats", S_IRUGO,
|
||||||
|
ssi_private->dbg_dir, ssi_private, &fsl_ssi_stats_ops);
|
||||||
|
if (!ssi_private->dbg_stats) {
|
||||||
|
debugfs_remove(ssi_private->dbg_dir);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
|
||||||
|
{
|
||||||
|
debugfs_remove(ssi_private->dbg_stats);
|
||||||
|
debugfs_remove(ssi_private->dbg_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private,
|
||||||
|
struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable/Disable all rx/tx config flags at once.
|
||||||
|
*/
|
||||||
|
static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
|
||||||
|
bool enable)
|
||||||
|
{
|
||||||
|
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
||||||
|
struct fsl_ssi_rxtx_reg_val *vals = &ssi_private->rxtx_reg_val;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
write_ssi_mask(&ssi->sier, 0, vals->rx.sier | vals->tx.sier);
|
||||||
|
write_ssi_mask(&ssi->srcr, 0, vals->rx.srcr | vals->tx.srcr);
|
||||||
|
write_ssi_mask(&ssi->stcr, 0, vals->rx.stcr | vals->tx.stcr);
|
||||||
|
} else {
|
||||||
|
write_ssi_mask(&ssi->srcr, vals->rx.srcr | vals->tx.srcr, 0);
|
||||||
|
write_ssi_mask(&ssi->stcr, vals->rx.stcr | vals->tx.stcr, 0);
|
||||||
|
write_ssi_mask(&ssi->sier, vals->rx.sier | vals->tx.sier, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable/Disable a ssi configuration. You have to pass either
|
||||||
|
* ssi_private->rxtx_reg_val.rx or tx as vals parameter.
|
||||||
|
*/
|
||||||
|
static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
|
||||||
|
struct fsl_ssi_reg_val *vals)
|
||||||
|
{
|
||||||
|
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
||||||
|
struct fsl_ssi_reg_val *avals;
|
||||||
|
u32 scr_val = read_ssi(&ssi->scr);
|
||||||
|
int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
|
||||||
|
!!(scr_val & CCSR_SSI_SCR_RE);
|
||||||
|
|
||||||
|
/* Find the other direction values rx or tx which we do not want to
|
||||||
|
* modify */
|
||||||
|
if (&ssi_private->rxtx_reg_val.rx == vals)
|
||||||
|
avals = &ssi_private->rxtx_reg_val.tx;
|
||||||
|
else
|
||||||
|
avals = &ssi_private->rxtx_reg_val.rx;
|
||||||
|
|
||||||
|
/* If vals should be disabled, start with disabling the unit */
|
||||||
|
if (!enable) {
|
||||||
|
u32 scr = vals->scr & (vals->scr ^ avals->scr);
|
||||||
|
write_ssi_mask(&ssi->scr, scr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We are running on a SoC which does not support online SSI
|
||||||
|
* reconfiguration, so we have to enable all necessary flags at once
|
||||||
|
* even if we do not use them later (capture and playback configuration)
|
||||||
|
*/
|
||||||
|
if (ssi_private->offline_config) {
|
||||||
|
if ((enable && !nr_active_streams) ||
|
||||||
|
(!enable && nr_active_streams == 1))
|
||||||
|
fsl_ssi_rxtx_config(ssi_private, enable);
|
||||||
|
|
||||||
|
goto config_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configure single direction units while the SSI unit is running
|
||||||
|
* (online configuration)
|
||||||
|
*/
|
||||||
|
if (enable) {
|
||||||
|
write_ssi_mask(&ssi->sier, 0, vals->sier);
|
||||||
|
write_ssi_mask(&ssi->srcr, 0, vals->srcr);
|
||||||
|
write_ssi_mask(&ssi->stcr, 0, vals->stcr);
|
||||||
|
} else {
|
||||||
|
u32 sier;
|
||||||
|
u32 srcr;
|
||||||
|
u32 stcr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disabling the necessary flags for one of rx/tx while the
|
||||||
|
* other stream is active is a little bit more difficult. We
|
||||||
|
* have to disable only those flags that differ between both
|
||||||
|
* streams (rx XOR tx) and that are set in the stream that is
|
||||||
|
* disabled now. Otherwise we could alter flags of the other
|
||||||
|
* stream
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* These assignments are simply vals without bits set in avals*/
|
||||||
|
sier = vals->sier & (vals->sier ^ avals->sier);
|
||||||
|
srcr = vals->srcr & (vals->srcr ^ avals->srcr);
|
||||||
|
stcr = vals->stcr & (vals->stcr ^ avals->stcr);
|
||||||
|
|
||||||
|
write_ssi_mask(&ssi->srcr, srcr, 0);
|
||||||
|
write_ssi_mask(&ssi->stcr, stcr, 0);
|
||||||
|
write_ssi_mask(&ssi->sier, sier, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
config_done:
|
||||||
|
/* Enabling of subunits is done after configuration */
|
||||||
|
if (enable)
|
||||||
|
write_ssi_mask(&ssi->scr, 0, vals->scr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void fsl_ssi_rx_config(struct fsl_ssi_private *ssi_private, bool enable)
|
||||||
|
{
|
||||||
|
fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fsl_ssi_tx_config(struct fsl_ssi_private *ssi_private, bool enable)
|
||||||
|
{
|
||||||
|
fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup rx/tx register values used to enable/disable the streams. These will
|
||||||
|
* be used later in fsl_ssi_config to setup the streams without the need to
|
||||||
|
* check for all different SSI modes.
|
||||||
|
*/
|
||||||
|
static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private)
|
||||||
|
{
|
||||||
|
struct fsl_ssi_rxtx_reg_val *reg = &ssi_private->rxtx_reg_val;
|
||||||
|
|
||||||
|
reg->rx.sier = CCSR_SSI_SIER_RFF0_EN;
|
||||||
|
reg->rx.srcr = CCSR_SSI_SRCR_RFEN0;
|
||||||
|
reg->rx.scr = 0;
|
||||||
|
reg->tx.sier = CCSR_SSI_SIER_TFE0_EN;
|
||||||
|
reg->tx.stcr = CCSR_SSI_STCR_TFEN0;
|
||||||
|
reg->tx.scr = 0;
|
||||||
|
|
||||||
|
if (!ssi_private->imx_ac97) {
|
||||||
|
reg->rx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE;
|
||||||
|
reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN;
|
||||||
|
reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE;
|
||||||
|
reg->tx.sier |= CCSR_SSI_SIER_TFE0_EN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssi_private->use_dma) {
|
||||||
|
reg->rx.sier |= CCSR_SSI_SIER_RDMAE;
|
||||||
|
reg->tx.sier |= CCSR_SSI_SIER_TDMAE;
|
||||||
|
} else {
|
||||||
|
reg->rx.sier |= CCSR_SSI_SIER_RIE;
|
||||||
|
reg->tx.sier |= CCSR_SSI_SIER_TIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg->rx.sier |= FSLSSI_SIER_DBG_RX_FLAGS;
|
||||||
|
reg->tx.sier |= FSLSSI_SIER_DBG_TX_FLAGS;
|
||||||
|
}
|
||||||
|
|
||||||
static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
|
static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
|
||||||
{
|
{
|
||||||
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
||||||
|
@ -358,6 +647,8 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
|
||||||
u8 wm;
|
u8 wm;
|
||||||
int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
|
int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
|
||||||
|
|
||||||
|
fsl_ssi_setup_reg_vals(ssi_private);
|
||||||
|
|
||||||
if (ssi_private->imx_ac97)
|
if (ssi_private->imx_ac97)
|
||||||
ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
|
ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
|
||||||
else
|
else
|
||||||
|
@ -381,13 +672,12 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
|
||||||
ssi_private->i2s_mode |
|
ssi_private->i2s_mode |
|
||||||
(synchronous ? CCSR_SSI_SCR_SYN : 0));
|
(synchronous ? CCSR_SSI_SCR_SYN : 0));
|
||||||
|
|
||||||
write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
|
write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFSI |
|
||||||
CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
|
CCSR_SSI_STCR_TEFS | CCSR_SSI_STCR_TSCKP, &ssi->stcr);
|
||||||
CCSR_SSI_STCR_TSCKP, &ssi->stcr);
|
|
||||||
|
write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFSI |
|
||||||
|
CCSR_SSI_SRCR_REFS | CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
|
||||||
|
|
||||||
write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
|
|
||||||
CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
|
|
||||||
CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
|
|
||||||
/*
|
/*
|
||||||
* The DC and PM bits are only used if the SSI is the clock master.
|
* The DC and PM bits are only used if the SSI is the clock master.
|
||||||
*/
|
*/
|
||||||
|
@ -420,6 +710,17 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
|
||||||
if (ssi_private->imx_ac97)
|
if (ssi_private->imx_ac97)
|
||||||
fsl_ssi_setup_ac97(ssi_private);
|
fsl_ssi_setup_ac97(ssi_private);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set a default slot number so that there is no need for those common
|
||||||
|
* cases like I2S mode to call the extra set_tdm_slot() any more.
|
||||||
|
*/
|
||||||
|
if (!ssi_private->imx_ac97) {
|
||||||
|
write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK,
|
||||||
|
CCSR_SSI_SxCCR_DC(2));
|
||||||
|
write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK,
|
||||||
|
CCSR_SSI_SxCCR_DC(2));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,50 +1063,26 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||||
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
||||||
unsigned int sier_bits;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
/*
|
|
||||||
* Enable only the interrupts and DMA requests
|
|
||||||
* that are needed for the channel. As the fiq
|
|
||||||
* is polling for this bits, we have to ensure
|
|
||||||
* that this are aligned with the preallocated
|
|
||||||
* buffers
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
||||||
if (ssi_private->use_dma)
|
|
||||||
sier_bits = SIER_FLAGS;
|
|
||||||
else
|
|
||||||
sier_bits = CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN;
|
|
||||||
} else {
|
|
||||||
if (ssi_private->use_dma)
|
|
||||||
sier_bits = SIER_FLAGS;
|
|
||||||
else
|
|
||||||
sier_bits = CCSR_SSI_SIER_RIE | CCSR_SSI_SIER_RFF0_EN;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case SNDRV_PCM_TRIGGER_START:
|
case SNDRV_PCM_TRIGGER_START:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
write_ssi_mask(&ssi->scr, 0,
|
fsl_ssi_tx_config(ssi_private, true);
|
||||||
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
|
|
||||||
else
|
else
|
||||||
write_ssi_mask(&ssi->scr, 0,
|
fsl_ssi_rx_config(ssi_private, true);
|
||||||
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SNDRV_PCM_TRIGGER_STOP:
|
case SNDRV_PCM_TRIGGER_STOP:
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
|
fsl_ssi_tx_config(ssi_private, false);
|
||||||
else
|
else
|
||||||
write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
|
fsl_ssi_rx_config(ssi_private, false);
|
||||||
|
|
||||||
if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) &
|
if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) &
|
||||||
(CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) {
|
(CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) {
|
||||||
write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
|
|
||||||
spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
|
spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
|
||||||
ssi_private->baudclk_locked = false;
|
ssi_private->baudclk_locked = false;
|
||||||
spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
|
spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
|
||||||
|
@ -816,7 +1093,12 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_ssi(sier_bits, &ssi->sier);
|
if (ssi_private->imx_ac97) {
|
||||||
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
|
write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
|
||||||
|
else
|
||||||
|
write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -864,58 +1146,6 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
|
||||||
.name = "fsl-ssi",
|
.name = "fsl-ssi",
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* fsl_ssi_ac97_trigger: start and stop the AC97 receive/transmit.
|
|
||||||
*
|
|
||||||
* This function is called by ALSA to start, stop, pause, and resume the
|
|
||||||
* transfer of data.
|
|
||||||
*/
|
|
||||||
static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
|
|
||||||
struct snd_soc_dai *dai)
|
|
||||||
{
|
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
||||||
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(
|
|
||||||
rtd->cpu_dai);
|
|
||||||
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case SNDRV_PCM_TRIGGER_START:
|
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
||||||
write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_TIE |
|
|
||||||
CCSR_SSI_SIER_TFE0_EN);
|
|
||||||
else
|
|
||||||
write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_RIE |
|
|
||||||
CCSR_SSI_SIER_RFF0_EN);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SNDRV_PCM_TRIGGER_STOP:
|
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
||||||
write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_TIE |
|
|
||||||
CCSR_SSI_SIER_TFE0_EN, 0);
|
|
||||||
else
|
|
||||||
write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_RIE |
|
|
||||||
CCSR_SSI_SIER_RFF0_EN, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
||||||
write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
|
|
||||||
else
|
|
||||||
write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct snd_soc_dai_ops fsl_ssi_ac97_dai_ops = {
|
|
||||||
.startup = fsl_ssi_startup,
|
|
||||||
.trigger = fsl_ssi_ac97_trigger,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
|
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
|
||||||
.ac97_control = 1,
|
.ac97_control = 1,
|
||||||
.playback = {
|
.playback = {
|
||||||
|
@ -932,7 +1162,7 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
|
||||||
.rates = SNDRV_PCM_RATE_48000,
|
.rates = SNDRV_PCM_RATE_48000,
|
||||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||||
},
|
},
|
||||||
.ops = &fsl_ssi_ac97_dai_ops,
|
.ops = &fsl_ssi_dai_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -990,56 +1220,6 @@ static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
|
||||||
.write = fsl_ssi_ac97_write,
|
.write = fsl_ssi_ac97_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Show the statistics of a flag only if its interrupt is enabled. The
|
|
||||||
* compiler will optimze this code to a no-op if the interrupt is not
|
|
||||||
* enabled.
|
|
||||||
*/
|
|
||||||
#define SIER_SHOW(flag, name) \
|
|
||||||
do { \
|
|
||||||
if (SIER_FLAGS & CCSR_SSI_SIER_##flag) \
|
|
||||||
length += sprintf(buf + length, #name "=%u\n", \
|
|
||||||
ssi_private->stats.name); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fsl_sysfs_ssi_show: display SSI statistics
|
|
||||||
*
|
|
||||||
* Display the statistics for the current SSI device. To avoid confusion,
|
|
||||||
* we only show those counts that are enabled.
|
|
||||||
*/
|
|
||||||
static ssize_t fsl_sysfs_ssi_show(struct device *dev,
|
|
||||||
struct device_attribute *attr, char *buf)
|
|
||||||
{
|
|
||||||
struct fsl_ssi_private *ssi_private =
|
|
||||||
container_of(attr, struct fsl_ssi_private, dev_attr);
|
|
||||||
ssize_t length = 0;
|
|
||||||
|
|
||||||
SIER_SHOW(RFRC_EN, rfrc);
|
|
||||||
SIER_SHOW(TFRC_EN, tfrc);
|
|
||||||
SIER_SHOW(CMDAU_EN, cmdau);
|
|
||||||
SIER_SHOW(CMDDU_EN, cmddu);
|
|
||||||
SIER_SHOW(RXT_EN, rxt);
|
|
||||||
SIER_SHOW(RDR1_EN, rdr1);
|
|
||||||
SIER_SHOW(RDR0_EN, rdr0);
|
|
||||||
SIER_SHOW(TDE1_EN, tde1);
|
|
||||||
SIER_SHOW(TDE0_EN, tde0);
|
|
||||||
SIER_SHOW(ROE1_EN, roe1);
|
|
||||||
SIER_SHOW(ROE0_EN, roe0);
|
|
||||||
SIER_SHOW(TUE1_EN, tue1);
|
|
||||||
SIER_SHOW(TUE0_EN, tue0);
|
|
||||||
SIER_SHOW(TFS_EN, tfs);
|
|
||||||
SIER_SHOW(RFS_EN, rfs);
|
|
||||||
SIER_SHOW(TLS_EN, tls);
|
|
||||||
SIER_SHOW(RLS_EN, rls);
|
|
||||||
SIER_SHOW(RFF1_EN, rff1);
|
|
||||||
SIER_SHOW(RFF0_EN, rff0);
|
|
||||||
SIER_SHOW(TFE1_EN, tfe1);
|
|
||||||
SIER_SHOW(TFE0_EN, tfe0);
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make every character in a string lower-case
|
* Make every character in a string lower-case
|
||||||
*/
|
*/
|
||||||
|
@ -1061,6 +1241,8 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct device_attribute *dev_attr = NULL;
|
struct device_attribute *dev_attr = NULL;
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
const struct of_device_id *of_id;
|
||||||
|
enum fsl_ssi_type hw_type;
|
||||||
const char *p, *sprop;
|
const char *p, *sprop;
|
||||||
const uint32_t *iprop;
|
const uint32_t *iprop;
|
||||||
struct resource res;
|
struct resource res;
|
||||||
|
@ -1075,6 +1257,11 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||||
if (!of_device_is_available(np))
|
if (!of_device_is_available(np))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
of_id = of_match_device(fsl_ssi_ids, &pdev->dev);
|
||||||
|
if (!of_id)
|
||||||
|
return -EINVAL;
|
||||||
|
hw_type = (enum fsl_ssi_type) of_id->data;
|
||||||
|
|
||||||
/* We only support the SSI in "I2S Slave" mode */
|
/* We only support the SSI in "I2S Slave" mode */
|
||||||
sprop = of_get_property(np, "fsl,mode", NULL);
|
sprop = of_get_property(np, "fsl,mode", NULL);
|
||||||
if (!sprop) {
|
if (!sprop) {
|
||||||
|
@ -1101,6 +1288,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ssi_private->use_dma = !of_property_read_bool(np,
|
ssi_private->use_dma = !of_property_read_bool(np,
|
||||||
"fsl,fiq-stream-filter");
|
"fsl,fiq-stream-filter");
|
||||||
|
ssi_private->hw_type = hw_type;
|
||||||
|
|
||||||
if (ac97) {
|
if (ac97) {
|
||||||
memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai,
|
memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai,
|
||||||
|
@ -1154,7 +1342,34 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||||
ssi_private->baudclk_locked = false;
|
ssi_private->baudclk_locked = false;
|
||||||
spin_lock_init(&ssi_private->baudclk_lock);
|
spin_lock_init(&ssi_private->baudclk_lock);
|
||||||
|
|
||||||
if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
|
/*
|
||||||
|
* imx51 and later SoCs have a slightly different IP that allows the
|
||||||
|
* SSI configuration while the SSI unit is running.
|
||||||
|
*
|
||||||
|
* More important, it is necessary on those SoCs to configure the
|
||||||
|
* sperate TX/RX DMA bits just before starting the stream
|
||||||
|
* (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi
|
||||||
|
* sends any DMA requests to the SDMA unit, otherwise it is not defined
|
||||||
|
* how the SDMA unit handles the DMA request.
|
||||||
|
*
|
||||||
|
* SDMA units are present on devices starting at imx35 but the imx35
|
||||||
|
* reference manual states that the DMA bits should not be changed
|
||||||
|
* while the SSI unit is running (SSIEN). So we support the necessary
|
||||||
|
* online configuration of fsl-ssi starting at imx51.
|
||||||
|
*/
|
||||||
|
switch (hw_type) {
|
||||||
|
case FSL_SSI_MCP8610:
|
||||||
|
case FSL_SSI_MX21:
|
||||||
|
case FSL_SSI_MX35:
|
||||||
|
ssi_private->offline_config = true;
|
||||||
|
break;
|
||||||
|
case FSL_SSI_MX51:
|
||||||
|
ssi_private->offline_config = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hw_type == FSL_SSI_MX21 || hw_type == FSL_SSI_MX51 ||
|
||||||
|
hw_type == FSL_SSI_MX35) {
|
||||||
u32 dma_events[2];
|
u32 dma_events[2];
|
||||||
ssi_private->ssi_on_imx = true;
|
ssi_private->ssi_on_imx = true;
|
||||||
|
|
||||||
|
@ -1176,7 +1391,8 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||||
*/
|
*/
|
||||||
ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
|
ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
|
||||||
if (IS_ERR(ssi_private->baudclk))
|
if (IS_ERR(ssi_private->baudclk))
|
||||||
dev_warn(&pdev->dev, "could not get baud clock: %d\n", ret);
|
dev_warn(&pdev->dev, "could not get baud clock: %ld\n",
|
||||||
|
PTR_ERR(ssi_private->baudclk));
|
||||||
else
|
else
|
||||||
clk_prepare_enable(ssi_private->baudclk);
|
clk_prepare_enable(ssi_private->baudclk);
|
||||||
|
|
||||||
|
@ -1218,31 +1434,24 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||||
dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
|
dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
|
||||||
imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx,
|
imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx,
|
||||||
dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
|
dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
|
||||||
} else if (ssi_private->use_dma) {
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable interrupts only for MCP8610 and MX51. The other MXs have
|
||||||
|
* different writeable interrupt status registers.
|
||||||
|
*/
|
||||||
|
if (ssi_private->use_dma) {
|
||||||
/* The 'name' should not have any slashes in it. */
|
/* The 'name' should not have any slashes in it. */
|
||||||
ret = devm_request_irq(&pdev->dev, ssi_private->irq,
|
ret = devm_request_irq(&pdev->dev, ssi_private->irq,
|
||||||
fsl_ssi_isr, 0, ssi_private->name,
|
fsl_ssi_isr, 0, ssi_private->name,
|
||||||
ssi_private);
|
ssi_private);
|
||||||
|
ssi_private->irq_stats = true;
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "could not claim irq %u\n",
|
dev_err(&pdev->dev, "could not claim irq %u\n",
|
||||||
ssi_private->irq);
|
ssi_private->irq);
|
||||||
goto error_irqmap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize the the device_attribute structure */
|
|
||||||
dev_attr = &ssi_private->dev_attr;
|
|
||||||
sysfs_attr_init(&dev_attr->attr);
|
|
||||||
dev_attr->attr.name = "statistics";
|
|
||||||
dev_attr->attr.mode = S_IRUGO;
|
|
||||||
dev_attr->show = fsl_sysfs_ssi_show;
|
|
||||||
|
|
||||||
ret = device_create_file(&pdev->dev, dev_attr);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(&pdev->dev, "could not create sysfs %s file\n",
|
|
||||||
ssi_private->dev_attr.attr.name);
|
|
||||||
goto error_clk;
|
goto error_clk;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Register with ASoC */
|
/* Register with ASoC */
|
||||||
dev_set_drvdata(&pdev->dev, ssi_private);
|
dev_set_drvdata(&pdev->dev, ssi_private);
|
||||||
|
@ -1254,6 +1463,10 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||||
goto error_dev;
|
goto error_dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = fsl_ssi_debugfs_create(ssi_private, &pdev->dev);
|
||||||
|
if (ret)
|
||||||
|
goto error_dbgfs;
|
||||||
|
|
||||||
if (ssi_private->ssi_on_imx) {
|
if (ssi_private->ssi_on_imx) {
|
||||||
if (!ssi_private->use_dma) {
|
if (!ssi_private->use_dma) {
|
||||||
|
|
||||||
|
@ -1273,11 +1486,11 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
|
ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_dev;
|
goto error_pcm;
|
||||||
} else {
|
} else {
|
||||||
ret = imx_pcm_dma_init(pdev);
|
ret = imx_pcm_dma_init(pdev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_dev;
|
goto error_pcm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1319,6 +1532,13 @@ done:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_dai:
|
error_dai:
|
||||||
|
if (ssi_private->ssi_on_imx && !ssi_private->use_dma)
|
||||||
|
imx_pcm_fiq_exit(pdev);
|
||||||
|
|
||||||
|
error_pcm:
|
||||||
|
fsl_ssi_debugfs_remove(ssi_private);
|
||||||
|
|
||||||
|
error_dbgfs:
|
||||||
snd_soc_unregister_component(&pdev->dev);
|
snd_soc_unregister_component(&pdev->dev);
|
||||||
|
|
||||||
error_dev:
|
error_dev:
|
||||||
|
@ -1332,6 +1552,7 @@ error_clk:
|
||||||
}
|
}
|
||||||
|
|
||||||
error_irqmap:
|
error_irqmap:
|
||||||
|
if (ssi_private->irq_stats)
|
||||||
irq_dispose_mapping(ssi_private->irq);
|
irq_dispose_mapping(ssi_private->irq);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1341,27 +1562,22 @@ static int fsl_ssi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
|
struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
|
fsl_ssi_debugfs_remove(ssi_private);
|
||||||
|
|
||||||
if (!ssi_private->new_binding)
|
if (!ssi_private->new_binding)
|
||||||
platform_device_unregister(ssi_private->pdev);
|
platform_device_unregister(ssi_private->pdev);
|
||||||
snd_soc_unregister_component(&pdev->dev);
|
snd_soc_unregister_component(&pdev->dev);
|
||||||
device_remove_file(&pdev->dev, &ssi_private->dev_attr);
|
|
||||||
if (ssi_private->ssi_on_imx) {
|
if (ssi_private->ssi_on_imx) {
|
||||||
if (!IS_ERR(ssi_private->baudclk))
|
if (!IS_ERR(ssi_private->baudclk))
|
||||||
clk_disable_unprepare(ssi_private->baudclk);
|
clk_disable_unprepare(ssi_private->baudclk);
|
||||||
clk_disable_unprepare(ssi_private->clk);
|
clk_disable_unprepare(ssi_private->clk);
|
||||||
}
|
}
|
||||||
|
if (ssi_private->irq_stats)
|
||||||
irq_dispose_mapping(ssi_private->irq);
|
irq_dispose_mapping(ssi_private->irq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id fsl_ssi_ids[] = {
|
|
||||||
{ .compatible = "fsl,mpc8610-ssi", },
|
|
||||||
{ .compatible = "fsl,imx21-ssi", },
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
|
|
||||||
|
|
||||||
static struct platform_driver fsl_ssi_driver = {
|
static struct platform_driver fsl_ssi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "fsl-ssi-dai",
|
.name = "fsl-ssi-dai",
|
||||||
|
|
|
@ -41,9 +41,6 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
|
||||||
SNDRV_PCM_INFO_PAUSE |
|
SNDRV_PCM_INFO_PAUSE |
|
||||||
SNDRV_PCM_INFO_RESUME,
|
SNDRV_PCM_INFO_RESUME,
|
||||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||||
.rate_min = 8000,
|
|
||||||
.channels_min = 2,
|
|
||||||
.channels_max = 2,
|
|
||||||
.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
|
.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
|
||||||
.period_bytes_min = 128,
|
.period_bytes_min = 128,
|
||||||
.period_bytes_max = 65535, /* Limited by SDMA engine */
|
.period_bytes_max = 65535, /* Limited by SDMA engine */
|
||||||
|
|
|
@ -162,9 +162,6 @@ static struct snd_pcm_hardware snd_imx_hardware = {
|
||||||
SNDRV_PCM_INFO_PAUSE |
|
SNDRV_PCM_INFO_PAUSE |
|
||||||
SNDRV_PCM_INFO_RESUME,
|
SNDRV_PCM_INFO_RESUME,
|
||||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||||
.rate_min = 8000,
|
|
||||||
.channels_min = 2,
|
|
||||||
.channels_max = 2,
|
|
||||||
.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
|
.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
|
||||||
.period_bytes_min = 128,
|
.period_bytes_min = 128,
|
||||||
.period_bytes_max = 16 * 1024,
|
.period_bytes_max = 16 * 1024,
|
||||||
|
|
|
@ -200,10 +200,6 @@ static const struct snd_pcm_hardware psc_dma_hardware = {
|
||||||
SNDRV_PCM_INFO_BATCH,
|
SNDRV_PCM_INFO_BATCH,
|
||||||
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
|
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
|
||||||
SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
|
SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
|
||||||
.rate_min = 8000,
|
|
||||||
.rate_max = 48000,
|
|
||||||
.channels_min = 1,
|
|
||||||
.channels_max = 2,
|
|
||||||
.period_bytes_max = 1024 * 1024,
|
.period_bytes_max = 1024 * 1024,
|
||||||
.period_bytes_min = 32,
|
.period_bytes_min = 32,
|
||||||
.periods_min = 2,
|
.periods_min = 2,
|
||||||
|
|
|
@ -26,8 +26,7 @@
|
||||||
* ALSA that we support all rates and let the codec driver decide what rates
|
* ALSA that we support all rates and let the codec driver decide what rates
|
||||||
* are really supported.
|
* are really supported.
|
||||||
*/
|
*/
|
||||||
#define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
|
#define PSC_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
|
||||||
SNDRV_PCM_RATE_CONTINUOUS)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
|
* PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
|
||||||
|
|
|
@ -9,14 +9,12 @@
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/module.h>
|
#include <linux/string.h>
|
||||||
#include <sound/simple_card.h>
|
#include <sound/simple_card.h>
|
||||||
|
|
||||||
#define asoc_simple_get_card_info(p) \
|
|
||||||
container_of(p->dai_link, struct asoc_simple_card_info, snd_link)
|
|
||||||
|
|
||||||
static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
|
static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
|
||||||
struct asoc_simple_dai *set,
|
struct asoc_simple_dai *set,
|
||||||
unsigned int daifmt)
|
unsigned int daifmt)
|
||||||
|
@ -41,7 +39,8 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
|
||||||
|
|
||||||
static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
||||||
{
|
{
|
||||||
struct asoc_simple_card_info *info = asoc_simple_get_card_info(rtd);
|
struct asoc_simple_card_info *info =
|
||||||
|
snd_soc_card_get_drvdata(rtd->card);
|
||||||
struct snd_soc_dai *codec = rtd->codec_dai;
|
struct snd_soc_dai *codec = rtd->codec_dai;
|
||||||
struct snd_soc_dai *cpu = rtd->cpu_dai;
|
struct snd_soc_dai *cpu = rtd->cpu_dai;
|
||||||
unsigned int daifmt = info->daifmt;
|
unsigned int daifmt = info->daifmt;
|
||||||
|
@ -106,11 +105,7 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
|
||||||
&dai->sysclk);
|
&dai->sysclk);
|
||||||
} else {
|
} else {
|
||||||
clk = of_clk_get(*node, 0);
|
clk = of_clk_get(*node, 0);
|
||||||
if (IS_ERR(clk)) {
|
if (!IS_ERR(clk))
|
||||||
ret = PTR_ERR(clk);
|
|
||||||
goto parse_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
dai->sysclk = clk_get_rate(clk);
|
dai->sysclk = clk_get_rate(clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,10 +133,12 @@ static int asoc_simple_card_parse_of(struct device_node *node,
|
||||||
(SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK);
|
(SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK);
|
||||||
|
|
||||||
/* DAPM routes */
|
/* DAPM routes */
|
||||||
|
if (of_property_read_bool(node, "simple-audio-card,routing")) {
|
||||||
ret = snd_soc_of_parse_audio_routing(&info->snd_card,
|
ret = snd_soc_of_parse_audio_routing(&info->snd_card,
|
||||||
"simple-audio-routing");
|
"simple-audio-card,routing");
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* CPU sub-node */
|
/* CPU sub-node */
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -197,16 +194,20 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
struct device_node *of_cpu, *of_codec, *of_platform;
|
struct device_node *of_cpu, *of_codec, *of_platform;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
cinfo = NULL;
|
cinfo = NULL;
|
||||||
of_cpu = NULL;
|
of_cpu = NULL;
|
||||||
of_codec = NULL;
|
of_codec = NULL;
|
||||||
of_platform = NULL;
|
of_platform = NULL;
|
||||||
if (np && of_device_is_available(np)) {
|
|
||||||
cinfo = devm_kzalloc(dev, sizeof(*cinfo), GFP_KERNEL);
|
cinfo = devm_kzalloc(dev, sizeof(*cinfo), GFP_KERNEL);
|
||||||
if (cinfo) {
|
if (!cinfo)
|
||||||
int ret;
|
return -ENOMEM;
|
||||||
cinfo->snd_card.dev = &pdev->dev;
|
|
||||||
|
if (np && of_device_is_available(np)) {
|
||||||
|
cinfo->snd_card.dev = dev;
|
||||||
|
|
||||||
ret = asoc_simple_card_parse_of(np, cinfo, dev,
|
ret = asoc_simple_card_parse_of(np, cinfo, dev,
|
||||||
&of_cpu,
|
&of_cpu,
|
||||||
&of_codec,
|
&of_codec,
|
||||||
|
@ -216,17 +217,16 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
||||||
dev_err(dev, "parse error %d\n", ret);
|
dev_err(dev, "parse error %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
cinfo->snd_card.dev = &pdev->dev;
|
if (!dev->platform_data) {
|
||||||
cinfo = pdev->dev.platform_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cinfo) {
|
|
||||||
dev_err(dev, "no info for asoc-simple-card\n");
|
dev_err(dev, "no info for asoc-simple-card\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(cinfo, dev->platform_data, sizeof(*cinfo));
|
||||||
|
cinfo->snd_card.dev = dev;
|
||||||
|
}
|
||||||
|
|
||||||
if (!cinfo->name ||
|
if (!cinfo->name ||
|
||||||
!cinfo->card ||
|
!cinfo->card ||
|
||||||
!cinfo->codec_dai.name ||
|
!cinfo->codec_dai.name ||
|
||||||
|
@ -259,6 +259,8 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
||||||
cinfo->snd_card.dai_link = &cinfo->snd_link;
|
cinfo->snd_card.dai_link = &cinfo->snd_link;
|
||||||
cinfo->snd_card.num_links = 1;
|
cinfo->snd_card.num_links = 1;
|
||||||
|
|
||||||
|
snd_soc_card_set_drvdata(&cinfo->snd_card, cinfo);
|
||||||
|
|
||||||
return devm_snd_soc_register_card(&pdev->dev, &cinfo->snd_card);
|
return devm_snd_soc_register_card(&pdev->dev, &cinfo->snd_card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,16 +89,6 @@ static struct snd_pcm_hardware sst_platform_pcm_hw = {
|
||||||
SNDRV_PCM_INFO_MMAP_VALID |
|
SNDRV_PCM_INFO_MMAP_VALID |
|
||||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||||
SNDRV_PCM_INFO_SYNC_START),
|
SNDRV_PCM_INFO_SYNC_START),
|
||||||
.formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |
|
|
||||||
SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 |
|
|
||||||
SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32),
|
|
||||||
.rates = (SNDRV_PCM_RATE_8000|
|
|
||||||
SNDRV_PCM_RATE_44100 |
|
|
||||||
SNDRV_PCM_RATE_48000),
|
|
||||||
.rate_min = SST_MIN_RATE,
|
|
||||||
.rate_max = SST_MAX_RATE,
|
|
||||||
.channels_min = SST_MIN_CHANNEL,
|
|
||||||
.channels_max = SST_MAX_CHANNEL,
|
|
||||||
.buffer_bytes_max = SST_MAX_BUFFER,
|
.buffer_bytes_max = SST_MAX_BUFFER,
|
||||||
.period_bytes_min = SST_MIN_PERIOD_BYTES,
|
.period_bytes_min = SST_MIN_PERIOD_BYTES,
|
||||||
.period_bytes_max = SST_MAX_PERIOD_BYTES,
|
.period_bytes_max = SST_MAX_PERIOD_BYTES,
|
||||||
|
|
|
@ -33,10 +33,6 @@
|
||||||
#define SST_STEREO 2
|
#define SST_STEREO 2
|
||||||
#define SST_MAX_CAP 5
|
#define SST_MAX_CAP 5
|
||||||
|
|
||||||
#define SST_MIN_RATE 8000
|
|
||||||
#define SST_MAX_RATE 48000
|
|
||||||
#define SST_MIN_CHANNEL 1
|
|
||||||
#define SST_MAX_CHANNEL 5
|
|
||||||
#define SST_MAX_BUFFER (800*1024)
|
#define SST_MAX_BUFFER (800*1024)
|
||||||
#define SST_MIN_BUFFER (800*1024)
|
#define SST_MIN_BUFFER (800*1024)
|
||||||
#define SST_MIN_PERIOD_BYTES 32
|
#define SST_MIN_PERIOD_BYTES 32
|
||||||
|
|
|
@ -21,16 +21,6 @@
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
#include "kirkwood.h"
|
#include "kirkwood.h"
|
||||||
|
|
||||||
#define KIRKWOOD_RATES \
|
|
||||||
(SNDRV_PCM_RATE_8000_192000 | \
|
|
||||||
SNDRV_PCM_RATE_CONTINUOUS | \
|
|
||||||
SNDRV_PCM_RATE_KNOT)
|
|
||||||
|
|
||||||
#define KIRKWOOD_FORMATS \
|
|
||||||
(SNDRV_PCM_FMTBIT_S16_LE | \
|
|
||||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
|
||||||
SNDRV_PCM_FMTBIT_S32_LE)
|
|
||||||
|
|
||||||
static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs)
|
static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *soc_runtime = subs->private_data;
|
struct snd_soc_pcm_runtime *soc_runtime = subs->private_data;
|
||||||
|
@ -43,12 +33,6 @@ static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
|
||||||
SNDRV_PCM_INFO_MMAP_VALID |
|
SNDRV_PCM_INFO_MMAP_VALID |
|
||||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||||
SNDRV_PCM_INFO_PAUSE),
|
SNDRV_PCM_INFO_PAUSE),
|
||||||
.formats = KIRKWOOD_FORMATS,
|
|
||||||
.rates = KIRKWOOD_RATES,
|
|
||||||
.rate_min = 8000,
|
|
||||||
.rate_max = 384000,
|
|
||||||
.channels_min = 1,
|
|
||||||
.channels_max = 8,
|
|
||||||
.buffer_bytes_max = KIRKWOOD_SND_MAX_BUFFER_BYTES,
|
.buffer_bytes_max = KIRKWOOD_SND_MAX_BUFFER_BYTES,
|
||||||
.period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES,
|
.period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES,
|
||||||
.period_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES,
|
.period_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES,
|
||||||
|
|
|
@ -36,11 +36,6 @@ static const struct snd_pcm_hardware snd_mxs_hardware = {
|
||||||
SNDRV_PCM_INFO_RESUME |
|
SNDRV_PCM_INFO_RESUME |
|
||||||
SNDRV_PCM_INFO_INTERLEAVED |
|
SNDRV_PCM_INFO_INTERLEAVED |
|
||||||
SNDRV_PCM_INFO_HALF_DUPLEX,
|
SNDRV_PCM_INFO_HALF_DUPLEX,
|
||||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
|
||||||
SNDRV_PCM_FMTBIT_S20_3LE |
|
|
||||||
SNDRV_PCM_FMTBIT_S24_LE,
|
|
||||||
.channels_min = 2,
|
|
||||||
.channels_max = 2,
|
|
||||||
.period_bytes_min = 32,
|
.period_bytes_min = 32,
|
||||||
.period_bytes_max = 8192,
|
.period_bytes_max = 8192,
|
||||||
.periods_min = 1,
|
.periods_min = 1,
|
||||||
|
@ -57,7 +52,6 @@ static const struct snd_dmaengine_pcm_config mxs_dmaengine_pcm_config = {
|
||||||
int mxs_pcm_platform_register(struct device *dev)
|
int mxs_pcm_platform_register(struct device *dev)
|
||||||
{
|
{
|
||||||
return devm_snd_dmaengine_pcm_register(dev, &mxs_dmaengine_pcm_config,
|
return devm_snd_dmaengine_pcm_register(dev, &mxs_dmaengine_pcm_config,
|
||||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
|
|
||||||
SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX);
|
SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mxs_pcm_platform_register);
|
EXPORT_SYMBOL_GPL(mxs_pcm_platform_register);
|
||||||
|
|
|
@ -32,9 +32,6 @@ static const struct snd_pcm_hardware nuc900_pcm_hardware = {
|
||||||
SNDRV_PCM_INFO_MMAP_VALID |
|
SNDRV_PCM_INFO_MMAP_VALID |
|
||||||
SNDRV_PCM_INFO_PAUSE |
|
SNDRV_PCM_INFO_PAUSE |
|
||||||
SNDRV_PCM_INFO_RESUME,
|
SNDRV_PCM_INFO_RESUME,
|
||||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
|
||||||
.channels_min = 1,
|
|
||||||
.channels_max = 2,
|
|
||||||
.buffer_bytes_max = 4*1024,
|
.buffer_bytes_max = 4*1024,
|
||||||
.period_bytes_min = 1*1024,
|
.period_bytes_min = 1*1024,
|
||||||
.period_bytes_max = 4*1024,
|
.period_bytes_max = 4*1024,
|
||||||
|
|
|
@ -405,8 +405,7 @@ static int s6000_i2s_dai_probe(struct snd_soc_dai *dai)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define S6000_I2S_RATES (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
|
#define S6000_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
|
||||||
SNDRV_PCM_RATE_8000_192000)
|
|
||||||
#define S6000_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
#define S6000_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||||
|
|
||||||
static const struct snd_soc_dai_ops s6000_i2s_dai_ops = {
|
static const struct snd_soc_dai_ops s6000_i2s_dai_ops = {
|
||||||
|
|
|
@ -68,7 +68,6 @@ int samsung_asoc_dma_platform_register(struct device *dev)
|
||||||
{
|
{
|
||||||
return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config,
|
return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config,
|
||||||
SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
|
SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
|
||||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
|
|
||||||
SND_DMAENGINE_PCM_FLAG_COMPAT);
|
SND_DMAENGINE_PCM_FLAG_COMPAT);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
|
EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
|
||||||
|
|
|
@ -89,29 +89,12 @@ struct camelot_pcm {
|
||||||
#define DMABRG_PREALLOC_BUFFER 32 * 1024
|
#define DMABRG_PREALLOC_BUFFER 32 * 1024
|
||||||
#define DMABRG_PREALLOC_BUFFER_MAX 32 * 1024
|
#define DMABRG_PREALLOC_BUFFER_MAX 32 * 1024
|
||||||
|
|
||||||
/* support everything the SSI supports */
|
|
||||||
#define DMABRG_RATES \
|
|
||||||
SNDRV_PCM_RATE_8000_192000
|
|
||||||
|
|
||||||
#define DMABRG_FMTS \
|
|
||||||
(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
|
|
||||||
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
|
|
||||||
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
|
|
||||||
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \
|
|
||||||
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
|
|
||||||
|
|
||||||
static struct snd_pcm_hardware camelot_pcm_hardware = {
|
static struct snd_pcm_hardware camelot_pcm_hardware = {
|
||||||
.info = (SNDRV_PCM_INFO_MMAP |
|
.info = (SNDRV_PCM_INFO_MMAP |
|
||||||
SNDRV_PCM_INFO_INTERLEAVED |
|
SNDRV_PCM_INFO_INTERLEAVED |
|
||||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||||
SNDRV_PCM_INFO_MMAP_VALID |
|
SNDRV_PCM_INFO_MMAP_VALID |
|
||||||
SNDRV_PCM_INFO_BATCH),
|
SNDRV_PCM_INFO_BATCH),
|
||||||
.formats = DMABRG_FMTS,
|
|
||||||
.rates = DMABRG_RATES,
|
|
||||||
.rate_min = 8000,
|
|
||||||
.rate_max = 192000,
|
|
||||||
.channels_min = 2,
|
|
||||||
.channels_max = 8, /* max of the SSI */
|
|
||||||
.buffer_bytes_max = DMABRG_PERIOD_MAX,
|
.buffer_bytes_max = DMABRG_PERIOD_MAX,
|
||||||
.period_bytes_min = DMABRG_PERIOD_MIN,
|
.period_bytes_min = DMABRG_PERIOD_MIN,
|
||||||
.period_bytes_max = DMABRG_PERIOD_MAX / 2,
|
.period_bytes_max = DMABRG_PERIOD_MAX / 2,
|
||||||
|
|
|
@ -1787,12 +1787,6 @@ static struct snd_pcm_hardware fsi_pcm_hardware = {
|
||||||
SNDRV_PCM_INFO_MMAP |
|
SNDRV_PCM_INFO_MMAP |
|
||||||
SNDRV_PCM_INFO_MMAP_VALID |
|
SNDRV_PCM_INFO_MMAP_VALID |
|
||||||
SNDRV_PCM_INFO_PAUSE,
|
SNDRV_PCM_INFO_PAUSE,
|
||||||
.formats = FSI_FMTS,
|
|
||||||
.rates = FSI_RATES,
|
|
||||||
.rate_min = 8000,
|
|
||||||
.rate_max = 192000,
|
|
||||||
.channels_min = 2,
|
|
||||||
.channels_max = 2,
|
|
||||||
.buffer_bytes_max = 64 * 1024,
|
.buffer_bytes_max = 64 * 1024,
|
||||||
.period_bytes_min = 32,
|
.period_bytes_min = 32,
|
||||||
.period_bytes_max = 8192,
|
.period_bytes_max = 8192,
|
||||||
|
|
|
@ -628,12 +628,6 @@ static struct snd_pcm_hardware rsnd_pcm_hardware = {
|
||||||
SNDRV_PCM_INFO_MMAP |
|
SNDRV_PCM_INFO_MMAP |
|
||||||
SNDRV_PCM_INFO_MMAP_VALID |
|
SNDRV_PCM_INFO_MMAP_VALID |
|
||||||
SNDRV_PCM_INFO_PAUSE,
|
SNDRV_PCM_INFO_PAUSE,
|
||||||
.formats = RSND_FMTS,
|
|
||||||
.rates = RSND_RATES,
|
|
||||||
.rate_min = 8000,
|
|
||||||
.rate_max = 192000,
|
|
||||||
.channels_min = 2,
|
|
||||||
.channels_max = 2,
|
|
||||||
.buffer_bytes_max = 64 * 1024,
|
.buffer_bytes_max = 64 * 1024,
|
||||||
.period_bytes_min = 32,
|
.period_bytes_min = 32,
|
||||||
.period_bytes_max = 8192,
|
.period_bytes_max = 8192,
|
||||||
|
|
|
@ -1728,6 +1728,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_soc_dapm_link_dai_widgets(card);
|
snd_soc_dapm_link_dai_widgets(card);
|
||||||
|
snd_soc_dapm_connect_dai_link_widgets(card);
|
||||||
|
|
||||||
if (card->controls)
|
if (card->controls)
|
||||||
snd_soc_add_card_controls(card, card->controls, card->num_controls);
|
snd_soc_add_card_controls(card, card->controls, card->num_controls);
|
||||||
|
@ -3484,7 +3485,7 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||||
return dai->codec->driver->set_sysclk(dai->codec, clk_id, 0,
|
return dai->codec->driver->set_sysclk(dai->codec, clk_id, 0,
|
||||||
freq, dir);
|
freq, dir);
|
||||||
else
|
else
|
||||||
return -EINVAL;
|
return -ENOTSUPP;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
|
EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
|
||||||
|
|
||||||
|
@ -3505,7 +3506,7 @@ int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
|
||||||
return codec->driver->set_sysclk(codec, clk_id, source,
|
return codec->driver->set_sysclk(codec, clk_id, source,
|
||||||
freq, dir);
|
freq, dir);
|
||||||
else
|
else
|
||||||
return -EINVAL;
|
return -ENOTSUPP;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk);
|
EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk);
|
||||||
|
|
||||||
|
|
|
@ -371,12 +371,16 @@ static void dapm_reset(struct snd_soc_card *card)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg)
|
static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg,
|
||||||
|
unsigned int *value)
|
||||||
{
|
{
|
||||||
if (w->codec)
|
if (w->codec) {
|
||||||
return snd_soc_read(w->codec, reg);
|
*value = snd_soc_read(w->codec, reg);
|
||||||
else if (w->platform)
|
return 0;
|
||||||
return snd_soc_platform_read(w->platform, reg);
|
} else if (w->platform) {
|
||||||
|
*value = snd_soc_platform_read(w->platform, reg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
dev_err(w->dapm->dev, "ASoC: no valid widget read method\n");
|
dev_err(w->dapm->dev, "ASoC: no valid widget read method\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -430,13 +434,12 @@ static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w,
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
soc_widget_lock(w);
|
soc_widget_lock(w);
|
||||||
ret = soc_widget_read(w, reg);
|
ret = soc_widget_read(w, reg, &old);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
soc_widget_unlock(w);
|
soc_widget_unlock(w);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
old = ret;
|
|
||||||
new = (old & ~mask) | (value & mask);
|
new = (old & ~mask) | (value & mask);
|
||||||
change = old != new;
|
change = old != new;
|
||||||
if (change) {
|
if (change) {
|
||||||
|
@ -513,7 +516,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
|
||||||
unsigned int invert = mc->invert;
|
unsigned int invert = mc->invert;
|
||||||
|
|
||||||
if (reg != SND_SOC_NOPM) {
|
if (reg != SND_SOC_NOPM) {
|
||||||
val = soc_widget_read(w, reg);
|
soc_widget_read(w, reg, &val);
|
||||||
val = (val >> shift) & mask;
|
val = (val >> shift) & mask;
|
||||||
if (invert)
|
if (invert)
|
||||||
val = max - val;
|
val = max - val;
|
||||||
|
@ -529,7 +532,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
|
||||||
w->kcontrol_news[i].private_value;
|
w->kcontrol_news[i].private_value;
|
||||||
int val, item;
|
int val, item;
|
||||||
|
|
||||||
val = soc_widget_read(w, e->reg);
|
soc_widget_read(w, e->reg, &val);
|
||||||
item = (val >> e->shift_l) & e->mask;
|
item = (val >> e->shift_l) & e->mask;
|
||||||
|
|
||||||
if (item < e->max && !strcmp(p->name, e->texts[item]))
|
if (item < e->max && !strcmp(p->name, e->texts[item]))
|
||||||
|
@ -558,7 +561,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
|
||||||
w->kcontrol_news[i].private_value;
|
w->kcontrol_news[i].private_value;
|
||||||
int val, item;
|
int val, item;
|
||||||
|
|
||||||
val = soc_widget_read(w, e->reg);
|
soc_widget_read(w, e->reg, &val);
|
||||||
val = (val >> e->shift_l) & e->mask;
|
val = (val >> e->shift_l) & e->mask;
|
||||||
for (item = 0; item < e->max; item++) {
|
for (item = 0; item < e->max; item++) {
|
||||||
if (val == e->values[item])
|
if (val == e->values[item])
|
||||||
|
@ -2782,7 +2785,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
|
||||||
|
|
||||||
/* Read the initial power state from the device */
|
/* Read the initial power state from the device */
|
||||||
if (w->reg >= 0) {
|
if (w->reg >= 0) {
|
||||||
val = soc_widget_read(w, w->reg) >> w->shift;
|
soc_widget_read(w, w->reg, &val);
|
||||||
|
val = val >> w->shift;
|
||||||
val &= w->mask;
|
val &= w->mask;
|
||||||
if (val == w->on_val)
|
if (val == w->on_val)
|
||||||
w->power = 1;
|
w->power = 1;
|
||||||
|
@ -3634,6 +3638,55 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
|
||||||
|
{
|
||||||
|
struct snd_soc_pcm_runtime *rtd = card->rtd;
|
||||||
|
struct snd_soc_dai *cpu_dai, *codec_dai;
|
||||||
|
struct snd_soc_dapm_route r;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(&r, 0, sizeof(r));
|
||||||
|
|
||||||
|
/* for each BE DAI link... */
|
||||||
|
for (i = 0; i < card->num_rtd; i++) {
|
||||||
|
rtd = &card->rtd[i];
|
||||||
|
cpu_dai = rtd->cpu_dai;
|
||||||
|
codec_dai = rtd->codec_dai;
|
||||||
|
|
||||||
|
/* dynamic FE links have no fixed DAI mapping */
|
||||||
|
if (rtd->dai_link->dynamic)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* there is no point in connecting BE DAI links with dummies */
|
||||||
|
if (snd_soc_dai_is_dummy(codec_dai) ||
|
||||||
|
snd_soc_dai_is_dummy(cpu_dai))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* connect BE DAI playback if widgets are valid */
|
||||||
|
if (codec_dai->playback_widget && cpu_dai->playback_widget) {
|
||||||
|
r.source = cpu_dai->playback_widget->name;
|
||||||
|
r.sink = codec_dai->playback_widget->name;
|
||||||
|
dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
|
||||||
|
cpu_dai->codec->name, r.source,
|
||||||
|
codec_dai->platform->name, r.sink);
|
||||||
|
|
||||||
|
snd_soc_dapm_add_route(&card->dapm, &r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* connect BE DAI capture if widgets are valid */
|
||||||
|
if (codec_dai->capture_widget && cpu_dai->capture_widget) {
|
||||||
|
r.source = codec_dai->capture_widget->name;
|
||||||
|
r.sink = cpu_dai->capture_widget->name;
|
||||||
|
dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
|
||||||
|
codec_dai->codec->name, r.source,
|
||||||
|
cpu_dai->platform->name, r.sink);
|
||||||
|
|
||||||
|
snd_soc_dapm_add_route(&card->dapm, &r);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
|
static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
|
||||||
int event)
|
int event)
|
||||||
{
|
{
|
||||||
|
|
|
@ -144,6 +144,8 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
if (dma_caps.cmd_pause)
|
if (dma_caps.cmd_pause)
|
||||||
hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
|
hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
|
||||||
|
if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
|
||||||
|
hw.info |= SNDRV_PCM_INFO_BATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
return snd_soc_set_runtime_hwparams(substream, &hw);
|
return snd_soc_set_runtime_hwparams(substream, &hw);
|
||||||
|
@ -187,6 +189,21 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
|
||||||
dma_data->filter_data);
|
dma_data->filter_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool dmaengine_pcm_can_report_residue(struct dma_chan *chan)
|
||||||
|
{
|
||||||
|
struct dma_slave_caps dma_caps;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = dma_get_slave_caps(chan, &dma_caps);
|
||||||
|
if (ret != 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||||
{
|
{
|
||||||
struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
|
struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
|
||||||
|
@ -239,6 +256,16 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||||
max_buffer_size);
|
max_buffer_size);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free;
|
goto err_free;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This will only return false if we know for sure that at least
|
||||||
|
* one channel does not support residue reporting. If the DMA
|
||||||
|
* driver does not implement the slave_caps API we rely having
|
||||||
|
* the NO_RESIDUE flag set manually in case residue reporting is
|
||||||
|
* not supported.
|
||||||
|
*/
|
||||||
|
if (!dmaengine_pcm_can_report_residue(pcm->chan[i]))
|
||||||
|
pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -248,6 +275,18 @@ err_free:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static snd_pcm_uframes_t dmaengine_pcm_pointer(
|
||||||
|
struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
|
struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
|
||||||
|
|
||||||
|
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
|
||||||
|
return snd_dmaengine_pcm_pointer_no_residue(substream);
|
||||||
|
else
|
||||||
|
return snd_dmaengine_pcm_pointer(substream);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct snd_pcm_ops dmaengine_pcm_ops = {
|
static const struct snd_pcm_ops dmaengine_pcm_ops = {
|
||||||
.open = dmaengine_pcm_open,
|
.open = dmaengine_pcm_open,
|
||||||
.close = snd_dmaengine_pcm_close,
|
.close = snd_dmaengine_pcm_close,
|
||||||
|
@ -255,7 +294,7 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = {
|
||||||
.hw_params = dmaengine_pcm_hw_params,
|
.hw_params = dmaengine_pcm_hw_params,
|
||||||
.hw_free = snd_pcm_lib_free_pages,
|
.hw_free = snd_pcm_lib_free_pages,
|
||||||
.trigger = snd_dmaengine_pcm_trigger,
|
.trigger = snd_dmaengine_pcm_trigger,
|
||||||
.pointer = snd_dmaengine_pcm_pointer,
|
.pointer = dmaengine_pcm_pointer,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
|
static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
|
||||||
|
@ -265,23 +304,6 @@ static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
|
||||||
.probe_order = SND_SOC_COMP_ORDER_LATE,
|
.probe_order = SND_SOC_COMP_ORDER_LATE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct snd_pcm_ops dmaengine_no_residue_pcm_ops = {
|
|
||||||
.open = dmaengine_pcm_open,
|
|
||||||
.close = snd_dmaengine_pcm_close,
|
|
||||||
.ioctl = snd_pcm_lib_ioctl,
|
|
||||||
.hw_params = dmaengine_pcm_hw_params,
|
|
||||||
.hw_free = snd_pcm_lib_free_pages,
|
|
||||||
.trigger = snd_dmaengine_pcm_trigger,
|
|
||||||
.pointer = snd_dmaengine_pcm_pointer_no_residue,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct snd_soc_platform_driver dmaengine_no_residue_pcm_platform = {
|
|
||||||
.ops = &dmaengine_no_residue_pcm_ops,
|
|
||||||
.pcm_new = dmaengine_pcm_new,
|
|
||||||
.pcm_free = dmaengine_pcm_free,
|
|
||||||
.probe_order = SND_SOC_COMP_ORDER_LATE,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char * const dmaengine_pcm_dma_channel_names[] = {
|
static const char * const dmaengine_pcm_dma_channel_names[] = {
|
||||||
[SNDRV_PCM_STREAM_PLAYBACK] = "tx",
|
[SNDRV_PCM_STREAM_PLAYBACK] = "tx",
|
||||||
[SNDRV_PCM_STREAM_CAPTURE] = "rx",
|
[SNDRV_PCM_STREAM_CAPTURE] = "rx",
|
||||||
|
@ -374,10 +396,6 @@ int snd_dmaengine_pcm_register(struct device *dev,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_dma;
|
goto err_free_dma;
|
||||||
|
|
||||||
if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
|
|
||||||
ret = snd_soc_add_platform(dev, &pcm->platform,
|
|
||||||
&dmaengine_no_residue_pcm_platform);
|
|
||||||
else
|
|
||||||
ret = snd_soc_add_platform(dev, &pcm->platform,
|
ret = snd_soc_add_platform(dev, &pcm->platform,
|
||||||
&dmaengine_pcm_platform);
|
&dmaengine_pcm_platform);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
@ -240,14 +240,15 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
|
||||||
cpu_stream->channels_min);
|
cpu_stream->channels_min);
|
||||||
hw->channels_max = min(codec_stream->channels_max,
|
hw->channels_max = min(codec_stream->channels_max,
|
||||||
cpu_stream->channels_max);
|
cpu_stream->channels_max);
|
||||||
|
if (hw->formats)
|
||||||
|
hw->formats &= codec_stream->formats & cpu_stream->formats;
|
||||||
|
else
|
||||||
hw->formats = codec_stream->formats & cpu_stream->formats;
|
hw->formats = codec_stream->formats & cpu_stream->formats;
|
||||||
hw->rates = codec_stream->rates & cpu_stream->rates;
|
hw->rates = snd_pcm_rate_mask_intersect(codec_stream->rates,
|
||||||
if (codec_stream->rates
|
cpu_stream->rates);
|
||||||
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
|
|
||||||
hw->rates |= cpu_stream->rates;
|
hw->rate_min = 0;
|
||||||
if (cpu_stream->rates
|
hw->rate_max = UINT_MAX;
|
||||||
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
|
|
||||||
hw->rates |= codec_stream->rates;
|
|
||||||
|
|
||||||
snd_pcm_limit_hw_rates(runtime);
|
snd_pcm_limit_hw_rates(runtime);
|
||||||
|
|
||||||
|
@ -776,7 +777,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platform->driver->ops && platform->driver->bespoke_trigger) {
|
if (platform->driver->bespoke_trigger) {
|
||||||
ret = platform->driver->bespoke_trigger(substream, cmd);
|
ret = platform->driver->bespoke_trigger(substream, cmd);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1235,6 +1236,20 @@ unwind:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
|
||||||
|
struct snd_soc_pcm_stream *stream)
|
||||||
|
{
|
||||||
|
runtime->hw.rate_min = stream->rate_min;
|
||||||
|
runtime->hw.rate_max = stream->rate_max;
|
||||||
|
runtime->hw.channels_min = stream->channels_min;
|
||||||
|
runtime->hw.channels_max = stream->channels_max;
|
||||||
|
if (runtime->hw.formats)
|
||||||
|
runtime->hw.formats &= stream->formats;
|
||||||
|
else
|
||||||
|
runtime->hw.formats = stream->formats;
|
||||||
|
runtime->hw.rates = stream->rates;
|
||||||
|
}
|
||||||
|
|
||||||
static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
|
static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
@ -1242,21 +1257,10 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
|
||||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||||
struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
|
struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
|
||||||
|
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
runtime->hw.rate_min = cpu_dai_drv->playback.rate_min;
|
dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback);
|
||||||
runtime->hw.rate_max = cpu_dai_drv->playback.rate_max;
|
else
|
||||||
runtime->hw.channels_min = cpu_dai_drv->playback.channels_min;
|
dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
|
||||||
runtime->hw.channels_max = cpu_dai_drv->playback.channels_max;
|
|
||||||
runtime->hw.formats &= cpu_dai_drv->playback.formats;
|
|
||||||
runtime->hw.rates = cpu_dai_drv->playback.rates;
|
|
||||||
} else {
|
|
||||||
runtime->hw.rate_min = cpu_dai_drv->capture.rate_min;
|
|
||||||
runtime->hw.rate_max = cpu_dai_drv->capture.rate_max;
|
|
||||||
runtime->hw.channels_min = cpu_dai_drv->capture.channels_min;
|
|
||||||
runtime->hw.channels_max = cpu_dai_drv->capture.channels_max;
|
|
||||||
runtime->hw.formats &= cpu_dai_drv->capture.formats;
|
|
||||||
runtime->hw.rates = cpu_dai_drv->capture.rates;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
|
static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
|
||||||
|
@ -2137,10 +2141,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
|
||||||
int ret = 0, playback = 0, capture = 0;
|
int ret = 0, playback = 0, capture = 0;
|
||||||
|
|
||||||
if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
|
if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
|
||||||
if (cpu_dai->driver->playback.channels_min)
|
playback = rtd->dai_link->dpcm_playback;
|
||||||
playback = 1;
|
capture = rtd->dai_link->dpcm_capture;
|
||||||
if (cpu_dai->driver->capture.channels_min)
|
|
||||||
capture = 1;
|
|
||||||
} else {
|
} else {
|
||||||
if (codec_dai->driver->playback.channels_min &&
|
if (codec_dai->driver->playback.channels_min &&
|
||||||
cpu_dai->driver->playback.channels_min)
|
cpu_dai->driver->playback.channels_min)
|
||||||
|
|
|
@ -119,6 +119,13 @@ static struct snd_soc_dai_driver dummy_dai = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int snd_soc_dai_is_dummy(struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
if (dai->driver == &dummy_dai)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int snd_soc_dummy_probe(struct platform_device *pdev)
|
static int snd_soc_dummy_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
|
@ -91,6 +91,8 @@ static int mop500_of_probe(struct platform_device *pdev,
|
||||||
for (i = 0; i < 2; i++) {
|
for (i = 0; i < 2; i++) {
|
||||||
mop500_dai_links[i].cpu_of_node = msp_np[i];
|
mop500_dai_links[i].cpu_of_node = msp_np[i];
|
||||||
mop500_dai_links[i].cpu_dai_name = NULL;
|
mop500_dai_links[i].cpu_dai_name = NULL;
|
||||||
|
mop500_dai_links[i].platform_of_node = msp_np[i];
|
||||||
|
mop500_dai_links[i].platform_name = NULL;
|
||||||
mop500_dai_links[i].codec_of_node = codec_np;
|
mop500_dai_links[i].codec_of_node = codec_np;
|
||||||
mop500_dai_links[i].codec_name = NULL;
|
mop500_dai_links[i].codec_name = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,14 @@
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/mfd/dbx500-prcmu.h>
|
#include <linux/mfd/dbx500-prcmu.h>
|
||||||
#include <linux/platform_data/asoc-ux500-msp.h>
|
#include <linux/platform_data/asoc-ux500-msp.h>
|
||||||
|
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
#include <sound/soc-dai.h>
|
#include <sound/soc-dai.h>
|
||||||
|
#include <sound/dmaengine_pcm.h>
|
||||||
|
|
||||||
#include "ux500_msp_i2s.h"
|
#include "ux500_msp_i2s.h"
|
||||||
#include "ux500_msp_dai.h"
|
#include "ux500_msp_dai.h"
|
||||||
|
@ -654,16 +656,52 @@ static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ux500_msp_dai_of_probe(struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
|
||||||
|
struct snd_dmaengine_dai_dma_data *playback_dma_data;
|
||||||
|
struct snd_dmaengine_dai_dma_data *capture_dma_data;
|
||||||
|
|
||||||
|
playback_dma_data = devm_kzalloc(dai->dev,
|
||||||
|
sizeof(*playback_dma_data),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!playback_dma_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
capture_dma_data = devm_kzalloc(dai->dev,
|
||||||
|
sizeof(*capture_dma_data),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!capture_dma_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
playback_dma_data->addr = drvdata->msp->playback_dma_data.tx_rx_addr;
|
||||||
|
capture_dma_data->addr = drvdata->msp->capture_dma_data.tx_rx_addr;
|
||||||
|
|
||||||
|
playback_dma_data->maxburst = 4;
|
||||||
|
capture_dma_data->maxburst = 4;
|
||||||
|
|
||||||
|
snd_soc_dai_init_dma_data(dai, playback_dma_data, capture_dma_data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int ux500_msp_dai_probe(struct snd_soc_dai *dai)
|
static int ux500_msp_dai_probe(struct snd_soc_dai *dai)
|
||||||
{
|
{
|
||||||
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
|
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
|
||||||
|
struct msp_i2s_platform_data *pdata = dai->dev->platform_data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
dai->playback_dma_data = &drvdata->msp->playback_dma_data;
|
if (!pdata) {
|
||||||
dai->capture_dma_data = &drvdata->msp->capture_dma_data;
|
ret = ux500_msp_dai_of_probe(dai);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
drvdata->msp->playback_dma_data.data_size = drvdata->slot_width;
|
drvdata->msp->playback_dma_data.data_size = drvdata->slot_width;
|
||||||
drvdata->msp->capture_dma_data.data_size = drvdata->slot_width;
|
drvdata->msp->capture_dma_data.data_size = drvdata->slot_width;
|
||||||
|
|
||||||
|
snd_soc_dai_init_dma_data(dai,
|
||||||
|
&drvdata->msp->playback_dma_data,
|
||||||
|
&drvdata->msp->capture_dma_data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,87 +718,19 @@ static struct snd_soc_dai_ops ux500_msp_dai_ops[] = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
|
static struct snd_soc_dai_driver ux500_msp_dai_drv = {
|
||||||
{
|
|
||||||
.name = "ux500-msp-i2s.0",
|
|
||||||
.probe = ux500_msp_dai_probe,
|
|
||||||
.id = 0,
|
|
||||||
.suspend = NULL,
|
|
||||||
.resume = NULL,
|
|
||||||
.playback = {
|
|
||||||
.channels_min = UX500_MSP_MIN_CHANNELS,
|
|
||||||
.channels_max = UX500_MSP_MAX_CHANNELS,
|
|
||||||
.rates = UX500_I2S_RATES,
|
|
||||||
.formats = UX500_I2S_FORMATS,
|
|
||||||
},
|
|
||||||
.capture = {
|
|
||||||
.channels_min = UX500_MSP_MIN_CHANNELS,
|
|
||||||
.channels_max = UX500_MSP_MAX_CHANNELS,
|
|
||||||
.rates = UX500_I2S_RATES,
|
|
||||||
.formats = UX500_I2S_FORMATS,
|
|
||||||
},
|
|
||||||
.ops = ux500_msp_dai_ops,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "ux500-msp-i2s.1",
|
|
||||||
.probe = ux500_msp_dai_probe,
|
|
||||||
.id = 1,
|
|
||||||
.suspend = NULL,
|
|
||||||
.resume = NULL,
|
|
||||||
.playback = {
|
|
||||||
.channels_min = UX500_MSP_MIN_CHANNELS,
|
|
||||||
.channels_max = UX500_MSP_MAX_CHANNELS,
|
|
||||||
.rates = UX500_I2S_RATES,
|
|
||||||
.formats = UX500_I2S_FORMATS,
|
|
||||||
},
|
|
||||||
.capture = {
|
|
||||||
.channels_min = UX500_MSP_MIN_CHANNELS,
|
|
||||||
.channels_max = UX500_MSP_MAX_CHANNELS,
|
|
||||||
.rates = UX500_I2S_RATES,
|
|
||||||
.formats = UX500_I2S_FORMATS,
|
|
||||||
},
|
|
||||||
.ops = ux500_msp_dai_ops,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "ux500-msp-i2s.2",
|
|
||||||
.id = 2,
|
|
||||||
.probe = ux500_msp_dai_probe,
|
.probe = ux500_msp_dai_probe,
|
||||||
.suspend = NULL,
|
.suspend = NULL,
|
||||||
.resume = NULL,
|
.resume = NULL,
|
||||||
.playback = {
|
.playback.channels_min = UX500_MSP_MIN_CHANNELS,
|
||||||
.channels_min = UX500_MSP_MIN_CHANNELS,
|
.playback.channels_max = UX500_MSP_MAX_CHANNELS,
|
||||||
.channels_max = UX500_MSP_MAX_CHANNELS,
|
.playback.rates = UX500_I2S_RATES,
|
||||||
.rates = UX500_I2S_RATES,
|
.playback.formats = UX500_I2S_FORMATS,
|
||||||
.formats = UX500_I2S_FORMATS,
|
.capture.channels_min = UX500_MSP_MIN_CHANNELS,
|
||||||
},
|
.capture.channels_max = UX500_MSP_MAX_CHANNELS,
|
||||||
.capture = {
|
.capture.rates = UX500_I2S_RATES,
|
||||||
.channels_min = UX500_MSP_MIN_CHANNELS,
|
.capture.formats = UX500_I2S_FORMATS,
|
||||||
.channels_max = UX500_MSP_MAX_CHANNELS,
|
|
||||||
.rates = UX500_I2S_RATES,
|
|
||||||
.formats = UX500_I2S_FORMATS,
|
|
||||||
},
|
|
||||||
.ops = ux500_msp_dai_ops,
|
.ops = ux500_msp_dai_ops,
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "ux500-msp-i2s.3",
|
|
||||||
.probe = ux500_msp_dai_probe,
|
|
||||||
.id = 3,
|
|
||||||
.suspend = NULL,
|
|
||||||
.resume = NULL,
|
|
||||||
.playback = {
|
|
||||||
.channels_min = UX500_MSP_MIN_CHANNELS,
|
|
||||||
.channels_max = UX500_MSP_MAX_CHANNELS,
|
|
||||||
.rates = UX500_I2S_RATES,
|
|
||||||
.formats = UX500_I2S_FORMATS,
|
|
||||||
},
|
|
||||||
.capture = {
|
|
||||||
.channels_min = UX500_MSP_MIN_CHANNELS,
|
|
||||||
.channels_max = UX500_MSP_MAX_CHANNELS,
|
|
||||||
.rates = UX500_I2S_RATES,
|
|
||||||
.formats = UX500_I2S_FORMATS,
|
|
||||||
},
|
|
||||||
.ops = ux500_msp_dai_ops,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct snd_soc_component_driver ux500_msp_component = {
|
static const struct snd_soc_component_driver ux500_msp_component = {
|
||||||
|
@ -771,10 +741,14 @@ static const struct snd_soc_component_driver ux500_msp_component = {
|
||||||
static int ux500_msp_drv_probe(struct platform_device *pdev)
|
static int ux500_msp_drv_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct ux500_msp_i2s_drvdata *drvdata;
|
struct ux500_msp_i2s_drvdata *drvdata;
|
||||||
|
struct msp_i2s_platform_data *pdata = pdev->dev.platform_data;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "%s: Enter (pdev->name = %s).\n", __func__,
|
if (!pdata && !np) {
|
||||||
pdev->name);
|
dev_err(&pdev->dev, "No platform data or Device Tree found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
drvdata = devm_kzalloc(&pdev->dev,
|
drvdata = devm_kzalloc(&pdev->dev,
|
||||||
sizeof(struct ux500_msp_i2s_drvdata),
|
sizeof(struct ux500_msp_i2s_drvdata),
|
||||||
|
@ -826,7 +800,7 @@ static int ux500_msp_drv_probe(struct platform_device *pdev)
|
||||||
dev_set_drvdata(&pdev->dev, drvdata);
|
dev_set_drvdata(&pdev->dev, drvdata);
|
||||||
|
|
||||||
ret = snd_soc_register_component(&pdev->dev, &ux500_msp_component,
|
ret = snd_soc_register_component(&pdev->dev, &ux500_msp_component,
|
||||||
&ux500_msp_dai_drv[drvdata->msp->id], 1);
|
&ux500_msp_dai_drv, 1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n",
|
dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n",
|
||||||
__func__, drvdata->msp->id);
|
__func__, drvdata->msp->id);
|
||||||
|
|
|
@ -646,6 +646,34 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ux500_msp_i2s_of_init_msp(struct platform_device *pdev,
|
||||||
|
struct ux500_msp *msp,
|
||||||
|
struct msp_i2s_platform_data **platform_data)
|
||||||
|
{
|
||||||
|
struct msp_i2s_platform_data *pdata;
|
||||||
|
|
||||||
|
*platform_data = devm_kzalloc(&pdev->dev,
|
||||||
|
sizeof(struct msp_i2s_platform_data),
|
||||||
|
GFP_KERNEL);
|
||||||
|
pdata = *platform_data;
|
||||||
|
if (!pdata)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
msp->playback_dma_data.dma_cfg = devm_kzalloc(&pdev->dev,
|
||||||
|
sizeof(struct stedma40_chan_cfg),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!msp->playback_dma_data.dma_cfg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
msp->capture_dma_data.dma_cfg = devm_kzalloc(&pdev->dev,
|
||||||
|
sizeof(struct stedma40_chan_cfg),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!msp->capture_dma_data.dma_cfg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int ux500_msp_i2s_init_msp(struct platform_device *pdev,
|
int ux500_msp_i2s_init_msp(struct platform_device *pdev,
|
||||||
struct ux500_msp **msp_p,
|
struct ux500_msp **msp_p,
|
||||||
struct msp_i2s_platform_data *platform_data)
|
struct msp_i2s_platform_data *platform_data)
|
||||||
|
@ -653,30 +681,28 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
|
||||||
struct resource *res = NULL;
|
struct resource *res = NULL;
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
struct ux500_msp *msp;
|
struct ux500_msp *msp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
*msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
|
*msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
|
||||||
msp = *msp_p;
|
msp = *msp_p;
|
||||||
if (!msp)
|
if (!msp)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (np) {
|
|
||||||
if (!platform_data) {
|
if (!platform_data) {
|
||||||
platform_data = devm_kzalloc(&pdev->dev,
|
if (np) {
|
||||||
sizeof(struct msp_i2s_platform_data), GFP_KERNEL);
|
ret = ux500_msp_i2s_of_init_msp(pdev, msp,
|
||||||
if (!platform_data)
|
&platform_data);
|
||||||
return -ENOMEM;
|
if (ret)
|
||||||
}
|
return ret;
|
||||||
} else
|
} else
|
||||||
if (!platform_data)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
} else {
|
||||||
dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__,
|
|
||||||
pdev->name, platform_data->id);
|
|
||||||
|
|
||||||
msp->id = platform_data->id;
|
|
||||||
msp->dev = &pdev->dev;
|
|
||||||
msp->playback_dma_data.dma_cfg = platform_data->msp_i2s_dma_tx;
|
msp->playback_dma_data.dma_cfg = platform_data->msp_i2s_dma_tx;
|
||||||
msp->capture_dma_data.dma_cfg = platform_data->msp_i2s_dma_rx;
|
msp->capture_dma_data.dma_cfg = platform_data->msp_i2s_dma_rx;
|
||||||
|
msp->id = platform_data->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
msp->dev = &pdev->dev;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (res == NULL) {
|
if (res == NULL) {
|
||||||
|
|
|
@ -475,7 +475,7 @@ struct ux500_msp_dma_params {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ux500_msp {
|
struct ux500_msp {
|
||||||
enum msp_i2s_id id;
|
int id;
|
||||||
void __iomem *registers;
|
void __iomem *registers;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct ux500_msp_dma_params playback_dma_data;
|
struct ux500_msp_dma_params playback_dma_data;
|
||||||
|
|
|
@ -28,12 +28,6 @@
|
||||||
#include "ux500_msp_i2s.h"
|
#include "ux500_msp_i2s.h"
|
||||||
#include "ux500_pcm.h"
|
#include "ux500_pcm.h"
|
||||||
|
|
||||||
#define UX500_PLATFORM_MIN_RATE 8000
|
|
||||||
#define UX500_PLATFORM_MAX_RATE 48000
|
|
||||||
|
|
||||||
#define UX500_PLATFORM_MIN_CHANNELS 1
|
|
||||||
#define UX500_PLATFORM_MAX_CHANNELS 8
|
|
||||||
|
|
||||||
#define UX500_PLATFORM_PERIODS_BYTES_MIN 128
|
#define UX500_PLATFORM_PERIODS_BYTES_MIN 128
|
||||||
#define UX500_PLATFORM_PERIODS_BYTES_MAX (64 * PAGE_SIZE)
|
#define UX500_PLATFORM_PERIODS_BYTES_MAX (64 * PAGE_SIZE)
|
||||||
#define UX500_PLATFORM_PERIODS_MIN 2
|
#define UX500_PLATFORM_PERIODS_MIN 2
|
||||||
|
@ -45,15 +39,6 @@ static const struct snd_pcm_hardware ux500_pcm_hw = {
|
||||||
SNDRV_PCM_INFO_MMAP |
|
SNDRV_PCM_INFO_MMAP |
|
||||||
SNDRV_PCM_INFO_RESUME |
|
SNDRV_PCM_INFO_RESUME |
|
||||||
SNDRV_PCM_INFO_PAUSE,
|
SNDRV_PCM_INFO_PAUSE,
|
||||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
|
||||||
SNDRV_PCM_FMTBIT_U16_LE |
|
|
||||||
SNDRV_PCM_FMTBIT_S16_BE |
|
|
||||||
SNDRV_PCM_FMTBIT_U16_BE,
|
|
||||||
.rates = SNDRV_PCM_RATE_KNOT,
|
|
||||||
.rate_min = UX500_PLATFORM_MIN_RATE,
|
|
||||||
.rate_max = UX500_PLATFORM_MAX_RATE,
|
|
||||||
.channels_min = UX500_PLATFORM_MIN_CHANNELS,
|
|
||||||
.channels_max = UX500_PLATFORM_MAX_CHANNELS,
|
|
||||||
.buffer_bytes_max = UX500_PLATFORM_BUFFER_BYTES_MAX,
|
.buffer_bytes_max = UX500_PLATFORM_BUFFER_BYTES_MAX,
|
||||||
.period_bytes_min = UX500_PLATFORM_PERIODS_BYTES_MIN,
|
.period_bytes_min = UX500_PLATFORM_PERIODS_BYTES_MIN,
|
||||||
.period_bytes_max = UX500_PLATFORM_PERIODS_BYTES_MAX,
|
.period_bytes_max = UX500_PLATFORM_PERIODS_BYTES_MAX,
|
||||||
|
@ -65,14 +50,10 @@ static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd,
|
||||||
struct snd_pcm_substream *substream)
|
struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_soc_dai *dai = rtd->cpu_dai;
|
struct snd_soc_dai *dai = rtd->cpu_dai;
|
||||||
struct device *dev = dai->dev;
|
|
||||||
u16 per_data_width, mem_data_width;
|
u16 per_data_width, mem_data_width;
|
||||||
struct stedma40_chan_cfg *dma_cfg;
|
struct stedma40_chan_cfg *dma_cfg;
|
||||||
struct ux500_msp_dma_params *dma_params;
|
struct ux500_msp_dma_params *dma_params;
|
||||||
|
|
||||||
dev_dbg(dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id,
|
|
||||||
snd_pcm_stream_str(substream));
|
|
||||||
|
|
||||||
dma_params = snd_soc_dai_get_dma_data(dai, substream);
|
dma_params = snd_soc_dai_get_dma_data(dai, substream);
|
||||||
dma_cfg = dma_params->dma_cfg;
|
dma_cfg = dma_params->dma_cfg;
|
||||||
|
|
||||||
|
@ -108,26 +89,36 @@ static int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
|
||||||
struct dma_slave_config *slave_config)
|
struct dma_slave_config *slave_config)
|
||||||
{
|
{
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
struct ux500_msp_dma_params *dma_params;
|
struct msp_i2s_platform_data *pdata = rtd->cpu_dai->dev->platform_data;
|
||||||
struct stedma40_chan_cfg *dma_cfg;
|
struct snd_dmaengine_dai_dma_data *snd_dma_params;
|
||||||
|
struct ux500_msp_dma_params *ste_dma_params;
|
||||||
|
dma_addr_t dma_addr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
if (pdata) {
|
||||||
dma_cfg = dma_params->dma_cfg;
|
ste_dma_params =
|
||||||
|
snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||||
|
dma_addr = ste_dma_params->tx_rx_addr;
|
||||||
|
} else {
|
||||||
|
snd_dma_params =
|
||||||
|
snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||||
|
dma_addr = snd_dma_params->addr;
|
||||||
|
}
|
||||||
|
|
||||||
ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
|
ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
slave_config->dst_maxburst = 4;
|
slave_config->dst_maxburst = 4;
|
||||||
slave_config->dst_addr_width = dma_cfg->dst_info.data_width;
|
|
||||||
slave_config->src_maxburst = 4;
|
slave_config->src_maxburst = 4;
|
||||||
slave_config->src_addr_width = dma_cfg->src_info.data_width;
|
|
||||||
|
slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||||
|
slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||||
|
|
||||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
slave_config->dst_addr = dma_params->tx_rx_addr;
|
slave_config->dst_addr = dma_addr;
|
||||||
else
|
else
|
||||||
slave_config->src_addr = dma_params->tx_rx_addr;
|
slave_config->src_addr = dma_addr;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -139,15 +130,25 @@ static const struct snd_dmaengine_pcm_config ux500_dmaengine_pcm_config = {
|
||||||
.prepare_slave_config = ux500_pcm_prepare_slave_config,
|
.prepare_slave_config = ux500_pcm_prepare_slave_config,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct snd_dmaengine_pcm_config ux500_dmaengine_of_pcm_config = {
|
||||||
|
.compat_request_channel = ux500_pcm_request_chan,
|
||||||
|
.prepare_slave_config = ux500_pcm_prepare_slave_config,
|
||||||
|
};
|
||||||
|
|
||||||
int ux500_pcm_register_platform(struct platform_device *pdev)
|
int ux500_pcm_register_platform(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
const struct snd_dmaengine_pcm_config *pcm_config;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = snd_dmaengine_pcm_register(&pdev->dev,
|
if (np)
|
||||||
&ux500_dmaengine_pcm_config,
|
pcm_config = &ux500_dmaengine_of_pcm_config;
|
||||||
|
else
|
||||||
|
pcm_config = &ux500_dmaengine_pcm_config;
|
||||||
|
|
||||||
|
ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config,
|
||||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
|
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
|
||||||
SND_DMAENGINE_PCM_FLAG_COMPAT |
|
SND_DMAENGINE_PCM_FLAG_COMPAT);
|
||||||
SND_DMAENGINE_PCM_FLAG_NO_DT);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"%s: ERROR: Failed to register platform '%s' (%d)!\n",
|
"%s: ERROR: Failed to register platform '%s' (%d)!\n",
|
||||||
|
|
Loading…
Reference in New Issue