Trying to write Maestro2 driver ...

This is probably lame question but I’m really newbie with this stuff…

I got impression from DDK docs that is would be pretty easy to get mixer
part working if a card has AC97 compliant mixer. Basically, it suggests
that as soon ad I provide ‘code_write’ and ‘codec_read’ it would allow
me to control volume.

I tried to take template driver, substitute PCI ids with proper values
and give it those functions. It compiled and io-audio started, but
/dev/snd is empty and mixer does not work of course. What do I miss? I
thought AC97 DLL would take care of everything? Or do I still need to
mess with mixer groups?

Also, when I started it first time there was a thread in INTR state, but
that’s as much as I got to know before machine locked up for good (mouse
pointer did not move and keyboard was ignored). Next time I started it
there was no thread in INTR state and machine did not lock. Weird…

  • igor

I got impression from DDK docs that is would be pretty easy to get mixer
part working if a card has AC97 compliant mixer. Basically, it suggests
that as soon ad I provide ‘code_write’ and ‘codec_read’ it would allow
me to control volume.

Correct, but those functions need to work correctly or the
AC97 DLL may fail in interesting ways. Before loading the
mixer dll try reading some codec registers (7c & 7e are the
vendor ids) and then try writing and reading some registers
(02 is master volume). The ac97 dll expects to be able to
write a register and then read it back, this is how some the
ac97 optional bits are determined.

I tried to take template driver, substitute PCI ids with proper values
and give it those functions. It compiled and io-audio started, but
/dev/snd is empty and mixer does not work of course. What do I miss? I
thought AC97 DLL would take care of everything? Or do I still need to
mess with mixer groups?

The AC97 dll will handle all mixer functionality, with just
the read & write functions, but if the read returns the
wrong value for register 26 powerdown the dll will never be
able to bring the AC97 out of powerdown and will return an
error. As you can see from the template code if the
mixer_dll returns an error the “ctrl_init” returns -1 and
io-audio unmounts the card because it didn’t initialize
correctly. If the mixer_dll returns in error it will also
send a message to slogger why it failed.

Also, when I started it first time there was a thread in INTR state, but
that’s as much as I got to know before machine locked up for good (mouse
pointer did not move and keyboard was ignored). Next time I started it
there was no thread in INTR state and machine did not lock. Weird…

The thread in the INTR state is the card’s interrupt handler
thread. It uses the “InterruptWait” technique to run your
handler. It was started by the “ado_interrupt_attach” call
on line 262 of the template. You could comment out this line
until until you need to handle interrupts.

“Audio Support” <audio_support@qnx.com> wrote in message
news:9k4cmd$gjf$1@nntp.qnx.com

I got impression from DDK docs that is would be pretty easy to get mixer
part working if a card has AC97 compliant mixer. Basically, it suggests
that as soon ad I provide ‘code_write’ and ‘codec_read’ it would allow
me to control volume.

Correct, but those functions need to work correctly or the
AC97 DLL may fail in interesting ways. Before loading the
mixer dll try reading some codec registers (7c & 7e are the
vendor ids) and then try writing and reading some registers
(02 is master volume). The ac97 dll expects to be able to
write a register and then read it back, this is how some the
ac97 optional bits are determined.

Thanks, I’ll try that. Meanwhile I tried to understand the PCM part, which
is much more fun. I’d be nice if someone clarified few things:

  1. The VIA driver appears to be strange wrt DMA memory allocations. It does
    ado_shm_alloc(DMA_SAFE) and then it does bunch of mmap(MAP_ANON|MAP_PHYS)
    with total size matching the adm_shm_alloc. The addresses returned from
    mmap() are then apparently programmed into DMA engine. What confuses me is
    what the ado_shmem_alloc() is there for? The address it returns does not
    seem to be used anyhow and memory it refers to does not correspond to areas
    used by DMA… Other examples only use ado_shm_alloc() and pass that address
    into DMA engine.

  2. Why do we need both ado_shm_alloc() and ado_mmap() ?

  3. Is there any way to restrict addresses returned by mmap() other than
    MAP_16M? The Maestro can only use 28bits for DMA, that is 256Mb. Using
    MAP_16M would be unnecessary restrictive. Many other chips have similar
    restrictions, so it is common issues with drivers.

  4. The Maestro’s wavecache also can only fetch data from single memory space
    of 4Mb size, which means all allocations must be within 4Mb of each other. I
    suspect this is also common issue. Are there any zone-based allocators or
    everyone has to come up with his own? Maestro also complicates the issue by
    being capable to support multiple hardware subchannels.

The AC97 dll will handle all mixer functionality, with just
the read & write functions, but if the read returns the
wrong value for register 26 powerdown the dll will never be
able to bring the AC97 out of powerdown and will return an
error. As you can see from the template code if the
mixer_dll returns an error the “ctrl_init” returns -1 and
io-audio unmounts the card because it didn’t initialize
correctly. If the mixer_dll returns in error it will also
send a message to slogger why it failed.

Perhaps the chip needs to be initialized (reset) first by crtl_init()? The
Maestro also has internal programmable timer interrupt and it needs to be
programmed to generate interrupts at proper rate for given playback/capture
parameters. I could not have picked an easier chip for learning :wink:

The thread in the INTR state is the card’s interrupt handler
thread. It uses the “InterruptWait” technique to run your
handler. It was started by the “ado_interrupt_attach” call
on line 262 of the template. You could comment out this line
until until you need to handle interrupts.

Driver still locks up the machine, even without attaching to interrupt.

Thanks,

  • Igor

“Audio Support” <audio_support@qnx.com> wrote in message
news:9k4cmd$gjf$1@nntp.qnx.com

I got impression from DDK docs that is would be pretty easy to get mixer
part working if a card has AC97 compliant mixer. Basically, it suggests
that as soon ad I provide ‘code_write’ and ‘codec_read’ it would allow
me to control volume.

Correct, but those functions need to work correctly or the
AC97 DLL may fail in interesting ways. Before loading the
mixer dll try reading some codec registers (7c & 7e are the
vendor ids) and then try writing and reading some registers
(02 is master volume). The ac97 dll expects to be able to
write a register and then read it back, this is how some the
ac97 optional bits are determined.

This was good hint, I tried ans figured functions did not work right. It
turned out, linuxish outb/outw take same arguments as QNX out16/out8 but in
opposite order :wink:

Now I have mixer part ‘half working’. The DLL loads fine and all stuff in
/dev/snd gets created. I can run Mixer and it allows me to change volume. I
verified it can write it and read back. That however takes 100% CPU because
codec read/write funcs need to busy wait for bus to be free. Mixer tells it
is ‘Generic AC’97 0x00000000’. What is that number?

Now real trouble is, clicking on ‘mute’ and ‘select’ checkboxes has no
effect at all, except for load of ‘ac97: timeout’ messages in syslog which
codec_wait() writes when bus was still busy after loop ended. All channels
are unmuted and unselected when mixer starts, except for MIC input which is
selected. Feeding signals to line-in or MIC does not seem to have any
effect.

For codec_wait() ALSA code uses simple loop on counter (to 100000), I also
tried using nanospin_xx which helped with CPU utilisation but not with
timeouts. Using nanospin also leads to timeout messages on startup, although
I still can read vendor ids (7c is 414bh and 7e is 4d00h) and read/write
volume.

Also it looks like chip indeed has to be initialized properly or driver
works only once per boot (if you slay it next time no devices will be
created). Initialisation code seems to work fine now.

Any ideas what could be wrong?
Thanks,

  • Igor

Thanks, I’ll try that. Meanwhile I tried to understand the PCM part, which
is much more fun. I’d be nice if someone clarified few things:

  1. The VIA driver appears to be strange wrt DMA memory allocations. It does
    ado_shm_alloc(DMA_SAFE) and then it does bunch of mmap(MAP_ANON|MAP_PHYS)
    with total size matching the adm_shm_alloc. The addresses returned from
    mmap() are then apparently programmed into DMA engine. What confuses me is
    what the ado_shmem_alloc() is there for? The address it returns does not
    seem to be used anyhow and memory it refers to does not correspond to areas
    used by DMA… Other examples only use ado_shm_alloc() and pass that address
    into DMA engine.

It’s differences in how the DMA engines work, the via engine
needs a linear array of buffer descriptors that contain
flags, sizes and the physical addresses of the dma buffer to
be played. So the DMA buffer for data is alloacted with
ado_shm_alloc and the descriptors (which are private) are
alloacted with a mmap. I suppose we should have a
ado_phys_alloc_private() but it would be a direct cover for
mmap.


  1. Why do we need both ado_shm_alloc() and ado_mmap() ?

the ado_shm_alloc() allocates a shared memory region and is
mostly used for the dma buffer so it can be shared. The
ado_shm_mmap() is for cards where the dma buffer is in
on-board memory, it mmaps the card memory and makes the
region shared without allocating it.


  1. Is there any way to restrict addresses returned by mmap() other than
    MAP_16M? The Maestro can only use 28bits for DMA, that is 256Mb. Using
    MAP_16M would be unnecessary restrictive. Many other chips have similar
    restrictions, so it is common issues with drivers.

Not yet, but you could ask for timelines in the os group.


  1. The Maestro’s wavecache also can only fetch data from single memory space
    of 4Mb size, which means all allocations must be within 4Mb of each other. I
    suspect this is also common issue. Are there any zone-based allocators or
    everyone has to come up with his own? Maestro also complicates the issue by
    being capable to support multiple hardware subchannels.

Perhaps the chip needs to be initialized (reset) first by crtl_init()? The
Maestro also has internal programmable timer interrupt and it needs to be
programmed to generate interrupts at proper rate for given playback/capture
parameters. I could not have picked an easier chip for learning > :wink:

Yes, thats the reason we don’t have a driver, full
documentation was not availiable.

The thread in the INTR state is the card’s interrupt handler
thread. It uses the “InterruptWait” technique to run your
handler. It was started by the “ado_interrupt_attach” call
on line 262 of the template. You could comment out this line
until until you need to handle interrupts.

Driver still locks up the machine, even without attaching to interrupt.

Without the interrupt attach the only code touching hw
should be the init and the codec_read & write_routines.
You’ll have to try to isolate whats causing the lockup by
commenting out these bits until it’s stable.

“Igor Kovalenko” <kovalenko@home.com> wrote in message
news:9k8dqk$45k$1@inn.qnx.com

Now I have mixer part ‘half working’. The DLL loads fine and all stuff in
/dev/snd gets created. I can run Mixer and it allows me to change volume.
I
verified it can write it and read back. That however takes 100% CPU
because
codec read/write funcs need to busy wait for bus to be free. Mixer tells
it
is ‘Generic AC’97 0x00000000’. What is that number?

Now real trouble is, clicking on ‘mute’ and ‘select’ checkboxes has no
effect at all, except for load of ‘ac97: timeout’ messages in syslog which
codec_wait() writes when bus was still busy after loop ended. All channels
are unmuted and unselected when mixer starts, except for MIC input which
is
selected. Feeding signals to line-in or MIC does not seem to have any
effect.

For codec_wait() ALSA code uses simple loop on counter (to 100000), I also
tried using nanospin_xx which helped with CPU utilisation but not with
timeouts. Using nanospin also leads to timeout messages on startup,
although
I still can read vendor ids (7c is 414bh and 7e is 4d00h) and read/write
volume.

Some more digging shows that codec_wait() works fine before AC97 DLL is
loaded and it also works fine first 7 times after it is loaded (always). The
8th and all subsequent invocations always give ‘timeout’.

The codec_wait() gets called once from codec_write() and twice from
codec_read(). Looks to me like AC97 DLL is doing something what causes
permanent ‘busy’ state on AC97 bus at 7th invocation… Any ideas?

Also, is there any way for a thread to tell if interrupts are currently
enabled or disabled? Linux code uses spin_lock_irq_save &
spin_lock_irq_restore to guard access to codec. Those functions
acquire/release spinlock while disabling interrupts and then restore
interrupts state to whatever it was before. Do I need to simulate this with
QNX DDK? And how? Would InterruptLock/Unlock be an equivalent?

Thanks,

  • Igor

“Igor Kovalenko” <kovalenko@home.com> wrote in message
news:9ka9o1$96h$1@inn.qnx.com

“Igor Kovalenko” <> kovalenko@home.com> > wrote in message
news:9k8dqk$45k$> 1@inn.qnx.com> …

Now I have mixer part ‘half working’. The DLL loads fine and all stuff
in
/dev/snd gets created. I can run Mixer and it allows me to change
volume.
I
verified it can write it and read back. That however takes 100% CPU
because
codec read/write funcs need to busy wait for bus to be free. Mixer tells
it
is ‘Generic AC’97 0x00000000’. What is that number?

To answer myself, that means DLL did not read useful vendor id and revision
because codec bus is stuck in busy state.

Some more digging shows that codec_wait() works fine before AC97 DLL is
loaded and it also works fine first 7 times after it is loaded (always).
The
8th and all subsequent invocations always give ‘timeout’.

The codec_wait() gets called once from codec_write() and twice from
codec_read(). Looks to me like AC97 DLL is doing something what causes
permanent ‘busy’ state on AC97 bus at 7th invocation… Any ideas?

Okay, I looked into what AC97 DLL is doing by dumping all read/write calls
into syslog. I was right, the trouble was with reading registers 0x28
(AC97_EXTENDED_ID) and 0x4 (AC97_HEADPHONE). The first one is extended
register and second is optional. Any attempt to read them leaves AC97 bus in
busy state and that can only be fixed by AC97_RESET.

Once I started to bypass those (by checking values in codec_read) everything
magically started to work. The codec is identified as ‘Asahi Kasei 45xx’ and
mixer behaves well. I tried to listen to an external signal and it was fine.
The only features which do not work are ‘bass boost’ and ‘simulated stereo’
in ‘advanced’ section. The DLL was toggling AC97_GENERAL_PURPOSE (0x20) for
them, but if I closed and opened ‘advanced’ dialog those options were
cleared again. The ‘loopback’ and ‘mic gain’ appear to work. There was one
occasion though when unclicking ‘loopback’ locked the machine and almost
every attempt to play with ‘advanced’ stuff lead to one of io-audio threads
running READY forever (thankfully, with priority 5). I did not catch yet
what exactly caused the runaway affect.

I did not look into specs yet to figure if reading 0x28 and 0x4 can be
unsupported. Perhaps it is just my brain dead hardware (Micron laptop), but
in any case it looks like AC97 DLL has compatibility issue. It would be nice
to have ability to pass it an array of registers which it should not touch.
Filtering them in codec_read/write funcs is yucky…

  • Igor