diff mbox series

[v2,6/6] pcm: Update pcm->avail_min with needed_slave_avail_min, after reading unaligned frames

Message ID 1557901597-19215-7-git-send-email-vanitha.channaiah@in.bosch.com (mailing list archive)
State New, archived
Headers show
Series [v2,1/6] pcm: direct: Add generic hw_ptr_alignment function for dmix, dshare and dsnoop | expand

Commit Message

Channaiah Vanitha (RBEI/ECF3) May 15, 2019, 6:26 a.m. UTC
From: Vanitha Channaiah <vanitha.channaiah@in.bosch.com>

This Fix was analyzed for below usecase :

alsa configuration:
pcm.line_in {
    type dsnoop
    ipc_key  INT
    slave {
        pcm hardware
	channels 2
	period_time 8000
        rate 48000
        format S16_LE
    }
   bindings {
       0 0
       1 1
   }
}
pcm.hardware {
    type hw
    card "gmd-card"
    device 0
    subdevice 0
    channels 2
    period_time 2000
    rate 48000
    format S16_LE
}

command:
$arecord -v -D line_in -r 48000 -c 2 -f S16_LE recordfile.wav
Direct Snoop PCM
Its setup is:
  stream       : CAPTURE
  access       : RW_INTERLEAVED
  format       : S16_LE
  subformat    : STD
  channels     : 2
  rate         : 48000
  exact rate   : 48000 (48000/1)
  msbits       : 16
  buffer_size  : 1536
  period_size  : 384
  period_time  : 8000
  tstamp_mode  : NONE
  tstamp_type  : MONOTONIC
  period_step  : 1
  avail_min    : 384
  period_event : 0
  start_threshold  : 1
  stop_threshold   : 1536
  silence_threshold: 0
  silence_size : 0
  boundary     : huge value
Hardware PCM card 3 'gmd-card' device 0 subdevice 0
Its setup is:
  stream       : CAPTURE
  access       : MMAP_INTERLEAVED
  format       : S16_LE
  subformat    : STD
  channels     : 2
  rate         : 48000
  exact rate   : 48000 (48000/1)
  msbits       : 16
  buffer_size  : 1536
  period_size  : 96
  period_time  : 2000
  tstamp_mode  : ENABLE
  tstamp_type  : MONOTONIC
  period_step  : 1
  avail_min    : 96
  period_event : 0
  start_threshold  : 1
  stop_threshold   : huge value
  silence_threshold: 0
  silence_size : 0
  boundary     : huge value
  appl_ptr     : 0
  hw_ptr       : 576

Here, there are no other plugins apart from Dsnoop.

Issue: After partial read of unaligned frames(not one period frames),
snd_pcm_wait() would  block for the pcm->avail_min which would result in
blocking for longer periods i.e more than one period as specified by
pcm->avail_min

For e.g.:
Slave period size = 0x60
Client period-size=0x180
No. of Ticks = 4
pcm->avail_min = one period size = 0x180

Issue:
- During the start of streaming, the position of slave hw_ptr returned
  from the driver is 0x20.
- avail is 0x20
- hw_ptr is calculated by new_slave_hw_ptr - old_slave_hw_ptr i.e.
  0x20 - 0 = 0x20
- hw_ptr updated to 0x20
- avail is 0x20
- app_ptr updated to 0x20
- Now, avail = 0
- snd_pcm_wait() waits till avail=0x180 because pcm->avail_min = 0x180
- After 4 snd_pcm_elapsed(), slave_hw_ptr = 0x180
- Since app_ptr has updated with 0x20, avail becomes 0x160
  There is a shortage of 0x20 frames and hence snd_pcm_wait()
  goes back to wait again.
- Now, snd_pcm_wait is locked.
- After another 4 snd_pcm_elapsed() slave_hw_ptr = 0x300
- avail = 0x2e0
- snd_pcm_wait() unlocks.
So, here snd_pcm_wait() is locked for more than 1 period(exactly 2 periods)

Also, this issue can be seen without dsnoop plugin, when HW reports unaligned hw_ptr
For e.g.
period size = 0x60
pcm->avail_min = 0x60
- During the start of streaming, the position of slave hw_ptr returned
  from the driver is 0x20.
- hw_ptr is calculated by new_slave_hw_ptr - old_slave_hw_ptr i.e.
  0x20 - 0 = 0x20
- hw_ptr updated to 0x20
- avail is 0x20
- app_ptr updated to 0x20
- Now, avail = 0
- snd_pcm_wait() waits till avail=0x60 because pcm->avail_min=0x60
- After 1 snd_pcm_elapsed(), slave_hw_ptr = 0x60
- Since app_ptr has updated with 0x20, avail becomes 0x40
  There is a shortage of 0x20 frames and hence snd_pcm_wait()
  goes back to wait again.
- Now, snd_pcm_wait is locked.
- After another 1 snd_pcm_elapsed(), slave_hw_ptr = 0x120
- avail = 0xe0
- snd_pcm_wait() unlocks.
So, here snd_pcm_wait() is locked for more than 1 period (exactly 2 periods)

Fix: After reading unaligned frames(not one period frames),
set the pcm->avail_min to the needed_avail_slave_min frames
so that snd_pcm_wait() blocks till needed_avail_slave_min available
Once needed_avail_slave_min frames are read, set back the original
pcm->avail_min

For ex:
Slave period size = 0x60
Client period-size=0x180
No. of Ticks = 4
pcm->avail_min = one period size = 0x180

Fix:
- During the start of streaming, the position of slave_hw_ptr returned
  from the driver is 0x20.
  - hw_ptr is calculated by new_slave_hw_ptr - old_slave_hw_ptr
  i.e. 0x20 - 0 = 0x20
- hw_ptr updated to 0x20
- avail is 0x20
- app_ptr updated to 0x20
- Now, avail = 0
- calculate needed_avail_slave_min = 0x160
- update the needed_avail_slave_min to pcm->avail_min
  i.e. pcm->avail_min = 0x160
- snd_pcm_wait() waits till avail=0x160
- After 4 snd_pcm_elapsed(), slave_hw_ptr = 0x180
- snd_pcm_wait() unlocks.
- Once needed_avail_slave_min frames are read, set back the
  original pcm->avail_min to 0x180
So, here snd_pcm_wait() is locked for 1 period only.

Signed-off-by: Vanitha Channaiah <vanitha.channaiah@in.bosch.com>
---
 src/pcm/pcm.c       | 21 +++++++++++++++++++++
 src/pcm/pcm_local.h |  2 ++
 2 files changed, 23 insertions(+)

Comments

Takashi Iwai May 15, 2019, 3:32 p.m. UTC | #1
On Wed, 15 May 2019 08:26:37 +0200,
<vanitha.channaiah@in.bosch.com> wrote:
> 
> From: Vanitha Channaiah <vanitha.channaiah@in.bosch.com>
> 
> This Fix was analyzed for below usecase :
> 
> alsa configuration:
> pcm.line_in {
>     type dsnoop
>     ipc_key  INT
>     slave {
>         pcm hardware
> 	channels 2
> 	period_time 8000
>         rate 48000
>         format S16_LE
>     }
>    bindings {
>        0 0
>        1 1
>    }
> }
> pcm.hardware {
>     type hw
>     card "gmd-card"
>     device 0
>     subdevice 0
>     channels 2
>     period_time 2000
>     rate 48000
>     format S16_LE
> }
> 
> command:
> $arecord -v -D line_in -r 48000 -c 2 -f S16_LE recordfile.wav
> Direct Snoop PCM
> Its setup is:
>   stream       : CAPTURE
>   access       : RW_INTERLEAVED
>   format       : S16_LE
>   subformat    : STD
>   channels     : 2
>   rate         : 48000
>   exact rate   : 48000 (48000/1)
>   msbits       : 16
>   buffer_size  : 1536
>   period_size  : 384
>   period_time  : 8000
>   tstamp_mode  : NONE
>   tstamp_type  : MONOTONIC
>   period_step  : 1
>   avail_min    : 384
>   period_event : 0
>   start_threshold  : 1
>   stop_threshold   : 1536
>   silence_threshold: 0
>   silence_size : 0
>   boundary     : huge value
> Hardware PCM card 3 'gmd-card' device 0 subdevice 0
> Its setup is:
>   stream       : CAPTURE
>   access       : MMAP_INTERLEAVED
>   format       : S16_LE
>   subformat    : STD
>   channels     : 2
>   rate         : 48000
>   exact rate   : 48000 (48000/1)
>   msbits       : 16
>   buffer_size  : 1536
>   period_size  : 96
>   period_time  : 2000
>   tstamp_mode  : ENABLE
>   tstamp_type  : MONOTONIC
>   period_step  : 1
>   avail_min    : 96
>   period_event : 0
>   start_threshold  : 1
>   stop_threshold   : huge value
>   silence_threshold: 0
>   silence_size : 0
>   boundary     : huge value
>   appl_ptr     : 0
>   hw_ptr       : 576
> 
> Here, there are no other plugins apart from Dsnoop.
> 
> Issue: After partial read of unaligned frames(not one period frames),
> snd_pcm_wait() would  block for the pcm->avail_min which would result in
> blocking for longer periods i.e more than one period as specified by
> pcm->avail_min
> 
> For e.g.:
> Slave period size = 0x60
> Client period-size=0x180
> No. of Ticks = 4
> pcm->avail_min = one period size = 0x180
> 
> Issue:
> - During the start of streaming, the position of slave hw_ptr returned
>   from the driver is 0x20.
> - avail is 0x20
> - hw_ptr is calculated by new_slave_hw_ptr - old_slave_hw_ptr i.e.
>   0x20 - 0 = 0x20
> - hw_ptr updated to 0x20
> - avail is 0x20
> - app_ptr updated to 0x20
> - Now, avail = 0
> - snd_pcm_wait() waits till avail=0x180 because pcm->avail_min = 0x180
> - After 4 snd_pcm_elapsed(), slave_hw_ptr = 0x180
> - Since app_ptr has updated with 0x20, avail becomes 0x160
>   There is a shortage of 0x20 frames and hence snd_pcm_wait()
>   goes back to wait again.
> - Now, snd_pcm_wait is locked.
> - After another 4 snd_pcm_elapsed() slave_hw_ptr = 0x300
> - avail = 0x2e0
> - snd_pcm_wait() unlocks.
> So, here snd_pcm_wait() is locked for more than 1 period(exactly 2 periods)
> 
> Also, this issue can be seen without dsnoop plugin, when HW reports unaligned hw_ptr
> For e.g.
> period size = 0x60
> pcm->avail_min = 0x60
> - During the start of streaming, the position of slave hw_ptr returned
>   from the driver is 0x20.
> - hw_ptr is calculated by new_slave_hw_ptr - old_slave_hw_ptr i.e.
>   0x20 - 0 = 0x20
> - hw_ptr updated to 0x20
> - avail is 0x20
> - app_ptr updated to 0x20
> - Now, avail = 0
> - snd_pcm_wait() waits till avail=0x60 because pcm->avail_min=0x60
> - After 1 snd_pcm_elapsed(), slave_hw_ptr = 0x60
> - Since app_ptr has updated with 0x20, avail becomes 0x40
>   There is a shortage of 0x20 frames and hence snd_pcm_wait()
>   goes back to wait again.
> - Now, snd_pcm_wait is locked.
> - After another 1 snd_pcm_elapsed(), slave_hw_ptr = 0x120
> - avail = 0xe0
> - snd_pcm_wait() unlocks.
> So, here snd_pcm_wait() is locked for more than 1 period (exactly 2 periods)
> 
> Fix: After reading unaligned frames(not one period frames),
> set the pcm->avail_min to the needed_avail_slave_min frames
> so that snd_pcm_wait() blocks till needed_avail_slave_min available
> Once needed_avail_slave_min frames are read, set back the original
> pcm->avail_min
> 
> For ex:
> Slave period size = 0x60
> Client period-size=0x180
> No. of Ticks = 4
> pcm->avail_min = one period size = 0x180
> 
> Fix:
> - During the start of streaming, the position of slave_hw_ptr returned
>   from the driver is 0x20.
>   - hw_ptr is calculated by new_slave_hw_ptr - old_slave_hw_ptr
>   i.e. 0x20 - 0 = 0x20
> - hw_ptr updated to 0x20
> - avail is 0x20
> - app_ptr updated to 0x20
> - Now, avail = 0
> - calculate needed_avail_slave_min = 0x160
> - update the needed_avail_slave_min to pcm->avail_min
>   i.e. pcm->avail_min = 0x160
> - snd_pcm_wait() waits till avail=0x160
> - After 4 snd_pcm_elapsed(), slave_hw_ptr = 0x180
> - snd_pcm_wait() unlocks.
> - Once needed_avail_slave_min frames are read, set back the
>   original pcm->avail_min to 0x180
> So, here snd_pcm_wait() is locked for 1 period only.
> 
> Signed-off-by: Vanitha Channaiah <vanitha.channaiah@in.bosch.com>
> ---
>  src/pcm/pcm.c       | 21 +++++++++++++++++++++
>  src/pcm/pcm_local.h |  2 ++
>  2 files changed, 23 insertions(+)
> 
> diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
> index f0db545..f361eb1 100644
> --- a/src/pcm/pcm.c
> +++ b/src/pcm/pcm.c
> @@ -973,6 +973,7 @@ int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
>  		__snd_pcm_unlock(pcm);
>  		return err;
>  	}
> +	pcm->original_avail_min = pcm->avail_min;
>  	__snd_pcm_unlock(pcm);
>  	return 0;
>  }
> @@ -7267,6 +7268,17 @@ void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
>  	snd_pcm_unlock(pcm);
>  }
>  
> +static void snd_pcm_set_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail)
> +{
> +	if (avail != pcm->avail_min) {
> +		snd_pcm_sw_params_t swparams;
> +
> +		snd_pcm_sw_params_current(pcm, &swparams);
> +		swparams.avail_min = avail;
> +		_snd_pcm_sw_params_internal(pcm, &swparams);
> +	}
> +}
> +
>  snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
>  				     snd_pcm_uframes_t offset, snd_pcm_uframes_t size,
>  				     snd_pcm_xfer_areas_func_t func)
> @@ -7274,6 +7286,7 @@ snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_
>  	snd_pcm_uframes_t xfer = 0;
>  	snd_pcm_sframes_t err = 0;
>  	snd_pcm_state_t state;
> +	snd_pcm_uframes_t needed_slave_avail_min = 0;
>  
>  	if (size == 0)
>  		return 0;
> @@ -7332,6 +7345,14 @@ snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_
>  		if (err < 0)
>  			break;
>  		frames = err;
> +		pcm->unaligned_frames += frames;
> +		pcm->unaligned_frames %= pcm->period_size;
> +		if (pcm->unaligned_frames) {
> +			needed_slave_avail_min = pcm->period_size - pcm->unaligned_frames;
> +			snd_pcm_set_avail_min(pcm, needed_slave_avail_min);
> +		} else {
> +			snd_pcm_set_avail_min(pcm, pcm->original_avail_min);
> +		}
>  		offset += frames;
>  		size -= frames;
>  		xfer += frames;
> diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h
> index e103f72..3fdffb4 100644
> --- a/src/pcm/pcm_local.h
> +++ b/src/pcm/pcm_local.h
> @@ -210,6 +210,8 @@ struct _snd_pcm {
>  	snd_pcm_tstamp_type_t tstamp_type;	/* timestamp type */
>  	unsigned int period_step;
>  	snd_pcm_uframes_t avail_min;	/* min avail frames for wakeup */
> +	snd_pcm_uframes_t unaligned_frames;
> +	snd_pcm_uframes_t original_avail_min;
>  	int period_event;
>  	snd_pcm_uframes_t start_threshold;
>  	snd_pcm_uframes_t stop_threshold;

Can we avoid adding such a hack in the core code?

Basically the issue is very specific to direct-plugins, so this sort
of workaround should be implemented locally there instead.  I guess we
can do similar tricks by overriding the calls in the callbacks.


thanks,

Takashi
Channaiah Vanitha (RBEI/ECF3) May 16, 2019, 5:26 p.m. UTC | #2
Hello  Takashi-san,

>  Can we avoid adding such a hack in the core code?
>  Basically the issue is very specific to direct-plugins, so this sort of workaround should be implemented locally there instead.  I guess we can do similar tricks by overriding the calls in the callbacks.

Issue can be seen without direct plugins too with involvement of only hw which reports unaligned hw ptr. As I have explained in below  detailed description:
"Also, this issue can be seen without dsnoop plugin, when HW reports unaligned hw_ptr For e.g. "

Best regards,
Vanitha Channaiah
RBEI/ECF3

Tel. +91 80 6136-4436



-----Original Message-----
From: Takashi Iwai <tiwai@suse.de>
Sent: Wednesday, May 15, 2019 9:03 PM
To: Channaiah Vanitha (RBEI/ECF3) <Vanitha.Channaiah@in.bosch.com>
Cc: alsa-devel@alsa-project.org; Wischer Timo (ADITG/ESS) <twischer@de.adit-jv.com>
Subject: Re: [PATCH v2 6/6] pcm: Update pcm->avail_min with needed_slave_avail_min, after reading unaligned frames

On Wed, 15 May 2019 08:26:37 +0200,
<vanitha.channaiah@in.bosch.com<mailto:vanitha.channaiah@in.bosch.com>> wrote:
>
> From: Vanitha Channaiah <vanitha.channaiah@in.bosch.com<mailto:vanitha.channaiah@in.bosch.com>>
>
> This Fix was analyzed for below usecase :
>
> alsa configuration:
> pcm.line_in {
>     type dsnoop
>     ipc_key  INT
>     slave {
>         pcm hardware
>       channels 2
>       period_time 8000
>         rate 48000
>         format S16_LE
>     }
>    bindings {
>        0 0
>        1 1
>    }
> }
> pcm.hardware {
>     type hw
>     card "gmd-card"
>     device 0
>     subdevice 0
>     channels 2
>     period_time 2000
>     rate 48000
>     format S16_LE
> }
>
> command:
> $arecord -v -D line_in -r 48000 -c 2 -f S16_LE recordfile.wav Direct
> Snoop PCM Its setup is:
>   stream       : CAPTURE
>   access       : RW_INTERLEAVED
>   format       : S16_LE
>   subformat    : STD
>   channels     : 2
>   rate         : 48000
>   exact rate   : 48000 (48000/1)
>   msbits       : 16
>   buffer_size  : 1536
>   period_size  : 384
>   period_time  : 8000
>   tstamp_mode  : NONE
>   tstamp_type  : MONOTONIC
>   period_step  : 1
>   avail_min    : 384
>   period_event : 0
>   start_threshold  : 1
>   stop_threshold   : 1536
>   silence_threshold: 0
>   silence_size : 0
>   boundary     : huge value
> Hardware PCM card 3 'gmd-card' device 0 subdevice 0 Its setup is:
>   stream       : CAPTURE
>   access       : MMAP_INTERLEAVED
>   format       : S16_LE
>   subformat    : STD
>   channels     : 2
>   rate         : 48000
>   exact rate   : 48000 (48000/1)
>   msbits       : 16
>   buffer_size  : 1536
>   period_size  : 96
>   period_time  : 2000
>   tstamp_mode  : ENABLE
>   tstamp_type  : MONOTONIC
>   period_step  : 1
>   avail_min    : 96
>   period_event : 0
>   start_threshold  : 1
>   stop_threshold   : huge value
>   silence_threshold: 0
>   silence_size : 0
>   boundary     : huge value
>   appl_ptr     : 0
>   hw_ptr       : 576
>
> Here, there are no other plugins apart from Dsnoop.
>
> Issue: After partial read of unaligned frames(not one period frames),
> snd_pcm_wait() would  block for the pcm->avail_min which would result
> in blocking for longer periods i.e more than one period as specified
> by
> pcm->avail_min
>
> For e.g.:
> Slave period size = 0x60
> Client period-size=0x180
> No. of Ticks = 4
> pcm->avail_min = one period size = 0x180
>
> Issue:
> - During the start of streaming, the position of slave hw_ptr returned
>   from the driver is 0x20.
> - avail is 0x20
> - hw_ptr is calculated by new_slave_hw_ptr - old_slave_hw_ptr i.e.
>   0x20 - 0 = 0x20
> - hw_ptr updated to 0x20
> - avail is 0x20
> - app_ptr updated to 0x20
> - Now, avail = 0
> - snd_pcm_wait() waits till avail=0x180 because pcm->avail_min = 0x180
> - After 4 snd_pcm_elapsed(), slave_hw_ptr = 0x180
> - Since app_ptr has updated with 0x20, avail becomes 0x160
>   There is a shortage of 0x20 frames and hence snd_pcm_wait()
>   goes back to wait again.
> - Now, snd_pcm_wait is locked.
> - After another 4 snd_pcm_elapsed() slave_hw_ptr = 0x300
> - avail = 0x2e0
> - snd_pcm_wait() unlocks.
> So, here snd_pcm_wait() is locked for more than 1 period(exactly 2
> periods)
>
> Also, this issue can be seen without dsnoop plugin, when HW reports
> unaligned hw_ptr For e.g.
> period size = 0x60
> pcm->avail_min = 0x60
> - During the start of streaming, the position of slave hw_ptr returned
>   from the driver is 0x20.
> - hw_ptr is calculated by new_slave_hw_ptr - old_slave_hw_ptr i.e.
>   0x20 - 0 = 0x20
> - hw_ptr updated to 0x20
> - avail is 0x20
> - app_ptr updated to 0x20
> - Now, avail = 0
> - snd_pcm_wait() waits till avail=0x60 because pcm->avail_min=0x60
> - After 1 snd_pcm_elapsed(), slave_hw_ptr = 0x60
> - Since app_ptr has updated with 0x20, avail becomes 0x40
>   There is a shortage of 0x20 frames and hence snd_pcm_wait()
>   goes back to wait again.
> - Now, snd_pcm_wait is locked.
> - After another 1 snd_pcm_elapsed(), slave_hw_ptr = 0x120
> - avail = 0xe0
> - snd_pcm_wait() unlocks.
> So, here snd_pcm_wait() is locked for more than 1 period (exactly 2
> periods)
>
> Fix: After reading unaligned frames(not one period frames), set the
> pcm->avail_min to the needed_avail_slave_min frames so that
> snd_pcm_wait() blocks till needed_avail_slave_min available Once
> needed_avail_slave_min frames are read, set back the original
> pcm->avail_min
>
> For ex:
> Slave period size = 0x60
> Client period-size=0x180
> No. of Ticks = 4
> pcm->avail_min = one period size = 0x180
>
> Fix:
> - During the start of streaming, the position of slave_hw_ptr returned
>   from the driver is 0x20.
>   - hw_ptr is calculated by new_slave_hw_ptr - old_slave_hw_ptr
>   i.e. 0x20 - 0 = 0x20
> - hw_ptr updated to 0x20
> - avail is 0x20
> - app_ptr updated to 0x20
> - Now, avail = 0
> - calculate needed_avail_slave_min = 0x160
> - update the needed_avail_slave_min to pcm->avail_min
>   i.e. pcm->avail_min = 0x160
> - snd_pcm_wait() waits till avail=0x160
> - After 4 snd_pcm_elapsed(), slave_hw_ptr = 0x180
> - snd_pcm_wait() unlocks.
> - Once needed_avail_slave_min frames are read, set back the
>   original pcm->avail_min to 0x180
> So, here snd_pcm_wait() is locked for 1 period only.
>
> Signed-off-by: Vanitha Channaiah <vanitha.channaiah@in.bosch.com<mailto:vanitha.channaiah@in.bosch.com>>
> ---
>  src/pcm/pcm.c       | 21 +++++++++++++++++++++
>  src/pcm/pcm_local.h |  2 ++
>  2 files changed, 23 insertions(+)
>
> diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index f0db545..f361eb1
> 100644
> --- a/src/pcm/pcm.c
> +++ b/src/pcm/pcm.c
> @@ -973,6 +973,7 @@ int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
>               __snd_pcm_unlock(pcm);
>               return err;
>       }
> +     pcm->original_avail_min = pcm->avail_min;
>       __snd_pcm_unlock(pcm);
>       return 0;
>  }
> @@ -7267,6 +7268,17 @@ void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
>       snd_pcm_unlock(pcm);
>  }
>
> +static void snd_pcm_set_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t
> +avail) {
> +     if (avail != pcm->avail_min) {
> +             snd_pcm_sw_params_t swparams;
> +
> +             snd_pcm_sw_params_current(pcm, &swparams);
> +             swparams.avail_min = avail;
> +             _snd_pcm_sw_params_internal(pcm, &swparams);
> +     }
> +}
> +
>  snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
>                                    snd_pcm_uframes_t offset, snd_pcm_uframes_t size,
>                                    snd_pcm_xfer_areas_func_t func) @@ -7274,6 +7286,7 @@
> snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_
>       snd_pcm_uframes_t xfer = 0;
>       snd_pcm_sframes_t err = 0;
>       snd_pcm_state_t state;
> +     snd_pcm_uframes_t needed_slave_avail_min = 0;
>
>       if (size == 0)
>               return 0;
> @@ -7332,6 +7345,14 @@ snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_
>               if (err < 0)
>                       break;
>               frames = err;
> +             pcm->unaligned_frames += frames;
> +             pcm->unaligned_frames %= pcm->period_size;
> +             if (pcm->unaligned_frames) {
> +                     needed_slave_avail_min = pcm->period_size - pcm->unaligned_frames;
> +                     snd_pcm_set_avail_min(pcm, needed_slave_avail_min);
> +             } else {
> +                     snd_pcm_set_avail_min(pcm, pcm->original_avail_min);
> +             }
>               offset += frames;
>               size -= frames;
>               xfer += frames;
> diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index
> e103f72..3fdffb4 100644
> --- a/src/pcm/pcm_local.h
> +++ b/src/pcm/pcm_local.h
> @@ -210,6 +210,8 @@ struct _snd_pcm {
>       snd_pcm_tstamp_type_t tstamp_type;      /* timestamp type */
>       unsigned int period_step;
>       snd_pcm_uframes_t avail_min;    /* min avail frames for wakeup */
> +     snd_pcm_uframes_t unaligned_frames;
> +     snd_pcm_uframes_t original_avail_min;
>       int period_event;
>       snd_pcm_uframes_t start_threshold;
>       snd_pcm_uframes_t stop_threshold;

Can we avoid adding such a hack in the core code?

Basically the issue is very specific to direct-plugins, so this sort of workaround should be implemented locally there instead.  I guess we can do similar tricks by overriding the calls in the callbacks.


thanks,

Takashi
Jaroslav Kysela May 16, 2019, 5:35 p.m. UTC | #3
Dne 16. 05. 19 v 19:26 Channaiah Vanitha (RBEI/ECF3) napsal(a):
> Hello  Takashi-san,
> 
>>  Can we avoid adding such a hack in the core code?
>>  Basically the issue is very specific to direct-plugins, so this sort of workaround should be implemented locally there instead.  I guess we can do similar tricks by overriding the calls in the callbacks.
> 
> Issue can be seen without direct plugins too with involvement of only hw which reports unaligned hw ptr. As I have explained in below  detailed description:
> "Also, this issue can be seen without dsnoop plugin, when HW reports unaligned hw_ptr For e.g. "

Which hardware exactly? The hw_ptr should be reset when the streaming starts.
It appears that the problem is specific to the direct plugins only when the
period wakeups are a bit different than for the direct hardware access.

						Jaroslav
Channaiah Vanitha (RBEI/ECF3) June 17, 2019, 11:15 p.m. UTC | #4
Hello Jaroslav-san, Takashi-san,

> Which hardware exactly? The hw_ptr should be reset when the streaming starts.
> It appears that the problem is specific to the direct plugins only when the period wakeups are a bit different than for the direct hardware access.

Firstly, sorry for late reply.

Issue is seen in RCar Kingfischer H3 es2.0 target.

The issue was seen during the below commit :
commit 07b7acb51d283d8469696c906b91f1882696a4d4
("ASoC: rsnd: update pointer more accurate")
https://patchwork.kernel.org/patch/9772671/


There could be a non-uniform jitter exists between when interrupt raised [rcar_dmac_isr_channel(), rcar_dmac_isr_channel_thread()]
and the interrupt is processed to read/calculate the DMA position [dma_set_residue()]
This could result in unaligned hw_ptr reported at user-space alsa lib.


Best regards,
Vanitha Channaiah
RBEI/ECF3

Tel. +91 80 6136-4436


-----Original Message-----
From: Jaroslav Kysela <perex@perex.cz>
Sent: Thursday, May 16, 2019 11:05 PM
To: Channaiah Vanitha (RBEI/ECF3) <Vanitha.Channaiah@in.bosch.com>; Takashi Iwai <tiwai@suse.de>
Cc: Wischer Timo (ADITG/ESS) <twischer@de.adit-jv.com>; alsa-devel@alsa-project.org
Subject: Re: [alsa-devel] [PATCH v2 6/6] pcm: Update pcm->avail_min with needed_slave_avail_min, after reading unaligned frames

Dne 16. 05. 19 v 19:26 Channaiah Vanitha (RBEI/ECF3) napsal(a):
> Hello  Takashi-san,
>
>>  Can we avoid adding such a hack in the core code?
>>  Basically the issue is very specific to direct-plugins, so this sort of workaround should be implemented locally there instead.  I guess we can do similar tricks by overriding the calls in the callbacks.
>
> Issue can be seen without direct plugins too with involvement of only hw which reports unaligned hw ptr. As I have explained in below  detailed description:
> "Also, this issue can be seen without dsnoop plugin, when HW reports unaligned hw_ptr For e.g. "

Which hardware exactly? The hw_ptr should be reset when the streaming starts.
It appears that the problem is specific to the direct plugins only when the period wakeups are a bit different than for the direct hardware access.

                                                Jaroslav


--
Jaroslav Kysela <perex@perex.cz<mailto:perex@perex.cz>>
Linux Sound Maintainer; ALSA Project; Red Hat, Inc.
Channaiah Vanitha (RBEI/ECF3) July 16, 2019, 3:58 a.m. UTC | #5
Hello Jaroslav-san, Takashi-san,

Can you please reply your feedback for below mail chain.

Best regards,
Vanitha Channaiah 
RBEI/ECF3
Takashi Iwai July 16, 2019, 5:03 a.m. UTC | #6
On Tue, 16 Jul 2019 05:58:15 +0200,
Channaiah Vanitha (RBEI/ECF3) wrote:
> 
> Hello Jaroslav-san, Takashi-san,
> 
> Can you please reply your feedback for below mail chain.
> 
> Best regards,
> Vanitha Channaiah 
> RBEI/ECF3
> 
> _____________________________________________
> From: Channaiah Vanitha (RBEI/ECF3) 
> Sent: Tuesday, June 18, 2019 4:45 AM
> To: 'Jaroslav Kysela' <perex@perex.cz>; Takashi Iwai <tiwai@suse.de>
> Cc: Wischer Timo (ADITG/ESS) <twischer@de.adit-jv.com>; alsa-devel@alsa-project.org
> Subject: RE: [alsa-devel] [PATCH v2 6/6] pcm: Update pcm->avail_min with needed_slave_avail_min, after reading unaligned frames
> 
> 
> Hello Jaroslav-san, Takashi-san,
> 
> > Which hardware exactly? The hw_ptr should be reset when the streaming starts.
> > It appears that the problem is specific to the direct plugins only when the period wakeups are a bit different than for the direct hardware access.
> 
> Firstly, sorry for late reply.
> 
> Issue is seen in RCar Kingfischer H3 es2.0 target.	
> 
> The issue was seen during the below commit :
> commit 07b7acb51d283d8469696c906b91f1882696a4d4
> ("ASoC: rsnd: update pointer more accurate")
> https://patchwork.kernel.org/patch/9772671/
> 
> 
> There could be a non-uniform jitter exists between when interrupt raised [rcar_dmac_isr_channel(), rcar_dmac_isr_channel_thread()]
> and the interrupt is processed to read/calculate the DMA position [dma_set_residue()]
> This could result in unaligned hw_ptr reported at user-space alsa lib.

It looks rather like a workaround for the bug in driver.
Better fix the driver instead.


thanks,

Takashi
diff mbox series

Patch

diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
index f0db545..f361eb1 100644
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -973,6 +973,7 @@  int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
 		__snd_pcm_unlock(pcm);
 		return err;
 	}
+	pcm->original_avail_min = pcm->avail_min;
 	__snd_pcm_unlock(pcm);
 	return 0;
 }
@@ -7267,6 +7268,17 @@  void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
 	snd_pcm_unlock(pcm);
 }
 
+static void snd_pcm_set_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail)
+{
+	if (avail != pcm->avail_min) {
+		snd_pcm_sw_params_t swparams;
+
+		snd_pcm_sw_params_current(pcm, &swparams);
+		swparams.avail_min = avail;
+		_snd_pcm_sw_params_internal(pcm, &swparams);
+	}
+}
+
 snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
 				     snd_pcm_uframes_t offset, snd_pcm_uframes_t size,
 				     snd_pcm_xfer_areas_func_t func)
@@ -7274,6 +7286,7 @@  snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_
 	snd_pcm_uframes_t xfer = 0;
 	snd_pcm_sframes_t err = 0;
 	snd_pcm_state_t state;
+	snd_pcm_uframes_t needed_slave_avail_min = 0;
 
 	if (size == 0)
 		return 0;
@@ -7332,6 +7345,14 @@  snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_
 		if (err < 0)
 			break;
 		frames = err;
+		pcm->unaligned_frames += frames;
+		pcm->unaligned_frames %= pcm->period_size;
+		if (pcm->unaligned_frames) {
+			needed_slave_avail_min = pcm->period_size - pcm->unaligned_frames;
+			snd_pcm_set_avail_min(pcm, needed_slave_avail_min);
+		} else {
+			snd_pcm_set_avail_min(pcm, pcm->original_avail_min);
+		}
 		offset += frames;
 		size -= frames;
 		xfer += frames;
diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h
index e103f72..3fdffb4 100644
--- a/src/pcm/pcm_local.h
+++ b/src/pcm/pcm_local.h
@@ -210,6 +210,8 @@  struct _snd_pcm {
 	snd_pcm_tstamp_type_t tstamp_type;	/* timestamp type */
 	unsigned int period_step;
 	snd_pcm_uframes_t avail_min;	/* min avail frames for wakeup */
+	snd_pcm_uframes_t unaligned_frames;
+	snd_pcm_uframes_t original_avail_min;
 	int period_event;
 	snd_pcm_uframes_t start_threshold;
 	snd_pcm_uframes_t stop_threshold;