struct rsnd_dma {
struct rsnd_mod mod;
+ struct rsnd_mod *mod_from;
+ struct rsnd_mod *mod_to;
dma_addr_t src_addr;
dma_addr_t dst_addr;
union {
rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
}
+static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod_from,
+ struct rsnd_mod *mod_to)
+{
+ if ((!mod_from && !mod_to) ||
+ (mod_from && mod_to))
+ return NULL;
+
+ if (mod_from)
+ return rsnd_mod_dma_req(io, mod_from);
+ else
+ return rsnd_mod_dma_req(io, mod_to);
+}
+
static int rsnd_dmaen_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
- dmaengine_terminate_all(dmaen->chan);
+ if (dmaen->chan) {
+ dmaengine_terminate_all(dmaen->chan);
+ }
+
+ return 0;
+}
+
+static int rsnd_dmaen_nolock_stop(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+ struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+
+ /*
+ * DMAEngine release uses mutex lock.
+ * Thus, it shouldn't be called under spinlock.
+ * Let's call it under nolock_start
+ */
+ if (dmaen->chan)
+ dma_release_channel(dmaen->chan);
+
+ dmaen->chan = NULL;
+
+ return 0;
+}
+
+static int rsnd_dmaen_nolock_start(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+ struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ if (dmaen->chan) {
+ dev_err(dev, "it already has dma channel\n");
+ return -EIO;
+ }
+
+ /*
+ * DMAEngine request uses mutex lock.
+ * Thus, it shouldn't be called under spinlock.
+ * Let's call it under nolock_start
+ */
+ dmaen->chan = rsnd_dmaen_request_channel(io,
+ dma->mod_from,
+ dma->mod_to);
+ if (IS_ERR_OR_NULL(dmaen->chan)) {
+ int ret = PTR_ERR(dmaen->chan);
+
+ dmaen->chan = NULL;
+ dev_err(dev, "can't get dma channel\n");
+ return ret;
+ }
return 0;
}
struct snd_pcm_substream *substream = io->substream;
struct device *dev = rsnd_priv_to_dev(priv);
struct dma_async_tx_descriptor *desc;
+ struct dma_slave_config cfg = {};
int is_play = rsnd_io_is_play(io);
+ int ret;
+
+ cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+ cfg.src_addr = dma->src_addr;
+ cfg.dst_addr = dma->dst_addr;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ dev_dbg(dev, "%s[%d] %pad -> %pad\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod),
+ &cfg.src_addr, &cfg.dst_addr);
+
+ ret = dmaengine_slave_config(dmaen->chan, &cfg);
+ if (ret < 0)
+ return ret;
desc = dmaengine_prep_dma_cyclic(dmaen->chan,
substream->runtime->dma_addr,
return chan;
}
-static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod_from,
- struct rsnd_mod *mod_to)
-{
- if ((!mod_from && !mod_to) ||
- (mod_from && mod_to))
- return NULL;
-
- if (mod_from)
- return rsnd_mod_dma_req(io, mod_from);
- else
- return rsnd_mod_dma_req(io, mod_to);
-}
-
-static int rsnd_dmaen_remove(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
- struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
-
- if (dmaen->chan)
- dma_release_channel(dmaen->chan);
-
- dmaen->chan = NULL;
-
- return 0;
-}
-
static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
struct rsnd_dma *dma,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{
- struct rsnd_mod *mod = rsnd_mod_get(dma);
- struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
- struct device *dev = rsnd_priv_to_dev(priv);
- struct dma_slave_config cfg = {};
- int is_play = rsnd_io_is_play(io);
- int ret;
-
- if (dmaen->chan) {
- dev_err(dev, "it already has dma channel\n");
- return -EIO;
- }
-
- dmaen->chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
-
- if (IS_ERR_OR_NULL(dmaen->chan)) {
- dmaen->chan = NULL;
- dev_err(dev, "can't get dma channel\n");
- goto rsnd_dma_channel_err;
+ struct dma_chan *chan;
+
+ /* try to get DMAEngine channel */
+ chan = rsnd_dmaen_request_channel(io, mod_from, mod_to);
+ if (IS_ERR_OR_NULL(chan)) {
+ /*
+ * DMA failed. try to PIO mode
+ * see
+ * rsnd_ssi_fallback()
+ * rsnd_rdai_continuance_probe()
+ */
+ return -EAGAIN;
}
- cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
- cfg.src_addr = dma->src_addr;
- cfg.dst_addr = dma->dst_addr;
- cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-
- dev_dbg(dev, "%s[%d] %pad -> %pad\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- &cfg.src_addr, &cfg.dst_addr);
-
- ret = dmaengine_slave_config(dmaen->chan, &cfg);
- if (ret < 0)
- goto rsnd_dma_attach_err;
+ dma_release_channel(chan);
dmac->dmaen_num++;
return 0;
-
-rsnd_dma_attach_err:
- rsnd_dmaen_remove(mod, io, priv);
-rsnd_dma_channel_err:
-
- /*
- * DMA failed. try to PIO mode
- * see
- * rsnd_ssi_fallback()
- * rsnd_rdai_continuance_probe()
- */
- return -EAGAIN;
}
static struct rsnd_mod_ops rsnd_dmaen_ops = {
.name = "audmac",
+ .nolock_start = rsnd_dmaen_nolock_start,
+ .nolock_stop = rsnd_dmaen_nolock_stop,
.start = rsnd_dmaen_start,
.stop = rsnd_dmaen_stop,
- .remove = rsnd_dmaen_remove,
};
/*
*dma_mod = rsnd_mod_get(dma);
- dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
- dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
-
ret = rsnd_mod_init(priv, *dma_mod, ops, NULL,
rsnd_mod_get_status, type, dma_id);
if (ret < 0)
ret = attach(io, dma, mod_from, mod_to);
if (ret < 0)
return ret;
+
+ dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
+ dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
+ dma->mod_from = mod_from;
+ dma->mod_to = mod_to;
}
ret = rsnd_dai_connect(*dma_mod, io, type);