Commit fc800b84 authored by Mark Brown's avatar Mark Brown

Add support for non-interleaved mode in qmc_audio

Merge series from Herve Codina <herve.codina@bootlin.com>:

The qmc_audio driver supports only audio in interleaved mode.
Non-interleaved mode can be easily supported using several QMC channel
per DAI. In that case, data related to ch0 are sent to (received from)
the first QMC channel, data related to ch1 use the next QMC channel and
so on up to the last channel.

In terms of constraints and settings, the interleaved and
non-interleaved modes are slightly different.

In interleaved mode:
  - The sample size should fit in the number of time-slots available for
    the QMC channel.
  - The number of audio channels should fit in the number of time-slots
    (taking into account the sample size) available for the QMC channel.

In non-interleaved mode:
  - The number of audio channels is the number of available QMC
    channels.
  - Each QMC channel should have the same number of time-slots.
  - The sample size equals the number of time-slots of one QMC channel.

This series add support for the non-interleaved mode in the qmc_audio
driver and is composed of the following parts:
  - Patches 1 and 2: Fix some issues in the qmc_audio
  - Patches 3 to 6: Prepare qmc_audio for the non-interleaved mode
  - Patches 7 and 8: Extend the QMC driver API
  - Patches 9 and 10: The support for non-interleaved mode itself

