summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorDan Murphy <DMurphy@ti.com>2014-11-10 06:33:37 -0600
committerDan Murphy <DMurphy@ti.com>2014-11-10 06:33:37 -0600
commitfaf72fe2799d05dda704cdf313cba0255714f7a1 (patch)
treec84cb86bd52e41e1a475a7304d6363290053fc66 /sound
parentb46b72e9e879961687300668cdfeeb003188bc7e (diff)
parent2f9e453fe0f1b3cbd57276344a8cd800ce4c7c45 (diff)
Merge branch 'audio-display-ti-linux-3.14.y' of git://git.ti.com/~darrene/ti-linux-kernel/audio-display-linux-feature-tree into ti-linux-3.14.y
TI-Feature: audio-display TI-Tree: git://git.ti.com/~darrene/ti-linux-kernel/audio-display-linux-feature-tree.git TI-Branch: audio-display-ti-linux-3.14.y * 'audio-display-ti-linux-3.14.y' of git://git.ti.com/~darrene/ti-linux-kernel/audio-display-linux-feature-tree: (26 commits) ti_fragments: audio_display: Make DRA7-EVM audio a builtin ASoC: davinci-mcasp: Add overrun/underrun event handling ASoC: davinci-mcasp: Fix rx format when more bclk is used on the bus ASoC: davinci-mcasp: Symmetric sample bits for IIS mode ASoC: davinci-mcasp: Active slots depend on active serializers ASoC: davinci-mcasp: Place constraint on number of channels ASoC: davinci-mcasp: Validate tdm_slots parameter at probe time ASoC: tlv320aic3x: Add TDM support ASoC: tlv320aic3x: Add output driver pop reduction controls ARM: DTS: dra72-evm: Correct the clock name used by the sound node ARM: DTS: dra72-evm: Complete the audio routes ARM: DTS: dra7-evm: Correct the clock name used by the sound node ARM: DTS: dra7-evm: Complete the audio routes ARM: DTS: dra72-evm: Fix McASP3 num of serializers ARM: DTS: dra7-evm: Fix McASP3 num of serializers ASoC: davinci-mcasp: Move the AFIFO related code under start_tx/rx functions ASoC: davinci-mcasp: When stopping TX/RX stop the AFIFO as the last step ASoC: davinci-mcasp: Correct RX start sequence ASoC: davinci-mcasp: Correct TX start sequence ti_fragments: audio_display: Add more audio options to be enabled ... Signed-off-by: Dan Murphy <DMurphy@ti.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/Kconfig105
-rw-r--r--sound/soc/codecs/tlv320aic3x.c78
-rw-r--r--sound/soc/codecs/tlv320aic3x.h1
-rw-r--r--sound/soc/davinci/Kconfig8
-rw-r--r--sound/soc/davinci/davinci-mcasp.c300
-rw-r--r--sound/soc/davinci/davinci-mcasp.h16
6 files changed, 405 insertions, 103 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 66f6c53ea32..537084c790a 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -8,6 +8,8 @@ config SND_SOC_I2C_AND_SPI
default y if I2C=y
default y if SPI_MASTER=y
+menu "CODEC drivers"
+
config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
depends on COMPILE_TEST
@@ -190,8 +192,9 @@ config SND_SOC_AD73311
tristate
config SND_SOC_ADAU1701
+ tristate "Analog Devices ADAU1701 CODEC"
+ depends on I2C
select SND_SOC_SIGMADSP
- tristate
config SND_SOC_ADAU1373
tristate
@@ -203,28 +206,31 @@ config SND_SOC_ADS117X
tristate
config SND_SOC_AK4104
- tristate
+ tristate "AKM AK4104 CODEC"
+ depends on SPI_MASTER
config SND_SOC_AK4535
tristate
config SND_SOC_AK4554
- tristate
+ tristate "AKM AK4554 CODEC"
config SND_SOC_AK4641
tristate
config SND_SOC_AK4642
- tristate
+ tristate "AKM AK4642 CODEC"
+ depends on I2C
config SND_SOC_AK4671
tristate
config SND_SOC_AK5386
- tristate
+ tristate "AKM AK5638 CODEC"
config SND_SOC_ALC5623
tristate
+
config SND_SOC_ALC5632
tristate
@@ -235,14 +241,17 @@ config SND_SOC_CS42L51
tristate
config SND_SOC_CS42L52
- tristate
+ tristate "Cirrus Logic CS42L52 CODEC"
+ depends on I2C
config SND_SOC_CS42L73
- tristate
+ tristate "Cirrus Logic CS42L73 CODEC"
+ depends on I2C
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
- tristate
+ tristate "Cirrus Logic CS4270 CODEC"
+ depends on I2C
# Cirrus Logic CS4270 Codec VD = 3.3V Errata
# Select if you are affected by the errata where the part will not function
@@ -253,7 +262,8 @@ config SND_SOC_CS4270_VD33_ERRATA
depends on SND_SOC_CS4270
config SND_SOC_CS4271
- tristate
+ tristate "Cirrus Logic CS4271 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_CX20442
tristate
@@ -284,6 +294,9 @@ config SND_SOC_BT_SCO
config SND_SOC_DMIC
tristate
+config SND_SOC_HDMI_CODEC
+ tristate "HDMI stub CODEC"
+
config SND_SOC_ISABELLE
tristate
@@ -302,14 +315,13 @@ config SND_SOC_MAX98095
config SND_SOC_MAX9850
tristate
-config SND_SOC_HDMI_CODEC
- tristate
-
config SND_SOC_PCM1681
- tristate
+ tristate "Texas Instruments PCM1681 CODEC"
+ depends on I2C
config SND_SOC_PCM1792A
- tristate
+ tristate "Texas Instruments PCM1792A CODEC"
+ depends on SPI_MASTER
config SND_SOC_PCM3008
tristate
@@ -322,7 +334,8 @@ config SND_SOC_RT5640
#Freescale sgtl5000 codec
config SND_SOC_SGTL5000
- tristate
+ tristate "Freescale SGTL5000 CODEC"
+ depends on I2C
config SND_SOC_SI476X
tristate
@@ -335,7 +348,7 @@ config SND_SOC_SN95031
tristate
config SND_SOC_SPDIF
- tristate
+ tristate "S/PDIF CODEC"
config SND_SOC_SSM2518
tristate
@@ -353,7 +366,8 @@ config SND_SOC_STAC9766
tristate
config SND_SOC_TAS5086
- tristate
+ tristate "Texas Instruments TAS5086 speaker amplifier"
+ depends on I2C
config SND_SOC_TLV320AIC23
tristate
@@ -363,13 +377,16 @@ config SND_SOC_TLV320AIC26
depends on SPI
config SND_SOC_TLV320AIC31XX
- tristate
+ tristate "Texas Instruments TLV320AIC31xx CODECs"
+ depends on I2C
+ select REGMAP_I2C
config SND_SOC_TLV320AIC32X4
tristate
config SND_SOC_TLV320AIC3X
- tristate
+ tristate "Texas Instruments TLV320AIC3x CODECs"
+ depends on I2C
config SND_SOC_TLV320DAC33
tristate
@@ -418,55 +435,69 @@ config SND_SOC_WM8400
tristate
config SND_SOC_WM8510
- tristate
+ tristate "Wolfson Microelectronics WM8510 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8523
- tristate
+ tristate "Wolfson Microelectronics WM8523 DAC"
+ depends on I2C
config SND_SOC_WM8580
- tristate
+ tristate "Wolfson Microelectronics WM8523 CODEC"
+ depends on I2C
config SND_SOC_WM8711
- tristate
+ tristate "Wolfson Microelectronics WM8711 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8727
tristate
config SND_SOC_WM8728
- tristate
+ tristate "Wolfson Microelectronics WM8728 DAC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8731
- tristate
+ tristate "Wolfson Microelectronics WM8731 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8737
- tristate
+ tristate "Wolfson Microelectronics WM8737 ADC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8741
- tristate
+ tristate "Wolfson Microelectronics WM8737 DAC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8750
- tristate
+ tristate "Wolfson Microelectronics WM8750 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8753
- tristate
+ tristate "Wolfson Microelectronics WM8753 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8770
- tristate
+ tristate "Wolfson Microelectronics WM8770 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8776
- tristate
+ tristate "Wolfson Microelectronics WM8776 CODEC"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8782
tristate
config SND_SOC_WM8804
- tristate
+ tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver"
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8900
tristate
config SND_SOC_WM8903
- tristate
+ tristate "Wolfson Microelectronics WM8903 CODEC"
+ depends on I2C
config SND_SOC_WM8904
tristate
@@ -484,7 +515,8 @@ config SND_SOC_WM8961
tristate
config SND_SOC_WM8962
- tristate
+ tristate "Wolfson Microelectronics WM8962 CODEC"
+ depends on I2C
config SND_SOC_WM8971
tristate
@@ -557,4 +589,7 @@ config SND_SOC_ML26124
tristate
config SND_SOC_TPA6130A2
- tristate
+ tristate "Texas Instruments TPA6130A2 headphone amplifier"
+ depends on I2C
+
+endmenu
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 58343562286..a6f6afa4d19 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -78,6 +78,8 @@ struct aic3x_priv {
struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES];
struct aic3x_setup_data *setup;
unsigned int sysclk;
+ unsigned int dai_fmt;
+ unsigned int tdm_delay;
struct list_head list;
int master;
int gpio_reset;
@@ -270,6 +272,18 @@ static const struct soc_enum aic3x_agc_decay_enum[] = {
SOC_ENUM_SINGLE(RAGC_CTRL_A, 0, 4, aic3x_agc_decay),
};
+static const char * const aic3x_poweron_time[] = {
+ "0us", "10us", "100us", "1ms", "10ms", "50ms", "100ms",
+ "200ms", "400ms", "800ms", "2s", "4s",
+};
+static const char * const aic3x_rampup_step[] = {
+ "0ms", "1ms", "2ms", "4ms"
+};
+static const struct soc_enum aic3x_pop_reduction_enum[] = {
+ SOC_ENUM_SINGLE(HPOUT_POP_REDUCTION, 4, 12, aic3x_poweron_time),
+ SOC_ENUM_SINGLE(HPOUT_POP_REDUCTION, 2, 4, aic3x_rampup_step),
+};
+
/*
* DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps
*/
@@ -399,6 +413,10 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
+
+ /* Pop reduction */
+ SOC_ENUM("Output Driver Power-On time", aic3x_pop_reduction_enum[0]),
+ SOC_ENUM("Output Driver Ramp-up step", aic3x_pop_reduction_enum[1]),
};
static const struct snd_kcontrol_new aic3x_mono_controls[] = {
@@ -1010,6 +1028,25 @@ found:
return 0;
}
+static int aic3x_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ int delay = 0;
+
+ /* TDM slot selection only valid in DSP_A/_B mode */
+ if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A)
+ delay += (aic3x->tdm_delay + 1);
+ else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B)
+ delay += aic3x->tdm_delay;
+
+ /* Configure data delay */
+ snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, aic3x->tdm_delay);
+
+ return 0;
+}
+
static int aic3x_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
@@ -1049,7 +1086,6 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct snd_soc_codec *codec = codec_dai->codec;
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
u8 iface_areg, iface_breg;
- int delay = 0;
iface_areg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
iface_breg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
@@ -1077,7 +1113,6 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
break;
case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
- delay = 1;
case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
iface_breg |= (0x01 << 6);
break;
@@ -1091,10 +1126,45 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
+ aic3x->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
/* set iface */
snd_soc_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
- snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
+
+ return 0;
+}
+
+static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+ unsigned int lsb;
+
+ if (tx_mask != rx_mask) {
+ dev_err(codec->dev, "tx and rx masks must be symmetric\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(!tx_mask)) {
+ dev_err(codec->dev, "tx and rx masks need to be non 0\n");
+ return -EINVAL;
+ }
+
+ /* TDM based on DSP mode requires slots to be adjacent */
+ lsb = __ffs(tx_mask);
+ if ((lsb + 1) != __fls(tx_mask)) {
+ dev_err(codec->dev, "Invalid mask, slots must be adjacent\n");
+ return -EINVAL;
+ }
+
+ aic3x->tdm_delay = lsb * slot_width;
+
+ /* DOUT in high-impedance on inactive bit clocks */
+ snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA,
+ DOUT_TRISTATE, DOUT_TRISTATE);
return 0;
}
@@ -1200,9 +1270,11 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops aic3x_dai_ops = {
.hw_params = aic3x_hw_params,
+ .prepare = aic3x_prepare,
.digital_mute = aic3x_mute,
.set_sysclk = aic3x_set_dai_sysclk,
.set_fmt = aic3x_set_dai_fmt,
+ .set_tdm_slot = aic3x_set_dai_tdm_slot,
};
static struct snd_soc_dai_driver aic3x_dai = {
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index e521ac3ddde..89fa692df20 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -169,6 +169,7 @@
/* Audio serial data interface control register A bits */
#define BIT_CLK_MASTER 0x80
#define WORD_CLK_MASTER 0x40
+#define DOUT_TRISTATE 0x20
/* Codec Datapath setup register 7 */
#define FSREF_44100 (1 << 7)
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 8e3b4a055d3..54582e2f8df 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -16,8 +16,14 @@ config SND_DAVINCI_SOC_I2S
tristate
config SND_DAVINCI_SOC_MCASP
+ tristate "Multichannel Audio Serial Port (McASP) support"
depends on SND_DAVINCI_SOC || SND_OMAP_SOC || SND_EDMA_SOC
- tristate
+ help
+ Say Y or M here if you want to have support for McASP IP found in
+ various Texas Instruments SoCs like:
+ - daVinci devices
+ - Sitara line of SoCs (AM335x, AM438x, etc)
+ - DRA7x devices
config SND_DAVINCI_SOC_VCIF
tristate
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index a6490284c29..eeb51f9d478 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -67,6 +67,7 @@ struct davinci_mcasp {
void __iomem *base;
u32 fifo_base;
struct device *dev;
+ struct snd_pcm_substream *substreams[2];
/* McASP specific data */
int tdm_slots;
@@ -87,6 +88,9 @@ struct davinci_mcasp {
bool dat_port;
+ /* Used for comstraint setting on the second stream */
+ u32 channels;
+
#ifdef CONFIG_PM_SLEEP
struct davinci_mcasp_context context;
#endif
@@ -151,9 +155,16 @@ static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
static void mcasp_start_rx(struct davinci_mcasp *mcasp)
{
+ if (mcasp->rxnumevt) { /* enable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+ }
+
+ /* Start clocks */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
-
/*
* When ASYNC == 0 the transmit and receive sections operate
* synchronously from the transmit clock and frame sync. We need to make
@@ -164,74 +175,66 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp)
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
}
+ /* Activate serializer(s) */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
+ /* Release RX state machine */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+ /* Release Frame Sync generator */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
-
if (mcasp_is_synchronous(mcasp))
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+ /* enable rececive overrun IRQ */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, ROVRN);
}
static void mcasp_start_tx(struct davinci_mcasp *mcasp)
{
- u8 offset = 0, i;
u32 cnt;
+ if (mcasp->txnumevt) { /* enable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+ }
+
+ /* Start clocks */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+ /* Activate serializer(s) */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
- for (i = 0; i < mcasp->num_serializer; i++) {
- if (mcasp->serial_dir[i] == TX_MODE) {
- offset = i;
- break;
- }
- }
-
- /* wait for TX ready */
+ /* wait for XDATA to be cleared */
cnt = 0;
- while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(offset)) &
- TXSTATE) && (cnt < 100000))
+ while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) &
+ ~XRDATA) && (cnt < 100000))
cnt++;
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
+ /* Release TX state machine */
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+ /* Release Frame Sync generator */
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+ /* enable transmit underrun IRQ */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, XUNDRN);
}
static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream)
{
- u32 reg;
-
mcasp->streams++;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (mcasp->txnumevt) { /* enable FIFO */
- reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
- }
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
mcasp_start_tx(mcasp);
- } else {
- if (mcasp->rxnumevt) { /* enable FIFO */
- reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
- }
+ else
mcasp_start_rx(mcasp);
- }
}
static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
{
+ /* disable IRQ sources */
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, ROVRN);
+
/*
* In synchronous mode stop the TX clocks if no other stream is
* running
@@ -241,12 +244,21 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+
+ if (mcasp->rxnumevt) { /* disable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ }
}
static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
{
u32 val = 0;
+ /* disable IRQ sources */
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, XUNDRN);
+
/*
* In synchronous mode keep TX clocks running if the capture stream is
* still running.
@@ -256,27 +268,68 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+
+ if (mcasp->txnumevt) { /* disable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ }
}
static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream)
{
- u32 reg;
-
mcasp->streams--;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (mcasp->txnumevt) { /* disable FIFO */
- reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- }
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
mcasp_stop_tx(mcasp);
- } else {
- if (mcasp->rxnumevt) { /* disable FIFO */
- reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- }
+ else
mcasp_stop_rx(mcasp);
+}
+
+static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data)
+{
+ struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+ struct snd_pcm_substream *substream;
+ u32 stat;
+
+ stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG);
+ if (stat & XUNDRN) {
+ dev_warn(mcasp->dev, "Transmit buffer underflow\n");
+ substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream) {
+ snd_pcm_stream_lock_irq(substream);
+ if (snd_pcm_running(substream))
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irq(substream);
+ }
}
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, stat);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data)
+{
+ struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+ struct snd_pcm_substream *substream;
+ u32 stat;
+
+ stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG);
+ if (stat & ROVRN) {
+ dev_warn(mcasp->dev, "Receive buffer overflow\n");
+ substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream) {
+ snd_pcm_stream_lock_irq(substream);
+ if (snd_pcm_running(substream))
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irq(substream);
+ }
+ }
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, stat);
+
+ return IRQ_HANDLED;
}
static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
@@ -497,8 +550,17 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
* both left and right channels), so it has to be divided by number of
* tdm-slots (for I2S - divided by 2).
*/
- if (mcasp->bclk_lrclk_ratio)
- word_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
+ if (mcasp->bclk_lrclk_ratio) {
+ u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
+
+ /*
+ * When we have more bclk then it is needed for the data, we
+ * need to use the rotation to move the received samples to have
+ * correct alignment.
+ */
+ rx_rotate = (slot_length - word_length) / 4;
+ word_length = slot_length;
+ }
/* mapping of the XSSZ bit-field as described in the datasheet */
fmt = (word_length >> 1) - 1;
@@ -632,19 +694,29 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
return 0;
}
-static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
+static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
+ int channels)
{
int i, active_slots;
+ int total_slots;
+ int active_serializers;
u32 mask = 0;
u32 busel = 0;
- if ((mcasp->tdm_slots < 2) || (mcasp->tdm_slots > 32)) {
- dev_err(mcasp->dev, "tdm slot %d not supported\n",
- mcasp->tdm_slots);
- return -EINVAL;
- }
+ total_slots = mcasp->tdm_slots;
+
+ /*
+ * If more than one serializer is needed, then use them with
+ * their specified tdm_slots count. Otherwise, one serializer
+ * can cope with the transaction using as many slots as channels
+ * in the stream, requires channels symmetry
+ */
+ active_serializers = (channels + total_slots - 1) / total_slots;
+ if (active_serializers == 1)
+ active_slots = channels;
+ else
+ active_slots = total_slots;
- active_slots = (mcasp->tdm_slots > 31) ? 32 : mcasp->tdm_slots;
for (i = 0; i < active_slots; i++)
mask |= (1 << i);
@@ -656,12 +728,12 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
- FSXMOD(mcasp->tdm_slots), FSXMOD(0x1FF));
+ FSXMOD(total_slots), FSXMOD(0x1FF));
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
- FSRMOD(mcasp->tdm_slots), FSRMOD(0x1FF));
+ FSRMOD(total_slots), FSRMOD(0x1FF));
return 0;
}
@@ -775,7 +847,8 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
ret = mcasp_dit_hw_param(mcasp, params_rate(params));
else
- ret = mcasp_i2s_hw_param(mcasp, substream->stream);
+ ret = mcasp_i2s_hw_param(mcasp, substream->stream,
+ channels);
if (ret)
return ret;
@@ -823,6 +896,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
davinci_config_channel_size(mcasp, word_length);
+ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
+ mcasp->channels = channels;
+
return 0;
}
@@ -851,7 +927,65 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
return ret;
}
+static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+ u32 max_channels = 0;
+ int i, dir;
+
+ mcasp->substreams[substream->stream] = substream;
+
+ if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+ return 0;
+
+ /*
+ * Limit the maximum allowed channels for the first stream:
+ * number of serializers for the direction * tdm slots per serializer
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = TX_MODE;
+ else
+ dir = RX_MODE;
+
+ for (i = 0; i < mcasp->num_serializer; i++) {
+ if (mcasp->serial_dir[i] == dir)
+ max_channels++;
+ }
+ max_channels *= mcasp->tdm_slots;
+ /*
+ * If the already active stream has less channels than the calculated
+ * limnit based on the seirializers * tdm_slots, we need to use that as
+ * a constraint for the second stream.
+ * Otherwise (first stream or less allowed channels) we use the
+ * calculated constraint.
+ */
+ if (mcasp->channels && mcasp->channels < max_channels)
+ max_channels = mcasp->channels;
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 2, max_channels);
+ return 0;
+}
+
+static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+
+ mcasp->substreams[substream->stream] = NULL;
+
+ if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+ return;
+
+ if (!cpu_dai->active)
+ mcasp->channels = 0;
+}
+
static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
+ .startup = davinci_mcasp_startup,
+ .shutdown = davinci_mcasp_shutdown,
.trigger = davinci_mcasp_trigger,
.hw_params = davinci_mcasp_hw_params,
.set_fmt = davinci_mcasp_set_dai_fmt,
@@ -986,6 +1120,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
},
.ops = &davinci_mcasp_dai_ops,
+ .symmetric_samplebits = 1,
},
{
.name = "davinci-mcasp.1",
@@ -1209,6 +1344,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
struct resource *mem, *ioarea, *res, *dat;
struct davinci_mcasp_pdata *pdata;
struct davinci_mcasp *mcasp;
+ int irq;
int ret;
if (!pdev->dev.platform_data && !pdev->dev.of_node) {
@@ -1261,7 +1397,21 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
}
mcasp->op_mode = pdata->op_mode;
- mcasp->tdm_slots = pdata->tdm_slots;
+ /* sanity check for tdm slots parameter */
+ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+ if (pdata->tdm_slots < 2) {
+ dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+ pdata->tdm_slots);
+ mcasp->tdm_slots = 2;
+ } else if (pdata->tdm_slots > 32) {
+ dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+ pdata->tdm_slots);
+ mcasp->tdm_slots = 32;
+ } else {
+ mcasp->tdm_slots = pdata->tdm_slots;
+ }
+ }
+
mcasp->num_serializer = pdata->num_serializer;
#ifdef CONFIG_PM_SLEEP
mcasp->context.xrsrctl = devm_kzalloc(&pdev->dev,
@@ -1275,6 +1425,28 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
mcasp->dev = &pdev->dev;
+ irq = platform_get_irq_byname(pdev, "rx");
+ if (irq >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ davinci_mcasp_rx_irq_handler,
+ IRQF_ONESHOT, dev_name(&pdev->dev), mcasp);
+ if (ret) {
+ dev_err(&pdev->dev, "RX IRQ request failed\n");
+ goto err;
+ }
+ }
+
+ irq = platform_get_irq_byname(pdev, "tx");
+ if (irq >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ davinci_mcasp_tx_irq_handler,
+ IRQF_ONESHOT, dev_name(&pdev->dev), mcasp);
+ if (ret) {
+ dev_err(&pdev->dev, "TX IRQ request failed\n");
+ goto err;
+ }
+ }
+
dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
if (dat)
mcasp->dat_port = true;
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
index 98fbc451892..cbba1daa25e 100644
--- a/sound/soc/davinci/davinci-mcasp.h
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -253,6 +253,12 @@
#define TXFSRST BIT(12) /* Frame Sync Generator Reset */
/*
+ * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits
+ * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits
+ */
+#define XRDATA BIT(5) /* Transmit/Receive data ready */
+
+/*
* DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits
*/
#define MUTENA(val) (val)
@@ -279,6 +285,16 @@
#define TXDATADMADIS BIT(0)
/*
+ * DAVINCI_MCASP_EVTCTLR_REG - Receiver Interrupt Control Register Bits
+ */
+#define ROVRN BIT(0)
+
+/*
+ * DAVINCI_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register Bits
+ */
+#define XUNDRN BIT(0)
+
+/*
* DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
*/
#define FIFO_ENABLE BIT(16)