📄 s3c24xx-iis.c
字号:
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 + -