📄 s3c24xx-iis_c.txt
字号:
if (ret)
goto exit_err;
/* configure the pins for audio (iis) control */
s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
if (s3c24xx_snd_is_clkmaster(chip))
s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
}
/* initialise the stream */
runtime->hw = s3c24xx_snd_hw;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
or = &chip->capture;
else
or = &chip->playback;
/* call all the registered helpers */
ret = call_open(chip->base_ops, substream);
if (ret < 0)
goto exit_err;
ret = call_open(chip->chip_ops, substream);
if (ret < 0)
goto exit_err;
or->state |= ST_OPENED;
runtime->private_data = or;
or->stream = substream;
up(&chip->sem);
return ret;
exit_err:
up(&chip->sem);
return ret;
}
static void call_shutdown(struct s3c24xx_iis_ops *ops)
{
pr_debug("--- %s\n", __FUNCTION__);
if (ops && ops->shutdown)
(ops->shutdown)(ops);
}
static int call_close(struct s3c24xx_iis_ops *ops,
struct snd_pcm_substream *substream)
{
pr_debug("--- %s\n", __FUNCTION__);
if (ops && ops->close)
return (ops->close)(ops, substream);
return 0;
}
/* close callback */
static int s3c24xx_snd_close(struct snd_pcm_substream *substream)
{
s3c24xx_card_t *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
s3c24xx_runtime_t *or = runtime->private_data;
pr_debug("--- %s\n", __FUNCTION__);
DBG("%s: substream=%p, chip=%p\n", __FUNCTION__, substream, chip);
down(&chip->sem);
snd_assert(or->state != 0, chip = chip);
/* mark stream as closed */
or->state &= ~ST_OPENED;
/* close all the devices associated with this */
call_close(chip->chip_ops, substream);
call_close(chip->base_ops, substream);
/* are both shut-down? */
if (chip->playback.state == 0 && chip->capture.state == 0) {
/* call shut-down methods for all */
call_shutdown(chip->chip_ops);
call_shutdown(chip->base_ops);
/* de-configure the IIS pins, go to input */
s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_INP);
s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_INP);
if (s3c24xx_snd_is_clkmaster(chip))
s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_INP);
s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_INP);
s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_INP);
/* free any hold we had on the modules */
if (chip->base_ops_claimed) {
chip->base_ops_claimed = 0;
module_put(chip->base_ops->owner);
}
if (chip->chip_ops_claimed) {
chip->chip_ops_claimed = 0;
module_put(chip->chip_ops->owner);
}
}
up(&chip->sem);
return 0;
}
/* hw_params callback */
static int s3c24xx_snd_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
pr_debug("--- %s\n", __FUNCTION__);
DBG("%s: sub=%p, hwp=%p\n", __FUNCTION__, substream, hw_params);
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
return 0;
}
/* hw_free callback */
static int s3c24xx_snd_pcm_hw_free(struct snd_pcm_substream *substream)
{
pr_debug("--- %s\n", __FUNCTION__);
DBG("%s: substream %p\n", __FUNCTION__, substream);
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
/* frequency control */
#define DIV_ABS(a) ((a) < 0 ? -(a) : (a))
#define MANY_FPS 1
static long s3c24xx_snd_getdiv(s3c24xx_card_t *card, unsigned int rate,
unsigned long *iismod, unsigned long iisdiv, unsigned long *err)
{
unsigned long clkrate = clk_get_rate(card->clock);
long div;
long diff;
pr_debug("--- %s\n", __FUNCTION__);
DBG("%s: %ld\n", __FUNCTION__, clkrate);
if (iisdiv & S3C2410_IISMOD_384FS)
clkrate /= 384;
else
clkrate /= 256;
div = clkrate / rate;
/* is divisor in range */
if (div < 1)
div =1;
if (div > 32)
div = 32;
if (div < 32) {
if ( DIV_ABS(rate*div - clkrate) > DIV_ABS(rate*(div+1) - clkrate))
div = div +1;
}
/* is the rate generated near enough our clock rate? */
diff = (clkrate / div) - rate;
if (err)
*err = DIV_ABS(diff);
#ifndef MANY_FPS
if (DIV_ABS(diff) > 100) {
//return -1L;
printk("Warning, i2s clock is rather out of specs (found %ld, expected %d)!\n", clkrate/div, rate);
}
#endif
/* update info and return */
*iismod &= ~S3C2410_IISMOD_384FS;
*iismod |= iisdiv;
return (div-1) << S3C2410_IISPSR_INTSHIFT;
}
/* s3c24xx_snd_setrate
*
* configure the clock rate for when the s3c24xx is in clock master
* mode.
*/
int s3c24xx_snd_setrate(s3c24xx_card_t *chip, struct snd_pcm_runtime *run)
{
unsigned long iismod = readl(chip->regs + S3C2410_IISMOD);
unsigned long rate;
unsigned long tmp;
long prescaler;
#ifdef MANY_FPS
long prescaler_256, prescaler_384;
unsigned long iismod_256, iismod_384;
unsigned long err_256, err_384, err;
#endif
/* if this is set to slave mode, then our clocks are all
* inputs anyway, so we cannot alter the frequency here */
pr_debug("--- %s\n", __FUNCTION__);
if (iismod & S3C2410_IISMOD_SLAVE) {
printk(KERN_ERR "%s: called with IISMOD as slave\n",
__FUNCTION__);
return -EINVAL;
}
rate = run->rate;
#ifdef MANY_FPS
/* try both */
iismod_256 = iismod_384 = iismod;
prescaler_256 = s3c24xx_snd_getdiv(chip, rate, &iismod_256,
S3C2410_IISMOD_256FS, &err_256);
prescaler_384 = s3c24xx_snd_getdiv(chip, rate, &iismod_384,
S3C2410_IISMOD_384FS, &err_384);
if (err_256 < err_384) {
prescaler = prescaler_256;
iismod = iismod_256;
err = err_256;
chip->is_384 = 0;
}
else {
prescaler = prescaler_384;
iismod = iismod_384;
err = err_384;
chip->is_384 = 1;
}
#if 1
if (err > 100)
printk("Imprecise sample rate (err is %ld)\n", err);
#endif
#else
/* CHRI: here we force to 384fs. We should study a way to tell
the codec to change this parameter. grep: ONLY384 */
DBG("%s: clock rate %ld\n", __FUNCTION__, rate);
prescaler = s3c24xx_snd_getdiv(chip, rate, &iismod,
S3C2410_IISMOD_384FS, NULL);
DBG("%s: prescaler is %ld\n", __FUNCTION__, prescaler >> S3C2410_IISPSR_INTSHIFT);
chip->is_384 = 1;
#endif
if (prescaler < 0) {
printk(KERN_ERR "cannot get rate %d Hz\n", run->rate);
return -ERANGE;
}
/* update timing registers */
writel(iismod, chip->regs + S3C2410_IISMOD);
tmp = readl(chip->regs + S3C2410_IISPSR) & ~S3C2410_IISPSR_INTMASK;
tmp |= prescaler;
tmp &= ~S3C2410_IISPSR_EXTMASK; /* no harm in always setting ext prescaler too */
tmp |= prescaler >> S3C2410_IISPSR_INTSHIFT;
writel(tmp, chip->regs + S3C2410_IISPSR);
tmp = readl(chip->regs + S3C2410_IISPSR);
DBG("%s: IISPSR is %08lx\n", __FUNCTION__, tmp);
return 0;
}
EXPORT_SYMBOL(s3c24xx_snd_setrate);
/* s3c24xx_iismod_cfg
*
* Update the s3c24xx IISMOD register
*/
void s3c24xx_iismod_cfg(s3c24xx_card_t *chip,
unsigned long set,
unsigned long mask)
{
unsigned long flags;
unsigned long tmp;
pr_debug("--- %s\n", __FUNCTION__);
local_irq_save(flags);
tmp = readl(chip->regs + S3C2410_IISMOD);
tmp &= ~mask;
tmp |= set;
DBG("%s: new iismod 0x%08lx\n", __FUNCTION__, tmp);
writel(tmp, chip->regs + S3C2410_IISMOD);
local_irq_restore(flags);
}
EXPORT_SYMBOL(s3c24xx_iismod_cfg);
static int call_prepare(struct s3c24xx_iis_ops *ops,
struct snd_pcm_substream *substream, struct snd_pcm_runtime *rt)
{
pr_debug("--- %s\n", __FUNCTION__);
if (ops && ops->prepare)
return (ops->prepare)(ops, substream, rt);
return 0;
}
/* prepare callback */
static int s3c24xx_snd_pcm_prepare(struct snd_pcm_substream *substream)
{
s3c24xx_card_t *chip = snd_pcm_substream_chip(substream);
s3c24xx_runtime_t *or = substream->runtime->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long flags;
int ret = 0;
pr_debug("--- %s\n", __FUNCTION__);
DBG("%s: substream=%p, chip=%p\n", __FUNCTION__, substream, chip);
DBG("%s: r->dma_addr=%lx, r->dma_area=%p\n", __FUNCTION__,
(long)runtime->dma_addr, runtime->dma_area);
/* if we are master set clock rate */
if (s3c24xx_snd_is_clkmaster(chip)) {
s3c24xx_snd_setrate(chip, runtime);
}
/* prepare DMA */
spin_lock_irqsave(&or->lock, flags);
or->dma_loaded = 0;
or->dma_limit = runtime->hw.periods_min;
or->dma_period = snd_pcm_lib_period_bytes(substream);
or->dma_start = runtime->dma_addr;
or->dma_pos = or->dma_start;
or->dma_end = or->dma_start + snd_pcm_lib_buffer_bytes(substream);
spin_unlock_irqrestore(&or->lock, flags);
DBG("%s: dma: period=%d, start=%lx, pos=%lx, end=%lx\n", __FUNCTION__,
or->dma_period, (long)or->dma_start,
(long)or->dma_pos, (long)or->dma_end);
/* flush the DMA channel */
s3c2410_dma_ctrl(or->dma_ch, S3C2410_DMAOP_FLUSH);
/* call the register ops for prepare, to allow things like
* sample rate and format to be set
*/
ret = call_prepare(chip->base_ops, substream, runtime);
if (ret < 0)
goto exit_err;
ret = call_prepare(chip->chip_ops, substream, runtime);
exit_err:
return ret;
}
/* s3c24xx_snd_lrsync
*
* Wait for the LR signal to allow synchronisation to the L/R clock
* from the DAC. May only be needed for slave mode.
*/
static int s3c24xx_snd_lrsync(s3c24xx_card_t *chip)
{
unsigned long iiscon;
int timeout = 10000;
pr_debug("--- %s\n", __FUNCTION__);
while (1) {
iiscon = readl(chip->regs + S3C2410_IISCON);
if (iiscon & S3C2410_IISCON_LRINDEX)
break;
if (--timeout < 0)
return -ETIMEDOUT;
}
return 0;
}
/* trigger callback */
static int s3c24xx_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
s3c24xx_runtime_t *or = substream->runtime->private_data;
s3c24xx_card_t *chip = snd_pcm_substream_chip(substream);
unsigned long flags;
int ret = -EINVAL;
pr_debug("--- %s\n", __FUNCTION__);
DBG("%s: substream=%p, cmd=%d\n", __FUNCTION__, substream, cmd);
spin_lock_irqsave(&or->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
or->state |= ST_RUNNING;
/* enqueue dma buffers and start the IIS engine */
s3c24xx_pcm_enqueue(or);
s3c24xx_snd_showregs(chip);
s3c2410_dma_ctrl(or->dma_ch, S3C2410_DMAOP_START);
if (!s3c24xx_snd_is_clkmaster(chip)) {
ret = s3c24xx_snd_lrsync(chip);
if (ret)
goto exit_err;
}
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
s3c24xx_snd_rxctrl(chip, 1);
else
s3c24xx_snd_txctrl(chip, 1);
ret = 0;
break;
case SNDRV_PCM_TRIGGER_STOP:
or->state &= ~ST_RUNNING;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
s3c24xx_snd_rxctrl(chip, 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -