📄 s3c24xx-iis.c
字号:
/* sound/arm/s3c24xx-iis.c * * (c) 2004-2005 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks <ben@simtec.co.uk> * * S3C24XX ALSA IIS audio driver core * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation.*//* Notes: * The I2S pins that are needed (SCLK,LRCLK,SDO,SDI) are always * configured when the I2S system is active. SDO and SDI are * always configured even if the system is not open for both * capture and playback. This ensures that if the DAC is * not muted, then the output line is held in place, reducing * the chance of any random data being picked up. CDCLK is * only selected when the system is in master mode. * * When the I2S unit is not being used, all pins are set to * input, so either the internal resistors need to be configured * or the lines need to have external pull-up or pull-downs.*///#define DEBUG 1#include "debug.h"#include <linux/autoconf.h>#include <sound/driver.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/ioctl.h>#include <linux/delay.h>#include <linux/device.h>#include <linux/pm.h>#include <linux/dma-mapping.h>#include <linux/clk.h>#include <asm/hardware.h>#include <asm/dma.h>#include <asm/io.h>#include <sound/driver.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/initval.h>#include <asm/arch/regs-iis.h>#include <asm/arch/regs-gpio.h>#include <asm/arch/audio.h>#include "s3c24xx-iis.h"/* debugging macros */#ifdef DEBUG#undef pr_debug#define pr_debug(fmt,arg...) printk(fmt,##arg)#endif#ifndef DEBUG #define DBG(x...)#else#define DBG(x...) do { printk(x); } while(0)#endif#ifndef DEBUG#define DBG_DMA(x...)#else#define DBG_DMA(x...) do { printk(x); } while(0)#endifstatic s3c24xx_card_t *our_card;/* s3c24xx_snd_capture_hw * * the default initialiser for the capture hardware stream, * not to be altered*/static struct snd_pcm_hardware s3c24xx_snd_hw = { .info = ( SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME ),#if 1 /* only this is supported by our codec */ .formats = SNDRV_PCM_FMTBIT_S16_LE,#else .formats = ( SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 ),#endif .rates = SNDRV_PCM_RATE_8000_44100, // | SNDRV_PCM_RATE_KNOT, .rate_min = 8000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 64*1024, .period_bytes_min = 4, .period_bytes_max = PAGE_SIZE, .periods_min = 2, .periods_max = 128, .fifo_size = 32,};static struct s3c2410_dma_client s3c24xx_snd_playback_client = { .name = "S3C24XX Audio Playback",};static struct s3c2410_dma_client s3c24xx_snd_capture_client = { .name = "S3C24XX Audio Capture",};/* debug helpers */static void s3c24xx_snd_showregs(s3c24xx_card_t *card){ pr_debug("--- %s\n", __FUNCTION__);#ifdef DEBUG printk(KERN_DEBUG "IIS: CON=%08x, MOD=%08x, PSR=%08x, FCON=%08x\n", readl(card->regs + S3C2410_IISCON), readl(card->regs + S3C2410_IISMOD), readl(card->regs + S3C2410_IISPSR), readl(card->regs + S3C2410_IISFCON));#endif}static void s3c24xx_snd_showdma(s3c24xx_runtime_t *or){ dma_addr_t src, dst; pr_debug("--- %s\n", __FUNCTION__); s3c2410_dma_getposition(or->dma_ch, &src, &dst); DBG("dma position: src=0x%lx, dst=0x%lx\n", (long)src, (long)dst);}/* conversion */static struct s3c24xx_platdata_iis *to_platdata(s3c24xx_card_t *card){ pr_debug("--- %s\n", __FUNCTION__); return (card->device == NULL) ? NULL : card->device->platform_data;}/* helpers */static inline int s3c24xx_snd_is_clkmaster(s3c24xx_card_t *chip){ pr_debug("--- %s\n", __FUNCTION__); return (readl(chip->regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;}/* audio hardware control */static void s3c24xx_snd_txctrl(s3c24xx_card_t *card, int on){ unsigned long iisfcon; unsigned long iiscon; unsigned long iismod; pr_debug("--- %s\n", __FUNCTION__); iisfcon = readl(card->regs + S3C2410_IISFCON); iiscon = readl(card->regs + S3C2410_IISCON); iismod = readl(card->regs + S3C2410_IISMOD); if (on) { iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE; iiscon |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN; iiscon &= ~S3C2410_IISCON_TXIDLE; iismod |= S3C2410_IISMOD_TXMODE; iismod |= S3C2410_IISMOD_RXMODE; if (s3c24xx_snd_is_clkmaster(card)) iiscon |= S3C2410_IISCON_PSCEN; else iiscon &= ~S3C2410_IISCON_PSCEN; writel(iismod, card->regs + S3C2410_IISMOD); writel(iisfcon, card->regs + S3C2410_IISFCON); writel(iiscon, card->regs + S3C2410_IISCON); } else { /* note, we have to disable the FIFOs otherwise bad things * seem to happen when the DMA stops. According to the * Samsung supplied kernel, this should allow the DMA * engine and FIFOs to reset. If this isn't allowed, the * DMA engine will simply freeze randomly. */ iisfcon &= ~S3C2410_IISFCON_TXENABLE; iisfcon &= ~S3C2410_IISFCON_TXDMA; iiscon |= S3C2410_IISCON_TXIDLE; iiscon &= ~S3C2410_IISCON_TXDMAEN; //iismod &= ~S3C2410_IISMOD_TXMODE; if (s3c24xx_snd_is_clkmaster(card)) iiscon |= S3C2410_IISCON_PSCEN; else iiscon &= ~S3C2410_IISCON_PSCEN; writel(iiscon, card->regs + S3C2410_IISCON); writel(iisfcon, card->regs + S3C2410_IISFCON); //writel(iismod, card->regs + S3C2410_IISMOD); } DBG("%s: iismod=0x%08lx, iisfcon=0x%08lx, iiscon=0x%08lx\n", __FUNCTION__, iismod, iisfcon, iiscon);}static void s3c24xx_snd_rxctrl(s3c24xx_card_t *card, int on){ unsigned long iisfcon; unsigned long iiscon; unsigned long iismod; pr_debug("--- %s\n", __FUNCTION__); iisfcon = readl(card->regs + S3C2410_IISFCON); iiscon = readl(card->regs + S3C2410_IISCON); iismod = readl(card->regs + S3C2410_IISMOD); if (on) { iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE; iiscon |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN| (s3c24xx_snd_is_clkmaster(card)?S3C2410_IISCON_PSCEN:0); iiscon &= (~S3C2410_IISCON_RXIDLE); iismod |= S3C2410_IISMOD_RXMODE; iismod |= S3C2410_IISMOD_TXMODE; writel(iismod, card->regs + S3C2410_IISMOD); writel(iisfcon, card->regs + S3C2410_IISFCON); writel(iiscon, card->regs + S3C2410_IISCON); } else { /* note, we have to disable the FIFOs otherwise bad things * seem to happen when the DMA stops. According to the * Samsung supplied kernel, this should allow the DMA * engine and FIFOs to reset. If this isn't allowed, the * DMA engine will simply freeze randomly. */ iisfcon &= ~S3C2410_IISFCON_RXENABLE; iisfcon &= ~S3C2410_IISFCON_RXDMA; iiscon |= S3C2410_IISCON_RXIDLE; iiscon &= ~S3C2410_IISCON_RXDMAEN; //iismod &= ~S3C2410_IISMOD_RXMODE; writel(iisfcon, card->regs + S3C2410_IISFCON); writel(iiscon, card->regs + S3C2410_IISCON); //writel(iismod, card->regs + S3C2410_IISMOD); } DBG("%s: iismod=0x%08lx, iisfcon=0x%08lx, iiscon=0x%08lx\n", __FUNCTION__, iismod, iisfcon, iiscon);}int s3c24xx_iis_cfgclksrc(s3c24xx_card_t *card, const char *name){ struct s3c24xx_platdata_iis *pdata = to_platdata(card); unsigned long iismod; pr_debug("--- %s\n", __FUNCTION__); if (name == NULL && pdata != NULL) name = pdata->codec_clk; if (name == NULL) return -EINVAL; DBG("%s: clock source %s\n", __FUNCTION__, name); /* alter clock source (master/slave) appropriately */ iismod = readl(card->regs + S3C2410_IISMOD); if (strcmp(name, "pclk") == 0 || strcmp(name, "cdclk") == 0) { iismod &= ~S3C2410_IISMOD_SLAVE; } else { iismod |= S3C2410_IISMOD_SLAVE; } writel(iismod, card->regs + S3C2410_IISMOD); return 0;}EXPORT_SYMBOL(s3c24xx_iis_cfgclksrc);/* s3c24xx_pcm_enqueue * * place a dma buffer onto the queue for the dma system * to handle.*/static void s3c24xx_pcm_enqueue(s3c24xx_runtime_t *or){ dma_addr_t pos = or->dma_pos; int ret; pr_debug("--- %s\n", __FUNCTION__); DBG_DMA("%s: pos=%lx, period=%x, end=%lx, loaded=%d\n", __FUNCTION__, (long)or->dma_start, or->dma_period, (long)or->dma_end, or->dma_loaded); do { DBG_DMA("%s: load buffer %lx\n", __FUNCTION__, (long)pos); ret = s3c2410_dma_enqueue(or->dma_ch, or, pos, or->dma_period); if (ret == 0) { or->dma_loaded++; pos += or->dma_period; if (pos >= or->dma_end) pos = or->dma_start; } } while (ret == 0 && or->dma_loaded < or->dma_limit); or->dma_pos = pos;}static void s3c24xx_snd_playback_buffdone(struct s3c2410_dma_chan *ch, void *bufid, int size, enum s3c2410_dma_buffresult result){ s3c24xx_runtime_t *or = bufid; pr_debug("--- %s\n", __FUNCTION__); DBG_DMA("%s: %p,%p, or %p, sz %d, res %d\n", __FUNCTION__, ch, bufid, or, size, result); if (or->stream) snd_pcm_period_elapsed(or->stream); spin_lock(&or->lock); if (or->state & ST_RUNNING) { or->dma_loaded--; s3c24xx_pcm_enqueue(or); } spin_unlock(&or->lock);}static void s3c24xx_snd_capture_buffdone(struct s3c2410_dma_chan *ch, void *bufid, int size, enum s3c2410_dma_buffresult result){ s3c24xx_runtime_t *or = bufid; pr_debug("--- %s\n", __FUNCTION__); DBG_DMA("%s: %p,%p, or %p, sz %d, res %d\n", __FUNCTION__, ch, bufid, or, size, result); if (or->stream) snd_pcm_period_elapsed(or->stream); spin_lock(&or->lock); if (or->state & ST_RUNNING) { or->dma_loaded--; s3c24xx_pcm_enqueue(or); } spin_unlock(&or->lock);}static int call_startup(struct s3c24xx_iis_ops *ops){ pr_debug("--- %s\n", __FUNCTION__); if (ops && ops->startup) return (ops->startup)(ops); return 0;}static int call_open(struct s3c24xx_iis_ops *ops, struct snd_pcm_substream *substream){ pr_debug("--- %s\n", __FUNCTION__); if (ops && ops->open) return (ops->open)(ops, substream); return 0;}static int s3c24xx_snd_open(struct snd_pcm_substream *substream){ s3c24xx_card_t *chip = snd_pcm_substream_chip(substream); s3c24xx_runtime_t *or; struct snd_pcm_runtime *runtime = substream->runtime; int ret = 0; pr_debug("--- %s\n", __FUNCTION__); DBG("%s: substream=%p, chip=%p\n", __FUNCTION__, substream, chip); runtime->private_data = &chip->playback; runtime->hw = s3c24xx_snd_hw; down(&chip->sem); /* todo - request dma channel nicely */ DBG("%s: state: playback 0x%x, capture 0x%x\n", __FUNCTION__, chip->playback.state, chip->capture.state); if (chip->playback.state == 0 && chip->capture.state == 0) { /* ensure we hold all the modules we need */ if (chip->base_ops) { if (try_module_get(chip->base_ops->owner)) chip->base_ops_claimed = 1; else { dev_err(chip->device, "cannot claim module\n"); ret = -EINVAL; goto exit_err; } } if (chip->chip_ops) { if (try_module_get(chip->chip_ops->owner)) chip->chip_ops_claimed = 1; else { dev_err(chip->device, "cannot claim module\n"); ret = -EINVAL; goto exit_err; } } /* ensure the chip is started */ ret = call_startup(chip->base_ops); if (ret) goto exit_err; ret = call_startup(chip->chip_ops);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -