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

发布者:EtherealBeauty最新更新时间:2024-07-11 来源: elecfans关键字:FS2410开发板 手机看文章 扫描二维码
随时随地手机看文章

一、实验环境


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 声卡驱动


关键字:FS2410开发板 引用地址:ALSA声卡_从零编写之框架(基于优龙FS2410开发板,UDA1341声卡)

上一篇:ALSA声卡_从零编写之参数设置(基于优龙FS2410开发板,UDA1341声卡)
下一篇:ALSA声卡_裸板之编译和测试(基于优龙FS2410开发板)

推荐阅读最新更新时间:2024-11-03 03:08

车载操作系统:SDV的框架
与特斯拉、小鹏、小米一众新势力一开始就走电动化和智能化的路线不同,传统车企的组织架构发源于传统燃油车2.0时代,除了组织架构上的问题之外最重要的是人才类型的失衡,SDV的框架对于软件人才的需求将打破传统车企的人才梯队,不同的人才结构对于企业文化以及项目流程体系都将是一个重大的冲击。 智能车从基于功能集成的工业产品转型为软件定义的移动智能终端,单车软件价值大幅上升,车型差异更多由软件体现,随着整车电子电气架构逐步走向中央集成,主机厂的新一代中高端产品具有更先进的电子电气架构和相对更高的价格,可为这些科技公司的产品提供合适的落地“土壤”,软硬件开发的解耦将使得整车软件研发可以独立进行。 软件研发的项目体系需要完全不同于工业生产的组织形
[嵌入式]
车载操作系统:SDV的<font color='red'>框架</font>
引线框架市场规模进一步增长!ASM和智路资本合作建立合资
7月28日,ASM PACIFIC(00522-HK)于港交所发布公告称,与融信产业联盟核心成员智路资本牵头的财团共同投资建立高科技合资企业,其中由智路资本控股。该合资公司将专注于为存储器、模拟芯片、微控制器和汽车芯片等提供引线框架。 ASM向智路资本有限公司及Asia-IO Capital Management Limited发行目标股份,认购金额为2亿美元。该交易完成时,目标股份将占目标公司扩大后已发行股份总数的55.56%。 据集微网了解,ASM公司的引线框架事业部在全球引线框架领域排名前三,该事业部具有先进的制造技术与长远的产品路线规划。ASM引线框架业务连续20年盈利,过去两年平均年营收超过2.5亿美元。同时,该事业部
[手机便携]
[单片机框架][DFU] Dfu升级例子 带crc校验+超时机制+led指示灯+芯片加锁+芯片自擦
自动检测剩余空间是否支持备份升级,防止升级失败变砖。 /******************************************************************************** * @file main.c * @author jianqiang.xue * @Version V1.0.0 * @Date 2021-04-03 * @brief NULL ********************************************************************************/ /* Includes -------------------
[单片机]
[单片机框架][bsp层][esp32s3][bsp_pwm] PWM的使用
LED PWM 控制器 概述 LED 控制器 (LEDC) 主要用于控制 LED,也可产生 PWM 信号用于其他设备的控制。 该控制器有 8 路通道,可以产生独立的波形来驱动 RGB LED 等设备。 LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实现亮度和颜色渐变。 功能概览 设置 LEDC 通道分三步完成。注意,与 ESP32 不同,ESP32-S3 仅支持设置通道为低速模式。 定时器配置 指定 PWM 信号的频率和占空比分辨率。 通道配置 绑定定时器和输出 PWM 信号的 GPIO。 改变 PWM 信号 输出 PWM 信号来驱动 LED。可通过软件控制或使用硬件渐变功能来改变 LED
[单片机]
[单片机<font color='red'>框架</font>][bsp层][esp32s3][bsp_pwm] PWM的使用
高手谈单片机裸奔的程序框架
从07年参加全国大学生电子设计大赛初次接触单片机开发至今已经有4年了,初学单片机时,都会纠结于其各个模块功能的应用,如串口(232,485)对各种功能IC的控制,电机控制PWM,中断应用,定时器应用,人机界面应用,CAN总线等. 这是一个学习过程中必需的阶段,是基本功。很庆幸,在参加电子设计大赛赛前培训时,MCU周围的控制都训练的很扎实。经过这个阶段后,后来接触不同的MCU就会发现,都大同小异,各有各的优势而已,学任何一种新的MCU都很容易入手包括一些复杂的处理器。而且对MCU的编程控制会提升一个高度概况—— 就是对各种外围进行控制(如果是对复杂算法的运算就会 用DSP了),而外围与MCU的通信方式一般也就几种时序:IIC,SP
[单片机]
[单片机框架][bsp层][nrf51822][nrf51422][nrf51802][bsp_uart] UART配置和使用
Universal Asynchronous Receiver/Transmitter (UART) The Universal Asynchronous Receiver/Transmitter offers fast, full-duplex, asynchronous serial communication with built-in flow control (CTS, RTS) support in hardware up to 1 Mbps baud. Parity checking is supported. The GPIOs used for each UART interface line can be
[单片机]
mini2440 ADC可调电阻驱动程序开发源代码(杂项设备驱动框架)
/*********************************************************/ /****s3c2440 ADC可调电阻驱动程序开发源代码(杂项设备驱动框架)****/ /********************************************************/ #include #include #include #define DEVICE_NAME adc_driver /*设备名称*/ static void __iomem *adc_base; /*定义了一个用来保存经过虚拟映射后的内存地址 */ static struct clk *adc_cl
[单片机]
[单片机框架][bsp层][cx32l003][bsp_key] KEY配置和使用
按键的基本原理是设置单片机IO口(PB0-PB3)为输入状态,如DDRB = 0XF0(方向寄存器,“1”为输出,“0”为输入); 单片机一直检测按键端口(PB0-PB3)的状态,当端口为低电平时(即按键按下),实行相应的动作(比如控制LED灯)。 原理就是这么回事,但是正真实现时,按键会有抖动,要进行按键去抖,下图为按键按下时的抖动图。 按键实行一个动作过程是需要一定时间的,一般为100mS-1S左右,而一个单片机执行一个机器周期的时间很短,时钟为10MH的周期为0.1μs,这样按键每一次动作程序就会多次检测按键,出现误判(一次按下,多次动作)。 /********************************
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved