From 28a6c969f37a7f4c17356eb15fded72ba3ce5afc Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 10 May 2019 00:12:40 -0700 Subject: [PATCH] dai: assign/free link DMA channel during dai_config() The recommended HDA HW programming sequence for setting the DMA format requires that the link DMA and host DMA channels be coupled before setting the format. This change means that host DMA or link DMA channels be reserved even if only one is used. Statically assigned link DMA channels would mean that all the corresponding host DMA channels will need to be reserved, leaving only a few channels available at run-time. So, the suggestion here is to switch to dynamically assigning both host DMA channels and link DMA channels are run-time. This change means that the DAI_CONFIG IPC will be sent multiple times during link hw_params and link hw_free ioctl. The DAI config parameters will remain the same except for the link DMA channel that will be assigned at run-time. A value of DMA_CHAN_INVALID from the driver during hw_free indicates a request to free the current link DMA channel in use. The current channel in use is freed before assiging the new channel requested in the DAI_CONFIG IPC. Signed-off-by: Ranjani Sridharan --- src/audio/dai.c | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/audio/dai.c b/src/audio/dai.c index 71071a6e98b1..3877fdd4d379 100644 --- a/src/audio/dai.c +++ b/src/audio/dai.c @@ -718,6 +718,20 @@ static int dai_config(struct comp_dev *dev, struct sof_ipc_dai_config *config) */ dev->frame_bytes = 4; channel = config->hda.link_dma_ch; + trace_dai_with_ids(dev, "dai_config(), channel = %d", + channel); + + /* + * For HDA DAIs, the driver sends the DAI_CONFIG IPC + * during every link hw_params and hw_free, apart from the + * the first DAI_CONFIG IPC sent during topology parsing. + * Free the channel that is currently in use before + * assigning the new one. + */ + if (dd->chan != DMA_CHAN_INVALID) { + dma_channel_put(dd->dma, dd->chan); + dd->chan = DMA_CHAN_INVALID; + } break; default: /* other types of DAIs not handled for now */ @@ -735,21 +749,24 @@ static int dai_config(struct comp_dev *dev, struct sof_ipc_dai_config *config) return -EINVAL; } - if (dd->chan == DMA_CHAN_INVALID) - /* get dma channel at first config only */ - dd->chan = dma_channel_get(dd->dma, channel); + if (channel != DMA_CHAN_INVALID) { + if (dd->chan == DMA_CHAN_INVALID) + /* get dma channel at first config only */ + dd->chan = dma_channel_get(dd->dma, channel); - if (dd->chan < 0) { - trace_dai_error_with_ids(dev, "dai_config() error: " - "dma_channel_get() failed"); - return -EIO; - } - - /* set up callback */ - dma_set_cb(dd->dma, dd->chan, DMA_CB_TYPE_IRQ | DMA_CB_TYPE_COPY, - dai_dma_cb, dev); + if (dd->chan < 0) { + trace_dai_error_with_ids(dev, "dai_config() error: " + "dma_channel_get() failed"); + dd->chan = DMA_CHAN_INVALID; + return -EIO; + } - dev->is_dma_connected = 1; + /* set up callback */ + dma_set_cb(dd->dma, dd->chan, + DMA_CB_TYPE_IRQ | DMA_CB_TYPE_COPY, + dai_dma_cb, dev); + dev->is_dma_connected = 1; + } return 0; }