struct device *dev;
        unsigned int nb_tx_ts;
        unsigned int nb_rx_ts;
-       struct qmc_dai_chan chan;
+
+       unsigned int nb_chans_avail;
+       unsigned int nb_chans_used_tx;
+       unsigned int nb_chans_used_rx;
+       struct qmc_dai_chan *chans;
 };
 
 struct qmc_audio {
        dma_addr_t ch_dma_addr_current;
        dma_addr_t ch_dma_addr_end;
        size_t ch_dma_size;
+       size_t ch_dma_offset;
 
+       unsigned int channels;
+       DECLARE_BITMAP(chans_pending, 64);
        struct snd_pcm_substream *substream;
 };
 
        return 0;
 }
 
+static bool qmc_audio_access_is_interleaved(snd_pcm_access_t access)
+{
+       switch (access) {
+       case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
+       case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
+               return true;
+       default:
+               break;
+       }
+       return false;
+}
+
 static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
                                   struct snd_pcm_substream *substream,
                                   struct snd_pcm_hw_params *params)
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct qmc_dai_prtd *prtd = substream->runtime->private_data;
 
+       /*
+        * In interleaved mode, the driver uses one QMC channel for all audio
+        * channels whereas in non-interleaved mode, it uses one QMC channel per
+        * audio channel.
+        */
+       prtd->channels = qmc_audio_access_is_interleaved(params_access(params)) ?
+                               1 : params_channels(params);
+
        prtd->substream = substream;
 
        prtd->buffer_ended = 0;
        prtd->period_size = params_period_size(params);
 
        prtd->ch_dma_addr_start = runtime->dma_addr;
-       prtd->ch_dma_addr_end = runtime->dma_addr + params_buffer_bytes(params);
+       prtd->ch_dma_offset = params_buffer_bytes(params) / prtd->channels;
+       prtd->ch_dma_addr_end = runtime->dma_addr + prtd->ch_dma_offset;
        prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
-       prtd->ch_dma_size = params_period_bytes(params);
+       prtd->ch_dma_size = params_period_bytes(params) / prtd->channels;
 
        return 0;
 }
 
 static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd)
 {
+       unsigned int i;
        int ret;
 
-       ret = qmc_chan_write_submit(prtd->qmc_dai->chan.qmc_chan,
-                                   prtd->ch_dma_addr_current, prtd->ch_dma_size,
-                                   qmc_audio_pcm_write_complete,
-                                   &prtd->qmc_dai->chan);
-       if (ret) {
-               dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n",
-                       ret);
-               return ret;
+       for (i = 0; i < prtd->channels; i++) {
+               bitmap_set(prtd->chans_pending, i, 1);
+
+               ret = qmc_chan_write_submit(prtd->qmc_dai->chans[i].qmc_chan,
+                                           prtd->ch_dma_addr_current + i * prtd->ch_dma_offset,
+                                           prtd->ch_dma_size,
+                                           qmc_audio_pcm_write_complete,
+                                           &prtd->qmc_dai->chans[i]);
+               if (ret) {
+                       dev_err(prtd->qmc_dai->dev, "write_submit %u failed %d\n",
+                               i, ret);
+                       bitmap_clear(prtd->chans_pending, i, 1);
+                       return ret;
+               }
        }
 
        return 0;
 
        prtd = chan->prtd_tx;
 
+       /* Mark the current channel as completed */
+       bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1);
+
+       /*
+        * All QMC channels involved must have completed their transfer before
+        * submitting a new one.
+        */
+       if (!bitmap_empty(prtd->chans_pending, 64))
+               return;
+
        prtd->buffer_ended += prtd->period_size;
        if (prtd->buffer_ended >= prtd->buffer_size)
                prtd->buffer_ended = 0;
 
 static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd)
 {
+       unsigned int i;
        int ret;
 
-       ret = qmc_chan_read_submit(prtd->qmc_dai->chan.qmc_chan,
-                                  prtd->ch_dma_addr_current, prtd->ch_dma_size,
-                                  qmc_audio_pcm_read_complete,
-                                  &prtd->qmc_dai->chan);
-       if (ret) {
-               dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n",
-                       ret);
+       for (i = 0; i < prtd->channels; i++) {
+               bitmap_set(prtd->chans_pending, i, 1);
+
+               ret = qmc_chan_read_submit(prtd->qmc_dai->chans[i].qmc_chan,
+                                          prtd->ch_dma_addr_current + i * prtd->ch_dma_offset,
+                                          prtd->ch_dma_size,
+                                          qmc_audio_pcm_read_complete,
+                                          &prtd->qmc_dai->chans[i]);
+               if (ret) {
+                       dev_err(prtd->qmc_dai->dev, "read_submit %u failed %d\n",
+                               i, ret);
+                       bitmap_clear(prtd->chans_pending, i, 1);
+                       return ret;
+               }
        }
 
        return 0;
 
        prtd = chan->prtd_rx;
 
+       /* Mark the current channel as completed */
+       bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1);
+
        if (length != prtd->ch_dma_size) {
                dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n",
                        length, prtd->ch_dma_size);
        }
 
