diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 12fb6968e9a8..7a3dd4e57afa 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -224,6 +224,15 @@ struct msm_dai_q6_mi2s_dai_data { struct msm_dai_q6_mi2s_dai_config rx_dai; }; +struct msm_dai_q6_cdc_dma_dai_data { + DECLARE_BITMAP(status_mask, STATUS_MAX); + DECLARE_BITMAP(hwfree_status, STATUS_MAX); + u32 rate; + u32 channels; + u32 bitwidth; + union afe_port_config port_config; +}; + struct msm_dai_q6_auxpcm_dai_data { /* BITMAP to track Rx and Tx port usage count */ DECLARE_BITMAP(auxpcm_port_status, STATUS_MAX); @@ -270,6 +279,15 @@ static const struct soc_enum mi2s_config_enum[] = { SOC_ENUM_SINGLE_EXT(2, mi2s_vi_feed_mono), }; +static const char *const cdc_dma_format[] = { + "UNPACKED", + "PACKED_16B", +}; + +static const struct soc_enum cdc_dma_config_enum[] = { + SOC_ENUM_SINGLE_EXT(2, cdc_dma_format), +}; + static const char *const sb_format[] = { "UNPACKED", "PACKED_16B", @@ -5223,6 +5241,7 @@ static int msm_dai_q6_probe(struct platform_device *pdev) static int msm_dai_q6_remove(struct platform_device *pdev) { + of_platform_depopulate(&pdev->dev); return 0; } @@ -9048,6 +9067,511 @@ static struct platform_driver msm_dai_q6_tdm_driver = { }, }; +static int msm_dai_q6_cdc_dma_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->port_config.cdc_dma.data_format = value; + pr_debug("%s: format = %d\n", __func__, value); + return 0; +} + +static int msm_dai_q6_cdc_dma_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->port_config.cdc_dma.data_format; + return 0; +} +static const struct snd_kcontrol_new cdc_dma_config_controls[] = { + SOC_ENUM_EXT("WSA_CDC_DMA_0 TX Format", cdc_dma_config_enum[0], + msm_dai_q6_cdc_dma_format_get, + msm_dai_q6_cdc_dma_format_put), +}; + +/* SOC probe for codec DMA interface */ +static int msm_dai_q6_dai_cdc_dma_probe(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_cdc_dma_dai_data *dai_data = NULL; + int rc = 0; + + if (!dai) { + pr_err("%s: Invalid params dai\n", __func__); + return -EINVAL; + } + if (!dai->dev) { + pr_err("%s: Invalid params dai dev\n", __func__); + return -EINVAL; + } + + msm_dai_q6_set_dai_id(dai); + dai_data = dev_get_drvdata(dai->dev); + + switch (dai->id) { + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&cdc_dma_config_controls[0], + dai_data)); + break; + default: + break; + } + + if (rc < 0) + dev_err(dai->dev, "%s: err add config ctl, DAI = %s\n", + __func__, dai->name); + rc = msm_dai_q6_dai_add_route(dai); + return rc; +} + +static int msm_dai_q6_dai_cdc_dma_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_cdc_dma_dai_data *dai_data = + dev_get_drvdata(dai->dev); + int rc = 0; + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + dev_dbg(dai->dev, "%s: stop codec dma port:%d\n", __func__, + dai->id); + rc = afe_close(dai->id); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + return rc; +} + +static int msm_dai_q6_cdc_dma_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num_ch, unsigned int *tx_ch_mask, + unsigned int rx_num_ch, unsigned int *rx_ch_mask) + +{ + struct msm_dai_q6_cdc_dma_dai_data *dai_data = + dev_get_drvdata(dai->dev); + unsigned int ch_mask = 0, ch_num = 0; + + dev_dbg(dai->dev, "%s: id = %d\n", __func__, dai->id); + switch (dai->id) { + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_RX_1: + if (!rx_ch_mask) { + dev_err(dai->dev, "%s: invalid rx ch mask\n", __func__); + return -EINVAL; + } + if (rx_num_ch > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "%s: invalid rx_num_ch %d\n", + __func__, rx_num_ch); + return -EINVAL; + } + ch_mask = *rx_ch_mask; + ch_num = rx_num_ch; + break; + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_2: + case AFE_PORT_ID_VA_CODEC_DMA_TX_0: + case AFE_PORT_ID_VA_CODEC_DMA_TX_1: + if (!tx_ch_mask) { + dev_err(dai->dev, "%s: invalid tx ch mask\n", __func__); + return -EINVAL; + } + if (tx_num_ch > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "%s: invalid tx_num_ch %d\n", + __func__, tx_num_ch); + return -EINVAL; + } + ch_mask = *tx_ch_mask; + ch_num = tx_num_ch; + break; + default: + dev_err(dai->dev, "%s: invalid dai id %d\n", __func__, dai->id); + return -EINVAL; + } + + dai_data->port_config.cdc_dma.active_channels_mask = ch_mask; + dev_dbg(dai->dev, "%s: CDC_DMA_%d_ch cnt[%d] ch mask[0x%x]\n", __func__, + dai->id, ch_num, ch_mask); + return 0; +} + +static int msm_dai_q6_cdc_dma_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_cdc_dma_dai_data *dai_data = + dev_get_drvdata(dai->dev); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_SPECIAL: + dai_data->port_config.cdc_dma.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->port_config.cdc_dma.bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->port_config.cdc_dma.bit_width = 32; + break; + default: + dev_err(dai->dev, "%s: format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + + dai_data->port_config.cdc_dma.cdc_dma_cfg_minor_version = + AFE_API_VERSION_CODEC_DMA_CONFIG; + dai_data->port_config.cdc_dma.sample_rate = dai_data->rate; + dai_data->port_config.cdc_dma.num_channels = dai_data->channels; + dev_dbg(dai->dev, "%s: bit_wd[%hu] format[%hu]\n" + "num_channel %hu sample_rate %d\n", __func__, + dai_data->port_config.cdc_dma.bit_width, + dai_data->port_config.cdc_dma.data_format, + dai_data->port_config.cdc_dma.num_channels, + dai_data->rate); + + return 0; +} + +static int msm_dai_q6_cdc_dma_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_cdc_dma_dai_data *dai_data = + dev_get_drvdata(dai->dev); + int rc = 0; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_port_start(dai->id, &dai_data->port_config, + dai_data->rate); + if (rc < 0) + dev_err(dai->dev, "fail to open AFE port 0x%x\n", + dai->id); + else + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + } + return rc; +} + + +static void msm_dai_q6_cdc_dma_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)) { + dev_dbg(dai->dev, "%s: stop AFE port:%d\n", __func__, + dai->id); + rc = afe_close(dai->id); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + + dev_dbg(dai->dev, "%s: dai_data->status_mask = %ld\n", __func__, + *dai_data->status_mask); + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + + if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) + clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status); +} + + +static struct snd_soc_dai_ops msm_dai_q6_cdc_dma_ops = { + .prepare = msm_dai_q6_cdc_dma_prepare, + .hw_params = msm_dai_q6_cdc_dma_hw_params, + .shutdown = msm_dai_q6_cdc_dma_shutdown, + .set_channel_map = msm_dai_q6_cdc_dma_set_channel_map, +}; + +static struct snd_soc_dai_driver msm_dai_q6_cdc_dma_dai[] = { + { + .playback = { + .stream_name = "WSA CDC DMA0 Playback", + .aif_name = "WSA_CDC_DMA_RX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_WSA_CODEC_DMA_RX_0, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "WSA CDC DMA0 Capture", + .aif_name = "WSA_CDC_DMA_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_WSA_CODEC_DMA_TX_0, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .playback = { + .stream_name = "WSA CDC DMA1 Playback", + .aif_name = "WSA_CDC_DMA_RX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_WSA_CODEC_DMA_RX_1, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "WSA CDC DMA1 Capture", + .aif_name = "WSA_CDC_DMA_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_WSA_CODEC_DMA_TX_1, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "WSA CDC DMA2 Capture", + .aif_name = "WSA_CDC_DMA_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_WSA_CODEC_DMA_TX_2, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "VA CDC DMA0 Capture", + .aif_name = "VA_CDC_DMA_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_VA_CODEC_DMA_TX_0, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "VA CDC DMA1 Capture", + .aif_name = "VA_CDC_DMA_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_VA_CODEC_DMA_TX_1, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + +}; + +static const struct snd_soc_component_driver msm_q6_cdc_dma_dai_component = { + .name = "msm-dai-cdc-dma-dev", +}; + +/* DT related probe for each codec DMA interface device */ +static int msm_dai_q6_cdc_dma_dev_probe(struct platform_device *pdev) +{ + const char *q6_cdc_dma_dev_id = "qcom,msm-dai-cdc-dma-dev-id"; + u16 cdc_dma_id = 0; + int i; + int rc = 0; + struct msm_dai_q6_cdc_dma_dai_data *dai_data = NULL; + + rc = of_property_read_u16(pdev->dev.of_node, q6_cdc_dma_dev_id, + &cdc_dma_id); + if (rc) { + dev_err(&pdev->dev, + "%s: missing 0x%x in dt node\n", __func__, cdc_dma_id); + return rc; + } + + dev_dbg(&pdev->dev, "%s: dev name %s dev id 0x%x\n", __func__, + dev_name(&pdev->dev), cdc_dma_id); + + pdev->id = cdc_dma_id; + + dai_data = devm_kzalloc(&pdev->dev, + sizeof(struct msm_dai_q6_cdc_dma_dai_data), + GFP_KERNEL); + + if (!dai_data) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, dai_data); + + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_cdc_dma_dai); i++) { + if (msm_dai_q6_cdc_dma_dai[i].id == cdc_dma_id) { + return snd_soc_register_component(&pdev->dev, + &msm_q6_cdc_dma_dai_component, + &msm_dai_q6_cdc_dma_dai[i], 1); + } + } + return -ENODEV; +} + +static int msm_dai_q6_cdc_dma_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_q6_cdc_dma_dev_dt_match[] = { + { .compatible = "qcom,msm-dai-cdc-dma-dev", }, + { } +}; + +MODULE_DEVICE_TABLE(of, msm_dai_q6_cdc_dma_dev_dt_match); + +static struct platform_driver msm_dai_q6_cdc_dma_driver = { + .probe = msm_dai_q6_cdc_dma_dev_probe, + .remove = msm_dai_q6_cdc_dma_dev_remove, + .driver = { + .name = "msm-dai-cdc-dma-dev", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_cdc_dma_dev_dt_match, + }, +}; + +/* DT related probe for codec DMA interface device group */ +static int msm_dai_cdc_dma_q6_probe(struct platform_device *pdev) +{ + int rc; + + 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_cdc_dma_q6_remove(struct platform_device *pdev) +{ + of_platform_depopulate(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_cdc_dma_dt_match[] = { + { .compatible = "qcom,msm-dai-cdc-dma", }, + { } +}; + +MODULE_DEVICE_TABLE(of, msm_dai_cdc_dma_dt_match); + +static struct platform_driver msm_dai_cdc_dma_q6 = { + .probe = msm_dai_cdc_dma_q6_probe, + .remove = msm_dai_cdc_dma_q6_remove, + .driver = { + .name = "msm-dai-cdc-dma", + .owner = THIS_MODULE, + .of_match_table = msm_dai_cdc_dma_dt_match, + }, +}; + int __init msm_dai_q6_init(void) { int rc; @@ -9099,8 +9623,25 @@ int __init msm_dai_q6_init(void) pr_err("%s: fail to register dai TDM\n", __func__); goto dai_tdm_q6_fail; } + + rc = platform_driver_register(&msm_dai_q6_cdc_dma_driver); + if (rc) { + pr_err("%s: fail to register dai CDC DMA dev\n", __func__); + goto dai_cdc_dma_q6_dev_fail; + } + + + rc = platform_driver_register(&msm_dai_cdc_dma_q6); + if (rc) { + pr_err("%s: fail to register dai CDC DMA\n", __func__); + goto dai_cdc_dma_q6_fail; + } return rc; +dai_cdc_dma_q6_fail: + platform_driver_unregister(&msm_dai_q6_cdc_dma_driver); +dai_cdc_dma_q6_dev_fail: + platform_driver_unregister(&msm_dai_tdm_q6); dai_tdm_q6_fail: platform_driver_unregister(&msm_dai_q6_tdm_driver); dai_q6_tdm_drv_fail: @@ -9121,6 +9662,8 @@ int __init msm_dai_q6_init(void) void msm_dai_q6_exit(void) { + platform_driver_unregister(&msm_dai_cdc_dma_q6); + platform_driver_unregister(&msm_dai_q6_cdc_dma_driver); platform_driver_unregister(&msm_dai_tdm_q6); platform_driver_unregister(&msm_dai_q6_tdm_driver); platform_driver_unregister(&msm_dai_q6_spdif_driver);