aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/sound/soc/meson/axg-tdm-formatter.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/meson/axg-tdm-formatter.c')
-rw-r--r--sound/soc/meson/axg-tdm-formatter.c94
1 files changed, 40 insertions, 54 deletions
diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c
index f7e8e9da68a0..63333a2b0a9c 100644
--- a/sound/soc/meson/axg-tdm-formatter.c
+++ b/sound/soc/meson/axg-tdm-formatter.c
@@ -30,27 +30,32 @@ int axg_tdm_formatter_set_channel_masks(struct regmap *map,
struct axg_tdm_stream *ts,
unsigned int offset)
{
- unsigned int val, ch = ts->channels;
- unsigned long mask;
- int i, j;
+ unsigned int ch = ts->channels;
+ u32 val[AXG_TDM_NUM_LANES];
+ int i, j, k;
+
+ /*
+ * We need to mimick the slot distribution used by the HW to keep the
+ * channel placement consistent regardless of the number of channel
+ * in the stream. This is why the odd algorithm below is used.
+ */
+ memset(val, 0, sizeof(*val) * AXG_TDM_NUM_LANES);
/*
* Distribute the channels of the stream over the available slots
- * of each TDM lane
+ * of each TDM lane. We need to go over the 32 slots ...
*/
- for (i = 0; i < AXG_TDM_NUM_LANES; i++) {
- val = 0;
- mask = ts->mask[i];
-
- for (j = find_first_bit(&mask, 32);
- (j < 32) && ch;
- j = find_next_bit(&mask, 32, j + 1)) {
- val |= 1 << j;
- ch -= 1;
+ for (i = 0; (i < 32) && ch; i += 2) {
+ /* ... of all the lanes ... */
+ for (j = 0; j < AXG_TDM_NUM_LANES; j++) {
+ /* ... then distribute the channels in pairs */
+ for (k = 0; k < 2; k++) {
+ if ((BIT(i + k) & ts->mask[j]) && ch) {
+ val[j] |= BIT(i + k);
+ ch -= 1;
+ }
+ }
}
-
- regmap_write(map, offset, val);
- offset += regmap_get_reg_stride(map);
}
/*
@@ -63,6 +68,11 @@ int axg_tdm_formatter_set_channel_masks(struct regmap *map,
return -EINVAL;
}
+ for (i = 0; i < AXG_TDM_NUM_LANES; i++) {
+ regmap_write(map, offset, val[i]);
+ offset += regmap_get_reg_stride(map);
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks);
@@ -255,7 +265,6 @@ int axg_tdm_formatter_probe(struct platform_device *pdev)
const struct axg_tdm_formatter_driver *drv;
struct axg_tdm_formatter *formatter;
void __iomem *regs;
- int ret;
drv = of_device_get_match_data(dev);
if (!drv) {
@@ -282,57 +291,34 @@ int axg_tdm_formatter_probe(struct platform_device *pdev)
/* Peripharal clock */
formatter->pclk = devm_clk_get(dev, "pclk");
- if (IS_ERR(formatter->pclk)) {
- ret = PTR_ERR(formatter->pclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get pclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->pclk))
+ return dev_err_probe(dev, PTR_ERR(formatter->pclk), "failed to get pclk\n");
/* Formatter bit clock */
formatter->sclk = devm_clk_get(dev, "sclk");
- if (IS_ERR(formatter->sclk)) {
- ret = PTR_ERR(formatter->sclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get sclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->sclk))
+ return dev_err_probe(dev, PTR_ERR(formatter->sclk), "failed to get sclk\n");
/* Formatter sample clock */
formatter->lrclk = devm_clk_get(dev, "lrclk");
- if (IS_ERR(formatter->lrclk)) {
- ret = PTR_ERR(formatter->lrclk);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get lrclk: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->lrclk))
+ return dev_err_probe(dev, PTR_ERR(formatter->lrclk), "failed to get lrclk\n");
/* Formatter bit clock input multiplexer */
formatter->sclk_sel = devm_clk_get(dev, "sclk_sel");
- if (IS_ERR(formatter->sclk_sel)) {
- ret = PTR_ERR(formatter->sclk_sel);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get sclk_sel: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->sclk_sel))
+ return dev_err_probe(dev, PTR_ERR(formatter->sclk_sel), "failed to get sclk_sel\n");
/* Formatter sample clock input multiplexer */
formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel");
- if (IS_ERR(formatter->lrclk_sel)) {
- ret = PTR_ERR(formatter->lrclk_sel);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get lrclk_sel: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->lrclk_sel))
+ return dev_err_probe(dev, PTR_ERR(formatter->lrclk_sel),
+ "failed to get lrclk_sel\n");
/* Formatter dedicated reset line */
formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
- if (IS_ERR(formatter->reset)) {
- ret = PTR_ERR(formatter->reset);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get reset: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(formatter->reset))
+ return dev_err_probe(dev, PTR_ERR(formatter->reset), "failed to get reset\n");
return devm_snd_soc_register_component(dev, drv->component_drv,
NULL, 0);
@@ -398,7 +384,7 @@ void axg_tdm_stream_free(struct axg_tdm_stream *ts)
/*
* If the list is not empty, it would mean that one of the formatter
* widget is still powered and attached to the interface while we
- * we are removing the TDM DAI. It should not be possible
+ * are removing the TDM DAI. It should not be possible
*/
WARN_ON(!list_empty(&ts->formatter_list));
mutex_destroy(&ts->lock);