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(®ister_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设备的创建过程