diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 16a5c778cf0..50894fb054b 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -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"; diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi index 0d1d818eb53..3a0f0b933ba 100644 --- a/arch/arm/boot/dts/msm8974.dtsi +++ b/arch/arm/boot/dts/msm8974.dtsi @@ -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"; diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index 8e8778a4c04..ac4ec098d80 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -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 diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h index 1587d38e781..b1ed9a49680 100644 --- a/include/sound/q6afe-v2.h +++ b/include/sound/q6afe-v2.h @@ -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); diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 783a03d39cb..99fd1d3ed74 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -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); } diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 67ee8e4eb5e..fbbb3a52566 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -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; } } diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index aed6273381d..e5837b2531d 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -16,7 +16,7 @@ #include #include #include - +#include #include #include @@ -25,7 +25,7 @@ #include #include #include - +#include #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; diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c index 4875a69e113..756cb1855ba 100644 --- a/sound/soc/msm/qdsp6v2/q6afe.c +++ b/sound/soc/msm/qdsp6v2/q6afe.c @@ -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;