1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. 4 */ 5 6 #include <linux/auxiliary_bus.h> 7 #include <linux/device.h> 8 #include <linux/dma-mapping.h> 9 #include <linux/dma-map-ops.h> 10 #include <linux/err.h> 11 #include <linux/init.h> 12 #include <linux/iommu.h> 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 #include <linux/slab.h> 16 17 #include <sound/asound.h> 18 #include <sound/jack.h> 19 #include <sound/pcm.h> 20 #include <sound/pcm_params.h> 21 #include <sound/q6usboffload.h> 22 #include <sound/soc.h> 23 #include <sound/soc-usb.h> 24 25 #include <dt-bindings/sound/qcom,q6afe.h> 26 27 #include "q6afe.h" 28 #include "q6dsp-lpass-ports.h" 29 30 #define Q6_USB_SID_MASK 0xF 31 32 struct q6usb_port_data { 33 struct auxiliary_device uauxdev; 34 struct q6afe_usb_cfg usb_cfg; 35 struct snd_soc_usb *usb; 36 struct snd_soc_jack *hs_jack; 37 struct q6usb_offload priv; 38 39 /* Protects against operations between SOC USB and ASoC */ 40 struct mutex mutex; 41 struct list_head devices; 42 }; 43 44 static const struct snd_soc_dapm_widget q6usb_dai_widgets[] = { 45 SND_SOC_DAPM_HP("USB_RX_BE", NULL), 46 }; 47 48 static const struct snd_soc_dapm_route q6usb_dapm_routes[] = { 49 {"USB Playback", NULL, "USB_RX_BE"}, 50 }; 51 52 static int q6usb_hw_params(struct snd_pcm_substream *substream, 53 struct snd_pcm_hw_params *params, 54 struct snd_soc_dai *dai) 55 { 56 struct q6usb_port_data *data = dev_get_drvdata(dai->dev); 57 struct snd_soc_pcm_runtime *rtd = substream->private_data; 58 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 59 int direction = substream->stream; 60 struct q6afe_port *q6usb_afe; 61 struct snd_soc_usb_device *sdev; 62 int ret = -EINVAL; 63 64 mutex_lock(&data->mutex); 65 66 /* No active chip index */ 67 if (list_empty(&data->devices)) 68 goto out; 69 70 sdev = list_last_entry(&data->devices, struct snd_soc_usb_device, list); 71 72 ret = snd_soc_usb_find_supported_format(sdev->chip_idx, params, direction); 73 if (ret < 0) 74 goto out; 75 76 q6usb_afe = q6afe_port_get_from_id(cpu_dai->dev, USB_RX); 77 if (IS_ERR(q6usb_afe)) 78 goto out; 79 80 /* Notify audio DSP about the devices being offloaded */ 81 ret = afe_port_send_usb_dev_param(q6usb_afe, sdev->card_idx, 82 sdev->ppcm_idx[sdev->num_playback - 1]); 83 84 out: 85 mutex_unlock(&data->mutex); 86 87 return ret; 88 } 89 90 static const struct snd_soc_dai_ops q6usb_ops = { 91 .hw_params = q6usb_hw_params, 92 }; 93 94 static struct snd_soc_dai_driver q6usb_be_dais[] = { 95 { 96 .playback = { 97 .stream_name = "USB BE RX", 98 .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | 99 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | 100 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | 101 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | 102 SNDRV_PCM_RATE_192000, 103 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | 104 SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | 105 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | 106 SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE, 107 .channels_min = 1, 108 .channels_max = 2, 109 .rate_max = 192000, 110 .rate_min = 8000, 111 }, 112 .id = USB_RX, 113 .name = "USB_RX_BE", 114 .ops = &q6usb_ops, 115 }, 116 }; 117 118 static int q6usb_audio_ports_of_xlate_dai_name(struct snd_soc_component *component, 119 const struct of_phandle_args *args, 120 const char **dai_name) 121 { 122 int id = args->args[0]; 123 int ret = -EINVAL; 124 int i; 125 126 for (i = 0; i < ARRAY_SIZE(q6usb_be_dais); i++) { 127 if (q6usb_be_dais[i].id == id) { 128 *dai_name = q6usb_be_dais[i].name; 129 ret = 0; 130 break; 131 } 132 } 133 134 return ret; 135 } 136 137 static int q6usb_get_pcm_id_from_widget(struct snd_soc_dapm_widget *w) 138 { 139 struct snd_soc_pcm_runtime *rtd; 140 struct snd_soc_dai *dai; 141 142 for_each_card_rtds(w->dapm->card, rtd) { 143 dai = snd_soc_rtd_to_cpu(rtd, 0); 144 /* 145 * Only look for playback widget. RTD number carries the assigned 146 * PCM index. 147 */ 148 if (dai->stream[0].widget == w) 149 return rtd->id; 150 } 151 152 return -1; 153 } 154 155 static int q6usb_usb_mixer_enabled(struct snd_soc_dapm_widget *w) 156 { 157 struct snd_soc_dapm_path *p; 158 159 /* Checks to ensure USB path is enabled/connected */ 160 snd_soc_dapm_widget_for_each_sink_path(w, p) 161 if (!strcmp(p->sink->name, "USB Mixer") && p->connect) 162 return 1; 163 164 return 0; 165 } 166 167 static int q6usb_get_pcm_id(struct snd_soc_component *component) 168 { 169 struct snd_soc_dapm_widget *w; 170 struct snd_soc_dapm_path *p; 171 int pidx; 172 173 /* 174 * Traverse widgets to find corresponding FE widget. The DAI links are 175 * built like the following: 176 * MultiMedia* <-> MM_DL* <-> USB Mixer* 177 */ 178 for_each_card_widgets(component->card, w) { 179 if (!strncmp(w->name, "MultiMedia", 10)) { 180 /* 181 * Look up all paths associated with the FE widget to see if 182 * the USB BE is enabled. The sink widget is responsible to 183 * link with the USB mixers. 184 */ 185 snd_soc_dapm_widget_for_each_sink_path(w, p) { 186 if (q6usb_usb_mixer_enabled(p->sink)) { 187 pidx = q6usb_get_pcm_id_from_widget(w); 188 return pidx; 189 } 190 } 191 } 192 } 193 194 return -1; 195 } 196 197 static int q6usb_update_offload_route(struct snd_soc_component *component, int card, 198 int pcm, int direction, enum snd_soc_usb_kctl path, 199 long *route) 200 { 201 struct q6usb_port_data *data = dev_get_drvdata(component->dev); 202 struct snd_soc_usb_device *sdev; 203 int ret = 0; 204 int idx = -1; 205 206 mutex_lock(&data->mutex); 207 208 if (list_empty(&data->devices) || 209 direction == SNDRV_PCM_STREAM_CAPTURE) { 210 ret = -ENODEV; 211 goto out; 212 } 213 214 sdev = list_last_entry(&data->devices, struct snd_soc_usb_device, list); 215 216 /* 217 * Will always look for last PCM device discovered/probed as the 218 * active offload index. 219 */ 220 if (card == sdev->card_idx && 221 pcm == sdev->ppcm_idx[sdev->num_playback - 1]) { 222 idx = path == SND_SOC_USB_KCTL_CARD_ROUTE ? 223 component->card->snd_card->number : 224 q6usb_get_pcm_id(component); 225 } 226 227 out: 228 route[0] = idx; 229 mutex_unlock(&data->mutex); 230 231 return ret; 232 } 233 234 static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb, 235 struct snd_soc_usb_device *sdev, bool connected) 236 { 237 struct q6usb_port_data *data; 238 239 if (!usb->component) 240 return -ENODEV; 241 242 data = dev_get_drvdata(usb->component->dev); 243 244 mutex_lock(&data->mutex); 245 if (connected) { 246 if (data->hs_jack) 247 snd_jack_report(data->hs_jack->jack, SND_JACK_USB); 248 249 /* Selects the latest USB headset plugged in for offloading */ 250 list_add_tail(&sdev->list, &data->devices); 251 } else { 252 list_del(&sdev->list); 253 254 if (data->hs_jack) 255 snd_jack_report(data->hs_jack->jack, 0); 256 } 257 mutex_unlock(&data->mutex); 258 259 return 0; 260 } 261 262 static void q6usb_component_disable_jack(struct q6usb_port_data *data) 263 { 264 /* Offload jack has already been disabled */ 265 if (!data->hs_jack) 266 return; 267 268 snd_jack_report(data->hs_jack->jack, 0); 269 data->hs_jack = NULL; 270 } 271 272 static void q6usb_component_enable_jack(struct q6usb_port_data *data, 273 struct snd_soc_jack *jack) 274 { 275 snd_jack_report(jack->jack, !list_empty(&data->devices) ? SND_JACK_USB : 0); 276 data->hs_jack = jack; 277 } 278 279 static int q6usb_component_set_jack(struct snd_soc_component *component, 280 struct snd_soc_jack *jack, void *priv) 281 { 282 struct q6usb_port_data *data = dev_get_drvdata(component->dev); 283 284 mutex_lock(&data->mutex); 285 if (jack) 286 q6usb_component_enable_jack(data, jack); 287 else 288 q6usb_component_disable_jack(data); 289 mutex_unlock(&data->mutex); 290 291 return 0; 292 } 293 294 static void q6usb_dai_aux_release(struct device *dev) {} 295 296 static int q6usb_dai_add_aux_device(struct q6usb_port_data *data, 297 struct auxiliary_device *auxdev) 298 { 299 int ret; 300 301 auxdev->dev.parent = data->priv.dev; 302 auxdev->dev.release = q6usb_dai_aux_release; 303 auxdev->name = "qc-usb-audio-offload"; 304 305 ret = auxiliary_device_init(auxdev); 306 if (ret) 307 return ret; 308 309 ret = auxiliary_device_add(auxdev); 310 if (ret) 311 auxiliary_device_uninit(auxdev); 312 313 return ret; 314 } 315 316 static int q6usb_component_probe(struct snd_soc_component *component) 317 { 318 struct q6usb_port_data *data = dev_get_drvdata(component->dev); 319 struct snd_soc_usb *usb; 320 int ret; 321 322 /* Add the QC USB SND aux device */ 323 ret = q6usb_dai_add_aux_device(data, &data->uauxdev); 324 if (ret < 0) 325 return ret; 326 327 usb = snd_soc_usb_allocate_port(component, &data->priv); 328 if (IS_ERR(usb)) 329 return -ENOMEM; 330 331 usb->connection_status_cb = q6usb_alsa_connection_cb; 332 usb->update_offload_route_info = q6usb_update_offload_route; 333 334 snd_soc_usb_add_port(usb); 335 data->usb = usb; 336 337 return 0; 338 } 339 340 static void q6usb_component_remove(struct snd_soc_component *component) 341 { 342 struct q6usb_port_data *data = dev_get_drvdata(component->dev); 343 344 snd_soc_usb_remove_port(data->usb); 345 auxiliary_device_delete(&data->uauxdev); 346 auxiliary_device_uninit(&data->uauxdev); 347 snd_soc_usb_free_port(data->usb); 348 } 349 350 static const struct snd_soc_component_driver q6usb_dai_component = { 351 .probe = q6usb_component_probe, 352 .set_jack = q6usb_component_set_jack, 353 .remove = q6usb_component_remove, 354 .name = "q6usb-dai-component", 355 .dapm_widgets = q6usb_dai_widgets, 356 .num_dapm_widgets = ARRAY_SIZE(q6usb_dai_widgets), 357 .dapm_routes = q6usb_dapm_routes, 358 .num_dapm_routes = ARRAY_SIZE(q6usb_dapm_routes), 359 .of_xlate_dai_name = q6usb_audio_ports_of_xlate_dai_name, 360 }; 361 362 static int q6usb_dai_dev_probe(struct platform_device *pdev) 363 { 364 struct device_node *node = pdev->dev.of_node; 365 struct q6usb_port_data *data; 366 struct device *dev = &pdev->dev; 367 struct of_phandle_args args; 368 int ret; 369 370 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 371 if (!data) 372 return -ENOMEM; 373 374 ret = of_property_read_u16(node, "qcom,usb-audio-intr-idx", 375 &data->priv.intr_num); 376 if (ret) { 377 dev_err(&pdev->dev, "failed to read intr idx.\n"); 378 return ret; 379 } 380 381 ret = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args); 382 if (!ret) 383 data->priv.sid = args.args[0] & Q6_USB_SID_MASK; 384 385 ret = devm_mutex_init(dev, &data->mutex); 386 if (ret < 0) 387 return ret; 388 389 data->priv.domain = iommu_get_domain_for_dev(&pdev->dev); 390 391 data->priv.dev = dev; 392 INIT_LIST_HEAD(&data->devices); 393 dev_set_drvdata(dev, data); 394 395 return devm_snd_soc_register_component(dev, &q6usb_dai_component, 396 q6usb_be_dais, ARRAY_SIZE(q6usb_be_dais)); 397 } 398 399 static const struct of_device_id q6usb_dai_device_id[] = { 400 { .compatible = "qcom,q6usb" }, 401 {}, 402 }; 403 MODULE_DEVICE_TABLE(of, q6usb_dai_device_id); 404 405 static struct platform_driver q6usb_dai_platform_driver = { 406 .driver = { 407 .name = "q6usb-dai", 408 .of_match_table = of_match_ptr(q6usb_dai_device_id), 409 }, 410 .probe = q6usb_dai_dev_probe, 411 /* 412 * Remove not required as resources are cleaned up as part of 413 * component removal. Others are device managed resources. 414 */ 415 }; 416 module_platform_driver(q6usb_dai_platform_driver); 417 418 MODULE_DESCRIPTION("Q6 USB backend dai driver"); 419 MODULE_LICENSE("GPL"); 420