sound/soc/loongson/loongson_i2s.c

Source file repositories/reference/linux-study-clean/sound/soc/loongson/loongson_i2s.c

File Facts

System
Linux kernel
Corpus path
sound/soc/loongson/loongson_i2s.c
Extension
.c
Size
8085 bytes
Lines
331
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.

Dependency Surface

Detected Declarations

Annotated Snippet

// SPDX-License-Identifier: GPL-2.0
//
// Common functions for loongson I2S controller driver
//
// Copyright (C) 2023 Loongson Technology Corporation Limited.
// Author: Yingkun Meng <mengyingkun@loongson.cn>
//

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <sound/soc.h>
#include <linux/regmap.h>
#include <sound/pcm_params.h>
#include "loongson_i2s.h"

#define LOONGSON_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
			SNDRV_PCM_FMTBIT_S16_LE | \
			SNDRV_PCM_FMTBIT_S20_3LE | \
			SNDRV_PCM_FMTBIT_S24_LE)

#define LOONGSON_I2S_TX_ENABLE	(I2S_CTRL_TX_EN | I2S_CTRL_TX_DMA_EN)
#define LOONGSON_I2S_RX_ENABLE	(I2S_CTRL_RX_EN | I2S_CTRL_RX_DMA_EN)

#define LOONGSON_I2S_DEF_DELAY		10
#define LOONGSON_I2S_DEF_TIMEOUT	500000

static int loongson_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
				struct snd_soc_dai *dai)
{
	struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
	unsigned int mask;
	int ret = 0;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		mask = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
		       LOONGSON_I2S_TX_ENABLE : LOONGSON_I2S_RX_ENABLE;
		regmap_update_bits(i2s->regmap, LS_I2S_CTRL, mask, mask);
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		mask = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
		       LOONGSON_I2S_TX_ENABLE : LOONGSON_I2S_RX_ENABLE;
		regmap_update_bits(i2s->regmap, LS_I2S_CTRL, mask, 0);
		break;
	default:
		ret = -EINVAL;
	}

	return ret;
}

static int loongson_i2s_hw_params(struct snd_pcm_substream *substream,
				  struct snd_pcm_hw_params *params,
				  struct snd_soc_dai *dai)
{
	struct loongson_i2s *i2s = snd_soc_dai_get_drvdata(dai);
	u32 clk_rate = i2s->clk_rate;
	u32 sysclk = i2s->sysclk;
	u32 bits = params_width(params);
	u32 chans = params_channels(params);
	u32 fs = params_rate(params);
	u32 bclk_ratio, mclk_ratio;
	u32 mclk_ratio_frac;
	u32 val = 0;

	switch (i2s->rev_id) {
	case 0:
		bclk_ratio = DIV_ROUND_CLOSEST(clk_rate,
					       (bits * chans * fs * 2)) - 1;
		mclk_ratio = DIV_ROUND_CLOSEST(clk_rate, (sysclk * 2)) - 1;

		/* According to 2k1000LA user manual, set bits == depth */
		val |= (bits << 24);
		val |= (bits << 16);
		val |= (bclk_ratio << 8);
		val |= mclk_ratio;
		regmap_write(i2s->regmap, LS_I2S_CFG, val);

		break;
	case 1:
		bclk_ratio = DIV_ROUND_CLOSEST(sysclk,
					       (bits * chans * fs * 2)) - 1;

Annotation

Implementation Notes