Compared to the previous iteration, this v2 series mainly improves
qmc_audio_access_is_interleaved().
parents ecaec47b 188d9cae
...@@ -12,7 +12,9 @@ maintainers: ...@@ -12,7 +12,9 @@ maintainers:
description: | description: |
The QMC audio is an ASoC component which uses QMC (QUICC Multichannel The QMC audio is an ASoC component which uses QMC (QUICC Multichannel
Controller) channels to transfer the audio data. Controller) channels to transfer the audio data.
It provides as many DAI as the number of QMC channel used. It provides several DAIs. For each DAI, the DAI is working in interleaved mode
if only one QMC channel is used by the DAI or it is working in non-interleaved
mode if several QMC channels are used by the DAI.
allOf: allOf:
- $ref: dai-common.yaml# - $ref: dai-common.yaml#
...@@ -45,12 +47,19 @@ patternProperties: ...@@ -45,12 +47,19 @@ patternProperties:
fsl,qmc-chan: fsl,qmc-chan:
$ref: /schemas/types.yaml#/definitions/phandle-array $ref: /schemas/types.yaml#/definitions/phandle-array
items: items:
- items: items:
- description: phandle to QMC node - description: phandle to QMC node
- description: Channel number - description: Channel number
minItems: 1
description: description:
Should be a phandle/number pair. The phandle to QMC node and the QMC Should be a phandle/number pair list. The list of phandle to QMC node
channel to use for this DAI. and the QMC channel pair to use for this DAI.
If only one phandle/number pair is provided, this DAI works in
interleaved mode, i.e. audio channels for this DAI are interleaved in
the QMC channel. If more than one pair is provided, this DAI works
in non-interleave mode. In that case the first audio channel uses the
the first QMC channel, the second audio channel uses the second QMC
channel, etc...
required: required:
- reg - reg
...@@ -79,6 +88,11 @@ examples: ...@@ -79,6 +88,11 @@ examples:
reg = <17>; reg = <17>;
fsl,qmc-chan = <&qmc 17>; fsl,qmc-chan = <&qmc 17>;
}; };
dai@18 {
reg = <18>;
/* Non-interleaved mode */
fsl,qmc-chan = <&qmc 18>, <&qmc 19>;
};
}; };
sound { sound {
...@@ -115,4 +129,19 @@ examples: ...@@ -115,4 +129,19 @@ examples:
dai-tdm-slot-rx-mask = <0 0 1 0 1 0 1 0 1>; dai-tdm-slot-rx-mask = <0 0 1 0 1 0 1 0 1>;
}; };
}; };
simple-audio-card,dai-link@2 {
reg = <2>;
format = "dsp_b";
cpu {
sound-dai = <&audio_controller 18>;
};
codec {
sound-dai = <&codec3>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <8>;
/* TS 9, 10 */
dai-tdm-slot-tx-mask = <0 0 0 0 0 0 0 0 0 1 1>;
dai-tdm-slot-rx-mask = <0 0 0 0 0 0 0 0 0 1 1>;
};
};
}; };
...@@ -1777,13 +1777,28 @@ static struct qmc_chan *qmc_chan_get_from_qmc(struct device_node *qmc_np, unsign ...@@ -1777,13 +1777,28 @@ static struct qmc_chan *qmc_chan_get_from_qmc(struct device_node *qmc_np, unsign
return qmc_chan; return qmc_chan;
} }
struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name) int qmc_chan_count_phandles(struct device_node *np, const char *phandles_name)
{
int count;
/* phandles are fixed args phandles with one arg */
count = of_count_phandle_with_args(np, phandles_name, NULL);
if (count < 0)
return count;
return count / 2;
}
EXPORT_SYMBOL(qmc_chan_count_phandles);
struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np,
const char *phandles_name,
int index)
{ {
struct of_phandle_args out_args; struct of_phandle_args out_args;
struct qmc_chan *qmc_chan; struct qmc_chan *qmc_chan;
int ret; int ret;
ret = of_parse_phandle_with_fixed_args(np, phandle_name, 1, 0, ret = of_parse_phandle_with_fixed_args(np, phandles_name, 1, index,
&out_args); &out_args);
if (ret < 0) if (ret < 0)
return ERR_PTR(ret); return ERR_PTR(ret);
...@@ -1797,7 +1812,7 @@ struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phan ...@@ -1797,7 +1812,7 @@ struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phan
of_node_put(out_args.np); of_node_put(out_args.np);
return qmc_chan; return qmc_chan;
} }
EXPORT_SYMBOL(qmc_chan_get_byphandle); EXPORT_SYMBOL(qmc_chan_get_byphandles_index);
struct qmc_chan *qmc_chan_get_bychild(struct device_node *np) struct qmc_chan *qmc_chan_get_bychild(struct device_node *np)
{ {
...@@ -1827,9 +1842,10 @@ static void devm_qmc_chan_release(struct device *dev, void *res) ...@@ -1827,9 +1842,10 @@ static void devm_qmc_chan_release(struct device *dev, void *res)
qmc_chan_put(*qmc_chan); qmc_chan_put(*qmc_chan);
} }
struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, struct qmc_chan *devm_qmc_chan_get_byphandles_index(struct device *dev,
struct device_node *np, struct device_node *np,
const char *phandle_name) const char *phandles_name,
int index)
{ {
struct qmc_chan *qmc_chan; struct qmc_chan *qmc_chan;
struct qmc_chan **dr; struct qmc_chan **dr;
...@@ -1838,7 +1854,7 @@ struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, ...@@ -1838,7 +1854,7 @@ struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev,
if (!dr) if (!dr)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
qmc_chan = qmc_chan_get_byphandle(np, phandle_name); qmc_chan = qmc_chan_get_byphandles_index(np, phandles_name, index);
if (!IS_ERR(qmc_chan)) { if (!IS_ERR(qmc_chan)) {
*dr = qmc_chan; *dr = qmc_chan;
devres_add(dev, dr); devres_add(dev, dr);
...@@ -1848,7 +1864,7 @@ struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, ...@@ -1848,7 +1864,7 @@ struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev,
return qmc_chan; return qmc_chan;
} }
EXPORT_SYMBOL(devm_qmc_chan_get_byphandle); EXPORT_SYMBOL(devm_qmc_chan_get_byphandles_index);
struct qmc_chan *devm_qmc_chan_get_bychild(struct device *dev, struct qmc_chan *devm_qmc_chan_get_bychild(struct device *dev,
struct device_node *np) struct device_node *np)
......
...@@ -16,11 +16,32 @@ struct device_node; ...@@ -16,11 +16,32 @@ struct device_node;
struct device; struct device;
struct qmc_chan; struct qmc_chan;
struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name); int qmc_chan_count_phandles(struct device_node *np, const char *phandles_name);
struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np,
const char *phandles_name,
int index);
struct qmc_chan *devm_qmc_chan_get_byphandles_index(struct device *dev,
struct device_node *np,
const char *phandles_name,
int index);
static inline struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np,
const char *phandle_name)
{
return qmc_chan_get_byphandles_index(np, phandle_name, 0);
}
static inline struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev,
struct device_node *np,
const char *phandle_name)
{
return devm_qmc_chan_get_byphandles_index(dev, np, phandle_name, 0);
}
struct qmc_chan *qmc_chan_get_bychild(struct device_node *np); struct qmc_chan *qmc_chan_get_bychild(struct device_node *np);
void qmc_chan_put(struct qmc_chan *chan); void qmc_chan_put(struct qmc_chan *chan);
struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, struct device_node *np,
const char *phandle_name);
struct qmc_chan *devm_qmc_chan_get_bychild(struct device *dev, struct device_node *np); struct qmc_chan *devm_qmc_chan_get_bychild(struct device *dev, struct device_node *np);
enum qmc_mode { enum qmc_mode {
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment