diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 8c4d6830597fff..e09a2d108e8c42 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -220,7 +220,7 @@ struct snd_soc_component { int (*init)(struct snd_soc_component *component); /* function mark */ - struct snd_pcm_substream *mark_module; + void *mark_module; struct snd_pcm_substream *mark_open; struct snd_pcm_substream *mark_hw_params; struct snd_pcm_substream *mark_trigger; @@ -391,15 +391,13 @@ void snd_soc_component_exit_regmap(struct snd_soc_component *component); #define snd_soc_component_module_get_when_open(component, substream) \ snd_soc_component_module_get(component, substream, 1) int snd_soc_component_module_get(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - int upon_open); + void *mark, int upon_open); #define snd_soc_component_module_put_when_remove(component) \ snd_soc_component_module_put(component, NULL, 0, 0) #define snd_soc_component_module_put_when_close(component, substream, rollback) \ snd_soc_component_module_put(component, substream, 1, rollback) void snd_soc_component_module_put(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - int upon_open, int rollback); + void *mark, int upon_open, int rollback); static inline void snd_soc_component_set_drvdata(struct snd_soc_component *c, void *data) @@ -455,8 +453,10 @@ int snd_soc_component_of_xlate_dai_id(struct snd_soc_component *component, int snd_soc_component_of_xlate_dai_name(struct snd_soc_component *component, const struct of_phandle_args *args, const char **dai_name); -int snd_soc_component_compr_open(struct snd_compr_stream *cstream); -void snd_soc_component_compr_free(struct snd_compr_stream *cstream, +int snd_soc_component_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream); +void snd_soc_component_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int rollback); int snd_soc_component_compr_trigger(struct snd_compr_stream *cstream, int cmd); int snd_soc_component_compr_set_params(struct snd_compr_stream *cstream, diff --git a/include/sound/sof.h b/include/sound/sof.h index 23b374311d1616..9102fdbe1d4615 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -17,6 +17,28 @@ struct snd_sof_dsp_ops; +/** + * enum snd_sof_fw_state - DSP firmware state definitions + * @SOF_FW_BOOT_NOT_STARTED: firmware boot is not yet started + * @SOF_FW_BOOT_PREPARE: preparing for boot (firmware loading for exaqmple) + * @SOF_FW_BOOT_IN_PROGRESS: firmware boot is in progress + * @SOF_FW_BOOT_FAILED: firmware boot failed + * @SOF_FW_BOOT_READY_FAILED: firmware booted but fw_ready op failed + * @SOF_FW_BOOT_READY_OK: firmware booted and fw_ready op is OK + * @SOF_FW_BOOT_COMPLETE: firmware is booted up and functional + * @SOF_FW_CRASHED: firmware crashed after successful boot + */ +enum snd_sof_fw_state { + SOF_FW_BOOT_NOT_STARTED = 0, + SOF_FW_BOOT_PREPARE, + SOF_FW_BOOT_IN_PROGRESS, + SOF_FW_BOOT_FAILED, + SOF_FW_BOOT_READY_FAILED, + SOF_FW_BOOT_READY_OK, + SOF_FW_BOOT_COMPLETE, + SOF_FW_CRASHED, +}; + /* * SOF Platform data. */ diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 8e8d917d22f8ff..a08a897c52305f 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -251,8 +251,7 @@ int snd_soc_component_set_jack(struct snd_soc_component *component, EXPORT_SYMBOL_GPL(snd_soc_component_set_jack); int snd_soc_component_module_get(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - int upon_open) + void *mark, int upon_open) { int ret = 0; @@ -260,25 +259,24 @@ int snd_soc_component_module_get(struct snd_soc_component *component, !try_module_get(component->dev->driver->owner)) ret = -ENODEV; - /* mark substream if succeeded */ + /* mark module if succeeded */ if (ret == 0) - soc_component_mark_push(component, substream, module); + soc_component_mark_push(component, mark, module); return soc_component_ret(component, ret); } void snd_soc_component_module_put(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - int upon_open, int rollback) + void *mark, int upon_open, int rollback) { - if (rollback && !soc_component_mark_match(component, substream, module)) + if (rollback && !soc_component_mark_match(component, mark, module)) return; if (component->driver->module_get_upon_open == !!upon_open) module_put(component->dev->driver->owner); - /* remove marked substream */ - soc_component_mark_pop(component, substream, module); + /* remove the mark from module */ + soc_component_mark_pop(component, mark, module); } int snd_soc_component_open(struct snd_soc_component *component, @@ -425,43 +423,36 @@ EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap); #endif -int snd_soc_component_compr_open(struct snd_compr_stream *cstream) +int snd_soc_component_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream) { - struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_component *component; - int i, ret; + int ret = 0; - for_each_rtd_components(rtd, i, component) { - if (component->driver->compress_ops && - component->driver->compress_ops->open) { - ret = component->driver->compress_ops->open(component, cstream); - if (ret < 0) - return soc_component_ret(component, ret); - } + if (component->driver->compress_ops && + component->driver->compress_ops->open) + ret = component->driver->compress_ops->open(component, cstream); + + /* mark substream if succeeded */ + if (ret == 0) soc_component_mark_push(component, cstream, compr_open); - } - return 0; + return soc_component_ret(component, ret); } EXPORT_SYMBOL_GPL(snd_soc_component_compr_open); -void snd_soc_component_compr_free(struct snd_compr_stream *cstream, +void snd_soc_component_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int rollback) { - struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_component *component; - int i; + if (rollback && !soc_component_mark_match(component, cstream, compr_open)) + return; - for_each_rtd_components(rtd, i, component) { - if (rollback && !soc_component_mark_match(component, cstream, compr_open)) - continue; + if (component->driver->compress_ops && + component->driver->compress_ops->free) + component->driver->compress_ops->free(component, cstream); - if (component->driver->compress_ops && - component->driver->compress_ops->free) - component->driver->compress_ops->free(component, cstream); - - soc_component_mark_pop(component, cstream, compr_open); - } + /* remove marked substream */ + soc_component_mark_pop(component, cstream, compr_open); } EXPORT_SYMBOL_GPL(snd_soc_component_compr_free); diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 36060800e9bd97..8e2494a9f3a7f0 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -22,6 +22,39 @@ #include #include +static int snd_soc_compr_components_open(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component; + int ret = 0; + int i; + + for_each_rtd_components(rtd, i, component) { + ret = snd_soc_component_module_get_when_open(component, cstream); + if (ret < 0) + break; + + ret = snd_soc_component_compr_open(component, cstream); + if (ret < 0) + break; + } + + return ret; +} + +static void snd_soc_compr_components_free(struct snd_compr_stream *cstream, + int rollback) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component; + int i; + + for_each_rtd_components(rtd, i, component) { + snd_soc_component_compr_free(component, cstream, rollback); + snd_soc_component_module_put_when_close(component, cstream, rollback); + } +} + static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -44,7 +77,7 @@ static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback) snd_soc_link_compr_shutdown(cstream, rollback); - snd_soc_component_compr_free(cstream, rollback); + snd_soc_compr_components_free(cstream, rollback); snd_soc_dai_compr_shutdown(cpu_dai, cstream, rollback); @@ -80,7 +113,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream) if (ret < 0) goto err; - ret = snd_soc_component_compr_open(cstream); + ret = snd_soc_compr_components_open(cstream); if (ret < 0) goto err; @@ -137,7 +170,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) if (ret < 0) goto out; - ret = snd_soc_component_compr_open(cstream); + ret = snd_soc_compr_components_open(cstream); if (ret < 0) goto open_err; @@ -160,7 +193,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) return 0; machine_err: - snd_soc_component_compr_free(cstream, 1); + snd_soc_compr_components_free(cstream, 1); open_err: snd_soc_dai_compr_shutdown(cpu_dai, cstream, 1); out: @@ -205,7 +238,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) snd_soc_link_compr_shutdown(cstream, 0); - snd_soc_component_compr_free(cstream, 0); + snd_soc_compr_components_free(cstream, 0); snd_soc_dai_compr_shutdown(cpu_dai, cstream, 0); diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 06e5f49f7ee861..add7e9e214b45c 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o utils.o sof-audio.o stream-ipc.o + control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o @@ -12,9 +12,12 @@ snd-sof-of-objs := sof-of-dev.o snd-sof-nocodec-objs := nocodec.o +snd-sof-utils-objs := sof-utils.o + obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o +obj-$(CONFIG_SND_SOC_SOF) += snd-sof-utils.o obj-$(CONFIG_SND_SOC_SOF_ACPI_DEV) += snd-sof-acpi.o obj-$(CONFIG_SND_SOC_SOF_OF) += snd-sof-of.o diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2ea29186e397b0..8625753e2bd81b 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -147,7 +147,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return ret; } - sdev->fw_state = SOF_FW_BOOT_PREPARE; + sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); /* check machine info */ ret = sof_machine_check(sdev); @@ -189,7 +189,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_load_err; } - sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; + sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS); /* * Boot the firmware. The FW boot status will be modified @@ -265,7 +265,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) snd_sof_remove(sdev); /* all resources freed, update state to match */ - sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); sdev->first_boot = true; return ret; @@ -300,7 +300,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->pdata = plat_data; sdev->first_boot = true; - sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; #endif @@ -362,6 +362,13 @@ int snd_sof_device_remove(struct device *dev) if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); + /* + * Unregister machine driver. This will unbind the snd_card which + * will remove the component driver and unload the topology + * before freeing the snd_card. + */ + snd_sof_machine_unregister(sdev, pdata); + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { ret = snd_sof_dsp_power_down_notify(sdev); if (ret < 0) @@ -373,13 +380,6 @@ int snd_sof_device_remove(struct device *dev) snd_sof_free_trace(sdev); } - /* - * Unregister machine driver. This will unbind the snd_card which - * will remove the component driver and unload the topology - * before freeing the snd_card. - */ - snd_sof_machine_unregister(sdev, pdata); - /* * Unregistering the machine driver results in unloading the topology. * Some widgets, ex: scheduler, attempt to power down the core they are diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index bcd381f495c0c5..6d937ee7de910e 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -831,6 +831,7 @@ void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) { dev_err(sdev->dev, "------------[ DSP dump start ]------------\n"); + dev_err(sdev->dev, "DSP firmware state: %d\n", sdev->fw_state); sof_ops(sdev)->dbg_dump(sdev, flags); dev_err(sdev->dev, "------------[ DSP dump end ]------------\n"); if (!print_all) @@ -864,6 +865,7 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) /* dump vital information to the logs */ snd_sof_ipc_dump(sdev); snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + sof_set_fw_state(sdev, SOF_FW_CRASHED); snd_sof_trace_notify_for_error(sdev); } EXPORT_SYMBOL(snd_sof_handle_fw_exception); diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index 29e3da3c63db01..c5dc833b57b8fc 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -19,16 +19,15 @@ #include "../ops.h" #include "hda.h" -static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev) +static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct hdac_ext_stream *stream = hda->dtrace_stream; struct hdac_stream *hstream = &stream->hstream; - struct snd_dma_buffer *dmab = &sdev->dmatb; int ret; hstream->period_bytes = 0;/* initialize period_bytes */ - hstream->bufsize = sdev->dmatb.bytes; + hstream->bufsize = dmab->bytes; ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); if (ret < 0) @@ -57,7 +56,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) * initialize capture stream, set BDL address and return corresponding * stream tag which will be sent to the firmware by IPC message. */ - ret = hda_dsp_trace_prepare(sdev); + ret = hda_dsp_trace_prepare(sdev, &sdev->dmatb); if (ret < 0) { dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret); hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, *stream_tag); diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/iomem-utils.c similarity index 59% rename from sound/soc/sof/utils.c rename to sound/soc/sof/iomem-utils.c index 66fa6602fb67a3..7228a6e041c39f 100644 --- a/sound/soc/sof/utils.c +++ b/sound/soc/sof/iomem-utils.c @@ -3,7 +3,7 @@ // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // -// Copyright(c) 2018 Intel Corporation. All rights reserved. +// Copyright(c) 2018-2021 Intel Corporation. All rights reserved. // // Author: Keyon Jie // @@ -125,62 +125,3 @@ int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, return 0; } EXPORT_SYMBOL(sof_block_read); - -/* - * Generic buffer page table creation. - * Take the each physical page address and drop the least significant unused - * bits from each (based on PAGE_SIZE). Then pack valid page address bits - * into compressed page table. - */ - -int snd_sof_create_page_table(struct device *dev, - struct snd_dma_buffer *dmab, - unsigned char *page_table, size_t size) -{ - int i, pages; - - pages = snd_sgbuf_aligned_pages(size); - - dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n", - dmab->area, size, pages); - - for (i = 0; i < pages; i++) { - /* - * The number of valid address bits for each page is 20. - * idx determines the byte position within page_table - * where the current page's address is stored - * in the compressed page_table. - * This can be calculated by multiplying the page number by 2.5. - */ - u32 idx = (5 * i) >> 1; - u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; - u8 *pg_table; - - dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); - - pg_table = (u8 *)(page_table + idx); - - /* - * pagetable compression: - * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5 - * ___________pfn 0__________ __________pfn 1___________ _pfn 2... - * .... .... .... .... .... .... .... .... .... .... .... - * It is created by: - * 1. set current location to 0, PFN index i to 0 - * 2. put pfn[i] at current location in Little Endian byte order - * 3. calculate an intermediate value as - * x = (pfn[i+1] << 4) | (pfn[i] & 0xf) - * 4. put x at offset (current location + 2) in LE byte order - * 5. increment current location by 5 bytes, increment i by 2 - * 6. continue to (2) - */ - if (i & 1) - put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4, - pg_table); - else - put_unaligned_le32(pfn, pg_table); - } - - return pages; -} -EXPORT_SYMBOL(snd_sof_create_page_table); diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 70e067b434af57..54dee28c439e52 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -18,8 +18,10 @@ #include "sof-audio.h" #include "ops.h" -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type); -static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd); +typedef void (*ipc_rx_callback)(struct snd_sof_dev *sdev, void *full_msg); + +static void ipc_trace_message(struct snd_sof_dev *sdev, void *full_msg); +static void ipc_stream_message(struct snd_sof_dev *sdev, void *full_msg); /* * IPC message Tx/Rx message handling. @@ -281,7 +283,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, struct snd_sof_ipc_msg *msg; int ret; - if (ipc->disable_ipc_tx) + if (ipc->disable_ipc_tx || sdev->fw_state == SOF_FW_CRASHED) return -ENODEV; /* @@ -391,44 +393,30 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) } EXPORT_SYMBOL(snd_sof_ipc_reply); -static void ipc_comp_notification(struct snd_sof_dev *sdev, - struct sof_ipc_cmd_hdr *hdr) +static void ipc_comp_notification(struct snd_sof_dev *sdev, void *full_msg) { + struct sof_ipc_cmd_hdr *hdr = full_msg; u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; - struct sof_ipc_ctrl_data *cdata; - int ret; switch (msg_type) { case SOF_IPC_COMP_GET_VALUE: case SOF_IPC_COMP_GET_DATA: - cdata = kmalloc(hdr->size, GFP_KERNEL); - if (!cdata) - return; - - /* read back full message */ - ret = snd_sof_ipc_msg_data(sdev, NULL, cdata, hdr->size); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to read component event: %d\n", ret); - goto err; - } break; default: dev_err(sdev->dev, "error: unhandled component message %#x\n", msg_type); return; } - snd_sof_control_notify(sdev, cdata); - -err: - kfree(cdata); + snd_sof_control_notify(sdev, full_msg); } /* DSP firmware has sent host a message */ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) { + ipc_rx_callback rx_callback = NULL; struct sof_ipc_cmd_hdr hdr; - u32 cmd, type; + void *full_msg; + u32 cmd; int err; /* read back header */ @@ -440,7 +428,6 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) ipc_log_header(sdev->dev, "ipc rx", hdr.cmd); cmd = hdr.cmd & SOF_GLB_TYPE_MASK; - type = hdr.cmd & SOF_CMD_TYPE_MASK; /* check message type */ switch (cmd) { @@ -452,9 +439,9 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) { err = sof_ops(sdev)->fw_ready(sdev, cmd); if (err < 0) - sdev->fw_state = SOF_FW_BOOT_READY_FAILED; + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED); else - sdev->fw_state = SOF_FW_BOOT_COMPLETE; + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK); /* wake up firmware loader */ wake_up(&sdev->boot_wait); @@ -465,20 +452,34 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) case SOF_IPC_GLB_PM_MSG: break; case SOF_IPC_GLB_COMP_MSG: - ipc_comp_notification(sdev, &hdr); + rx_callback = ipc_comp_notification; break; case SOF_IPC_GLB_STREAM_MSG: - /* need to pass msg id into the function */ - ipc_stream_message(sdev, hdr.cmd); + rx_callback = ipc_stream_message; break; case SOF_IPC_GLB_TRACE_MSG: - ipc_trace_message(sdev, type); + rx_callback = ipc_trace_message; break; default: dev_err(sdev->dev, "error: unknown DSP message 0x%x\n", cmd); break; } + if (rx_callback) { + /* read the full message as there we have rx handler for it */ + full_msg = kmalloc(hdr.size, GFP_KERNEL); + if (!full_msg) + return; + + err = snd_sof_ipc_msg_data(sdev, NULL, full_msg, hdr.size); + if (err < 0) + dev_err(sdev->dev, "failed to read message\n"); + else + rx_callback(sdev, full_msg); + + kfree(full_msg); + } + ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd); } EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); @@ -487,19 +488,14 @@ EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); * IPC trace mechanism. */ -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type) +static void ipc_trace_message(struct snd_sof_dev *sdev, void *full_msg) { - struct sof_ipc_dma_trace_posn posn; - int ret; + struct sof_ipc_cmd_hdr *hdr = full_msg; + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; switch (msg_type) { case SOF_IPC_TRACE_DMA_POSITION: - /* read back full message */ - ret = snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn)); - if (ret < 0) - dev_warn(sdev->dev, "failed to read trace position: %d\n", ret); - else - snd_sof_trace_update_pos(sdev, &posn); + snd_sof_trace_update_pos(sdev, full_msg); break; default: dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type); @@ -580,11 +576,11 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) } /* stream notifications from DSP FW */ -static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) +static void ipc_stream_message(struct snd_sof_dev *sdev, void *full_msg) { - /* get msg cmd type and msd id */ - u32 msg_type = msg_cmd & SOF_CMD_TYPE_MASK; - u32 msg_id = SOF_IPC_MESSAGE_ID(msg_cmd); + struct sof_ipc_cmd_hdr *hdr = full_msg; + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; + u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd); switch (msg_type) { case SOF_IPC_STREAM_POSITION: diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 147441bd265258..462de18e7d5aaf 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -839,13 +839,11 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) dev_err(sdev->dev, "error: firmware boot failure\n"); snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); - sdev->fw_state = SOF_FW_BOOT_FAILED; + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); return -EIO; } - if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) - dev_dbg(sdev->dev, "firmware boot complete\n"); - else + if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) return -EIO; /* FW boots but fw_ready op failed */ /* perform post fw run operations */ @@ -855,6 +853,9 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) return ret; } + dev_dbg(sdev->dev, "firmware boot complete\n"); + sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); + return 0; } EXPORT_SYMBOL(snd_sof_run_firmware); diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 11ecebd079073f..92a64114bfd0ac 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -158,6 +158,7 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) sdev->dsp_oops_offset, offset); snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + sof_set_fw_state(sdev, SOF_FW_CRASHED); snd_sof_trace_notify_for_error(sdev); } EXPORT_SYMBOL(snd_sof_dsp_panic); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 56db927390c98b..506ddad6f5c058 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -19,6 +19,7 @@ #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) #include "sof-probes.h" #endif +#include "sof-utils.h" /* Create DMA buffer page table for DSP */ static int create_page_table(struct snd_soc_component *component, diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index e65f4f4d6df943..f22b5ee2347868 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -122,7 +122,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) old_state == SOF_DSP_PM_D0) return 0; - sdev->fw_state = SOF_FW_BOOT_PREPARE; + sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); /* load the firmware */ ret = snd_sof_load_firmware(sdev); @@ -133,7 +133,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } - sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; + sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS); /* * Boot the firmware. The FW boot status will be modified @@ -257,7 +257,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) return ret; /* reset FW state */ - sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); sdev->enabled_cores_mask = 0; return ret; @@ -312,6 +312,13 @@ int snd_sof_prepare(struct device *dev) /* will suspend to S3 by default */ sdev->system_suspend_target = SOF_SUSPEND_S3; + /* + * if the firmware is crashed then we try to aim for S3 to reboot the + * firmware + */ + if (sdev->fw_state == SOF_FW_CRASHED) + return 0; + if (!desc->use_acpi_target_states) return 0; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index bac52ff8b50ca4..5075924c16d962 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -371,15 +371,6 @@ struct snd_sof_ipc_msg { bool ipc_complete; }; -enum snd_sof_fw_state { - SOF_FW_BOOT_NOT_STARTED = 0, - SOF_FW_BOOT_PREPARE, - SOF_FW_BOOT_IN_PROGRESS, - SOF_FW_BOOT_FAILED, - SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */ - SOF_FW_BOOT_COMPLETE, -}; - /* * SOF Device Level. */ @@ -512,10 +503,6 @@ void snd_sof_complete(struct device *dev); void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); -int snd_sof_create_page_table(struct device *dev, - struct snd_dma_buffer *dmab, - unsigned char *page_table, size_t size); - /* * Firmware loading. */ @@ -586,6 +573,19 @@ static inline void sof_oops(struct snd_sof_dev *sdev, void *oops) extern const struct dsp_arch_ops sof_xtensa_arch_ops; +/* + * Firmware state tracking + */ +static inline void sof_set_fw_state(struct snd_sof_dev *sdev, + enum snd_sof_fw_state new_state) +{ + if (sdev->fw_state == new_state) + return; + + dev_dbg(sdev->dev, "New firmware state: %d\n", new_state); + sdev->fw_state = new_state; +} + /* * Utilities */ diff --git a/sound/soc/sof/sof-utils.c b/sound/soc/sof/sof-utils.c new file mode 100644 index 00000000000000..0d48f7174ec237 --- /dev/null +++ b/sound/soc/sof/sof-utils.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2018-2021 Intel Corporation. All rights reserved. +// +// Author: Keyon Jie +// + +#include +#include +#include +#include +#include +#include "sof-utils.h" + +/* + * Generic buffer page table creation. + * Take the each physical page address and drop the least significant unused + * bits from each (based on PAGE_SIZE). Then pack valid page address bits + * into compressed page table. + */ + +int snd_sof_create_page_table(struct device *dev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size) +{ + int i, pages; + + pages = snd_sgbuf_aligned_pages(size); + + dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n", + dmab->area, size, pages); + + for (i = 0; i < pages; i++) { + /* + * The number of valid address bits for each page is 20. + * idx determines the byte position within page_table + * where the current page's address is stored + * in the compressed page_table. + * This can be calculated by multiplying the page number by 2.5. + */ + u32 idx = (5 * i) >> 1; + u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; + u8 *pg_table; + + dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); + + pg_table = (u8 *)(page_table + idx); + + /* + * pagetable compression: + * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5 + * ___________pfn 0__________ __________pfn 1___________ _pfn 2... + * .... .... .... .... .... .... .... .... .... .... .... + * It is created by: + * 1. set current location to 0, PFN index i to 0 + * 2. put pfn[i] at current location in Little Endian byte order + * 3. calculate an intermediate value as + * x = (pfn[i+1] << 4) | (pfn[i] & 0xf) + * 4. put x at offset (current location + 2) in LE byte order + * 5. increment current location by 5 bytes, increment i by 2 + * 6. continue to (2) + */ + if (i & 1) + put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4, + pg_table); + else + put_unaligned_le32(pfn, pg_table); + } + + return pages; +} +EXPORT_SYMBOL(snd_sof_create_page_table); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-utils.h b/sound/soc/sof/sof-utils.h new file mode 100644 index 00000000000000..cf508ff7ff5ede --- /dev/null +++ b/sound/soc/sof/sof-utils.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Intel Corporation. All rights reserved. + */ + +#ifndef __SOC_SOF_UTILS_H +#define __SOC_SOF_UTILS_H + +struct snd_dma_buffer; +struct device; + +int snd_sof_create_page_table(struct device *dev, + struct snd_dma_buffer *dmab, + unsigned char *page_table, size_t size); + +#endif diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index e3afc3dac7d17a..a5f8b9e74cc2d6 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -12,6 +12,7 @@ #include #include "sof-priv.h" #include "ops.h" +#include "sof-utils.h" #define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 #define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 @@ -308,9 +309,6 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, lpos_64 = lpos; lpos = do_div(lpos_64, buffer_size); - if (count > buffer_size - lpos) /* min() not used to avoid sparse warnings */ - count = buffer_size - lpos; - /* get available count based on current host offset */ avail = sof_wait_trace_avail(sdev, lpos, buffer_size); if (sdev->dtrace_error) { @@ -319,7 +317,8 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, } /* make sure count is <= avail */ - count = avail > count ? count : avail; + if (count > avail) + count = avail; /* copy available trace data to debugfs */ rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count);