+       /*
+        * All QMC channels involved must have completed their transfer before
+        * submitting a new one.
+        */
+       if (!bitmap_empty(prtd->chans_pending, 64))
+               return;
+
        prtd->buffer_ended += prtd->period_size;
        if (prtd->buffer_ended >= prtd->buffer_size)
                prtd->buffer_ended = 0;
                                 struct snd_pcm_substream *substream, int cmd)
 {
        struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+       unsigned int i;
        int ret;
 
        if (!prtd->qmc_dai) {
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
+               bitmap_zero(prtd->chans_pending, 64);
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-                       prtd->qmc_dai->chan.prtd_tx = prtd;
+                       for (i = 0; i < prtd->channels; i++)
+                               prtd->qmc_dai->chans[i].prtd_tx = prtd;
 
                        /* Submit first chunk ... */
                        ret = qmc_audio_pcm_write_submit(prtd);
                        if (ret)
                                return ret;
                } else {
-                       prtd->qmc_dai->chan.prtd_rx = prtd;
+                       for (i = 0; i < prtd->channels; i++)
+                               prtd->qmc_dai->chans[i].prtd_rx = prtd;
 
                        /* Submit first chunk ... */
                        ret = qmc_audio_pcm_read_submit(prtd);
        .info                   = SNDRV_PCM_INFO_MMAP |
                                  SNDRV_PCM_INFO_MMAP_VALID |
                                  SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_NONINTERLEAVED |
                                  SNDRV_PCM_INFO_PAUSE,
        .period_bytes_min       = 32,
        .period_bytes_max       = 64 * 1024,
        snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
        snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
        unsigned int frame_bits;
+       u64 access;
        int ret;
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
                return ret;
        }
 
+       access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED |
+                1ULL << (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+       ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS,
+                                          access);
+       if (ret) {
+               dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int qmc_dai_constraints_noninterleaved(struct snd_pcm_substream *substream,
+                                             struct qmc_dai *qmc_dai)
+{
+       unsigned int frame_bits;
+       u64 access;
+       int ret;
+
+       frame_bits = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+                       qmc_dai->nb_rx_ts * 8 : qmc_dai->nb_tx_ts * 8;
+       ret = snd_pcm_hw_constraint_single(substream->runtime,
+                                          SNDRV_PCM_HW_PARAM_FRAME_BITS,
+                                          frame_bits);
+       if (ret < 0) {
+               dev_err(qmc_dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+               return ret;
+       }
+
+       access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED |
+                1ULL << (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
+       ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS,
+                                          access);
+       if (ret) {
+               dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret);
+               return ret;
+       }
+
        return 0;
 }
 
 
        prtd->qmc_dai = qmc_dai;
 
-       return qmc_dai_constraints_interleaved(substream, qmc_dai);
+       return qmc_dai->nb_chans_avail > 1 ?
+               qmc_dai_constraints_noninterleaved(substream, qmc_dai) :
+               qmc_dai_constraints_interleaved(substream, qmc_dai);
 }
 
 static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
                             struct snd_soc_dai *dai)
 {
        struct qmc_chan_param chan_param = {0};
+       unsigned int nb_chans_used;
        struct qmc_dai *qmc_dai;
+       unsigned int i;
        int ret;
 
        qmc_dai = qmc_dai_get_data(dai);
                return -EINVAL;
        }
 
