Re: [PATCH] ASoC: add xtensa xtfpga I2S interface and platform

From: Max Filippov
Date: Tue Oct 28 2014 - 13:00:51 EST


On Tue, Oct 28, 2014 at 6:42 PM, Mark Brown <broonie@xxxxxxxxxx> wrote:
> On Mon, Oct 27, 2014 at 11:38:53PM +0300, Max Filippov wrote:
>> On Mon, Oct 27, 2014 at 10:32 PM, Mark Brown <broonie@xxxxxxxxxx> wrote:
>> > On Mon, Oct 27, 2014 at 10:07:05PM +0300, Max Filippov wrote:
>
>> >> +config SND_SOC_XTENSA_XTFPGA
>> >> + tristate "SoC Audio for xtensa xtfpga"
>> >> + depends on XTENSA_PLATFORM_XTFPGA
>> >> + select SND_SOC_XTFPGA_I2S
>> >> + select SND_SOC_TLV320AIC23_SPI
>> >> + select SND_SIMPLE_CARD
>
>> > I've only got this far in the file and have to leave now so I'll look
>> > properly at the actual code later but the above shouldn't have the CODEC
>> > or card driver selected, if the I2S driver is well written it should be
>> > usable independently of either.
>
>> The above entry is for the whole sound subsystem of that SoC.
>> An entry for I2S driver is above it.
>
> Then this just shouldn't exist at all, adding Kconfig entries for all
> the simple-card devices would defeat the point of having simple-card
> which is why we don't do it for other systems.

sound/soc/sh/Kconfig does that as well.
Not having single config item to enable at once all relevant drivers feels
a bit strange... But ok, I'll drop that.

>> > Some documentation explaining what your RCU usage is all about would
>> > also be good... I'm not clear why it's being used, none of the FIQ
>> > drivers do this and I can't entirely figure out what we're defending
>> > against other than the tasklet which we should be stopping anyway and
>> > why what we're doing is safe.
>
>> Ok, I'll document it. I'm synchronizing trigger callback with both interrupt
>> handler and the tasklet. All that they need to know is whether we're playing
>> a stream or not, so I protect the stream pointer with RCU. A spinlock for
>> protection of a single pointer seems too big for me.
>
> So atomics don't work? Simple flags are one of the few cases where they
> might cover it... again, the fact that this isn't similar to other code
> doing the same thing is worrying.

tx_substream use pattern is the same as of a typical RCU variable.
RCU wrappers combine ACCESS_ONCE and barriers that I'd otherwise
have to write myself.

>> > I would also expect to see the data transfer and I2S bits more split
>> > out, presumably this IP can actually be used in designs with a DMA
>> > controller and one would expect that for practical systems this would be
>> > the common case?
>
>> I don't know of other designs that use this IP block. Can we do it when
>> we get such users?
>
> It's also about ensuring that the code is cleanly split up so that
> someone can actually go in and add the required support later (and TBH

Can you point me to an example of such split, so that I don't write it in
an unusual way?

> it'd be better to just add a DMA controller on the FPGA, everyone will
> be much happier).

Hardware people think different and it's been like that for more than 5
years.

>> >> + if (int_status & XTFPGA_I2S_INT_UNDERRUN) {
>> >> + i2s->tx_fifo_sz = 0;
>> >> + regmap_update_bits(i2s->regmap, XTFPGA_I2S_CONFIG,
>> >> + XTFPGA_I2S_CONFIG_INT_ENABLE |
>> >> + XTFPGA_I2S_CONFIG_TX_ENABLE, 0);
>> >> + } else {
>> >> + i2s->tx_fifo_sz = i2s->tx_fifo_low;
>> >> + regmap_update_bits(i2s->regmap, XTFPGA_I2S_CONFIG,
>> >> + XTFPGA_I2S_CONFIG_INT_ENABLE, 0);
>> >> + }
>
>> > We're trying to implement a NAPI style thing here?
>
>> No, there's no way to retrieve the exact FIFO level from the hardware,
>> there's no such register. But there are two interrupt reasons: one
>> when the FIFO is below preset level and another when FIFO is empty.
>> So this code tracks the FIFO level according to the interrupt reason.
>
> ...and then it appears to try to implement something like NAPI?

If the programmed FIFO level is low enough I refill FIFO right in the
interrupt handler. If I use tasklet for such short FIFOs the sound
becomes choppy.

>> >> + switch (params_format(params)) {
>> >> + case SNDRV_PCM_FORMAT_S16_LE:
>> >> + case SNDRV_PCM_FORMAT_S32_LE:
>> >> + break;
>
>> > This looks buggy, we're accepting two different formats but not storing
>> > anything or telling the hardware - especially concerning given that this
>> > is a master only driver.
>
>> xtfpga_i2s_push_tx does the right thing based on runtime->sample_bits
>> value.
>
> So add a comment then. The thing I was saying about the data push code
> being hard to understand apply generally.

Ok.

>> >> + err = clk_prepare_enable(i2s->clk);
>> >> + if (err < 0)
>> >> + return err;
>> >> + i2s->clk_enabled = true;
>
>> > This stuff with the clock seems complicated, why not just leave it
>> > disabled when not in use and take advantage of the reference counting
>> > the core already has?
>
>> Because this callback is said to be potentially called multiple times per
>> initialization. Is it not?
>
> It can but but I'm not seeing any connection between that and the idea
> of not keeping the clock enabled when the device isn't in use?

hw_params callback can change MCLK rate, so it has to disable and
enable the clock anyway, and since enable can fail it does not guarantee
that the clock will be left in the same state. Or should I adjust MCLK rate
w/o disabling the clock?

>> >> + i2s->tx_fifo_low = XTFPGA_I2S_FIFO_SIZE / 2;
>> >> + for (level = 1;
>> >> + /* period_size * 2: FIFO always gets 2 samples per frame */
>> >> + i2s->tx_fifo_low / 2 >= period_size * 2;
>> >> + ++level)
>> >> + i2s->tx_fifo_low /= 2;
>
>> > This can't come out with a bad value?
>
>> I've tested it in the range of frequencies that we support -- works well.
>
> I'm not sure I find that terribly convincing, I'd like to be able to
> look at the code and tell that it's not going to blow up. This again
> comes back to the general comment about clarity - the code *looks*
> suspicious (strange indentation of the for loop with a comment in the
> middle of the statement itself for example).

The level field in the control register is 4 bit wide, so the allowed range of
level is 0..15. FIFO size is 8192 entries, level = 1 corresponds to
FIFO size / 2, level = 14 -- to FIFO size of 0. I guess this function
won't get period_size of 0?

--
Thanks.
-- Max
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/