[Sound-open-firmware] [PATCH] platform: byt: Add support for masking all DMACs and SSPs
Add the remaining DMACs and SSP to platform IRQ mask/unmask and clear.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/platform/baytrail/platform.c | 99 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 3 deletions(-)
diff --git a/src/platform/baytrail/platform.c b/src/platform/baytrail/platform.c index ce8a689..651c3ce 100644 --- a/src/platform/baytrail/platform.c +++ b/src/platform/baytrail/platform.c @@ -105,6 +105,18 @@ int platform_boot_complete(uint32_t boot_message) void platform_interrupt_clear(uint32_t irq, uint32_t mask) { switch (irq) { + case IRQ_NUM_EXT_SSP0: + shim_write(SHIM_PISR, mask << 3); + interrupt_clear(irq); + break; + case IRQ_NUM_EXT_SSP1: + shim_write(SHIM_PISR, mask << 4); + interrupt_clear(irq); + break; + case IRQ_NUM_EXT_SSP2: + shim_write(SHIM_PISR, mask << 5); + interrupt_clear(irq); + break; case IRQ_NUM_EXT_DMAC0: shim_write(SHIM_PISR, mask << 16); interrupt_clear(irq); @@ -118,6 +130,18 @@ void platform_interrupt_clear(uint32_t irq, uint32_t mask) shim_write(SHIM_PISRH, mask << 0); interrupt_clear(irq); break; + case IRQ_NUM_EXT_SSP3: + shim_write(SHIM_PISRH, mask << 8); + interrupt_clear(irq); + break; + case IRQ_NUM_EXT_SSP4: + shim_write(SHIM_PISRH, mask << 9); + interrupt_clear(irq); + break; + case IRQ_NUM_EXT_SSP5: + shim_write(SHIM_PISRH, mask << 10); + interrupt_clear(irq); + break; #endif default: break; @@ -132,12 +156,76 @@ uint32_t platform_interrupt_get_enabled(void)
void platform_interrupt_mask(uint32_t irq, uint32_t mask) { - + switch (irq) { + case IRQ_NUM_EXT_SSP0: + shim_write(SHIM_PIMR, mask << 3); + break; + case IRQ_NUM_EXT_SSP1: + shim_write(SHIM_PIMR, mask << 4); + break; + case IRQ_NUM_EXT_SSP2: + shim_write(SHIM_PIMR, mask << 5); + break; + case IRQ_NUM_EXT_DMAC0: + shim_write(SHIM_PIMR, mask << 16); + break; + case IRQ_NUM_EXT_DMAC1: + shim_write(SHIM_PIMR, mask << 24); + break; +#if defined CONFIG_CHERRYTRAIL + case IRQ_NUM_EXT_DMAC2: + shim_write(SHIM_PISMH, mask << 8); + break; + case IRQ_NUM_EXT_SSP3: + shim_write(SHIM_PIMRH, mask << 0); + break; + case IRQ_NUM_EXT_SSP4: + shim_write(SHIM_PIMRH, mask << 1); + break; + case IRQ_NUM_EXT_SSP5: + shim_write(SHIM_PIMRH, mask << 2); + break; +#endif + default: + break; + } }
void platform_interrupt_unmask(uint32_t irq, uint32_t mask) { - + switch (irq) { + case IRQ_NUM_EXT_SSP0: + shim_write(SHIM_PIMR, shim_read(SHIM_PIMR) & ~(mask << 3)); + break; + case IRQ_NUM_EXT_SSP1: + shim_write(SHIM_PIMR, shim_read(SHIM_PIMR) & ~(mask << 4)); + break; + case IRQ_NUM_EXT_SSP2: + shim_write(SHIM_PIMR, shim_read(SHIM_PIMR) & ~(mask << 5)); + break; + case IRQ_NUM_EXT_DMAC0: + shim_write(SHIM_PIMR, shim_read(SHIM_PIMR) & ~(mask << 16)); + break; + case IRQ_NUM_EXT_DMAC1: + shim_write(SHIM_PIMR, shim_read(SHIM_PIMR) & ~(mask << 24)); + break; +#if defined CONFIG_CHERRYTRAIL + case IRQ_NUM_EXT_DMAC2: + shim_write(SHIM_PIMRH, shim_read(SHIM_PIMRH) & ~(mask << 8)); + break; + case IRQ_NUM_EXT_SSP3: + shim_write(SHIM_PIMRH, shim_read(SHIM_PIMRH) & ~(mask << 0)); + break; + case IRQ_NUM_EXT_SSP4: + shim_write(SHIM_PIMRH, shim_read(SHIM_PIMRH) & ~(mask << 1)); + break; + case IRQ_NUM_EXT_SSP5: + shim_write(SHIM_PIMRH, shim_read(SHIM_PIMRH) & ~(mask << 2)); + break; +#endif + default: + break; + } }
int platform_init(struct reef *reef) @@ -213,9 +301,14 @@ int platform_init(struct reef *reef) dma_probe(dmac2); #endif
- /* mask SSP interrupts */ + /* mask SSP 0 - 2 interrupts */ shim_write(SHIM_PIMR, shim_read(SHIM_PIMR) | 0x00000038);
+#if defined CONFIG_CHERRYTRAIL + /* mask SSP 3 - 5 interrupts */ + shim_write(SHIM_PIMRH, shim_read(SHIM_PIMRH) | 0x00000700); +#endif + /* init SSP ports */ trace_point(TRACE_BOOT_PLATFORM_SSP); ssp0 = dai_get(SOF_DAI_INTEL_SSP, 0);
Add an API call to allow pipelines to cancel any scheduled work.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/audio/pipeline.c | 6 ++++++ src/include/reef/audio/pipeline.h | 1 + 2 files changed, 7 insertions(+)
diff --git a/src/audio/pipeline.c b/src/audio/pipeline.c index 687efe9..626cc92 100644 --- a/src/audio/pipeline.c +++ b/src/audio/pipeline.c @@ -953,9 +953,15 @@ void pipeline_schedule_copy(struct pipeline *p, struct comp_dev *dev) { schedule_task(&p->pipe_task, p->ipc_pipe.deadline, p->ipc_pipe.priority, dev); + schedule(); }
+void pipeline_schedule_cancel(struct pipeline *p, struct comp_dev *dev) +{ + schedule_task_complete(&p->pipe_task); +} + static void pipeline_task(void *arg) { struct pipeline *p = arg; diff --git a/src/include/reef/audio/pipeline.h b/src/include/reef/audio/pipeline.h index ad01dfd..29c509f 100644 --- a/src/include/reef/audio/pipeline.h +++ b/src/include/reef/audio/pipeline.h @@ -113,6 +113,7 @@ int init_pipeline(void);
/* schedule a copy operation for this pipeline */ void pipeline_schedule_copy(struct pipeline *p, struct comp_dev *dev); +void pipeline_schedule_cancel(struct pipeline *p, struct comp_dev *dev);
/* get time pipeline timestamps from host to dai */ void pipeline_get_timestamp(struct pipeline *p, struct comp_dev *host_dev,
Don't propagate the copy into other pipelines that may have different scheduling.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/audio/pipeline.c | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/src/audio/pipeline.c b/src/audio/pipeline.c index 626cc92..2800d64 100644 --- a/src/audio/pipeline.c +++ b/src/audio/pipeline.c @@ -691,6 +691,10 @@ static int pipeline_copy_from_upstream(struct comp_dev *start, if (!buffer->connected || buffer->source->state != COMP_STATE_RUNNING) continue;
+ /* dont go upstream if this source is from another pipeline */ + if (buffer->source->pipeline != current->pipeline) + continue; + /* continue upstream */ err = pipeline_copy_from_upstream(start, buffer->source); if (err < 0) { @@ -745,6 +749,10 @@ static int pipeline_copy_to_downstream(struct comp_dev *start, if (!buffer->connected || buffer->sink->state != COMP_STATE_RUNNING) continue;
+ /* dont go downstream if this sink is from another pipeline */ + if (buffer->sink->pipeline != current->pipeline) + continue; + /* continue downstream */ err = pipeline_copy_to_downstream(start, buffer->sink); if (err < 0) {
Align all components, DAIs and DMACs to use the same state levels and transitions. This simplifies DAI and DMAC components integration.
The DRAIN state has also been removed to further reduce DMAC and DAI complexity.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/audio/component.c | 10 +-- src/audio/dai.c | 73 +++++----------- src/audio/eq_iir.c | 11 ++- src/audio/host.c | 7 +- src/audio/mixer.c | 6 +- src/audio/pipeline.c | 16 ++-- src/drivers/dw-dma.c | 173 +++++-------------------------------- src/drivers/ssp.c | 135 +++++++---------------------- src/include/reef/audio/component.h | 33 ++++--- src/include/reef/dma.h | 16 +--- src/include/reef/ssp.h | 9 -- 11 files changed, 125 insertions(+), 364 deletions(-)
diff --git a/src/audio/component.c b/src/audio/component.c index 2a397d7..fd0d65e 100644 --- a/src/audio/component.c +++ b/src/audio/component.c @@ -93,7 +93,6 @@ struct comp_dev *comp_new(struct sof_ipc_comp *comp) /* init component */ memcpy(&cdev->comp, comp, sizeof(*comp)); cdev->drv = drv; - cdev->state = COMP_STATE_INIT; spinlock_init(&cdev->lock); list_init(&cdev->bsource_list); list_init(&cdev->bsink_list); @@ -124,14 +123,13 @@ int comp_set_state(struct comp_dev *dev, int cmd) switch (cmd) { case COMP_CMD_START: case COMP_CMD_RELEASE: - dev->state = COMP_STATE_RUNNING; + dev->state = COMP_STATE_ACTIVE; break; case COMP_CMD_STOP: - if (dev->state == COMP_STATE_RUNNING || - dev->state == COMP_STATE_DRAINING || + if (dev->state == COMP_STATE_ACTIVE || dev->state == COMP_STATE_PAUSED) { comp_buffer_reset(dev); - dev->state = COMP_STATE_SETUP; + dev->state = COMP_STATE_READY; } else { trace_comp_error("CEs"); ret = -EINVAL; @@ -139,7 +137,7 @@ int comp_set_state(struct comp_dev *dev, int cmd) break; case COMP_CMD_PAUSE: /* only support pausing for running */ - if (dev->state == COMP_STATE_RUNNING) + if (dev->state == COMP_STATE_ACTIVE) dev->state = COMP_STATE_PAUSED; else { trace_comp_error("CEp"); diff --git a/src/audio/dai.c b/src/audio/dai.c index 358ad2d..7f4cde6 100644 --- a/src/audio/dai.c +++ b/src/audio/dai.c @@ -82,6 +82,17 @@ static void dai_dma_cb(void *data, uint32_t type, struct dma_sg_elem *next)
tracev_dai("irq");
+ /* is stream stopped or paused ? */ + if (dev->state == COMP_STATE_PAUSED) { + + /* stop the DAI */ + dai_trigger(dd->dai, COMP_CMD_STOP, dev->params.direction); + + /* tell DMA not to reload */ + next->size = DMA_RELOAD_END; + return; + } + if (dev->params.direction == SOF_IPC_STREAM_PLAYBACK) { dma_buffer = list_first_item(&dev->bsource_list, struct comp_buffer, sink_list); @@ -178,7 +189,7 @@ static struct comp_dev *dai_new_ssp(struct sof_ipc_comp *comp)
/* set up callback */ dma_set_cb(dd->dma, dd->chan, DMA_IRQ_TYPE_LLIST, dai_dma_cb, dev); - + dev->state = COMP_STATE_READY; return dev;
error: @@ -340,7 +351,7 @@ static int dai_params(struct comp_dev *dev) trace_dai("par");
/* can set params on only init state */ - if (dev->state != COMP_STATE_INIT) { + if (dev->state != COMP_STATE_READY) { trace_dai_error("wdp"); return -EINVAL; } @@ -417,13 +428,13 @@ static int dai_reset(struct comp_dev *dev) rfree(elem); }
- dev->state = COMP_STATE_INIT; dd->dai_pos_blks = 0; if (dd->dai_pos) *dd->dai_pos = 0; dd->dai_pos = NULL; dd->last_bytes = 0; dev->position = 0; + dev->state = COMP_STATE_READY;
return 0; } @@ -439,61 +450,25 @@ static int dai_cmd(struct comp_dev *dev, int cmd, void *data) tracev_value(cmd);
switch (cmd) { - case COMP_CMD_PAUSE: - if (dev->state == COMP_STATE_RUNNING) { - dma_pause(dd->dma, dd->chan); - dai_trigger(dd->dai, cmd, dev->params.direction); - dev->state = COMP_STATE_PAUSED; - } - break; case COMP_CMD_STOP: - switch (dev->state) { - case COMP_STATE_RUNNING: - case COMP_STATE_PAUSED: - dma_stop(dd->dma, dd->chan, - dev->state == COMP_STATE_RUNNING ? 1 : 0); - /* need stop ssp */ - dai_trigger(dd->dai, cmd, dev->params.direction); - /* go through */ - case COMP_STATE_PREPARE: - dd->last_bytes = 0; - dev->state = COMP_STATE_SETUP; - break; - } + dev->state = COMP_STATE_PAUSED; break; case COMP_CMD_RELEASE: - /* only release from paused*/ - if (dev->state == COMP_STATE_PAUSED) { - dai_trigger(dd->dai, cmd, dev->params.direction); - dma_release(dd->dma, dd->chan); - - /* update starting wallclock */ - platform_dai_wallclock(dev, &dd->wallclock); - dev->state = COMP_STATE_RUNNING; - } - break; case COMP_CMD_START: - /* only start from prepared*/ - if (dev->state == COMP_STATE_PREPARE) { - ret = dma_start(dd->dma, dd->chan); - if (ret < 0) - return ret; - dai_trigger(dd->dai, cmd, dev->params.direction); - - /* update starting wallclock */ - platform_dai_wallclock(dev, &dd->wallclock); - dev->state = COMP_STATE_RUNNING; - } + + ret = dma_start(dd->dma, dd->chan); + if (ret < 0) + return ret; + dai_trigger(dd->dai, cmd, dev->params.direction); + + /* update starting wallclock */ + platform_dai_wallclock(dev, &dd->wallclock); + dev->state = COMP_STATE_ACTIVE; break; case COMP_CMD_SUSPEND: case COMP_CMD_RESUME: break; - case COMP_CMD_IPC_MMAP_PPOS: - dd->dai_pos = data; - if (dd->dai_pos) - *dd->dai_pos = 0; - break; default: break; } diff --git a/src/audio/eq_iir.c b/src/audio/eq_iir.c index 66a613c..4a4cff1 100644 --- a/src/audio/eq_iir.c +++ b/src/audio/eq_iir.c @@ -398,27 +398,26 @@ static int eq_iir_cmd(struct comp_dev *dev, int cmd, void *data) break; case COMP_CMD_START: trace_eq_iir("EFs"); - dev->state = COMP_STATE_RUNNING; + dev->state = COMP_STATE_ACTIVE; break; case COMP_CMD_STOP: trace_eq_iir("ESp"); - if (dev->state == COMP_STATE_RUNNING || - dev->state == COMP_STATE_DRAINING || + if (dev->state == COMP_STATE_ACTIVE || dev->state == COMP_STATE_PAUSED) { comp_buffer_reset(dev); - dev->state = COMP_STATE_SETUP; + dev->state = COMP_STATE_READY; } break; case COMP_CMD_PAUSE: trace_eq_iir("EPe"); /* only support pausing for running */ - if (dev->state == COMP_STATE_RUNNING) + if (dev->state == COMP_STATE_ACTIVE) dev->state = COMP_STATE_PAUSED;
break; case COMP_CMD_RELEASE: trace_eq_iir("ERl"); - dev->state = COMP_STATE_RUNNING; + dev->state = COMP_STATE_ACTIVE; break; default: trace_eq_iir("EDf"); diff --git a/src/audio/host.c b/src/audio/host.c index e61e2d9..36e4c29 100644 --- a/src/audio/host.c +++ b/src/audio/host.c @@ -527,7 +527,7 @@ static int host_stop(struct comp_dev *dev) /* now reset downstream buffer */ comp_buffer_reset(dev);
- dev->state = COMP_STATE_SETUP; + dev->state = COMP_STATE_READY; return 0; }
@@ -552,8 +552,7 @@ static int host_cmd(struct comp_dev *dev, int cmd, void *data) // TODO: align cmd macros. switch (cmd) { case COMP_CMD_STOP: - if (dev->state == COMP_STATE_RUNNING || - dev->state == COMP_STATE_DRAINING || + if (dev->state == COMP_STATE_ACTIVE || dev->state == COMP_STATE_PAUSED) ret = host_stop(dev); break; @@ -631,7 +630,7 @@ static int host_copy(struct comp_dev *dev)
tracev_host("cpy");
- if (dev->state != COMP_STATE_RUNNING) + if (dev->state != COMP_STATE_ACTIVE) return 0;
local_elem = list_first_item(&hd->config.elem_list, diff --git a/src/audio/mixer.c b/src/audio/mixer.c index a4dea4d..30ac8cc 100644 --- a/src/audio/mixer.c +++ b/src/audio/mixer.c @@ -270,7 +270,7 @@ static int mixer_reset(struct comp_dev *dev) list_for_item(blist, &dev->bsource_list) { source = container_of(blist, struct comp_buffer, sink_list); /* only mix the sources with the same state with mixer*/ - if (source->source->state > COMP_STATE_SETUP) + if (source->source->state > COMP_STATE_READY) return 1; /* should not reset the downstream components */ }
@@ -295,7 +295,7 @@ static int mixer_prepare(struct comp_dev *dev)
trace_mixer("pre");
- if (dev->state != COMP_STATE_RUNNING) { + if (dev->state != COMP_STATE_ACTIVE) { md->mix_func = mix_n; dev->state = COMP_STATE_PREPARE; //dev->preload = PLAT_INT_PERIODS; @@ -307,7 +307,7 @@ static int mixer_prepare(struct comp_dev *dev)
/* only prepare downstream if we have no active sources */ if (source->source->state == COMP_STATE_PAUSED || - source->source->state == COMP_STATE_RUNNING) { + source->source->state == COMP_STATE_ACTIVE) { downstream = 1; } } diff --git a/src/audio/pipeline.c b/src/audio/pipeline.c index 2800d64..c7b95f9 100644 --- a/src/audio/pipeline.c +++ b/src/audio/pipeline.c @@ -237,7 +237,7 @@ int pipeline_free(struct pipeline *p) trace_pipe("fre");
/* make sure we are not in use */ - if (p->sched_comp->state > COMP_STATE_SETUP) { + if (p->sched_comp->state > COMP_STATE_READY) { trace_pipe_error("epb"); return -EBUSY; } @@ -325,7 +325,7 @@ static int component_op_downstream(struct op_data *op_data, case COMP_OPS_PARAMS:
/* dont do any params downstream if current is running */ - if (current->state == COMP_STATE_RUNNING) + if (current->state == COMP_STATE_ACTIVE) return 0;
/* send params to the component */ @@ -401,7 +401,7 @@ static int component_op_upstream(struct op_data *op_data, case COMP_OPS_PARAMS:
/* dont do any params upstream if current is running */ - if (current->state == COMP_STATE_RUNNING) + if (current->state == COMP_STATE_ACTIVE) return 0;
/* send params to the component */ @@ -688,7 +688,7 @@ static int pipeline_copy_from_upstream(struct comp_dev *start, buffer = container_of(clist, struct comp_buffer, sink_list);
/* dont go upstream if this component is not connected */ - if (!buffer->connected || buffer->source->state != COMP_STATE_RUNNING) + if (!buffer->connected || buffer->source->state != COMP_STATE_ACTIVE) continue;
/* dont go upstream if this source is from another pipeline */ @@ -746,7 +746,7 @@ static int pipeline_copy_to_downstream(struct comp_dev *start, buffer = container_of(clist, struct comp_buffer, source_list);
/* dont go downstream if this component is not connected */ - if (!buffer->connected || buffer->sink->state != COMP_STATE_RUNNING) + if (!buffer->connected || buffer->sink->state != COMP_STATE_ACTIVE) continue;
/* dont go downstream if this sink is from another pipeline */ @@ -794,7 +794,7 @@ downstream: buffer = container_of(clist, struct comp_buffer, source_list);
/* dont go downstream if this component is not connected */ - if (!buffer->connected || buffer->sink->state != COMP_STATE_RUNNING) + if (!buffer->connected || buffer->sink->state != COMP_STATE_ACTIVE) continue;
/* continue downstream */ @@ -837,7 +837,7 @@ upstream: buffer = container_of(clist, struct comp_buffer, sink_list);
/* dont go downstream if this component is not connected */ - if (!buffer->connected || buffer->source->state != COMP_STATE_RUNNING) + if (!buffer->connected || buffer->source->state != COMP_STATE_ACTIVE) continue;
/* continue downstream */ @@ -942,7 +942,7 @@ void pipeline_xrun(struct pipeline *p, struct comp_dev *dev, return;
/* only send when we are running */ - if (dev->state != COMP_STATE_RUNNING) + if (dev->state != COMP_STATE_ACTIVE) return;
memset(&posn, 0, sizeof(posn)); diff --git a/src/drivers/dw-dma.c b/src/drivers/dw-dma.c index e851e54..1395ecc 100644 --- a/src/drivers/dw-dma.c +++ b/src/drivers/dw-dma.c @@ -56,6 +56,7 @@ #include <reef/lock.h> #include <reef/trace.h> #include <reef/wait.h> +#include <reef/audio/component.h> #include <platform/dma.h> #include <platform/platform.h> #include <platform/interrupt.h> @@ -160,8 +161,6 @@ struct dma_chan_data { uint32_t status; uint32_t direction; - completion_t complete; - int32_t drain_count; struct dw_lli2 *lli; struct dw_lli2 *lli_current; uint32_t desc_count; @@ -170,8 +169,6 @@ struct dma_chan_data { struct dma *dma; int32_t channel;
- struct work work; - void (*cb)(void *data, uint32_t type, struct dma_sg_elem *next); /* client callback function */ void *cb_data; /* client callback data */ int cb_type; /* callback type */ @@ -218,10 +215,10 @@ static int dw_dma_channel_get(struct dma *dma) for (i = 0; i < DW_MAX_CHAN; i++) {
/* use channel if it's free */ - if (p->chan[i].status != DMA_STATUS_FREE) + if (p->chan[i].status != COMP_STATE_READY) continue;
- p->chan[i].status = DMA_STATUS_IDLE; + p->chan[i].status = COMP_STATE_PREPARE;
/* unmask block, transfer and error interrupts for channel */ dw_write(dma, DW_MASK_TFR, INT_UNMASK(i)); @@ -244,27 +241,7 @@ static void dw_dma_channel_put_unlocked(struct dma *dma, int channel) { struct dma_pdata *p = dma_get_drvdata(dma);
- trace_dma("Dpt"); - - /* channel can only be freed if it's not still draining */ - if (p->chan[channel].status == DMA_STATUS_DRAINING) { - p->chan[channel].status = DMA_STATUS_CLOSING; - return; - } - -#ifdef DW_CFG_CH_DRAIN /* have drain feature */ - if (p->chan[channel].status == DMA_STATUS_PAUSED) { - - dw_update_bits(dma, DW_CFG_LOW(channel), - DW_CFG_CH_SUSPEND | DW_CFG_CH_DRAIN, - DW_CFG_CH_SUSPEND | DW_CFG_CH_DRAIN); - - /* free channel later */ - p->chan[channel].status = DMA_STATUS_CLOSING; - work_schedule_default(&p->chan[channel].work, 100); - return; - } -#endif + tracev_dma("Dpt");
/* mask block, transfer and error interrupts for channel */ dw_write(dma, DW_MASK_TFR, INT_MASK(channel)); @@ -278,7 +255,7 @@ static void dw_dma_channel_put_unlocked(struct dma *dma, int channel) }
/* set new state */ - p->chan[channel].status = DMA_STATUS_FREE; + p->chan[channel].status = COMP_STATE_READY; p->chan[channel].cb = NULL; p->chan[channel].desc_count = 0; } @@ -304,7 +281,7 @@ static int dw_dma_start(struct dma *dma, int channel) tracev_dma("DEn");
/* is channel idle, disabled and ready ? */ - if (p->chan[channel].status != DMA_STATUS_IDLE || + if (p->chan[channel].status != COMP_STATE_PREPARE || (dw_read(dma, DW_DMA_CHAN_EN) & (0x1 << channel))) { ret = -EBUSY; trace_dma_error("eDi"); @@ -364,7 +341,7 @@ static int dw_dma_start(struct dma *dma, int channel) dw_write(dma, DW_CFG_HIGH(channel), p->chan[channel].cfg_hi);
/* enable the channel */ - p->chan[channel].status = DMA_STATUS_RUNNING; + p->chan[channel].status = COMP_STATE_ACTIVE; p->chan[channel].lli_current = p->chan[channel].lli; dw_write(dma, DW_DMA_CHAN_EN, CHAN_ENABLE(channel));
@@ -382,12 +359,12 @@ static int dw_dma_release(struct dma *dma, int channel)
trace_dma("Dpr");
- if (p->chan[channel].status == DMA_STATUS_PAUSED) { + if (p->chan[channel].status == COMP_STATE_PAUSED) { dw_dma_chan_reload_lli(dma, channel); }
/* resume and reload DMA */ - p->chan[channel].status = DMA_STATUS_RUNNING; + p->chan[channel].status = COMP_STATE_ACTIVE;
spin_unlock_irq(&dma->lock, flags); return 0; @@ -402,136 +379,37 @@ static int dw_dma_pause(struct dma *dma, int channel)
trace_dma("Dpa");
- if (p->chan[channel].status != DMA_STATUS_RUNNING) + if (p->chan[channel].status != COMP_STATE_ACTIVE) goto out;
/* pause the channel */ - p->chan[channel].status = DMA_STATUS_PAUSING; + p->chan[channel].status = COMP_STATE_PAUSED;
out: spin_unlock_irq(&dma->lock, flags); return 0; }
-/* - * Wait for DMA drain completion using delayed work. This allows the stream - * IPC to return immediately without blocking the host. This work is called - * by the general system timer. - */ -static uint32_t dw_dma_fifo_work(void *data, uint32_t udelay) -{ - struct dma_chan_data *cd = (struct dma_chan_data *)data; - struct dma *dma = cd->dma; - int schedule = 0; - uint32_t flags; - - spin_lock_irq(&dma->lock, flags); - - trace_dma("DFw"); - - /* only check channels that are still draining */ - if (cd->status != DMA_STATUS_DRAINING && - cd->status != DMA_STATUS_CLOSING && - cd->status != DMA_STATUS_STOPPING) - goto out; - - /* is channel finished */ - if (cd->drain_count-- <= 0) { - trace_dma("wDw"); - - /* do we need to free it ? */ - if (cd->status == DMA_STATUS_CLOSING) - dw_dma_channel_put_unlocked(dma, cd->channel); - - /* clear suspend */ - dw_update_bits(dma, DW_CFG_LOW(cd->channel), -#ifdef DW_CFG_CH_DRAIN /* have drain feature */ - DW_CFG_CH_SUSPEND | DW_CFG_CH_DRAIN, 0); -#else - DW_CFG_CH_SUSPEND, 0); -#endif - cd->status = DMA_STATUS_IDLE; - goto out; - } - - /* is draining complete ? */ - if (dw_read(dma, DW_CFG_LOW(cd->channel)) & DW_CFG_CH_FIFO_EMPTY) { - - /* fifo is empty so now check if channel is disabled */ - if (!(dw_read(dma, DW_DMA_CHAN_EN) & (0x1 << cd->channel))) { - - /* clear suspend */ - dw_update_bits(dma, DW_CFG_LOW(cd->channel), -#ifdef DW_CFG_CH_DRAIN /* have drain feature */ - DW_CFG_CH_SUSPEND | DW_CFG_CH_DRAIN, 0); -#else - DW_CFG_CH_SUSPEND, 0); -#endif - /* do we need to free it ? */ - if (cd->status == DMA_STATUS_CLOSING) - dw_dma_channel_put_unlocked(dma, cd->channel); - - cd->status = DMA_STATUS_IDLE; - wait_completed(&cd->complete); - goto out; - } - - /* disable the channel */ - dw_write(dma, DW_DMA_CHAN_EN, CHAN_DISABLE(cd->channel)); - } - - /* need to reschedule and check again */ - schedule = 100; - -out: - spin_unlock_irq(&dma->lock, flags); - - /* still waiting on more FIFOs to drain ? */ - return schedule; -} - -static int dw_dma_stop(struct dma *dma, int channel, int drain) +static int dw_dma_stop(struct dma *dma, int channel) { struct dma_pdata *p = dma_get_drvdata(dma); - int schedule = 0, ret = 0; - uint32_t flags, bits; + int ret = 0; + uint32_t flags;
spin_lock_irq(&dma->lock, flags);
- trace_dma("DDi"); + tracev_dma("DDi");
/* has channel already disabled ? */ if (!(dw_read(dma, DW_DMA_CHAN_EN) & (0x1 << channel))) { - p->chan[channel].status = DMA_STATUS_IDLE; + p->chan[channel].status = COMP_STATE_PREPARE; goto out; }
- /* suspend and drain */ - bits = DW_CFG_CH_SUSPEND; - p->chan[channel].drain_count = 3; - p->chan[channel].status = DMA_STATUS_STOPPING; - -#ifdef DW_CFG_CH_DRAIN /* have drain feature, then may drain */ - if (drain) { - bits |= DW_CFG_CH_DRAIN; - p->chan[channel].drain_count = 14; - p->chan[channel].status = DMA_STATUS_DRAINING; - } -#endif - - dw_update_bits(dma, DW_CFG_LOW(channel), bits, bits); - schedule = 1; + p->chan[channel].status = COMP_STATE_PAUSED;
out: spin_unlock_irq(&dma->lock, flags); - - /* buffer and FIFO drain done by general purpose timer */ - if (schedule) { - wait_clear(&p->chan[channel].complete); - work_schedule_default(&p->chan[channel].work, 100); - ret = wait_for_completion_timeout(&p->chan[channel].complete); - } - return ret; }
@@ -739,7 +617,7 @@ static inline void dw_dma_chan_reload_lli(struct dma *dma, int channel)
/* only need to reload if this is a block transfer */ if (lli == 0 || (lli && lli->llp == 0)) { - p->chan[channel].status = DMA_STATUS_IDLE; + p->chan[channel].status = COMP_STATE_PREPARE; return; }
@@ -833,9 +711,8 @@ static void dw_dma_irq_handler(void *data)
for (i = 0; i < DW_MAX_CHAN; i++) {
- /* skip if channel is not running or pausing*/ - if (p->chan[i].status != DMA_STATUS_RUNNING && - p->chan[i].status != DMA_STATUS_PAUSING) + /* skip if channel is not running */ + if (p->chan[i].status != COMP_STATE_ACTIVE) continue;
mask = 0x1 << i; @@ -857,14 +734,13 @@ static void dw_dma_irq_handler(void *data) * otherwise, reload lli */ if (next.size == DMA_RELOAD_END) { - p->chan[i].status = DMA_STATUS_IDLE; + p->chan[i].status = COMP_STATE_PREPARE; continue; } else if (next.size != DMA_RELOAD_LLI) dw_dma_chan_reload_next(dma, i, &next); /* reload lli, but let's check if we are pausing first */ - else if (p->chan[i].status == DMA_STATUS_PAUSING) { - p->chan[i].status = DMA_STATUS_PAUSED; + else if (p->chan[i].status == COMP_STATE_PAUSED) { continue; } else dw_dma_chan_reload_lli(dma, i); @@ -952,12 +828,9 @@ static int dw_dma_probe(struct dma *dma)
/* init work */ for (i = 0; i < DW_MAX_CHAN; i++) { - work_init(&dw_pdata->chan[i].work, dw_dma_fifo_work, - &dw_pdata->chan[i], WORK_ASYNC); dw_pdata->chan[i].dma = dma; dw_pdata->chan[i].channel = i; - dw_pdata->chan[i].complete.timeout = PLATFORM_DMA_TIMEOUT; - wait_init(&dw_pdata->chan[i].complete); + dw_pdata->chan[i].status = COMP_STATE_READY; }
/* register our IRQ handler */ diff --git a/src/drivers/ssp.c b/src/drivers/ssp.c index 0b7589a..6d2c004 100644 --- a/src/drivers/ssp.c +++ b/src/drivers/ssp.c @@ -76,9 +76,10 @@ static inline int ssp_set_config(struct dai *dai, spin_lock(&ssp->lock);
/* is playback/capture already running */ - if (ssp->state[DAI_DIR_PLAYBACK] > SSP_STATE_IDLE || - ssp->state[DAI_DIR_CAPTURE] > SSP_STATE_IDLE) { + if (ssp->state[DAI_DIR_PLAYBACK] == COMP_STATE_ACTIVE || + ssp->state[DAI_DIR_CAPTURE] == COMP_STATE_ACTIVE) { trace_ssp_error("ec1"); + ret = -EINVAL; goto out; }
@@ -258,8 +259,8 @@ static inline int ssp_set_config(struct dai *dai, ssp_write(dai, SSPSP, sspsp); ssp_write(dai, SFIFOTT, sfifott);
- ssp->state[DAI_DIR_PLAYBACK] = SSP_STATE_IDLE; - ssp->state[DAI_DIR_CAPTURE] = SSP_STATE_IDLE; + ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE; + ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_PREPARE;
out: spin_unlock(&ssp->lock); @@ -291,7 +292,7 @@ static void ssp_start(struct dai *dai, int direction)
/* enable port */ ssp_update_bits(dai, SSCR0, SSCR0_SSE, SSCR0_SSE); - ssp->state[direction] = SSP_STATE_RUNNING; + ssp->state[direction] = COMP_STATE_ACTIVE;
trace_ssp("sta");
@@ -304,70 +305,37 @@ static void ssp_start(struct dai *dai, int direction) spin_unlock(&ssp->lock); }
-/* stop the SSP port stream DMA and disable SSP port if no users */ +/* stop the SSP for either playback or capture */ static void ssp_stop(struct dai *dai, int direction) { struct ssp_pdata *ssp = dai_get_drvdata(dai); - uint32_t sscr1;
spin_lock(&ssp->lock);
- trace_ssp("sto"); - - /* disable DMA */ - if (direction == DAI_DIR_PLAYBACK) { - if (ssp->state[DAI_DIR_PLAYBACK] == SSP_STATE_DRAINING) - ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0); - } else + /* stop Rx if we are not capturing */ + if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE) { ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0); - - /* disable port if no users */ - sscr1 = ssp_read(dai, SSCR1); - if (!(sscr1 & (SSCR1_TSRE | SSCR1_RSRE))) { - ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0); - trace_ssp("SDp"); + trace_ssp("Ss0"); }
- ssp->state[direction] = SSP_STATE_IDLE; - - spin_unlock(&ssp->lock); -} - -static void ssp_pause(struct dai *dai, int direction) -{ - struct ssp_pdata *ssp = dai_get_drvdata(dai); - - spin_lock(&ssp->lock); - - trace_ssp("pau"); - - /* disable DMA */ - if (direction == DAI_DIR_PLAYBACK) { - if (ssp->state[DAI_DIR_PLAYBACK] == SSP_STATE_PAUSING) - ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0); - } else - ssp_update_bits(dai, SSCR1, SSCR1_RSRE, 0); + /* stop Tx if we are not playing */ + if (ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) { + ssp_update_bits(dai, SSCR1, SSCR1_TSRE, 0); + trace_ssp("Ss1"); + }
- ssp->state[direction] = SSP_STATE_PAUSED; + /* disable SSP port if no users */ + if (ssp->state[SOF_IPC_STREAM_CAPTURE] != COMP_STATE_ACTIVE && + ssp->state[SOF_IPC_STREAM_PLAYBACK] != COMP_STATE_ACTIVE) { + ssp_update_bits(dai, SSCR0, SSCR0_SSE, 0); + ssp->state[SOF_IPC_STREAM_CAPTURE] = COMP_STATE_PREPARE; + ssp->state[SOF_IPC_STREAM_PLAYBACK] = COMP_STATE_PREPARE; + trace_ssp("Ss2"); + }
spin_unlock(&ssp->lock); }
-static uint32_t ssp_drain_work(void *data, uint32_t udelay) -{ - struct dai *dai = (struct dai *)data; - struct ssp_pdata *ssp = dai_get_drvdata(dai); - - trace_ssp("dra"); - - if (ssp->state[SOF_IPC_STREAM_CAPTURE] == SSP_STATE_DRAINING) - ssp_stop(dai, SOF_IPC_STREAM_CAPTURE); - else - ssp_pause(dai, SOF_IPC_STREAM_CAPTURE); - wait_completed(&ssp->drain_complete); - return 0; -} - static int ssp_trigger(struct dai *dai, int cmd, int direction) { struct ssp_pdata *ssp = dai_get_drvdata(dai); @@ -376,63 +344,23 @@ static int ssp_trigger(struct dai *dai, int cmd, int direction)
switch (cmd) { case COMP_CMD_START: -/* let's only wait until draining finished(timout) before another start */ -#if 0 - /* cancel any scheduled work */ - if (ssp->state[direction] == SSP_STATE_DRAINING) - work_cancel_default(&ssp->work); -#endif - if (ssp->state[direction] == SSP_STATE_IDLE) + if (ssp->state[direction] == COMP_STATE_PREPARE || + ssp->state[direction] == COMP_STATE_PAUSED) ssp_start(dai, direction); break; case COMP_CMD_RELEASE: -/* let's only wait until pausing finished(timout) before next release */ -#if 0 - if (ssp->state[direction] == SSP_STATE_PAUSING) - work_cancel_default(&ssp->work); -#endif - if (ssp->state[direction] == SSP_STATE_PAUSED) + if (ssp->state[direction] == COMP_STATE_PAUSED) ssp_start(dai, direction); break; - case COMP_CMD_PAUSE: - if (ssp->state[direction] != SSP_STATE_RUNNING) { - trace_ssp_error("wsP"); - return 0; - } - if (direction == SOF_IPC_STREAM_CAPTURE) { - ssp->state[SOF_IPC_STREAM_CAPTURE] = - SSP_STATE_PAUSING; - /* make sure the maximum 256 bytes are drained */ - work_schedule_default(&ssp->work, 1333); - wait_init(&ssp->drain_complete); - ssp->drain_complete.timeout = 1500; - wait_for_completion_timeout(&ssp->drain_complete); - } else - ssp_pause(dai, direction); - break; case COMP_CMD_STOP: - if (ssp->state[direction] != SSP_STATE_RUNNING && - ssp->state[direction] != SSP_STATE_PAUSED) { - trace_ssp_error("wsO"); - return 0; - } - if (direction == SOF_IPC_STREAM_PLAYBACK && - ssp->state[direction] == SSP_STATE_RUNNING) { - ssp->state[SOF_IPC_STREAM_PLAYBACK] = - SSP_STATE_DRAINING; - work_schedule_default(&ssp->work, 2000); - wait_init(&ssp->drain_complete); - ssp->drain_complete.timeout = 3000; - wait_for_completion_timeout(&ssp->drain_complete); - } else - ssp_stop(dai, direction); + case COMP_CMD_PAUSE: + ssp->state[direction] = COMP_STATE_PAUSED; + ssp_stop(dai, direction); break; case COMP_CMD_RESUME: ssp_context_restore(dai); - ssp_start(dai, direction); break; case COMP_CMD_SUSPEND: - ssp_stop(dai, direction); ssp_context_store(dai); break; default: @@ -450,11 +378,10 @@ static int ssp_probe(struct dai *dai) ssp = rzalloc(RZONE_SYS, RFLAGS_NONE, sizeof(*ssp)); dai_set_drvdata(dai, ssp);
- work_init(&ssp->work, ssp_drain_work, dai, WORK_ASYNC); spinlock_init(&ssp->lock);
- ssp->state[DAI_DIR_PLAYBACK] = SSP_STATE_INIT; - ssp->state[DAI_DIR_CAPTURE] = SSP_STATE_INIT; + ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_READY; + ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_READY;
return 0; } diff --git a/src/include/reef/audio/component.h b/src/include/reef/audio/component.h index d7ea29a..d092210 100644 --- a/src/include/reef/audio/component.h +++ b/src/include/reef/audio/component.h @@ -44,20 +44,29 @@ #include <reef/audio/pipeline.h> #include <uapi/ipc.h>
-/* audio component states - * the states may transform as below: - * new() params() start() - * none -----> init ------> setup -----> running - * none <----- init <------ setup <----- running - * free() reset() stop() +/* + * Audio Component States + * + * States may transform as below:- + * + * 1) i.e. Initialisation to playback and pause/release + * init --> setup --> preprare --> active <-> paused --+ + * ^ | + * +-----------------------------+ + * + * 2) i.e. Suspend + * + * setup --> suspend --> setup OR + * prepare --> suspend -> prepare OR + * paused --> suspend --> paused */ + #define COMP_STATE_INIT 0 /* component being initialised */ -#define COMP_STATE_SETUP 1 /* component inactive, but ready */ -#define COMP_STATE_SUSPEND 2 /* component suspended */ -#define COMP_STATE_DRAINING 3 /* component draining */ -#define COMP_STATE_PREPARE 4 /* component prepared */ -#define COMP_STATE_PAUSED 5 /* component paused */ -#define COMP_STATE_RUNNING 6 /* component active */ +#define COMP_STATE_READY 1 /* component inactive, but ready */ +#define COMP_STATE_SUSPEND 2 /* component suspended */ +#define COMP_STATE_PREPARE 3 /* component prepared */ +#define COMP_STATE_PAUSED 4 /* component paused */ +#define COMP_STATE_ACTIVE 5 /* component active */
/* * standard component stream commands diff --git a/src/include/reef/dma.h b/src/include/reef/dma.h index 6311260..881332f 100644 --- a/src/include/reef/dma.h +++ b/src/include/reef/dma.h @@ -45,16 +45,6 @@ #define DMA_DIR_DEV_TO_MEM 4 #define DMA_DIR_DEV_TO_DEV 5
-/* DMA status flags */ -#define DMA_STATUS_FREE 0 -#define DMA_STATUS_IDLE 1 -#define DMA_STATUS_RUNNING 2 -#define DMA_STATUS_DRAINING 4 -#define DMA_STATUS_CLOSING 5 -#define DMA_STATUS_PAUSED 6 -#define DMA_STATUS_PAUSING 7 -#define DMA_STATUS_STOPPING 8 - /* DMA IRQ types */ #define DMA_IRQ_TYPE_BLOCK (1 << 0) #define DMA_IRQ_TYPE_LLIST (1 << 1) @@ -101,7 +91,7 @@ struct dma_ops { void (*channel_put)(struct dma *dma, int channel);
int (*start)(struct dma *dma, int channel); - int (*stop)(struct dma *dma, int channel, int drain); + int (*stop)(struct dma *dma, int channel); int (*pause)(struct dma *dma, int channel); int (*release)(struct dma *dma, int channel); int (*status)(struct dma *dma, int channel, @@ -180,9 +170,9 @@ static inline int dma_start(struct dma *dma, int channel) return dma->ops->start(dma, channel); }
-static inline int dma_stop(struct dma *dma, int channel, int drain) +static inline int dma_stop(struct dma *dma, int channel) { - return dma->ops->stop(dma, channel, drain); + return dma->ops->stop(dma, channel); }
static inline int dma_pause(struct dma *dma, int channel) diff --git a/src/include/reef/ssp.h b/src/include/reef/ssp.h index 42efb14..70113df 100644 --- a/src/include/reef/ssp.h +++ b/src/include/reef/ssp.h @@ -149,14 +149,6 @@ extern const struct dai_ops ssp_ops; #define SFIFOTT_TX(x) (x - 1) #define SFIFOTT_RX(x) ((x - 1) << 16)
-/* SSP port status */ -#define SSP_STATE_INIT 0 -#define SSP_STATE_RUNNING 1 -#define SSP_STATE_IDLE 2 -#define SSP_STATE_DRAINING 3 -#define SSP_STATE_PAUSING 4 -#define SSP_STATE_PAUSED 5 - /* tracing */ #define trace_ssp(__e) trace_event(TRACE_CLASS_SSP, __e) #define trace_ssp_error(__e) trace_error(TRACE_CLASS_SSP, __e) @@ -167,7 +159,6 @@ struct ssp_pdata { uint32_t sscr0; uint32_t sscr1; uint32_t psp; - struct work work; spinlock_t lock; uint32_t state[2]; /* SSP_STATE_ for each direction */ completion_t drain_complete;
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/include/reef/clock.h | 2 +- src/platform/baytrail/clk.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/include/reef/clock.h b/src/include/reef/clock.h index 305044c..f43f30a 100644 --- a/src/include/reef/clock.h +++ b/src/include/reef/clock.h @@ -51,7 +51,7 @@ uint32_t clock_set_freq(int clock, unsigned int hz);
uint32_t clock_get_freq(int clock);
-uint32_t clock_us_to_ticks(int clock, uint32_t us); +uint64_t clock_us_to_ticks(int clock, uint64_t us);
uint32_t clock_time_elapsed(int clock, uint32_t previous, uint32_t *current);
diff --git a/src/platform/baytrail/clk.c b/src/platform/baytrail/clk.c index fdffcd5..1b1504e 100644 --- a/src/platform/baytrail/clk.c +++ b/src/platform/baytrail/clk.c @@ -225,7 +225,7 @@ uint32_t clock_get_freq(int clock) return clk_pdata->clk[clock].freq; }
-uint32_t clock_us_to_ticks(int clock, uint32_t us) +uint64_t clock_us_to_ticks(int clock, uint64_t us) { return clk_pdata->clk[clock].ticks_per_usec * us; }
Add an interrupt handler to slear any SSP IRQs. TODO: extend this to report XRUNS and other errors.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/drivers/ssp.c | 23 +++++++++++++++++++++++ src/include/reef/ssp.h | 6 +++++- 2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/src/drivers/ssp.c b/src/drivers/ssp.c index 6d2c004..547c91c 100644 --- a/src/drivers/ssp.c +++ b/src/drivers/ssp.c @@ -370,6 +370,24 @@ static int ssp_trigger(struct dai *dai, int cmd, int direction) return 0; }
+/* The IRQ handler allows the SSP port to drain the playback FIFO to make sure + * every sample has been played */ +static void ssp_irq_handler(void *data) +{ + struct dai *dai = data; + int i; + + trace_value(ssp_read(dai, SSSR)); + + /* empty Rx FIFO */ + for (i = 0; i < 16; i++) + ssp_read(dai, SSDR); + + /* clear IRQ */ + ssp_write(dai, SSSR, ssp_read(dai, SSSR)); + platform_interrupt_clear(ssp_irq(dai), 1); +} + static int ssp_probe(struct dai *dai) { struct ssp_pdata *ssp; @@ -383,6 +401,11 @@ static int ssp_probe(struct dai *dai) ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_READY; ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_READY;
+ /* register our IRQ handler */ + interrupt_register(ssp_irq(dai), ssp_irq_handler, dai); + platform_interrupt_unmask(ssp_irq(dai), 1); + interrupt_enable(ssp_irq(dai)); + return 0; }
diff --git a/src/include/reef/ssp.h b/src/include/reef/ssp.h index 70113df..448a0dd 100644 --- a/src/include/reef/ssp.h +++ b/src/include/reef/ssp.h @@ -78,7 +78,7 @@ extern const struct dai_ops ssp_ops; #define SSCR0_EDSS (1 << 20) #define SSCR0_NCS (1 << 21) #define SSCR0_RIM (1 << 22) -#define SSCR0_TUM (1 << 23) +#define SSCR0_TIM (1 << 23) #define SSCR0_FRDC(x) ((x - 1) << 24) #define SSCR0_ACS (1 << 30) #define SSCR0_MOD (1 << 31) @@ -154,6 +154,10 @@ extern const struct dai_ops ssp_ops; #define trace_ssp_error(__e) trace_error(TRACE_CLASS_SSP, __e) #define tracev_ssp(__e) tracev_event(TRACE_CLASS_SSP, __e)
+ +#define ssp_irq(ssp) \ + ssp->plat_data.irq + /* SSP private data */ struct ssp_pdata { uint32_t sscr0;
On 9/20/17 8:58 AM, Liam Girdwood wrote:
Add an interrupt handler to slear any SSP IRQs. TODO: extend this to report XRUNS and other errors.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com
src/drivers/ssp.c | 23 +++++++++++++++++++++++ src/include/reef/ssp.h | 6 +++++- 2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/src/drivers/ssp.c b/src/drivers/ssp.c index 6d2c004..547c91c 100644 --- a/src/drivers/ssp.c +++ b/src/drivers/ssp.c @@ -370,6 +370,24 @@ static int ssp_trigger(struct dai *dai, int cmd, int direction) return 0; }
+/* The IRQ handler allows the SSP port to drain the playback FIFO to make sure
- every sample has been played */
+static void ssp_irq_handler(void *data) +{
- struct dai *dai = data;
- int i;
- trace_value(ssp_read(dai, SSSR));
- /* empty Rx FIFO */
- for (i = 0; i < 16; i++)
ssp_read(dai, SSDR);
I am having a hard-time with this one. Is this really the playback FIFO that we are draining if we read from a FIFO? and why do we need to drain, it's that what DMAs are for?
- /* clear IRQ */
- ssp_write(dai, SSSR, ssp_read(dai, SSSR));
- platform_interrupt_clear(ssp_irq(dai), 1);
+}
- static int ssp_probe(struct dai *dai) { struct ssp_pdata *ssp;
@@ -383,6 +401,11 @@ static int ssp_probe(struct dai *dai) ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_READY; ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_READY;
- /* register our IRQ handler */
- interrupt_register(ssp_irq(dai), ssp_irq_handler, dai);
- platform_interrupt_unmask(ssp_irq(dai), 1);
- interrupt_enable(ssp_irq(dai));
- return 0; }
diff --git a/src/include/reef/ssp.h b/src/include/reef/ssp.h index 70113df..448a0dd 100644 --- a/src/include/reef/ssp.h +++ b/src/include/reef/ssp.h @@ -78,7 +78,7 @@ extern const struct dai_ops ssp_ops; #define SSCR0_EDSS (1 << 20) #define SSCR0_NCS (1 << 21) #define SSCR0_RIM (1 << 22) -#define SSCR0_TUM (1 << 23) +#define SSCR0_TIM (1 << 23) #define SSCR0_FRDC(x) ((x - 1) << 24) #define SSCR0_ACS (1 << 30) #define SSCR0_MOD (1 << 31) @@ -154,6 +154,10 @@ extern const struct dai_ops ssp_ops; #define trace_ssp_error(__e) trace_error(TRACE_CLASS_SSP, __e) #define tracev_ssp(__e) tracev_event(TRACE_CLASS_SSP, __e)
+#define ssp_irq(ssp) \
- ssp->plat_data.irq
- /* SSP private data */ struct ssp_pdata { uint32_t sscr0;
On Wed, 2017-09-20 at 10:38 -0500, Pierre-Louis Bossart wrote:
On 9/20/17 8:58 AM, Liam Girdwood wrote:
Add an interrupt handler to slear any SSP IRQs. TODO: extend this to report XRUNS and other errors.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com
src/drivers/ssp.c | 23 +++++++++++++++++++++++ src/include/reef/ssp.h | 6 +++++- 2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/src/drivers/ssp.c b/src/drivers/ssp.c index 6d2c004..547c91c 100644 --- a/src/drivers/ssp.c +++ b/src/drivers/ssp.c @@ -370,6 +370,24 @@ static int ssp_trigger(struct dai *dai, int cmd, int direction) return 0; }
+/* The IRQ handler allows the SSP port to drain the playback FIFO to make sure
- every sample has been played */
+static void ssp_irq_handler(void *data) +{
- struct dai *dai = data;
- int i;
- trace_value(ssp_read(dai, SSSR));
- /* empty Rx FIFO */
- for (i = 0; i < 16; i++)
ssp_read(dai, SSDR);
I am having a hard-time with this one. Is this really the playback FIFO that we are draining if we read from a FIFO? and why do we need to drain, it's that what DMAs are for?
Oh comment is wrong. I'll fix. FIFO loop should be removed too.
Liam
Make sure we have a valid pipeline for stream free IPC calls.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/ipc/intel-ipc.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/src/ipc/intel-ipc.c b/src/ipc/intel-ipc.c index 0c1b10a..fb6c846 100644 --- a/src/ipc/intel-ipc.c +++ b/src/ipc/intel-ipc.c @@ -287,6 +287,13 @@ static int ipc_stream_pcm_free(uint32_t header) return -ENODEV; }
+ /* sanity check comp */ + if (pcm_dev->cd->pipeline == NULL) { + trace_ipc_error("eF1"); + trace_value(free_req->comp_id); + return -EINVAL; + } + /* reset the pipeline */ return pipeline_reset(pcm_dev->cd->pipeline, pcm_dev->cd); }
DAI driver is generic so shouldn't refer to SSP.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/audio/dai.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/audio/dai.c b/src/audio/dai.c index 7f4cde6..7859267 100644 --- a/src/audio/dai.c +++ b/src/audio/dai.c @@ -138,7 +138,7 @@ static void dai_dma_cb(void *data, uint32_t type, struct dma_sg_elem *next) return; }
-static struct comp_dev *dai_new_ssp(struct sof_ipc_comp *comp) +static struct comp_dev *dai_new(struct sof_ipc_comp *comp) { struct comp_dev *dev; struct sof_ipc_comp_dai *dai; @@ -525,7 +525,7 @@ static int dai_config(struct comp_dev *dev, struct sof_ipc_dai_config *config) static struct comp_driver comp_dai = { .type = SOF_COMP_DAI, .ops = { - .new = dai_new_ssp, + .new = dai_new, .free = dai_free, .params = dai_params, .cmd = dai_cmd,
Make sure we reset the DAI wallclock to 0 during reset()
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/audio/dai.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/src/audio/dai.c b/src/audio/dai.c index 7859267..ebec519 100644 --- a/src/audio/dai.c +++ b/src/audio/dai.c @@ -433,6 +433,7 @@ static int dai_reset(struct comp_dev *dev) *dd->dai_pos = 0; dd->dai_pos = NULL; dd->last_bytes = 0; + dd->wallclock = 0; dev->position = 0; dev->state = COMP_STATE_READY;
Reread the DMA IRQ status after clearing the IRQ source to make sure that the IRQ has been cleared correctly.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/drivers/dw-dma.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/src/drivers/dw-dma.c b/src/drivers/dw-dma.c index 1395ecc..7c6cd86 100644 --- a/src/drivers/dw-dma.c +++ b/src/drivers/dw-dma.c @@ -709,6 +709,11 @@ static void dw_dma_irq_handler(void *data) pmask = status_block | status_tfr | status_err; platform_interrupt_clear(dma_irq(dma), pmask);
+ /* confirm IRQ cleared */ + if (dw_read(dma, DW_STATUS_BLOCK)) { + trace_dma_error("eii"); + } + for (i = 0; i < DW_MAX_CHAN; i++) {
/* skip if channel is not running */
Configure FIFO levels. TODO add to topology.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/drivers/ssp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/drivers/ssp.c b/src/drivers/ssp.c index 547c91c..efd7a9a 100644 --- a/src/drivers/ssp.c +++ b/src/drivers/ssp.c @@ -249,7 +249,7 @@ static inline int ssp_set_config(struct dai *dai, sscr0 |= SSCR0_DSIZE(data_size);
/* watermarks - (RFT + 1) should equal DMA SRC_MSIZE */ - sfifott = (SFIFOTT_TX(8) | SFIFOTT_RX(8)); + sfifott = (SFIFOTT_TX(4) | SFIFOTT_RX(12));
trace_ssp("coe"); ssp_write(dai, SSCR0, sscr0);
Mask Rx/Tx FIFO status IRQs and make sure RWOT is always enabled.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/drivers/ssp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/drivers/ssp.c b/src/drivers/ssp.c index efd7a9a..b87806e 100644 --- a/src/drivers/ssp.c +++ b/src/drivers/ssp.c @@ -86,9 +86,9 @@ static inline int ssp_set_config(struct dai *dai, trace_ssp("cos");
/* reset SSP settings */ - sscr0 = 0; - sscr1 = 0; - sscr2 = 0xc1; + sscr0 = SSCR0_RIM | SSCR0_TIM; + sscr1 = SSCR1_PINTE | SSCR1_RWOT; + sscr2 = 0x1c1; sscr3 = 0x2c018; sspsp = 0;
@@ -104,7 +104,7 @@ static inline int ssp_set_config(struct dai *dai, sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR; break; case SOF_DAI_FMT_CBS_CFS: - sscr1 |= SSCR1_SCFR | SSCR1_RWOT; + sscr1 |= SSCR1_SCFR; sscr3 |= SSCR3_I2S_FRM_MST | SSCR3_I2S_CLK_MST; break; case SOF_DAI_FMT_CBM_CFS:
Add support for rescheduling work with a new deadline and for scheduling on a specific clock tick value.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/include/reef/work.h | 3 ++ src/lib/work.c | 74 ++++++++++++++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 28 deletions(-)
diff --git a/src/include/reef/work.h b/src/include/reef/work.h index 199e46c..516f98f 100644 --- a/src/include/reef/work.h +++ b/src/include/reef/work.h @@ -70,10 +70,13 @@ struct work_queue_timesource {
/* schedule/cancel work on work queue */ void work_schedule(struct work_queue *queue, struct work *w, uint64_t timeout); +void work_reschedule(struct work_queue *queue, struct work *w, uint64_t timeout); void work_cancel(struct work_queue *queue, struct work *work);
/* schedule/cancel work on default system work queue */ void work_schedule_default(struct work *work, uint64_t timeout); +void work_reschedule_default(struct work *work, uint64_t timeout); +void work_reschedule_default_at(struct work *w, uint64_t time); void work_cancel_default(struct work *work);
/* create new work queue */ diff --git a/src/lib/work.c b/src/lib/work.c index cac3deb..c53f029 100644 --- a/src/lib/work.c +++ b/src/lib/work.c @@ -351,64 +351,82 @@ out: spin_unlock_irq(&queue->lock, flags); }
-void work_cancel(struct work_queue *queue, struct work *w) +void work_schedule_default(struct work *w, uint64_t timeout) +{ + work_schedule(queue_, w, timeout); +} + +static void reschedule(struct work_queue *queue, struct work *w, uint64_t time) { + struct work *work; + struct list_item *wlist; uint32_t flags;
spin_lock_irq(&queue->lock, flags);
- /* remove work from list */ - list_item_del(&w->list); + /* check to see if we are already scheduled ? */ + list_for_item(wlist, &queue->work) { + work = container_of(wlist, struct work, list);
+ /* found it */ + if (work == w) + goto found; + } + + /* not found insert work into list */ + list_item_prepend(&w->list, &queue->work); + +found: /* re-calc timer and re-arm */ + w->timeout = time; queue_reschedule(queue);
spin_unlock_irq(&queue->lock, flags); }
-void work_schedule_default(struct work *w, uint64_t timeout) +void work_reschedule(struct work_queue *queue, struct work *w, uint64_t timeout) { - struct work *work; - struct list_item *wlist; - uint32_t flags; + uint64_t time;
- spin_lock_irq(&queue_->lock, flags); - - /* check to see if we are already scheduled ? */ - list_for_item(wlist, &queue_->work) { - work = container_of(wlist, struct work, list); + /* convert timeout micro seconds to CPU clock ticks */ + time = queue->ticks_per_usec * timeout + work_get_timer(queue);
- /* keep original timeout */ - if (work == w) - goto out; - } + reschedule(queue, w, time); +}
- /* convert timeout microsecs to CPU clock ticks */ - w->timeout = queue_->ticks_per_usec * timeout + work_get_timer(queue_); +void work_reschedule_default(struct work *w, uint64_t timeout) +{ + uint64_t time;
- /* insert work into list */ - list_item_prepend(&w->list, &queue_->work); + /* convert timeout micro seconds to CPU clock ticks */ + time = queue_->ticks_per_usec * timeout + work_get_timer(queue_);
- /* re-calc timer and re-arm */ - queue_reschedule(queue_); + reschedule(queue_, w, time); +}
-out: - spin_unlock_irq(&queue_->lock, flags); +void work_reschedule_default_at(struct work *w, uint64_t time) +{ + reschedule(queue_, w, time); }
-void work_cancel_default(struct work *w) +void work_cancel(struct work_queue *queue, struct work *w) { uint32_t flags;
- spin_lock_irq(&queue_->lock, flags); + spin_lock_irq(&queue->lock, flags);
/* remove work from list */ list_item_del(&w->list);
/* re-calc timer and re-arm */ - queue_reschedule(queue_); + queue_reschedule(queue);
- spin_unlock_irq(&queue_->lock, flags); + spin_unlock_irq(&queue->lock, flags); +} + +void work_cancel_default(struct work *w) +{ + work_cancel(queue_, w); }
struct work_queue *work_new_queue(struct work_queue_timesource *ts)
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/ipc/intel-ipc.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/src/ipc/intel-ipc.c b/src/ipc/intel-ipc.c index fb6c846..66ba937 100644 --- a/src/ipc/intel-ipc.c +++ b/src/ipc/intel-ipc.c @@ -396,6 +396,7 @@ static int ipc_stream_trigger(uint32_t header) cmd, NULL); if (ret < 0) { trace_ipc_error("eRc"); + trace_value(ipc_cmd); }
return ret;
participants (2)
-
Liam Girdwood
-
Pierre-Louis Bossart