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

二、调试过程记录

1. 编译声卡驱动,修改语法错误

2. 配置内核去掉原来的声卡驱动

    -> Device Drivers

    -> Sound card support

    -> Advanced Linux Sound Architecture

    -> ALSA for SoC audio support

        < > ASoC support for Samsung // CONFIG_SND_SOC_SAMSUNG 这项去掉勾选后,下面这项会连带自动消失

        < > SoC I2S Audio support UDA134X wired to a S3C24XX // CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X // s3c24xx_uda134x.c

3. 编译内核,然后通过nfs把新的内核下载到开发板,并启动

4. 把新的声卡驱动通过nfs拷贝到根文件系统里,然后安装

    insmod /alsa/driver/myalsa/platform/s3c2440_iis.ko

    insmod /alsa/driver/myalsa/platform/s3c2440_dma.ko

    insmod /alsa/driver/myalsa/codec/uda1341.ko

    insmod /alsa/driver/myalsa/machine/s3c2440_uda1341.ko

    mkdir /dev/snd

    cd /dev/snd/

    ln -s /dev/controlC0

    ln -s /dev/pcmC0D0p

    ln -s /dev/pcmC0D0c

    cd /

    为了方便,把以上这些语句放到prepare.sh里:


#!/bin/sh

#insmod alsa/driver/myalsa/platform/s3c2440_iis.ko

#insmod alsa/driver/myalsa/platform/s3c2440_dma.ko #这样写的话,执行时会报错:can’t insert s3c2440_iis.ko,invalid parameter c2440_iis.ko,原因待查

#insmod alsa/driver/myalsa/codec/uda1341.ko        #同上

#insmod alsa/driver/myalsa/machine/s3c2440_uda1341.ko #同上

mkdir -p /dev/snd && cd /dev/snd

cd /alsa/driver/myalsa/platform/  #得按照这种套路写才行?

insmod s3c2440_iis.ko

insmod s3c2440_dma.ko

cd /alsa/driver/myalsa/codec/

insmod uda1341.ko

#insmod wm8976.ko  #在JZ2440开发板上,codec驱动用wm8976.ko

cd /alsa/driver/myalsa/machine/

insmod s3c2440_uda1341.ko

cd /dev/snd && ln -s /dev/controlC0 && ln -s /dev/pcmC0D0p && ln -s /dev/pcmC0D0c

cd /


    执行prepare.sh,报错:

        soc-audio: coherent DMA mask is unset

        asoc: platform pcm constructor failed

        asoc: can’t create pcm 100ask_UDA1341:-12

    解决办法:

        参考内核自带的soundsocsamsungdma.c 的dma_new,在我们的s3c2440_dma.c的s3c2440_dma_new()中添加:

        if (!card->dev->dma_mask)

            card->dev->dma_mask = &dma_mask;

        if (!card->dev->coherent_dma_mask)

            card->dev->coherent_dma_mask = DMA_BIT_MASK(32);

5. aplay windows.wav后kernel oops了:

        LR指向snd_pcm_info,PC指向0

    猜测是在snd_pcm_info里调用了某个不存在的子函数,推测是在这里出错:substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info);

    在《第2课第1.1_17节_ALSA声卡05_ASoC驱动框架》里,曾分析过这个ioctl的赋值是在soc_new_pcm里:

        soc_pcm_ops->ioctl = platform->driver->ops->ioctl;

    遂查看s3c2440_dma_ops,发现确实没有ioctl,遂参考内核的soundsocsamsungdma.c,加上 .ioctl = snd_pcm_lib_ioctl,

6. 再次aplay windows.wav,又kernel oops:

        pc is at copy_from_user

        backtrace:

        snd_pcm_lib_write_transfer

        copy_from_user

   查snd_pcm_lib_write_transfer:


...

else {

char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);

if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))

    return -EFAULT;

}

    而runtime->dma_area的赋值是在:


(sound/soc/samsung/dma.c)


dma_hw_params


    snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); //substream->dma_buffer是在dma_new里分配的


        struct snd_pcm_runtime *runtime = substream->runtime;


        runtime->dma_area = bufp->area; 


    查我们的s3c2440_dma_new,发现确实没有提供这个信息给内核,遂加上:


        buf->area = playback_dma_info.virt_addr;


        buf->bytes = playback_dma_info.buf_max_size;


7. 重新编译安装驱动,再次aplay windows.wav,这次没有oops,但报:aplay: pcm_write:1939:write error: Input/output error


    aplay windows.wav &  让它后台运行


    cat /proc/interrupts


    发现中断一次都没有发生,难办了☹!


8. 用devmem2来查看相关寄存器有没有被正确的设置:


   注:我在实验中没有用二期课程中的寄存器编辑器ker_wr和regeditor,因为在编译ker_rw的时候,报了一些错误,比如:


     error: asm/arch/regs-gpio.h: No such file or directory


     error: implicit declaration of function 'class_device_create'


     原因应该是原代码是针对2.6的内核编译的,和3.4.2内核不兼容。修改了代码之后,虽然编译通过,而且也能运行,但读不出寄存器的值,原因待查。


    / # devmem2 0x4B000080

     /dev/mem opened.

     Memory mapped at address 0xb6f9a000.

     Value at address 0x4B000080 (0xb6f9a080): 0x33B00000 //读DMA2_BASE_ADDR寄存器, 结果是有值的

     / # devmem2 0x55000000

     /dev/mem opened.

     Memory mapped at address 0xb6f50000.

     Value at address 0x55000000 (0xb6f50000): 0x100  //读IISCON寄存器,结果都是0 (bit8的1是默认值,代表Left/Right channel index (Read only))


    原因是:s3c2440的IIS的时钟没有使能?(排查思路是怎样的?是凭经验么?)


    解决办法:在s3c2440_iis_init()里加入:


    clk = clk_get(NULL, "iis");


    clk_enable(clk);


    clk_put(clk);


    疑问:为什么裸板程序没有问题?


    答案:因为裸板程序开始运行时,板子的时钟都是默认使能的,而linux启动后,为了省电,默认都是关闭的!


9. 重新编译安装驱动,再次aplay windows.wav,仍然没有声音,


    cat /proc/interrupts


    中断还是没有发生


    / # devmem2 0x55000000

     /dev/mem opened.

     Memory mapped at address 0xb6fd8000.

     Value at address 0x55000000 (0xb6fd8000): 0x1A2 //读IISCON寄存器,bit0(iis_start)=0,表明iis传输没有启动

    解决办法:


    s3c2440_iis.c加入s3c2440_i2s_trigger、s3c2440_iis_start、s3c2440_iis_stop


10. 重新编译安装驱动,再次aplay windows.wav,这次终于有声音了!


image

   最后一个改进:


    虽然能播放声音,但会有周期性杂音。视频略去排查过程,直接上结论:load_dma_period()里,要把dma_regs->dcon的bit22设为1:noreload


    不知道排查思路是怎样的?


    推测是由于period_size比较小,导致音频数据被分割成了多块来传输,并且由于dcon的bit[22] =0即autoreload模式,所以每次传完一个period的数据后,dma会自动将src、dst、TC的值加载到CURR_SRC、CURR_DST、CURR_TC,并开始一次新的DMA传输(所以如果新的数据还来不及加载到CURR_SRC,则导致相当于重复播放上个period的数据)。而这之后才调用了DMA中断服务(硬件总是比软件快),进而load_dma_period()加载下一个period的数据到DMA的CURR_SRC。因此出现了播放时有周期性的杂音。


    如果把s3c2440_dma_hardware修改一下(仅用于实验),使其能在一个period里播完windows.wav,则也能消除周期性的杂音:


// .buffer_bytes_max = 128*1024,

// .period_bytes_min = PAGE_SIZE,

// .period_bytes_max = PAGE_SIZE*2,

.buffer_bytes_max = 1024*1024,

.period_bytes_min = 512*1024,

.period_bytes_max = 512*1024,

    另外,如果用内核自带的驱动,则不会出现上述问题。经初步分析,因为它利用了dcon的autoload功能,并且设计了一个比较复杂的框架,使得当一个period正在传输时,下一个period准备就绪。这样就能平滑的播放出声音。


三、参考资料


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


2. 李兰溪  S3C24XX DMA框架源码分析


3. rushzengjianmei  Alsa period_size/periods/buffer_size计算逻辑


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

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

原厂代理商合作

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

闲置物料合作

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

生态合作

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

加工与定制类服务商合作

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

线上代理合作

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

邮件营销及广告服务

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