Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: SOF: imx: introduce more common structures and functions

The SOF drivers for imx chips have a lot of duplicate code and
routines/code snippets that could certainly be reused among drivers.

As such, introduce a new set of structures and functions that will help
eliminate the redundancy and code size of the drivers.

Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20250207162246.3104-2-laurentiumihalcea111@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Laurentiu Mihalcea and committed by
Mark Brown
651e0ed3 f98d4200

+581 -1
+430 -1
sound/soc/sof/imx/imx-common.c
··· 1 1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 2 // 3 - // Copyright 2020 NXP 3 + // Copyright 2020-2025 NXP 4 4 // 5 5 // Common helpers for the audio DSP on i.MX8 6 6 7 + #include <linux/firmware/imx/dsp.h> 7 8 #include <linux/module.h> 9 + #include <linux/of_address.h> 10 + #include <linux/of_reserved_mem.h> 11 + #include <linux/pm_domain.h> 8 12 #include <sound/sof/xtensa.h> 13 + 9 14 #include "../ops.h" 10 15 11 16 #include "imx-common.h" ··· 78 73 &panic_info, stack, IMX8_STACK_DUMP_SIZE); 79 74 } 80 75 EXPORT_SYMBOL(imx8_dump); 76 + 77 + static void imx_handle_reply(struct imx_dsp_ipc *ipc) 78 + { 79 + struct snd_sof_dev *sdev; 80 + unsigned long flags; 81 + 82 + sdev = imx_dsp_get_data(ipc); 83 + 84 + spin_lock_irqsave(&sdev->ipc_lock, flags); 85 + snd_sof_ipc_process_reply(sdev, 0); 86 + spin_unlock_irqrestore(&sdev->ipc_lock, flags); 87 + } 88 + 89 + static void imx_handle_request(struct imx_dsp_ipc *ipc) 90 + { 91 + struct snd_sof_dev *sdev; 92 + u32 panic_code; 93 + 94 + sdev = imx_dsp_get_data(ipc); 95 + 96 + if (get_chip_info(sdev)->ipc_info.has_panic_code) { 97 + sof_mailbox_read(sdev, sdev->debug_box.offset + 0x4, 98 + &panic_code, 99 + sizeof(panic_code)); 100 + 101 + if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { 102 + snd_sof_dsp_panic(sdev, panic_code, true); 103 + return; 104 + } 105 + } 106 + 107 + snd_sof_ipc_msgs_rx(sdev); 108 + } 109 + 110 + static struct imx_dsp_ops imx_ipc_ops = { 111 + .handle_reply = imx_handle_reply, 112 + .handle_request = imx_handle_request, 113 + }; 114 + 115 + static int imx_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) 116 + { 117 + struct imx_common_data *common = sdev->pdata->hw_pdata; 118 + 119 + sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, msg->msg_size); 120 + imx_dsp_ring_doorbell(common->ipc_handle, 0x0); 121 + 122 + return 0; 123 + } 124 + 125 + static int imx_get_bar_index(struct snd_sof_dev *sdev, u32 type) 126 + { 127 + switch (type) { 128 + case SOF_FW_BLK_TYPE_IRAM: 129 + case SOF_FW_BLK_TYPE_SRAM: 130 + return type; 131 + default: 132 + return -EINVAL; 133 + } 134 + } 135 + 136 + static int imx_get_mailbox_offset(struct snd_sof_dev *sdev) 137 + { 138 + return get_chip_info(sdev)->ipc_info.boot_mbox_offset; 139 + } 140 + 141 + static int imx_get_window_offset(struct snd_sof_dev *sdev, u32 id) 142 + { 143 + return get_chip_info(sdev)->ipc_info.window_offset; 144 + } 145 + 146 + static int imx_set_power_state(struct snd_sof_dev *sdev, 147 + const struct sof_dsp_power_state *target) 148 + { 149 + sdev->dsp_power_state = *target; 150 + 151 + return 0; 152 + } 153 + 154 + static int imx_common_resume(struct snd_sof_dev *sdev) 155 + { 156 + struct imx_common_data *common; 157 + int ret, i; 158 + 159 + common = sdev->pdata->hw_pdata; 160 + 161 + ret = clk_bulk_prepare_enable(common->clk_num, common->clks); 162 + if (ret) 163 + dev_err(sdev->dev, "failed to enable clocks: %d\n", ret); 164 + 165 + for (i = 0; i < DSP_MU_CHAN_NUM; i++) 166 + imx_dsp_request_channel(common->ipc_handle, i); 167 + 168 + /* done. If need be, core will be started by SOF core immediately after */ 169 + return 0; 170 + } 171 + 172 + static int imx_common_suspend(struct snd_sof_dev *sdev) 173 + { 174 + struct imx_common_data *common; 175 + int i, ret; 176 + 177 + common = sdev->pdata->hw_pdata; 178 + 179 + ret = imx_chip_core_shutdown(sdev); 180 + if (ret < 0) { 181 + dev_err(sdev->dev, "failed to shutdown core: %d\n", ret); 182 + return ret; 183 + } 184 + 185 + for (i = 0; i < DSP_MU_CHAN_NUM; i++) 186 + imx_dsp_free_channel(common->ipc_handle, i); 187 + 188 + clk_bulk_disable_unprepare(common->clk_num, common->clks); 189 + 190 + return 0; 191 + } 192 + 193 + static int imx_runtime_resume(struct snd_sof_dev *sdev) 194 + { 195 + const struct sof_dsp_power_state target_state = { 196 + .state = SOF_DSP_PM_D0, 197 + }; 198 + int ret; 199 + 200 + ret = imx_common_resume(sdev); 201 + if (ret < 0) { 202 + dev_err(sdev->dev, "failed to runtime common resume: %d\n", ret); 203 + return ret; 204 + } 205 + 206 + return snd_sof_dsp_set_power_state(sdev, &target_state); 207 + } 208 + 209 + static int imx_resume(struct snd_sof_dev *sdev) 210 + { 211 + const struct sof_dsp_power_state target_state = { 212 + .state = SOF_DSP_PM_D0, 213 + }; 214 + int ret; 215 + 216 + ret = imx_common_resume(sdev); 217 + if (ret < 0) { 218 + dev_err(sdev->dev, "failed to common resume: %d\n", ret); 219 + return ret; 220 + } 221 + 222 + if (pm_runtime_suspended(sdev->dev)) { 223 + pm_runtime_disable(sdev->dev); 224 + pm_runtime_set_active(sdev->dev); 225 + pm_runtime_mark_last_busy(sdev->dev); 226 + pm_runtime_enable(sdev->dev); 227 + pm_runtime_idle(sdev->dev); 228 + } 229 + 230 + return snd_sof_dsp_set_power_state(sdev, &target_state); 231 + } 232 + 233 + static int imx_runtime_suspend(struct snd_sof_dev *sdev) 234 + { 235 + const struct sof_dsp_power_state target_state = { 236 + .state = SOF_DSP_PM_D3, 237 + }; 238 + int ret; 239 + 240 + ret = imx_common_suspend(sdev); 241 + if (ret < 0) 242 + dev_err(sdev->dev, "failed to runtime common suspend: %d\n", ret); 243 + 244 + return snd_sof_dsp_set_power_state(sdev, &target_state); 245 + } 246 + 247 + static int imx_suspend(struct snd_sof_dev *sdev, unsigned int target_state) 248 + { 249 + const struct sof_dsp_power_state target_power_state = { 250 + .state = target_state, 251 + }; 252 + int ret; 253 + 254 + if (!pm_runtime_suspended(sdev->dev)) { 255 + ret = imx_common_suspend(sdev); 256 + if (ret < 0) { 257 + dev_err(sdev->dev, "failed to common suspend: %d\n", ret); 258 + return ret; 259 + } 260 + } 261 + 262 + return snd_sof_dsp_set_power_state(sdev, &target_power_state); 263 + } 264 + 265 + static int imx_region_name_to_blk_type(const char *region_name) 266 + { 267 + if (!strcmp(region_name, "iram")) 268 + return SOF_FW_BLK_TYPE_IRAM; 269 + else if (!strcmp(region_name, "dram")) 270 + return SOF_FW_BLK_TYPE_DRAM; 271 + else if (!strcmp(region_name, "sram")) 272 + return SOF_FW_BLK_TYPE_SRAM; 273 + else 274 + return -EINVAL; 275 + } 276 + 277 + static int imx_parse_ioremap_memory(struct snd_sof_dev *sdev) 278 + { 279 + const struct imx_chip_info *chip_info; 280 + struct reserved_mem *reserved; 281 + struct platform_device *pdev; 282 + struct device_node *res_np; 283 + phys_addr_t base, size; 284 + struct resource *res; 285 + int i, blk_type, ret; 286 + 287 + pdev = to_platform_device(sdev->dev); 288 + chip_info = get_chip_info(sdev); 289 + 290 + for (i = 0; chip_info->memory[i].name; i++) { 291 + blk_type = imx_region_name_to_blk_type(chip_info->memory[i].name); 292 + if (blk_type < 0) 293 + return dev_err_probe(sdev->dev, blk_type, 294 + "no blk type for region %s\n", 295 + chip_info->memory[i].name); 296 + 297 + if (!chip_info->memory[i].reserved) { 298 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 299 + chip_info->memory[i].name); 300 + if (!res) 301 + return dev_err_probe(sdev->dev, -ENODEV, 302 + "failed to fetch %s resource\n", 303 + chip_info->memory[i].name); 304 + 305 + base = res->start; 306 + size = resource_size(res); 307 + } else { 308 + ret = of_property_match_string(pdev->dev.of_node, 309 + "memory-region-names", 310 + chip_info->memory[i].name); 311 + if (ret < 0) 312 + return dev_err_probe(sdev->dev, ret, 313 + "no valid index for %s\n", 314 + chip_info->memory[i].name); 315 + 316 + res_np = of_parse_phandle(pdev->dev.of_node, 317 + "memory-region", 318 + ret); 319 + if (!res_np) 320 + return dev_err_probe(sdev->dev, -ENODEV, 321 + "failed to parse phandle %s\n", 322 + chip_info->memory[i].name); 323 + 324 + reserved = of_reserved_mem_lookup(res_np); 325 + of_node_put(res_np); 326 + if (!reserved) 327 + return dev_err_probe(sdev->dev, -ENODEV, 328 + "failed to get %s reserved\n", 329 + chip_info->memory[i].name); 330 + 331 + base = reserved->base; 332 + size = reserved->size; 333 + } 334 + 335 + sdev->bar[blk_type] = devm_ioremap(sdev->dev, base, size); 336 + if (IS_ERR(sdev->bar[blk_type])) 337 + return dev_err_probe(sdev->dev, 338 + PTR_ERR(sdev->bar[blk_type]), 339 + "failed to ioremap %s region\n", 340 + chip_info->memory[i].name); 341 + } 342 + 343 + return 0; 344 + } 345 + 346 + static void imx_unregister_action(void *data) 347 + { 348 + struct imx_common_data *common; 349 + struct snd_sof_dev *sdev; 350 + 351 + sdev = data; 352 + common = sdev->pdata->hw_pdata; 353 + 354 + if (get_chip_info(sdev)->has_dma_reserved) 355 + of_reserved_mem_device_release(sdev->dev); 356 + 357 + platform_device_unregister(common->ipc_dev); 358 + } 359 + 360 + static int imx_probe(struct snd_sof_dev *sdev) 361 + { 362 + struct dev_pm_domain_attach_data domain_data = { 363 + .pd_names = NULL, /* no filtering */ 364 + .pd_flags = PD_FLAG_DEV_LINK_ON, 365 + }; 366 + struct imx_common_data *common; 367 + struct platform_device *pdev; 368 + int ret; 369 + 370 + pdev = to_platform_device(sdev->dev); 371 + 372 + common = devm_kzalloc(sdev->dev, sizeof(*common), GFP_KERNEL); 373 + if (!common) 374 + return dev_err_probe(sdev->dev, -ENOMEM, 375 + "failed to allocate common data\n"); 376 + 377 + common->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp", 378 + PLATFORM_DEVID_NONE, 379 + pdev, sizeof(*pdev)); 380 + if (IS_ERR(common->ipc_dev)) 381 + return dev_err_probe(sdev->dev, PTR_ERR(common->ipc_dev), 382 + "failed to create IPC device\n"); 383 + 384 + if (get_chip_info(sdev)->has_dma_reserved) { 385 + ret = of_reserved_mem_device_init_by_name(sdev->dev, 386 + pdev->dev.of_node, 387 + "dma"); 388 + if (ret) { 389 + platform_device_unregister(common->ipc_dev); 390 + 391 + return dev_err_probe(sdev->dev, ret, 392 + "failed to bind DMA region\n"); 393 + } 394 + } 395 + 396 + /* let the devres API take care of the cleanup */ 397 + ret = devm_add_action_or_reset(sdev->dev, 398 + imx_unregister_action, 399 + sdev); 400 + if (ret) 401 + return dev_err_probe(sdev->dev, ret, "failed to add devm action\n"); 402 + 403 + common->ipc_handle = dev_get_drvdata(&common->ipc_dev->dev); 404 + if (!common->ipc_handle) 405 + return dev_err_probe(sdev->dev, -EPROBE_DEFER, 406 + "failed to fetch IPC handle\n"); 407 + 408 + ret = imx_parse_ioremap_memory(sdev); 409 + if (ret < 0) 410 + return dev_err_probe(sdev->dev, ret, 411 + "failed to parse/ioremap memory regions\n"); 412 + 413 + if (!sdev->dev->pm_domain) { 414 + ret = devm_pm_domain_attach_list(sdev->dev, 415 + &domain_data, &common->pd_list); 416 + if (ret < 0) 417 + return dev_err_probe(sdev->dev, ret, "failed to attach PDs\n"); 418 + } 419 + 420 + ret = devm_clk_bulk_get_all(sdev->dev, &common->clks); 421 + if (ret < 0) 422 + return dev_err_probe(sdev->dev, common->clk_num, 423 + "failed to fetch clocks\n"); 424 + common->clk_num = ret; 425 + 426 + ret = clk_bulk_prepare_enable(common->clk_num, common->clks); 427 + if (ret < 0) 428 + return dev_err_probe(sdev->dev, ret, "failed to enable clocks\n"); 429 + 430 + common->ipc_handle->ops = &imx_ipc_ops; 431 + imx_dsp_set_data(common->ipc_handle, sdev); 432 + 433 + sdev->num_cores = 1; 434 + sdev->pdata->hw_pdata = common; 435 + sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; 436 + sdev->dsp_box.offset = get_chip_info(sdev)->ipc_info.boot_mbox_offset; 437 + 438 + return imx_chip_probe(sdev); 439 + } 440 + 441 + static void imx_remove(struct snd_sof_dev *sdev) 442 + { 443 + struct imx_common_data *common; 444 + int ret; 445 + 446 + common = sdev->pdata->hw_pdata; 447 + 448 + if (!pm_runtime_suspended(sdev->dev)) { 449 + ret = imx_chip_core_shutdown(sdev); 450 + if (ret < 0) 451 + dev_err(sdev->dev, "failed to shutdown core: %d\n", ret); 452 + 453 + clk_bulk_disable_unprepare(common->clk_num, common->clks); 454 + } 455 + } 456 + 457 + const struct snd_sof_dsp_ops sof_imx_ops = { 458 + .probe = imx_probe, 459 + .remove = imx_remove, 460 + 461 + .run = imx_chip_core_kick, 462 + .reset = imx_chip_core_reset, 463 + 464 + .block_read = sof_block_read, 465 + .block_write = sof_block_write, 466 + 467 + .mailbox_read = sof_mailbox_read, 468 + .mailbox_write = sof_mailbox_write, 469 + 470 + .send_msg = imx_send_msg, 471 + .get_mailbox_offset = imx_get_mailbox_offset, 472 + .get_window_offset = imx_get_window_offset, 473 + 474 + .ipc_msg_data = sof_ipc_msg_data, 475 + .set_stream_data_offset = sof_set_stream_data_offset, 476 + 477 + .get_bar_index = imx_get_bar_index, 478 + .load_firmware = snd_sof_load_firmware_memcpy, 479 + 480 + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, 481 + 482 + .pcm_open = sof_stream_pcm_open, 483 + .pcm_close = sof_stream_pcm_close, 484 + 485 + .runtime_suspend = imx_runtime_suspend, 486 + .runtime_resume = imx_runtime_resume, 487 + .suspend = imx_suspend, 488 + .resume = imx_resume, 489 + 490 + .set_power_state = imx_set_power_state, 491 + 492 + .hw_info = SNDRV_PCM_INFO_MMAP | 493 + SNDRV_PCM_INFO_MMAP_VALID | 494 + SNDRV_PCM_INFO_INTERLEAVED | 495 + SNDRV_PCM_INFO_PAUSE | 496 + SNDRV_PCM_INFO_BATCH | 497 + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, 498 + }; 499 + EXPORT_SYMBOL(sof_imx_ops); 81 500 82 501 MODULE_LICENSE("Dual BSD/GPL"); 83 502 MODULE_DESCRIPTION("SOF helpers for IMX platforms");
+151
sound/soc/sof/imx/imx-common.h
··· 4 4 #define __IMX_COMMON_H__ 5 5 6 6 #include <linux/clk.h> 7 + #include <linux/of_platform.h> 8 + #include <sound/sof/xtensa.h> 9 + 10 + #include "../sof-of-dev.h" 11 + #include "../ops.h" 7 12 8 13 #define EXCEPT_MAX_HDR_SIZE 0x400 9 14 #define IMX8_STACK_DUMP_SIZE 32 15 + 16 + /* chip_info refers to the data stored in struct sof_dev_desc's chip_info */ 17 + #define get_chip_info(sdev)\ 18 + ((const struct imx_chip_info *)((sdev)->pdata->desc->chip_info)) 19 + 20 + /* chip_pdata refers to the data stored in struct imx_common_data's chip_pdata */ 21 + #define get_chip_pdata(sdev)\ 22 + (((struct imx_common_data *)((sdev)->pdata->hw_pdata))->chip_pdata) 23 + 24 + /* can be used if: 25 + * 1) The only supported IPC version is IPC3. 26 + * 2) The default paths/FW name match values below. 27 + * 28 + * otherwise, just explicitly declare the structure 29 + */ 30 + #define IMX_SOF_DEV_DESC(mach_name, of_machs, \ 31 + mach_chip_info, mach_ops, mach_ops_init) \ 32 + static struct sof_dev_desc sof_of_##mach_name##_desc = { \ 33 + .of_machines = of_machs, \ 34 + .chip_info = mach_chip_info, \ 35 + .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), \ 36 + .ipc_default = SOF_IPC_TYPE_3, \ 37 + .default_fw_path = { \ 38 + [SOF_IPC_TYPE_3] = "imx/sof", \ 39 + }, \ 40 + .default_tplg_path = { \ 41 + [SOF_IPC_TYPE_3] = "imx/sof-tplg", \ 42 + }, \ 43 + .default_fw_filename = { \ 44 + [SOF_IPC_TYPE_3] = "sof-" #mach_name ".ri", \ 45 + }, \ 46 + .ops = mach_ops, \ 47 + .ops_init = mach_ops_init, \ 48 + } 49 + 50 + /* to be used alongside IMX_SOF_DEV_DESC() */ 51 + #define IMX_SOF_DEV_DESC_NAME(mach_name) sof_of_##mach_name##_desc 52 + 53 + /* dai driver entry w/ playback and capture caps. If one direction is missing 54 + * then set the channels to 0. 55 + */ 56 + #define IMX_SOF_DAI_DRV_ENTRY(dai_name, pb_cmin, pb_cmax, cap_cmin, cap_cmax) \ 57 + { \ 58 + .name = dai_name, \ 59 + .playback = { \ 60 + .channels_min = pb_cmin, \ 61 + .channels_max = pb_cmax, \ 62 + }, \ 63 + .capture = { \ 64 + .channels_min = cap_cmin, \ 65 + .channels_max = cap_cmax, \ 66 + }, \ 67 + } 68 + 69 + /* use if playback and capture have the same min/max channel count */ 70 + #define IMX_SOF_DAI_DRV_ENTRY_BIDIR(dai_name, cmin, cmax)\ 71 + IMX_SOF_DAI_DRV_ENTRY(dai_name, cmin, cmax, cmin, cmax) 72 + 73 + struct imx_ipc_info { 74 + /* true if core is able to write a panic code to the debug box */ 75 + bool has_panic_code; 76 + /* offset to mailbox in which firmware initially writes FW_READY */ 77 + int boot_mbox_offset; 78 + /* offset to region at which the mailboxes start */ 79 + int window_offset; 80 + }; 81 + 82 + struct imx_chip_ops { 83 + /* called after clocks and PDs are enabled */ 84 + int (*probe)(struct snd_sof_dev *sdev); 85 + /* used directly by the SOF core */ 86 + int (*core_kick)(struct snd_sof_dev *sdev); 87 + /* called during suspend()/remove() before clocks are disabled */ 88 + int (*core_shutdown)(struct snd_sof_dev *sdev); 89 + /* used directly by the SOF core */ 90 + int (*core_reset)(struct snd_sof_dev *sdev); 91 + }; 92 + 93 + struct imx_memory_info { 94 + const char *name; 95 + bool reserved; 96 + }; 97 + 98 + struct imx_chip_info { 99 + struct imx_ipc_info ipc_info; 100 + /* does the chip have a reserved memory region for DMA? */ 101 + bool has_dma_reserved; 102 + struct imx_memory_info *memory; 103 + struct snd_soc_dai_driver *drv; 104 + int num_drv; 105 + /* optional */ 106 + const struct imx_chip_ops *ops; 107 + }; 108 + 109 + struct imx_common_data { 110 + struct platform_device *ipc_dev; 111 + struct imx_dsp_ipc *ipc_handle; 112 + /* core may have no clocks */ 113 + struct clk_bulk_data *clks; 114 + int clk_num; 115 + /* core may have no PDs */ 116 + struct dev_pm_domain_list *pd_list; 117 + void *chip_pdata; 118 + }; 119 + 120 + static inline int imx_chip_core_kick(struct snd_sof_dev *sdev) 121 + { 122 + const struct imx_chip_ops *ops = get_chip_info(sdev)->ops; 123 + 124 + if (ops && ops->core_kick) 125 + return ops->core_kick(sdev); 126 + 127 + return 0; 128 + } 129 + 130 + static inline int imx_chip_core_shutdown(struct snd_sof_dev *sdev) 131 + { 132 + const struct imx_chip_ops *ops = get_chip_info(sdev)->ops; 133 + 134 + if (ops && ops->core_shutdown) 135 + return ops->core_shutdown(sdev); 136 + 137 + return 0; 138 + } 139 + 140 + static inline int imx_chip_core_reset(struct snd_sof_dev *sdev) 141 + { 142 + const struct imx_chip_ops *ops = get_chip_info(sdev)->ops; 143 + 144 + if (ops && ops->core_reset) 145 + return ops->core_reset(sdev); 146 + 147 + return 0; 148 + } 149 + 150 + static inline int imx_chip_probe(struct snd_sof_dev *sdev) 151 + { 152 + const struct imx_chip_ops *ops = get_chip_info(sdev)->ops; 153 + 154 + if (ops && ops->probe) 155 + return ops->probe(sdev); 156 + 157 + return 0; 158 + } 10 159 11 160 void imx8_get_registers(struct snd_sof_dev *sdev, 12 161 struct sof_ipc_dsp_oops_xtensa *xoops, ··· 163 14 u32 *stack, size_t stack_words); 164 15 165 16 void imx8_dump(struct snd_sof_dev *sdev, u32 flags); 17 + 18 + extern const struct snd_sof_dsp_ops sof_imx_ops; 166 19 167 20 #endif