Skip to content

Commit fee9f96

Browse files
author
Marcin Maka
committed
dma: hda: playback preload phase synced with logical buffer state
Removed dma/dsp race condition that happened within preload phase. Min buffer size set to entire buffer to avoid dma and dsp pointers misalignmen in case the period size is not multiple of the hda dma burst size. Signed-off-by: Marcin Maka <marcin.maka@linux.intel.com>
1 parent 7abb55f commit fee9f96

4 files changed

Lines changed: 112 additions & 39 deletions

File tree

src/audio/dai.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@
4848
#define DAI_PLAYBACK_STREAM 0
4949
#define DAI_CAPTURE_STREAM 1
5050

51+
#define DAI_PTR_INIT_DAI 1 /* buffer ptr initialized by dai */
52+
#define DAI_PTR_INIT_HOST 2 /* buffer ptr initialized by host */
53+
5154
/* tracing */
5255
#define trace_dai(__e) trace_event(TRACE_CLASS_DAI, __e)
5356
#define trace_dai_error(__e) trace_error(TRACE_CLASS_DAI, __e)
@@ -523,6 +526,8 @@ static void dai_pointer_init(struct comp_dev *dev)
523526
struct comp_buffer *dma_buffer;
524527
struct dai_data *dd = comp_get_drvdata(dev);
525528

