diff mbox

[RFC,16/26] ALSA: rme32: Convert to copy_silence ops

Message ID 20170511210925.18208-17-tiwai@suse.de (mailing list archive)
State New, archived
Headers show

Commit Message

Takashi Iwai May 11, 2017, 9:09 p.m. UTC
Replace the copy and the silence ops with the new merged ops.
The conversion is straightforward with standard helper functions.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/rme32.c | 49 ++++++++++++++++++++++++-------------------------
 1 file changed, 24 insertions(+), 25 deletions(-)

Comments

René Rebe July 18, 2018, 10:22 a.m. UTC | #1
Hello everyone,

to have another digital audio i/o card for our studio / office, I got a pair of RME32 the other week from ebay. (mostly as reference to implement ADAT for the RAD1 Sgi/Octane ALSA driver, …)

Unfortunately they do not work with Linux. They are recognised and all the usual devices and /proc/… entries show up, however, the hardware pointer does not move during playback or capture no matter what clock source I choose.
I tried attaching coax s/pdif as well as an 8-channel Behringer Ultragain ADAT source w/ clock.

The two cards came from the same seller, look ok and both behave the same. 
I went so far to install a Windows XP test install where both cards work “more”.
(They are not perfect in windows, however, at least s/pdif can come out with or with-out external ADAT clock source. However, the digital signal strangely unclean, but that may be a bug in the window system sound device emulation, I only tested with foobar2k and not some Pro audio app which I do not really have).

Long story short, does someone still have such a card, or not moving hardware ptr (or missing interrupts?) does ring a bell regarding this RME generation? Instead of a working ADAT reference card, I apparently have two ALSA driver to hack on, … ;-)

I tested two different “PC” boards and the results were the same, too.

Thanks,
	René

On 11 May 2017, at 23:09, Takashi Iwai <tiwai@suse.de> wrote:

> Replace the copy and the silence ops with the new merged ops.
> The conversion is straightforward with standard helper functions.
> 
> Signed-off-by: Takashi Iwai <tiwai@suse.de>
> ---
> sound/pci/rme32.c | 49 ++++++++++++++++++++++++-------------------------
> 1 file changed, 24 insertions(+), 25 deletions(-)
> 
> diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
> index 96d15db65dfd..d2b4a3ef0bd3 100644
> --- a/sound/pci/rme32.c
> +++ b/sound/pci/rme32.c
> @@ -253,41 +253,42 @@ static inline unsigned int snd_rme32_pcm_byteptr(struct rme32 * rme32)
> 		& RME32_RCR_AUDIO_ADDR_MASK);
> }
> 
> -/* silence callback for halfduplex mode */
> -static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
> -				      snd_pcm_uframes_t pos,
> -				      snd_pcm_uframes_t count)
> -{
> -	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
> -	count <<= rme32->playback_frlog;
> -	pos <<= rme32->playback_frlog;
> -	memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
> -	return 0;
> -}
> -
> /* copy callback for halfduplex mode */
> -static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
> +static int snd_rme32_playback_copy(struct snd_pcm_substream *substream,
> +				   int channel,	/* not used (interleaved data) */
> 				   snd_pcm_uframes_t pos,
> -				   void __user *src, snd_pcm_uframes_t count)
> +				   void __user *src, snd_pcm_uframes_t count,
> +				   bool in_kernel)
> {
> 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
> 	count <<= rme32->playback_frlog;
> 	pos <<= rme32->playback_frlog;
> -	if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
> -			    src, count))
> +	if (!src)
> +		memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
> +	else if (in_kernel)
> +		memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
> +			    (void *)src, count);
> +	else if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
> +				     src, count))
> 		return -EFAULT;
> 	return 0;
> }
> 
> /* copy callback for halfduplex mode */
> -static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
> +static int snd_rme32_capture_copy(struct snd_pcm_substream *substream,
> +				  int channel,	/* not used (interleaved data) */
> 				  snd_pcm_uframes_t pos,
> -				  void __user *dst, snd_pcm_uframes_t count)
> +				  void __user *dst, snd_pcm_uframes_t count,
> +				  bool in_kernel)
> {
> 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
> 	count <<= rme32->capture_frlog;
> 	pos <<= rme32->capture_frlog;
> -	if (copy_to_user_fromio(dst,
> +	if (in_kernel)
> +		memcpy_fromio((void *)dst,
> +			      rme32->iobase + RME32_IO_DATA_BUFFER + pos,
> +			      count);
> +	else if (copy_to_user_fromio(dst,
> 			    rme32->iobase + RME32_IO_DATA_BUFFER + pos,
> 			    count))
> 		return -EFAULT;
> @@ -1205,8 +1206,7 @@ static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
> 	.prepare =	snd_rme32_playback_prepare,
> 	.trigger =	snd_rme32_pcm_trigger,
> 	.pointer =	snd_rme32_playback_pointer,
> -	.copy =		snd_rme32_playback_copy,
> -	.silence =	snd_rme32_playback_silence,
> +	.copy_silence =	snd_rme32_playback_copy,
> 	.mmap =		snd_pcm_lib_mmap_iomem,
> };
> 
> @@ -1219,7 +1219,7 @@ static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
> 	.prepare =	snd_rme32_capture_prepare,
> 	.trigger =	snd_rme32_pcm_trigger,
> 	.pointer =	snd_rme32_capture_pointer,
> -	.copy =		snd_rme32_capture_copy,
> +	.copy_silence =	snd_rme32_capture_copy,
> 	.mmap =		snd_pcm_lib_mmap_iomem,
> };
> 
> @@ -1231,8 +1231,7 @@ static const struct snd_pcm_ops snd_rme32_playback_adat_ops = {
> 	.prepare =	snd_rme32_playback_prepare,
> 	.trigger =	snd_rme32_pcm_trigger,
> 	.pointer =	snd_rme32_playback_pointer,
> -	.copy =		snd_rme32_playback_copy,
> -	.silence =	snd_rme32_playback_silence,
> +	.copy_silence =	snd_rme32_playback_copy,
> 	.mmap =		snd_pcm_lib_mmap_iomem,
> };
> 
> @@ -1244,7 +1243,7 @@ static const struct snd_pcm_ops snd_rme32_capture_adat_ops = {
> 	.prepare =	snd_rme32_capture_prepare,
> 	.trigger =	snd_rme32_pcm_trigger,
> 	.pointer =	snd_rme32_capture_pointer,
> -	.copy =		snd_rme32_capture_copy,
> +	.copy_silence =	snd_rme32_capture_copy,
> 	.mmap =		snd_pcm_lib_mmap_iomem,
> };
> 
> -- 
> 2.12.2
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Takashi Iwai July 18, 2018, 10:56 a.m. UTC | #2
On Wed, 18 Jul 2018 12:22:11 +0200,
René Rebe wrote:
> 
> Hello everyone,
> 
> to have another digital audio i/o card for our studio / office, I got a pair of RME32 the other week from ebay. (mostly as reference to implement ADAT for the RAD1 Sgi/Octane ALSA driver, …)
> 
> Unfortunately they do not work with Linux. They are recognised and all the usual devices and /proc/… entries show up, however, the hardware pointer does not move during playback or capture no matter what clock source I choose.
> I tried attaching coax s/pdif as well as an 8-channel Behringer Ultragain ADAT source w/ clock.

Does the /proc/asound/card*/rme32 entry show the right setup?
RME32 seems to have only few registers, and it behaves differently for
read and write.  Maybe you should try to watch the register 0x20000.
The hwptr is the LSB 33 bits.


Takashi

> The two cards came from the same seller, look ok and both behave the same. 
> I went so far to install a Windows XP test install where both cards work “more”.
> (They are not perfect in windows, however, at least s/pdif can come out with or with-out external ADAT clock source. However, the digital signal strangely unclean, but that may be a bug in the window system sound device emulation, I only tested with foobar2k and not some Pro audio app which I do not really have).
> 
> Long story short, does someone still have such a card, or not moving hardware ptr (or missing interrupts?) does ring a bell regarding this RME generation? Instead of a working ADAT reference card, I apparently have two ALSA driver to hack on, … ;-)
> 
> I tested two different “PC” boards and the results were the same, too.
> 
> Thanks,
> 	René
> 
> On 11 May 2017, at 23:09, Takashi Iwai <tiwai@suse.de> wrote:
> 
> > Replace the copy and the silence ops with the new merged ops.
> > The conversion is straightforward with standard helper functions.
> > 
> > Signed-off-by: Takashi Iwai <tiwai@suse.de>
> > ---
> > sound/pci/rme32.c | 49 ++++++++++++++++++++++++-------------------------
> > 1 file changed, 24 insertions(+), 25 deletions(-)
> > 
> > diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
> > index 96d15db65dfd..d2b4a3ef0bd3 100644
> > --- a/sound/pci/rme32.c
> > +++ b/sound/pci/rme32.c
> > @@ -253,41 +253,42 @@ static inline unsigned int snd_rme32_pcm_byteptr(struct rme32 * rme32)
> > 		& RME32_RCR_AUDIO_ADDR_MASK);
> > }
> > 
> > -/* silence callback for halfduplex mode */
> > -static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
> > -				      snd_pcm_uframes_t pos,
> > -				      snd_pcm_uframes_t count)
> > -{
> > -	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
> > -	count <<= rme32->playback_frlog;
> > -	pos <<= rme32->playback_frlog;
> > -	memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
> > -	return 0;
> > -}
> > -
> > /* copy callback for halfduplex mode */
> > -static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
> > +static int snd_rme32_playback_copy(struct snd_pcm_substream *substream,
> > +				   int channel,	/* not used (interleaved data) */
> > 				   snd_pcm_uframes_t pos,
> > -				   void __user *src, snd_pcm_uframes_t count)
> > +				   void __user *src, snd_pcm_uframes_t count,
> > +				   bool in_kernel)
> > {
> > 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
> > 	count <<= rme32->playback_frlog;
> > 	pos <<= rme32->playback_frlog;
> > -	if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
> > -			    src, count))
> > +	if (!src)
> > +		memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
> > +	else if (in_kernel)
> > +		memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
> > +			    (void *)src, count);
> > +	else if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
> > +				     src, count))
> > 		return -EFAULT;
> > 	return 0;
> > }
> > 
> > /* copy callback for halfduplex mode */
> > -static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
> > +static int snd_rme32_capture_copy(struct snd_pcm_substream *substream,
> > +				  int channel,	/* not used (interleaved data) */
> > 				  snd_pcm_uframes_t pos,
> > -				  void __user *dst, snd_pcm_uframes_t count)
> > +				  void __user *dst, snd_pcm_uframes_t count,
> > +				  bool in_kernel)
> > {
> > 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
> > 	count <<= rme32->capture_frlog;
> > 	pos <<= rme32->capture_frlog;
> > -	if (copy_to_user_fromio(dst,
> > +	if (in_kernel)
> > +		memcpy_fromio((void *)dst,
> > +			      rme32->iobase + RME32_IO_DATA_BUFFER + pos,
> > +			      count);
> > +	else if (copy_to_user_fromio(dst,
> > 			    rme32->iobase + RME32_IO_DATA_BUFFER + pos,
> > 			    count))
> > 		return -EFAULT;
> > @@ -1205,8 +1206,7 @@ static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
> > 	.prepare =	snd_rme32_playback_prepare,
> > 	.trigger =	snd_rme32_pcm_trigger,
> > 	.pointer =	snd_rme32_playback_pointer,
> > -	.copy =		snd_rme32_playback_copy,
> > -	.silence =	snd_rme32_playback_silence,
> > +	.copy_silence =	snd_rme32_playback_copy,
> > 	.mmap =		snd_pcm_lib_mmap_iomem,
> > };
> > 
> > @@ -1219,7 +1219,7 @@ static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
> > 	.prepare =	snd_rme32_capture_prepare,
> > 	.trigger =	snd_rme32_pcm_trigger,
> > 	.pointer =	snd_rme32_capture_pointer,
> > -	.copy =		snd_rme32_capture_copy,
> > +	.copy_silence =	snd_rme32_capture_copy,
> > 	.mmap =		snd_pcm_lib_mmap_iomem,
> > };
> > 
> > @@ -1231,8 +1231,7 @@ static const struct snd_pcm_ops snd_rme32_playback_adat_ops = {
> > 	.prepare =	snd_rme32_playback_prepare,
> > 	.trigger =	snd_rme32_pcm_trigger,
> > 	.pointer =	snd_rme32_playback_pointer,
> > -	.copy =		snd_rme32_playback_copy,
> > -	.silence =	snd_rme32_playback_silence,
> > +	.copy_silence =	snd_rme32_playback_copy,
> > 	.mmap =		snd_pcm_lib_mmap_iomem,
> > };
> > 
> > @@ -1244,7 +1243,7 @@ static const struct snd_pcm_ops snd_rme32_capture_adat_ops = {
> > 	.prepare =	snd_rme32_capture_prepare,
> > 	.trigger =	snd_rme32_pcm_trigger,
> > 	.pointer =	snd_rme32_capture_pointer,
> > -	.copy =		snd_rme32_capture_copy,
> > +	.copy_silence =	snd_rme32_capture_copy,
> > 	.mmap =		snd_pcm_lib_mmap_iomem,
> > };
> > 
> > -- 
> > 2.12.2
> > 
> > _______________________________________________
> > Alsa-devel mailing list
> > Alsa-devel@alsa-project.org
> > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 
> -- 
>  ExactCODE GmbH, Lietzenburger Str. 42, DE-10789 Berlin
>  DE Legal: Amtsgericht Berlin (Charlottenburg) HRB 105123B, Tax-ID#: DE251602478
>  Managing Director: René Rebe
>  http://exactcode.com | http://exactscan.com | http://ocrkit.com | http://t2-project.org | http://rene.rebe.de
>
René Rebe July 18, 2018, 6:10 p.m. UTC | #3
Hi,

On 18 Jul 2018, at 12:56, Takashi Iwai <tiwai@suse.de> wrote:

> On Wed, 18 Jul 2018 12:22:11 +0200,
> René Rebe wrote:
>> 
>> Hello everyone,
>> 
>> to have another digital audio i/o card for our studio / office, I got a pair of RME32 the other week from ebay. (mostly as reference to implement ADAT for the RAD1 Sgi/Octane ALSA driver, …)
>> 
>> Unfortunately they do not work with Linux. They are recognised and all the usual devices and /proc/… entries show up, however, the hardware pointer does not move during playback or capture no matter what clock source I choose.
>> I tried attaching coax s/pdif as well as an 8-channel Behringer Ultragain ADAT source w/ clock.
> 
> Does the /proc/asound/card*/rme32 entry show the right setup?

Well, I think it never showed external clock when I set it to external ADAT, right now, freshly booted (without the ext. ADAT) just trying to play stereo on the s/pdif it shows:

RME Digi32/8 (Rev. 101) at 0xec000000, irq 5 (index #1)

General settings
  Half-duplex mode
  receiver: CS8412
  format: 16 bit, Stereo

Input settings
  input: optical
  sample rate: no valid signal

Output settings
  output signal: normal playback (muted)
  sample rate: 44100 Hz
  sample clock source: Internal
  format: IEC958 (consumer)
  emphasis: off

Which should be right for internally word clock’ed s/pdif output. However, aplay does not play anything, after some 20? seconds or so of hanging it even prints:

aplay: pcm_write:2011: write error: Input/output error

While it is “trying to playback” /proc shows:

root@hostname:/proc/asound/card0# grep . pcm*p/sub0/*
pcm0p/sub0/hw_params:access: RW_INTERLEAVED
pcm0p/sub0/hw_params:format: S16_LE
pcm0p/sub0/hw_params:subformat: STD
pcm0p/sub0/hw_params:channels: 2
pcm0p/sub0/hw_params:rate: 44100 (44100/1)
pcm0p/sub0/hw_params:period_size: 2048
pcm0p/sub0/hw_params:buffer_size: 32768
pcm0p/sub0/info:card: 0
pcm0p/sub0/info:device: 0
pcm0p/sub0/info:subdevice: 0
pcm0p/sub0/info:stream: PLAYBACK
pcm0p/sub0/info:id: Digi32 IEC958
pcm0p/sub0/info:name: Digi32 IEC958
pcm0p/sub0/info:subname: subdevice #0
pcm0p/sub0/info:class: 0
pcm0p/sub0/info:subclass: 0
pcm0p/sub0/info:subdevices_count: 1
pcm0p/sub0/info:subdevices_avail: 0
pcm0p/sub0/status:state: RUNNING
pcm0p/sub0/status:owner_pid   : 753
pcm0p/sub0/status:trigger_time: 1195.174967384
pcm0p/sub0/status:tstamp      : 0.000000000
pcm0p/sub0/status:delay       : 32768
pcm0p/sub0/status:avail       : 0
pcm0p/sub0/status:avail_max   : 30720
pcm0p/sub0/status:-----
pcm0p/sub0/status:hw_ptr      : 10243
pcm0p/sub0/status:appl_ptr    : 43011
pcm0p/sub0/sw_params:tstamp_mode: NONE
pcm0p/sub0/sw_params:period_step: 1
pcm0p/sub0/sw_params:avail_min: 2048
pcm0p/sub0/sw_params:start_threshold: 32768
pcm0p/sub0/sw_params:stop_threshold: 32768
pcm0p/sub0/sw_params:silence_threshold: 0
pcm0p/sub0/sw_params:silence_size: 0
pcm0p/sub0/sw_params:boundary: 1073741824

I tried many kernels, down to 2.6.31, which I had initially on the box.
So unless some this hwparams never worked, this driver appears to not work a looong looooong time.

I doubt they are fully defect, as both behave the same and at least output “something” on Windows XP, ...

> RME32 seems to have only few registers, and it behaves differently for
> read and write.  Maybe you should try to watch the register 0x20000.
> The hwptr is the LSB 33 bits.

Yeah, guess I have to instrument this driver to track what is happening, maybe register not flushed to the hw?

> Takashi
> 
>> The two cards came from the same seller, look ok and both behave the same. 
>> I went so far to install a Windows XP test install where both cards work “more”.
>> (They are not perfect in windows, however, at least s/pdif can come out with or with-out external ADAT clock source. However, the digital signal strangely unclean, but that may be a bug in the window system sound device emulation, I only tested with foobar2k and not some Pro audio app which I do not really have).
>> 
>> Long story short, does someone still have such a card, or not moving hardware ptr (or missing interrupts?) does ring a bell regarding this RME generation? Instead of a working ADAT reference card, I apparently have two ALSA driver to hack on, … ;-)
>> 
>> I tested two different “PC” boards and the results were the same, too.
>> 
>> Thanks,
>> 	René
>> 
>> On 11 May 2017, at 23:09, Takashi Iwai <tiwai@suse.de> wrote:
>> 
>>> Replace the copy and the silence ops with the new merged ops.
>>> The conversion is straightforward with standard helper functions.
>>> 
>>> Signed-off-by: Takashi Iwai <tiwai@suse.de>
>>> ---
>>> sound/pci/rme32.c | 49 ++++++++++++++++++++++++-------------------------
>>> 1 file changed, 24 insertions(+), 25 deletions(-)
>>> 
>>> diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
>>> index 96d15db65dfd..d2b4a3ef0bd3 100644
>>> --- a/sound/pci/rme32.c
>>> +++ b/sound/pci/rme32.c
>>> @@ -253,41 +253,42 @@ static inline unsigned int snd_rme32_pcm_byteptr(struct rme32 * rme32)
>>> 		& RME32_RCR_AUDIO_ADDR_MASK);
>>> }
>>> 
>>> -/* silence callback for halfduplex mode */
>>> -static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
>>> -				      snd_pcm_uframes_t pos,
>>> -				      snd_pcm_uframes_t count)
>>> -{
>>> -	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
>>> -	count <<= rme32->playback_frlog;
>>> -	pos <<= rme32->playback_frlog;
>>> -	memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
>>> -	return 0;
>>> -}
>>> -
>>> /* copy callback for halfduplex mode */
>>> -static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
>>> +static int snd_rme32_playback_copy(struct snd_pcm_substream *substream,
>>> +				   int channel,	/* not used (interleaved data) */
>>> 				   snd_pcm_uframes_t pos,
>>> -				   void __user *src, snd_pcm_uframes_t count)
>>> +				   void __user *src, snd_pcm_uframes_t count,
>>> +				   bool in_kernel)
>>> {
>>> 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
>>> 	count <<= rme32->playback_frlog;
>>> 	pos <<= rme32->playback_frlog;
>>> -	if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>> -			    src, count))
>>> +	if (!src)
>>> +		memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
>>> +	else if (in_kernel)
>>> +		memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>> +			    (void *)src, count);
>>> +	else if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>> +				     src, count))
>>> 		return -EFAULT;
>>> 	return 0;
>>> }
>>> 
>>> /* copy callback for halfduplex mode */
>>> -static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
>>> +static int snd_rme32_capture_copy(struct snd_pcm_substream *substream,
>>> +				  int channel,	/* not used (interleaved data) */
>>> 				  snd_pcm_uframes_t pos,
>>> -				  void __user *dst, snd_pcm_uframes_t count)
>>> +				  void __user *dst, snd_pcm_uframes_t count,
>>> +				  bool in_kernel)
>>> {
>>> 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
>>> 	count <<= rme32->capture_frlog;
>>> 	pos <<= rme32->capture_frlog;
>>> -	if (copy_to_user_fromio(dst,
>>> +	if (in_kernel)
>>> +		memcpy_fromio((void *)dst,
>>> +			      rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>> +			      count);
>>> +	else if (copy_to_user_fromio(dst,
>>> 			    rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>> 			    count))
>>> 		return -EFAULT;
>>> @@ -1205,8 +1206,7 @@ static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
>>> 	.prepare =	snd_rme32_playback_prepare,
>>> 	.trigger =	snd_rme32_pcm_trigger,
>>> 	.pointer =	snd_rme32_playback_pointer,
>>> -	.copy =		snd_rme32_playback_copy,
>>> -	.silence =	snd_rme32_playback_silence,
>>> +	.copy_silence =	snd_rme32_playback_copy,
>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>> };
>>> 
>>> @@ -1219,7 +1219,7 @@ static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
>>> 	.prepare =	snd_rme32_capture_prepare,
>>> 	.trigger =	snd_rme32_pcm_trigger,
>>> 	.pointer =	snd_rme32_capture_pointer,
>>> -	.copy =		snd_rme32_capture_copy,
>>> +	.copy_silence =	snd_rme32_capture_copy,
>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>> };
>>> 
>>> @@ -1231,8 +1231,7 @@ static const struct snd_pcm_ops snd_rme32_playback_adat_ops = {
>>> 	.prepare =	snd_rme32_playback_prepare,
>>> 	.trigger =	snd_rme32_pcm_trigger,
>>> 	.pointer =	snd_rme32_playback_pointer,
>>> -	.copy =		snd_rme32_playback_copy,
>>> -	.silence =	snd_rme32_playback_silence,
>>> +	.copy_silence =	snd_rme32_playback_copy,
>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>> };
>>> 
>>> @@ -1244,7 +1243,7 @@ static const struct snd_pcm_ops snd_rme32_capture_adat_ops = {
>>> 	.prepare =	snd_rme32_capture_prepare,
>>> 	.trigger =	snd_rme32_pcm_trigger,
>>> 	.pointer =	snd_rme32_capture_pointer,
>>> -	.copy =		snd_rme32_capture_copy,
>>> +	.copy_silence =	snd_rme32_capture_copy,
>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>> };
>>> 
>>> -- 
>>> 2.12.2
>>> 
>>> _______________________________________________
>>> Alsa-devel mailing list
>>> Alsa-devel@alsa-project.org
>>> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>> 
>> -- 
>> ExactCODE GmbH, Lietzenburger Str. 42, DE-10789 Berlin
>> DE Legal: Amtsgericht Berlin (Charlottenburg) HRB 105123B, Tax-ID#: DE251602478
>> Managing Director: René Rebe
>> http://exactcode.com | http://exactscan.com | http://ocrkit.com | http://t2-project.org | http://rene.rebe.de
>>
René Rebe July 18, 2018, 6:43 p.m. UTC | #4
Hi,

On 18 Jul 2018, at 20:10, René Rebe <rene@exactcode.de> wrote:

> Hi,
> 
> On 18 Jul 2018, at 12:56, Takashi Iwai <tiwai@suse.de> wrote:
> 
>> On Wed, 18 Jul 2018 12:22:11 +0200,
>> René Rebe wrote:
>>> 
>>> Hello everyone,
>>> 
>>> to have another digital audio i/o card for our studio / office, I got a pair of RME32 the other week from ebay. (mostly as reference to implement ADAT for the RAD1 Sgi/Octane ALSA driver, …)
>>> 
>>> Unfortunately they do not work with Linux. They are recognised and all the usual devices and /proc/… entries show up, however, the hardware pointer does not move during playback or capture no matter what clock source I choose.
>>> I tried attaching coax s/pdif as well as an 8-channel Behringer Ultragain ADAT source w/ clock.
>> 
>> Does the /proc/asound/card*/rme32 entry show the right setup?
> 
> Well, I think it never showed external clock when I set it to external ADAT, right now, freshly booted (without the ext. ADAT) just trying to play stereo on the s/pdif it shows:

as initial step getting familiar with this thing, I added some prink’s, I get exactly one interrupt each time I start a playback:

$ dmesg 
[    0.000000] rme32 int: 86008004 80000000
[    0.000000] CONFIRM_IRQ
[    0.000000] IRQ_HANDLED
$ grep rme32 /proc/interrupts 
  5:          4    XT-PIC      snd_rme32

hm, … at least some io access and interrupt is happening, ...

> RME Digi32/8 (Rev. 101) at 0xec000000, irq 5 (index #1)
> 
> General settings
>  Half-duplex mode
>  receiver: CS8412
>  format: 16 bit, Stereo
> 
> Input settings
>  input: optical
>  sample rate: no valid signal
> 
> Output settings
>  output signal: normal playback (muted)
>  sample rate: 44100 Hz
>  sample clock source: Internal
>  format: IEC958 (consumer)
>  emphasis: off
> 
> Which should be right for internally word clock’ed s/pdif output. However, aplay does not play anything, after some 20? seconds or so of hanging it even prints:
> 
> aplay: pcm_write:2011: write error: Input/output error
> 
> While it is “trying to playback” /proc shows:
> 
> root@hostname:/proc/asound/card0# grep . pcm*p/sub0/*
> pcm0p/sub0/hw_params:access: RW_INTERLEAVED
> pcm0p/sub0/hw_params:format: S16_LE
> pcm0p/sub0/hw_params:subformat: STD
> pcm0p/sub0/hw_params:channels: 2
> pcm0p/sub0/hw_params:rate: 44100 (44100/1)
> pcm0p/sub0/hw_params:period_size: 2048
> pcm0p/sub0/hw_params:buffer_size: 32768
> pcm0p/sub0/info:card: 0
> pcm0p/sub0/info:device: 0
> pcm0p/sub0/info:subdevice: 0
> pcm0p/sub0/info:stream: PLAYBACK
> pcm0p/sub0/info:id: Digi32 IEC958
> pcm0p/sub0/info:name: Digi32 IEC958
> pcm0p/sub0/info:subname: subdevice #0
> pcm0p/sub0/info:class: 0
> pcm0p/sub0/info:subclass: 0
> pcm0p/sub0/info:subdevices_count: 1
> pcm0p/sub0/info:subdevices_avail: 0
> pcm0p/sub0/status:state: RUNNING
> pcm0p/sub0/status:owner_pid   : 753
> pcm0p/sub0/status:trigger_time: 1195.174967384
> pcm0p/sub0/status:tstamp      : 0.000000000
> pcm0p/sub0/status:delay       : 32768
> pcm0p/sub0/status:avail       : 0
> pcm0p/sub0/status:avail_max   : 30720
> pcm0p/sub0/status:-----
> pcm0p/sub0/status:hw_ptr      : 10243
> pcm0p/sub0/status:appl_ptr    : 43011
> pcm0p/sub0/sw_params:tstamp_mode: NONE
> pcm0p/sub0/sw_params:period_step: 1
> pcm0p/sub0/sw_params:avail_min: 2048
> pcm0p/sub0/sw_params:start_threshold: 32768
> pcm0p/sub0/sw_params:stop_threshold: 32768
> pcm0p/sub0/sw_params:silence_threshold: 0
> pcm0p/sub0/sw_params:silence_size: 0
> pcm0p/sub0/sw_params:boundary: 1073741824
> 
> I tried many kernels, down to 2.6.31, which I had initially on the box.
> So unless some this hwparams never worked, this driver appears to not work a looong looooong time.
> 
> I doubt they are fully defect, as both behave the same and at least output “something” on Windows XP, ...
> 
>> RME32 seems to have only few registers, and it behaves differently for
>> read and write.  Maybe you should try to watch the register 0x20000.
>> The hwptr is the LSB 33 bits.
> 
> Yeah, guess I have to instrument this driver to track what is happening, maybe register not flushed to the hw?
> 
>> Takashi
>> 
>>> The two cards came from the same seller, look ok and both behave the same. 
>>> I went so far to install a Windows XP test install where both cards work “more”.
>>> (They are not perfect in windows, however, at least s/pdif can come out with or with-out external ADAT clock source. However, the digital signal strangely unclean, but that may be a bug in the window system sound device emulation, I only tested with foobar2k and not some Pro audio app which I do not really have).
>>> 
>>> Long story short, does someone still have such a card, or not moving hardware ptr (or missing interrupts?) does ring a bell regarding this RME generation? Instead of a working ADAT reference card, I apparently have two ALSA driver to hack on, … ;-)
>>> 
>>> I tested two different “PC” boards and the results were the same, too.
>>> 
>>> Thanks,
>>> 	René
>>> 
>>> On 11 May 2017, at 23:09, Takashi Iwai <tiwai@suse.de> wrote:
>>> 
>>>> Replace the copy and the silence ops with the new merged ops.
>>>> The conversion is straightforward with standard helper functions.
>>>> 
>>>> Signed-off-by: Takashi Iwai <tiwai@suse.de>
>>>> ---
>>>> sound/pci/rme32.c | 49 ++++++++++++++++++++++++-------------------------
>>>> 1 file changed, 24 insertions(+), 25 deletions(-)
>>>> 
>>>> diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
>>>> index 96d15db65dfd..d2b4a3ef0bd3 100644
>>>> --- a/sound/pci/rme32.c
>>>> +++ b/sound/pci/rme32.c
>>>> @@ -253,41 +253,42 @@ static inline unsigned int snd_rme32_pcm_byteptr(struct rme32 * rme32)
>>>> 		& RME32_RCR_AUDIO_ADDR_MASK);
>>>> }
>>>> 
>>>> -/* silence callback for halfduplex mode */
>>>> -static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
>>>> -				      snd_pcm_uframes_t pos,
>>>> -				      snd_pcm_uframes_t count)
>>>> -{
>>>> -	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
>>>> -	count <<= rme32->playback_frlog;
>>>> -	pos <<= rme32->playback_frlog;
>>>> -	memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
>>>> -	return 0;
>>>> -}
>>>> -
>>>> /* copy callback for halfduplex mode */
>>>> -static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
>>>> +static int snd_rme32_playback_copy(struct snd_pcm_substream *substream,
>>>> +				   int channel,	/* not used (interleaved data) */
>>>> 				   snd_pcm_uframes_t pos,
>>>> -				   void __user *src, snd_pcm_uframes_t count)
>>>> +				   void __user *src, snd_pcm_uframes_t count,
>>>> +				   bool in_kernel)
>>>> {
>>>> 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
>>>> 	count <<= rme32->playback_frlog;
>>>> 	pos <<= rme32->playback_frlog;
>>>> -	if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>>> -			    src, count))
>>>> +	if (!src)
>>>> +		memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
>>>> +	else if (in_kernel)
>>>> +		memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>>> +			    (void *)src, count);
>>>> +	else if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>>> +				     src, count))
>>>> 		return -EFAULT;
>>>> 	return 0;
>>>> }
>>>> 
>>>> /* copy callback for halfduplex mode */
>>>> -static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
>>>> +static int snd_rme32_capture_copy(struct snd_pcm_substream *substream,
>>>> +				  int channel,	/* not used (interleaved data) */
>>>> 				  snd_pcm_uframes_t pos,
>>>> -				  void __user *dst, snd_pcm_uframes_t count)
>>>> +				  void __user *dst, snd_pcm_uframes_t count,
>>>> +				  bool in_kernel)
>>>> {
>>>> 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
>>>> 	count <<= rme32->capture_frlog;
>>>> 	pos <<= rme32->capture_frlog;
>>>> -	if (copy_to_user_fromio(dst,
>>>> +	if (in_kernel)
>>>> +		memcpy_fromio((void *)dst,
>>>> +			      rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>>> +			      count);
>>>> +	else if (copy_to_user_fromio(dst,
>>>> 			    rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>>> 			    count))
>>>> 		return -EFAULT;
>>>> @@ -1205,8 +1206,7 @@ static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
>>>> 	.prepare =	snd_rme32_playback_prepare,
>>>> 	.trigger =	snd_rme32_pcm_trigger,
>>>> 	.pointer =	snd_rme32_playback_pointer,
>>>> -	.copy =		snd_rme32_playback_copy,
>>>> -	.silence =	snd_rme32_playback_silence,
>>>> +	.copy_silence =	snd_rme32_playback_copy,
>>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>>> };
>>>> 
>>>> @@ -1219,7 +1219,7 @@ static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
>>>> 	.prepare =	snd_rme32_capture_prepare,
>>>> 	.trigger =	snd_rme32_pcm_trigger,
>>>> 	.pointer =	snd_rme32_capture_pointer,
>>>> -	.copy =		snd_rme32_capture_copy,
>>>> +	.copy_silence =	snd_rme32_capture_copy,
>>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>>> };
>>>> 
>>>> @@ -1231,8 +1231,7 @@ static const struct snd_pcm_ops snd_rme32_playback_adat_ops = {
>>>> 	.prepare =	snd_rme32_playback_prepare,
>>>> 	.trigger =	snd_rme32_pcm_trigger,
>>>> 	.pointer =	snd_rme32_playback_pointer,
>>>> -	.copy =		snd_rme32_playback_copy,
>>>> -	.silence =	snd_rme32_playback_silence,
>>>> +	.copy_silence =	snd_rme32_playback_copy,
>>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>>> };
>>>> 
>>>> @@ -1244,7 +1243,7 @@ static const struct snd_pcm_ops snd_rme32_capture_adat_ops = {
>>>> 	.prepare =	snd_rme32_capture_prepare,
>>>> 	.trigger =	snd_rme32_pcm_trigger,
>>>> 	.pointer =	snd_rme32_capture_pointer,
>>>> -	.copy =		snd_rme32_capture_copy,
>>>> +	.copy_silence =	snd_rme32_capture_copy,
>>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>>> };
>>>> 
>>>> -- 
>>>> 2.12.2
>>>> 
>>>> _______________________________________________
>>>> Alsa-devel mailing list
>>>> Alsa-devel@alsa-project.org
>>>> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>>> 
>>> -- 
>>> ExactCODE GmbH, Lietzenburger Str. 42, DE-10789 Berlin
>>> DE Legal: Amtsgericht Berlin (Charlottenburg) HRB 105123B, Tax-ID#: DE251602478
>>> Managing Director: René Rebe
>>> http://exactcode.com | http://exactscan.com | http://ocrkit.com | http://t2-project.org | http://rene.rebe.de
>>> 
> 
> -- 
> ExactCODE GmbH, Lietzenburger Str. 42, DE-10789 Berlin
> DE Legal: Amtsgericht Berlin (Charlottenburg) HRB 105123B, Tax-ID#: DE251602478
> Managing Director: René Rebe
> http://exactcode.com | http://exactscan.com | http://ocrkit.com | http://t2-project.org | http://rene.rebe.de
>
René Rebe July 19, 2018, 8:09 a.m. UTC | #5
Hi,

On 18 Jul 2018, at 20:43, René Rebe <rene@exactcode.de> wrote:

> Hi,
> 
> On 18 Jul 2018, at 20:10, René Rebe <rene@exactcode.de> wrote:
> 
>> Hi,
>> 
>> On 18 Jul 2018, at 12:56, Takashi Iwai <tiwai@suse.de> wrote:
>> 
>>> On Wed, 18 Jul 2018 12:22:11 +0200,
>>> René Rebe wrote:
>>>> 
>>>> Hello everyone,
>>>> 
>>>> to have another digital audio i/o card for our studio / office, I got a pair of RME32 the other week from ebay. (mostly as reference to implement ADAT for the RAD1 Sgi/Octane ALSA driver, …)
>>>> 
>>>> Unfortunately they do not work with Linux. They are recognised and all the usual devices and /proc/… entries show up, however, the hardware pointer does not move during playback or capture no matter what clock source I choose.
>>>> I tried attaching coax s/pdif as well as an 8-channel Behringer Ultragain ADAT source w/ clock.
>>> 
>>> Does the /proc/asound/card*/rme32 entry show the right setup?
>> 
>> Well, I think it never showed external clock when I set it to external ADAT, right now, freshly booted (without the ext. ADAT) just trying to play stereo on the s/pdif it shows:
> 
> as initial step getting familiar with this thing, I added some prink’s, I get exactly one interrupt each time I start a playback:
> 
> $ dmesg 
> [    0.000000] rme32 int: 86008004 80000000
> [    0.000000] CONFIRM_IRQ
> [    0.000000] IRQ_HANDLED
> $ grep rme32 /proc/interrupts 
>  5:          4    XT-PIC      snd_rme32

also dumping the read and write control words, I realize:

  rcreg: 6000000
  wcreg: 218

that the error bit is set, …
#define RME32_RCR_ERF       (1 << 26)   /* 1=Error, 0=no Error */

as well as not documented as macro bit 25, … hm, …

Does anyone still happen to have some documentation for this card ;-)?

	René

> hm, … at least some io access and interrupt is happening, ...
> 
>> RME Digi32/8 (Rev. 101) at 0xec000000, irq 5 (index #1)
>> 
>> General settings
>> Half-duplex mode
>> receiver: CS8412
>> format: 16 bit, Stereo
>> 
>> Input settings
>> input: optical
>> sample rate: no valid signal
>> 
>> Output settings
>> output signal: normal playback (muted)
>> sample rate: 44100 Hz
>> sample clock source: Internal
>> format: IEC958 (consumer)
>> emphasis: off
>> 
>> Which should be right for internally word clock’ed s/pdif output. However, aplay does not play anything, after some 20? seconds or so of hanging it even prints:
>> 
>> aplay: pcm_write:2011: write error: Input/output error
>> 
>> While it is “trying to playback” /proc shows:
>> 
>> root@hostname:/proc/asound/card0# grep . pcm*p/sub0/*
>> pcm0p/sub0/hw_params:access: RW_INTERLEAVED
>> pcm0p/sub0/hw_params:format: S16_LE
>> pcm0p/sub0/hw_params:subformat: STD
>> pcm0p/sub0/hw_params:channels: 2
>> pcm0p/sub0/hw_params:rate: 44100 (44100/1)
>> pcm0p/sub0/hw_params:period_size: 2048
>> pcm0p/sub0/hw_params:buffer_size: 32768
>> pcm0p/sub0/info:card: 0
>> pcm0p/sub0/info:device: 0
>> pcm0p/sub0/info:subdevice: 0
>> pcm0p/sub0/info:stream: PLAYBACK
>> pcm0p/sub0/info:id: Digi32 IEC958
>> pcm0p/sub0/info:name: Digi32 IEC958
>> pcm0p/sub0/info:subname: subdevice #0
>> pcm0p/sub0/info:class: 0
>> pcm0p/sub0/info:subclass: 0
>> pcm0p/sub0/info:subdevices_count: 1
>> pcm0p/sub0/info:subdevices_avail: 0
>> pcm0p/sub0/status:state: RUNNING
>> pcm0p/sub0/status:owner_pid   : 753
>> pcm0p/sub0/status:trigger_time: 1195.174967384
>> pcm0p/sub0/status:tstamp      : 0.000000000
>> pcm0p/sub0/status:delay       : 32768
>> pcm0p/sub0/status:avail       : 0
>> pcm0p/sub0/status:avail_max   : 30720
>> pcm0p/sub0/status:-----
>> pcm0p/sub0/status:hw_ptr      : 10243
>> pcm0p/sub0/status:appl_ptr    : 43011
>> pcm0p/sub0/sw_params:tstamp_mode: NONE
>> pcm0p/sub0/sw_params:period_step: 1
>> pcm0p/sub0/sw_params:avail_min: 2048
>> pcm0p/sub0/sw_params:start_threshold: 32768
>> pcm0p/sub0/sw_params:stop_threshold: 32768
>> pcm0p/sub0/sw_params:silence_threshold: 0
>> pcm0p/sub0/sw_params:silence_size: 0
>> pcm0p/sub0/sw_params:boundary: 1073741824
>> 
>> I tried many kernels, down to 2.6.31, which I had initially on the box.
>> So unless some this hwparams never worked, this driver appears to not work a looong looooong time.
>> 
>> I doubt they are fully defect, as both behave the same and at least output “something” on Windows XP, ...
>> 
>>> RME32 seems to have only few registers, and it behaves differently for
>>> read and write.  Maybe you should try to watch the register 0x20000.
>>> The hwptr is the LSB 33 bits.
>> 
>> Yeah, guess I have to instrument this driver to track what is happening, maybe register not flushed to the hw?
>> 
>>> Takashi
>>> 
>>>> The two cards came from the same seller, look ok and both behave the same. 
>>>> I went so far to install a Windows XP test install where both cards work “more”.
>>>> (They are not perfect in windows, however, at least s/pdif can come out with or with-out external ADAT clock source. However, the digital signal strangely unclean, but that may be a bug in the window system sound device emulation, I only tested with foobar2k and not some Pro audio app which I do not really have).
>>>> 
>>>> Long story short, does someone still have such a card, or not moving hardware ptr (or missing interrupts?) does ring a bell regarding this RME generation? Instead of a working ADAT reference card, I apparently have two ALSA driver to hack on, … ;-)
>>>> 
>>>> I tested two different “PC” boards and the results were the same, too.
>>>> 
>>>> Thanks,
>>>> 	René
>>>> 
>>>> On 11 May 2017, at 23:09, Takashi Iwai <tiwai@suse.de> wrote:
>>>> 
>>>>> Replace the copy and the silence ops with the new merged ops.
>>>>> The conversion is straightforward with standard helper functions.
>>>>> 
>>>>> Signed-off-by: Takashi Iwai <tiwai@suse.de>
>>>>> ---
>>>>> sound/pci/rme32.c | 49 ++++++++++++++++++++++++-------------------------
>>>>> 1 file changed, 24 insertions(+), 25 deletions(-)
>>>>> 
>>>>> diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
>>>>> index 96d15db65dfd..d2b4a3ef0bd3 100644
>>>>> --- a/sound/pci/rme32.c
>>>>> +++ b/sound/pci/rme32.c
>>>>> @@ -253,41 +253,42 @@ static inline unsigned int snd_rme32_pcm_byteptr(struct rme32 * rme32)
>>>>> 		& RME32_RCR_AUDIO_ADDR_MASK);
>>>>> }
>>>>> 
>>>>> -/* silence callback for halfduplex mode */
>>>>> -static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
>>>>> -				      snd_pcm_uframes_t pos,
>>>>> -				      snd_pcm_uframes_t count)
>>>>> -{
>>>>> -	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
>>>>> -	count <<= rme32->playback_frlog;
>>>>> -	pos <<= rme32->playback_frlog;
>>>>> -	memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
>>>>> -	return 0;
>>>>> -}
>>>>> -
>>>>> /* copy callback for halfduplex mode */
>>>>> -static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
>>>>> +static int snd_rme32_playback_copy(struct snd_pcm_substream *substream,
>>>>> +				   int channel,	/* not used (interleaved data) */
>>>>> 				   snd_pcm_uframes_t pos,
>>>>> -				   void __user *src, snd_pcm_uframes_t count)
>>>>> +				   void __user *src, snd_pcm_uframes_t count,
>>>>> +				   bool in_kernel)
>>>>> {
>>>>> 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
>>>>> 	count <<= rme32->playback_frlog;
>>>>> 	pos <<= rme32->playback_frlog;
>>>>> -	if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>>>> -			    src, count))
>>>>> +	if (!src)
>>>>> +		memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
>>>>> +	else if (in_kernel)
>>>>> +		memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>>>> +			    (void *)src, count);
>>>>> +	else if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>>>> +				     src, count))
>>>>> 		return -EFAULT;
>>>>> 	return 0;
>>>>> }
>>>>> 
>>>>> /* copy callback for halfduplex mode */
>>>>> -static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
>>>>> +static int snd_rme32_capture_copy(struct snd_pcm_substream *substream,
>>>>> +				  int channel,	/* not used (interleaved data) */
>>>>> 				  snd_pcm_uframes_t pos,
>>>>> -				  void __user *dst, snd_pcm_uframes_t count)
>>>>> +				  void __user *dst, snd_pcm_uframes_t count,
>>>>> +				  bool in_kernel)
>>>>> {
>>>>> 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
>>>>> 	count <<= rme32->capture_frlog;
>>>>> 	pos <<= rme32->capture_frlog;
>>>>> -	if (copy_to_user_fromio(dst,
>>>>> +	if (in_kernel)
>>>>> +		memcpy_fromio((void *)dst,
>>>>> +			      rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>>>> +			      count);
>>>>> +	else if (copy_to_user_fromio(dst,
>>>>> 			    rme32->iobase + RME32_IO_DATA_BUFFER + pos,
>>>>> 			    count))
>>>>> 		return -EFAULT;
>>>>> @@ -1205,8 +1206,7 @@ static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
>>>>> 	.prepare =	snd_rme32_playback_prepare,
>>>>> 	.trigger =	snd_rme32_pcm_trigger,
>>>>> 	.pointer =	snd_rme32_playback_pointer,
>>>>> -	.copy =		snd_rme32_playback_copy,
>>>>> -	.silence =	snd_rme32_playback_silence,
>>>>> +	.copy_silence =	snd_rme32_playback_copy,
>>>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>>>> };
>>>>> 
>>>>> @@ -1219,7 +1219,7 @@ static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
>>>>> 	.prepare =	snd_rme32_capture_prepare,
>>>>> 	.trigger =	snd_rme32_pcm_trigger,
>>>>> 	.pointer =	snd_rme32_capture_pointer,
>>>>> -	.copy =		snd_rme32_capture_copy,
>>>>> +	.copy_silence =	snd_rme32_capture_copy,
>>>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>>>> };
>>>>> 
>>>>> @@ -1231,8 +1231,7 @@ static const struct snd_pcm_ops snd_rme32_playback_adat_ops = {
>>>>> 	.prepare =	snd_rme32_playback_prepare,
>>>>> 	.trigger =	snd_rme32_pcm_trigger,
>>>>> 	.pointer =	snd_rme32_playback_pointer,
>>>>> -	.copy =		snd_rme32_playback_copy,
>>>>> -	.silence =	snd_rme32_playback_silence,
>>>>> +	.copy_silence =	snd_rme32_playback_copy,
>>>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>>>> };
>>>>> 
>>>>> @@ -1244,7 +1243,7 @@ static const struct snd_pcm_ops snd_rme32_capture_adat_ops = {
>>>>> 	.prepare =	snd_rme32_capture_prepare,
>>>>> 	.trigger =	snd_rme32_pcm_trigger,
>>>>> 	.pointer =	snd_rme32_capture_pointer,
>>>>> -	.copy =		snd_rme32_capture_copy,
>>>>> +	.copy_silence =	snd_rme32_capture_copy,
>>>>> 	.mmap =		snd_pcm_lib_mmap_iomem,
>>>>> };
>>>>> 
>>>>> -- 
>>>>> 2.12.2
>>>>> 
>>>>> _______________________________________________
>>>>> Alsa-devel mailing list
>>>>> Alsa-devel@alsa-project.org
>>>>> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>>>> 
>>>> -- 
>>>> ExactCODE GmbH, Lietzenburger Str. 42, DE-10789 Berlin
>>>> DE Legal: Amtsgericht Berlin (Charlottenburg) HRB 105123B, Tax-ID#: DE251602478
>>>> Managing Director: René Rebe
>>>> http://exactcode.com | http://exactscan.com | http://ocrkit.com | http://t2-project.org | http://rene.rebe.de
>>>> 
>> 
>> -- 
>> ExactCODE GmbH, Lietzenburger Str. 42, DE-10789 Berlin
>> DE Legal: Amtsgericht Berlin (Charlottenburg) HRB 105123B, Tax-ID#: DE251602478
>> Managing Director: René Rebe
>> http://exactcode.com | http://exactscan.com | http://ocrkit.com | http://t2-project.org | http://rene.rebe.de
>> 
> 
> -- 
> ExactCODE GmbH, Lietzenburger Str. 42, DE-10789 Berlin
> DE Legal: Amtsgericht Berlin (Charlottenburg) HRB 105123B, Tax-ID#: DE251602478
> Managing Director: René Rebe
> http://exactcode.com | http://exactscan.com | http://ocrkit.com | http://t2-project.org | http://rene.rebe.de
>
René Rebe March 2, 2019, 5:19 p.m. UTC | #6
Hi Takashi,

On 18 Jul 2018, at 12:56, Takashi Iwai <tiwai@suse.de> wrote:

>> to have another digital audio i/o card for our studio / office, I got a pair of RME32 the other week from ebay. (mostly as reference to implement ADAT for the RAD1 Sgi/Octane ALSA driver, …)
>> 
>> Unfortunately they do not work with Linux. They are recognised and all the usual devices and /proc/… entries show up, however, the hardware pointer does not move during playback or capture no matter what clock source I choose.
>> I tried attaching coax s/pdif as well as an 8-channel Behringer Ultragain ADAT source w/ clock.
> 
> Does the /proc/asound/card*/rme32 entry show the right setup?
> RME32 seems to have only few registers, and it behaves differently for
> read and write.  Maybe you should try to watch the register 0x20000.
> The hwptr is the LSB 33 bits.


thank you again for your reply, and I finally took a deep breath to "shortly"
take a closer look at this again. So with current linux-kernel :HEAD nothing
has changed, and I get one IRQ when the playback starts.

As I do not have the spec I did a quick hack and commented out the IRQ
acknowledge around rme32.c line 829:

-                writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ);

surprisingly this “works” in that I have more than the first frame coming out of
the optical fibre, aka quite constant running pcm stream.

"Quite constant” because I think I sometimes (rarely) here some samples
getting lost, probably because the IRQ keeps firing as it was not acknowledged.
However, I’m surprised this works 99%ish anyway.

Of course the questions is now to actually properly fix this, as acknowledging
this IRQ somehow makes the card stop entirely, I guess, somehow? At least
that is how it looks like. Maybe you have an idea? Maybe some ALSA stream
helper refactoring broke something a decade ago?

I should probably also mention that this works with aplay, while with sox’s
“play” this hack is somehow producing constant under-run’s:

In:85.6% 00:03:18.02 [00:00:33.35] Out:8.73M [!=====|=====!] Hd:0.0 Clip:0    play WARN alsa: under-run
play WARN alsa: under-run
play WARN alsa: under-run

which might be an indication of some stream pointer helper glue not being wired up correctly anymore (I tested some seriously old version the last time, like 2.6.29’ish, so this might be broken since over a decade or so already)?

Thanks again for any tips, as otherwise I might spend many more hours trying to understand the FPGA and ALSA API.

Have a nice weekend,
	René
diff mbox

Patch

diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index 96d15db65dfd..d2b4a3ef0bd3 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -253,41 +253,42 @@  static inline unsigned int snd_rme32_pcm_byteptr(struct rme32 * rme32)
 		& RME32_RCR_AUDIO_ADDR_MASK);
 }
 
