ALSA声卡_从零编写之框架(基于优龙FS2410开发板,UDA1341声卡)

发布时间:2024-07-11  

一、实验环境


1.1 虚拟机环境


    a) Vmware版本:Vmware Workstation 12.5.7


    b) Ubuntu版本:9.10


    c) 内核版本:2.6.31.14


    d) toolchain版本:arm-linux-gcc 4.3.2


1.2 开发板


    优龙FS2410开发板,UDA1341声卡


    内核版本:3.4.2


二、编写代码


2.1 实现machine驱动的框架(s3c2440_uda1341.c)

(参考soundsocsamsungs3c24xx_uda134x.c)


1. 分配注册一个名为soc-audio的平台设备


2. 这个平台设备有一个私有数据 snd_soc_card,里面有一项snd_soc_dai_link,被用来决定ASOC各部分的驱动


static struct snd_soc_ops s3c2440_uda1341_ops = {

// .startup = s3c24xx_uda134x_startup,   //从内核移植过来后,先暂时屏蔽掉这些函数,等用到的时候再加

// .shutdown = s3c24xx_uda134x_shutdown,

// .hw_params = s3c24xx_uda134x_hw_params,

};

static struct snd_soc_dai_link s3c2440_uda1341_dai_link = {

.name = "100ask_UDA1341",

.stream_name = "100ask_UDA1341",

.codec_name = "uda1341-codec", //必须和codec驱动的名字相同

.codec_dai_name = "uda1341-iis", //必须和codec_dai驱动的名字相同

.cpu_dai_name = "s3c2440-iis", //必须和cpu_dai驱动的名字相同

.ops = &s3c2440_uda1341_ops,

.platform_name = "s3c2440-dma",

};

static struct snd_soc_card myalsa_card = {

.name = "S3C2440_UDA1341",

.owner = THIS_MODULE,

.dai_link = &s3c2440_uda1341_dai_link, // snd_soc_dai_link被用来决定ASOC各部分的驱动

.num_links = 1,

};

static void asoc_release(struct device * dev)

{

}

static struct platform_device asoc_dev = {

    .name         = "soc-audio",

    .id       = -1,

    .dev = {

    .release = asoc_release,

},

};

static int s3c2440_uda1341_init(void)

{

    platform_set_drvdata(&asoc_dev, &myalsa_card); //这个平台设备有一个私有数据 snd_soc_card

    platform_device_register(&asoc_dev);    //分配注册一个名为soc-audio的平台设备

    return 0;

}


2.2 实现codec驱动的框架(uda1341.c)

(参考soundsoccodecsuda134x.c)


1. 构造一个snd_soc_dai_driver


static const struct snd_soc_dai_ops uda1341_dai_ops = {

// .startup = uda134x_startup, //从内核移植过来后,先暂时屏蔽掉这些函数,等用到的时候再加

// .shutdown = uda134x_shutdown,

// .hw_params = uda134x_hw_params,

// .digital_mute = uda134x_mute,

// .set_sysclk = uda134x_set_dai_sysclk,

// .set_fmt = uda134x_set_dai_fmt,

};

static struct snd_soc_dai_driver uda1341_dai = {

.name = "uda1341-iis",   //必须和machine驱动的s3c2440_uda1341_dai_link.codec_dai_name一致

/* playback capabilities */

//playback和capture的各个属性值会被用于在soc_pcm_open里调用诸如 runtime->hw.rate_min = max(codec_dai_drv->playback.rate_min, cpu_dai_drv->playback.rate_min);

        .playback = {

.stream_name = "Playback",

.channels_min = 1,

.channels_max = 2,

.rates = UDA134X_RATES,

.formats = UDA134X_FORMATS,

},

/* capture capabilities */

.capture = {

.stream_name = "Capture",

.channels_min = 1,

.channels_max = 2,

.rates = UDA134X_RATES,

.formats = UDA134X_FORMATS,

},

/* pcm operations */

.ops = &uda1341_dai_ops,

};


