msm: q6dspv2: Add slimbus data path support

Register slimbus CPU dai link to support slimbus data path.

Change-Id: I3584306ac1e0ad6561a19cecfe71f2a63aadafa9
Signed-off-by: Joonwoo Park <joonwoop@codeaurora.org>
This commit is contained in:
Joonwoo Park
2012-07-10 17:17:00 -07:00
committed by Stephen Boyd
parent 0e33cbb772
commit 1238a07489
8 changed files with 885 additions and 18 deletions

View File

@@ -1,4 +1,4 @@
Qualcomm audio devices for ALSA sound soc
Qualcomm audio devices for ALSA sound SoC
* msm-pcm
@@ -42,6 +42,22 @@ Required properties:
- compatible : "qcom,msm-dai-fe"
* msm-dai-q6
[First Level Nodes]
Required properties:
- compatible : "msm-dai-q6"
[Second Level Nodes]
Required properties:
- compatible : "qcom,msm-dai-q6-dev"
- qcom,msm-dai-q6-dev-id : The slimbus multi channel port ID
Value is from 16384 to 16393
* msm-auxpcm
[First Level Nodes]
@@ -117,6 +133,19 @@ Example:
compatible = "qcom,msm-dai-fe";
};
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <16384>;
};
qcom,msm-dai-q6-sb-0-tx {
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <16385>;
};
};
qcom,msm-auxpcm {
compatible = "qcom,msm-auxpcm-resource";
qcom,msm-cpudai-auxpcm-clk = "pcm_clk";

View File

@@ -400,6 +400,19 @@
compatible = "qcom,msm-dai-fe";
};
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <16384>;
};
qcom,msm-dai-q6-sb-0-tx {
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <16385>;
};
};
qcom,msm-auxpcm {
compatible = "qcom,msm-auxpcm-resource";
qcom,msm-cpudai-auxpcm-clk = "pcm_clk";

View File

@@ -573,6 +573,7 @@ struct adm_cmd_matrix_mute_v5 {
/* Payload of the #ADM_CMD_CONNECT_AFE_PORT_V5 command.*/
struct adm_cmd_connect_afe_port_v5 {
struct apr_hdr hdr;
u8 mode;
/* ID of the stream router (RX/TX). Use the
* ADM_STRTR_ID_RX or ADM_STRTR_IDX macros

View File

@@ -77,6 +77,7 @@ int afe_loopback(u16 enable, u16 rx_port, u16 tx_port);
int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain);
int afe_loopback_gain(u16 port_id, u16 volume);
int afe_validate_port(u16 port_id);
int afe_get_port_index(u16 port_id);
int afe_start_pseudo_port(u16 port_id);
int afe_stop_pseudo_port(u16 port_id);
int afe_cmd_memory_map(u32 dma_addr_p, u32 dma_buf_sz);

View File

@@ -325,6 +325,494 @@ static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_tx_dai = {
.remove = msm_dai_q6_dai_auxpcm_remove,
};
static int msm_dai_q6_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
int rc = 0;
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
/* PORT START should be set if prepare called in active state */
rc = afe_q6_interface_prepare();
if (IS_ERR_VALUE(rc))
dev_err(dai->dev, "fail to open AFE APR\n");
}
return rc;
}
static int msm_dai_q6_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
int rc = 0;
/*
* Start/stop port without waiting for Q6 AFE response. Need to have
* native q6 AFE driver propagates AFE response in order to handle
* port start/stop command error properly if error does arise.
*/
pr_debug("%s:port:%d cmd:%d dai_data->status_mask = %ld",
__func__, dai->id, cmd, *dai_data->status_mask);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
switch (dai->id) {
case VOICE_PLAYBACK_TX:
case VOICE_RECORD_TX:
case VOICE_RECORD_RX:
afe_pseudo_port_start_nowait(dai->id);
break;
default:
afe_port_start_nowait(dai->id,
&dai_data->port_config, dai_data->rate);
break;
}
set_bit(STATUS_PORT_STARTED,
dai_data->status_mask);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
switch (dai->id) {
case VOICE_PLAYBACK_TX:
case VOICE_RECORD_TX:
case VOICE_RECORD_RX:
afe_pseudo_port_stop_nowait(dai->id);
break;
default:
afe_port_stop_nowait(dai->id);
break;
}
clear_bit(STATUS_PORT_STARTED,
dai_data->status_mask);
}
break;
default:
rc = -EINVAL;
}
return rc;
}
static int msm_dai_q6_cdc_hw_params(struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai, int stream)
{
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
dai_data->channels = params_channels(params);
switch (dai_data->channels) {
case 2:
dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO;
break;
case 1:
dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
break;
default:
return -EINVAL;
break;
}
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 =
AFE_API_VERSION_I2S_CONFIG;
dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA;
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;
}
static u8 num_of_bits_set(u8 sd_line_mask)
{
u8 num_bits_set = 0;
while (sd_line_mask) {
num_bits_set++;
sd_line_mask = sd_line_mask & (sd_line_mask - 1);
}
return num_bits_set;
}
static int msm_dai_q6_i2s_hw_params(struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai, int stream)
{
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
struct msm_i2s_data *i2s_pdata =
(struct msm_i2s_data *) dai->dev->platform_data;
dai_data->channels = params_channels(params);
if (num_of_bits_set(i2s_pdata->sd_lines) == 1) {
switch (dai_data->channels) {
case 2:
dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO;
break;
case 1:
dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
break;
default:
pr_warn("greater than stereo has not been validated");
break;
}
}
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 =
AFE_API_VERSION_I2S_CONFIG;
dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA;
/* Q6 only supports 16 as now */
dai_data->port_config.i2s.bit_width = 16;
dai_data->port_config.i2s.channel_mode = 1;
return 0;
}
static int msm_dai_q6_slim_bus_hw_params(struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai, int stream)
{
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
dai_data->channels = params_channels(params);
dai_data->rate = params_rate(params);
/* Q6 only supports 16 as now */
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;
dev_dbg(dai->dev, "%s:slimbus_dev_id[%hu] bit_wd[%hu] format[%hu]\n"
"num_channel %hu shared_ch_mapping[0] %hu\n"
"slave_port_mapping[1] %hu slave_port_mapping[2] %hu\n"
"sample_rate %d\n", __func__,
dai_data->port_config.slim_sch.slimbus_dev_id,
dai_data->port_config.slim_sch.bit_width,
dai_data->port_config.slim_sch.data_format,
dai_data->port_config.slim_sch.num_channels,
dai_data->port_config.slim_sch.shared_ch_mapping[0],
dai_data->port_config.slim_sch.shared_ch_mapping[1],
dai_data->port_config.slim_sch.shared_ch_mapping[2],
dai_data->rate);
return 0;
}
static int msm_dai_q6_bt_fm_hw_params(struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai, int stream)
{
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
dai_data->channels = params_channels(params);
dai_data->rate = params_rate(params);
dev_dbg(dai->dev, "channels %d sample rate %d entered\n",
dai_data->channels, dai_data->rate);
memset(&dai_data->port_config, 0, sizeof(dai_data->port_config));
return 0;
}
static int msm_dai_q6_afe_rtproxy_hw_params(struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
dai_data->rate = params_rate(params);
dai_data->port_config.rtproxy.num_channels = params_channels(params);
dai_data->port_config.rtproxy.sample_rate = params_rate(params);
pr_debug("channel %d entered,dai_id: %d,rate: %d\n",
dai_data->port_config.rtproxy.num_channels, dai->id, dai_data->rate);
dai_data->port_config.rtproxy.rt_proxy_cfg_minor_version =
AFE_API_VERSION_RT_PROXY_CONFIG;
dai_data->port_config.rtproxy.bit_width = 16; /* Q6 only supports 16 */
dai_data->port_config.rtproxy.interleaved = 1;
dai_data->port_config.rtproxy.frame_size = params_period_bytes(params);
dai_data->port_config.rtproxy.jitter_allowance =
dai_data->port_config.rtproxy.frame_size/2;
dai_data->port_config.rtproxy.low_water_mark = 0;
dai_data->port_config.rtproxy.high_water_mark = 0;
return 0;
}
/* Current implementation assumes hw_param is called once
* This may not be the case but what to do when ADM and AFE
* port are already opened and parameter changes
*/
static int msm_dai_q6_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
int rc = 0;
switch (dai->id) {
case PRIMARY_I2S_TX:
case PRIMARY_I2S_RX:
case SECONDARY_I2S_RX:
rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream);
break;
case MI2S_RX:
rc = msm_dai_q6_i2s_hw_params(params, dai, substream->stream);
break;
case SLIMBUS_0_RX:
case SLIMBUS_1_RX:
case SLIMBUS_0_TX:
case SLIMBUS_1_TX:
rc = msm_dai_q6_slim_bus_hw_params(params, dai,
substream->stream);
break;
case INT_BT_SCO_RX:
case INT_BT_SCO_TX:
case INT_FM_RX:
case INT_FM_TX:
rc = msm_dai_q6_bt_fm_hw_params(params, dai, substream->stream);
break;
case RT_PROXY_DAI_001_TX:
case RT_PROXY_DAI_001_RX:
case RT_PROXY_DAI_002_TX:
case RT_PROXY_DAI_002_RX:
rc = msm_dai_q6_afe_rtproxy_hw_params(params, dai);
break;
case VOICE_PLAYBACK_TX:
case VOICE_RECORD_RX:
case VOICE_RECORD_TX:
rc = 0;
break;
default:
dev_err(dai->dev, "invalid AFE port ID\n");
rc = -EINVAL;
break;
}
return rc;
}
static void msm_dai_q6_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
int rc = 0;
if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
switch (dai->id) {
case VOICE_PLAYBACK_TX:
case VOICE_RECORD_TX:
case VOICE_RECORD_RX:
pr_debug("%s, stop pseudo port:%d\n",
__func__, dai->id);
rc = afe_stop_pseudo_port(dai->id);
break;
default:
rc = afe_close(dai->id); /* can block */
break;
}
if (IS_ERR_VALUE(rc))
dev_err(dai->dev, "fail to close AFE port\n");
pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
*dai_data->status_mask);
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
}
}
static int msm_dai_q6_cdc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
dai_data->port_config.i2s.ws_src = 1; /* CPU is master */
break;
case SND_SOC_DAIFMT_CBM_CFM:
dai_data->port_config.i2s.ws_src = 0; /* CPU is slave */
break;
default:
return -EINVAL;
}
return 0;
}
static int msm_dai_q6_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
int rc = 0;
dev_dbg(dai->dev, "enter %s, id = %d fmt[%d]\n", __func__,
dai->id, fmt);
switch (dai->id) {
case PRIMARY_I2S_TX:
case PRIMARY_I2S_RX:
case MI2S_RX:
case SECONDARY_I2S_RX:
rc = msm_dai_q6_cdc_set_fmt(dai, fmt);
break;
default:
dev_err(dai->dev, "invalid cpu_dai set_fmt\n");
rc = -EINVAL;
break;
}
return rc;
}
static int msm_dai_q6_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
int rc = 0;
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
unsigned int i = 0;
dev_dbg(dai->dev, "enter %s, id = %d\n", __func__, dai->id);
switch (dai->id) {
case SLIMBUS_0_RX:
case SLIMBUS_1_RX:
/*
* channel number to be between 128 and 255.
* For RX port use channel numbers
* from 138 to 144 for pre-Taiko
* from 144 to 159 for Taiko
*/
if (!rx_slot)
return -EINVAL;
for (i = 0; i < rx_num; i++) {
dai_data->port_config.slim_sch.shared_ch_mapping[i] =
rx_slot[i];
pr_err("%s: find number of channels[%d] ch[%d]\n",
__func__, i, rx_slot[i]);
}
dai_data->port_config.slim_sch.num_channels = rx_num;
pr_debug("%s:SLIMBUS_0_RX cnt[%d] ch[%d %d]\n", __func__,
rx_num, dai_data->port_config.slim_sch.shared_ch_mapping[0],
dai_data->port_config.slim_sch.shared_ch_mapping[1]);
break;
case SLIMBUS_0_TX:
case SLIMBUS_1_TX:
/*
* channel number to be between 128 and 255.
* For TX port use channel numbers
* from 128 to 137 for pre-Taiko
* from 128 to 143 for Taiko
*/
if (!tx_slot)
return -EINVAL;
for (i = 0; i < tx_num; i++) {
dai_data->port_config.slim_sch.shared_ch_mapping[i] =
tx_slot[i];
pr_debug("%s: find number of channels[%d] ch[%d]\n",
__func__, i, tx_slot[i]);
}
dai_data->port_config.slim_sch.num_channels = tx_num;
pr_debug("%s:SLIMBUS_0_TX cnt[%d] ch[%d %d]\n", __func__,
tx_num,
dai_data->port_config.slim_sch.shared_ch_mapping[0],
dai_data->port_config.slim_sch.shared_ch_mapping[1]);
break;
default:
dev_err(dai->dev, "invalid cpu_dai id %d\n", dai->id);
rc = -EINVAL;
break;
}
return rc;
}
static struct snd_soc_dai_ops msm_dai_q6_ops = {
.prepare = msm_dai_q6_prepare,
.trigger = msm_dai_q6_trigger,
.hw_params = msm_dai_q6_hw_params,
.shutdown = msm_dai_q6_shutdown,
.set_fmt = msm_dai_q6_set_fmt,
.set_channel_map = msm_dai_q6_set_channel_map,
};
static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
{
struct msm_dai_q6_dai_data *dai_data;
int rc = 0;
dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data), GFP_KERNEL);
if (!dai_data) {
dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
dai->id);
rc = -ENOMEM;
} else
dev_set_drvdata(dai->dev, dai_data);
return rc;
}
static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
{
struct msm_dai_q6_dai_data *dai_data;
int rc;
dai_data = dev_get_drvdata(dai->dev);
/* If AFE port is still up, close it */
if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
switch (dai->id) {
case VOICE_PLAYBACK_TX:
case VOICE_RECORD_TX:
case VOICE_RECORD_RX:
pr_debug("%s, stop pseudo port:%d\n",
__func__, dai->id);
rc = afe_stop_pseudo_port(dai->id);
break;
default:
rc = afe_close(dai->id); /* can block */
}
if (IS_ERR_VALUE(rc))
dev_err(dai->dev, "fail to close AFE port\n");
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
}
kfree(dai_data);
snd_soc_unregister_dai(dai->dev);
return 0;
}
static struct snd_soc_dai_driver msm_dai_q6_slimbus_1_rx_dai = {
.playback = {
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 1,
.rate_min = 8000,
.rate_max = 16000,
},
.ops = &msm_dai_q6_ops,
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
};
static struct snd_soc_dai_driver msm_dai_q6_slimbus_1_tx_dai = {
.capture = {
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 1,
.rate_min = 8000,
.rate_max = 16000,
},
.ops = &msm_dai_q6_ops,
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
};
static int msm_auxpcm_dev_probe(struct platform_device *pdev)
{
int id;
@@ -512,6 +1000,135 @@ static struct platform_driver msm_auxpcm_resource = {
},
};
static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai = {
.playback = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.ops = &msm_dai_q6_ops,
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
};
static struct snd_soc_dai_driver msm_dai_q6_slimbus_tx_dai = {
.capture = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.ops = &msm_dai_q6_ops,
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
};
static int msm_dai_q6_dev_probe(struct platform_device *pdev)
{
int rc, id;
const char *q6_dev_id = "qcom,msm-dai-q6-dev-id";
rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id);
if (rc) {
dev_err(&pdev->dev,
"%s: missing %s in dt node\n", __func__, q6_dev_id);
return rc;
}
pdev->id = id;
dev_set_name(&pdev->dev, "%s.%d", "msm-dai-q6-dev", id);
pr_debug("%s: dev name %s, id:%d\n", __func__,
dev_name(&pdev->dev), pdev->id);
switch (id) {
case SLIMBUS_0_RX:
rc = snd_soc_register_dai(&pdev->dev,
&msm_dai_q6_slimbus_rx_dai);
break;
case SLIMBUS_0_TX:
rc = snd_soc_register_dai(&pdev->dev,
&msm_dai_q6_slimbus_tx_dai);
break;
case SLIMBUS_1_RX:
rc = snd_soc_register_dai(&pdev->dev,
&msm_dai_q6_slimbus_1_rx_dai);
break;
case SLIMBUS_1_TX:
rc = snd_soc_register_dai(&pdev->dev,
&msm_dai_q6_slimbus_1_tx_dai);
break;
default:
rc = -ENODEV;
break;
}
return rc;
}
static int msm_dai_q6_dev_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&pdev->dev);
return 0;
}
static const struct of_device_id msm_dai_q6_dev_dt_match[] = {
{ .compatible = "qcom,msm-dai-q6-dev", },
{ }
};
MODULE_DEVICE_TABLE(of, msm_dai_q6_dev_dt_match);
static struct platform_driver msm_dai_q6_dev = {
.probe = msm_dai_q6_dev_probe,
.remove = msm_dai_q6_dev_remove,
.driver = {
.name = "msm-dai-q6-dev",
.owner = THIS_MODULE,
.of_match_table = msm_dai_q6_dev_dt_match,
},
};
static int msm_dai_q6_probe(struct platform_device *pdev)
{
int rc;
pr_debug("%s: dev name %s, id:%d\n", __func__,
dev_name(&pdev->dev), pdev->id);
rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
if (rc) {
dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
__func__, rc);
} else
dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
return rc;
}
static int msm_dai_q6_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id msm_dai_q6_dt_match[] = {
{ .compatible = "qcom,msm-dai-q6", },
{ }
};
MODULE_DEVICE_TABLE(of, msm_dai_q6_dt_match);
static struct platform_driver msm_dai_q6 = {
.probe = msm_dai_q6_probe,
.remove = msm_dai_q6_remove,
.driver = {
.name = "msm-dai-q6",
.owner = THIS_MODULE,
.of_match_table = msm_dai_q6_dt_match,
},
};
static int __init msm_dai_q6_init(void)
{
@@ -522,10 +1139,27 @@ static int __init msm_dai_q6_init(void)
goto fail;
rc = platform_driver_register(&msm_auxpcm_resource);
if (rc) {
pr_err("%s: fail to register cpu dai driver\n", __func__);
platform_driver_unregister(&msm_auxpcm_dev);
goto fail;
}
rc = platform_driver_register(&msm_dai_q6);
if (rc) {
pr_err("%s: fail to register dai q6 driver", __func__);
platform_driver_unregister(&msm_auxpcm_dev);
platform_driver_unregister(&msm_auxpcm_resource);
goto fail;
}
rc = platform_driver_register(&msm_dai_q6_dev);
if (rc) {
pr_err("%s: fail to register dai q6 dev driver", __func__);
platform_driver_unregister(&msm_dai_q6);
platform_driver_unregister(&msm_auxpcm_dev);
platform_driver_unregister(&msm_auxpcm_resource);
goto fail;
}
fail:
return rc;
@@ -534,6 +1168,8 @@ module_init(msm_dai_q6_init);
static void __exit msm_dai_q6_exit(void)
{
platform_driver_unregister(&msm_dai_q6_dev);
platform_driver_unregister(&msm_dai_q6);
platform_driver_unregister(&msm_auxpcm_dev);
platform_driver_unregister(&msm_auxpcm_resource);
}

View File

@@ -217,11 +217,12 @@ void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
fe_dai_map[fedai_id][session_type] = dspst_id;
for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
if (!is_be_dai_extproc(i) &&
(afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
(msm_bedais[i].active) &&
(test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
(afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
(msm_bedais[i].active) &&
(test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
mode = afe_get_port_type(msm_bedais[i].port_id);
/*adm_connect_afe_port needs to be called*/
adm_connect_afe_port(mode, dspst_id,
msm_bedais[i].port_id);
break;
}
}

View File

@@ -16,7 +16,7 @@
#include <linux/jiffies.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
#include <linux/wait.h>
#include <mach/qdsp6v2/audio_acdb.h>
#include <mach/qdsp6v2/rtac.h>
@@ -25,7 +25,7 @@
#include <mach/qdsp6v2/apr.h>
#include <sound/q6adm-v2.h>
#include <sound/q6audio-v2.h>
#include <sound/q6afe-v2.h>
#define TIMEOUT_MS 1000
@@ -78,7 +78,7 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv)
return 0;
}
if (data->opcode == APR_BASIC_RSP_RESULT) {
pr_debug("APR_BASIC_RSP_RESULT\n");
pr_debug("APR_BASIC_RSP_RESULT id %x\n", payload[0]);
switch (payload[0]) {
case ADM_CMD_SET_PP_PARAMS_V5:
if (rtac_make_adm_callback(
@@ -142,6 +142,76 @@ static void send_adm_cal(int port_id, int path)
pr_debug("%s\n", __func__);
}
int adm_connect_afe_port(int mode, int session_id, int port_id)
{
struct adm_cmd_connect_afe_port_v5 cmd;
int ret = 0;
int index;
pr_debug("%s: port %d session id:%d mode:%d\n", __func__,
port_id, session_id, mode);
port_id = afe_convert_virtual_to_portid(port_id);
if (afe_validate_port(port_id) < 0) {
pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
return -ENODEV;
}
if (this_adm.apr == NULL) {
this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
0xFFFFFFFF, &this_adm);
if (this_adm.apr == NULL) {
pr_err("%s: Unable to register ADM\n", __func__);
ret = -ENODEV;
return ret;
}
rtac_set_adm_handle(this_adm.apr);
}
index = afe_get_port_index(port_id);
pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
cmd.hdr.pkt_size = sizeof(cmd);
cmd.hdr.src_svc = APR_SVC_ADM;
cmd.hdr.src_domain = APR_DOMAIN_APPS;
cmd.hdr.src_port = port_id;
cmd.hdr.dest_svc = APR_SVC_ADM;
cmd.hdr.dest_domain = APR_DOMAIN_ADSP;
cmd.hdr.dest_port = port_id;
cmd.hdr.token = port_id;
cmd.hdr.opcode = ADM_CMD_CONNECT_AFE_PORT_V5;
cmd.mode = mode;
cmd.session_id = session_id;
cmd.afe_port_id = port_id;
atomic_set(&this_adm.copp_stat[index], 0);
ret = apr_send_pkt(this_adm.apr, (uint32_t *)&cmd);
if (ret < 0) {
pr_err("%s:ADM enable for port %d failed\n",
__func__, port_id);
ret = -EINVAL;
goto fail_cmd;
}
/* Wait for the callback with copp id */
ret = wait_event_timeout(this_adm.wait[index],
atomic_read(&this_adm.copp_stat[index]),
msecs_to_jiffies(TIMEOUT_MS));
if (!ret) {
pr_err("%s ADM connect AFE failed for port %d\n", __func__,
port_id);
ret = -EINVAL;
goto fail_cmd;
}
atomic_inc(&this_adm.copp_cnt[index]);
return 0;
fail_cmd:
return ret;
}
int adm_open(int port_id, int path, int rate, int channel_mode, int topology)
{
struct adm_cmd_device_open_v5 open;

View File

@@ -144,6 +144,9 @@ int afe_get_port_type(u16 port_id)
case HDMI_RX:
case SLIMBUS_0_RX:
case SLIMBUS_1_RX:
case SLIMBUS_2_RX:
case SLIMBUS_3_RX:
case SLIMBUS_4_RX:
case INT_BT_SCO_RX:
case INT_BT_A2DP_RX:
case INT_FM_RX:
@@ -160,6 +163,9 @@ int afe_get_port_type(u16 port_id)
case VOICE_RECORD_TX:
case SLIMBUS_0_TX:
case SLIMBUS_1_TX:
case SLIMBUS_2_TX:
case SLIMBUS_3_TX:
case SLIMBUS_4_TX:
case INT_FM_TX:
case VOICE_RECORD_RX:
case INT_BT_SCO_TX:
@@ -168,6 +174,7 @@ int afe_get_port_type(u16 port_id)
break;
default:
WARN_ON(1);
pr_err("%s: invalid port id %d\n", __func__, port_id);
ret = -EINVAL;
}
@@ -255,7 +262,6 @@ int afe_port_start_nowait(u16 port_id, union afe_port_config *afe_config,
ret = -EINVAL;
return ret;
}
pr_err("%s: %d %d\n", __func__, port_id, rate);
index = q6audio_get_port_index(port_id);
if (q6audio_validate_port(port_id) < 0)
return -EINVAL;
@@ -279,11 +285,11 @@ int afe_port_start_nowait(u16 port_id, union afe_port_config *afe_config,
config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
config.hdr.pkt_size = afe_sizeof_cfg_cmd(port_id);
config.hdr.pkt_size = sizeof(config);
config.hdr.src_port = 0;
config.hdr.dest_port = 0;
config.hdr.token = index;
switch (port_id) {
case PRIMARY_I2S_RX:
case PRIMARY_I2S_TX:
@@ -320,15 +326,15 @@ int afe_port_start_nowait(u16 port_id, union afe_port_config *afe_config,
goto fail_cmd;
}
config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
config.param.port_id = port_id;
config.param.payload_size = (afe_sizeof_cfg_cmd(port_id) +
sizeof(struct afe_port_param_data_v2));
config.param.port_id = q6audio_get_port_id(port_id);
config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
sizeof(config.param);
config.param.payload_address_lsw = 0x00;
config.param.payload_address_msw = 0x00;
config.param.mem_map_handle = 0x00;
config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
config.pdata.param_id = cfg_type;
config.pdata.param_size = afe_sizeof_cfg_cmd(port_id);
config.pdata.param_size = sizeof(config.port);
config.port = *afe_config;
@@ -348,9 +354,11 @@ int afe_port_start_nowait(u16 port_id, union afe_port_config *afe_config,
start.hdr.pkt_size = sizeof(start);
start.hdr.src_port = 0;
start.hdr.dest_port = 0;
start.hdr.token = 0;
start.hdr.token = index;
start.hdr.opcode = AFE_PORT_CMD_DEVICE_START;
start.port_id = port_id;
start.port_id = q6audio_get_port_id(port_id);
pr_debug("%s: cmd device start opcode[0x%x] port id[0x%x]\n",
__func__, start.hdr.opcode, start.port_id);
ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
@@ -367,6 +375,45 @@ fail_cmd:
return ret;
}
int afe_get_port_index(u16 port_id)
{
switch (port_id) {
case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX;
case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX;
case PCM_RX: return IDX_PCM_RX;
case PCM_TX: return IDX_PCM_TX;
case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX;
case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX;
case MI2S_RX: return IDX_MI2S_RX;
case MI2S_TX: return IDX_MI2S_TX;
case HDMI_RX: return IDX_HDMI_RX;
case RSVD_2: return IDX_RSVD_2;
case RSVD_3: return IDX_RSVD_3;
case DIGI_MIC_TX: return IDX_DIGI_MIC_TX;
case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX;
case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX;
case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX;
case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX;
case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX;
case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX;
case SLIMBUS_1_TX: return IDX_SLIMBUS_1_TX;
case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX;
case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX;
case SLIMBUS_3_RX: return IDX_SLIMBUS_3_RX;
case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX;
case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX;
case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX;
case INT_FM_RX: return IDX_INT_FM_RX;
case INT_FM_TX: return IDX_INT_FM_TX;
case RT_PROXY_PORT_001_RX: return IDX_RT_PROXY_PORT_001_RX;
case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX;
case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX;
case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX;
default: return -EINVAL;
}
}
int afe_open(u16 port_id,
union afe_port_config *afe_config, int rate)
{
@@ -1469,6 +1516,75 @@ fail_cmd:
return ret;
}
int afe_validate_port(u16 port_id)
{
int ret;
switch (port_id) {
case PRIMARY_I2S_RX:
case PRIMARY_I2S_TX:
case PCM_RX:
case PCM_TX:
case SECONDARY_I2S_RX:
case SECONDARY_I2S_TX:
case MI2S_RX:
case MI2S_TX:
case HDMI_RX:
case RSVD_2:
case RSVD_3:
case DIGI_MIC_TX:
case VOICE_RECORD_RX:
case VOICE_RECORD_TX:
case VOICE_PLAYBACK_TX:
case SLIMBUS_0_RX:
case SLIMBUS_0_TX:
case SLIMBUS_1_RX:
case SLIMBUS_1_TX:
case SLIMBUS_2_RX:
case SLIMBUS_2_TX:
case SLIMBUS_3_RX:
case INT_BT_SCO_RX:
case INT_BT_SCO_TX:
case INT_BT_A2DP_RX:
case INT_FM_RX:
case INT_FM_TX:
case RT_PROXY_PORT_001_RX:
case RT_PROXY_PORT_001_TX:
case SLIMBUS_4_RX:
case SLIMBUS_4_TX:
{
ret = 0;
break;
}
default:
ret = -EINVAL;
}
return ret;
}
int afe_convert_virtual_to_portid(u16 port_id)
{
int ret;
/*
* if port_id is virtual, convert to physical..
* if port_id is already physical, return physical
*/
if (afe_validate_port(port_id) < 0) {
if (port_id == RT_PROXY_DAI_001_RX ||
port_id == RT_PROXY_DAI_001_TX ||
port_id == RT_PROXY_DAI_002_RX ||
port_id == RT_PROXY_DAI_002_TX)
ret = VIRTUAL_ID_TO_PORTID(port_id);
else
ret = -EINVAL;
} else
ret = port_id;
return ret;
}
int afe_port_stop_nowait(int port_id)
{
struct afe_port_cmd_device_stop stop;