[alsa-devel] [PATCH 3/7] Asoc: sti: add CPU DAI driver for playback
Arnaud Pouliquen
arnaud.pouliquen at st.com
Mon Apr 27 15:58:56 CEST 2015
On 04/24/2015 08:15 PM, Mark Brown wrote:
> On Tue, Apr 14, 2015 at 03:35:27PM +0200, Arnaud Pouliquen wrote:
>
>> +const struct snd_pcm_hardware uni_player_pcm_hw = {
>> + .info = (
>> + SNDRV_PCM_INFO_INTERLEAVED |
>> + SNDRV_PCM_INFO_BLOCK_TRANSFER |
>> + SNDRV_PCM_INFO_PAUSE),
>> + .formats = (SNDRV_PCM_FMTBIT_S32_LE |
>> + SNDRV_PCM_FMTBIT_S16_LE),
> Please use the normal kernel coding style, standard indentation and no
> brackets.
>
>> + .rates = SNDRV_PCM_RATE_CONTINUOUS,
>> + .rate_min = UNIPERIF_MIN_RATE,
>> + .rate_max = UNIPERIF_MAX_RATE,
>> +
>> + .channels_min = UNIPERIF_MIN_CHANNELS,
>> + .channels_max = UNIPERIF_MAX_CHANNELS,
>> +
>> + .periods_min = UNIPERIF_PERIODS_MIN,
>> + .periods_max = UNIPERIF_PERIODS_MAX,
>> +
>> + .period_bytes_min = UNIPERIF_PERIODS_BYTES_MIN,
>> + .period_bytes_max = UNIPERIF_PERIODS_BYTES_MAX,
>> + .buffer_bytes_max = UNIPERIF_BUFFER_BYTES_MAX
> Just use the values directly, the indirection is just making it harder
> to find what's actually set.
>
>> +static inline int reset_player(struct uniperif *player)
>> +{
>> + int count = 10;
>> +
>> + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
>> + while (GET_UNIPERIF_SOFT_RST_SOFT_RST(player) && count) {
>> + udelay(5);
>> + count--;
>> + }
> Braces for the if too. A cpu_relax() wouldn't go amiss in here either.
>
>> +static inline int get_property_hdl(struct device *dev, struct device_node *np,
>> + const char *prop, int idx)
>> +{
> This is very suspicous - why do you need this and if you need it why not
> add it to the generic DT code?
>
>> +static void uni_player_work(struct work_struct *work)
>> +{
>> + struct uniperif *player =
>> + container_of(work, struct uniperif, delayed_work.work);
>> +
>> + spin_lock(&player->lock);
>> + if (uni_player_suspend(player))
>> + dev_err(player->dev, "%s: failed to suspend player", __func__);
>> + spin_unlock(&player->lock);
>> +}
> What is this for, and why is there a spinlock which isn't IRQ safe? If
> it's sensible to do this under a spinlock it seems like it might not
> need to be done in a workqueue...
>
> There's a lot of very coarse grained spinlock usage in this driver which
> I'm having a hard time understanding, at the very least the decisions
> about locking need to be documented much more clearly and I suspect that
> either things need to be finer grained, we should be using mutexes more
> or both.
>
This part is linked to the standby mode described in binding ( patch[1/7])
it manage a runtime suspend, because ASOC runtime suspend is dedicated
to DAPM.
As you recommend i will try to change it by a DAPM linked to CPU_DAI.
Just need to find a wait
to retrieve CPU_DAI context in DAPM..
Concerning spinlock
I use it to protect context ( called by IRQ, on suspend, by user...). As
some code is called in atomic i can not use mutex.
I will review it and document it.
>> +static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
>> +{
>> + irqreturn_t ret = IRQ_NONE;
>> + struct uniperif *player = dev_id;
>> + unsigned int status;
>> + unsigned int tmp;
>> +
>> + /* Get interrupt status & clear them immediately */
>> + preempt_disable();
>> + status = GET_UNIPERIF_ITS(player);
>> + SET_UNIPERIF_ITS_BCLR(player, status);
>> + preempt_enable();
> preempt_disable()? Why is this being done, if you're doing unusual
> stuff like this the code needs to be very clear about what the locking
> rules are?
This is used to be sure to not miss an interrupt. If preempted between both
instruction i can clear an interruption flag before treat it. In this
case i will receive
a second interrupt with all flag to 0.
>
>> +
>> + if ((player->state == UNIPERIF_STATE_STANDBY) ||
>> + (player->state == UNIPERIF_STATE_STOPPED)) {
>> + /* unexpected IRQ: do nothing */
>> + dev_warn(player->dev, "unexpected IRQ: status flag: %#x",
>> + status);
>> + return IRQ_HANDLED;
> We didn't handle it, we ignored it (after acknowledging it...). I'd
> expect this check to be before we look at the hardware if it's needed.
>
>> + };
> Extra semicolon here.
>
>> + snd_pcm_stream_lock(player->substream);
> Again please explain the locking if we're doing something unusual.
This protect from a race condition, if application request a stop while
we receive Error from IP.
I call it here to avoid toprotect all snd_pcm_stop calls... but i can
protect only the snd_pcm_stop
calls if more clear
>
>> + /* Check for fifo error (under run) */
>> + if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) {
>> + dev_err(player->dev, "FIFO underflow error detected");
>> +
>> + /* Interrupt is just for information when underflow recovery */
>> + if (player->info->underflow_enabled) {
>> + /* Update state to underflow */
>> + player->state = UNIPERIF_STATE_UNDERFLOW;
> Why would underflow recovery be optional?
To propose 2 strategies:
- stop on underrun ( because plop will occurs)
- try to recover it: time is recovered when new data is available (
sample dropping)
>
>> + if (clk_set_rate(player->clk, rate_adjusted) < 0) {
>> + dev_err(player->dev, "Failed to clk set rate %d !\n",
>> + rate_adjusted);
>> + return -EINVAL;
>> + }
> Pass back the error code you got.
>
>> + rate_achieved = clk_get_rate(player->clk);
>> + if (!rate_achieved)
>> + return -EINVAL;
> That check doesn't seem right - if the clock was set to 1Hz instead of
> 1MHz it'll report success. Either trust that clk_set_rate() will give
> you an error if it fails or have some sort of reasonable check on the
> actual set value if that's important.
>
>> +static int snd_sti_clk_adjustment_get(struct snd_kcontrol *kcontrol,
>> + struct snd_ctl_elem_value *ucontrol)
>> +{
>> + struct uniperif *player = snd_kcontrol_chip(kcontrol);
>> +
>> + spin_lock(&player->lock);
>> + ucontrol->value.integer.value[0] = player->clk_adj;
>> + spin_unlock(&player->lock);
>> +
>> + return 0;
>> +}
> There was some discussion of interfaces for fine tuning clock rates with
> some other drivers recently, we need to think about this in some
> standard fashion. Please split this out into a separate patch. In
> general it's best to introduce unusual/new things like this and the IEC
> setting in separate patches to simplify review and allow the easy bits
> to get merged without being held up by complicated things like this.
Ok i will add Clk and IEC controls in separate patches
>> + of_property_read_u32(pnode, "version", &info->ver);
>> +
>> + of_property_read_u32(pnode, "uniperiph-id", &info->uni_num);
> We can't read this stuff from the hardware?
Unfortunately No...
More information about the Alsa-devel
mailing list