2. 构造一个snd_soc_codec_driver


static struct snd_soc_codec_driver soc_codec_dev_uda1341 = {

// .probe =        uda134x_soc_probe, // 从内核移植过来后,先暂时屏蔽掉这些函数,等用到时候再加

// .remove =       uda134x_soc_remove,

// .suspend =      uda134x_soc_suspend,

// .resume =       uda134x_soc_resume,

// .reg_cache_size = sizeof(uda134x_reg),

// .reg_word_size = sizeof(u8),

// .reg_cache_default = uda134x_reg,

// .reg_cache_step = 1,

// .read = uda134x_read_reg_cache,

// .write = uda134x_write,

// .set_bias_level = uda134x_set_bias_level,

};


3. 注册它们(一个小技巧:因为snd_soc_register_codec需要传入device参数,所以无法直接在uda1341_init()里调用snd_soc_register_codec,所以多绕一道弯子,通过注册platform_device、platform_driver,在其probe()函数中调用snd_soc_register_codec)


static struct platform_device uda1341_dev = {

    .name    = "uda1341-codec", //必须和machine驱动的s3c2440_uda1341_dai_link.codec_name一致

    .id       = -1,

    .dev = {

    .release = uda1341_dev_release,

},

};

struct platform_driver uda1341_drv = {

.probe = uda1341_probe, //在里面会调用snd_soc_register_codec注册codec驱动和codec_dai驱动

.remove = uda1341_remove,

.driver = {

.name = "uda1341-codec", //必须和uda1341_dev的name一致

}

};

static int uda1341_init(void)

{

    platform_device_register(&uda1341_dev);

    platform_driver_register(&uda1341_drv);

    return 0;

}

static void uda1341_exit(void)

{

    platform_device_unregister(&uda1341_dev);

    platform_driver_unregister(&uda1341_drv);

}

module_init(uda1341_init);

module_exit(uda1341_exit);

MODULE_LICENSE("GPL");


2.3 实现cpu_dai驱动的框架(s3c2440_iis.c)

(参考soundsocsamsungs3c24xx-i2s.c)


1. 构造一个snd_soc_codec_driver


static const struct snd_soc_dai_ops s3c2440_i2s_dai_ops = {

//.trigger = s3c24xx_i2s_trigger, //从内核移植过来后,先暂时屏蔽掉这些函数,等用到的时候再加

// .hw_params = s3c24xx_i2s_hw_params,

// .set_fmt = s3c24xx_i2s_set_fmt,

// .set_clkdiv = s3c24xx_i2s_set_clkdiv,

// .set_sysclk = s3c24xx_i2s_set_syscl 

};


#define S3C24XX_I2S_RATES

(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 |

SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |

SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)


static struct snd_soc_dai_driver s3c2440_i2s_dai = { //没有name,其实snd_soc_register_dai()里会把s3c2440_iis_dev的name复制给dai->name

// .probe = s3c24xx_i2s_probe,  //从内核移植过来后,先暂时屏蔽掉这些函数,等用到时候再加

// .suspend = s3c24xx_i2s_suspend,

// .resume = s3c24xx_i2s_resum

.playback = {

.channels_min = 2,

.channels_max = 2,

.rates = S3C24XX_I2S_RATES,

.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},

.capture = {

.channels_min = 2,

.channels_max = 2,

.rates = S3C24XX_I2S_RATES,

.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},

.ops = &s3c2440_i2s_dai_ops,

};


2. 注册它


static int s3c2440_iis_probe(struct platform_device *pdev)

{

    return snd_soc_register_dai(&pdev->dev, &s3c2440_i2s_dai);

}

static int s3c2440_iis_remove(struct platform_device *pdev)

{

    snd_soc_unregister_dai(&pdev->dev);

    return 0;

}

static void s3c2440_iis_release(struct device * dev)

{

}

static struct platform_device s3c2440_iis_dev = {

    .name         = "s3c2440-iis", //必须和machine驱动的s3c2440_uda1341_dai_link.cpu_dai_name一致

    .id       = -1,

    .dev = {

    .release = s3c2440_iis_release,

},

};

struct platform_driver s3c2440_iis_drv = {

.probe = s3c2440_iis_probe,

.remove = s3c2440_iis_remove,

.driver = {

    .name = "s3c2440-iis", //必须和s3c2440_iis_dev的name一致

}

};

static int s3c2440_iis_init(void)

{

    platform_device_register(&s3c2440_iis_dev);

    platform_driver_register(&s3c2440_iis_drv);

    return 0;

}


static void s3c2440_iis_exit(void)

{

    struct clk *clk;

    platform_device_unregister(&s3c2440_iis_dev);

    platform_driver_unregister(&s3c2440_iis_drv);

}

static void s3c2440_iis_exit(void)

{

    platform_device_unregister(&s3c2440_iis_dev);

    platform_driver_unregister(&s3c2440_iis_drv);

}

module_init(s3c2440_iis_init);

module_exit(s3c2440_iis_exit);

MODULE_LICENSE("GPL");


2.4 实现platform驱动的框架(s3c2440_dma.c)

(参考 soundsocsamsungdma.c)


1. 构造一个snd_soc_platform_driver


static struct snd_pcm_ops dma_ops = {

// .open = dma_open, //从内核移植过来后,先暂时屏蔽掉这些函数,等用到的时候再加

// .close = dma_close,

// .ioctl = snd_pcm_lib_ioctl,

// .hw_params = dma_hw_params,

// .hw_free = dma_hw_free,

// .prepare = dma_prepare,

// .trigger = dma_trigger,

// .pointer = dma_pointer,

// .mmap = dma_mmap,

};

static struct snd_soc_platform_driver s3c2440_dma_platform = {

.ops = &s3c2440_dma_ops,

// .pcm_new = dma_new,  //从内核移植过来后,先暂时屏蔽掉这些函数,等用到的时候再加

// .pcm_free = dma_free_dma_buffer 

};


2. 注册它


static int s3c2440_dma_probe(struct platform_device *pdev)

{

    return snd_soc_register_platform(&pdev->dev, &s3c2440_dma_platform);

}

static int s3c2440_dma_remove(struct platform_device *pdev)

{

    snd_soc_unregister_platform(&pdev->dev);

    return 0;

}

static void s3c2440_dma_release(struct device * dev)

{

}

static struct platform_device s3c2440_dma_dev = {

    .name         = "s3c2440-dma",  //必须和machine驱动的s3c2440_uda1341_dai_link. platform_name一致

    .id       = -1,

    .dev = {

    .release = s3c2440_dma_release,

},

};

struct platform_driver s3c2440_dma_drv = {

.probe = s3c2440_dma_probe,

.remove = s3c2440_dma_remove,

.driver = {

    .name = "s3c2440-dma", //必须和s3c2440_dma_dev的name一致

}

};

static int s3c2440_dma_init(void)

{

    dma_regs = ioremap(DMA2_BASE_ADDR, sizeof(struct s3c_dma_regs));

    platform_device_register(&s3c2440_dma_dev);

    platform_driver_register(&s3c2440_dma_drv);

    return 0;

}

static void s3c2440_dma_exit(void)

{

    platform_device_unregister(&s3c2440_dma_dev);

    platform_driver_unregister(&s3c2440_dma_drv);

    iounmap(dma_regs);

}

module_init(s3c2440_dma_init);

module_exit(s3c2440_dma_exit);

MODULE_LICENSE("GPL");


三、参考资料


1. 韦东山 嵌入式Linux视频教程_3期项目实战之ALSA声卡:第2课第1.1_17节_ALSA声卡08_从零编写之框架


2. DroidPhone 《Linux ALSA 声卡驱动》


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>