linux 音频驱动的注册

AK7757驱动调试

简介

使用的主控芯片是imx6q,AK7757芯片的主要功能是用于蓝牙,录音,和media的播放;

设备树的匹配过程

// DSP AK7757
	sound-ak7757 {
		compatible = "fsl,imx-audio-ak7757-codec";
		model = "imx-ak7757";
		esai-controller = <&esai>;
		asrc-controller = <&asrc_p2p>;
		audio-codec = <&akcodec>;
 	};

 	// DSP AK7757
	akcodec: ak7757-codec {
		compatible = "akm,ak7757";
		clocks = <&codec_osc 0>;
		clock-names = "mclk";
 	};

 	// DSP AK7757
 	clocks {
    	codec_osc: anaclk2 {
        	compatible = "fixed-clock";
        	#clock-cells = <0>;
        	clock-frequency = <24576000>;
    	};
	};

	// BT NF2208AE
	sound-bt {
		compatible = "fsl,imx-audio-bt-codec";
 		model = "imx6-wandboard-bt-codec";
 		ssi-controller = <&ssi1>;
 		audio-codec = <&btcodec>;
 		mux-int-port = <1>;
 		mux-ext-port = <4>;
	};

	// BT NF2208AE
	btcodec: bt-codec-pcm {       
		compatible = "fsl,bt-codec";
 	};

/*egle-card.c
static const struct of_device_id imx_cs42888_dt_ids[] = {
	{ .compatible = "fsl,imx-audio-ak7757-codec", },
	{ /* sentinel */ }
};

static struct platform_driver imx_cs42888_driver = {
	.probe = imx_cs42888_probe,
	.remove = imx_cs42888_remove,
	.driver = {
		.name = "imx-ak7757",
		.owner = THIS_MODULE,
		.pm = &snd_soc_pm_ops,
		.of_match_table = imx_cs42888_dt_ids,
	},
};
module_platform_driver(imx_cs42888_driver);


/*imx_bt_codec.c*/
static const struct of_device_id imx_bt_dt_ids[] = {
	{ .compatible = "fsl,imx-audio-bt-codec", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_bt_dt_ids);

static struct platform_driver imx_bt_audio_driver = {
	.driver = {
		.name = "imx-bt",
		.owner = THIS_MODULE,
		.of_match_table = imx_bt_dt_ids,
	},
	.probe = imx_bt_probe,
	.remove = imx_bt_remove,
};
module_platform_driver(imx_bt_audio_driver);


从设备树添加可以看出添加了蓝牙的音频部分,和meidia音频部分,可以看出驱动中蓝牙和声卡和设备树的匹配过程;

linux音频框架

linux音频驱动框架在linux内核顶层目录下、sound目录下;当驱动加载之后在/dev/snd/ 会生成声卡设备,pcmcapture ,pcmrecord,pcmcontral 等操作接口

音频驱动的注册过程:
static const struct of_device_id imx_bt_dt_ids[] = {
{ .compatible = “fsl,imx-audio-bt-codec”, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_bt_dt_ids);

static struct platform_driver imx_bt_audio_driver = {
.driver = {
.name = “imx-bt”,
.owner = THIS_MODULE,
.of_match_table = imx_bt_dt_ids,
},
.probe = imx_bt_probe,
.remove = imx_bt_remove,
};
module_platform_driver(imx_bt_audio_driver);
匹配成功之后进入到probe函数

static int imx_bt_probe(struct platform_device *pdev)
{

ret = snd_soc_of_parse_card_name(&data->card, "model");

ret = snd_soc_register_card(&data->card);

platform_set_drvdata(pdev, data);

}

由snd_soc_register_card函数进行声卡的注册,进入到soc-core.c文件

int snd_soc_register_card(struct snd_soc_card *card)
{
	ret = snd_soc_instantiate_card(card);
}

进入到snd_soc_instantiate_card

static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
	struct snd_soc_codec *codec;
	struct snd_soc_codec_conf *codec_conf;
	enum snd_soc_compress_type compress_type;
	struct snd_soc_dai_link *dai_link;
	int ret, i, order, dai_fmt;

	

	/* card bind complete so register a sound card */
	ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
			card->owner, 0, &card->snd_card);
	if (ret < 0) {
		dev_err(card->dev, "ASoC: can't create sound card for"
			" card %s: %d\n", card->name, ret);
		goto base_error;
	}

	/* probe all DAI links on this card */
	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
			order++) {
		for (i = 0; i < card->num_links; i++) {
			ret = soc_probe_link_dais(card, i, order);
			if (ret < 0) {
				dev_err(card->dev,
					"ASoC: failed to instantiate card %d\n",
					ret);
				goto probe_dai_err;
			}
		}
	}


	if (card->fully_routed)
		list_for_each_entry(codec, &card->codec_dev_list, card_list)
			snd_soc_dapm_auto_nc_codec_pins(codec);

	ret = snd_card_register(card->snd_card);
	
}

在snd_soc_instantiate_card中可以看到snd_card_create,soc_probe_link_dais,snd_card_register,这三个函数完成了声卡的创建,snd_card_create创建了声卡的控制设备,soc_probe_link_dais创建了声卡设备的playback和capture设备;

int snd_card_create(int idx, const char *xid,
		    struct module *module, int extra_size,
		    struct snd_card **card_ret)
{
	struct snd_card *card;
	int err, idx2;

	err = snd_ctl_create(card);
	if (err < 0) {
		snd_printk(KERN_ERR "unable to register control minors\n");
		goto __error;
	}

}

int snd_ctl_create(struct snd_card *card)
{
	static struct snd_device_ops ops = {
		.dev_free = snd_ctl_dev_free,
		.dev_register =	snd_ctl_dev_register,
		.dev_disconnect = snd_ctl_dev_disconnect,
	};

	if (snd_BUG_ON(!card))
		return -ENXIO;
	return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
}

static int snd_ctl_dev_register(struct snd_device *device)
{
	struct snd_card *card = device->device_data;
	int err, cardnum;
	char name[16];

	if (snd_BUG_ON(!card))
		return -ENXIO;
	cardnum = card->number;
	if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
		return -ENXIO;
	sprintf(name, "controlC%i", cardnum);
	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
				       &snd_ctl_f_ops, card, name)) < 0)
		return err;
	return 0;
}

static inline int snd_register_device(int type, struct snd_card *card, int dev,
				      const struct file_operations *f_ops,
				      void *private_data,
				      const char *name)
{
	return snd_register_device_for_dev(type, card, dev, f_ops,
					   private_data, name,
					   snd_card_get_device_link(card));
}



int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
				const struct file_operations *f_ops,
				void *private_data,
				const char *name, struct device *device)
{
	preg->dev = device_create(sound_class, device, MKDEV(major, minor),
				  private_data, "%s", name);
	if (IS_ERR(preg->dev)) {
		snd_minors[minor] = NULL;
		mutex_unlock(&sound_mutex);
		minor = PTR_ERR(preg->dev);
		kfree(preg);
		return minor;
	}


}

可见pcm控制设备创建完成;

static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
{
	struct snd_soc_dai_link *dai_link = &card->dai_link[num];
	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
	struct snd_soc_codec *codec = rtd->codec;
	struct snd_soc_platform *platform = rtd->platform;
	struct snd_soc_dai *codec_dai = rtd->codec_dai;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	struct snd_soc_dapm_widget *play_w, *capture_w;
	int ret;

	
	
		if (!dai_link->params) {
			/* create the pcm */
			ret = soc_new_pcm(rtd, num);
			if (ret < 0) {
				dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
				       dai_link->stream_name, ret);
				return ret;
			}
		} else {
			/* link the DAI widgets */
			play_w = codec_dai->playback_widget;
			capture_w = cpu_dai->capture_widget;
			if (play_w && capture_w) {
				ret = snd_soc_dapm_new_pcm(card, dai_link->params,
						   capture_w, play_w);
				if (ret != 0) {
					dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
						play_w->name, capture_w->name, ret);
					return ret;
				}
			}
}



int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
	struct snd_soc_platform *platform = rtd->platform;
	struct snd_soc_dai *codec_dai = rtd->codec_dai;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	struct snd_pcm *pcm;
	char new_name[64];
	
		ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
				playback, capture, &pcm);
	} else {
		if (rtd->dai_link->dynamic)
			snprintf(new_name, sizeof(new_name), "%s (*)",
				rtd->dai_link->stream_name);
		else
			snprintf(new_name, sizeof(new_name), "%s %s-%d",
				rtd->dai_link->stream_name, codec_dai->name, num);

		ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
}

int snd_pcm_new(struct snd_card *card, const char *id, int device,
		int playback_count, int capture_count, struct snd_pcm **rpcm)
{
	return _snd_pcm_new(card, id, device, playback_count, capture_count,
			false, rpcm);
}


static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
		int playback_count, int capture_count, bool internal,
		struct snd_pcm **rpcm)
{
	struct snd_pcm *pcm;
	int err;
	static struct snd_device_ops ops = {
		.dev_free = snd_pcm_dev_free,
		.dev_register =	snd_pcm_dev_register,
		.dev_disconnect = snd_pcm_dev_disconnect,
	};	
}

static int snd_pcm_dev_register(struct snd_device *device)
{
	int cidx, err;
	struct snd_pcm_substream *substream;
	struct snd_pcm_notify *notify;
	char str[16];
	struct snd_pcm *pcm;
	struct device *dev;
	for (cidx = 0; cidx < 2; cidx++) {
		int devtype = -1;
		if (pcm->streams[cidx].substream == NULL || pcm->internal)
			continue;
		switch (cidx) {
		case SNDRV_PCM_STREAM_PLAYBACK:
			sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
			devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
			break;
		case SNDRV_PCM_STREAM_CAPTURE:
			sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
			devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
			break;
		}
		/* device pointer to use, pcm->dev takes precedence if
		 * it is assigned, otherwise fall back to card's device
		 * if possible */
		dev = pcm->dev;
		if (!dev)
			dev = snd_card_get_device_link(pcm->card);
		/* register pcm */
		err = snd_register_device_for_dev(devtype, pcm->card,
						  pcm->device,
						  &snd_pcm_f_ops[cidx],
						  pcm, str, dev);
		if (err < 0) {
			list_del(&pcm->list);
			mutex_unlock(&register_mutex);
			return err;
		}
		snd_add_device_sysfs_file(devtype, pcm->card, pcm->device,
					  &pcm_attrs);
}


int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
				const struct file_operations *f_ops,
				void *private_data,
				const char *name, struct device *device)
{
	int minor;
	struct snd_minor *preg;

	if (snd_BUG_ON(!name))
		return -EINVAL;
	preg = kmalloc(sizeof *preg, GFP_KERNEL);
	if (preg == NULL)
		return -ENOMEM;
	preg->type = type;
	preg->card = card ? card->number : -1;
	preg->device = dev;
	preg->f_ops = f_ops;
	preg->private_data = private_data;
	preg->card_ptr = card;
	mutex_lock(&sound_mutex);
#ifdef CONFIG_SND_DYNAMIC_MINORS
	minor = snd_find_free_minor(type);
#else
	minor = snd_kernel_minor(type, card, dev);
	if (minor >= 0 && snd_minors[minor])
		minor = -EBUSY;
#endif
	if (minor < 0) {
		mutex_unlock(&sound_mutex);
		kfree(preg);
		return minor;
	}
	snd_minors[minor] = preg;
	preg->dev = device_create(sound_class, device, MKDEV(major, minor),
				  private_data, "%s", name);
	if (IS_ERR(preg->dev)) {
		snd_minors[minor] = NULL;
		mutex_unlock(&sound_mutex);
		minor = PTR_ERR(preg->dev);
		kfree(preg);
		return minor;
	}

	mutex_unlock(&sound_mutex);
	return 0;
}

由此可得出声卡playback,capture设备的创建过程

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值