-/* silence callback for halfduplex mode */
-static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
-				      snd_pcm_uframes_t pos,
-				      snd_pcm_uframes_t count)
-{
-	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
-	count <<= rme32->playback_frlog;
-	pos <<= rme32->playback_frlog;
-	memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
-	return 0;
-}
-
 /* copy callback for halfduplex mode */
-static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
+static int snd_rme32_playback_copy(struct snd_pcm_substream *substream,
+				   int channel,	/* not used (interleaved data) */
 				   snd_pcm_uframes_t pos,
-				   void __user *src, snd_pcm_uframes_t count)
+				   void __user *src, snd_pcm_uframes_t count,
+				   bool in_kernel)
 {
 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
 	count <<= rme32->playback_frlog;
 	pos <<= rme32->playback_frlog;
-	if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
-			    src, count))
+	if (!src)
+		memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
+	else if (in_kernel)
+		memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
+			    (void *)src, count);
+	else if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
+				     src, count))
 		return -EFAULT;
 	return 0;
 }
 
 /* copy callback for halfduplex mode */
-static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int channel,	/* not used (interleaved data) */
+static int snd_rme32_capture_copy(struct snd_pcm_substream *substream,
+				  int channel,	/* not used (interleaved data) */
 				  snd_pcm_uframes_t pos,
-				  void __user *dst, snd_pcm_uframes_t count)
+				  void __user *dst, snd_pcm_uframes_t count,
+				  bool in_kernel)
 {
 	struct rme32 *rme32 = snd_pcm_substream_chip(substream);
 	count <<= rme32->capture_frlog;
 	pos <<= rme32->capture_frlog;
-	if (copy_to_user_fromio(dst,
+	if (in_kernel)
+		memcpy_fromio((void *)dst,
+			      rme32->iobase + RME32_IO_DATA_BUFFER + pos,
+			      count);
+	else if (copy_to_user_fromio(dst,
 			    rme32->iobase + RME32_IO_DATA_BUFFER + pos,
 			    count))
 		return -EFAULT;
@@ -1205,8 +1206,7 @@  static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
 	.prepare =	snd_rme32_playback_prepare,
 	.trigger =	snd_rme32_pcm_trigger,
 	.pointer =	snd_rme32_playback_pointer,
-	.copy =		snd_rme32_playback_copy,
-	.silence =	snd_rme32_playback_silence,
+	.copy_silence =	snd_rme32_playback_copy,
 	.mmap =		snd_pcm_lib_mmap_iomem,
 };
 
@@ -1219,7 +1219,7 @@  static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
 	.prepare =	snd_rme32_capture_prepare,
 	.trigger =	snd_rme32_pcm_trigger,
 	.pointer =	snd_rme32_capture_pointer,
-	.copy =		snd_rme32_capture_copy,
+	.copy_silence =	snd_rme32_capture_copy,
 	.mmap =		snd_pcm_lib_mmap_iomem,
 };
 
@@ -1231,8 +1231,7 @@  static const struct snd_pcm_ops snd_rme32_playback_adat_ops = {
 	.prepare =	snd_rme32_playback_prepare,
 	.trigger =	snd_rme32_pcm_trigger,
 	.pointer =	snd_rme32_playback_pointer,
-	.copy =		snd_rme32_playback_copy,
-	.silence =	snd_rme32_playback_silence,
+	.copy_silence =	snd_rme32_playback_copy,
 	.mmap =		snd_pcm_lib_mmap_iomem,
 };
 
@@ -1244,7 +1243,7 @@  static const struct snd_pcm_ops snd_rme32_capture_adat_ops = {
 	.prepare =	snd_rme32_capture_prepare,
 	.trigger =	snd_rme32_pcm_trigger,
 	.pointer =	snd_rme32_capture_pointer,
-	.copy =		snd_rme32_capture_copy,
+	.copy_silence =	snd_rme32_capture_copy,
 	.mmap =		snd_pcm_lib_mmap_iomem,
 };