529+
dd->pointer_init = DAI_PTR_INIT_DAI;
530+
526531
/* not required for capture streams */
527532
if (dev->params.direction == SOF_IPC_STREAM_PLAYBACK) {
528533
dma_buffer = list_first_item(&dev->bsource_list,
@@ -532,6 +537,7 @@ static void dai_pointer_init(struct comp_dev *dev)
532537
case SOF_COMP_HOST:
533538
case SOF_COMP_SG_HOST:
534539
/* buffer is preloaded and advanced by host DMA engine */
540+
dd->pointer_init = DAI_PTR_INIT_HOST;
535541
break;
536542
default:
537543
/* advance source pipeline w_ptr by one period
@@ -540,8 +546,6 @@ static void dai_pointer_init(struct comp_dev *dev)
540546
break;
541547
}
542548
}
543-
544-
dd->pointer_init = 1;
545549
}
546550

547551
/* used to pass standard and bespoke command (with data) to component */
@@ -563,8 +567,12 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd)
563567
trace_dai("tsa");
564568
if (!dd->pointer_init)
565569
dai_pointer_init(dev);
566-
/* only start the DAI if we are not XRUN handling */
567-
if (dd->xrun == 0) {
570+
/* only start the DAI if we are not XRUN handling
571+
* and the ptr is not initialized by the host as in this
572+
* case start is deferred to the first copy call as the buffer
573+
* is populated by the host only then
574+
*/
575+
if (dd->xrun == 0 && dd->pointer_init != DAI_PTR_INIT_HOST) {
568576
/* start the DAI */
569577
ret = dma_start(dd->dma, dd->chan);
570578
if (ret < 0)
@@ -632,6 +640,18 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd)
632640
/* copy and process stream data from source to sink buffers */
633641
static int dai_copy(struct comp_dev *dev)
634642
{
643+
struct dai_data *dd = comp_get_drvdata(dev);
644+
int ret;
645+
646+
if (dd->pointer_init == DAI_PTR_INIT_HOST) {
647+
/* start the DAI */
648+
ret = dma_start(dd->dma, dd->chan);
649+
if (ret < 0)
650+
return ret;
651+
dai_trigger(dd->dai, COMP_TRIGGER_START, dev->params.direction);
652+
dd->pointer_init = DAI_PTR_INIT_DAI; /* next copy just quits */
653+
platform_dai_wallclock(dev, &dd->wallclock);
654+
}
635655
return 0;
636656
}
637657

src/audio/host.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,7 @@ static int host_copy_int(struct comp_dev *dev, bool preload_run)
741741
#if defined CONFIG_DMA_GW
742742
/* tell gateway to copy another period */
743743
ret = dma_copy(hd->dma, hd->chan, hd->period_bytes,
744-
preload_run ? DMA_COPY_NO_COMMIT : 0);
744+
preload_run ? DMA_COPY_PRELOAD : 0);
745745
if (ret < 0)
746746
goto out;
747747

src/drivers/intel/cavs/hda-dma.c

Lines changed: 86 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,15 @@
8686

8787
#define HDA_LINK_1MS_US 1000
8888

89+
#define HDA_STATE_PRELOAD BIT(0)
90+
#define HDA_STATE_BF_WAIT BIT(1)
91+
8992
struct hda_chan_data {
9093
struct dma *dma;
9194
uint32_t index;
9295
uint32_t stream_id;
93-
uint32_t status;
96+
uint32_t status; /* common status */
97+
uint32_t state; /* hda specific additional state */
9498
uint32_t desc_count;
9599
uint32_t desc_avail;
96100
uint32_t direction;
@@ -186,42 +190,77 @@ static inline uint32_t hda_dma_get_data_size(struct dma *dma, uint32_t chan)
186190
return ds;
187191
}
188192

193+
static int hda_dma_preload(struct dma *dma, struct hda_chan_data *chan)
194+
{
195+
struct dma_sg_elem next = {
196+
.src = DMA_RELOAD_LLI,
197+
.dest = DMA_RELOAD_LLI,
198+
.size = DMA_RELOAD_LLI
199+
};
200+
int i;
201+
int period_cnt;
202+
203+
/* waiting for buffer full after start
204+
* first try is unblocking, then blocking
205+
*/
206+
while (!(host_dma_reg_read(dma, chan->index, DGCS) & DGCS_BF) &&
207+
(chan->state & HDA_STATE_BF_WAIT))
208+
;
209+
210+
if (host_dma_reg_read(dma, chan->index, DGCS) & DGCS_BF) {
211+
chan->state &= ~(HDA_STATE_PRELOAD | HDA_STATE_BF_WAIT);
212+
if (chan->cb) {
213+
/* loop over each period */
214+
period_cnt = chan->buffer_bytes /
215+
chan->period_bytes;
216+
for (i = 0; i < period_cnt; i++)
217+
chan->cb(chan->cb_data,
218+
DMA_IRQ_TYPE_LLIST, &next);
219+
/* do not need to test out next in this path */
220+
}
221+
} else {
222+
/* next call in pre-load state will be blocking */
223+
chan->state |= HDA_STATE_BF_WAIT;
224+
}
225+
226+
return 0;
227+
}
228+
189229
static int hda_dma_copy_ch(struct dma *dma, struct hda_chan_data *chan,
190-
int bytes, uint32_t flags)
230+
int bytes)
191231
{
192-
struct dma_sg_elem next;
232+
struct dma_sg_elem next = {
233+
.src = DMA_RELOAD_LLI,
234+
.dest = DMA_RELOAD_LLI,
235+
.size = DMA_RELOAD_LLI
236+
};
237+
uint32_t flags;
193238
uint32_t dgcs = 0;
194239

195240
tracev_host("GwU");
196-
if (flags & DMA_COPY_NO_COMMIT) {
197-
/* delay to fill the buffer, only for the first time
198-
* for _input_ dma.
199-
*/
200-
idelay(PLATFORM_DEFAULT_DELAY);
201-
} else {
202-
/* clear link xruns */
203-
dgcs = host_dma_reg_read(dma, chan->index, DGCS);
204-
if (dgcs & DGCS_BOR)
205-
hda_update_bits(dma, chan->index,
206-
DGCS, DGCS_BOR, DGCS_BOR);
207-
208-
/* make sure that previous transfer is complete */
209-
if (chan->direction == DMA_DIR_MEM_TO_DEV) {
210-
while (hda_dma_get_free_size(dma, chan->index) <
211-
bytes)
212-
idelay(PLATFORM_DEFAULT_DELAY);
213-
}
214241

215-
/*
216-
* set BFPI to let host gateway knows we have read size,
217-
* which will trigger next copy start.
218-
*/
219-
if (chan->direction == DMA_DIR_MEM_TO_DEV)
220-
hda_dma_inc_link_fp(dma, chan->index, bytes);
221-
else
222-
hda_dma_inc_fp(dma, chan->index, bytes);
242+
/* clear link xruns */
243+
dgcs = host_dma_reg_read(dma, chan->index, DGCS);
244+
if (dgcs & DGCS_BOR)
245+
hda_update_bits(dma, chan->index,
246+
DGCS, DGCS_BOR, DGCS_BOR);
247+
248+
/* make sure that previous transfer is complete */
249+
if (chan->direction == DMA_DIR_MEM_TO_DEV) {
250+
while (hda_dma_get_free_size(dma, chan->index) <
251+
bytes)
252+
idelay(PLATFORM_DEFAULT_DELAY);
223253
}
224254

255+
/*
256+
* set BFPI to let host gateway knows we have read size,
257+
* which will trigger next copy start.
258+
*/
259+
if (chan->direction == DMA_DIR_MEM_TO_DEV)
260+
hda_dma_inc_link_fp(dma, chan->index, bytes);
261+
else
262+
hda_dma_inc_fp(dma, chan->index, bytes);
263+
225264
spin_lock_irq(&dma->lock, flags);
226265
if (chan->cb) {
227266
next.src = DMA_RELOAD_LLI;
@@ -245,17 +284,24 @@ static uint64_t hda_dma_work(void *data, uint64_t delay)
245284
{
246285
struct hda_chan_data *chan = (struct hda_chan_data *)data;
247286

248-
hda_dma_copy_ch(chan->dma, chan, chan->period_bytes, 0);
249-
/* next time to re-arm (TODO: should sub elapsed time?) */
287+
hda_dma_copy_ch(chan->dma, chan, chan->period_bytes);
288+
/* next time to re-arm */
250289
return HDA_LINK_1MS_US;
251290
}
252291

253292
/* notify DMA to copy bytes */
254293
static int hda_dma_copy(struct dma *dma, int channel, int bytes, uint32_t flags)
255294
{
256295
struct dma_pdata *p = dma_get_drvdata(dma);
296+
struct hda_chan_data *chan = p->chan + channel;
257297

258-
return hda_dma_copy_ch(dma, &p->chan[channel], bytes, flags);
298+
if (flags & DMA_COPY_PRELOAD)
299+
chan->state |= HDA_STATE_PRELOAD;
300+
301+
if (chan->state & HDA_STATE_PRELOAD)
302+
return hda_dma_preload(dma, chan);
303+
else
304+
return hda_dma_copy_ch(dma, chan, bytes);
259305
}
260306

261307
/* acquire the specific DMA channel */
@@ -292,6 +338,7 @@ static void hda_dma_channel_put_unlocked(struct dma *dma, int channel)
292338

293339
/* set new state */
294340
p->chan[channel].status = COMP_STATE_INIT;
341+
p->chan[channel].state = 0;
295342
p->chan[channel].period_bytes = 0;
296343
p->chan[channel].buffer_bytes = 0;
297344
p->chan[channel].cb = NULL;
@@ -495,6 +542,12 @@ static int hda_dma_set_config(struct dma *dma, int channel,
495542
if (buffer_addr == 0)
496543
buffer_addr = addr;
497544
}
545+
/* buffer size must be multiple of hda dma burst size */
546+
if (buffer_bytes % PLATFORM_HDA_BUFFER_ALIGNMENT) {
547+
ret = -EINVAL;
548+
goto out;
549+
}
550+
498551
p->chan[channel].period_bytes = period_bytes;
499552
p->chan[channel].buffer_bytes = buffer_bytes;
500553

@@ -511,7 +564,7 @@ static int hda_dma_set_config(struct dma *dma, int channel,
511564
if (config->direction == DMA_DIR_LMEM_TO_HMEM ||
512565
config->direction == DMA_DIR_HMEM_TO_LMEM)
513566
host_dma_reg_write(dma, channel, DGMBS,
514-
ALIGN_UP(period_bytes,
567+
ALIGN_UP(buffer_bytes,
515568
PLATFORM_HDA_BUFFER_ALIGNMENT));
516569

517570
/* firmware control buffer */

src/include/sof/dma.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
#define DMA_IRQ_TYPE_LLIST BIT(1)
8282

8383
/* DMA copy flags */
84-
#define DMA_COPY_NO_COMMIT BIT(0)
84+
#define DMA_COPY_PRELOAD BIT(0)
8585

8686
/* We will use this macro in cb handler to inform dma that
8787
* we need to stop the reload for special purpose

0 commit comments

Comments
 (0)