diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 76ac08d8d0883e..b9604b4ea27cd6 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -66,7 +66,8 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, hstream->format_val = format; hstream->bufsize = size; - ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, + NULL, SOF_HDA_CL_STREAM); if (ret < 0) { dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); goto error; diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index d5a09dcceeee4d..cde830311725a7 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -121,7 +121,8 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); - ret = hda_dsp_stream_hw_params(sdev, stream, dmab, params); + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, + params, SOF_HDA_AUDIO_STREAM); if (ret < 0) { dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); return ret; diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 42a986ce35a08f..6455d980b682e4 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -88,31 +88,46 @@ static int hda_setup_bdle(struct snd_sof_dev *sdev, /* * set up Buffer Descriptor List (BDL) for host memory transfer * BDL describes the location of the individual buffers and is little endian. + * + * The typical BDL entries after set up should looks like this: + * + * bdl[0] from the init_offset, n entries before wrap, + * the e-1 full entries, and the last e entry. + * + * init_offset + * | + * |------|...........|---------S-------|------|........|--------|-------| + * bdl[n] bdl[n+j] bdl[e-1] | bdl[0] bdl[1] bdl[m] bdl[n-2] bdl[n-1] */ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, - struct hdac_stream *stream) + struct hdac_stream *stream, u32 init_offset) { struct sof_intel_dsp_bdl *bdl; - int i, offset, period_bytes, periods; - int remain, ioc; + unsigned int i, period_bytes, entries; + u32 bytes, offset, size = 0; + bool ioc, set_ioc, is_wrap = false; period_bytes = stream->period_bytes; dev_dbg(sdev->dev, "period_bytes:0x%x\n", period_bytes); if (!period_bytes) period_bytes = stream->bufsize; - periods = stream->bufsize / period_bytes; + entries = stream->bufsize / period_bytes; - dev_dbg(sdev->dev, "periods:%d\n", periods); + /* count the last offset fragment entry */ + if (stream->bufsize % period_bytes) + entries++; - remain = stream->bufsize % period_bytes; - if (remain) - periods++; + /* count the last index fragment entry */ + if (init_offset % period_bytes) + entries++; + + dev_dbg(sdev->dev, "entries:%d\n", entries); /* program the initial BDL entries */ bdl = (struct sof_intel_dsp_bdl *)stream->bdl.area; - offset = 0; + offset = init_offset; stream->frags = 0; /* @@ -122,19 +137,28 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, ioc = sdev->hda->no_ipc_position ? !stream->no_period_wakeup : 0; - for (i = 0; i < periods; i++) { - if (i == (periods - 1) && remain) - /* set the last small entry */ - offset = hda_setup_bdle(sdev, dmab, - stream, &bdl, offset, - remain, 0); + for (i = 0; i < entries; i++) { + bytes = period_bytes - offset % period_bytes; + if (is_wrap) + bytes = min(bytes, init_offset - offset); else - offset = hda_setup_bdle(sdev, dmab, - stream, &bdl, offset, - period_bytes, ioc); + bytes = min(bytes, stream->bufsize - offset); + + /* only set ioc at period bundary */ + set_ioc = (offset + bytes) % period_bytes ? 0 : 1; + offset = hda_setup_bdle(sdev, dmab, + stream, &bdl, offset, + bytes, set_ioc && ioc); + + if (offset == stream->bufsize) { + offset = 0; + is_wrap = true; + } + + size += bytes; } - return offset; + return size; } int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, @@ -344,19 +368,21 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, } /* - * prepare for common hdac registers settings, for both code loader + * prepare for common hdac registers settings, for code loader, dma trace, * and normal stream. */ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, struct snd_dma_buffer *dmab, - struct snd_pcm_hw_params *params) + struct snd_pcm_hw_params *params, + enum sof_hda_tream_type stream_type) { struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_stream *hstream = &stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; u32 val, mask; + u32 init_offset = 0; if (!stream) { dev_err(sdev->dev, "error: no stream available\n"); @@ -436,10 +462,13 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, hstream->frags = 0; - ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream); - if (ret < 0) { - dev_err(sdev->dev, "error: set up of BDL failed\n"); - return ret; + if (stream_type == SOF_HDA_TRACE_STREAM) + init_offset = sdev->trace_init_offset; + + ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream, init_offset); + if (ret != hstream->bufsize) { + dev_err(sdev->dev, "error: set up of BDL failed, bufsize:0x%x, only 0x%x set\n", hstream->bufsize, ret); + return -EFAULT; } /* set up stream descriptor for DMA */ diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index a1c9f1b45dca14..a158ae069c180c 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -43,7 +43,8 @@ static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev) hstream->bufsize = sdev->dmatb.bytes; - ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL); + ret = hda_dsp_stream_hw_params(sdev, stream, dmab, + NULL, SOF_HDA_TRACE_STREAM); if (ret < 0) dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); @@ -75,7 +76,6 @@ int hda_dsp_trace_release(struct snd_sof_dev *sdev) if (sdev->hda->dtrace_stream) { hstream = &sdev->hda->dtrace_stream->hstream; - hstream->opened = false; hda_dsp_stream_put_cstream(sdev, hstream->stream_tag); sdev->hda->dtrace_stream = NULL; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index c9c3e52508f00f..17962601dbf5de 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -343,6 +343,12 @@ struct sof_intel_dsp_desc { struct snd_sof_dsp_ops *ops; }; +enum sof_hda_tream_type { + SOF_HDA_AUDIO_STREAM = 0, /* normal playback/capture stream */ + SOF_HDA_CL_STREAM, /* code loader stream */ + SOF_HDA_TRACE_STREAM, /* dma trace capture stream */ +}; + #define SOF_HDA_PLAYBACK_STREAMS 16 #define SOF_HDA_CAPTURE_STREAMS 16 #define SOF_HDA_PLAYBACK 0 @@ -442,14 +448,15 @@ void hda_dsp_stream_free(struct snd_sof_dev *sdev); int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, struct snd_dma_buffer *dmab, - struct snd_pcm_hw_params *params); + struct snd_pcm_hw_params *params, + enum sof_hda_tream_type stream_type); int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, int cmd); irqreturn_t hda_dsp_stream_interrupt(int irq, void *context); irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context); int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, - struct hdac_stream *stream); + struct hdac_stream *stream, u32 init_offset); struct hdac_ext_stream * hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 0f7ddf19bf8a15..90ac1c1192497a 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -363,7 +363,8 @@ struct snd_sof_dev { struct snd_dma_buffer dmatp; int dma_trace_pages; wait_queue_head_t trace_sleep; - u32 host_offset; + u32 trace_init_offset; /* restore trace offset upon resume */ + u32 trace_offset; bool dtrace_is_enabled; bool dtrace_error; diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 5aeb6ea3c58b2b..c08bc7b98ea131 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -30,18 +30,23 @@ static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev, loff_t pos, size_t buffer_size) { wait_queue_entry_t wait; + u32 host_offset; + + /* count the initial offset to get the buffer offset */ + host_offset = (sdev->trace_init_offset + sdev->trace_offset) % + buffer_size; /* * If host offset is less than local pos, it means write pointer of * host DMA buffer has been wrapped. We should output the trace data * at the end of host DMA buffer at first. */ - if (sdev->host_offset < pos) + if (host_offset < pos) return buffer_size - pos; /* If there is available trace data now, it is unnecessary to wait. */ - if (sdev->host_offset > pos) - return sdev->host_offset - pos; + if (host_offset > pos) + return host_offset - pos; /* wait for available trace data from FW */ init_waitqueue_entry(&wait, current); @@ -58,11 +63,15 @@ static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev, remove_wait_queue(&sdev->trace_sleep, &wait); out: + /* update buffer offset after wait done */ + host_offset = (sdev->trace_init_offset + sdev->trace_offset) % + buffer_size; + /* return bytes available for copy */ - if (sdev->host_offset < pos) + if (host_offset < pos) return buffer_size - pos; else - return sdev->host_offset - pos; + return host_offset - pos; } static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, @@ -154,16 +163,24 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) if (sdev->dtrace_is_enabled) return -EINVAL; + /* update init_offset, and reinitial offset to 0 */ + sdev->trace_init_offset = (sdev->trace_init_offset + + sdev->trace_offset) % + sdev->dmatb.bytes; + sdev->trace_offset = 0; + /* set IPC parameters */ params.hdr.size = sizeof(params); params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_PARAMS; params.buffer.phy_addr = sdev->dmatp.addr; params.buffer.size = sdev->dmatb.bytes; - params.buffer.offset = 0; params.buffer.pages = sdev->dma_trace_pages; - init_waitqueue_head(&sdev->trace_sleep); - sdev->host_offset = 0; + /* + * send offset to FW to require copying new logs + * start from new offset if possible. + */ + params.buffer.offset = sdev->trace_init_offset; ret = snd_sof_dma_trace_init(sdev, ¶ms.stream_tag); if (ret < 0) { @@ -201,6 +218,8 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev) /* set false before start initialization */ sdev->dtrace_is_enabled = false; + sdev->trace_offset = 0; + sdev->trace_init_offset = 0; /* allocate trace page table buffer */ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->parent, @@ -235,6 +254,10 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev) goto table_err; } + init_waitqueue_head(&sdev->trace_sleep); + + init_waitqueue_head(&sdev->trace_sleep); + ret = snd_sof_init_trace_ipc(sdev); if (ret < 0) goto table_err; @@ -251,8 +274,9 @@ EXPORT_SYMBOL(snd_sof_init_trace); int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, struct sof_ipc_dma_trace_posn *posn) { - if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) { - sdev->host_offset = posn->host_offset; + if (sdev->dtrace_is_enabled && + sdev->trace_offset != posn->host_offset) { + sdev->trace_offset = posn->host_offset; wake_up(&sdev->trace_sleep); }