ASoC: msm: Add support for 24-bit playback
Update MSM ALSA driver to support 24-bit PCM sample packed into 32-bit
frame. A mixer control is added to allow 24/16 bitwidth selection on
SLIMBUS_0_RX backend
Change-Id: Ib2530159841bf191d4b27d0750db4c4a191bc0e0
Signed-off-by: Bhalchandra Gajare <gajare@codeaurora.org>
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
index 0bce766..f2d71b6 100644
--- a/drivers/mfd/wcd9xxx-slimslave.c
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -214,7 +214,7 @@
prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
prop.ratem = (rate/4000);
- prop.sampleszbits = 16;
+ prop.sampleszbits = bit_width;
pr_debug("Before slim_define_ch:\n"
"ch_cnt %d,ch_h[0] %d ch_h[1] %d, grph %d\n",
diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h
index 699e63a..77a805c 100644
--- a/include/sound/q6adm-v2.h
+++ b/include/sound/q6adm-v2.h
@@ -30,10 +30,10 @@
int srs_trumedia_open(int port_id, int srs_tech_id, void *srs_params);
int adm_open(int port, int path, int rate, int mode, int topology,
- bool perf_mode);
+ bool perf_mode, uint16_t bits_per_sample);
int adm_multi_ch_copp_open(int port, int path, int rate, int mode,
- int topology, bool perf_mode);
+ int topology, bool perf_mode, uint16_t bits_per_sample);
int adm_memory_map_regions(int port_id, uint32_t *buf_add, uint32_t mempool_id,
uint32_t *bufsz, uint32_t bufcnt);
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 553b33b..dc30cd6 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -179,6 +179,9 @@
int q6asm_open_write(struct audio_client *ac, uint32_t format
/*, uint16_t bits_per_sample*/);
+int q6asm_open_write_v2(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample);
+
int q6asm_open_read_write(struct audio_client *ac,
uint32_t rd_format,
uint32_t wr_format);
@@ -263,10 +266,20 @@
int q6asm_media_format_block_pcm(struct audio_client *ac,
uint32_t rate, uint32_t channels);
+int q6asm_media_format_block_pcm_format_support(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample);
+
int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
uint32_t rate, uint32_t channels,
bool use_default_chmap, char *channel_map);
+int q6asm_media_format_block_multi_ch_pcm_v2(
+ struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap, char *channel_map,
+ uint16_t bits_per_sample);
+
int q6asm_media_format_block_aac(struct audio_client *ac,
struct asm_aac_cfg *cfg);
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 6f601c1..f48dbf1 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -71,6 +71,11 @@
#define TAIKO_MCLK_CLK_12P288MHZ 12288000
#define TAIKO_MCLK_CLK_9P6HZ 9600000
+#define TAIKO_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FORMAT_S24_LE)
+
+#define TAIKO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
enum {
AIF1_PB = 0,
AIF1_CAP,
@@ -3196,7 +3201,6 @@
return 0;
}
-#define TAIKO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
static int taiko_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
@@ -3673,6 +3677,29 @@
snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_I2S_CTL,
0x03, (rx_fs_rate >> 0x05));
} else {
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec,
+ TAIKO_A_CDC_CONN_RX_SB_B1_CTL,
+ 0xFF, 0xAA);
+ snd_soc_update_bits(codec,
+ TAIKO_A_CDC_CONN_RX_SB_B2_CTL,
+ 0xFF, 0x2A);
+ taiko->dai[dai->id].bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec,
+ TAIKO_A_CDC_CONN_RX_SB_B1_CTL,
+ 0xFF, 0x00);
+ snd_soc_update_bits(codec,
+ TAIKO_A_CDC_CONN_RX_SB_B2_CTL,
+ 0xFF, 0x00);
+ taiko->dai[dai->id].bit_width = 24;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid format\n");
+ break;
+ }
taiko->dai[dai->id].rate = params_rate(params);
}
break;
@@ -3702,7 +3729,7 @@
.playback = {
.stream_name = "AIF1 Playback",
.rates = WCD9320_RATES,
- .formats = TAIKO_FORMATS,
+ .formats = TAIKO_FORMATS_S16_S24_LE,
.rate_max = 192000,
.rate_min = 8000,
.channels_min = 1,
@@ -3730,7 +3757,7 @@
.playback = {
.stream_name = "AIF2 Playback",
.rates = WCD9320_RATES,
- .formats = TAIKO_FORMATS,
+ .formats = TAIKO_FORMATS_S16_S24_LE,
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 1,
@@ -3772,7 +3799,7 @@
.playback = {
.stream_name = "AIF3 Playback",
.rates = WCD9320_RATES,
- .formats = TAIKO_FORMATS,
+ .formats = TAIKO_FORMATS_S16_S24_LE,
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 1,
@@ -4897,10 +4924,6 @@
{TAIKO_A_CDC_CONN_TX_SB_B9_CTL, 0x60, 0x40},
{TAIKO_A_CDC_CONN_TX_SB_B10_CTL, 0x60, 0x40},
- /* Use 16 bit sample size for RX */
- {TAIKO_A_CDC_CONN_RX_SB_B1_CTL, 0xFF, 0xAA},
- {TAIKO_A_CDC_CONN_RX_SB_B2_CTL, 0xFF, 0x2A},
-
/*enable HPF filter for TX paths */
{TAIKO_A_CDC_TX1_MUX_CTL, 0x8, 0x0},
{TAIKO_A_CDC_TX2_MUX_CTL, 0x8, 0x0},
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 3f0c1d7..7a15299 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -54,7 +54,8 @@
.aif_name = "MM_DL1",
.rates = (SNDRV_PCM_RATE_8000_48000|
SNDRV_PCM_RATE_KNOT),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -80,7 +81,8 @@
.aif_name = "MM_DL2",
.rates = (SNDRV_PCM_RATE_8000_48000|
SNDRV_PCM_RATE_KNOT),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -156,7 +158,8 @@
.aif_name = "MM_DL3",
.rates = (SNDRV_PCM_RATE_8000_48000 |
SNDRV_PCM_RATE_KNOT),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 6,
.rate_min = 8000,
@@ -171,7 +174,8 @@
.aif_name = "MM_DL4",
.rates = (SNDRV_PCM_RATE_8000_48000 |
SNDRV_PCM_RATE_KNOT),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -197,7 +201,8 @@
.aif_name = "MM_DL5",
.rates = (SNDRV_PCM_RATE_8000_48000 |
SNDRV_PCM_RATE_KNOT),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -223,7 +228,8 @@
.aif_name = "MM_DL6",
.rates = (SNDRV_PCM_RATE_8000_48000 |
SNDRV_PCM_RATE_KNOT),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -238,7 +244,8 @@
.aif_name = "MM_DL7",
.rates = (SNDRV_PCM_RATE_8000_48000 |
SNDRV_PCM_RATE_KNOT),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -253,7 +260,8 @@
.aif_name = "MM_DL8",
.rates = (SNDRV_PCM_RATE_8000_48000 |
SNDRV_PCM_RATE_KNOT),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -268,7 +276,8 @@
.stream_name = "SLIMBUS0 Hostless Playback",
.aif_name = "SLIM0_DL_HL",
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -292,7 +301,8 @@
.stream_name = "SLIMBUS1 Hostless Playback",
.aif_name = "SLIM1_DL_HL",
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
@@ -316,7 +326,8 @@
.stream_name = "SLIMBUS3 Hostless Playback",
.aif_name = "SLIM3_DL_HL",
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
@@ -340,7 +351,8 @@
.stream_name = "SLIMBUS4 Hostless Playback",
.aif_name = "SLIM4_DL_HL",
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index 8bb06d4..de8505a 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -26,6 +26,7 @@
#include <sound/jack.h>
#include <asm/mach-types.h>
#include <mach/socinfo.h>
+#include <sound/pcm_params.h>
#include <qdsp6v2/msm-pcm-routing-v2.h>
#include "../codecs/wcd9320.h"
#include <linux/io.h>
@@ -41,6 +42,8 @@
#define BTSCO_RATE_8KHZ 8000
#define BTSCO_RATE_16KHZ 16000
+static int slim0_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
static int msm8974_auxpcm_rate = 8000;
#define LO_1_SPK_AMP 0x1
#define LO_3_SPK_AMP 0x2
@@ -68,6 +71,29 @@
#define NUM_OF_AUXPCM_GPIOS 4
+static inline int param_is_mask(int p)
+{
+ return ((p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_MASK));
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
+{
+ return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
+{
+ if (bit >= SNDRV_MASK_MAX)
+ return;
+ if (param_is_mask(n)) {
+ struct snd_mask *m = param_to_mask(p, n);
+ m->bits[0] = 0;
+ m->bits[1] = 0;
+ m->bits[bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
static const char *const auxpcm_rate_text[] = {"rate_8000", "rate_16000"};
static const struct soc_enum msm8974_auxpcm_enum[] = {
SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text),
@@ -543,6 +569,7 @@
static const char *const slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five",
"Six", "Seven", "Eight"};
+static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE"};
static const struct soc_enum msm_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
@@ -555,6 +582,43 @@
SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
};
+static int slim0_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ switch (slim0_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: slim0_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, slim0_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int slim0_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ slim0_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ slim0_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return 0;
+}
+
static int msm_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -861,9 +925,15 @@
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
pr_debug("%s()\n", __func__);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ slim0_rx_bit_format);
rate->min = rate->max = 48000;
channels->min = channels->max = msm_slim_0_rx_ch;
+ pr_debug("%s: format = %d, rate = %d, channels = %d\n",
+ __func__, params_format(params), params_rate(params),
+ msm_slim_0_rx_ch);
+
return 0;
}
@@ -900,6 +970,7 @@
SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(2, rx_bit_format_text),
};
static const struct snd_kcontrol_new msm_snd_controls[] = {
@@ -913,6 +984,8 @@
msm8974_auxpcm_rate_get, msm8974_auxpcm_rate_put),
SOC_ENUM_EXT("HDMI_RX Channels", msm_snd_enum[3],
msm_hdmi_rx_ch_get, msm_hdmi_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_RX Format", msm_snd_enum[4],
+ slim0_rx_bit_format_get, slim0_rx_bit_format_put),
};
static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index 581d8ad..33b72e8 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -81,7 +81,7 @@
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
.rate_min = 8000,
.rate_max = 48000,
@@ -697,6 +697,8 @@
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
struct audio_buffer *buf;
int dir, ret;
+ uint16_t bits_per_sample = 16;
+
struct asm_softpause_params softpause = {
.enable = SOFT_PAUSE_ENABLE,
.period = SOFT_PAUSE_PERIOD,
@@ -714,9 +716,13 @@
dir = IN;
else
dir = OUT;
+
+ if (runtime->format == SNDRV_PCM_FORMAT_S24_LE)
+ bits_per_sample = 24;
+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ret = q6asm_open_write(prtd->audio_client,
- compr->codec);
+ ret = q6asm_open_write_v2(prtd->audio_client,
+ compr->codec, bits_per_sample);
if (ret < 0) {
pr_err("%s: Session out open failed\n",
__func__);
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 4efdb24..1797d83 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -482,6 +482,18 @@
return -EINVAL;
break;
}
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dai_data->port_config.i2s.bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dai_data->port_config.i2s.bit_width = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
dai_data->rate = params_rate(params);
dai_data->port_config.i2s.sample_rate = dai_data->rate;
dai_data->port_config.i2s.i2s_cfg_minor_version =
@@ -490,8 +502,6 @@
dev_dbg(dai->dev, " channel %d sample rate %d entered\n",
dai_data->channels, dai_data->rate);
- /* Q6 only supports 16 as now */
- dai_data->port_config.i2s.bit_width = 16;
dai_data->port_config.i2s.channel_mode = 1;
return 0;
}
@@ -548,10 +558,19 @@
dai_data->channels = params_channels(params);
dai_data->rate = params_rate(params);
- /* Q6 only supports 16 as now */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dai_data->port_config.slim_sch.bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dai_data->port_config.slim_sch.bit_width = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
dai_data->port_config.slim_sch.sb_cfg_minor_version =
AFE_API_VERSION_SLIMBUS_CONFIG;
- dai_data->port_config.slim_sch.bit_width = 16;
dai_data->port_config.slim_sch.data_format = 0;
dai_data->port_config.slim_sch.num_channels = dai_data->channels;
dai_data->port_config.slim_sch.sample_rate = dai_data->rate;
@@ -889,7 +908,7 @@
.playback = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
@@ -919,7 +938,7 @@
.playback = {
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
@@ -1247,7 +1266,7 @@
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
diff --git a/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
index 3c9190a..b03c63e 100644
--- a/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-multi-ch-pcm-q6-v2.c
@@ -76,7 +76,8 @@
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
@@ -208,6 +209,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_audio *prtd = runtime->private_data;
int ret;
+ uint16_t bits_per_sample = 16;
pr_debug("%s\n", __func__);
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
@@ -219,8 +221,19 @@
if (prtd->enabled)
return 0;
- ret = q6asm_media_format_block_pcm(prtd->audio_client,
- runtime->rate, runtime->channels);
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bits_per_sample = 16;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits_per_sample = 24;
+ break;
+ }
+
+ ret = q6asm_media_format_block_pcm_format_support(
+ prtd->audio_client, runtime->rate,
+ runtime->channels, bits_per_sample);
if (ret < 0)
pr_info("%s: CMD Format block failed\n", __func__);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
index b9fafb1..74a3af9 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
@@ -42,7 +42,8 @@
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
.rates = (SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_48000),
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
index 6491cc5..2fca464 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -52,7 +52,8 @@
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
.rate_min = 8000,
.rate_max = 48000,
@@ -196,6 +197,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_audio *prtd = runtime->private_data;
int ret;
+ uint16_t bits_per_sample = 16;
pr_debug("%s\n", __func__);
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
@@ -208,8 +210,19 @@
if (prtd->enabled)
return 0;
- ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
- runtime->channels);
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bits_per_sample = 16;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits_per_sample = 24;
+ break;
+ }
+
+ ret = q6asm_media_format_block_pcm_format_support(
+ prtd->audio_client, runtime->rate,
+ runtime->channels, bits_per_sample);
if (ret < 0)
pr_debug("%s: CMD Format block failed\n", __func__);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index c5f500a..3008071 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -29,6 +29,7 @@
#include <linux/dma-mapping.h>
#include <linux/android_pmem.h>
#include <linux/of_device.h>
+#include <sound/pcm_params.h>
#include "msm-pcm-q6-v2.h"
#include "msm-pcm-routing-v2.h"
@@ -79,7 +80,8 @@
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
@@ -209,6 +211,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_audio *prtd = runtime->private_data;
int ret;
+ uint16_t bits_per_sample = 16;
pr_debug("%s\n", __func__);
prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
@@ -220,11 +223,19 @@
if (prtd->enabled)
return 0;
- ret = q6asm_media_format_block_multi_ch_pcm(prtd->audio_client,
- runtime->rate,
- runtime->channels,
- !prtd->set_channel_map,
- prtd->channel_map);
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bits_per_sample = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits_per_sample = 24;
+ break;
+ }
+
+ ret = q6asm_media_format_block_multi_ch_pcm_v2(
+ prtd->audio_client, runtime->rate,
+ runtime->channels, !prtd->set_channel_map,
+ prtd->channel_map, bits_per_sample);
if (ret < 0)
pr_info("%s: CMD Format block failed\n", __func__);
@@ -309,17 +320,9 @@
static int msm_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct msm_audio *prtd;
- struct msm_plat_data *pdata;
int ret = 0;
- pdata = (struct msm_plat_data *)
- dev_get_drvdata(soc_prtd->platform->dev);
- if (!pdata) {
- pr_err("%s: platform data not populated\n", __func__);
- return -EINVAL;
- }
prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
if (prtd == NULL) {
pr_err("Failed to allocate memory for msm_audio\n");
@@ -333,27 +336,10 @@
kfree(prtd);
return -ENOMEM;
}
- prtd->audio_client->perf_mode = pdata->perf_mode;
- pr_debug("%s: perf: %x\n", __func__, pdata->perf_mode);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
runtime->hw = msm_pcm_hardware_playback;
- ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
- if (ret < 0) {
- pr_err("%s: pcm out open failed\n", __func__);
- q6asm_audio_client_free(prtd->audio_client);
- kfree(prtd);
- return -ENOMEM;
- }
- pr_debug("%s: session ID %d\n", __func__,
- prtd->audio_client->session);
- prtd->session_id = prtd->audio_client->session;
- msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
- prtd->audio_client->perf_mode,
- prtd->session_id, substream->stream);
- prtd->cmd_ack = 1;
-
- }
/* Capture path */
else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
runtime->hw = msm_pcm_hardware_capture;
@@ -664,11 +650,49 @@
struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
struct audio_buffer *buf;
int dir, ret;
+ struct msm_plat_data *pdata;
+ uint16_t bits_per_sample = 16;
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(soc_prtd->platform->dev);
+ if (!pdata) {
+ pr_err("%s: platform data not populated\n", __func__);
+ return -EINVAL;
+ }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dir = IN;
- else {
+ else
dir = OUT;
+
+ prtd->audio_client->perf_mode = pdata->perf_mode;
+ pr_debug("%s: perf: %x\n", __func__, pdata->perf_mode);
+ /* Playback Path */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE)
+ bits_per_sample = 24;
+
+ ret = q6asm_open_write_v2(prtd->audio_client,
+ FORMAT_LINEAR_PCM, bits_per_sample);
+ if (ret < 0) {
+ pr_err("%s: q6asm_open_write_v2 failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+
+ pr_debug("%s: session ID %d\n", __func__,
+ prtd->audio_client->session);
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
+ prtd->session_id, substream->stream);
+ prtd->cmd_ack = 1;
+ }
+
+ /* Capture Path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+
pr_debug("%s Opening %d-ch PCM read stream\n",
__func__, params_channels(params));
ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index f168dc1..c48132e 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -29,6 +29,8 @@
#include <sound/q6asm-v2.h>
#include <sound/q6afe-v2.h>
#include <sound/tlv.h>
+#include <sound/asound.h>
+#include <sound/pcm_params.h>
#include "msm-pcm-routing-v2.h"
#include "q6voice.h"
@@ -40,6 +42,7 @@
unsigned long port_sessions; /* track Tx BE ports -> Rx BE */
unsigned int sample_rate;
unsigned int channel;
+ unsigned int format;
bool perf_mode;
};
@@ -295,6 +298,7 @@
int i, session_type, path_type, port_type;
struct route_payload payload;
u32 channels;
+ uint16_t bits_per_sample = 16;
if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
/* bad ID assigned in machine driver */
@@ -329,29 +333,27 @@
channels = msm_bedais[i].channel;
+ if (msm_bedais[i].format == SNDRV_PCM_FORMAT_S16_LE)
+ bits_per_sample = 16;
+ else if (msm_bedais[i].format ==
+ SNDRV_PCM_FORMAT_S24_LE)
+ bits_per_sample = 24;
+
if ((stream_type == SNDRV_PCM_STREAM_PLAYBACK) &&
- ((channels == 1) || (channels == 2)) &&
- msm_bedais[i].perf_mode) {
- pr_debug("%s configure COPP to lowlatency mode",
- __func__);
+ (channels > 0))
adm_multi_ch_copp_open(msm_bedais[i].port_id,
path_type,
msm_bedais[i].sample_rate,
msm_bedais[i].channel,
- DEFAULT_COPP_TOPOLOGY, msm_bedais[i].perf_mode);
- } else if ((stream_type == SNDRV_PCM_STREAM_PLAYBACK) &&
- (channels > 2))
- adm_multi_ch_copp_open(msm_bedais[i].port_id,
- path_type,
- msm_bedais[i].sample_rate,
- msm_bedais[i].channel,
- DEFAULT_COPP_TOPOLOGY, msm_bedais[i].perf_mode);
+ DEFAULT_COPP_TOPOLOGY, msm_bedais[i].perf_mode,
+ bits_per_sample);
else
adm_open(msm_bedais[i].port_id,
path_type,
msm_bedais[i].sample_rate,
msm_bedais[i].channel,
- DEFAULT_COPP_TOPOLOGY, false);
+ DEFAULT_COPP_TOPOLOGY, false,
+ bits_per_sample);
payload.copp_ids[payload.num_copps++] =
msm_bedais[i].port_id;
@@ -420,6 +422,7 @@
{
int session_type, path_type;
u32 channels;
+ uint16_t bits_per_sample = 16;
pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
@@ -449,31 +452,23 @@
INVALID_SESSION) {
channels = msm_bedais[reg].channel;
+ if (msm_bedais[reg].format == SNDRV_PCM_FORMAT_S24_LE)
+ bits_per_sample = 24;
if ((session_type == SESSION_TYPE_RX) &&
- ((channels == 1) || (channels == 2))
- && msm_bedais[reg].perf_mode) {
+ (channels > 0)) {
adm_multi_ch_copp_open(msm_bedais[reg].port_id,
path_type,
msm_bedais[reg].sample_rate,
channels,
DEFAULT_COPP_TOPOLOGY,
- msm_bedais[reg].perf_mode);
- pr_debug("%s:configure COPP to lowlatency mode",
- __func__);
- } else if ((session_type == SESSION_TYPE_RX)
- && (channels > 2))
- adm_multi_ch_copp_open(msm_bedais[reg].port_id,
- path_type,
- msm_bedais[reg].sample_rate,
- channels,
- DEFAULT_COPP_TOPOLOGY,
- msm_bedais[reg].perf_mode);
- else
+ msm_bedais[reg].perf_mode,
+ bits_per_sample);
+ } else
adm_open(msm_bedais[reg].port_id,
path_type,
msm_bedais[reg].sample_rate, channels,
- DEFAULT_COPP_TOPOLOGY, false);
+ DEFAULT_COPP_TOPOLOGY, false, bits_per_sample);
msm_pcm_routing_build_matrix(val,
fe_dai_map[val][session_type], path_type);
@@ -2576,6 +2571,7 @@
mutex_lock(&routing_lock);
msm_bedais[be_id].sample_rate = params_rate(params);
msm_bedais[be_id].channel = params_channels(params);
+ msm_bedais[be_id].format = params_format(params);
mutex_unlock(&routing_lock);
return 0;
}
@@ -2620,6 +2616,8 @@
int i, path_type, session_type;
struct msm_pcm_routing_bdai_data *bedai;
u32 channels;
+ bool playback, capture;
+ uint16_t bits_per_sample = 16;
if (be_id >= MSM_BACKEND_DAI_MAX) {
pr_err("%s: unexpected be_id %d\n", __func__, be_id);
@@ -2647,36 +2645,31 @@
* is started.
*/
bedai->active = 1;
+ playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ capture = substream->stream == SNDRV_PCM_STREAM_CAPTURE;
+
for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
if (fe_dai_map[i][session_type] != INVALID_SESSION) {
channels = bedai->channel;
- if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
- substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- && ((channels == 2) || (channels == 1)) &&
- bedai->perf_mode) {
+ if (bedai->format == SNDRV_PCM_FORMAT_S24_LE)
+ bits_per_sample = 24;
+
+ if ((playback) && (channels > 0)) {
adm_multi_ch_copp_open(bedai->port_id,
- path_type,
- bedai->sample_rate,
- channels,
- DEFAULT_COPP_TOPOLOGY, bedai->perf_mode);
- pr_debug("%s:configure COPP to lowlatency mode",
- __func__);
- } else if ((substream->stream ==
- SNDRV_PCM_STREAM_PLAYBACK ||
- substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- && (channels > 2))
- adm_multi_ch_copp_open(bedai->port_id,
- path_type,
- bedai->sample_rate,
- channels,
- DEFAULT_COPP_TOPOLOGY, bedai->perf_mode);
- else
+ path_type,
+ bedai->sample_rate,
+ channels,
+ DEFAULT_COPP_TOPOLOGY, bedai->perf_mode,
+ bits_per_sample);
+ } else if (capture) {
adm_open(bedai->port_id,
path_type,
bedai->sample_rate,
channels,
- DEFAULT_COPP_TOPOLOGY, false);
+ DEFAULT_COPP_TOPOLOGY, false,
+ bits_per_sample);
+ }
msm_pcm_routing_build_matrix(i,
fe_dai_map[i][session_type], path_type);
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index e9c2735..093070c 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -741,7 +741,7 @@
}
int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
- bool perf_mode)
+ bool perf_mode, uint16_t bits_per_sample)
{
struct adm_cmd_device_open_v5 open;
int ret = 0;
@@ -817,7 +817,7 @@
open.topology_id = topology;
open.dev_num_channel = channel_mode & 0x00FF;
- open.bit_width = 16;
+ open.bit_width = bits_per_sample;
open.sample_rate = rate;
memset(open.dev_channel_mapping, 0, 8);
@@ -900,13 +900,13 @@
return ret;
}
-
int adm_multi_ch_copp_open(int port_id, int path, int rate, int channel_mode,
- int topology, bool perf_mode)
+ int topology, bool perf_mode, uint16_t bits_per_sample)
{
int ret = 0;
- ret = adm_open(port_id, path, rate, channel_mode, topology, perf_mode);
+ ret = adm_open(port_id, path, rate, channel_mode,
+ topology, perf_mode, bits_per_sample);
return ret;
}
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 14202a8..87990a9 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -1513,7 +1513,9 @@
fail_cmd:
return -EINVAL;
}
-int q6asm_open_write(struct audio_client *ac, uint32_t format)
+
+static int __q6asm_open_write(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
{
int rc = 0x00;
struct asm_stream_cmd_open_write_v3 open;
@@ -1538,7 +1540,7 @@
/* source endpoint : matrix */
open.sink_endpointype = ASM_END_POINT_DEVICE_MATRIX;
- open.bits_per_sample = 16;
+ open.bits_per_sample = bits_per_sample;
open.postprocopo_id = get_asm_topology();
if (open.postprocopo_id == 0)
@@ -1585,6 +1587,17 @@
return -EINVAL;
}
+int q6asm_open_write(struct audio_client *ac, uint32_t format)
+{
+ return __q6asm_open_write(ac, format, 16);
+}
+
+int q6asm_open_write_v2(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_open_write(ac, format, bits_per_sample);
+}
+
int q6asm_open_read_write(struct audio_client *ac,
uint32_t rd_format,
uint32_t wr_format)
@@ -2242,8 +2255,9 @@
return q6asm_media_format_block_multi_aac(ac, cfg);
}
-int q6asm_media_format_block_pcm(struct audio_client *ac,
- uint32_t rate, uint32_t channels)
+static int __q6asm_media_format_block_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample)
{
struct asm_multi_channel_pcm_fmt_blk_v2 fmt;
u8 *channel_mapping;
@@ -2258,7 +2272,7 @@
fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
sizeof(fmt.fmt_blk);
fmt.num_channels = channels;
- fmt.bits_per_sample = 16;
+ fmt.bits_per_sample = bits_per_sample;
fmt.sample_rate = rate;
fmt.is_signed = 1;
@@ -2285,9 +2299,25 @@
return -EINVAL;
}
-int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+int q6asm_media_format_block_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ return __q6asm_media_format_block_pcm(ac, rate,
+ channels, 16);
+}
+
+int q6asm_media_format_block_pcm_format_support(struct audio_client *ac,
uint32_t rate, uint32_t channels,
- bool use_default_chmap, char *channel_map)
+ uint16_t bits_per_sample)
+{
+ return __q6asm_media_format_block_pcm(ac, rate,
+ channels, bits_per_sample);
+}
+
+static int __q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap, char *channel_map,
+ uint16_t bits_per_sample)
{
struct asm_multi_channel_pcm_fmt_blk_v2 fmt;
u8 *channel_mapping;
@@ -2335,6 +2365,26 @@
fail_cmd:
return -EINVAL;
}
+
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap, char *channel_map)
+{
+ return __q6asm_media_format_block_multi_ch_pcm(ac, rate,
+ channels, use_default_chmap, channel_map, 16);
+}
+
+int q6asm_media_format_block_multi_ch_pcm_v2(
+ struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap, char *channel_map,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_media_format_block_multi_ch_pcm(ac, rate,
+ channels, use_default_chmap, channel_map,
+ bits_per_sample);
+}
+
int q6asm_media_format_block_multi_aac(struct audio_client *ac,
struct asm_aac_cfg *cfg)
{