📄 s3c24xx-iis.c
字号:
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 1static 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);#endifs3c24xx_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_PMstatic 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 + -