ASoC: meson: aiu: introduce I2S output formatter

Introduce aiu-formatter-i2s, a gx_formatter implementation for the AIU I2S
playback path. This is going to replace data formatting tasks that are
currently being implemented in aiu-encoder-i2s.

This should ideally follow the same design pattern used on the AXG
platform (see axg-tdmout), where basically the widget/formatter corresponds
to a single audio component. This is not possible in the GX platform though
because all the features are currently implemented in the AIU audio
component and changing that would require backward incompatible
device-tree changes.

Therefore aiu-formatter-i2s is kept very simple and
it only implements the bare minimum functionalities to provide I2S
playback formatting. It's not a standalone component though because this
is still belongs to AIU.

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
Reviewed-by: Jerome Brunet <jbrunet@baylibre.com>
Link: https://patch.msgid.link/20260610-reshape-aiu-as-axg-v2-3-cac3663a8b51@baylibre.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Valerio Setti 2026-06-10 23:29:27 +02:00 committed by Mark Brown
parent 9335117a22
commit 2cbb32d8dc
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 105 additions and 0 deletions

View File

@ -5,6 +5,7 @@ snd-soc-meson-aiu-y += aiu-acodec-ctrl.o
snd-soc-meson-aiu-y += aiu-codec-ctrl.o
snd-soc-meson-aiu-y += aiu-encoder-i2s.o
snd-soc-meson-aiu-y += gx-formatter.o
snd-soc-meson-aiu-y += aiu-formatter-i2s.o
snd-soc-meson-aiu-y += aiu-encoder-spdif.o
snd-soc-meson-aiu-y += aiu-fifo.o
snd-soc-meson-aiu-y += aiu-fifo-i2s.o

View File

@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2026 BayLibre, SAS.
// Author: Valerio Setti <vsetti@baylibre.com>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include "aiu.h"
#include "gx-formatter.h"
#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0)
#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5)
#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9)
#define AIU_RST_SOFT_I2S_FAST BIT(0)
#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2)
static struct snd_soc_dai *
aiu_formatter_i2s_get_be(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_path *p;
struct snd_soc_dai *be;
snd_soc_dapm_widget_for_each_sink_path(w, p) {
if (!p->connect)
continue;
if (p->sink->id == snd_soc_dapm_dai_in)
return (struct snd_soc_dai *)p->sink->priv;
be = aiu_formatter_i2s_get_be(p->sink);
if (be)
return be;
}
return NULL;
}
static struct gx_stream *
aiu_formatter_i2s_get_stream(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dai *be = aiu_formatter_i2s_get_be(w);
if (!be)
return NULL;
return snd_soc_dai_dma_data_get_playback(be);
}
static int aiu_formatter_i2s_prepare(struct regmap *map,
const struct gx_formatter_hw *quirks,
struct gx_stream *ts)
{
/* Always operate in split (classic interleaved) mode */
unsigned int desc = 0;
unsigned int tmp;
/* Reset required to update the pipeline */
regmap_write(map, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST);
regmap_read(map, AIU_I2S_SYNC, &tmp);
switch (ts->physical_width) {
case 16: /* Nothing to do */
break;
case 32:
desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT |
AIU_I2S_SOURCE_DESC_MODE_32BIT);
break;
default:
return -EINVAL;
}
switch (ts->channels) {
case 2: /* Nothing to do */
break;
case 8:
desc |= AIU_I2S_SOURCE_DESC_MODE_8CH;
break;
default:
return -EINVAL;
}
regmap_update_bits(map, AIU_I2S_SOURCE_DESC,
AIU_I2S_SOURCE_DESC_MODE_8CH |
AIU_I2S_SOURCE_DESC_MODE_24BIT |
AIU_I2S_SOURCE_DESC_MODE_32BIT,
desc);
/* Send data MSB first */
regmap_update_bits(map, AIU_I2S_DAC_CFG,
AIU_I2S_DAC_CFG_MSB_FIRST,
AIU_I2S_DAC_CFG_MSB_FIRST);
return 0;
}
const struct gx_formatter_ops aiu_formatter_i2s_ops = {
.get_stream = aiu_formatter_i2s_get_stream,
.prepare = aiu_formatter_i2s_prepare,
};