Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright (c) 2021, Linaro Limited
0003 
0004 #include <linux/err.h>
0005 #include <linux/init.h>
0006 #include <linux/module.h>
0007 #include <linux/device.h>
0008 #include <linux/platform_device.h>
0009 #include <linux/slab.h>
0010 #include <sound/pcm.h>
0011 #include <sound/soc.h>
0012 #include <sound/pcm_params.h>
0013 #include "q6dsp-lpass-ports.h"
0014 #include "audioreach.h"
0015 #include "q6apm.h"
0016 
0017 #define AUDIOREACH_BE_PCM_BASE  16
0018 
0019 struct q6apm_lpass_dai_data {
0020     struct q6apm_graph *graph[APM_PORT_MAX];
0021     bool is_port_started[APM_PORT_MAX];
0022     struct audioreach_module_config module_config[APM_PORT_MAX];
0023 };
0024 
0025 static int q6dma_set_channel_map(struct snd_soc_dai *dai,
0026                  unsigned int tx_num, unsigned int *tx_ch_mask,
0027                  unsigned int rx_num, unsigned int *rx_ch_mask)
0028 {
0029 
0030     struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
0031     struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
0032     int ch_mask;
0033 
0034     switch (dai->id) {
0035     case WSA_CODEC_DMA_TX_0:
0036     case WSA_CODEC_DMA_TX_1:
0037     case WSA_CODEC_DMA_TX_2:
0038     case VA_CODEC_DMA_TX_0:
0039     case VA_CODEC_DMA_TX_1:
0040     case VA_CODEC_DMA_TX_2:
0041     case TX_CODEC_DMA_TX_0:
0042     case TX_CODEC_DMA_TX_1:
0043     case TX_CODEC_DMA_TX_2:
0044     case TX_CODEC_DMA_TX_3:
0045     case TX_CODEC_DMA_TX_4:
0046     case TX_CODEC_DMA_TX_5:
0047         if (!tx_ch_mask) {
0048             dev_err(dai->dev, "tx slot not found\n");
0049             return -EINVAL;
0050         }
0051 
0052         if (tx_num > AR_PCM_MAX_NUM_CHANNEL) {
0053             dev_err(dai->dev, "invalid tx num %d\n",
0054                 tx_num);
0055             return -EINVAL;
0056         }
0057         ch_mask = *tx_ch_mask;
0058 
0059         break;
0060     case WSA_CODEC_DMA_RX_0:
0061     case WSA_CODEC_DMA_RX_1:
0062     case RX_CODEC_DMA_RX_0:
0063     case RX_CODEC_DMA_RX_1:
0064     case RX_CODEC_DMA_RX_2:
0065     case RX_CODEC_DMA_RX_3:
0066     case RX_CODEC_DMA_RX_4:
0067     case RX_CODEC_DMA_RX_5:
0068     case RX_CODEC_DMA_RX_6:
0069     case RX_CODEC_DMA_RX_7:
0070         /* rx */
0071         if (!rx_ch_mask) {
0072             dev_err(dai->dev, "rx slot not found\n");
0073             return -EINVAL;
0074         }
0075         if (rx_num > APM_PORT_MAX_AUDIO_CHAN_CNT) {
0076             dev_err(dai->dev, "invalid rx num %d\n",
0077                 rx_num);
0078             return -EINVAL;
0079         }
0080         ch_mask = *rx_ch_mask;
0081 
0082         break;
0083     default:
0084         dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
0085             __func__, dai->id);
0086         return -EINVAL;
0087     }
0088 
0089     cfg->active_channels_mask = ch_mask;
0090 
0091     return 0;
0092 }
0093 
0094 static int q6dma_hw_params(struct snd_pcm_substream *substream,
0095                struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
0096 {
0097     struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
0098     struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
0099 
0100     cfg->bit_width = params_width(params);
0101     cfg->sample_rate = params_rate(params);
0102     cfg->num_channels = params_channels(params);
0103 
0104     return 0;
0105 }
0106 
0107 static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
0108 {
0109     struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
0110     int rc;
0111 
0112     if (!dai_data->is_port_started[dai->id])
0113         return;
0114     rc = q6apm_graph_stop(dai_data->graph[dai->id]);
0115     if (rc < 0)
0116         dev_err(dai->dev, "fail to close APM port (%d)\n", rc);
0117 
0118     q6apm_graph_close(dai_data->graph[dai->id]);
0119     dai_data->is_port_started[dai->id] = false;
0120 }
0121 
0122 static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
0123 {
0124     struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
0125     struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
0126     struct q6apm_graph *graph;
0127     int graph_id = dai->id;
0128     int rc;
0129 
0130     /**
0131      * It is recommend to load DSP with source graph first and then sink
0132      * graph, so sequence for playback and capture will be different
0133      */
0134     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
0135         graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id);
0136         if (IS_ERR(graph)) {
0137             dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id);
0138             rc = PTR_ERR(graph);
0139             return rc;
0140         }
0141         dai_data->graph[graph_id] = graph;
0142     }
0143 
0144     cfg->direction = substream->stream;
0145     rc = q6apm_graph_media_format_pcm(dai_data->graph[dai->id], cfg);
0146 
0147     if (rc) {
0148         dev_err(dai->dev, "Failed to set media format %d\n", rc);
0149         return rc;
0150     }
0151 
0152     rc = q6apm_graph_prepare(dai_data->graph[dai->id]);
0153     if (rc) {
0154         dev_err(dai->dev, "Failed to prepare Graph %d\n", rc);
0155         return rc;
0156     }
0157 
0158     rc = q6apm_graph_start(dai_data->graph[dai->id]);
0159     if (rc < 0) {
0160         dev_err(dai->dev, "fail to start APM port %x\n", dai->id);
0161         return rc;
0162     }
0163     dai_data->is_port_started[dai->id] = true;
0164 
0165     return 0;
0166 }
0167 
0168 static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
0169 {
0170     struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
0171     struct q6apm_graph *graph;
0172     int graph_id = dai->id;
0173 
0174     if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
0175         graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id);
0176         if (IS_ERR(graph)) {
0177             dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id);
0178             return PTR_ERR(graph);
0179         }
0180         dai_data->graph[graph_id] = graph;
0181     }
0182 
0183     return 0;
0184 }
0185 
0186 static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
0187 {
0188     struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
0189     struct audioreach_module_config *cfg = &dai_data->module_config[dai->id];
0190 
0191     cfg->fmt = fmt;
0192 
0193     return 0;
0194 }
0195 
0196 static const struct snd_soc_dai_ops q6dma_ops = {
0197     .prepare    = q6apm_lpass_dai_prepare,
0198     .startup    = q6apm_lpass_dai_startup,
0199     .shutdown   = q6apm_lpass_dai_shutdown,
0200     .set_channel_map  = q6dma_set_channel_map,
0201     .hw_params        = q6dma_hw_params,
0202 };
0203 
0204 static const struct snd_soc_dai_ops q6i2s_ops = {
0205     .prepare    = q6apm_lpass_dai_prepare,
0206     .startup    = q6apm_lpass_dai_startup,
0207     .shutdown   = q6apm_lpass_dai_shutdown,
0208     .set_channel_map  = q6dma_set_channel_map,
0209     .hw_params        = q6dma_hw_params,
0210     .set_fmt    = q6i2s_set_fmt,
0211 };
0212 
0213 static const struct snd_soc_component_driver q6apm_lpass_dai_component = {
0214     .name = "q6apm-be-dai-component",
0215     .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name,
0216     .be_pcm_base = AUDIOREACH_BE_PCM_BASE,
0217     .use_dai_pcm_id = true,
0218 };
0219 
0220 static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev)
0221 {
0222     struct q6dsp_audio_port_dai_driver_config cfg;
0223     struct q6apm_lpass_dai_data *dai_data;
0224     struct snd_soc_dai_driver *dais;
0225     struct device *dev = &pdev->dev;
0226     int num_dais;
0227 
0228     dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL);
0229     if (!dai_data)
0230         return -ENOMEM;
0231 
0232     dev_set_drvdata(dev, dai_data);
0233 
0234     memset(&cfg, 0, sizeof(cfg));
0235     cfg.q6i2s_ops = &q6i2s_ops;
0236     cfg.q6dma_ops = &q6dma_ops;
0237     dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais);
0238 
0239     return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais);
0240 }
0241 
0242 #ifdef CONFIG_OF
0243 static const struct of_device_id q6apm_lpass_dai_device_id[] = {
0244     { .compatible = "qcom,q6apm-lpass-dais" },
0245     {},
0246 };
0247 MODULE_DEVICE_TABLE(of, q6apm_lpass_dai_device_id);
0248 #endif
0249 
0250 static struct platform_driver q6apm_lpass_dai_platform_driver = {
0251     .driver = {
0252         .name = "q6apm-lpass-dais",
0253         .of_match_table = of_match_ptr(q6apm_lpass_dai_device_id),
0254     },
0255     .probe = q6apm_lpass_dai_dev_probe,
0256 };
0257 module_platform_driver(q6apm_lpass_dai_platform_driver);
0258 
0259 MODULE_DESCRIPTION("AUDIOREACH APM LPASS dai driver");
0260 MODULE_LICENSE("GPL");