-
Notifications
You must be signed in to change notification settings - Fork 139
ASoC: SOF: trace: fix trace doesn't update issue #248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
|
||
plbossart marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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) | ||
ranj063 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| entries++; | ||
|
|
||
| dev_dbg(sdev->dev, "entries:%d\n", entries); | ||
|
|
||
plbossart marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /* 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); | ||
ranj063 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /* 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; | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am unable to determine if the code is correct or not, it's significantly changed from the initial code and I am hesitant to sign-off here. Maybe it's correct, but it's not self-explanatory nor easy to debug/maintain. It'd be easier to deal with logical steps, such as from init_offset to first period start, then all regular periods, then end of buffer alignment, then wrap until last period and again remaining bytes?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @plbossart I've verified that the logic here is correct. quite impressive @keyonjie But might be easier to follow if you break it up like pierre suggests.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, actually I spent hours to abstract and make it as simple as what it is now, otherwise, it will be inevitable lengthy and low efficiency with those 5 steps Pierre mentioned.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You may have spent hours but I have no way of knowing if it works or not.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, shall I try add more comments with logic analyzing?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ask is to find a way to make things self-explanatory with all corner cases handled. You can do this with simple code or comments, your choice. |
||
|
|
||
| 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 */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 */ | ||
plbossart marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and typo here as well
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks, will change. |
||
| 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); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
plbossart marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /* 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; | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like copy/paste code with different comments, can this be a helper function?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, make sense, let me refine it. |
||
|
|
||
| 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 */ | ||
plbossart marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am now officially confused. I understand trace_init_offset and the offset to be used on resume. Now you are using it as a dynamic value that's passed to firmware, not sure I get what you are trying to do.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is used to cover that case on SKL- platforms where we don't have host DMA, as I mentioned in the PR commit message: Todo: for SKL- platforms where not using BDL entries, FW to support
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then let's be clear: I will not merge a hardware-agnostic kernel patch that will only work with SKL+ firmware. Our team has the charter to deal with legacy platforms so the firmware changes need to happen first.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, this was the biggest concern from me when writing this patch, let me raise #544 and try if we can speed up the implementation in FW side.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something is wrong with your references, torvalds#544 does not refer to any firmware issues
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry, it should be thesofproject/sof#544. |
||
|
|
||
| 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why are we initializing the waitqueue twice?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry, this is refining error with 'patch am', will fix it, thanks. |
||
|
|
||
| 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code is becoming unreadable. Please keep the initial definition of host_offset and init_host_offset, the introduction of 'trace' makes things difficult to follow.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with Liam that 'trace' is better than 'host' here, as it is member of sdev, but only used for trace(not for other streams).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. then change the dma_trace_posn structure to use trace_offset... It makes no sense to use different fields in different structures when it's the same concept.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the dma_trace_posn struct is replied from FW, looks host_offset better there. the sdev is only used in driver, so they have actually different standpoint, can we just keep this?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand your reply. Why would the same concept be named differently if this is firmware or driver? |
||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@keyonjie this is so difficult to follow. If I understand it correctly there are only 2 cases:
if init_offset ==0,there are bufsize/periodbytes (+1 in case it is not evenly divisible) periods
else there are 2 periods (or entries as you call it) right?
I am confused with n,m,j and e in the above diagram.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ranj063 the 2nd case what you mentioned can be more complicated, there can be > 2 periods there.
What the comment I wrote here is aim to abstract all possible cases to this common one.
You can ignore n, m, j, e here, though they are useful when writing code in logic that Pierre mentioned:
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@keyonjie Yes, I understand the logic and ther is not doubt it is correct.
If I may make a suggestion: can we treat the case of a non-zero init_offset as 2 separate buffers,
then we end up with just as many bld entries except that the partial segments will be only at the end of the 2 buffer areas. For example:
Case 1: init_offset = 0
num of period_bytes size bdl entries = 5
num of partial bdl entries = 1
init_offset = 0
|
|------|------|------|------|------|---|
Case 2: init_offset = 16
num of period_bytes size bdl entries = 4
num of partial bdl entries = 2
init_offset = 16
|------|------|----|------|------|-----|
So the partial fragments are at the end of the 2 areas ( 0 - init_offset -1, init_offset - bufsize).
But with the current case there will be 3 partial entries.
Anyway, this is only to improve maintainability. I am fine if the code is merged as is too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I don't catch you here, there will be only 2 partial entries in your sample.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@keyonjie what I meant was just break up the stream->bufsize into 2 and then assign bdl entries individually for [init_offset, buf_size) and then [0, init_offset -1). This way the logic will be a lot easier to follow. Let me take a swipe at it today and send you a patch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ranj063 the init_offset may be used for normal audio stream also, so we still need assign bdl entries for periods when init_offset!=0.