+       /*
+        * In interleaved mode, the driver uses one QMC channel for all audio
+        * channels whereas in non-interleaved mode, it uses one QMC channel per
+        * audio channel.
+        */
+       nb_chans_used = qmc_audio_access_is_interleaved(params_access(params)) ?
+                               1 : params_channels(params);
+
+       if (nb_chans_used > qmc_dai->nb_chans_avail) {
+               dev_err(dai->dev, "Not enough qmc_chans. Need %u, avail %u\n",
+                       nb_chans_used, qmc_dai->nb_chans_avail);
+               return -EINVAL;
+       }
+
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
                chan_param.mode = QMC_TRANSPARENT;
-               chan_param.transp.max_rx_buf_size = params_period_bytes(params);
-               ret = qmc_chan_set_param(qmc_dai->chan.qmc_chan, &chan_param);
-               if (ret) {
-                       dev_err(dai->dev, "set param failed %d\n",
-                               ret);
-                       return ret;
+               chan_param.transp.max_rx_buf_size = params_period_bytes(params) / nb_chans_used;
+               for (i = 0; i < nb_chans_used; i++) {
+                       ret = qmc_chan_set_param(qmc_dai->chans[i].qmc_chan, &chan_param);
+                       if (ret) {
+                               dev_err(dai->dev, "chans[%u], set param failed %d\n",
+                                       i, ret);
+                               return ret;
+                       }
                }
+               qmc_dai->nb_chans_used_rx = nb_chans_used;
+       } else {
+               qmc_dai->nb_chans_used_tx = nb_chans_used;
        }
 
        return 0;
 static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
                           struct snd_soc_dai *dai)
 {
+       unsigned int nb_chans_used;
        struct qmc_dai *qmc_dai;
+       unsigned int i;
        int direction;
-       int ret;
+       int ret = 0;
+       int ret_tmp;
 
        qmc_dai = qmc_dai_get_data(dai);
        if (!qmc_dai) {
                return -EINVAL;
        }
 
-       direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
-                   QMC_CHAN_WRITE : QMC_CHAN_READ;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               direction = QMC_CHAN_WRITE;
+               nb_chans_used = qmc_dai->nb_chans_used_tx;
+       } else {
+               direction = QMC_CHAN_READ;
+               nb_chans_used = qmc_dai->nb_chans_used_rx;
+       }
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               ret = qmc_chan_start(qmc_dai->chan.qmc_chan, direction);
-               if (ret)
-                       return ret;
+               for (i = 0; i < nb_chans_used; i++) {
+                       ret = qmc_chan_start(qmc_dai->chans[i].qmc_chan, direction);
+                       if (ret)
+                               goto err_stop;
+               }
                break;
 
        case SNDRV_PCM_TRIGGER_STOP:
-               ret = qmc_chan_stop(qmc_dai->chan.qmc_chan, direction);
-               if (ret)
-                       return ret;
-               ret = qmc_chan_reset(qmc_dai->chan.qmc_chan, direction);
+               /* Stop and reset all QMC channels and return the first error encountered */
+               for (i = 0; i < nb_chans_used; i++) {
+                       ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
+                       if (!ret)
+                               ret = ret_tmp;
+                       if (ret_tmp)
+                               continue;
+
+                       ret_tmp = qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction);
+                       if (!ret)
+                               ret = ret_tmp;
+               }
                if (ret)
                        return ret;
                break;
 
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               ret = qmc_chan_stop(qmc_dai->chan.qmc_chan, direction);
+               /* Stop all QMC channels and return the first error encountered */
+               for (i = 0; i < nb_chans_used; i++) {
+                       ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
+                       if (!ret)
+                               ret = ret_tmp;
+               }
                if (ret)
                        return ret;
                break;
        }
 
        return 0;
+
+err_stop:
+       while (i--) {
+               qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
+               qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction);
+       }
+       return ret;
 }
 
 static const struct snd_soc_dai_ops qmc_dai_ops = {
        .hw_params      = qmc_dai_hw_params,
 };
 
-static u64 qmc_audio_formats(u8 nb_ts)
+static u64 qmc_audio_formats(u8 nb_ts, bool is_noninterleaved)
 {
        unsigned int format_width;
        unsigned int chan_width;
                if (format_width > chan_width || chan_width % format_width)
                        continue;
 
+               /*
+                * In non interleaved mode, we can only support formats that
+                * can fit only 1 time in the channel
+                */
+               if (is_noninterleaved && format_width != chan_width)
+                       continue;
+
                formats_mask |= pcm_format_to_bits(format);
        }
        return formats_mask;
                               struct snd_soc_dai_driver *qmc_soc_dai_driver)
 {
        struct qmc_chan_info info;
+       unsigned long rx_fs_rate;
+       unsigned long tx_fs_rate;
+       unsigned int nb_tx_ts;
+       unsigned int nb_rx_ts;
+       unsigned int i;
+       int count;
        u32 val;
        int ret;
 
        if (!qmc_dai->name)
                return -ENOMEM;
 
-       qmc_dai->chan.qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np,
-                                                            "fsl,qmc-chan");
-       if (IS_ERR(qmc_dai->chan.qmc_chan)) {
-               ret = PTR_ERR(qmc_dai->chan.qmc_chan);
-               return dev_err_probe(qmc_audio->dev, ret,
-                                    "dai %d get QMC channel failed\n", qmc_dai->id);
-       }
+       count = qmc_chan_count_phandles(np, "fsl,qmc-chan");
+       if (count < 0)
+               return dev_err_probe(qmc_audio->dev, count,
+                                    "dai %d get number of QMC channel failed\n", qmc_dai->id);
+       if (!count)
+               return dev_err_probe(qmc_audio->dev, -EINVAL,
+                                    "dai %d no QMC channel defined\n", qmc_dai->id);
 
-       qmc_soc_dai_driver->id = qmc_dai->id;
-       qmc_soc_dai_driver->name = qmc_dai->name;
+       qmc_dai->chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->chans), GFP_KERNEL);
+       if (!qmc_dai->chans)
+               return -ENOMEM;
 
-       ret = qmc_chan_get_info(qmc_dai->chan.qmc_chan, &info);
-       if (ret) {
-               dev_err(qmc_audio->dev, "dai %d get QMC channel info failed %d\n",
-                       qmc_dai->id, ret);
-               return ret;
-       }
-       dev_info(qmc_audio->dev, "dai %d QMC channel mode %d, nb_tx_ts %u, nb_rx_ts %u\n",
-                qmc_dai->id, info.mode, info.nb_tx_ts, info.nb_rx_ts);
+       for (i = 0; i < count; i++) {
+               qmc_dai->chans[i].qmc_chan = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np,
+                                                                               "fsl,qmc-chan", i);
+               if (IS_ERR(qmc_dai->chans[i].qmc_chan)) {
+                       return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->chans[i].qmc_chan),
+                                            "dai %d get QMC channel %d failed\n", qmc_dai->id, i);
+               }
 
-       if (info.mode != QMC_TRANSPARENT) {
-               dev_err(qmc_audio->dev, "dai %d QMC chan mode %d is not QMC_TRANSPARENT\n",
-                       qmc_dai->id, info.mode);
-               return -EINVAL;
+               ret = qmc_chan_get_info(qmc_dai->chans[i].qmc_chan, &info);
+               if (ret) {
+                       dev_err(qmc_audio->dev, "dai %d get QMC %d channel info failed %d\n",
+                               qmc_dai->id, i, ret);
+                       return ret;
+               }
+               dev_info(qmc_audio->dev, "dai %d QMC channel %d mode %d, nb_tx_ts %u, nb_rx_ts %u\n",
+                        qmc_dai->id, i, info.mode, info.nb_tx_ts, info.nb_rx_ts);
+
+               if (info.mode != QMC_TRANSPARENT) {
+                       dev_err(qmc_audio->dev, "dai %d QMC chan %d mode %d is not QMC_TRANSPARENT\n",
+                               qmc_dai->id, i, info.mode);
+                       return -EINVAL;
+               }
+
+               /*
+                * All channels must have the same number of Tx slots and the
+                * same numbers of Rx slots.
+                */
+               if (i == 0) {
+                       nb_tx_ts = info.nb_tx_ts;
+                       nb_rx_ts = info.nb_rx_ts;
+                       tx_fs_rate = info.tx_fs_rate;
+                       rx_fs_rate = info.rx_fs_rate;
+               } else {
+                       if (nb_tx_ts != info.nb_tx_ts) {
+                               dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Tx timeslots (%u instead of %u)\n",
+                                       qmc_dai->id, i, info.nb_tx_ts, nb_tx_ts);
+                               return -EINVAL;
+                       }
+                       if (nb_rx_ts != info.nb_rx_ts) {
+                               dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Rx timeslots (%u instead of %u)\n",
+                                       qmc_dai->id, i, info.nb_rx_ts, nb_rx_ts);
+                               return -EINVAL;
+                       }
+                       if (tx_fs_rate != info.tx_fs_rate) {
+                               dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Tx frame sample rate (%lu instead of %lu)\n",
+                                       qmc_dai->id, i, info.tx_fs_rate, tx_fs_rate);
+                               return -EINVAL;
+                       }
+                       if (rx_fs_rate != info.rx_fs_rate) {
+                               dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Rx frame sample rate (%lu instead of %lu)\n",
+                                       qmc_dai->id, i, info.rx_fs_rate, rx_fs_rate);
+                               return -EINVAL;
+                       }
+               }
        }
