sound/soc/apple/mca.c

Source file repositories/reference/linux-study-clean/sound/soc/apple/mca.c

File Facts

System
Linux kernel
Corpus path
sound/soc/apple/mca.c
Extension
.c
Size
31697 bytes
Lines
1209
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 mca_cluster {
	int no;
	__iomem void *base;
	struct mca_data *host;
	struct device *pd_dev;
	struct clk *clk_parent;
	struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1];

	bool port_started[SNDRV_PCM_STREAM_LAST + 1];
	int port_driver; /* The cluster driving this cluster's port */

	bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1];
	struct device_link *pd_link;

	unsigned int bclk_ratio;

	/* Masks etc. picked up via the set_tdm_slot method */
	int tdm_slots;
	int tdm_slot_width;
	unsigned int tdm_tx_mask;
	unsigned int tdm_rx_mask;
};

struct mca_data {
	struct device *dev;

	__iomem void *switch_base;

	struct device *pd_dev;
	struct reset_control *rstc;
	struct device_link *pd_link;

	/* Mutex for accessing port_driver of foreign clusters */
	struct mutex port_mutex;

	int nclusters;
	struct mca_cluster clusters[] __counted_by(nclusters);
};

static void mca_modify(struct mca_cluster *cl, int regoffset, u32 mask, u32 val)
{
	__iomem void *ptr = cl->base + regoffset;
	u32 newval;

	newval = (val & mask) | (readl_relaxed(ptr) & ~mask);
	writel_relaxed(newval, ptr);
}

/*
 * Get the cluster of FE or BE DAI
 */
static struct mca_cluster *mca_dai_to_cluster(struct snd_soc_dai *dai)
{
	struct mca_data *mca = snd_soc_dai_get_drvdata(dai);
	/*
	 * FE DAIs are         0 ... nclusters - 1
	 * BE DAIs are nclusters ... 2*nclusters - 1
	 */
	int cluster_no = dai->id % mca->nclusters;

	return &mca->clusters[cluster_no];
}

/* called before PCM trigger */
static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd,
				 struct snd_soc_dai *dai)
{
	struct mca_cluster *cl = mca_dai_to_cluster(dai);
	bool is_tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
	int serdes_unit = is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF;
	int serdes_conf =
		serdes_unit + (is_tx ? REG_TX_SERDES_CONF : REG_RX_SERDES_CONF);

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
			   FIELD_PREP(SERDES_CONF_SYNC_SEL, 0));
		mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
			   FIELD_PREP(SERDES_CONF_SYNC_SEL, 7));
		mca_modify(cl, serdes_unit + REG_SERDES_STATUS,
			   SERDES_STATUS_EN | SERDES_STATUS_RST,
			   SERDES_STATUS_RST);
		/*
		 * Experiments suggest that it takes at most ~1 us
		 * for the bit to clear, so wait 2 us for good measure.
		 */
		udelay(2);
		WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) &

Annotation

Implementation Notes