0001
0002
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
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
0132
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");