📄 s3c24xx-iis_c.txt
字号:
else
s3c24xx_snd_txctrl(chip, 0);
s3c2410_dma_ctrl(or->dma_ch, S3C2410_DMAOP_STOP);
s3c24xx_snd_showdma(or);
s3c24xx_snd_showregs(chip);
ret = 0;
break;
default:
ret = -EINVAL;
}
exit_err:
spin_unlock_irqrestore(&or->lock, flags);
return ret;
}
/* pointer callback */
static snd_pcm_uframes_t s3c24xx_snd_pcm_pointer(struct snd_pcm_substream *substream)
{
s3c24xx_runtime_t *or = substream->runtime->private_data;
unsigned long flags;
unsigned long res;
dma_addr_t src, dst;
pr_debug("--- %s\n", __FUNCTION__);
spin_lock_irqsave(&or->lock, flags);
s3c2410_dma_getposition(or->dma_ch, &src, &dst);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
res = dst - or->dma_start;
else
res = src - or->dma_start;
spin_unlock_irqrestore(&or->lock, flags);
/* we seem to be getting the odd error from the pcm library due
* to out-of-bounds pointers. this is maybe due to the dma engine
* not having loaded the new values for the channel before being
* callled... (todo - fix )
*/
if (res > (or->dma_end - or->dma_start))
DBG("%s: %lx,%lx (%lx) from %lx,%lx\n", __FUNCTION__,
(long)src, (long)dst, res,
(long)or->dma_start, (long)or->dma_end);
if (res >= snd_pcm_lib_buffer_bytes(substream)) {
if (res > snd_pcm_lib_buffer_bytes(substream))
DBG("%s: res %lx >= %lx\n", __FUNCTION__,
res, (long)snd_pcm_lib_buffer_bytes(substream));
if (res == snd_pcm_lib_buffer_bytes(substream))
res = 0;
}
return bytes_to_frames(substream->runtime, res);
}
static int s3c24xx_snd_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
pr_debug("-*- %s\n", __FUNCTION__);
ret = dma_mmap_writecombine(substream->pcm->card->dev, vma,
runtime->dma_area,
runtime->dma_addr,
runtime->dma_bytes);
pr_debug("-*- %s: %d\n", __FUNCTION__, ret);
return ret;
}
static int sc24xx_snd_dma_prealloc(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = s3c24xx_snd_hw.buffer_bytes_max;
pr_debug("--- %s\n", __FUNCTION__);
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
buf->bytes = size;
buf->area = dma_alloc_writecombine(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
if (buf->area == NULL) {
dev_err(pcm->card->dev, "failed to allocate dma buff\n");
return -ENOMEM;
}
DBG("%s: stream %d: dma area %p, 0x%lx\n", __FUNCTION__,
stream, buf->area, (long)buf->addr);
return 0;
}
static void sc24xx_snd_dma_free(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = s3c24xx_snd_hw.buffer_bytes_max;
pr_debug("--- %s\n", __FUNCTION__);
dma_free_writecombine(pcm->card->dev, size, buf->area, buf->addr);
buf->area = NULL;
buf->addr = (dma_addr_t)NULL;
}
/* operators */
static struct snd_pcm_ops s3c24xx_snd_pcm_ops = {
.open = s3c24xx_snd_open,
.close = s3c24xx_snd_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = s3c24xx_snd_pcm_hw_params,
.hw_free = s3c24xx_snd_pcm_hw_free,
.prepare = s3c24xx_snd_pcm_prepare,
.trigger = s3c24xx_snd_pcm_trigger,
.pointer = s3c24xx_snd_pcm_pointer,
.mmap = s3c24xx_snd_pcm_mmap,
};
static int snd_card_s3c24xx_pcm(s3c24xx_card_t *card)
{
struct snd_pcm *pcm;
int err;
pr_debug("--- %s\n", __FUNCTION__);
DBG("snd_card_s3c24xx_pcm: card=%p\n", card);
err = snd_pcm_new(card->card, "S3C24XX-IIS", 0, 1, 1, &pcm);
if (err < 0) {
printk(KERN_ERR "cannot register new pcm");
return err;
}
DBG("snd_card_s3c24xx_pcm: new pcm %p\n", pcm);
card->pcm = pcm;
pcm->private_data = card;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &s3c24xx_snd_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &s3c24xx_snd_pcm_ops);
/* allocate dma resources */
err = sc24xx_snd_dma_prealloc(pcm, SNDRV_PCM_STREAM_PLAYBACK);
if (err)
goto exit_err;
err = sc24xx_snd_dma_prealloc(pcm, SNDRV_PCM_STREAM_CAPTURE);
if (err)
goto exit_err;
strcpy(pcm->name, "s3c24xx");
DBG("snd_card_s3c24xx_pcm: ok\n");
return 0;
exit_err:
return err;
}
static int s3c24xx_iis_free(s3c24xx_card_t *card)
{
pr_debug("--- %s\n", __FUNCTION__);
sc24xx_snd_dma_free(card->pcm, SNDRV_PCM_STREAM_PLAYBACK);
sc24xx_snd_dma_free(card->pcm, SNDRV_PCM_STREAM_CAPTURE);
}
static int s3c24xx_iis_dma_init(s3c24xx_card_t *card)
{
int err = 0;
pr_debug("--- %s\n", __FUNCTION__);
our_card->playback.dma_ch = DMACH_I2S_OUT;
our_card->capture.dma_ch = DMACH_I2S_IN;
err = s3c2410_dma_request(our_card->playback.dma_ch,
&s3c24xx_snd_playback_client, NULL);
if (err) {
printk(KERN_ERR "failed to get dma channel\n");
return err;
}
s3c2410_dma_devconfig(our_card->playback.dma_ch,
S3C2410_DMASRC_MEM, 0x03,
S3C2410_PA_IIS + S3C2410_IISFIFO);
s3c2410_dma_config(our_card->playback.dma_ch,
2, S3C2410_DCON_CH2_I2SSDO|S3C2410_DCON_SYNC_PCLK|S3C2410_DCON_HANDSHAKE);
s3c2410_dma_set_buffdone_fn(our_card->playback.dma_ch,
s3c24xx_snd_playback_buffdone);
/* attach capture channel */
err = s3c2410_dma_request(our_card->capture.dma_ch,
&s3c24xx_snd_capture_client, NULL);
if (err) {
printk(KERN_ERR "failed to get dma channel\n");
return err;
}
s3c2410_dma_config(our_card->capture.dma_ch,
2, S3C2410_DCON_HANDSHAKE |
S3C2410_DCON_SYNC_PCLK |
(2 << S3C2410_DCON_SRCSHIFT));
s3c2410_dma_devconfig(our_card->capture.dma_ch,
S3C2410_DMASRC_HW, 0x3,
S3C2410_PA_IIS + S3C2410_IISFIFO);
s3c2410_dma_set_buffdone_fn(our_card->capture.dma_ch,
s3c24xx_snd_capture_buffdone);
return 0;
}
static int s3c24xx_iis_dma_free(s3c24xx_card_t *card)
{
pr_debug("--- %s\n", __FUNCTION__);
s3c2410_dma_free(card->capture.dma_ch, &s3c24xx_snd_capture_client);
s3c2410_dma_free(card->playback.dma_ch, &s3c24xx_snd_playback_client);
}
int s3c24xx_iis_remove(struct device *dev)
{
s3c24xx_card_t *card = dev_get_drvdata(dev);
pr_debug("--- %s\n", __FUNCTION__);
card = dev_get_drvdata(dev);
if (card == NULL)
return 0;
s3c24xx_iis_dma_free(card);
s3c24xx_iis_free(card);
kfree(card);
dev_set_drvdata(dev, NULL);
return 0;
}
EXPORT_SYMBOL(s3c24xx_iis_remove);
#if 1
static ssize_t s3c24xx_iis_show_regs(struct device *dev, struct device_attribute *attr, char *buf)
{
s3c24xx_card_t *our_card = dev_get_drvdata(dev);
pr_debug("--- %s\n", __FUNCTION__);
return snprintf(buf, PAGE_SIZE,
"IISMOD = %08x\n"
"IISCON = %08x\n"
"IISPSR = %08x\n"
"IISFCON= %08x\n",
readl(our_card->regs + S3C2410_IISCON),
readl(our_card->regs + S3C2410_IISMOD),
readl(our_card->regs + S3C2410_IISPSR),
readl(our_card->regs + S3C2410_IISFCON));
}
static DEVICE_ATTR(regs, S_IRUGO, s3c24xx_iis_show_regs, NULL);
#endif
s3c24xx_card_t *s3c24xx_iis_probe(struct device *dev)
{
struct s3c24xx_platdata_iis *pdata = dev->platform_data;
struct snd_card *card;
int err;
pr_debug("--- %s\n", __FUNCTION__);
dev_info(dev, "probe called\n");
card = snd_card_new(-1, 0, THIS_MODULE, sizeof(s3c24xx_card_t));
our_card = (s3c24xx_card_t *)card->private_data;
if (card == NULL) {
printk(KERN_ERR "snd_card_new() failed\n");
err = -EINVAL;
goto exit_err;
}
our_card->card = card;
our_card->device = dev;
our_card->base_ops = (pdata) ? pdata->ops : NULL;
/* get resources such as clocks and IO */
our_card->clock = clk_get(dev, "iis");
DBG("our_card.clock = %p\n", our_card->clock);
if (IS_ERR(our_card->clock)) {
err = PTR_ERR(our_card->clock);
goto exit_err;
}
our_card->regs = ioremap(S3C2410_PA_IIS, 0x100);
if (our_card->regs == NULL) {
err = -ENXIO;
goto exit_err;
}
DBG("our_card.regs at %p\n", our_card->regs);
clk_enable(our_card->clock);
init_MUTEX(&our_card->sem);
spin_lock_init(&our_card->capture.lock);
spin_lock_init(&our_card->playback.lock);
/* ensure the channels are both shutdown */
s3c24xx_snd_txctrl(our_card, 0);
s3c24xx_snd_rxctrl(our_card, 0);
/* static dma channel setup for the moment */
err = s3c24xx_iis_dma_init(our_card);
if (err)
goto exit_err;
/* default names for s3c2410 audio driver */
strcpy(card->driver, "S3C24XX");
strcpy(card->shortname, "S3C24XX");
strcpy(card->longname, "Samsung S3C24XX");
err = snd_card_s3c24xx_pcm(our_card);
if (err) {
printk(KERN_ERR "failed to init pcm devices\n");
goto exit_err;
}
dev_info(dev, "soundcard attached ok (%p)\n", our_card);
dev_set_drvdata(dev, our_card);
device_create_file(dev, &dev_attr_regs);
return our_card;
exit_err:
dev_err(dev, "error initialised card (%d)\n", err);
snd_card_free(our_card->card);
return ERR_PTR(err);
}
EXPORT_SYMBOL(s3c24xx_iis_probe);
#ifdef CONFIG_PM
static int call_suspend(struct s3c24xx_iis_ops *ops)
{
pr_debug("--- %s\n", __FUNCTION__);
if (ops && ops->suspend)
return (ops->suspend)(ops);
return 0;
}
int s3c24xx_iis_suspend(struct device *dev, u32 state)
{
s3c24xx_card_t *card = dev_get_drvdata(dev);
pr_debug("--- %s\n", __FUNCTION__);
if (card) {
if (card->card->power_state == SNDRV_CTL_POWER_D3cold)
goto exit;
/* inform sound core that we wish to suspend */
snd_pcm_suspend_all(card->pcm);
snd_power_change_state(card->card, SNDRV_CTL_POWER_D3cold);
/* inform any of our clients */
call_suspend(card->chip_ops);
call_suspend(card->base_ops);
clk_disable(card->clock);
}
exit:
return 0;
}
static int call_resume(struct s3c24xx_iis_ops *ops)
{
pr_debug("--- %s\n", __FUNCTION__);
if (ops && ops->resume)
return (ops->resume)(ops);
return 0;
}
int s3c24xx_iis_resume(struct device *dev)
{
s3c24xx_card_t *card = dev_get_drvdata(dev);
pr_debug("--- %s\n", __FUNCTION__);
if (card) {
if (card->card->power_state == SNDRV_CTL_POWER_D0)
goto exit;
clk_enable(card->clock);
/* inform any of our clients */
call_resume(card->base_ops);
call_resume(card->chip_ops);
/* inform sound core that we wish to resume */
snd_power_change_state(card->card, SNDRV_CTL_POWER_D0);
}
exit:
return 0;
}
EXPORT_SYMBOL(s3c24xx_iis_suspend);
EXPORT_SYMBOL(s3c24xx_iis_resume);
#endif /* CONFIG_PM */
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("S3C4XX iis driver");
MODULE_AUTHOR("Ben Dooks");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -