[alsa-devel] [patch 5/6] kirkwood: Add i2s support

Arnaud Patard apatard at mandriva.com
Wed May 12 16:38:25 CEST 2010


Mark Brown <broonie at opensource.wolfsonmicro.com> writes:

> On Tue, May 11, 2010 at 06:23:47PM +0200, apatard at mandriva.com wrote:
>> This patch enables support for the i2s codec available on kirkwood platforms
>
> It's not a CODEC, it's the I2S controller in the CPU.  A CODEC is a
> device with DACs and ADCs in it.
>
>> +	do {
>> +		/* we've enabled only bytes interrupts ... */
>
> You were checking for error interrupts further up the function?
>
>> +		if (status & ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES | \
>> +				KIRKWOOD_INT_CAUSE_REC_BYTES))
>> +			return IRQ_NONE;
>
> Surely it'd make sense to log the unexpected interrupts for diagnostics?
> You should never get here if there are no interrupts you've enabled.
> Unless the interrupt is shared, but that'd be a bit surprising for a
> SoC.

it's not shared iirc but there are different possible interrupt
cause. There used to be a message but I think it has been removed by
error. Should I had it back or remove the check ? (I prefer adding it
back but your point of view does matter).

>
> It also looks like it'd be more sensible to either drop the do/while
> loop entirely (the IRQ core should handle the interrupt still being
> asserted and it doesn't look like any of the handling should take so
> long that interrupts reassert while it's running anyway) or move the
> handling of error interrupts and the first read into the loop so you
> don't have two slightly different paths.

The loop is there to handle playback and record interrupts at the same
time. As regards the error interrupts, I preferred to put outside the
loop to differentiate "normal" interrupts from "error" interrupts.

>
>> +		/* ack int */
>> +		writel(status, priv->io + KIRKWOOD_INT_CAUSE);
>> +
>> +		if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES)
>> +			snd_pcm_period_elapsed(prdata->play_stream);
>> +
>> +		if (status & KIRKWOOD_INT_CAUSE_REC_BYTES)
>> +			snd_pcm_period_elapsed(prdata->rec_stream);
>> +
>> +		mask = readl(priv->io + KIRKWOOD_INT_MASK);
>> +		status = readl(priv->io + KIRKWOOD_INT_CAUSE) & mask;
>
> This masking didn't happen prior to the first entry into the loop.

Can you explain a little bit more ? at the beginning of the function,
there are the 2 same lines.

>
>> +       if (soc_runtime->dai->cpu_dai->private_data == NULL) {
>
> It seems a bit icky to be managing the DAI private data here...
>
>> +		err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
>> +				  "kirkwood-dma", prdata);
>
> I'd suggest "kirkwood-i2s" as a name here.  Or take the name from the
> DAI.

ok

>
>> +static int kirkwood_dma_trigger(struct snd_pcm_substream *substream, int cmd)
>> +{
>> +	return 0;
>> +}
>
> Remove this if it's not implemented.
>
>> +static int kirkwood_dma_mmap(struct snd_pcm_substream *substream,
>> +		struct vm_area_struct *vma)
>> +{
>> +	struct snd_pcm_runtime *runtime = substream->runtime;
>> +
>> +	return dma_mmap_coherent(substream->pcm->card->dev, vma,
>> +					runtime->dma_area,
>> +					runtime->dma_addr,
>> +					runtime->dma_bytes);
>> +}
>
> Hrm, this looks like there should be a utility function to do it, it's
> not terribly driver specific.

hmm... I need to double check. I got some troubles with this kind of
stuff on my ST LS2E/F mips platforms, so I wrote something known to be
working.

>
>> +struct snd_soc_platform kirkwood_soc_platform = {
>> +	.name		= "kirkwood-dma",
>
> I'd also add an audio in here or something.
>
>> +#define AUDIO_WIN_BASE_REG(win)			(0xA00 + ((win)<<3))
>> +#define AUDIO_WIN_CTRL_REG(win)			(0xA04 + ((win)<<3))
>
> Namespacing.  Or move into the driver, there seems little reason for
> anything else to be peering at these.
>
>> +#define KIRKWOOD_RATES \
>> +	(SNDRV_PCM_RATE_44100 | \
>> +	 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
>> +#define KIRKWOOD_FORMATS \
>> +	(SNDRV_PCM_FMTBIT_S16_LE | \
>> +	 SNDRV_PCM_FMTBIT_S24_LE | \
>> +	 SNDRV_PCM_FMTBIT_S32_LE)
>
> Move these into the driver.
>
>> +static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate)
>> +{
>> +	unsigned long value;
>> +
>> +	value = KIRKWOOD_DCO_CTL_OFFSET_0;
>> +	switch (rate) {
>> +	default:
>> +	case 44100:
>> +		value |= KIRKWOOD_DCO_CTL_FREQ_11;
>> +		break;
>> +	case 48000:
>> +		value |= KIRKWOOD_DCO_CTL_FREQ_12;
>> +		break;
>> +	case 96000:
>> +		value |= KIRKWOOD_DCO_CTL_FREQ_24;
>> +		break;
>> +	}
>> +	writel(value, io + KIRKWOOD_DCO_CTL);
>> +
>> +	/* wait for dco locked */
>> +	do {
>> +		cpu_relax();
>> +		value = readl(io + KIRKWOOD_DCO_SPCR_STATUS);
>> +		value &= KIRKWOOD_DCO_SPCR_STATUS;
>> +	} while (value == 0);
>> +}
>
> Why is this an inline function in the header?
>
>> +	 * Size settings in play/rec i2s control regs and play/rec control
>> +	 * regs must be the same.
>> +	 */
>
> Ideally you'd be setting up constraints for this.
>
>> +	/*
>> +	 * specs says KIRKWOOD_PLAYCTL must be read 2 times before
>> +	 * changing it. So read 1 time here and 1 later.
>> +	 */
>> +	value = readl(priv->io + KIRKWOOD_PLAYCTL);
>
> Nice...
>
>> +	switch (cmd) {
>> +	case SNDRV_PCM_TRIGGER_START:
>> +		/* stop audio, enable interrupts */
>
> If audio is running when you're getting a start trigger something is
> wrong...

I'm too paranoid ? here

>
>> +static int kirkwood_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
>> +				int clk_id, unsigned int freq, int dir)
>> +{
>> +	return 0;
>> +}
>
> Remove this.
>
>> +static void mv_audio_init(void __iomem *base)
>> +{
>> +	int timeout = 10000000;
>> +	unsigned int reg_data;
>> +
>> +	reg_data = readl(base + 0x1200);
>> +	reg_data &= (~(0x333FF8));
>> +	reg_data |= 0x111D18;
>> +	writel(reg_data, base + 0x1200);
>
> It's like magic!

yeah. I've not been able to find out any info in the specs about that
and last time I tried with that it was not working :(

>
>> +
>> +	do {
>> +		timeout--;
>> +	} while (timeout);
>
> The driver is busy waiting here but not polling anything.  Just use a
> sleep?
>
>> +	/* FIXME : should not be hardcoded */
>> +	priv->burst = 128;
>
> Platform data?
>
>> +static int kirkwood_i2s_probe(struct platform_device *pdev,
>> +                            struct snd_soc_dai *dai)
>
> This is the ASoC probe but...
>
> +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>
> ...it's pulling all sorts of resources out of the device which are
> specific to the I2S controller.  You should have the I2S controller have
> a normal platform device which is probed on system startup and triggers
> DAI registration, just as you're doing in the CODEC driver.  All the
> resources should be there.  Apart from anything else this means you only
> need to set up each I2S controller once in the arch/arm code for the
> CPU.

ok. Will look at that stuff.

>
>> +	mv_audio_init(priv->io);
>
> Seems a bit odd ot have the separate init function in the middle of a
> bunch of other register writes.

I want to have interrupts disable before calling it. I don't want bad
suprises.


Arnaud


More information about the Alsa-devel mailing list