-       qmc_dai->nb_tx_ts = info.nb_tx_ts;
-       qmc_dai->nb_rx_ts = info.nb_rx_ts;
+
+       qmc_dai->nb_chans_avail = count;
+       qmc_dai->nb_tx_ts = nb_tx_ts * count;
+       qmc_dai->nb_rx_ts = nb_rx_ts * count;
+
+       qmc_soc_dai_driver->id = qmc_dai->id;
+       qmc_soc_dai_driver->name = qmc_dai->name;
 
        qmc_soc_dai_driver->playback.channels_min = 0;
        qmc_soc_dai_driver->playback.channels_max = 0;
-       if (qmc_dai->nb_tx_ts) {
+       if (nb_tx_ts) {
                qmc_soc_dai_driver->playback.channels_min = 1;
-               qmc_soc_dai_driver->playback.channels_max = qmc_dai->nb_tx_ts;
+               qmc_soc_dai_driver->playback.channels_max = count > 1 ? count : nb_tx_ts;
        }
-       qmc_soc_dai_driver->playback.formats = qmc_audio_formats(qmc_dai->nb_tx_ts);
+       qmc_soc_dai_driver->playback.formats = qmc_audio_formats(nb_tx_ts,
+                                                                count > 1 ? true : false);
 
        qmc_soc_dai_driver->capture.channels_min = 0;
        qmc_soc_dai_driver->capture.channels_max = 0;
-       if (qmc_dai->nb_rx_ts) {
+       if (nb_rx_ts) {
                qmc_soc_dai_driver->capture.channels_min = 1;
-               qmc_soc_dai_driver->capture.channels_max = qmc_dai->nb_rx_ts;
+               qmc_soc_dai_driver->capture.channels_max = count > 1 ? count : nb_rx_ts;
        }
-       qmc_soc_dai_driver->capture.formats = qmc_audio_formats(qmc_dai->nb_rx_ts);
-
-       qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(info.tx_fs_rate);
-       qmc_soc_dai_driver->playback.rate_min = info.tx_fs_rate;
-       qmc_soc_dai_driver->playback.rate_max = info.tx_fs_rate;
-       qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(info.rx_fs_rate);
-       qmc_soc_dai_driver->capture.rate_min = info.rx_fs_rate;
-       qmc_soc_dai_driver->capture.rate_max = info.rx_fs_rate;
+       qmc_soc_dai_driver->capture.formats = qmc_audio_formats(nb_rx_ts,
+                                                               count > 1 ? true : false);
+
+       qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(tx_fs_rate);
+       qmc_soc_dai_driver->playback.rate_min = tx_fs_rate;
+       qmc_soc_dai_driver->playback.rate_max = tx_fs_rate;
+       qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(rx_fs_rate);
+       qmc_soc_dai_driver->capture.rate_min = rx_fs_rate;
+       qmc_soc_dai_driver->capture.rate_max = rx_fs_rate;
 
        qmc_soc_dai_driver->ops = &qmc_dai_ops;