📄 omap2-audio.c
字号:
/* * sound/arm/omap/omap2-audio.c * * Common ALSA audio handling for the OMAP processors * * Copyright (C) 2007 Texas Instruments. * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. * * Based on omap linux open source community alsa driver * sound/arm/omap/omap-alsa.c * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi> * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil * Based on sa11xx-uda1341.c, * Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz> * */// #include <linux/config.h>#include <linux/autoconf.h>#include <linux/module.h>#include <linux/init.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/poll.h>#include <linux/pm.h>#include <linux/errno.h>#include <linux/sysrq.h>#include <linux/delay.h>#include <linux/device.h>#include <linux/dma-mapping.h>#include <sound/driver.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/initval.h>#include <sound/control.h>#include <linux/clk.h>#include <linux/platform_device.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/hardware.h>// #include <asm/arch/bus.h>#include <asm/arch/clock.h>#include "omap2-audio_if.h"/* * Buffer management for alsa and dma */struct omap_alsa_stream { int stream_id; /* numeric identification */ int active:1; /* we are using this stream for transfer now */ int period; /* current transfer period */ int periods; /* current count of periods registerd in the DMA engine */ spinlock_t dma_lock; /* for locking in DMA operations */ int offset; /* store start position of the last period in the alsa buffer */ struct snd_pcm_substream *stream; /* the pcm stream */};struct omap_alsa_state { struct snd_card *card; struct snd_pcm *pcm; long samplerate; struct omap_alsa_stream s[2]; /* playback & capture */ struct omap_alsa_codec *codec;};/***************************** MACROS ************************************//* Change to define if need be */#undef DEBUG#define DPRINTK( x... )#define FN_IN#define FN_OUT(x)#define OMAP_AUDIO_NAME "omap-audio"#define DMA_NB_FRAGS 16#define DMA_FRAG_SIZE (1024 * 8)static int audio_probe(struct platform_device *dev);static int audio_remove(struct platform_device *dev);static void audio_release(struct device *dev);static int audio_suspend(struct platform_device *dev, pm_message_t state);static int audio_resume(struct platform_device *dev);static struct platform_driver omap_audio_driver = { .probe = audio_probe, .remove = audio_remove, .suspend = audio_suspend, .resume = audio_resume, .driver = { .name = OMAP_AUDIO_NAME, },};static struct platform_device omap_audio_device = { .name = OMAP_AUDIO_NAME, // .id = OMAP24xx_AUDIO_DEVID, .id = 7, .dev = { /* we may add later */ .release = audio_release, }};struct omap_alsa_state *omap_audio_state;// static int dpm_state;pm_message_t dpm_state;/** * @brief audio_suspend - Function to handle suspend operations * * @param dev * @param state * * @return */static int audio_suspend(struct platform_device *dev, pm_message_t state){ int ret = 0; struct omap_alsa_state *chip; struct snd_card *card = platform_get_drvdata(dev); struct omap_alsa_codec *codec; dpm_state = state; chip = card->private_data; codec = chip->codec; if (card->power_state != SNDRV_CTL_POWER_D3) { if (chip->card->power_state != SNDRV_CTL_POWER_D3) { snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3); snd_pcm_suspend_all(chip->pcm); if (omap_audio_state->s[SNDRV_PCM_STREAM_PLAYBACK].active) { ret = codec->codec_transfer_stop(SNDRV_PCM_STREAM_PLAYBACK); codec->codec_shutdown(); } if (omap_audio_state->s[SNDRV_PCM_STREAM_CAPTURE].active) { ret = codec->codec_transfer_stop(SNDRV_PCM_STREAM_CAPTURE); codec->codec_shutdown(); } } } return ret;}/** * @brief audio_resume - Function to handle resume operations * * @param dev * * @return */static int audio_resume(struct platform_device *dev){ int ret = 0; struct omap_alsa_state *chip; struct snd_card *card = platform_get_drvdata(dev); struct omap_alsa_codec *codec; chip = card->private_data; codec = chip->codec; if (card->power_state != SNDRV_CTL_POWER_D0) { if (chip->card->power_state != SNDRV_CTL_POWER_D0) { if (omap_audio_state->s[SNDRV_PCM_STREAM_PLAYBACK].active) { codec->codec_init(); ret = codec->codec_transfer_init(SNDRV_PCM_STREAM_PLAYBACK); } if (omap_audio_state->s[SNDRV_PCM_STREAM_CAPTURE].active) { codec->codec_init(); ret = codec->codec_transfer_init(SNDRV_PCM_STREAM_CAPTURE); } snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); } } return ret; }// static u_int audio_get_dma_pos(struct omap_alsa_stream *s)static snd_pcm_uframes_t audio_get_dma_pos(struct omap_alsa_stream *s){ struct snd_pcm_substream *substream = s->stream; struct snd_pcm_runtime *runtime = substream->runtime; struct omap_alsa_state *state = snd_pcm_substream_chip(substream); struct omap_alsa_codec *codec = state->codec; // unsigned int offset; snd_pcm_uframes_t offset; unsigned long flags; int ret = 0; FN_IN; if (unlikely(!s)) { printk(KERN_ERR "Stream IS NULL!!\n"); return -EPERM; } /* we always ask only one frame to transmit/recieve, * variant is the element num */ /* this must be called w/ interrupts locked as requested in dma.c */ spin_lock_irqsave(&s->dma_lock, flags); ret = codec->codec_transfer_posn(s->stream_id); spin_unlock_irqrestore(&s->dma_lock, flags); /* Now, the position related to the end of that period */ offset = bytes_to_frames(runtime, s->offset) - bytes_to_frames(runtime, ret); if (offset >= runtime->buffer_size) offset = 0; if (ret < 0) { printk(KERN_ERR "codec_transfer_posn: Unable to find index of " "transfer\n"); } return offset;}/* * Main dma routine, requests dma according where you are in main alsa buffer */static void audio_process_dma(struct omap_alsa_stream *s){ struct snd_pcm_substream *substream = s->stream; struct snd_pcm_runtime *runtime; struct omap_alsa_state *state = snd_pcm_substream_chip(substream); struct omap_alsa_codec *codec = state->codec; unsigned int dma_size; unsigned int offset; int ret; FN_IN; runtime = substream->runtime; if (s->active) { dma_size = frames_to_bytes(runtime, runtime->period_size); offset = dma_size * s->period; snd_assert(dma_size <= DMA_FRAG_SIZE,); ret = codec->codec_transfer_start(s->stream_id, (void *) (runtime->dma_addr) + offset, dma_size, s); if (ret) { printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i)\n", ret); return; } s->period++; s->period %= runtime->periods; s->periods++; s->offset = offset; }}/* * This is called when dma IRQ occurs at the end of each transmited block */void audio_period_handler(void *arg){ struct omap_alsa_stream *s = arg; FN_IN; if (s->active) snd_pcm_period_elapsed(s->stream); spin_lock(&s->dma_lock); if (s->periods > 0) s->periods--; audio_process_dma(s); spin_unlock(&s->dma_lock);}/* * Alsa section * PCM settings and callbacks */static int snd_omap_alsa_trigger(struct snd_pcm_substream *substream, int cmd){ int stream_id = substream->pstr->stream; struct omap_alsa_state *state = snd_pcm_substream_chip(substream); struct omap_alsa_codec *codec = state->codec; struct omap_alsa_stream *s = &state->s[stream_id]; int err = 0; unsigned long flags; FN_IN; /* note local interrupts are already disabled in the midlevel code */ spin_lock(&s->dma_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: /* requested stream startup */ s->active = 1; audio_process_dma(s); /* hnagalla -- 04/02/07 -- queue one more to get rid of poping noise */ audio_process_dma(s); break; case SNDRV_PCM_TRIGGER_STOP: /* requested stream shutdown */ spin_lock_irqsave(&s->dma_lock, flags); s->active = 0; s->period = 0; s->periods = 0; /* TODO: need return ret code? */ codec->codec_transfer_stop(s->stream_id); spin_unlock_irqrestore(&s->dma_lock, flags); break; default: err = -EINVAL; break; } spin_unlock(&s->dma_lock); return err;}static int snd_omap_alsa_prepare(struct snd_pcm_substream * substream){ struct omap_alsa_state *state = snd_pcm_substream_chip(substream); struct omap_alsa_codec *codec = state->codec; struct snd_pcm_runtime *runtime = substream->runtime; struct omap_alsa_stream *s = &state->s[substream->pstr->stream]; FN_IN; /* set requested samplerate */ codec->codec_set_samplerate(runtime->rate); state->samplerate = runtime->rate; if (runtime->channels == 1) { /* Set Mono_mode, with DSP option */ codec->codec_set_stereomode(0x02, 0x01); } else { /* Set Stereo_mode, with DSP option */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -