zynq 音乐播放流程

在《zynq audio pcm DMA 》里提到了snd_pcm_writei()这个函数,这个函数是alsa lib里的接口,其实现如下:

snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
	assert(pcm);
	assert(size == 0 || buffer);
	if (CHECK_SANITY(! pcm->setup)) {
		SNDMSG("PCM not set up");
		return -EIO;
	}
	if (pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED) {
		SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
		return -EINVAL;
	}
	return _snd_pcm_writei(pcm, buffer, size);
}
该函数调用了_snd_pcm_writei去实现具体的任务。

static inline snd_pcm_sframes_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
	return pcm->fast_ops->writei(pcm->fast_op_arg, buffer, size);
}
而_snd_pcm_writei实际上调用了一个函数指针操作集去实现写操作。该writei函数实际上是alsa库里的snd_pcm_hw_writei()。

static snd_pcm_sframes_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
	int err;
	snd_pcm_hw_t *hw = pcm->private_data;
	int fd = hw->fd;
	struct snd_xferi xferi;
	xferi.buf = (char*) buffer;
	xferi.frames = size;
	xferi.result = 0; /* make valgrind happy */
	err = ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi);
	err = err >= 0 ? sync_ptr(hw, SNDRV_PCM_SYNC_PTR_APPL) : -errno;
#ifdef DEBUG_RW
	fprintf(stderr, "hw_writei: frames = %li, xferi.result = %li, err = %i\n", size, xferi.result, err);
#endif
	if (err < 0)
		return snd_pcm_check_error(pcm, err);
	return xferi.result;
}
这个函数实际上是调用了ioctl函数完成了相关的操作,实际上alsa lib是对pcm操作的封装,简化了alsa的用户空间编程。
ioctl系统调用是在linux 内核头文件syscalls.h里声明的。

<4>[<c03a9a2c>] (snd_pcm_playback_ioctl1) from [<c00d7af0>] (do_vfs_ioctl+0x564/0x688)
<4>[<c00d7af0>] (do_vfs_ioctl) from [<c00d7c48>] (SyS_ioctl+0x34/0x5c)
<4>[<c00d7c48>] (SyS_ioctl) from [<c000edc0>] (ret_fast_syscall+0x0/0x3c)
为什么对应到linux kernel对应里是snd_pcm_writei?这在注册时就确定的。

snd_soc_register_card-->snd_card_register--->snd_device_register_all-->__snd_device_register.part.0-->
snd_pcm_dev_register--->snd_pcm_dev_register()
{
		err = snd_register_device(devtype, pcm->card, pcm->device,
					  &snd_pcm_f_ops[cidx], pcm,
					  &pcm->streams[cidx].dev);
}
.unlocked_ioctl =	snd_pcm_playback_ioctl,
snd_pcm_playback_ioctl--->snd_pcm_playback_ioctl1
由于用户空间传递的ioctl命令是SNDRV_PCM_IOCTL_WRITEI_FRAMES,则执行相应的case语句。播放调用流程如下:

snd_pcm_playback_ioctl1-->snd_pcm_common_ioctl1-->snd_pcm_action_lock_irq-->snd_pcm_action-->snd_pcm_action_single-->snd_pcm_do_start-->soc_pcm_trigger

2807 static int snd_pcm_playback_ioctl1(struct file *file,
2808                    struct snd_pcm_substream *substream,
2809                    unsigned int cmd, void __user *arg)
2810 {
2811     if (snd_BUG_ON(!substream))
2812         return -ENXIO;
2813     if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
2814         return -EINVAL;
2815 
2816     switch (cmd) {
2817     case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
2818     {
2819         struct snd_xferi xferi;
2820         struct snd_xferi __user *_xferi = arg;
2821         struct snd_pcm_runtime *runtime = substream->runtime;
2822         snd_pcm_sframes_t result;
2823         if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
2824             return -EBADFD;
2825         if (put_user(0, &_xferi->result))
2826             return -EFAULT;
2827         if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
2828             return -EFAULT;
2829         result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
2830         __put_user(result, &_xferi->result);
2831         return result < 0 ? result : 0;
2832     }
这个函数调用的write,最终调用

<sound/core/pcm_lib.c>实现

static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
    struct snd_soc_pcm_runtime *rtd = substream->private_data;
    struct snd_soc_platform *platform = rtd->platform;
    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
    struct snd_soc_dai *codec_dai;
    int i, ret;

    for (i = 0; i < rtd->num_codecs; i++) {
        codec_dai = rtd->codec_dais[i];
        if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) {
            ret = codec_dai->driver->ops->trigger(substream,
                                  cmd, codec_dai);
            if (ret < 0)
                return ret;
        }
    }

    if (platform->driver->ops && platform->driver->ops->trigger) {
        ret = platform->driver->ops->trigger(substream, cmd);
        if (ret < 0)
            return ret;
    }

    if (cpu_dai->driver->ops && cpu_dai->driver->ops->trigger) {
        ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
        if (ret < 0)
            return ret;
    }

    if (rtd->dai_link->ops && rtd->dai_link->ops->trigger) {
        ret = rtd->dai_link->ops->trigger(substream, cmd);
        if (ret < 0)
            return ret;
    }

    return 0;
}
这里调用了四个triger函数,每一个函数对应一个trigger。platform对应的trigger函数是snd_dmaengine_pcm_trigger(), cpu侧的trigger函数是axi_i2s_trigger()。而codec则没有实现trigger函数。axi-i2s_trigger()用于设置cpu侧dai的使能。真正和DMA相关的操作在

<sound/core/pcm_dmaengine.c>

184 int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
185 {
186     struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
187     struct snd_pcm_runtime *runtime = substream->runtime;
188     int ret;
189 
190     switch (cmd) {
191     case SNDRV_PCM_TRIGGER_START:
192         ret = dmaengine_pcm_prepare_and_submit(substream);
193         if (ret)
194             return ret;
195         dma_async_issue_pending(prtd->dma_chan);
196         break;
197     case SNDRV_PCM_TRIGGER_RESUME:
198     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
199         dmaengine_resume(prtd->dma_chan);
200         break;
201     case SNDRV_PCM_TRIGGER_SUSPEND:
202         if (runtime->info & SNDRV_PCM_INFO_PAUSE)
203             dmaengine_pause(prtd->dma_chan);
204         else
205             dmaengine_terminate_all(prtd->dma_chan);
206         break;
207     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
208         dmaengine_pause(prtd->dma_chan);
209         break;
210     case SNDRV_PCM_TRIGGER_STOP:
211         dmaengine_terminate_all(prtd->dma_chan);
212         break;
213     default:
214         return -EINVAL;
215     }
216 
217     return 0;
218 }
219 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
这个函数调用dmaengine_pcm_prepare_and_submit()进行发送操作。

145 static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
146 {
147     struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
148     struct dma_chan *chan = prtd->dma_chan;
149     struct dma_async_tx_descriptor *desc;
150     enum dma_transfer_direction direction;
151     unsigned long flags = DMA_CTRL_ACK;
152 
153     direction = snd_pcm_substream_to_dma_direction(substream);
154 
155     if (!substream->runtime->no_period_wakeup)
156         flags |= DMA_PREP_INTERRUPT;
157 
158     prtd->pos = 0;
159     desc = dmaengine_prep_dma_cyclic(chan,
160         substream->runtime->dma_addr,
161         snd_pcm_lib_buffer_bytes(substream),
162         snd_pcm_lib_period_bytes(substream), direction, flags);
163 
164     if (!desc)
165         return -ENOMEM;
166 
167     desc->callback = dmaengine_pcm_dma_complete;
168     desc->callback_param = substream;
169     prtd->cookie = dmaengine_submit(desc);
170 
171     return 0;
172 }
上面这个函数159调用pl330_prep_dma_cyclic()函数,dmaengine_submit()调用pl330_tx_submit()函数添加发送描述符。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
csdn zynq7000开发流程包括以下几个步骤: 1. 硬件设计:确定项目需求并设计系统架构,选择Zynq7000系列芯片作为硬件平台,设计硬件电路和原理图。包括选择和配置处理系统(PS)和可编程逻辑(PL)部分、板级设计和PCB布局等。 2. Vivado工程:使用Xilinx Vivado工具创建一个新工程,设置约束条件,并进行整个设计的综合、实现和比特流。 3. PS配置:对处理系统(PS)进行配置,包括设计PS部分的参数、选择启动器和外设以及配置处理器等。这个过程可以使用Xilinx SDK工具进行。 4. PL设计:使用HDL语言(如VHDL或Verilog)进行可编程逻辑(PL)的设计。设计包括FPGA逻辑、IP核的选择、配置和连接、约束和验证等。 5. 代码编写:使用Xilinx SDK工具开发嵌入式软件。这一步骤包括写C/C++代码、编译、链接等,以控制硬件逻辑、实现算法等功能。 6. 运行和调试:将代码烧录到Zynq7000平台上,通过调试工具(如JTAG)进行程序调试和性能优化。 7. 系统集成:在硬件和软件开发完成后,将软件和硬件进行集成测试。通过使用各种测试工具和算法验证系统的功能和性能。 8. 项目验证和优化:进行系统的整体性能测试,并重新优化系统设计,以满足项目需求和性能要求。 总之,csdn zynq7000开发流程涵盖了硬件设计、Vivado工程、PS配置、PL设计、代码编写、运行调试、系统集成、项目验证和优化等多个环节,通过这些步骤可以完成一个完整的Zynq7000开发项目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shichaog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值