sound/soc/meson/axg-tdm-formatter.c
Source file repositories/reference/linux-study-clean/sound/soc/meson/axg-tdm-formatter.c
File Facts
- System
- Linux kernel
- Corpus path
sound/soc/meson/axg-tdm-formatter.c- Extension
.c- Size
- 11111 bytes
- Lines
- 432
- Domain
- Driver Families
- Bucket
- sound/soc
- Inferred role
- Driver Families: exported/initcall integration point
- Status
- integration implementation candidate
Why This File Exists
Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.
- Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.
- Exports symbols or registers init work; inspect boot/module ordering and who consumes the exported contract.
- Allocates kernel memory; connect allocation flags and lifetime to context constraints.
- Defines or uses C structs; map object ownership, embedded links, reference counts, and lock ownership.
Dependency Surface
linux/clk.hlinux/module.hlinux/of_platform.hlinux/regmap.hlinux/reset.hsound/soc.haxg-tdm-formatter.h
Detected Declarations
struct axg_tdm_formatterfunction axg_tdm_formatter_set_channel_masksfunction axg_tdm_formatter_enablefunction axg_tdm_formatter_disablefunction axg_tdm_formatter_attachfunction axg_tdm_formatter_dettachfunction axg_tdm_formatter_power_upfunction axg_tdm_formatter_power_downfunction axg_tdm_formatter_eventfunction axg_tdm_formatter_probefunction axg_tdm_stream_startfunction axg_tdm_stream_stopfunction axg_tdm_stream_freefunction axg_tdm_stream_set_cont_clocksexport axg_tdm_formatter_set_channel_masksexport axg_tdm_formatter_eventexport axg_tdm_formatter_probeexport axg_tdm_stream_startexport axg_tdm_stream_stopexport axg_tdm_stream_allocexport axg_tdm_stream_freeexport axg_tdm_stream_set_cont_clocks
Annotated Snippet
struct axg_tdm_formatter {
struct list_head list;
struct axg_tdm_stream *stream;
const struct axg_tdm_formatter_driver *drv;
struct clk *pclk;
struct clk *sclk;
struct clk *lrclk;
struct clk *sclk_sel;
struct clk *lrclk_sel;
struct reset_control *reset;
bool enabled;
struct regmap *map;
};
int axg_tdm_formatter_set_channel_masks(struct regmap *map,
struct axg_tdm_stream *ts,
unsigned int offset)
{
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. We need to go over the 32 slots ...
*/
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;
}
}
}
}
/*
* If we still have channel left at the end of the process, it means
* the stream has more channels than we can accommodate and we should
* have caught this earlier.
*/
if (WARN_ON(ch != 0)) {
pr_err("channel mask error\n");
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);
static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter)
{
struct axg_tdm_stream *ts = formatter->stream;
bool invert;
int ret;
/* Do nothing if the formatter is already enabled */
if (formatter->enabled)
return 0;
/*
* On the g12a (and possibly other SoCs), when a stream using
* multiple lanes is restarted, it will sometimes not start
* from the first lane, but randomly from another used one.
* The result is an unexpected and random channel shift.
*
* The hypothesis is that an HW counter is not properly reset
* and the formatter simply starts on the lane it stopped
* before. Unfortunately, there does not seems to be a way to
* reset this through the registers of the block.
*
* However, the g12a has indenpendent reset lines for each audio
* devices. Using this reset before each start solves the issue.
*/
ret = reset_control_reset(formatter->reset);
Annotation
- Immediate include surface: `linux/clk.h`, `linux/module.h`, `linux/of_platform.h`, `linux/regmap.h`, `linux/reset.h`, `sound/soc.h`, `axg-tdm-formatter.h`.
- Detected declarations: `struct axg_tdm_formatter`, `function axg_tdm_formatter_set_channel_masks`, `function axg_tdm_formatter_enable`, `function axg_tdm_formatter_disable`, `function axg_tdm_formatter_attach`, `function axg_tdm_formatter_dettach`, `function axg_tdm_formatter_power_up`, `function axg_tdm_formatter_power_down`, `function axg_tdm_formatter_event`, `function axg_tdm_formatter_probe`.
- Atlas domain: Driver Families / sound/soc.
- Implementation status: integration implementation candidate.
Implementation Notes
- This generated page is the file-by-file coverage layer; curated subsystem chapters should link here when they synthesize a multi-file control flow.
- Core OS pages should be promoted from atlas-only to deep-reviewed when they explain data structures, invariants, locking, lifecycle, and C implementation snippets.
- Driver-family pages are intentionally pattern-oriented unless they are part of the selected PCIe/NVMe representative device path.