linux 驱动学习笔记-ALSA声卡驱动(二)

前言

ASoC是建立在标准ALSA驱动层上 ,对底层的alsa框架封装了一层,为了更好的支持嵌入式cpu和音频解码器设备的一套软件体系
在ASOC出现之前 解码器驱动和平台CPU驱动联系过于紧密,导致不同平台的解码器驱动代码的重复,耳机麦克风的热插拔都需要根据不同平台来进行修改等,ASOC就是为了解决这些问题才被提出来的。

ASOC驱动框架

Soc(Platform)

一般指某个平台比如s3c2440,imx6ull等,一般包括了DMA,I2S,PCM等接口,使得一款CPU封装好Platform驱动 就可以在不同的机器上进行使用。一般包括了这个CPU的音频DMA和音频接口控制和配置等,与解码器无关。

Codec

解码器驱动,例如解码器WM8960 里面包含了I2S(一般用来修改寄存器),I2C 多个输入多个输出等接口,codec驱动使得同一款解码器在不同机器上变得可重用。codec驱动的设计与平台本身无关,一般包含了一些音频控件,音频接口,动态音频电源管理,IO等功能。

Machine

机器的驱动,每个不同的机器对应一个不同的Machine驱动,Machine驱动是为了绑定Platform驱动和Codec 驱动来实现的

结构体介绍

1.snd_soc_dai_ops

struct snd_soc_dai_ops {

	int (*set_sysclk)(struct snd_soc_dai *dai,
		int clk_id, unsigned int freq, int dir);//设置主时钟
	int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
		unsigned int freq_in, unsigned int freq_out);//设置PLL参数
	int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);//设置分频系数
	int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio);

	/*
	 * DAI format configuration
	 * Called by soc_card drivers, normally in their hw_params.
	 */
	int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);//设置dai的格式
	int (*xlate_tdm_slot_mask)(unsigned int slots,
		unsigned int *tx_mask, unsigned int *rx_mask);
	int (*set_tdm_slot)(struct snd_soc_dai *dai,
		unsigned int tx_mask, unsigned int rx_mask,
		int slots, int slot_width);//如果dai支持时分复用,用于设置时分复用的slot
	int (*set_channel_map)(struct snd_soc_dai *dai,
		unsigned int tx_num, unsigned int *tx_slot,
		unsigned int rx_num, unsigned int *rx_slot);//声道的时分复用映射设置
	int (*get_channel_map)(struct snd_soc_dai *dai,
			unsigned int *tx_num, unsigned int *tx_slot,
			unsigned int *rx_num, unsigned int *rx_slot);//获取声道的时分复用映射的配置
	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);//设置dai引脚的状态,当与其他dai并联使用同一引脚时需要使用该回调

	int (*set_sdw_stream)(struct snd_soc_dai *dai,
			void *stream, int direction);
	/*
	 * DAI digital mute - optional.
	 * Called by soc-core to minimise any pops.
	 */
	int (*digital_mute)(struct snd_soc_dai *dai, int mute);
	int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream);


	int (*startup)(struct snd_pcm_substream *,
		struct snd_soc_dai *);//当应用程序打开一个pcm设备时,该函数会被调用
	void (*shutdown)(struct snd_pcm_substream *,
		struct snd_soc_dai *);//当应用程序关闭一个pcm设备时,该函数会被调用
	int (*hw_params)(struct snd_pcm_substream *,
		struct snd_pcm_hw_params *, struct snd_soc_dai *);//驱动的hw_params阶段,该函数会被调用
	int (*hw_free)(struct snd_pcm_substream *,
		struct snd_soc_dai *);
	int (*prepare)(struct snd_pcm_substream *,
		struct snd_soc_dai *);//正式开始数据传送之前会调用该函数

	int (*trigger)(struct snd_pcm_substream *, int,
		struct snd_soc_dai *);//数据传送的开始,暂停,恢复和停止时,该函数会被调用
	int (*bespoke_trigger)(struct snd_pcm_substream *, int,
		struct snd_soc_dai *);
	/*
	 * For hardware based FIFO caused delay reporting.
	 * Optional.
	 */
	snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
		struct snd_soc_dai *);
};

machine驱动调用下面的函数,machine驱动在他的snd_pcm_ops字段中的hw_params回调中使用这些

int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
			   int div_id, int div)
int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
			   unsigned int freq, int dir)
int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
			unsigned int freq_in, unsigned int freq_out)
int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
/*
	fmt :
		[0 : 3] 用于设置接口的格式
		SND_SOC_DAIFMT_I2S		1 /* I2S mode 
		SND_SOC_DAIFMT_RIGHT_J		2 /* Right Justified mode 
		SND_SOC_DAIFMT_LEFT_J		3 /* Left Justified mode 
		SND_SOC_DAIFMT_DSP_A		4 /* L data MSB after FRM LRC 
		SND_SOC_DAIFMT_DSP_B		5 /* L data MSB during FRM LRC 
		SND_SOC_DAIFMT_AC97		6 /* AC97 
		SND_SOC_DAIFMT_PDM		7 /* Pulse density modulation 
		[4 : 7] 用于设置接口的格式
		SND_SOC_DAIFMT_CONT		(1 << 4) /* continuous clock 
		SND_SOC_DAIFMT_GATED		(2 << 4) /* clock is gated 
		[8 : 11] 用于设置接口的格式
		SND_SOC_DAIFMT_NB_NF		(1 << 8) /* normal bit clock + frame 
		SND_SOC_DAIFMT_NB_IF		(2 << 8) /* normal BCLK + inv FRM 
		SND_SOC_DAIFMT_IB_NF		(3 << 8) /* invert BCLK + nor FRM 
		SND_SOC_DAIFMT_IB_IF		(4 << 8) /* invert BCLK + FRM 
		[12 : 15] 用于设置接口的格式
		SND_SOC_DAIFMT_CBM_CFM		(1 << 12) /* codec clk & FRM master 
		SND_SOC_DAIFMT_CBS_CFM		(2 << 12) /* codec clk slave & FRM master 
		SND_SOC_DAIFMT_CBM_CFS		(3 << 12) /* codec clk master & frame slave 
		SND_SOC_DAIFMT_CBS_CFS		(4 << 12) /* codec clk & FRM slave */
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)

2.snd_soc_dai

struct snd_soc_dai {
	const char *name;/* dai的名字 */
	int id;
	struct device *dev;/* 设备指针 */

	/* driver ops */
	struct snd_soc_dai_driver *driver;/* 指向dai驱动结构的指针 */

	/* DAI runtime info */
	unsigned int capture_active;		/* stream usage count */
	unsigned int playback_active;		/* stream usage count */
	unsigned int probed:1;

	unsigned int active;

	struct snd_soc_dapm_widget *playback_widget;/* dapm部件指针 */
	struct snd_soc_dapm_widget *capture_widget;/* dapm部件指针 */

	/* DAI DMA data */
	void *playback_dma_data;/* 用于管理playback dma */
	void *capture_dma_data;/* 用于管理capture dma */

	/* Symmetry data - only valid if symmetry is being enforced */
	unsigned int rate;//采样率
	unsigned int channels;//通道数
	unsigned int sample_bits;//采样位数

	/* parent platform/codec */
	struct snd_soc_component *component;//所属控件

	/* CODEC TDM slot masks and params (for fixup) */
	unsigned int tx_mask;
	unsigned int rx_mask;

	struct list_head list;
};

3.snd_soc_dai_driver

struct snd_soc_dai_driver {
	/* DAI description */
	const char *name;/* dai驱动名字 */
	unsigned int id;/* dai驱动ID */
	unsigned int base;
	struct snd_soc_dobj dobj;

	/* DAI driver callbacks */
	int (*probe)(struct snd_soc_dai *dai);/* dai驱动的probe函数,由snd_soc_instantiate_card回调 */
	int (*remove)(struct snd_soc_dai *dai);
	int (*suspend)(struct snd_soc_dai *dai);/* 电源挂起 */
	int (*resume)(struct snd_soc_dai *dai);/* 电源恢复 */
	/* compress dai */
	int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);
	/* Optional Callback used at pcm creation*/
	int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,
		       struct snd_soc_dai *dai);

	/* ops */
	const struct snd_soc_dai_ops *ops;/* 指向本dai的snd_soc_dai_ops结构 */
	const struct snd_soc_cdai_ops *cops;

	/* DAI capabilities */
	struct snd_soc_pcm_stream capture;  /* 描述capture的能力 */
	struct snd_soc_pcm_stream playback;  /* 描述playback的能力 */
	unsigned int symmetric_rates:1;
	unsigned int symmetric_channels:1;
	unsigned int symmetric_samplebits:1;
	unsigned int bus_control:1; /* DAI is also used for the control bus */

	/* probe ordering - for components with runtime dependencies */
	int probe_order;
	int remove_order;
};

4.snd_soc_component

struct snd_soc_component {
	const char *name;/*component名字  也就是codec名字或者platform名字*/
	int id;
	const char *name_prefix;
	struct device *dev;/*component所属设备*/
	struct snd_soc_card *card;/* 指向Machine驱动的card实例 */

	unsigned int active;

	unsigned int suspended:1; /* is in suspend PM state */

	struct list_head list;
	struct list_head card_aux_list; /* for auxiliary bound components */
	struct list_head card_list;

	const struct snd_soc_component_driver *driver;/* 指向该component_driver指针 */

	struct list_head dai_list;/*用于管理snd_soc_dai的链表*/
	int num_dai;/*拥有snd_soc_dai个数*/

	struct regmap *regmap;
	int val_bytes;

	struct mutex io_mutex;

	/* attached dynamic objects */
	struct list_head dobj_list;

	/*
	 * DO NOT use any of the fields below in drivers, they are temporary and
	 * are going to be removed again soon. If you use them in driver code
	 * the driver will be marked as BROKEN when these fields are removed.
	 */

	/* Don't use these, use snd_soc_component_get_dapm() */
	struct snd_soc_dapm_context dapm;

	/* machine specific init */
	int (*init)(struct snd_soc_component *component);

#ifdef CONFIG_DEBUG_FS
	struct dentry *debugfs_root;
	const char *debugfs_prefix;
#endif
};

5.snd_soc_component_driver

struct snd_soc_component_driver {
	const char *name;/*component_driver 名*/

	/* Default control and setup, added after probe() is run */
	const struct snd_kcontrol_new *controls; /* 音频控件指针 */
	unsigned int num_controls;/*总个数*/
	const struct snd_soc_dapm_widget *dapm_widgets;/* dapm部件指针 */
	unsigned int num_dapm_widgets;/*总个数*/
	const struct snd_soc_dapm_route *dapm_routes;/* dapm路线指针 */
	unsigned int num_dapm_routes;/*总个数*/
	/*加载卸载 电源管理函数*/
	int (*probe)(struct snd_soc_component *component);
	void (*remove)(struct snd_soc_component *component);
	int (*suspend)(struct snd_soc_component *component);
	int (*resume)(struct snd_soc_component *component);
	/* 读写寄存器函数 */
	unsigned int (*read)(struct snd_soc_component *component,
			     unsigned int reg);
	int (*write)(struct snd_soc_component *component,
		     unsigned int reg, unsigned int val);
	/* PCM申请和释放函数 */
	/* pcm creation and destruction */
	int (*pcm_new)(struct snd_soc_pcm_runtime *rtd);
	void (*pcm_free)(struct snd_pcm *pcm);
	/* 时钟配置函数 */
	/* component wide operations */
	int (*set_sysclk)(struct snd_soc_component *component,
			  int clk_id, int source, unsigned int freq, int dir);
	int (*set_pll)(struct snd_soc_component *component, int pll_id,
		       int source, unsigned int freq_in, unsigned int freq_out);
	int (*set_jack)(struct snd_soc_component *component,
			struct snd_soc_jack *jack,  void *data);

	/* DT */
	int (*of_xlate_dai_name)(struct snd_soc_component *component,
				 struct of_phandle_args *args,
				 const char **dai_name);
	int (*of_xlate_dai_id)(struct snd_soc_component *comment,
			       struct device_node *endpoint);
	void (*seq_notifier)(struct snd_soc_component *component,
			     enum snd_soc_dapm_type type, int subseq);
	int (*stream_event)(struct snd_soc_component *component, int event);
	int (*set_bias_level)(struct snd_soc_component *component,
			      enum snd_soc_bias_level level);/* 偏置电压配置函数 */

	const struct snd_pcm_ops *ops;
	const struct snd_compr_ops *compr_ops;

	/* probe ordering - for components with runtime dependencies */
	int probe_order;
	int remove_order;

	/*
	 * signal if the module handling the component should not be removed
	 * if a pcm is open. Setting this would prevent the module
	 * refcount being incremented in probe() but allow it be incremented
	 * when a pcm is opened and decremented when it is closed.
	 */
	unsigned int module_get_upon_open:1;

	/* bits */
	unsigned int idle_bias_on:1;
	unsigned int suspend_bias_off:1;
	unsigned int use_pmdown_time:1; /* care pmdown_time at stop */
	unsigned int endianness:1;
	unsigned int non_legacy_dai_naming:1;

	/* this component uses topology and ignore machine driver FEs */
	const char *ignore_machine;
	const char *topology_name_prefix;
	int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,
				  struct snd_pcm_hw_params *params);
	bool use_dai_pcm_id;	/* use DAI link PCM ID as PCM device number */
	int be_pcm_base;	/* base device ID for all BE PCMs */
};

Platform驱动分析

以openwrt21.02 kernel5.4中的/sound/soc/ralink/ralink-i2s.c为例:

1.platform驱动

struct rt_i2s_data {
	u32 flags;
	void (*refclk_setup)(void);
};
static const struct snd_soc_dai_ops ralink_i2s_dai_ops = {
	.set_sysclk = ralink_i2s_set_sysclk,
	.set_fmt = ralink_i2s_set_fmt,
	.startup = ralink_i2s_startup,
	.shutdown = ralink_i2s_shutdown,
	.hw_params = ralink_i2s_hw_params,
	.trigger = ralink_i2s_trigger,
};

static struct snd_soc_dai_driver ralink_i2s_dai = {
	.name = "ralink-i2s",
	.probe = ralink_i2s_dai_probe,
	.remove = ralink_i2s_dai_remove,
	.ops = &ralink_i2s_dai_ops,
	.capture = {
		.stream_name = "I2S Capture",
		.channels_min = 2,
		.channels_max = 2,
		.rate_min = 5512,
		.rate_max = 192000,
		.rates = SNDRV_PCM_RATE_CONTINUOUS,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
	.playback = {
		.stream_name = "I2S Playback",
		.channels_min = 2,
		.channels_max = 2,
		.rate_min = 5512,
		.rate_max = 192000,
		.rates = SNDRV_PCM_RATE_CONTINUOUS,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
	.symmetric_rates = 1,
};
/*
	info:标明了PCM类型和能力
		SNDRV_PCM_INFO_MMAP 支持mmap
		交错格式,或者非交错格式SNDRV_PCM_INFO_INTERLEAVED 和SNDRV_PCM_INFO_NONINTERLEAVED
	formats:支持的格式	
	rates:支持的速率
	channel_min和channe_max:最小,最大通道数
	period_byte 周期大小
	periods 周期数量
	buffer_bytes_max最大buffer大小,单位字节
*/
static struct snd_pcm_hardware ralink_pcm_hardware = {
	.info = SNDRV_PCM_INFO_MMAP |
		SNDRV_PCM_INFO_MMAP_VALID |
		SNDRV_PCM_INFO_INTERLEAVED |
		SNDRV_PCM_INFO_BLOCK_TRANSFER,
	.formats = SNDRV_PCM_FMTBIT_S16_LE,
	.channels_min		= 2,
	.channels_max		= 2,
	.period_bytes_min	= PAGE_SIZE,
	.period_bytes_max	= PAGE_SIZE * 2,
	.periods_min		= 2,
	.periods_max		= 128,
	.buffer_bytes_max	= 128 * 1024,
	.fifo_size		= RALINK_I2S_FIFO_SIZE,
};

static const struct snd_dmaengine_pcm_config ralink_dmaengine_pcm_config = {
	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
	.pcm_hardware = &ralink_pcm_hardware,
	.prealloc_buffer_size = 256 * PAGE_SIZE,
};
/*name用于machine dai_link匹配platform驱动*/
static const struct snd_soc_component_driver ralink_i2s_component = {
	.name = "ralink-i2s",
};
struct rt_i2s_data mt7628_i2s_data = {
	.flags = (RALINK_FLAGS_ENDIAN | RALINK_FLAGS_24BIT |
			RALINK_FLAGS_LEFT_J),
	.refclk_setup = mt7628_refclk_setup};

static const struct of_device_id ralink_i2s_match_table[] = {
	...
	{ .compatible = "mediatek,mt7628-i2s",
		.data = (void *)&mt7628_i2s_data },
};
static int ralink_i2s_probe(struct platform_device *pdev)
{
	const struct of_device_id *match;
	struct device_node *np = pdev->dev.of_node;
	struct ralink_i2s *i2s;
	struct resource *res;
	int irq, ret;
	u32 dma_req;
	struct rt_i2s_data *data;

	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
	if (!i2s)
		return -ENOMEM;

	platform_set_drvdata(pdev, i2s);
	i2s->dev = &pdev->dev;

	match = of_match_device(ralink_i2s_match_table, &pdev->dev);
	if (!match)
		return -EINVAL;
	data = (struct rt_i2s_data *)match->data;
	i2s->flags = data->flags;
	/* setup out 12Mhz refclk to codec as mclk */
	if (data->refclk_setup)
		data->refclk_setup();
	/*获得i2s设备树 dma 的ID*/
	if (of_property_read_u32(np, "txdma-req", &dma_req)) {
		dev_err(&pdev->dev, "no txdma-req define\n");
		return -EINVAL;
	}
	i2s->txdma_req = (u16)dma_req;
	if (!(i2s->flags & RALINK_FLAGS_TXONLY)) {
		if (of_property_read_u32(np, "rxdma-req", &dma_req)) {
			dev_err(&pdev->dev, "no rxdma-req define\n");
			return -EINVAL;
		}
		i2s->rxdma_req = (u16)dma_req;
	}
	/*获得I2S寄存器基地址*/
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	i2s->regs = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(i2s->regs))
		return PTR_ERR(i2s->regs);

	i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->regs,
			&ralink_i2s_regmap_config);
	if (IS_ERR(i2s->regmap)) {
		dev_err(&pdev->dev, "regmap init failed\n");
		return PTR_ERR(i2s->regmap);
	}
	/*获得i2s的中断号*/
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(&pdev->dev, "failed to get irq\n");
                return -EINVAL;
        }

#if (RALINK_I2S_INT_EN)
	ret = devm_request_irq(&pdev->dev, irq, ralink_i2s_irq,
			0, dev_name(&pdev->dev), i2s);
	if (ret) {
		dev_err(&pdev->dev, "failed to request irq\n");
		return ret;
	}
#endif
	/*获得I2S的时钟*/
	i2s->clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(i2s->clk)) {
		dev_err(&pdev->dev, "no clock defined\n");
		return PTR_ERR(i2s->clk);
	}
	/*使能时钟*/
	ret = clk_prepare_enable(i2s->clk);
	if (ret)
		return ret;
	/*初始化DMA的目标地址,dma传送单元大小和通道号等*/
	ralink_i2s_init_dma_data(i2s, res);

	device_reset(&pdev->dev);
	
	ret = ralink_i2s_debugfs_create(i2s);
	if (ret) {
		dev_err(&pdev->dev, "create debugfs failed\n");
		goto err_clk_disable;
	}

	/* dormat支持24bit */
	if (i2s->flags & RALINK_FLAGS_24BIT) {
		ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S24_LE;
		ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S24_LE;
	}

	/* 大端模式*/
	if (i2s->flags & RALINK_FLAGS_ENDIAN) {
		ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S16_BE;
		ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S16_BE;
		ralink_pcm_hardware.formats |= SNDRV_PCM_FMTBIT_S16_BE;
		if (i2s->flags & RALINK_FLAGS_24BIT) {
			ralink_i2s_dai.capture.formats |=
				SNDRV_PCM_FMTBIT_S24_BE;
			ralink_i2s_dai.playback.formats |=
				SNDRV_PCM_FMTBIT_S24_BE;
			ralink_pcm_hardware.formats |=
				SNDRV_PCM_FMTBIT_S24_BE;
		}
	}

	/* disable capture support */
	if (i2s->flags & RALINK_FLAGS_TXONLY)
		memset(&ralink_i2s_dai.capture, sizeof(ralink_i2s_dai.capture),
				0);
	/*注册控件cpu_dai*/
	ret = devm_snd_soc_register_component(&pdev->dev, &ralink_i2s_component,
			&ralink_i2s_dai, 1);
	if (ret)
		goto err_debugfs;
	/*注册DMA控件*/
	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
			&ralink_dmaengine_pcm_config,
			SND_DMAENGINE_PCM_FLAG_COMPAT);
	if (ret)
		goto err_debugfs;

	dev_info(i2s->dev, "mclk %luMHz\n", clk_get_rate(i2s->clk) / 1000000);

	return 0;

err_debugfs:
	ralink_i2s_debugfs_remove(i2s);

err_clk_disable:
	clk_disable_unprepare(i2s->clk);

	return ret;
}

2.cpu_dai 注册流程

(1).ralink_i2s控件注册

devm_snd_soc_register_component()
	->snd_soc_register_component()
		->snd_soc_add_component()
			->snd_soc_component_initialize()
			->snd_soc_register_dais()
				->soc_add_dai()
					->list_add_tail(&dai->list, &component->dai_list);
			->snd_soc_component_add(component);
				->list_add(&component->list, &component_list);	

(2).snd_dmaengine_pcm控件注册

devm_snd_dmaengine_pcm_register()
	->snd_dmaengine_pcm_register()
		->snd_soc_add_component(dev, &pcm->component,&dmaengine_pcm_component, NULL, 0);
			->snd_soc_component_initialize()
			->snd_soc_register_dais()
				->soc_add_dai()
					->list_add_tail(&dai->list, &component->dai_list);
			->snd_soc_component_add(component);
				->list_add(&component->list, &component_list);	

Codec驱动分析

以openwrt21.02 kernel5.4中的/sound/soc/codecs/wm8960.c为例:

1.CODEC的平台无关性

static const struct regmap_config wm8960_regmap = {
	.reg_bits = 7,//寄存器地址位数
	.val_bits = 9,//数据位数
	.max_register = WM8960_PLL4,

	.reg_defaults = wm8960_reg_defaults,
	.num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults),
	.cache_type = REGCACHE_RBTREE,

	.volatile_reg = wm8960_volatile,
};
static int wm8960_i2c_probe(struct i2c_client *i2c,
			    const struct i2c_device_id *id)
{
	struct wm8960_data *pdata = dev_get_platdata(&i2c->dev);
	struct wm8960_priv *wm8960;
	int ret;
	/*为结构体分配内存并清零*/
	wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv),
			      GFP_KERNEL);
	if (wm8960 == NULL)
		return -ENOMEM;
	/*获取mclk*/
	wm8960->mclk = devm_clk_get(&i2c->dev, "mclk");
	if (IS_ERR(wm8960->mclk)) {
		if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER)
			return -EPROBE_DEFER;
	}
	/*wm8960 i2c的regmap子系统获取 用于寄存器读写*/
	wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap);
	if (IS_ERR(wm8960->regmap))
		return PTR_ERR(wm8960->regmap);

	if (pdata)
		memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data));
	else if (i2c->dev.of_node)
		wm8960_set_pdata_from_of(i2c, &wm8960->pdata);

	ret = wm8960_reset(wm8960->regmap);
	if (ret != 0) {
		dev_err(&i2c->dev, "Failed to issue reset\n");
		return ret;
	}

	if (wm8960->pdata.shared_lrclk) {
		ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2,
					 0x4, 0x4);
		if (ret != 0) {
			dev_err(&i2c->dev, "Failed to enable LRCM: %d\n",
				ret);
			return ret;
		}
	}

	/* Latch the update bits */
	regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x100, 0x100);
	regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x100, 0x100);
	regmap_update_bits(wm8960->regmap, WM8960_LADC, 0x100, 0x100);
	regmap_update_bits(wm8960->regmap, WM8960_RADC, 0x100, 0x100);
	regmap_update_bits(wm8960->regmap, WM8960_LDAC, 0x100, 0x100);
	regmap_update_bits(wm8960->regmap, WM8960_RDAC, 0x100, 0x100);
	regmap_update_bits(wm8960->regmap, WM8960_LOUT1, 0x100, 0x100);
	regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100);
	regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100);
	regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100);

	i2c_set_clientdata(i2c, wm8960);
	/*注册一个codec 组件 流程和上面一样*/
	ret = devm_snd_soc_register_component(&i2c->dev,
			&soc_component_dev_wm8960, &wm8960_dai, 1);

	return ret;
}
static const struct of_device_id wm8960_of_match[] = {
       { .compatible = "wlf,wm8960", },
       { }
};
static struct i2c_driver wm8960_i2c_driver = {
	.driver = {
		.name = "wm8960",
		.of_match_table = wm8960_of_match,
	},
	.probe =    wm8960_i2c_probe,
	.remove =   wm8960_i2c_remove,
	.id_table = wm8960_i2c_id,
};

2.codec组件驱动


static int wm8960_probe(struct snd_soc_component *component)
{
	struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component);
	struct wm8960_data *pdata = &wm8960->pdata;

	if (pdata->capless)
		wm8960->set_bias_level = wm8960_set_bias_level_capless;
	else
		wm8960->set_bias_level = wm8960_set_bias_level_out3;
	snd_soc_add_component_controls(component, wm8960_snd_controls,
				     ARRAY_SIZE(wm8960_snd_controls));/*添加控件*/
	wm8960_add_widgets(component);/*添加DAPM控件*/

	return 0;
}
static const struct snd_soc_component_driver soc_component_dev_wm8960 = {
	.probe			= wm8960_probe,
	.set_bias_level		= wm8960_set_bias_level,
	.suspend_bias_off	= 1,
	.idle_bias_on		= 1,
	.use_pmdown_time	= 1,
	.endianness		= 1,
	.non_legacy_dai_naming	= 1,
};

3.codec_dai

static const struct snd_soc_dai_ops wm8960_dai_ops = {
	.hw_params = wm8960_hw_params,
	.hw_free = wm8960_hw_free,
	.digital_mute = wm8960_mute,
	.set_fmt = wm8960_set_dai_fmt,
	.set_clkdiv = wm8960_set_dai_clkdiv,
	.set_pll = wm8960_set_dai_pll,
	.set_sysclk = wm8960_set_dai_sysclk,
};

static struct snd_soc_dai_driver wm8960_dai = {
	.name = "wm8960-hifi",
	.playback = {
		.stream_name = "Playback",
		.channels_min = 1,
		.channels_max = 2,
		.rates = WM8960_RATES,
		.formats = WM8960_FORMATS,},
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 2,
		.rates = WM8960_RATES,
		.formats = WM8960_FORMATS,},
	.ops = &wm8960_dai_ops,
	.symmetric_rates = 1,
};
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值