sound/soc/meson/axg-spdifin.c

Source file repositories/reference/linux-study-clean/sound/soc/meson/axg-spdifin.c

File Facts

System
Linux kernel
Corpus path
sound/soc/meson/axg-spdifin.c
Extension
.c
Size
13180 bytes
Lines
497
Domain
Driver Families
Bucket
sound/soc
Inferred role
Driver Families: implementation source
Status
source 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.

Dependency Surface

Detected Declarations

Annotated Snippet

struct axg_spdifin_cfg {
	const unsigned int *mode_rates;
	unsigned int ref_rate;
};

struct axg_spdifin {
	const struct axg_spdifin_cfg *conf;
	struct regmap *map;
	struct clk *refclk;
	struct clk *pclk;
};

/*
 * TODO:
 * It would have been nice to check the actual rate against the sample rate
 * requested in hw_params(). Unfortunately, I was not able to make the mode
 * detection and IRQ work reliably:
 *
 * 1. IRQs are generated on mode change only, so there is no notification
 *    on transition between no signal and mode 0 (32kHz).
 * 2. Mode detection very often has glitches, and may detects the
 *    lowest or the highest mode before zeroing in on the actual mode.
 *
 * This makes calling snd_pcm_stop() difficult to get right. Even notifying
 * the kcontrol would be very unreliable at this point.
 * Let's keep things simple until the magic spell that makes this work is
 * found.
 */

static unsigned int axg_spdifin_get_rate(struct axg_spdifin *priv)
{
	unsigned int stat, mode, rate = 0;

	regmap_read(priv->map, SPDIFIN_STAT0, &stat);
	mode = FIELD_GET(SPDIFIN_STAT0_MODE, stat);

	/*
	 * If max width is zero, we are not capturing anything.
	 * Also Sometimes, when the capture is on but there is no data,
	 * mode is SPDIFIN_MODE_NUM, but not always ...
	 */
	if (FIELD_GET(SPDIFIN_STAT0_MAXW, stat) &&
	    mode < SPDIFIN_MODE_NUM)
		rate = priv->conf->mode_rates[mode];

	return rate;
}

static int axg_spdifin_prepare(struct snd_pcm_substream *substream,
			       struct snd_soc_dai *dai)
{
	struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai);

	/* Apply both reset */
	regmap_update_bits(priv->map, SPDIFIN_CTRL0,
			   SPDIFIN_CTRL0_RST_OUT |
			   SPDIFIN_CTRL0_RST_IN,
			   0);

	/* Clear out reset before in reset */
	regmap_update_bits(priv->map, SPDIFIN_CTRL0,
			   SPDIFIN_CTRL0_RST_OUT, SPDIFIN_CTRL0_RST_OUT);
	regmap_update_bits(priv->map, SPDIFIN_CTRL0,
			   SPDIFIN_CTRL0_RST_IN,  SPDIFIN_CTRL0_RST_IN);

	return 0;
}

static void axg_spdifin_write_mode_param(struct regmap *map, int mode,
					 unsigned int val,
					 unsigned int num_per_reg,
					 unsigned int base_reg,
					 unsigned int width)
{
	uint64_t offset = mode;
	unsigned int reg, shift, rem;

	rem = do_div(offset, num_per_reg);

	reg = offset * regmap_get_reg_stride(map) + base_reg;
	shift = width * (num_per_reg - 1 - rem);

	regmap_update_bits(map, reg, GENMASK(width - 1, 0) << shift,
			   val << shift);
}

static void axg_spdifin_write_timer(struct regmap *map, int mode,
				    unsigned int val)
{
	axg_spdifin_write_mode_param(map, mode, val, SPDIFIN_TIMER_PER_REG,

Annotation

Implementation Notes