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.
|
||||
|
||||
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.
|
||||
- reg: Offset and length of the register set for the device.
|
||||
- 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.
|
||||
"i2s", "right_j", "left_j" , "dsp_a"
|
||||
"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
|
||||
connection's sink, the second being the connection's
|
||||
source.
|
||||
|
|
|
@ -6,6 +6,7 @@ Required properties:
|
|||
|
||||
- compatible - "string" - One of:
|
||||
"ti,tlv320aic3x" - Generic TLV320AIC3x device
|
||||
"ti,tlv320aic32x4" - TLV320AIC32x4
|
||||
"ti,tlv320aic33" - TLV320AIC33
|
||||
"ti,tlv320aic3007" - TLV320AIC3007
|
||||
"ti,tlv320aic3106" - TLV320AIC3106
|
||||
|
|
|
@ -49,18 +49,23 @@ features :-
|
|||
* Machine specific controls: Allow machines to add controls to the sound card
|
||||
(e.g. volume control for speaker amplifier).
|
||||
|
||||
To achieve all this, ASoC basically splits an embedded audio system into 3
|
||||
components :-
|
||||
To achieve all this, ASoC basically splits an embedded audio system into
|
||||
multiple re-usable component drivers :-
|
||||
|
||||
* Codec driver: The codec driver is platform independent and contains audio
|
||||
controls, audio interface capabilities, codec DAPM definition and codec IO
|
||||
functions.
|
||||
* Codec class drivers: The codec class driver is platform independent and
|
||||
contains audio controls, audio interface capabilities, codec DAPM
|
||||
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
|
||||
interface drivers (e.g. I2S, AC97, PCM) for that platform.
|
||||
* Platform class drivers: The platform class driver includes the audio DMA
|
||||
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
|
||||
audio events (e.g. turning on an amp at start of playback).
|
||||
* Machine class driver: The machine driver class acts as the glue that
|
||||
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
|
||||
|
@ -84,3 +89,7 @@ machine.txt: Machine driver internals.
|
|||
pop_clicks.txt: How to minimise audio artifacts.
|
||||
|
||||
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 = {
|
||||
.id = MSP_I2S_0,
|
||||
.id = 0,
|
||||
.msp_i2s_dma_rx = &msp0_dma_rx,
|
||||
.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 = {
|
||||
.id = MSP_I2S_1,
|
||||
.id = 1,
|
||||
.msp_i2s_dma_rx = NULL,
|
||||
.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 = {
|
||||
.id = MSP_I2S_2,
|
||||
.id = 2,
|
||||
.msp_i2s_dma_rx = &msp2_dma_rx,
|
||||
.msp_i2s_dma_tx = &msp2_dma_tx,
|
||||
};
|
||||
|
||||
struct msp_i2s_platform_data msp3_platform_data = {
|
||||
.id = MSP_I2S_3,
|
||||
.id = 3,
|
||||
.msp_i2s_dma_rx = &msp1_dma_rx,
|
||||
.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->cmd_pause = false;
|
||||
caps->cmd_terminate = true;
|
||||
caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,9 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/twl.h>
|
||||
|
||||
/* Register descriptions for audio */
|
||||
#include <linux/mfd/twl4030-audio.h>
|
||||
|
||||
#include "twl-core.h"
|
||||
|
||||
/*
|
||||
|
@ -200,6 +203,105 @@ static struct twl_mapping twl4030_map[] = {
|
|||
{ 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] = {
|
||||
{
|
||||
/* Address 0x48 */
|
||||
|
@ -212,6 +314,15 @@ static struct regmap_config twl4030_regmap_config[4] = {
|
|||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.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 */
|
||||
|
@ -301,6 +412,32 @@ unsigned int twl_rev(void)
|
|||
}
|
||||
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
|
||||
* @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)
|
||||
{
|
||||
struct regmap *regmap = twl_get_regmap(mod_no);
|
||||
int ret;
|
||||
int sid;
|
||||
struct twl_client *twl;
|
||||
|
||||
if (unlikely(!twl_priv || !twl_priv->ready)) {
|
||||
pr_err("%s: not initialized\n", DRIVER_NAME);
|
||||
if (!regmap)
|
||||
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;
|
||||
twl = &twl_priv->twl_modules[sid];
|
||||
|
||||
ret = regmap_bulk_write(twl->regmap,
|
||||
twl_priv->twl_map[mod_no].base + reg, value,
|
||||
num_bytes);
|
||||
ret = regmap_bulk_write(regmap, twl_priv->twl_map[mod_no].base + reg,
|
||||
value, num_bytes);
|
||||
|
||||
if (ret)
|
||||
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)
|
||||
{
|
||||
struct regmap *regmap = twl_get_regmap(mod_no);
|
||||
int ret;
|
||||
int sid;
|
||||
struct twl_client *twl;
|
||||
|
||||
if (unlikely(!twl_priv || !twl_priv->ready)) {
|
||||
pr_err("%s: not initialized\n", DRIVER_NAME);
|
||||
if (!regmap)
|
||||
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;
|
||||
twl = &twl_priv->twl_modules[sid];
|
||||
|
||||
ret = regmap_bulk_read(twl->regmap,
|
||||
twl_priv->twl_map[mod_no].base + reg, value,
|
||||
num_bytes);
|
||||
ret = regmap_bulk_read(regmap, twl_priv->twl_map[mod_no].base + reg,
|
||||
value, num_bytes);
|
||||
|
||||
if (ret)
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
{ 0x00000492, 0x0069 }, /* R1170 - PDM SPK2 CTRL 1 */
|
||||
{ 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 */
|
||||
{ 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx 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_SPK2_CTRL_1:
|
||||
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_TX_PIN_CTRL:
|
||||
case ARIZONA_AIF1_RX_PIN_CTRL:
|
||||
|
|
|
@ -364,6 +364,32 @@ struct dma_slave_config {
|
|||
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
|
||||
*
|
||||
* @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
|
||||
* @cmd_pause: true, if pause and thereby resume is supported
|
||||
* @cmd_terminate: true, if terminate cmd is supported
|
||||
* @residue_granularity: granularity of the reported transfer residue
|
||||
*/
|
||||
struct dma_slave_caps {
|
||||
u32 src_addr_widths;
|
||||
|
@ -381,6 +408,7 @@ struct dma_slave_caps {
|
|||
u32 directions;
|
||||
bool cmd_pause;
|
||||
bool cmd_terminate;
|
||||
enum dma_residue_granularity residue_granularity;
|
||||
};
|
||||
|
||||
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(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.
|
||||
*/
|
||||
|
@ -667,8 +670,6 @@ struct twl4030_codec_data {
|
|||
unsigned int digimic_delay; /* in ms */
|
||||
unsigned int ramp_delay_value;
|
||||
unsigned int offset_cncl_path;
|
||||
unsigned int check_defaults:1;
|
||||
unsigned int reset_registers:1;
|
||||
unsigned int hs_extmute:1;
|
||||
int hs_extmute_gpio;
|
||||
};
|
||||
|
|
|
@ -226,6 +226,9 @@
|
|||
#define ARIZONA_PDM_SPK1_CTRL_2 0x491
|
||||
#define ARIZONA_PDM_SPK2_CTRL_1 0x492
|
||||
#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_3 0x4B6
|
||||
#define ARIZONA_DAC_COMP_1 0x4DC
|
||||
|
@ -3332,6 +3335,30 @@
|
|||
#define ARIZONA_SPK2_FMT_SHIFT 0 /* 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
|
||||
*/
|
||||
|
|
|
@ -10,16 +10,9 @@
|
|||
|
||||
#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 */
|
||||
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_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);
|
||||
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_mask_intersect(unsigned int rates_a,
|
||||
unsigned int rates_b);
|
||||
|
||||
static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substream,
|
||||
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 direction);
|
||||
|
||||
int snd_soc_dai_is_dummy(struct snd_soc_dai *dai);
|
||||
|
||||
struct snd_soc_dai_ops {
|
||||
/*
|
||||
* 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,
|
||||
struct snd_soc_dai *dai);
|
||||
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,
|
||||
const struct snd_soc_pcm_stream *params,
|
||||
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)*/
|
||||
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 */
|
||||
unsigned int ignore_pmdown_time:1;
|
||||
|
||||
|
|
|
@ -514,3 +514,42 @@ unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit)
|
|||
return 0;
|
||||
}
|
||||
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)
|
||||
goto err_clk_disable;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
|
||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
|
|
|
@ -229,8 +229,7 @@ static int axi_spdif_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
|
||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||
if (ret)
|
||||
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_RESUME |
|
||||
SNDRV_PCM_INFO_PAUSE,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.period_bytes_min = 256, /* lighting DMA overhead */
|
||||
.period_bytes_max = 2 * 0xffff, /* if 2 bytes format */
|
||||
.periods_min = 8,
|
||||
|
|
|
@ -58,7 +58,6 @@ static const struct snd_pcm_hardware atmel_pcm_hardware = {
|
|||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.period_bytes_min = 32,
|
||||
.period_bytes_max = 8192,
|
||||
.periods_min = 2,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
config SND_BCM2835_SOC_I2S
|
||||
tristate "SoC Audio support for the Broadcom BCM2835 I2S module"
|
||||
depends on ARCH_BCM2835 || COMPILE_TEST
|
||||
select SND_SOC_DMAENGINE_PCM
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
|
|
|
@ -168,15 +168,15 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream,
|
|||
int word_len = 0;
|
||||
|
||||
/* bit size */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
word_len = AD1836_WORD_LEN_16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
word_len = AD1836_WORD_LEN_20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
case 24:
|
||||
case 32:
|
||||
word_len = AD1836_WORD_LEN_24;
|
||||
break;
|
||||
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);
|
||||
|
||||
/* bit size */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
word_len = 3;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
word_len = 1;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
case 24:
|
||||
case 32:
|
||||
word_len = 0;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1078,17 +1078,17 @@ static int adau1373_hw_params(struct snd_pcm_substream *substream,
|
|||
ADAU1373_BCLKDIV_SR_MASK | ADAU1373_BCLKDIV_BCLK_MASK,
|
||||
(div << 2) | ADAU1373_BCLKDIV_64);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
ctrl = ADAU1373_DAI_WLEN_16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
ctrl = ADAU1373_DAI_WLEN_20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
ctrl = ADAU1373_DAI_WLEN_24;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
case 32:
|
||||
ctrl = ADAU1373_DAI_WLEN_32;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
|
||||
#define ADAU1701_SEROCTL_WORD_LEN_24 0x0000
|
||||
#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_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,
|
||||
snd_pcm_format_t format)
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK;
|
||||
unsigned int val;
|
||||
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
val = ADAU1701_SEROCTL_WORD_LEN_16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
val = ADAU1701_SEROCTL_WORD_LEN_20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
val = ADAU1701_SEROCTL_WORD_LEN_24;
|
||||
break;
|
||||
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) {
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
val |= ADAU1701_SEROCTL_MSB_DEALY16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
val |= ADAU1701_SEROCTL_MSB_DEALY12;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
val |= ADAU1701_SEROCTL_MSB_DEALY8;
|
||||
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,
|
||||
snd_pcm_format_t format)
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
|
||||
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)
|
||||
return 0;
|
||||
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
val = ADAU1701_SERICTL_RIGHTJ_16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
val = ADAU1701_SERICTL_RIGHTJ_20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
val = ADAU1701_SERICTL_RIGHTJ_24;
|
||||
break;
|
||||
default:
|
||||
|
@ -374,7 +374,6 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int clkdiv = adau1701->sysclk / params_rate(params);
|
||||
snd_pcm_format_t format;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
|
@ -406,11 +405,10 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
|
|||
regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
|
||||
ADAU1701_DSPCTRL_SR_MASK, val);
|
||||
|
||||
format = params_format(params);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
return adau1701_set_playback_pcm_format(codec, format);
|
||||
return adau1701_set_playback_pcm_format(codec, params);
|
||||
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,
|
||||
|
|
|
@ -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,
|
||||
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);
|
||||
unsigned int val;
|
||||
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
val = ADAV80X_CAPTURE_WORD_LEN16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
||||
case 18:
|
||||
val = ADAV80X_CAPTRUE_WORD_LEN18;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
val = ADAV80X_CAPTURE_WORD_LEN20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
val = ADAV80X_CAPTURE_WORD_LEN24;
|
||||
break;
|
||||
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,
|
||||
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);
|
||||
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)
|
||||
return 0;
|
||||
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
||||
case 18:
|
||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
|
||||
break;
|
||||
default:
|
||||
|
@ -524,12 +524,10 @@ static int adav80x_hw_params(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
adav80x_set_playback_pcm_format(codec, dai,
|
||||
params_format(params));
|
||||
adav80x_set_playback_pcm_format(codec, dai, params);
|
||||
adav80x_set_dac_clock(codec, rate);
|
||||
} else {
|
||||
adav80x_set_capture_pcm_format(codec, dai,
|
||||
params_format(params));
|
||||
adav80x_set_capture_pcm_format(codec, dai, params);
|
||||
adav80x_set_adc_clock(codec, 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;
|
||||
|
||||
/* bit size */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
iface |= ALC5623_DAI_I2S_DL_16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
iface |= ALC5623_DAI_I2S_DL_20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
iface |= ALC5623_DAI_I2S_DL_24;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
case 32:
|
||||
iface |= ALC5623_DAI_I2S_DL_32;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -869,14 +869,14 @@ static int alc5632_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
iface &= ~ALC5632_DAI_I2S_DL_MASK;
|
||||
|
||||
/* bit size */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
iface |= ALC5632_DAI_I2S_DL_16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
iface |= ALC5632_DAI_I2S_DL_20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
iface |= ALC5632_DAI_I2S_DL_24;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -166,20 +166,21 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
|
|||
ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
|
||||
|
||||
#define ARIZONA_DSP_ROUTES(name) \
|
||||
{ name, NULL, name " Aux 1" }, \
|
||||
{ name, NULL, name " Aux 2" }, \
|
||||
{ name, NULL, name " Aux 3" }, \
|
||||
{ name, NULL, name " Aux 4" }, \
|
||||
{ name, NULL, name " Aux 5" }, \
|
||||
{ name, NULL, name " Aux 6" }, \
|
||||
{ name, NULL, name " Preloader"}, \
|
||||
{ name " Preloader", NULL, name " Aux 1" }, \
|
||||
{ name " Preloader", NULL, name " Aux 2" }, \
|
||||
{ name " Preloader", NULL, name " Aux 3" }, \
|
||||
{ name " Preloader", NULL, name " Aux 4" }, \
|
||||
{ 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 2"), \
|
||||
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \
|
||||
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \
|
||||
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \
|
||||
ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \
|
||||
ARIZONA_MIXER_ROUTES(name, name "L"), \
|
||||
ARIZONA_MIXER_ROUTES(name, name "R")
|
||||
ARIZONA_MIXER_ROUTES(name " Preloader", name "L"), \
|
||||
ARIZONA_MIXER_ROUTES(name " Preloader", name "R")
|
||||
|
||||
#define ARIZONA_RATE_ENUM_SIZE 4
|
||||
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);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
case SNDRV_PCM_FORMAT_S16_BE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
fmt = CS42L51_DAC_DIF_RJ16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
||||
case SNDRV_PCM_FORMAT_S18_3BE:
|
||||
case 18:
|
||||
fmt = CS42L51_DAC_DIF_RJ18;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case SNDRV_PCM_FORMAT_S20_3BE:
|
||||
case 20:
|
||||
fmt = CS42L51_DAC_DIF_RJ20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case SNDRV_PCM_FORMAT_S24_BE:
|
||||
case 24:
|
||||
fmt = CS42L51_DAC_DIF_RJ24;
|
||||
break;
|
||||
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);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
dai_cfg1 |= DA7210_DAI_WORD_S16_LE;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
dai_cfg1 |= DA7210_DAI_WORD_S20_3LE;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
dai_cfg1 |= DA7210_DAI_WORD_S24_LE;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
case 32:
|
||||
dai_cfg1 |= DA7210_DAI_WORD_S32_LE;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -1067,17 +1067,17 @@ static int da7213_hw_params(struct snd_pcm_substream *substream,
|
|||
u8 fs;
|
||||
|
||||
/* Set DAI format */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S16_LE;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S20_LE;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S24_LE;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
case 32:
|
||||
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S32_LE;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -973,17 +973,17 @@ static int da732x_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
reg_aif = dai->driver->base;
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
aif |= DA732X_AIF_WORD_16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
aif |= DA732X_AIF_WORD_20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
aif |= DA732X_AIF_WORD_24;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
case 32:
|
||||
aif |= DA732X_AIF_WORD_32;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -1058,17 +1058,17 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
|
|||
u8 aif_ctrl, fs;
|
||||
u32 sysclk;
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
aif_ctrl = DA9055_AIF_WORD_S16_LE;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
aif_ctrl = DA9055_AIF_WORD_S20_3LE;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
aif_ctrl = DA9055_AIF_WORD_S24_LE;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
case 32:
|
||||
aif_ctrl = DA9055_AIF_WORD_S32_LE;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -951,11 +951,11 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
|
|||
ISABELLE_FS_RATE_MASK, fs_val);
|
||||
|
||||
/* bit size */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
switch (params_width(params)) {
|
||||
case 20:
|
||||
aif |= ISABELLE_AIF_LENGTH_20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
case 32:
|
||||
aif |= ISABELLE_AIF_LENGTH_32;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -1233,12 +1233,12 @@ static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
rate = params_rate(params);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
|
||||
M98088_DAI_WS, 0);
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
|
||||
M98088_DAI_WS, M98088_DAI_WS);
|
||||
break;
|
||||
|
|
|
@ -1840,8 +1840,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
max98090->lrclk = params_rate(params);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
snd_soc_update_bits(codec, M98090_REG_INTERFACE_FORMAT,
|
||||
M98090_WS_MASK, 0);
|
||||
break;
|
||||
|
|
|
@ -1213,12 +1213,12 @@ static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
rate = params_rate(params);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
|
||||
M98095_DAI_WS, 0);
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
|
||||
M98095_DAI_WS, M98095_DAI_WS);
|
||||
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_LSB, lrclk_div & 0xff);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
da = 0;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
case 20:
|
||||
da = 0x2;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
case 24:
|
||||
da = 0x3;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -750,30 +750,26 @@ static struct snd_soc_codec_driver soc_codec_dev_mc13783 = {
|
|||
.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 mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
|
||||
int ret;
|
||||
|
||||
mc13xxx = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
priv->mc13xxx = mc13xxx;
|
||||
if (pdata) {
|
||||
priv->adc_ssi_port = pdata->adc_ssi_port;
|
||||
priv->dac_ssi_port = pdata->dac_ssi_port;
|
||||
} else {
|
||||
priv->adc_ssi_port = MC13783_SSI1_PORT;
|
||||
priv->dac_ssi_port = MC13783_SSI2_PORT;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
if (priv->adc_ssi_port == priv->dac_ssi_port)
|
||||
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -801,14 +789,12 @@ static int mc13783_codec_remove(struct platform_device *pdev)
|
|||
|
||||
static struct platform_driver mc13783_codec_driver = {
|
||||
.driver = {
|
||||
.name = "mc13783-codec",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mc13783_codec_probe,
|
||||
.name = "mc13783-codec",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.remove = mc13783_codec_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mc13783_codec_driver);
|
||||
module_platform_driver_probe(mc13783_codec_driver, mc13783_codec_probe);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC MC13783 driver");
|
||||
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[] = {
|
||||
8000, 32000, 48000, 96000,
|
||||
8000, 16000, 32000, 48000, 96000,
|
||||
};
|
||||
|
||||
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)},
|
||||
{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 */
|
||||
{12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 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;
|
||||
}
|
||||
|
||||
#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_32000 |\
|
||||
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
|
||||
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
|
||||
#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
|
||||
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
|
||||
SNDRV_PCM_RATE_96000)
|
||||
|
||||
#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||
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,
|
||||
.window_start = 0,
|
||||
.window_len = 128,
|
||||
.range_min = AIC32X4_PAGE1,
|
||||
.range_max = AIC32X4_PAGE1 + 127,
|
||||
.range_min = 0,
|
||||
.range_max = AIC32X4_RMICPGAVOL,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -48,86 +48,6 @@
|
|||
|
||||
#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 */
|
||||
struct twl4030_priv {
|
||||
unsigned int codec_powered;
|
||||
|
@ -150,81 +70,108 @@ struct twl4030_priv {
|
|||
u8 earpiece_enabled;
|
||||
u8 predrivel_enabled, predriver_enabled;
|
||||
u8 carkitl_enabled, carkitr_enabled;
|
||||
u8 ctl_cache[TWL4030_REG_PRECKR_CTL - TWL4030_REG_EAR_CTL + 1];
|
||||
|
||||
struct twl4030_codec_data *pdata;
|
||||
};
|
||||
|
||||
/*
|
||||
* read twl4030 register cache
|
||||
*/
|
||||
static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
static void tw4030_init_ctl_cache(struct twl4030_priv *twl4030)
|
||||
{
|
||||
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)
|
||||
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)
|
||||
static bool twl4030_can_write_to_chip(struct twl4030_priv *twl4030,
|
||||
unsigned int reg)
|
||||
{
|
||||
u8 *cache = codec->reg_cache;
|
||||
bool write_to_reg = false;
|
||||
|
||||
if (reg >= TWL4030_CACHEREGNUM)
|
||||
return;
|
||||
cache[reg] = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* write to the twl4030 register space
|
||||
*/
|
||||
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);
|
||||
int write_to_reg = 0;
|
||||
|
||||
twl4030_write_reg_cache(codec, reg, value);
|
||||
/* Decide if the given register can be written */
|
||||
switch (reg) {
|
||||
case TWL4030_REG_EAR_CTL:
|
||||
if (twl4030->earpiece_enabled)
|
||||
write_to_reg = 1;
|
||||
write_to_reg = true;
|
||||
break;
|
||||
case TWL4030_REG_PREDL_CTL:
|
||||
if (twl4030->predrivel_enabled)
|
||||
write_to_reg = 1;
|
||||
write_to_reg = true;
|
||||
break;
|
||||
case TWL4030_REG_PREDR_CTL:
|
||||
if (twl4030->predriver_enabled)
|
||||
write_to_reg = 1;
|
||||
write_to_reg = true;
|
||||
break;
|
||||
case TWL4030_REG_PRECKL_CTL:
|
||||
if (twl4030->carkitl_enabled)
|
||||
write_to_reg = 1;
|
||||
write_to_reg = true;
|
||||
break;
|
||||
case TWL4030_REG_PRECKR_CTL:
|
||||
if (twl4030->carkitr_enabled)
|
||||
write_to_reg = 1;
|
||||
write_to_reg = true;
|
||||
break;
|
||||
case TWL4030_REG_HS_GAIN_SET:
|
||||
if (twl4030->hsl_enabled || twl4030->hsr_enabled)
|
||||
write_to_reg = 1;
|
||||
write_to_reg = true;
|
||||
break;
|
||||
default:
|
||||
/* All other register can be written */
|
||||
write_to_reg = 1;
|
||||
write_to_reg = true;
|
||||
break;
|
||||
}
|
||||
if (write_to_reg)
|
||||
return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
|
||||
value, reg);
|
||||
|
||||
return write_to_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;
|
||||
}
|
||||
|
@ -252,46 +199,14 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
|
|||
else
|
||||
mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
|
||||
|
||||
if (mode >= 0) {
|
||||
twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode);
|
||||
if (mode >= 0)
|
||||
twl4030->codec_powered = enable;
|
||||
}
|
||||
|
||||
/* REVISIT: this delay is present in TI sample drivers */
|
||||
/* but there seems to be no TRM requirement for it */
|
||||
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,
|
||||
struct device_node *node)
|
||||
{
|
||||
|
@ -372,27 +287,17 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
|
|||
}
|
||||
}
|
||||
|
||||
/* Check defaults, if instructed before anything else */
|
||||
if (pdata && pdata->check_defaults)
|
||||
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);
|
||||
/* Initialize the local ctl register cache */
|
||||
tw4030_init_ctl_cache(twl4030);
|
||||
|
||||
/* 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,
|
||||
reg | TWL4030_SMOOTH_ANAVOL_EN);
|
||||
reg | TWL4030_SMOOTH_ANAVOL_EN);
|
||||
|
||||
twl4030_write(codec, TWL4030_REG_OPTION,
|
||||
TWL4030_ATXL1_EN | TWL4030_ATXR1_EN |
|
||||
TWL4030_ARXL2_EN | TWL4030_ARXR2_EN);
|
||||
TWL4030_ATXL1_EN | TWL4030_ATXR1_EN |
|
||||
TWL4030_ARXL2_EN | TWL4030_ARXR2_EN);
|
||||
|
||||
/* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */
|
||||
twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32);
|
||||
|
@ -403,19 +308,19 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
|
|||
|
||||
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 |= (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 */
|
||||
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 |= pdata->offset_cncl_path;
|
||||
twl4030_write(codec, TWL4030_REG_ANAMICL,
|
||||
reg | TWL4030_CNCL_OFFSET_START);
|
||||
reg | TWL4030_CNCL_OFFSET_START);
|
||||
|
||||
/*
|
||||
* Wait for offset cancellation to complete.
|
||||
|
@ -425,15 +330,14 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
|
|||
msleep(20);
|
||||
do {
|
||||
usleep_range(1000, 2000);
|
||||
twl_set_regcache_bypass(TWL4030_MODULE_AUDIO_VOICE, true);
|
||||
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) &&
|
||||
((byte & 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);
|
||||
}
|
||||
|
||||
|
@ -453,9 +357,6 @@ static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
|
|||
status = twl4030_audio_disable_resource(
|
||||
TWL4030_AUDIO_RES_APLL);
|
||||
}
|
||||
|
||||
if (status >= 0)
|
||||
twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
|
||||
}
|
||||
|
||||
/* Earpiece */
|
||||
|
@ -671,20 +572,18 @@ static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control =
|
|||
*/
|
||||
#define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \
|
||||
static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \
|
||||
struct snd_kcontrol *kcontrol, int event) \
|
||||
struct snd_kcontrol *kcontrol, int event) \
|
||||
{ \
|
||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); \
|
||||
\
|
||||
switch (event) { \
|
||||
case SND_SOC_DAPM_POST_PMU: \
|
||||
twl4030->pin_name##_enabled = 1; \
|
||||
twl4030_write(w->codec, reg, \
|
||||
twl4030_read_reg_cache(w->codec, reg)); \
|
||||
twl4030_write(w->codec, reg, twl4030_read(w->codec, reg)); \
|
||||
break; \
|
||||
case SND_SOC_DAPM_POST_PMD: \
|
||||
twl4030->pin_name##_enabled = 0; \
|
||||
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \
|
||||
0, reg); \
|
||||
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 0, reg); \
|
||||
break; \
|
||||
} \
|
||||
return 0; \
|
||||
|
@ -700,7 +599,7 @@ static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp)
|
|||
{
|
||||
unsigned char hs_ctl;
|
||||
|
||||
hs_ctl = twl4030_read_reg_cache(codec, reg);
|
||||
hs_ctl = twl4030_read(codec, reg);
|
||||
|
||||
if (ramp) {
|
||||
/* HF ramp-up */
|
||||
|
@ -727,7 +626,7 @@ static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp)
|
|||
}
|
||||
|
||||
static int handsfreelpga_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
|
@ -741,7 +640,7 @@ static int handsfreelpga_event(struct snd_soc_dapm_widget *w,
|
|||
}
|
||||
|
||||
static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
|
@ -755,14 +654,14 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
|
|||
}
|
||||
|
||||
static int vibramux_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
twl4030_write(w->codec, TWL4030_REG_VIBRA_SET, 0xff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apll_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
|
@ -776,11 +675,11 @@ static int apll_event(struct snd_soc_dapm_widget *w,
|
|||
}
|
||||
|
||||
static int aif_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
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) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
/* Enable AIF */
|
||||
|
@ -788,12 +687,12 @@ static int aif_event(struct snd_soc_dapm_widget *w,
|
|||
twl4030_apll_enable(w->codec, 1);
|
||||
|
||||
twl4030_write(w->codec, TWL4030_REG_AUDIO_IF,
|
||||
audio_if | TWL4030_AIF_EN);
|
||||
audio_if | TWL4030_AIF_EN);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
/* disable the DAI before we stop it's source PLL */
|
||||
twl4030_write(w->codec, TWL4030_REG_AUDIO_IF,
|
||||
audio_if & ~TWL4030_AIF_EN);
|
||||
audio_if & ~TWL4030_AIF_EN);
|
||||
twl4030_apll_enable(w->codec, 0);
|
||||
break;
|
||||
}
|
||||
|
@ -810,8 +709,8 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
|
|||
8388608, 16777216, 33554432, 67108864};
|
||||
unsigned int delay;
|
||||
|
||||
hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET);
|
||||
hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
|
||||
hs_gain = twl4030_read(codec, TWL4030_REG_HS_GAIN_SET);
|
||||
hs_pop = twl4030_read(codec, TWL4030_REG_HS_POPN_SET);
|
||||
delay = (ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
|
||||
twl4030->sysclk) + 1;
|
||||
|
||||
|
@ -831,9 +730,8 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
|
|||
hs_pop |= TWL4030_VMID_EN;
|
||||
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
|
||||
/* Actually write to the register */
|
||||
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
|
||||
hs_gain,
|
||||
TWL4030_REG_HS_GAIN_SET);
|
||||
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain,
|
||||
TWL4030_REG_HS_GAIN_SET);
|
||||
hs_pop |= TWL4030_RAMP_EN;
|
||||
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
|
||||
/* Wait ramp delay time + 1, so the VMID can settle */
|
||||
|
@ -846,9 +744,8 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
|
|||
/* Wait ramp delay time + 1, so the VMID can settle */
|
||||
twl4030_wait_ms(delay);
|
||||
/* Bypass the reg_cache to mute the headset */
|
||||
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
|
||||
hs_gain & (~0x0f),
|
||||
TWL4030_REG_HS_GAIN_SET);
|
||||
twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain & (~0x0f),
|
||||
TWL4030_REG_HS_GAIN_SET);
|
||||
|
||||
hs_pop &= ~TWL4030_VMID_EN;
|
||||
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
|
||||
|
@ -866,7 +763,7 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
|
|||
}
|
||||
|
||||
static int headsetlpga_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec);
|
||||
|
||||
|
@ -890,7 +787,7 @@ static int headsetlpga_event(struct snd_soc_dapm_widget *w,
|
|||
}
|
||||
|
||||
static int headsetrpga_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec);
|
||||
|
||||
|
@ -914,7 +811,7 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w,
|
|||
}
|
||||
|
||||
static int digimic_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec);
|
||||
struct twl4030_codec_data *pdata = twl4030->pdata;
|
||||
|
@ -935,7 +832,7 @@ static int digimic_event(struct snd_soc_dapm_widget *w,
|
|||
* Custom volsw and volsw_2r get/put functions to handle these gain bits.
|
||||
*/
|
||||
static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
|
@ -964,7 +861,7 @@ static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol,
|
|||
}
|
||||
|
||||
static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
|
@ -993,7 +890,7 @@ static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol,
|
|||
}
|
||||
|
||||
static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
|
@ -1020,7 +917,7 @@ static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
|
|||
}
|
||||
|
||||
static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
|
@ -1751,11 +1648,11 @@ static void twl4030_constraints(struct twl4030_priv *twl4030,
|
|||
/* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for
|
||||
* capture has to be enabled/disabled. */
|
||||
static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction,
|
||||
int enable)
|
||||
int enable)
|
||||
{
|
||||
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)
|
||||
mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN;
|
||||
|
@ -1784,14 +1681,14 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
|
|||
if (twl4030->configured)
|
||||
twl4030_constraints(twl4030, twl4030->master_substream);
|
||||
} else {
|
||||
if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) &
|
||||
if (!(twl4030_read(codec, TWL4030_REG_CODEC_MODE) &
|
||||
TWL4030_OPTION_1)) {
|
||||
/* In option2 4 channel is not supported, set the
|
||||
* constraint for the first stream for channels, the
|
||||
* second stream will 'inherit' this cosntraint */
|
||||
snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
2, 2);
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
2, 2);
|
||||
}
|
||||
twl4030->master_substream = substream;
|
||||
}
|
||||
|
@ -1823,8 +1720,8 @@ static void twl4030_shutdown(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
static int twl4030_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 twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||
|
@ -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 (params_channels(params) == 4) {
|
||||
format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
|
||||
mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
|
||||
format = twl4030_read(codec, TWL4030_REG_AUDIO_IF);
|
||||
mode = twl4030_read(codec, TWL4030_REG_CODEC_MODE);
|
||||
|
||||
/* Safety check: are we in the correct operating mode and
|
||||
* the interface is in TDM mode? */
|
||||
|
@ -1849,8 +1746,8 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
|
||||
/* bit rate */
|
||||
old_mode = twl4030_read_reg_cache(codec,
|
||||
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
|
||||
old_mode = twl4030_read(codec,
|
||||
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
|
||||
mode = old_mode & ~TWL4030_APLL_RATE;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
|
@ -1891,7 +1788,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
/* 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 &= ~TWL4030_DATA_WIDTH;
|
||||
switch (params_format(params)) {
|
||||
|
@ -1940,8 +1837,8 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
|
||||
unsigned int freq, int dir)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->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;
|
||||
}
|
||||
|
||||
static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
unsigned int fmt)
|
||||
static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||
u8 old_format, 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;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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)
|
||||
reg |= TWL4030_AIF_TRI_EN;
|
||||
|
@ -2037,11 +1933,11 @@ static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate)
|
|||
/* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R
|
||||
* (VTXL, VTXR) for uplink has to be enabled/disabled. */
|
||||
static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction,
|
||||
int enable)
|
||||
int enable)
|
||||
{
|
||||
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)
|
||||
mask = TWL4030_ARXL1_VRX_EN;
|
||||
|
@ -2057,7 +1953,7 @@ static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction,
|
|||
}
|
||||
|
||||
static int twl4030_voice_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||
|
@ -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
|
||||
* available.
|
||||
*/
|
||||
mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
|
||||
mode = twl4030_read(codec, TWL4030_REG_CODEC_MODE)
|
||||
& TWL4030_OPT_MODE;
|
||||
|
||||
if (mode != TWL4030_OPTION_2) {
|
||||
|
@ -2089,7 +1985,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
|
||||
|
@ -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,
|
||||
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 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);
|
||||
|
||||
/* bit rate */
|
||||
old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
|
||||
& ~(TWL4030_CODECPDZ);
|
||||
old_mode = twl4030_read(codec,
|
||||
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
|
||||
mode = old_mode;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
|
@ -2143,7 +2040,7 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||
|
@ -2164,14 +2061,14 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
|||
}
|
||||
|
||||
static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
unsigned int fmt)
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
|
||||
u8 old_format, 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;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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)
|
||||
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_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);
|
||||
|
||||
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 = {
|
||||
.probe = twl4030_soc_probe,
|
||||
.remove = twl4030_soc_remove,
|
||||
.read = twl4030_read_reg_cache,
|
||||
.read = twl4030_read,
|
||||
.write = twl4030_write,
|
||||
.set_bias_level = twl4030_set_bias_level,
|
||||
.idle_bias_off = true,
|
||||
.reg_cache_size = sizeof(twl4030_reg),
|
||||
.reg_word_size = sizeof(u8),
|
||||
.reg_cache_default = twl4030_reg,
|
||||
|
||||
.controls = twl4030_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(twl4030_snd_controls),
|
||||
|
@ -2342,7 +2234,7 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = {
|
|||
static int twl4030_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl4030,
|
||||
twl4030_dai, ARRAY_SIZE(twl4030_dai));
|
||||
twl4030_dai, ARRAY_SIZE(twl4030_dai));
|
||||
}
|
||||
|
||||
static int twl4030_codec_remove(struct platform_device *pdev)
|
||||
|
|
|
@ -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("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,
|
||||
ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
|
||||
SOC_DOUBLE_R("HPOUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
|
||||
|
|
|
@ -1497,6 +1497,126 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void wm_adsp2_boot_work(struct work_struct *work)
|
||||
{
|
||||
struct wm_adsp *dsp = container_of(work,
|
||||
struct wm_adsp,
|
||||
boot_work);
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
/*
|
||||
* For simplicity set the DSP clock rate to be the
|
||||
* SYSCLK rate rather than making it configurable.
|
||||
*/
|
||||
ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
|
||||
if (ret != 0) {
|
||||
adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
val = (val & ARIZONA_SYSCLK_FREQ_MASK)
|
||||
>> ARIZONA_SYSCLK_FREQ_SHIFT;
|
||||
|
||||
ret = regmap_update_bits_async(dsp->regmap,
|
||||
dsp->base + ADSP2_CLOCKING,
|
||||
ADSP2_CLK_SEL_MASK, val);
|
||||
if (ret != 0) {
|
||||
adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dsp->dvfs) {
|
||||
ret = regmap_read(dsp->regmap,
|
||||
dsp->base + ADSP2_CLOCKING, &val);
|
||||
if (ret != 0) {
|
||||
dev_err(dsp->dev, "Failed to read clocking: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
|
||||
ret = regulator_enable(dsp->dvfs);
|
||||
if (ret != 0) {
|
||||
dev_err(dsp->dev,
|
||||
"Failed to enable supply: %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = regulator_set_voltage(dsp->dvfs,
|
||||
1800000,
|
||||
1800000);
|
||||
if (ret != 0) {
|
||||
dev_err(dsp->dev,
|
||||
"Failed to raise supply: %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = wm_adsp2_ena(dsp);
|
||||
if (ret != 0)
|
||||
return;
|
||||
|
||||
ret = wm_adsp_load(dsp);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
ret = wm_adsp_setup_algs(dsp);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
ret = wm_adsp_load_coeff(dsp);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
/* Initialize caches for enabled and unset controls */
|
||||
ret = wm_coeff_init_control_caches(dsp);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
/* Sync set controls */
|
||||
ret = wm_coeff_sync_controls(dsp);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
ret = regmap_update_bits_async(dsp->regmap,
|
||||
dsp->base + ADSP2_CONTROL,
|
||||
ADSP2_CORE_ENA,
|
||||
ADSP2_CORE_ENA);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -1505,99 +1625,21 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
|||
struct wm_adsp *dsp = &dsps[w->shift];
|
||||
struct wm_adsp_alg_region *alg_region;
|
||||
struct wm_coeff_ctl *ctl;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
dsp->card = codec->card;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
/*
|
||||
* For simplicity set the DSP clock rate to be the
|
||||
* SYSCLK rate rather than making it configurable.
|
||||
*/
|
||||
ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
|
||||
if (ret != 0) {
|
||||
adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
val = (val & ARIZONA_SYSCLK_FREQ_MASK)
|
||||
>> ARIZONA_SYSCLK_FREQ_SHIFT;
|
||||
flush_work(&dsp->boot_work);
|
||||
|
||||
ret = regmap_update_bits_async(dsp->regmap,
|
||||
dsp->base + ADSP2_CLOCKING,
|
||||
ADSP2_CLK_SEL_MASK, val);
|
||||
if (ret != 0) {
|
||||
adsp_err(dsp, "Failed to set clock rate: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
if (!dsp->running)
|
||||
return -EIO;
|
||||
|
||||
if (dsp->dvfs) {
|
||||
ret = regmap_read(dsp->regmap,
|
||||
dsp->base + ADSP2_CLOCKING, &val);
|
||||
if (ret != 0) {
|
||||
dev_err(dsp->dev,
|
||||
"Failed to read clocking: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
|
||||
ret = regulator_enable(dsp->dvfs);
|
||||
if (ret != 0) {
|
||||
dev_err(dsp->dev,
|
||||
"Failed to enable supply: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_set_voltage(dsp->dvfs,
|
||||
1800000,
|
||||
1800000);
|
||||
if (ret != 0) {
|
||||
dev_err(dsp->dev,
|
||||
"Failed to raise supply: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = wm_adsp2_ena(dsp);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = wm_adsp_load(dsp);
|
||||
ret = regmap_update_bits(dsp->regmap,
|
||||
dsp->base + ADSP2_CONTROL,
|
||||
ADSP2_START,
|
||||
ADSP2_START);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
ret = wm_adsp_setup_algs(dsp);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
ret = wm_adsp_load_coeff(dsp);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
/* Initialize caches for enabled and unset controls */
|
||||
ret = wm_coeff_init_control_caches(dsp);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
/* Sync set controls */
|
||||
ret = wm_coeff_sync_controls(dsp);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
ret = regmap_update_bits_async(dsp->regmap,
|
||||
dsp->base + ADSP2_CONTROL,
|
||||
ADSP2_CORE_ENA | ADSP2_START,
|
||||
ADSP2_CORE_ENA | ADSP2_START);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
dsp->running = true;
|
||||
break;
|
||||
|
||||
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->ctl_list);
|
||||
INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work);
|
||||
|
||||
if (dvfs) {
|
||||
adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
|
||||
|
|
|
@ -59,6 +59,8 @@ struct wm_adsp {
|
|||
struct regulator *dvfs;
|
||||
|
||||
struct list_head ctl_list;
|
||||
|
||||
struct work_struct boot_work;
|
||||
};
|
||||
|
||||
#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)
|
||||
|
||||
#define WM_ADSP2(wname, num) \
|
||||
SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
|
||||
wm_adsp2_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
|
||||
{ .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \
|
||||
.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_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_adsp1_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ config SND_SOC_FSL_SSI
|
|||
config SND_SOC_FSL_SPDIF
|
||||
tristate
|
||||
|
||||
config SND_SOC_FSL_ESAI
|
||||
tristate
|
||||
|
||||
config SND_SOC_FSL_UTILS
|
||||
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-ssi-objs := fsl_ssi.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-dma-objs := fsl_dma.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_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_POWERPC_DMA) += snd-soc-fsl-dma.o
|
||||
|
||||
|
|
|
@ -55,10 +55,6 @@
|
|||
SNDRV_PCM_FMTBIT_S32_BE | \
|
||||
SNDRV_PCM_FMTBIT_U32_LE | \
|
||||
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 snd_soc_platform_driver dai;
|
||||
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_PAUSE,
|
||||
.formats = FSLDMA_PCM_FORMATS,
|
||||
.rates = FSLDMA_PCM_RATES,
|
||||
.rate_min = 5512,
|
||||
.rate_max = 192000,
|
||||
.period_bytes_min = 512, /* A reasonable limit */
|
||||
.period_bytes_max = (u32) -1,
|
||||
.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;
|
||||
|
||||
val_cr2 = sai_readl(sai, sai->base + reg_cr2);
|
||||
val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
|
||||
|
||||
switch (clk_id) {
|
||||
case FSL_SAI_CLK_BUS:
|
||||
val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
|
||||
val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
|
||||
break;
|
||||
case FSL_SAI_CLK_MAST1:
|
||||
val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
|
||||
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
|
||||
break;
|
||||
case FSL_SAI_CLK_MAST2:
|
||||
val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
|
||||
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
|
||||
break;
|
||||
case FSL_SAI_CLK_MAST3:
|
||||
val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
|
||||
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sai_writel(sai, val_cr2, sai->base + reg_cr2);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.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
|
||||
* are really supported.
|
||||
*/
|
||||
#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
|
||||
SNDRV_PCM_RATE_CONTINUOUS)
|
||||
#define FSLSSI_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
|
||||
|
||||
/**
|
||||
* 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)
|
||||
#endif
|
||||
|
||||
/* SIER bitflag of interrupts to enable */
|
||||
#define SIER_FLAGS (CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | \
|
||||
CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | \
|
||||
CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | \
|
||||
CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \
|
||||
CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN)
|
||||
#define FSLSSI_SIER_DBG_RX_FLAGS (CCSR_SSI_SIER_RFF0_EN | \
|
||||
CCSR_SSI_SIER_RLS_EN | CCSR_SSI_SIER_RFS_EN | \
|
||||
CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_RFRC_EN)
|
||||
#define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \
|
||||
CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_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
|
||||
|
@ -133,14 +154,16 @@ struct fsl_ssi_private {
|
|||
unsigned int irq;
|
||||
unsigned int fifo_depth;
|
||||
struct snd_soc_dai_driver cpu_dai_drv;
|
||||
struct device_attribute dev_attr;
|
||||
struct platform_device *pdev;
|
||||
|
||||
enum fsl_ssi_type hw_type;
|
||||
bool new_binding;
|
||||
bool ssi_on_imx;
|
||||
bool imx_ac97;
|
||||
bool use_dma;
|
||||
bool baudclk_locked;
|
||||
bool irq_stats;
|
||||
bool offline_config;
|
||||
u8 i2s_mode;
|
||||
spinlock_t baudclk_lock;
|
||||
struct clk *baudclk;
|
||||
|
@ -150,6 +173,8 @@ struct fsl_ssi_private {
|
|||
struct imx_dma_data filter_data_tx;
|
||||
struct imx_dma_data filter_data_rx;
|
||||
struct imx_pcm_fiq_params fiq_params;
|
||||
/* Register values for rx/tx configuration */
|
||||
struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
|
||||
|
||||
struct {
|
||||
unsigned int rfrc;
|
||||
|
@ -174,10 +199,21 @@ struct fsl_ssi_private {
|
|||
unsigned int tfe1;
|
||||
unsigned int tfe0;
|
||||
} stats;
|
||||
struct dentry *dbg_dir;
|
||||
struct dentry *dbg_stats;
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -196,23 +232,40 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
|||
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
__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
|
||||
were interrupted for. We mask it with the Interrupt Enable register
|
||||
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) {
|
||||
ssi_private->stats.rfrc++;
|
||||
sisr2 |= CCSR_SSI_SISR_RFRC;
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TFRC) {
|
||||
ssi_private->stats.tfrc++;
|
||||
sisr2 |= CCSR_SSI_SISR_TFRC;
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -253,25 +306,21 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
|||
|
||||
if (sisr & CCSR_SSI_SISR_ROE1) {
|
||||
ssi_private->stats.roe1++;
|
||||
sisr2 |= CCSR_SSI_SISR_ROE1;
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_ROE0) {
|
||||
ssi_private->stats.roe0++;
|
||||
sisr2 |= CCSR_SSI_SISR_ROE0;
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TUE1) {
|
||||
ssi_private->stats.tue1++;
|
||||
sisr2 |= CCSR_SSI_SISR_TUE1;
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (sisr & CCSR_SSI_SISR_TUE0) {
|
||||
ssi_private->stats.tue0++;
|
||||
sisr2 |= CCSR_SSI_SISR_TUE0;
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -315,6 +364,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
|||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
sisr2 = sisr & sisr_write_mask;
|
||||
/* Clear the bits that we set */
|
||||
if (sisr2)
|
||||
write_ssi(sisr2, &ssi->sisr);
|
||||
|
@ -322,6 +372,245 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
|
|||
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)
|
||||
{
|
||||
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;
|
||||
int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
|
||||
|
||||
fsl_ssi_setup_reg_vals(ssi_private);
|
||||
|
||||
if (ssi_private->imx_ac97)
|
||||
ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
|
||||
else
|
||||
|
@ -381,13 +672,12 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
|
|||
ssi_private->i2s_mode |
|
||||
(synchronous ? CCSR_SSI_SCR_SYN : 0));
|
||||
|
||||
write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
|
||||
CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
|
||||
CCSR_SSI_STCR_TSCKP, &ssi->stcr);
|
||||
write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFSI |
|
||||
CCSR_SSI_STCR_TEFS | 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.
|
||||
*/
|
||||
|
@ -420,6 +710,17 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
|
|||
if (ssi_private->imx_ac97)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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 fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
||||
unsigned int sier_bits;
|
||||
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) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
write_ssi_mask(&ssi->scr, 0,
|
||||
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
|
||||
fsl_ssi_tx_config(ssi_private, true);
|
||||
else
|
||||
write_ssi_mask(&ssi->scr, 0,
|
||||
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
|
||||
fsl_ssi_rx_config(ssi_private, true);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
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
|
||||
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) &
|
||||
(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);
|
||||
ssi_private->baudclk_locked = false;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -864,58 +1146,6 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
|
|||
.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 = {
|
||||
.ac97_control = 1,
|
||||
.playback = {
|
||||
|
@ -932,7 +1162,7 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
|
|||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.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,
|
||||
};
|
||||
|
||||
/* 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
|
||||
*/
|
||||
|
@ -1061,6 +1241,8 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|||
int ret = 0;
|
||||
struct device_attribute *dev_attr = NULL;
|
||||
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 uint32_t *iprop;
|
||||
struct resource res;
|
||||
|
@ -1075,6 +1257,11 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|||
if (!of_device_is_available(np))
|
||||
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 */
|
||||
sprop = of_get_property(np, "fsl,mode", NULL);
|
||||
if (!sprop) {
|
||||
|
@ -1101,6 +1288,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|||
|
||||
ssi_private->use_dma = !of_property_read_bool(np,
|
||||
"fsl,fiq-stream-filter");
|
||||
ssi_private->hw_type = hw_type;
|
||||
|
||||
if (ac97) {
|
||||
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;
|
||||
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];
|
||||
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");
|
||||
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
|
||||
clk_prepare_enable(ssi_private->baudclk);
|
||||
|
||||
|
@ -1218,32 +1434,25 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|||
dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
|
||||
imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx,
|
||||
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. */
|
||||
ret = devm_request_irq(&pdev->dev, ssi_private->irq,
|
||||
fsl_ssi_isr, 0, ssi_private->name,
|
||||
ssi_private);
|
||||
ssi_private->irq_stats = true;
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "could not claim irq %u\n",
|
||||
ssi_private->irq);
|
||||
goto error_irqmap;
|
||||
goto error_clk;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Register with ASoC */
|
||||
dev_set_drvdata(&pdev->dev, ssi_private);
|
||||
|
||||
|
@ -1254,6 +1463,10 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|||
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->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);
|
||||
if (ret)
|
||||
goto error_dev;
|
||||
goto error_pcm;
|
||||
} else {
|
||||
ret = imx_pcm_dma_init(pdev);
|
||||
if (ret)
|
||||
goto error_dev;
|
||||
goto error_pcm;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1319,6 +1532,13 @@ done:
|
|||
return 0;
|
||||
|
||||
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);
|
||||
|
||||
error_dev:
|
||||
|
@ -1332,7 +1552,8 @@ error_clk:
|
|||
}
|
||||
|
||||
error_irqmap:
|
||||
irq_dispose_mapping(ssi_private->irq);
|
||||
if (ssi_private->irq_stats)
|
||||
irq_dispose_mapping(ssi_private->irq);
|
||||
|
||||
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);
|
||||
|
||||
fsl_ssi_debugfs_remove(ssi_private);
|
||||
|
||||
if (!ssi_private->new_binding)
|
||||
platform_device_unregister(ssi_private->pdev);
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
device_remove_file(&pdev->dev, &ssi_private->dev_attr);
|
||||
if (ssi_private->ssi_on_imx) {
|
||||
if (!IS_ERR(ssi_private->baudclk))
|
||||
clk_disable_unprepare(ssi_private->baudclk);
|
||||
clk_disable_unprepare(ssi_private->clk);
|
||||
}
|
||||
irq_dispose_mapping(ssi_private->irq);
|
||||
if (ssi_private->irq_stats)
|
||||
irq_dispose_mapping(ssi_private->irq);
|
||||
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = "fsl-ssi-dai",
|
||||
|
|
|
@ -41,9 +41,6 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
|
|||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
|
||||
.period_bytes_min = 128,
|
||||
.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_RESUME,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rate_min = 8000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
|
||||
.period_bytes_min = 128,
|
||||
.period_bytes_max = 16 * 1024,
|
||||
|
|
|
@ -200,10 +200,6 @@ static const struct snd_pcm_hardware psc_dma_hardware = {
|
|||
SNDRV_PCM_INFO_BATCH,
|
||||
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_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_min = 32,
|
||||
.periods_min = 2,
|
||||
|
|
|
@ -26,8 +26,7 @@
|
|||
* ALSA that we support all rates and let the codec driver decide what rates
|
||||
* are really supported.
|
||||
*/
|
||||
#define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
|
||||
SNDRV_PCM_RATE_CONTINUOUS)
|
||||
#define PSC_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
|
||||
|
||||
/**
|
||||
* PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
|
||||
|
|
|
@ -9,14 +9,12 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.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,
|
||||
struct asoc_simple_dai *set,
|
||||
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)
|
||||
{
|
||||
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 *cpu = rtd->cpu_dai;
|
||||
unsigned int daifmt = info->daifmt;
|
||||
|
@ -106,12 +105,8 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
|
|||
&dai->sysclk);
|
||||
} else {
|
||||
clk = of_clk_get(*node, 0);
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
dai->sysclk = clk_get_rate(clk);
|
||||
if (!IS_ERR(clk))
|
||||
dai->sysclk = clk_get_rate(clk);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
@ -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);
|
||||
|
||||
/* DAPM routes */
|
||||
ret = snd_soc_of_parse_audio_routing(&info->snd_card,
|
||||
"simple-audio-routing");
|
||||
if (ret)
|
||||
return ret;
|
||||
if (of_property_read_bool(node, "simple-audio-card,routing")) {
|
||||
ret = snd_soc_of_parse_audio_routing(&info->snd_card,
|
||||
"simple-audio-card,routing");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* CPU sub-node */
|
||||
ret = -EINVAL;
|
||||
|
@ -197,34 +194,37 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
|||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *of_cpu, *of_codec, *of_platform;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
cinfo = NULL;
|
||||
of_cpu = NULL;
|
||||
of_codec = NULL;
|
||||
of_platform = NULL;
|
||||
|
||||
cinfo = devm_kzalloc(dev, sizeof(*cinfo), GFP_KERNEL);
|
||||
if (!cinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
if (np && of_device_is_available(np)) {
|
||||
cinfo = devm_kzalloc(dev, sizeof(*cinfo), GFP_KERNEL);
|
||||
if (cinfo) {
|
||||
int ret;
|
||||
cinfo->snd_card.dev = &pdev->dev;
|
||||
ret = asoc_simple_card_parse_of(np, cinfo, dev,
|
||||
&of_cpu,
|
||||
&of_codec,
|
||||
&of_platform);
|
||||
if (ret < 0) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "parse error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
cinfo->snd_card.dev = dev;
|
||||
|
||||
ret = asoc_simple_card_parse_of(np, cinfo, dev,
|
||||
&of_cpu,
|
||||
&of_codec,
|
||||
&of_platform);
|
||||
if (ret < 0) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "parse error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
cinfo->snd_card.dev = &pdev->dev;
|
||||
cinfo = pdev->dev.platform_data;
|
||||
}
|
||||
if (!dev->platform_data) {
|
||||
dev_err(dev, "no info for asoc-simple-card\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!cinfo) {
|
||||
dev_err(dev, "no info for asoc-simple-card\n");
|
||||
return -EINVAL;
|
||||
memcpy(cinfo, dev->platform_data, sizeof(*cinfo));
|
||||
cinfo->snd_card.dev = dev;
|
||||
}
|
||||
|
||||
if (!cinfo->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.num_links = 1;
|
||||
|
||||
snd_soc_card_set_drvdata(&cinfo->snd_card, cinfo);
|
||||
|
||||
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_BLOCK_TRANSFER |
|
||||
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,
|
||||
.period_bytes_min = SST_MIN_PERIOD_BYTES,
|
||||
.period_bytes_max = SST_MAX_PERIOD_BYTES,
|
||||
|
|
|
@ -33,10 +33,6 @@
|
|||
#define SST_STEREO 2
|
||||
#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_MIN_BUFFER (800*1024)
|
||||
#define SST_MIN_PERIOD_BYTES 32
|
||||
|
|
|
@ -21,16 +21,6 @@
|
|||
#include <sound/soc.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)
|
||||
{
|
||||
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_BLOCK_TRANSFER |
|
||||
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,
|
||||
.period_bytes_min = KIRKWOOD_SND_MIN_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_INTERLEAVED |
|
||||
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_max = 8192,
|
||||
.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)
|
||||
{
|
||||
return devm_snd_dmaengine_pcm_register(dev, &mxs_dmaengine_pcm_config,
|
||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
|
||||
SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX);
|
||||
}
|
||||
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_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 4*1024,
|
||||
.period_bytes_min = 1*1024,
|
||||
.period_bytes_max = 4*1024,
|
||||
|
|
|
@ -405,8 +405,7 @@ static int s6000_i2s_dai_probe(struct snd_soc_dai *dai)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define S6000_I2S_RATES (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
|
||||
SNDRV_PCM_RATE_8000_192000)
|
||||
#define S6000_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
|
||||
#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 = {
|
||||
|
|
|
@ -68,7 +68,6 @@ int samsung_asoc_dma_platform_register(struct device *dev)
|
|||
{
|
||||
return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config,
|
||||
SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
|
||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
|
||||
SND_DMAENGINE_PCM_FLAG_COMPAT);
|
||||
}
|
||||
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_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 = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
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,
|
||||
.period_bytes_min = DMABRG_PERIOD_MIN,
|
||||
.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_VALID |
|
||||
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,
|
||||
.period_bytes_min = 32,
|
||||
.period_bytes_max = 8192,
|
||||
|
|
|
@ -628,12 +628,6 @@ static struct snd_pcm_hardware rsnd_pcm_hardware = {
|
|||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
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,
|
||||
.period_bytes_min = 32,
|
||||
.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_connect_dai_link_widgets(card);
|
||||
|
||||
if (card->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,
|
||||
freq, dir);
|
||||
else
|
||||
return -EINVAL;
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
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,
|
||||
freq, dir);
|
||||
else
|
||||
return -EINVAL;
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
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)
|
||||
return snd_soc_read(w->codec, reg);
|
||||
else if (w->platform)
|
||||
return snd_soc_platform_read(w->platform, reg);
|
||||
if (w->codec) {
|
||||
*value = snd_soc_read(w->codec, reg);
|
||||
return 0;
|
||||
} 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");
|
||||
return -1;
|
||||
|
@ -430,13 +434,12 @@ static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w,
|
|||
return ret;
|
||||
} else {
|
||||
soc_widget_lock(w);
|
||||
ret = soc_widget_read(w, reg);
|
||||
ret = soc_widget_read(w, reg, &old);
|
||||
if (ret < 0) {
|
||||
soc_widget_unlock(w);
|
||||
return ret;
|
||||
}
|
||||
|
||||
old = ret;
|
||||
new = (old & ~mask) | (value & mask);
|
||||
change = old != new;
|
||||
if (change) {
|
||||
|
@ -513,7 +516,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
|
|||
unsigned int invert = mc->invert;
|
||||
|
||||
if (reg != SND_SOC_NOPM) {
|
||||
val = soc_widget_read(w, reg);
|
||||
soc_widget_read(w, reg, &val);
|
||||
val = (val >> shift) & mask;
|
||||
if (invert)
|
||||
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;
|
||||
int val, item;
|
||||
|
||||
val = soc_widget_read(w, e->reg);
|
||||
soc_widget_read(w, e->reg, &val);
|
||||
item = (val >> e->shift_l) & e->mask;
|
||||
|
||||
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;
|
||||
int val, item;
|
||||
|
||||
val = soc_widget_read(w, e->reg);
|
||||
soc_widget_read(w, e->reg, &val);
|
||||
val = (val >> e->shift_l) & e->mask;
|
||||
for (item = 0; item < e->max; 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 */
|
||||
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;
|
||||
if (val == w->on_val)
|
||||
w->power = 1;
|
||||
|
@ -3634,6 +3638,55 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
|
|||
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,
|
||||
int event)
|
||||
{
|
||||
|
|
|
@ -144,6 +144,8 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
|
|||
if (ret == 0) {
|
||||
if (dma_caps.cmd_pause)
|
||||
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);
|
||||
|
@ -187,6 +189,21 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
|
|||
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)
|
||||
{
|
||||
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);
|
||||
if (ret)
|
||||
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;
|
||||
|
@ -248,6 +275,18 @@ err_free:
|
|||
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 = {
|
||||
.open = dmaengine_pcm_open,
|
||||
.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_free = snd_pcm_lib_free_pages,
|
||||
.trigger = snd_dmaengine_pcm_trigger,
|
||||
.pointer = snd_dmaengine_pcm_pointer,
|
||||
.pointer = dmaengine_pcm_pointer,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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[] = {
|
||||
[SNDRV_PCM_STREAM_PLAYBACK] = "tx",
|
||||
[SNDRV_PCM_STREAM_CAPTURE] = "rx",
|
||||
|
@ -374,12 +396,8 @@ int snd_dmaengine_pcm_register(struct device *dev,
|
|||
if (ret)
|
||||
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,
|
||||
&dmaengine_pcm_platform);
|
||||
ret = snd_soc_add_platform(dev, &pcm->platform,
|
||||
&dmaengine_pcm_platform);
|
||||
if (ret)
|
||||
goto err_free_dma;
|
||||
|
||||
|
|
|
@ -240,14 +240,15 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
|
|||
cpu_stream->channels_min);
|
||||
hw->channels_max = min(codec_stream->channels_max,
|
||||
cpu_stream->channels_max);
|
||||
hw->formats = codec_stream->formats & cpu_stream->formats;
|
||||
hw->rates = codec_stream->rates & cpu_stream->rates;
|
||||
if (codec_stream->rates
|
||||
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
|
||||
hw->rates |= cpu_stream->rates;
|
||||
if (cpu_stream->rates
|
||||
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
|
||||
hw->rates |= codec_stream->rates;
|
||||
if (hw->formats)
|
||||
hw->formats &= codec_stream->formats & cpu_stream->formats;
|
||||
else
|
||||
hw->formats = codec_stream->formats & cpu_stream->formats;
|
||||
hw->rates = snd_pcm_rate_mask_intersect(codec_stream->rates,
|
||||
cpu_stream->rates);
|
||||
|
||||
hw->rate_min = 0;
|
||||
hw->rate_max = UINT_MAX;
|
||||
|
||||
snd_pcm_limit_hw_rates(runtime);
|
||||
|
||||
|
@ -776,7 +777,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (platform->driver->ops && platform->driver->bespoke_trigger) {
|
||||
if (platform->driver->bespoke_trigger) {
|
||||
ret = platform->driver->bespoke_trigger(substream, cmd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -1235,6 +1236,20 @@ unwind:
|
|||
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)
|
||||
{
|
||||
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_driver *cpu_dai_drv = cpu_dai->driver;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
runtime->hw.rate_min = cpu_dai_drv->playback.rate_min;
|
||||
runtime->hw.rate_max = cpu_dai_drv->playback.rate_max;
|
||||
runtime->hw.channels_min = cpu_dai_drv->playback.channels_min;
|
||||
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;
|
||||
}
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback);
|
||||
else
|
||||
dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
|
||||
if (cpu_dai->driver->playback.channels_min)
|
||||
playback = 1;
|
||||
if (cpu_dai->driver->capture.channels_min)
|
||||
capture = 1;
|
||||
playback = rtd->dai_link->dpcm_playback;
|
||||
capture = rtd->dai_link->dpcm_capture;
|
||||
} else {
|
||||
if (codec_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)
|
||||
{
|
||||
int ret;
|
||||
|
|
|
@ -91,6 +91,8 @@ static int mop500_of_probe(struct platform_device *pdev,
|
|||
for (i = 0; i < 2; i++) {
|
||||
mop500_dai_links[i].cpu_of_node = msp_np[i];
|
||||
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_name = NULL;
|
||||
}
|
||||
|
|
|
@ -17,12 +17,14 @@
|
|||
#include <linux/bitops.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
#include <linux/platform_data/asoc-ux500-msp.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#include "ux500_msp_i2s.h"
|
||||
#include "ux500_msp_dai.h"
|
||||
|
@ -654,16 +656,52 @@ static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream,
|
|||
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)
|
||||
{
|
||||
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;
|
||||
dai->capture_dma_data = &drvdata->msp->capture_dma_data;
|
||||
if (!pdata) {
|
||||
ret = ux500_msp_dai_of_probe(dai);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drvdata->msp->playback_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;
|
||||
}
|
||||
|
||||
|
@ -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] = {
|
||||
{
|
||||
.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,
|
||||
.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.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 struct snd_soc_dai_driver ux500_msp_dai_drv = {
|
||||
.probe = ux500_msp_dai_probe,
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.playback.channels_min = UX500_MSP_MIN_CHANNELS,
|
||||
.playback.channels_max = UX500_MSP_MAX_CHANNELS,
|
||||
.playback.rates = UX500_I2S_RATES,
|
||||
.playback.formats = UX500_I2S_FORMATS,
|
||||
.capture.channels_min = UX500_MSP_MIN_CHANNELS,
|
||||
.capture.channels_max = UX500_MSP_MAX_CHANNELS,
|
||||
.capture.rates = UX500_I2S_RATES,
|
||||
.capture.formats = UX500_I2S_FORMATS,
|
||||
.ops = ux500_msp_dai_ops,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
dev_dbg(&pdev->dev, "%s: Enter (pdev->name = %s).\n", __func__,
|
||||
pdev->name);
|
||||
if (!pdata && !np) {
|
||||
dev_err(&pdev->dev, "No platform data or Device Tree found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
drvdata = devm_kzalloc(&pdev->dev,
|
||||
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);
|
||||
|
||||
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) {
|
||||
dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n",
|
||||
__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,
|
||||
struct ux500_msp **msp_p,
|
||||
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 device_node *np = pdev->dev.of_node;
|
||||
struct ux500_msp *msp;
|
||||
int ret;
|
||||
|
||||
*msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
|
||||
msp = *msp_p;
|
||||
if (!msp)
|
||||
return -ENOMEM;
|
||||
|
||||
if (np) {
|
||||
if (!platform_data) {
|
||||
platform_data = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct msp_i2s_platform_data), GFP_KERNEL);
|
||||
if (!platform_data)
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else
|
||||
if (!platform_data)
|
||||
if (!platform_data) {
|
||||
if (np) {
|
||||
ret = ux500_msp_i2s_of_init_msp(pdev, msp,
|
||||
&platform_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else
|
||||
return -EINVAL;
|
||||
} else {
|
||||
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->id = platform_data->id;
|
||||
}
|
||||
|
||||
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->capture_dma_data.dma_cfg = platform_data->msp_i2s_dma_rx;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
|
|
|
@ -475,7 +475,7 @@ struct ux500_msp_dma_params {
|
|||
};
|
||||
|
||||
struct ux500_msp {
|
||||
enum msp_i2s_id id;
|
||||
int id;
|
||||
void __iomem *registers;
|
||||
struct device *dev;
|
||||
struct ux500_msp_dma_params playback_dma_data;
|
||||
|
|
|
@ -28,12 +28,6 @@
|
|||
#include "ux500_msp_i2s.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_MAX (64 * PAGE_SIZE)
|
||||
#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_RESUME |
|
||||
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,
|
||||
.period_bytes_min = UX500_PLATFORM_PERIODS_BYTES_MIN,
|
||||
.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_soc_dai *dai = rtd->cpu_dai;
|
||||
struct device *dev = dai->dev;
|
||||
u16 per_data_width, mem_data_width;
|
||||
struct stedma40_chan_cfg *dma_cfg;
|
||||
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_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 snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct ux500_msp_dma_params *dma_params;
|
||||
struct stedma40_chan_cfg *dma_cfg;
|
||||
struct msp_i2s_platform_data *pdata = rtd->cpu_dai->dev->platform_data;
|
||||
struct snd_dmaengine_dai_dma_data *snd_dma_params;
|
||||
struct ux500_msp_dma_params *ste_dma_params;
|
||||
dma_addr_t dma_addr;
|
||||
int ret;
|
||||
|
||||
dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
dma_cfg = dma_params->dma_cfg;
|
||||
if (pdata) {
|
||||
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);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
slave_config->dst_maxburst = 4;
|
||||
slave_config->dst_addr_width = dma_cfg->dst_info.data_width;
|
||||
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)
|
||||
slave_config->dst_addr = dma_params->tx_rx_addr;
|
||||
slave_config->dst_addr = dma_addr;
|
||||
else
|
||||
slave_config->src_addr = dma_params->tx_rx_addr;
|
||||
slave_config->src_addr = dma_addr;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
const struct snd_dmaengine_pcm_config *pcm_config;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int ret;
|
||||
|
||||
ret = snd_dmaengine_pcm_register(&pdev->dev,
|
||||
&ux500_dmaengine_pcm_config,
|
||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
|
||||
SND_DMAENGINE_PCM_FLAG_COMPAT |
|
||||
SND_DMAENGINE_PCM_FLAG_NO_DT);
|
||||
if (np)
|
||||
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_COMPAT);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: ERROR: Failed to register platform '%s' (%d)!\n",
|
||||
|
|
Loading…
Reference in New Issue