Commit 3e5f1270 authored by Dave Jones's avatar Dave Jones Committed by Linus Torvalds

[PATCH] OSS rme96xx update.

Lingering around for a while. 2.4 forward port.
parent 1b5f52b3
Beta release of the rme96xx (driver for RME 96XX cards like the
"Hammerfall" and the "Hammerfall light")
Important: The driver module has to be installed on a freshly rebooted system,
otherwise the driver might not be able to acquire its buffers.
features:
- OSS programming interface (i.e. runs with standard OSS soundsoftware)
- OSS/Multichannel interface (OSS multichannel is done by just aquiring
more than 2 channels). The driver does not use more than one device
( yet .. this feature may be implemented later )
- more than one RME card supported
The driver uses a specific multichannel interface, which I will document
when the driver gets stable. (take a look at the defines in rme96xx.h,
which adds blocked multichannel formats i.e instead of
lrlrlrlr --> llllrrrr etc.
Use the "rmectrl" programm to look at the status of the card ..
or use xrmectrl, a GUI interface for the ctrl program.
What you can do with the rmectrl program is to set the stereo device for
OSS emulation (e.g. if you use SPDIF out).
You do:
./ctrl offset 24 24
which makes the stereo device use channels 25 and 26.
Guenter Geiger <geiger@epy.co.at>
copy the first part of the attached source code into rmectrl.c
and the second part into xrmectrl (or get the program from
http://gige.xdv.org/pages/soft/pages/rme)
to compile: gcc -o rmectrl rmectrl.c
------------------------------ snip ------------------------------------
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/soundcard.h>
#include <math.h>
#include <unistd.h>
#include <stdlib.h>
#include "rme96xx.h"
/*
remctrl.c
(C) 2000 Guenter Geiger <geiger@debian.org>
HP20020201 - Heiko Purnhagen <purnhage@tnt.uni-hannover.de>
*/
/* # define DEVICE_NAME "/dev/mixer" */
# define DEVICE_NAME "/dev/mixer1"
void usage(void)
{
fprintf(stderr,"usage: rmectrl [/dev/mixer<n>] [command [options]]\n\n");
fprintf(stderr,"where command is one of:\n");
fprintf(stderr," help show this help\n");
fprintf(stderr," status show status bits\n");
fprintf(stderr," control show control bits\n");
fprintf(stderr," mix show mixer/offset status\n");
fprintf(stderr," master <n> set sync master\n");
fprintf(stderr," pro <n> set spdif out pro\n");
fprintf(stderr," emphasis <n> set spdif out emphasis\n");
fprintf(stderr," dolby <n> set spdif out no audio\n");
fprintf(stderr," optout <n> set spdif out optical\n");
fprintf(stderr," wordclock <n> set sync wordclock\n");
fprintf(stderr," spdifin <n> set spdif in (0=optical,1=coax,2=intern)\n");
fprintf(stderr," syncref <n> set sync source (0=ADAT1,1=ADAT2,2=ADAT3,3=SPDIF)\n");
fprintf(stderr," adat1cd <n> set ADAT1 on internal CD\n");
fprintf(stderr," offset <devnr> <in> <out> set dev (0..3) offset (0..25)\n");
exit(-1);
}
int main(int argc, char* argv[])
{
int cards;
int ret;
int i;
double ft;
int fd, fdwr;
int param,orig;
rme_status_t stat;
rme_ctrl_t ctrl;
char *device;
int argidx;
if (argc < 2)
usage();
if (*argv[1]=='/') {
device = argv[1];
argidx = 2;
}
else {
device = DEVICE_NAME;
argidx = 1;
}
fprintf(stdout,"mixer device %s\n",device);
if ((fd = open(device,O_RDONLY)) < 0) {
fprintf(stdout,"opening device failed\n");
exit(-1);
}
if ((fdwr = open(device,O_WRONLY)) < 0) {
fprintf(stdout,"opening device failed\n");
exit(-1);
}
if (argc < argidx+1)
usage();
if (!strcmp(argv[argidx],"help"))
usage();
if (!strcmp(argv[argidx],"-h"))
usage();
if (!strcmp(argv[argidx],"--help"))
usage();
if (!strcmp(argv[argidx],"status")) {
ioctl(fd,SOUND_MIXER_PRIVATE2,&stat);
fprintf(stdout,"stat.irq %d\n",stat.irq);
fprintf(stdout,"stat.lockmask %d\n",stat.lockmask);
fprintf(stdout,"stat.sr48 %d\n",stat.sr48);
fprintf(stdout,"stat.wclock %d\n",stat.wclock);
fprintf(stdout,"stat.bufpoint %d\n",stat.bufpoint);
fprintf(stdout,"stat.syncmask %d\n",stat.syncmask);
fprintf(stdout,"stat.doublespeed %d\n",stat.doublespeed);
fprintf(stdout,"stat.tc_busy %d\n",stat.tc_busy);
fprintf(stdout,"stat.tc_out %d\n",stat.tc_out);
fprintf(stdout,"stat.crystalrate %d (0=64k 3=96k 4=88.2k 5=48k 6=44.1k 7=32k)\n",stat.crystalrate);
fprintf(stdout,"stat.spdif_error %d\n",stat.spdif_error);
fprintf(stdout,"stat.bufid %d\n",stat.bufid);
fprintf(stdout,"stat.tc_valid %d\n",stat.tc_valid);
exit (0);
}
if (!strcmp(argv[argidx],"control")) {
ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl);
fprintf(stdout,"ctrl.start %d\n",ctrl.start);
fprintf(stdout,"ctrl.latency %d (0=64 .. 7=8192)\n",ctrl.latency);
fprintf(stdout,"ctrl.master %d\n",ctrl.master);
fprintf(stdout,"ctrl.ie %d\n",ctrl.ie);
fprintf(stdout,"ctrl.sr48 %d\n",ctrl.sr48);
fprintf(stdout,"ctrl.spare %d\n",ctrl.spare);
fprintf(stdout,"ctrl.doublespeed %d\n",ctrl.doublespeed);
fprintf(stdout,"ctrl.pro %d\n",ctrl.pro);
fprintf(stdout,"ctrl.emphasis %d\n",ctrl.emphasis);
fprintf(stdout,"ctrl.dolby %d\n",ctrl.dolby);
fprintf(stdout,"ctrl.opt_out %d\n",ctrl.opt_out);
fprintf(stdout,"ctrl.wordclock %d\n",ctrl.wordclock);
fprintf(stdout,"ctrl.spdif_in %d (0=optical,1=coax,2=intern)\n",ctrl.spdif_in);
fprintf(stdout,"ctrl.sync_ref %d (0=ADAT1,1=ADAT2,2=ADAT3,3=SPDIF)\n",ctrl.sync_ref);
fprintf(stdout,"ctrl.spdif_reset %d\n",ctrl.spdif_reset);
fprintf(stdout,"ctrl.spdif_select %d\n",ctrl.spdif_select);
fprintf(stdout,"ctrl.spdif_clock %d\n",ctrl.spdif_clock);
fprintf(stdout,"ctrl.spdif_write %d\n",ctrl.spdif_write);
fprintf(stdout,"ctrl.adat1_cd %d\n",ctrl.adat1_cd);
exit (0);
}
if (!strcmp(argv[argidx],"mix")) {
rme_mixer mix;
int i;
for (i=0; i<4; i++) {
mix.devnr = i;
ioctl(fd,SOUND_MIXER_PRIVATE1,&mix);
if (mix.devnr == i) {
fprintf(stdout,"devnr %d\n",mix.devnr);
fprintf(stdout,"mix.i_offset %2d (0-25)\n",mix.i_offset);
fprintf(stdout,"mix.o_offset %2d (0-25)\n",mix.o_offset);
}
}
exit (0);
}
/* the control flags */
if (argc < argidx+2)
usage();
if (!strcmp(argv[argidx],"master")) {
int val = atoi(argv[argidx+1]);
ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl);
printf("master = %d\n",val);
ctrl.master = val;
ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl);
exit (0);
}
if (!strcmp(argv[argidx],"pro")) {
int val = atoi(argv[argidx+1]);
ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl);
printf("pro = %d\n",val);
ctrl.pro = val;
ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl);
exit (0);
}
if (!strcmp(argv[argidx],"emphasis")) {
int val = atoi(argv[argidx+1]);
ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl);
printf("emphasis = %d\n",val);
ctrl.emphasis = val;
ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl);
exit (0);
}
if (!strcmp(argv[argidx],"dolby")) {
int val = atoi(argv[argidx+1]);
ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl);
printf("dolby = %d\n",val);
ctrl.dolby = val;
ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl);
exit (0);
}
if (!strcmp(argv[argidx],"optout")) {
int val = atoi(argv[argidx+1]);
ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl);
printf("optout = %d\n",val);
ctrl.opt_out = val;
ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl);
exit (0);
}
if (!strcmp(argv[argidx],"wordclock")) {
int val = atoi(argv[argidx+1]);
ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl);
printf("wordclock = %d\n",val);
ctrl.wordclock = val;
ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl);
exit (0);
}
if (!strcmp(argv[argidx],"spdifin")) {
int val = atoi(argv[argidx+1]);
ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl);
printf("spdifin = %d\n",val);
ctrl.spdif_in = val;
ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl);
exit (0);
}
if (!strcmp(argv[argidx],"syncref")) {
int val = atoi(argv[argidx+1]);
ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl);
printf("syncref = %d\n",val);
ctrl.sync_ref = val;
ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl);
exit (0);
}
if (!strcmp(argv[argidx],"adat1cd")) {
int val = atoi(argv[argidx+1]);
ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl);
printf("adat1cd = %d\n",val);
ctrl.adat1_cd = val;
ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl);
exit (0);
}
/* setting offset */
if (argc < argidx+4)
usage();
if (!strcmp(argv[argidx],"offset")) {
rme_mixer mix;
mix.devnr = atoi(argv[argidx+1]);
mix.i_offset = atoi(argv[argidx+2]);
mix.o_offset = atoi(argv[argidx+3]);
ioctl(fdwr,SOUND_MIXER_PRIVATE1,&mix);
fprintf(stdout,"devnr %d\n",mix.devnr);
fprintf(stdout,"mix.i_offset to %d\n",mix.i_offset);
fprintf(stdout,"mix.o_offset to %d\n",mix.o_offset);
exit (0);
}
usage();
exit (0); /* to avoid warning */
}
---------------------------- <snip> --------------------------------
#!/usr/bin/wish
# xrmectrl
# (C) 2000 Guenter Geiger <geiger@debian.org>
# HP20020201 - Heiko Purnhagen <purnhage@tnt.uni-hannover.de>
#set defaults "-relief ridged"
set CTRLPROG "./rmectrl"
if {$argc} {
set CTRLPROG "$CTRLPROG $argv"
}
puts "CTRLPROG $CTRLPROG"
frame .butts
button .butts.exit -text "Exit" -command "exit" -relief ridge
#button .butts.state -text "State" -command "get_all"
pack .butts.exit -side left
pack .butts -side bottom
#
# STATUS
#
frame .status
# Sampling Rate
frame .status.sr
label .status.sr.text -text "Sampling Rate" -justify left
radiobutton .status.sr.441 -selectcolor red -text "44.1 kHz" -width 10 -anchor nw -variable srate -value 44100 -font times
radiobutton .status.sr.480 -selectcolor red -text "48 kHz" -width 10 -anchor nw -variable srate -value 48000 -font times
radiobutton .status.sr.882 -selectcolor red -text "88.2 kHz" -width 10 -anchor nw -variable srate -value 88200 -font times
radiobutton .status.sr.960 -selectcolor red -text "96 kHz" -width 10 -anchor nw -variable srate -value 96000 -font times
pack .status.sr.text .status.sr.441 .status.sr.480 .status.sr.882 .status.sr.960 -side top -padx 3
# Lock
frame .status.lock
label .status.lock.text -text "Lock" -justify left
checkbutton .status.lock.adat1 -selectcolor red -text "ADAT1" -anchor nw -width 10 -variable adatlock1 -font times
checkbutton .status.lock.adat2 -selectcolor red -text "ADAT2" -anchor nw -width 10 -variable adatlock2 -font times
checkbutton .status.lock.adat3 -selectcolor red -text "ADAT3" -anchor nw -width 10 -variable adatlock3 -font times
pack .status.lock.text .status.lock.adat1 .status.lock.adat2 .status.lock.adat3 -side top -padx 3
# Sync
frame .status.sync
label .status.sync.text -text "Sync" -justify left
checkbutton .status.sync.adat1 -selectcolor red -text "ADAT1" -anchor nw -width 10 -variable adatsync1 -font times
checkbutton .status.sync.adat2 -selectcolor red -text "ADAT2" -anchor nw -width 10 -variable adatsync2 -font times
checkbutton .status.sync.adat3 -selectcolor red -text "ADAT3" -anchor nw -width 10 -variable adatsync3 -font times
pack .status.sync.text .status.sync.adat1 .status.sync.adat2 .status.sync.adat3 -side top -padx 3
# Timecode
frame .status.tc
label .status.tc.text -text "Timecode" -justify left
checkbutton .status.tc.busy -selectcolor red -text "busy" -anchor nw -width 10 -variable tcbusy -font times
checkbutton .status.tc.out -selectcolor red -text "out" -anchor nw -width 10 -variable tcout -font times
checkbutton .status.tc.valid -selectcolor red -text "valid" -anchor nw -width 10 -variable tcvalid -font times
pack .status.tc.text .status.tc.busy .status.tc.out .status.tc.valid -side top -padx 3
# SPDIF In
frame .status.spdif
label .status.spdif.text -text "SPDIF In" -justify left
label .status.spdif.sr -text "--.- kHz" -anchor n -width 10 -font times
checkbutton .status.spdif.error -selectcolor red -text "Input Lock" -anchor nw -width 10 -variable spdiferr -font times
pack .status.spdif.text .status.spdif.sr .status.spdif.error -side top -padx 3
pack .status.sr .status.lock .status.sync .status.tc .status.spdif -side left -fill x -anchor n -expand 1
#
# CONTROL
#
proc setprof {} {
global CTRLPROG
global spprof
exec $CTRLPROG pro $spprof
}
proc setemph {} {
global CTRLPROG
global spemph
exec $CTRLPROG emphasis $spemph
}
proc setnoaud {} {
global CTRLPROG
global spnoaud
exec $CTRLPROG dolby $spnoaud
}
proc setoptical {} {
global CTRLPROG
global spoptical
exec $CTRLPROG optout $spoptical
}
proc setspdifin {} {
global CTRLPROG
global spdifin
exec $CTRLPROG spdifin [expr $spdifin - 1]
}
proc setsyncsource {} {
global CTRLPROG
global syncsource
exec $CTRLPROG syncref [expr $syncsource -1]
}
proc setmaster {} {
global CTRLPROG
global master
exec $CTRLPROG master $master
}
proc setwordclock {} {
global CTRLPROG
global wordclock
exec $CTRLPROG wordclock $wordclock
}
proc setadat1cd {} {
global CTRLPROG
global adat1cd
exec $CTRLPROG adat1cd $adat1cd
}
frame .control
# SPDIF In & SPDIF Out
frame .control.spdif
frame .control.spdif.in
label .control.spdif.in.text -text "SPDIF In" -justify left
radiobutton .control.spdif.in.input1 -text "Optical" -anchor nw -width 13 -variable spdifin -value 1 -command setspdifin -selectcolor blue -font times
radiobutton .control.spdif.in.input2 -text "Coaxial" -anchor nw -width 13 -variable spdifin -value 2 -command setspdifin -selectcolor blue -font times
radiobutton .control.spdif.in.input3 -text "Intern " -anchor nw -width 13 -variable spdifin -command setspdifin -value 3 -selectcolor blue -font times
checkbutton .control.spdif.in.adat1cd -text "ADAT1 Intern" -anchor nw -width 13 -variable adat1cd -command setadat1cd -selectcolor blue -font times
pack .control.spdif.in.text .control.spdif.in.input1 .control.spdif.in.input2 .control.spdif.in.input3 .control.spdif.in.adat1cd
label .control.spdif.space
frame .control.spdif.out
label .control.spdif.out.text -text "SPDIF Out" -justify left
checkbutton .control.spdif.out.pro -text "Professional" -anchor nw -width 13 -variable spprof -command setprof -selectcolor blue -font times
checkbutton .control.spdif.out.emphasis -text "Emphasis" -anchor nw -width 13 -variable spemph -command setemph -selectcolor blue -font times
checkbutton .control.spdif.out.dolby -text "NoAudio" -anchor nw -width 13 -variable spnoaud -command setnoaud -selectcolor blue -font times
checkbutton .control.spdif.out.optout -text "Optical Out" -anchor nw -width 13 -variable spoptical -command setoptical -selectcolor blue -font times
pack .control.spdif.out.optout .control.spdif.out.dolby .control.spdif.out.emphasis .control.spdif.out.pro .control.spdif.out.text -side bottom
pack .control.spdif.in .control.spdif.space .control.spdif.out -side top -fill y -padx 3 -expand 1
# Sync Mode & Sync Source
frame .control.sync
frame .control.sync.mode
label .control.sync.mode.text -text "Sync Mode" -justify left
checkbutton .control.sync.mode.master -text "Master" -anchor nw -width 13 -variable master -command setmaster -selectcolor blue -font times
checkbutton .control.sync.mode.wc -text "Wordclock" -anchor nw -width 13 -variable wordclock -command setwordclock -selectcolor blue -font times
pack .control.sync.mode.text .control.sync.mode.master .control.sync.mode.wc
label .control.sync.space
frame .control.sync.src
label .control.sync.src.text -text "Sync Source" -justify left
radiobutton .control.sync.src.input1 -text "ADAT1" -anchor nw -width 13 -variable syncsource -value 1 -command setsyncsource -selectcolor blue -font times
radiobutton .control.sync.src.input2 -text "ADAT2" -anchor nw -width 13 -variable syncsource -value 2 -command setsyncsource -selectcolor blue -font times
radiobutton .control.sync.src.input3 -text "ADAT3" -anchor nw -width 13 -variable syncsource -command setsyncsource -value 3 -selectcolor blue -font times
radiobutton .control.sync.src.input4 -text "SPDIF" -anchor nw -width 13 -variable syncsource -command setsyncsource -value 4 -selectcolor blue -font times
pack .control.sync.src.input4 .control.sync.src.input3 .control.sync.src.input2 .control.sync.src.input1 .control.sync.src.text -side bottom
pack .control.sync.mode .control.sync.space .control.sync.src -side top -fill y -padx 3 -expand 1
label .control.space -text "" -width 10
# Buffer Size
frame .control.buf
label .control.buf.text -text "Buffer Size (Latency)" -justify left
radiobutton .control.buf.b1 -selectcolor red -text "64 (1.5 ms)" -width 13 -anchor nw -variable ssrate -value 1 -font times
radiobutton .control.buf.b2 -selectcolor red -text "128 (3 ms)" -width 13 -anchor nw -variable ssrate -value 2 -font times
radiobutton .control.buf.b3 -selectcolor red -text "256 (6 ms)" -width 13 -anchor nw -variable ssrate -value 3 -font times
radiobutton .control.buf.b4 -selectcolor red -text "512 (12 ms)" -width 13 -anchor nw -variable ssrate -value 4 -font times
radiobutton .control.buf.b5 -selectcolor red -text "1024 (23 ms)" -width 13 -anchor nw -variable ssrate -value 5 -font times
radiobutton .control.buf.b6 -selectcolor red -text "2048 (46 ms)" -width 13 -anchor nw -variable ssrate -value 6 -font times
radiobutton .control.buf.b7 -selectcolor red -text "4096 (93 ms)" -width 13 -anchor nw -variable ssrate -value 7 -font times
radiobutton .control.buf.b8 -selectcolor red -text "8192 (186 ms)" -width 13 -anchor nw -variable ssrate -value 8 -font times
pack .control.buf.text .control.buf.b1 .control.buf.b2 .control.buf.b3 .control.buf.b4 .control.buf.b5 .control.buf.b6 .control.buf.b7 .control.buf.b8 -side top -padx 3
# Offset
frame .control.offset
frame .control.offset.in
label .control.offset.in.text -text "Offset In" -justify left
label .control.offset.in.off0 -text "dev\#0: -" -anchor nw -width 10 -font times
label .control.offset.in.off1 -text "dev\#1: -" -anchor nw -width 10 -font times
label .control.offset.in.off2 -text "dev\#2: -" -anchor nw -width 10 -font times
label .control.offset.in.off3 -text "dev\#3: -" -anchor nw -width 10 -font times
pack .control.offset.in.text .control.offset.in.off0 .control.offset.in.off1 .control.offset.in.off2 .control.offset.in.off3
label .control.offset.space
frame .control.offset.out
label .control.offset.out.text -text "Offset Out" -justify left
label .control.offset.out.off0 -text "dev\#0: -" -anchor nw -width 10 -font times
label .control.offset.out.off1 -text "dev\#1: -" -anchor nw -width 10 -font times
label .control.offset.out.off2 -text "dev\#2: -" -anchor nw -width 10 -font times
label .control.offset.out.off3 -text "dev\#3: -" -anchor nw -width 10 -font times
pack .control.offset.out.off3 .control.offset.out.off2 .control.offset.out.off1 .control.offset.out.off0 .control.offset.out.text -side bottom
pack .control.offset.in .control.offset.space .control.offset.out -side top -fill y -padx 3 -expand 1
pack .control.spdif .control.sync .control.space .control.buf .control.offset -side left -fill both -anchor n -expand 1
label .statustext -text Status -justify center -relief ridge
label .controltext -text Control -justify center -relief ridge
label .statusspace
label .controlspace
pack .statustext .status .statusspace .controltext .control .controlspace -side top -anchor nw -fill both -expand 1
proc get_bit {output sstr} {
set idx1 [string last [concat $sstr 1] $output]
set idx1 [expr $idx1 != -1]
return $idx1
}
proc get_val {output sstr} {
set val [string wordend $output [string last $sstr $output]]
set val [string range $output $val [expr $val+1]]
return $val
}
proc get_val2 {output sstr} {
set val [string wordend $output [string first $sstr $output]]
set val [string range $output $val [expr $val+2]]
return $val
}
proc get_control {} {
global spprof
global spemph
global spnoaud
global spoptical
global spdifin
global ssrate
global master
global wordclock
global syncsource
global CTRLPROG
set f [open "| $CTRLPROG control" r+]
set ooo [read $f 1000]
close $f
# puts $ooo
set spprof [ get_bit $ooo "pro"]
set spemph [ get_bit $ooo "emphasis"]
set spnoaud [ get_bit $ooo "dolby"]
set spoptical [ get_bit $ooo "opt_out"]
set spdifin [ expr [ get_val $ooo "spdif_in"] + 1]
set ssrate [ expr [ get_val $ooo "latency"] + 1]
set master [ expr [ get_val $ooo "master"]]
set wordclock [ expr [ get_val $ooo "wordclock"]]
set syncsource [ expr [ get_val $ooo "sync_ref"] + 1]
}
proc get_status {} {
global srate
global ctrlcom
global adatlock1
global adatlock2
global adatlock3
global adatsync1
global adatsync2
global adatsync3
global tcbusy
global tcout
global tcvalid
global spdiferr
global crystal
global .status.spdif.text
global CTRLPROG
set f [open "| $CTRLPROG status" r+]
set ooo [read $f 1000]
close $f
# puts $ooo
# samplerate
set idx1 [string last "sr48 1" $ooo]
set idx2 [string last "doublespeed 1" $ooo]
if {$idx1 >= 0} {
set fact1 48000
} else {
set fact1 44100
}
if {$idx2 >= 0} {
set fact2 2
} else {
set fact2 1
}
set srate [expr $fact1 * $fact2]
# ADAT lock
set val [get_val $ooo lockmask]
set adatlock1 0
set adatlock2 0
set adatlock3 0
if {[expr $val & 1]} {
set adatlock3 1
}
if {[expr $val & 2]} {
set adatlock2 1
}
if {[expr $val & 4]} {
set adatlock1 1
}
# ADAT sync
set val [get_val $ooo syncmask]
set adatsync1 0
set adatsync2 0
set adatsync3 0
if {[expr $val & 1]} {
set adatsync3 1
}
if {[expr $val & 2]} {
set adatsync2 1
}
if {[expr $val & 4]} {
set adatsync1 1
}
# TC busy
set tcbusy [get_bit $ooo "busy"]
set tcout [get_bit $ooo "out"]
set tcvalid [get_bit $ooo "valid"]
set spdiferr [expr [get_bit $ooo "spdif_error"] == 0]
# 000=64kHz, 100=88.2kHz, 011=96kHz
# 111=32kHz, 110=44.1kHz, 101=48kHz
set val [get_val $ooo crystalrate]
set crystal "--.- kHz"
if {$val == 0} {
set crystal "64 kHz"
}
if {$val == 4} {
set crystal "88.2 kHz"
}
if {$val == 3} {
set crystal "96 kHz"
}
if {$val == 7} {
set crystal "32 kHz"
}
if {$val == 6} {
set crystal "44.1 kHz"
}
if {$val == 5} {
set crystal "48 kHz"
}
.status.spdif.sr configure -text $crystal
}
proc get_offset {} {
global inoffset
global outoffset
global CTRLPROG
set f [open "| $CTRLPROG mix" r+]
set ooo [read $f 1000]
close $f
# puts $ooo
if { [string match "*devnr*" $ooo] } {
set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end]
set val [get_val2 $ooo i_offset]
.control.offset.in.off0 configure -text "dev\#0: $val"
set val [get_val2 $ooo o_offset]
.control.offset.out.off0 configure -text "dev\#0: $val"
} else {
.control.offset.in.off0 configure -text "dev\#0: -"
.control.offset.out.off0 configure -text "dev\#0: -"
}
if { [string match "*devnr*" $ooo] } {
set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end]
set val [get_val2 $ooo i_offset]
.control.offset.in.off1 configure -text "dev\#1: $val"
set val [get_val2 $ooo o_offset]
.control.offset.out.off1 configure -text "dev\#1: $val"
} else {
.control.offset.in.off1 configure -text "dev\#1: -"
.control.offset.out.off1 configure -text "dev\#1: -"
}
if { [string match "*devnr*" $ooo] } {
set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end]
set val [get_val2 $ooo i_offset]
.control.offset.in.off2 configure -text "dev\#2: $val"
set val [get_val2 $ooo o_offset]
.control.offset.out.off2 configure -text "dev\#2: $val"
} else {
.control.offset.in.off2 configure -text "dev\#2: -"
.control.offset.out.off2 configure -text "dev\#2: -"
}
if { [string match "*devnr*" $ooo] } {
set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end]
set val [get_val2 $ooo i_offset]
.control.offset.in.off3 configure -text "dev\#3: $val"
set val [get_val2 $ooo o_offset]
.control.offset.out.off3 configure -text "dev\#3: $val"
} else {
.control.offset.in.off3 configure -text "dev\#3: -"
.control.offset.out.off3 configure -text "dev\#3: -"
}
}
proc get_all {} {
get_status
get_control
get_offset
}
# main
while {1} {
after 200
get_all
update
}
...@@ -10,6 +10,17 @@ ...@@ -10,6 +10,17 @@
* 11 May 2001: 0.4 fixed for SMP, included into kernel source tree * 11 May 2001: 0.4 fixed for SMP, included into kernel source tree
* 17 May 2001: 0.5 draining code didn't work on new cards * 17 May 2001: 0.5 draining code didn't work on new cards
* 18 May 2001: 0.6 remove synchronize_irq() call * 18 May 2001: 0.6 remove synchronize_irq() call
* 17 Jul 2001: 0.7 updated xrmectrl to make it work for newer cards
* 2 feb 2002: 0.8 fixed pci device handling, see below for patches from Heiko (Thanks!)
Marcus Meissner <Marcus.Meissner@caldera.de>
Modifications - Heiko Purnhagen <purnhage@tnt.uni-hannover.de>
HP20020108 fixed handling of "large" read()
HP20020116 towards REV 1.5 support, based on ALSA's card-rme9652.c
HP20020118 made mixer ioctl and handling of devices>1 more safe
HP20020201 fixed handling of "large" read() properly
added REV 1.5 S/P-DIF receiver support
SNDCTL_DSP_SPEED now returns the actual speed
* 10 Aug 2002: added synchronize_irq() again * 10 Aug 2002: added synchronize_irq() again
TODO: TODO:
...@@ -20,15 +31,17 @@ ...@@ -20,15 +31,17 @@
- mixer mmap interface - mixer mmap interface
- mixer ioctl - mixer ioctl
- get rid of noise upon first open (why ??) - get rid of noise upon first open (why ??)
- allow multiple open(at least for read) - allow multiple open (at least for read)
- allow multiple open for non overlapping regions - allow multiple open for non overlapping regions
- recheck the multiple devices part (offsets of different devices, etc) - recheck the multiple devices part (offsets of different devices, etc)
- do decent draining in _release --- done - do decent draining in _release --- done
- SMP support - SMP support
- what about using fragstotal>2 for small fragsize? (HP20020118)
- add support for AFMT_S32_LE
*/ */
#ifndef RMEVERSION #ifndef RMEVERSION
#define RMEVERSION "0.6" #define RMEVERSION "0.8"
#endif #endif
#include <linux/version.h> #include <linux/version.h>
...@@ -41,6 +54,8 @@ ...@@ -41,6 +54,8 @@
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/dma.h>
#include <asm/hardirq.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/poll.h> #include <linux/poll.h>
...@@ -134,45 +149,57 @@ MODULE_LICENSE("GPL"); ...@@ -134,45 +149,57 @@ MODULE_LICENSE("GPL");
#define RME96xx_F_0 0x0400000 /* 000=64kHz, 100=88.2kHz, 011=96kHz */ #define RME96xx_F_0 0x0400000 /* 000=64kHz, 100=88.2kHz, 011=96kHz */
#define RME96xx_F_1 0x0800000 /* 111=32kHz, 110=44.1kHz, 101=48kHz, */ #define RME96xx_F_1 0x0800000 /* 111=32kHz, 110=44.1kHz, 101=48kHz, */
#define RME96xx_F_2 0x1000000 /* od external Crystal Chip if ERF=1*/ #define RME96xx_F_2 0x1000000 /* 001=Rev 1.5+ external Crystal Chip */
#define RME96xx_ERF 0x2000000 /* Error-Flag of SDPIF Receiver (1=No Lock)*/ #define RME96xx_ERF 0x2000000 /* Error-Flag of SDPIF Receiver (1=No Lock)*/
#define RME96xx_buffer_id 0x4000000 /* toggles by each interrupt on rec/play */ #define RME96xx_buffer_id 0x4000000 /* toggles by each interrupt on rec/play */
#define RME96xx_tc_valid 0x8000000 /* 1 = a signal is detected on time-code input */ #define RME96xx_tc_valid 0x8000000 /* 1 = a signal is detected on time-code input */
#define RME96xx_SPDIF_READ 0x10000000 /* byte available from Rev 1.5+ SPDIF interface */
/* Status Register Fields */ /* Status Register Fields */
#define RME96xx_lock (RME96xx_lock_0|RME96xx_lock_1|RME96xx_lock_2) #define RME96xx_lock (RME96xx_lock_0|RME96xx_lock_1|RME96xx_lock_2)
#define RME96xx_buf_pos 0x000FFC0
#define RME96xx_sync (RME96xx_sync_0|RME96xx_sync_1|RME96xx_sync_2) #define RME96xx_sync (RME96xx_sync_0|RME96xx_sync_1|RME96xx_sync_2)
#define RME96xx_F (RME96xx_F_0|RME96xx_F_1|RME96xx_F_2) #define RME96xx_F (RME96xx_F_0|RME96xx_F_1|RME96xx_F_2)
#define rme96xx_decode_spdif_rate(x) ((x)>>22)
/* Bit 6..15 : h/w buffer pointer */
#define RME96xx_buf_pos 0x000FFC0
/* Bits 31,30,29 are bits 5,4,3 of h/w pointer position on later
Rev G EEPROMS and Rev 1.5 cards or later.
*/
#define RME96xx_REV15_buf_pos(x) ((((x)&0xE0000000)>>26)|((x)&RME96xx_buf_pos))
/* Control-Register: */ /* Control-Register: */
/*--------------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------------*/
#define RME96xx_start_bit 0x0001 /* start record/play */ #define RME96xx_start_bit 0x0001 /* start record/play */
#define RME96xx_latency0 0x0002 /* Bit 0 - Buffer size or latency */ #define RME96xx_latency0 0x0002 /* Buffer size / latency */
#define RME96xx_latency1 0x0004 /* Bit 1 - Buffer size or latency */ #define RME96xx_latency1 0x0004 /* buffersize = 512Bytes * 2^n */
#define RME96xx_latency2 0x0008 /* Bit 2 - Buffer size or latency */ #define RME96xx_latency2 0x0008 /* 0=64samples ... 7=8192samples */
#define RME96xx_Master 0x0010 /* Clock Mode Master=1,Slave/Auto=0 */ #define RME96xx_Master 0x0010 /* Clock Mode 1=Master, 0=Slave/Auto */
#define RME96xx_IE 0x0020 /* Interupt Enable */ #define RME96xx_IE 0x0020 /* Interupt Enable */
#define RME96xx_freq 0x0040 /* samplerate 0=44.1/88.2, 1=48/96 kHz*/ #define RME96xx_freq 0x0040 /* samplerate 0=44.1/88.2, 1=48/96 kHz*/
#define RME96xx_freq1 0x0080 /* samplerate 0=32 kHz, 1=other rates ??? (from ALSA, but may be wrong) */
#define RME96xx_DS 0x0100 /* double speed 0=44.1/48, 1=88.2/96 Khz */
#define RME96xx_PRO 0x0200 /* SPDIF-OUT 0=consumer, 1=professional */
#define RME96xx_EMP 0x0400 /* SPDIF-OUT emphasis 0=off, 1=on */
#define RME96xx_Dolby 0x0800 /* SPDIF-OUT non-audio bit 1=set, 0=unset */
#define RME96xx_opt_out 0x1000 /* use 1st optical OUT as SPDIF: 1=yes, 0=no */
#define RME96xx_wsel 0x2000 /* use Wordclock as sync (overwrites master) */
#define RME96xx_inp_0 0x4000 /* SPDIF-IN 00=optical (ADAT1), */
#define RME96xx_inp_1 0x8000 /* 01=coaxial (Cinch), 10=internal CDROM */
#define RME96xx_DS 0x0100 /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */ #define RME96xx_SyncRef0 0x10000 /* preferred sync-source in autosync */
#define RME96xx_PRO 0x0200 /* spdif 0=consumer, 1=professional Mode*/ #define RME96xx_SyncRef1 0x20000 /* 00=ADAT1, 01=ADAT2, 10=ADAT3, 11=SPDIF */
#define RME96xx_EMP 0x0400 /* spdif Emphasis 0=None, 1=ON */
#define RME96xx_Dolby 0x0800 /* spdif Non-audio bit 1=set, 0=unset */
#define RME96xx_opt_out 0x1000 /* Use 1st optical OUT as SPDIF: 1=yes,0=no */ #define RME96xx_SPDIF_RESET (1<<18) /* Rev 1.5+: h/w SPDIF receiver */
#define RME96xx_wsel 0x2000 /* use Wordclock as sync (overwrites master)*/ #define RME96xx_SPDIF_SELECT (1<<19)
#define RME96xx_inp_0 0x4000 /* SPDIF-IN: 00=optical (ADAT1), */ #define RME96xx_SPDIF_CLOCK (1<<20)
#define RME96xx_inp_1 0x8000 /* 01=koaxial (Cinch), 10=Internal CDROM*/ #define RME96xx_SPDIF_WRITE (1<<21)
#define RME96xx_ADAT1_INTERNAL (1<<22) /* Rev 1.5+: if set, internal CD connector carries ADAT */
#define RME96xx_SyncRef0 0x10000 /* preferred sync-source in autosync */
#define RME96xx_SyncRef1 0x20000 /* 00=ADAT1,01=ADAT2,10=ADAT3,11=SPDIF */
#define RME96xx_ctrl_init (RME96xx_latency0 |\ #define RME96xx_ctrl_init (RME96xx_latency0 |\
...@@ -186,7 +213,9 @@ MODULE_LICENSE("GPL"); ...@@ -186,7 +213,9 @@ MODULE_LICENSE("GPL");
#define RME96xx_latency (RME96xx_latency0|RME96xx_latency1|RME96xx_latency2) #define RME96xx_latency (RME96xx_latency0|RME96xx_latency1|RME96xx_latency2)
#define RME96xx_inp (RME96xx_inp_0|RME96xx_inp_1) #define RME96xx_inp (RME96xx_inp_0|RME96xx_inp_1)
#define RME96xx_SyncRef (RME96xx_SyncRef0|RME96xx_SyncRef1) #define RME96xx_SyncRef (RME96xx_SyncRef0|RME96xx_SyncRef1)
/* latency = 512Bytes * 2^n, where n is made from Bit3 ... Bit0 */ #define RME96xx_mixer_allowed (RME96xx_Master|RME96xx_PRO|RME96xx_EMP|RME96xx_Dolby|RME96xx_opt_out|RME96xx_wsel|RME96xx_inp|RME96xx_SyncRef|RME96xx_ADAT1_INTERNAL)
/* latency = 512Bytes * 2^n, where n is made from Bit3 ... Bit1 (??? HP20020201) */
#define RME96xx_SET_LATENCY(x) (((x)&0x7)<<1) #define RME96xx_SET_LATENCY(x) (((x)&0x7)<<1)
#define RME96xx_GET_LATENCY(x) (((x)>>1)&0x7) #define RME96xx_GET_LATENCY(x) (((x)>>1)&0x7)
...@@ -211,6 +240,7 @@ MODULE_LICENSE("GPL"); ...@@ -211,6 +240,7 @@ MODULE_LICENSE("GPL");
#define RME96xx_MAX_DEVS 4 /* we provide some OSS stereodevs */ #define RME96xx_MAX_DEVS 4 /* we provide some OSS stereodevs */
#define RME96xx_MASK_DEVS 0x3 /* RME96xx_MAX_DEVS-1 */
#define RME_MESS "rme96xx:" #define RME_MESS "rme96xx:"
/*------------------------------------------------------------------------ /*------------------------------------------------------------------------
...@@ -259,8 +289,10 @@ typedef struct _rme96xx_info { ...@@ -259,8 +289,10 @@ typedef struct _rme96xx_info {
u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */ u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */
int open_count; int hw_rev; /* h/w rev * 10 (i.e. 1.5 has hw_rev = 15) */
char *card_name; /* hammerfall or hammerfall light names */
int open_count; /* unused ??? HP20020201 */
int rate; int rate;
int latency; int latency;
...@@ -324,6 +356,181 @@ inline void rme96xx_unset_ctrl(rme96xx_info* s,int mask) ...@@ -324,6 +356,181 @@ inline void rme96xx_unset_ctrl(rme96xx_info* s,int mask)
} }
inline int rme96xx_get_sample_rate_status(rme96xx_info* s)
{
int val;
u32 status;
status = readl(s->iobase + RME96xx_status_register);
val = (status & RME96xx_fs48) ? 48000 : 44100;
if (status & RME96xx_DS_rd)
val *= 2;
return val;
}
inline int rme96xx_get_sample_rate_ctrl(rme96xx_info* s)
{
int val;
val = (s->control_register & RME96xx_freq) ? 48000 : 44100;
if (s->control_register & RME96xx_DS)
val *= 2;
return val;
}
/* code from ALSA card-rme9652.c for rev 1.5 SPDIF receiver HP 20020201 */
static void rme96xx_spdif_set_bit (rme96xx_info* s, int mask, int onoff)
{
if (onoff)
s->control_register |= mask;
else
s->control_register &= ~mask;
writel(s->control_register,s->iobase + RME96xx_control_register);
}
static void rme96xx_spdif_write_byte (rme96xx_info* s, const int val)
{
long mask;
long i;
for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) {
if (val & mask)
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_WRITE, 1);
else
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_WRITE, 0);
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 1);
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 0);
}
}
static int rme96xx_spdif_read_byte (rme96xx_info* s)
{
long mask;
long val;
long i;
val = 0;
for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) {
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 1);
if (readl(s->iobase + RME96xx_status_register) & RME96xx_SPDIF_READ)
val |= mask;
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 0);
}
return val;
}
static void rme96xx_write_spdif_codec (rme96xx_info* s, const int address, const int data)
{
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 1);
rme96xx_spdif_write_byte (s, 0x20);
rme96xx_spdif_write_byte (s, address);
rme96xx_spdif_write_byte (s, data);
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 0);
}
static int rme96xx_spdif_read_codec (rme96xx_info* s, const int address)
{
int ret;
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 1);
rme96xx_spdif_write_byte (s, 0x20);
rme96xx_spdif_write_byte (s, address);
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 0);
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 1);
rme96xx_spdif_write_byte (s, 0x21);
ret = rme96xx_spdif_read_byte (s);
rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 0);
return ret;
}
static void rme96xx_initialize_spdif_receiver (rme96xx_info* s)
{
/* XXX what unsets this ? */
/* no idea ??? HP 20020201 */
s->control_register |= RME96xx_SPDIF_RESET;
rme96xx_write_spdif_codec (s, 4, 0x40);
rme96xx_write_spdif_codec (s, 17, 0x13);
rme96xx_write_spdif_codec (s, 6, 0x02);
}
static inline int rme96xx_spdif_sample_rate (rme96xx_info *s, int *spdifrate)
{
unsigned int rate_bits;
*spdifrate = 0x1;
if (readl(s->iobase + RME96xx_status_register) & RME96xx_ERF) {
return -1; /* error condition */
}
if (s->hw_rev == 15) {
int x, y, ret;
x = rme96xx_spdif_read_codec (s, 30);
if (x != 0)
y = 48000 * 64 / x;
else
y = 0;
if (y > 30400 && y < 33600) {ret = 32000; *spdifrate = 0x7;}
else if (y > 41900 && y < 46000) {ret = 44100; *spdifrate = 0x6;}
else if (y > 46000 && y < 50400) {ret = 48000; *spdifrate = 0x5;}
else if (y > 60800 && y < 67200) {ret = 64000; *spdifrate = 0x0;}
else if (y > 83700 && y < 92000) {ret = 88200; *spdifrate = 0x4;}
else if (y > 92000 && y < 100000) {ret = 96000; *spdifrate = 0x3;}
else {ret = 0; *spdifrate = 0x1;}
return ret;
}
rate_bits = readl(s->iobase + RME96xx_status_register) & RME96xx_F;
switch (*spdifrate = rme96xx_decode_spdif_rate(rate_bits)) {
case 0x7:
return 32000;
break;
case 0x6:
return 44100;
break;
case 0x5:
return 48000;
break;
case 0x4:
return 88200;
break;
case 0x3:
return 96000;
break;
case 0x0:
return 64000;
break;
default:
/* was an ALSA warning ...
snd_printk("%s: unknown S/PDIF input rate (bits = 0x%x)\n",
s->card_name, rate_bits);
*/
return 0;
break;
}
}
/* end of code from ALSA card-rme9652.c */
/* the hwbuf in the status register seems to have some jitter, to get rid of /* the hwbuf in the status register seems to have some jitter, to get rid of
...@@ -662,6 +869,9 @@ static int rme96xx_dmabuf_init(rme96xx_info * s,struct dmabuf* dma,int ioffset,i ...@@ -662,6 +869,9 @@ static int rme96xx_dmabuf_init(rme96xx_info * s,struct dmabuf* dma,int ioffset,i
int rme96xx_init(rme96xx_info* s) int rme96xx_init(rme96xx_info* s)
{ {
int i; int i;
int status;
unsigned short rev;
DBG(printk(__FUNCTION__"\n")); DBG(printk(__FUNCTION__"\n"));
numcards++; numcards++;
...@@ -694,6 +904,56 @@ int rme96xx_init(rme96xx_info* s) ...@@ -694,6 +904,56 @@ int rme96xx_init(rme96xx_info* s)
rme96xx_dmabuf_init(s,dma,2*i,2*i); rme96xx_dmabuf_init(s,dma,2*i,2*i);
} }
/* code from ALSA card-rme9652.c HP 20020201 */
/* Determine the h/w rev level of the card. This seems like
a particularly kludgy way to encode it, but its what RME
chose to do, so we follow them ...
*/
status = readl(s->iobase + RME96xx_status_register);
if (rme96xx_decode_spdif_rate(status&RME96xx_F) == 1) {
s->hw_rev = 15;
} else {
s->hw_rev = 11;
}
/* Differentiate between the standard Hammerfall, and the
"Light", which does not have the expansion board. This
method comes from information received from Mathhias
Clausen at RME. Display the EEPROM and h/w revID where
relevant.
*/
pci_read_config_word(s->pcidev, PCI_CLASS_REVISION, &rev);
switch (rev & 0xff) {
case 8: /* original eprom */
if (s->hw_rev == 15) {
s->card_name = "RME Digi9636 (Rev 1.5)";
} else {
s->card_name = "RME Digi9636";
}
break;
case 9: /* W36_G EPROM */
s->card_name = "RME Digi9636 (Rev G)";
break;
case 4: /* W52_G EPROM */
s->card_name = "RME Digi9652 (Rev G)";
break;
default:
case 3: /* original eprom */
if (s->hw_rev == 15) {
s->card_name = "RME Digi9652 (Rev 1.5)";
} else {
s->card_name = "RME Digi9652";
}
break;
}
printk(KERN_INFO RME_MESS" detected %s (hw_rev %d)\n",s->card_name,s->hw_rev);
if (s->hw_rev == 15)
rme96xx_initialize_spdif_receiver (s);
s->started = 0; s->started = 0;
rme96xx_setlatency(s,7); rme96xx_setlatency(s,7);
...@@ -734,7 +994,7 @@ static int __devinit rme96xx_probe(struct pci_dev *pcidev, const struct pci_devi ...@@ -734,7 +994,7 @@ static int __devinit rme96xx_probe(struct pci_dev *pcidev, const struct pci_devi
if (pci_enable_device(pcidev)) if (pci_enable_device(pcidev))
goto err_irq; goto err_irq;
if (request_irq(s->irq, rme96xx_interrupt, SA_SHIRQ, "es1370", s)) { if (request_irq(s->irq, rme96xx_interrupt, SA_SHIRQ, "rme96xx", s)) {
printk(KERN_ERR RME_MESS" irq %u in use\n", s->irq); printk(KERN_ERR RME_MESS" irq %u in use\n", s->irq);
goto err_irq; goto err_irq;
} }
...@@ -836,6 +1096,7 @@ static int __init init_rme96xx(void) ...@@ -836,6 +1096,7 @@ static int __init init_rme96xx(void)
if (!pci_present()) /* No PCI bus in this machine! */ if (!pci_present()) /* No PCI bus in this machine! */
return -ENODEV; return -ENODEV;
printk(KERN_INFO RME_MESS" version "RMEVERSION" time " __TIME__ " " __DATE__ "\n"); printk(KERN_INFO RME_MESS" version "RMEVERSION" time " __TIME__ " " __DATE__ "\n");
devices = ((devices-1) & RME96xx_MASK_DEVS) + 1;
printk(KERN_INFO RME_MESS" reserving %d dsp device(s)\n",devices); printk(KERN_INFO RME_MESS" reserving %d dsp device(s)\n",devices);
numcards = 0; numcards = 0;
return pci_module_init(&rme96xx_driver); return pci_module_init(&rme96xx_driver);
...@@ -859,13 +1120,11 @@ module_exit(cleanup_rme96xx); ...@@ -859,13 +1120,11 @@ module_exit(cleanup_rme96xx);
---------------------------------------------------------------------------*/ ---------------------------------------------------------------------------*/
#define RME96xx_FMT (AFMT_S16_LE|AFMT_U8|AFMT_S32_BLOCKED) #define RME96xx_FMT (AFMT_S16_LE|AFMT_U8|AFMT_S32_BLOCKED)
/* AFTM_U8 is not (yet?) supported ... HP20020201 */
static int rme96xx_ioctl(struct inode *in, struct file *file, unsigned int cmd, unsigned long arg)
static int rme96xx_ioctl(struct inode *in, struct file *file,
unsigned int cmd, unsigned long arg)
{ {
struct dmabuf * dma = (struct dmabuf *)file->private_data; struct dmabuf * dma = (struct dmabuf *)file->private_data;
rme96xx_info *s = dma->s; rme96xx_info *s = dma->s;
unsigned long flags; unsigned long flags;
...@@ -918,14 +1177,23 @@ static int rme96xx_ioctl(struct inode *in, struct file *file, ...@@ -918,14 +1177,23 @@ static int rme96xx_ioctl(struct inode *in, struct file *file,
case 96000: case 96000:
rme96xx_set_ctrl(s,RME96xx_freq); rme96xx_set_ctrl(s,RME96xx_freq);
break; break;
/* just report current rate as default
e.g. use 0 to "select" current digital input rate
default: default:
rme96xx_unset_ctrl(s,RME96xx_freq); rme96xx_unset_ctrl(s,RME96xx_freq);
val = 44100; val = 44100;
*/
} }
if (val > 50000) if (val > 50000)
rme96xx_set_ctrl(s,RME96xx_DS); rme96xx_set_ctrl(s,RME96xx_DS);
else else
rme96xx_unset_ctrl(s,RME96xx_DS); rme96xx_unset_ctrl(s,RME96xx_DS);
/* set val to actual value HP 20020201 */
/* NOTE: if not "Sync Master", reported rate might be not yet "updated" ... but I don't want to insert a long udelay() here */
if ((s->control_register & RME96xx_Master) && !(s->control_register & RME96xx_wsel))
val = rme96xx_get_sample_rate_ctrl(s);
else
val = rme96xx_get_sample_rate_status(s);
s->rate = val; s->rate = val;
spin_unlock_irqrestore(&s->lock, flags); spin_unlock_irqrestore(&s->lock, flags);
} }
...@@ -1146,6 +1414,8 @@ static int rme96xx_ioctl(struct inode *in, struct file *file, ...@@ -1146,6 +1414,8 @@ static int rme96xx_ioctl(struct inode *in, struct file *file,
return 0; return 0;
case SOUND_PCM_READ_RATE: case SOUND_PCM_READ_RATE:
/* HP20020201 */
s->rate = rme96xx_get_sample_rate_status(s);
return put_user(s->rate, (int *)arg); return put_user(s->rate, (int *)arg);
case SOUND_PCM_READ_CHANNELS: case SOUND_PCM_READ_CHANNELS:
...@@ -1179,28 +1449,29 @@ static int rme96xx_open(struct inode *in, struct file *f) ...@@ -1179,28 +1449,29 @@ static int rme96xx_open(struct inode *in, struct file *f)
{ {
int minor = minor(in->i_rdev); int minor = minor(in->i_rdev);
struct list_head *list; struct list_head *list;
int devnum = ((minor-3)/16) % devices; /* default = 0 */ int devnum;
rme96xx_info *s; rme96xx_info *s;
struct dmabuf* dma; struct dmabuf* dma;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
DBG(printk("device num %d open\n",devnum)); DBG(printk("device num %d open\n",devnum));
/* ??? */
for (list = devs.next; ; list = list->next) { for (list = devs.next; ; list = list->next) {
if (list == &devs) if (list == &devs)
return -ENODEV; return -ENODEV;
s = list_entry(list, rme96xx_info, devs); s = list_entry(list, rme96xx_info, devs);
if (!((s->dspnum[devnum] ^ minor) & ~0xf)) for (devnum=0; devnum<devices; devnum++)
if (!((s->dspnum[devnum] ^ minor) & ~0xf))
break;
if (devnum<devices)
break; break;
} }
VALIDATE_STATE(s); VALIDATE_STATE(s);
/* ??? */
dma = &s->dma[devnum]; dma = &s->dma[devnum];
f->private_data = dma; f->private_data = dma;
/* wait for device to become free */ /* wait for device to become free */
down(&s->dma[devnum].open_sem); down(&dma->open_sem);
while (dma->open_mode & f->f_mode) { while (dma->open_mode & f->f_mode) {
if (f->f_flags & O_NONBLOCK) { if (f->f_flags & O_NONBLOCK) {
up(&dma->open_sem); up(&dma->open_sem);
...@@ -1219,11 +1490,11 @@ static int rme96xx_open(struct inode *in, struct file *f) ...@@ -1219,11 +1490,11 @@ static int rme96xx_open(struct inode *in, struct file *f)
COMM ("hardware open") COMM ("hardware open")
if (!s->dma[devnum].opened) rme96xx_dmabuf_init(dma->s,dma,dma->inoffset,dma->outoffset); if (!dma->opened) rme96xx_dmabuf_init(dma->s,dma,dma->inoffset,dma->outoffset);
s->dma[devnum].open_mode |= (f->f_mode & (FMODE_READ | FMODE_WRITE)); dma->open_mode |= (f->f_mode & (FMODE_READ | FMODE_WRITE));
s->dma[devnum].opened = 1; dma->opened = 1;
up(&s->dma[devnum].open_sem); up(&dma->open_sem);
DBG(printk("device num %d open finished\n",devnum)); DBG(printk("device num %d open finished\n",devnum));
return 0; return 0;
...@@ -1232,7 +1503,7 @@ static int rme96xx_open(struct inode *in, struct file *f) ...@@ -1232,7 +1503,7 @@ static int rme96xx_open(struct inode *in, struct file *f)
static int rme96xx_release(struct inode *in, struct file *file) static int rme96xx_release(struct inode *in, struct file *file)
{ {
struct dmabuf * dma = (struct dmabuf*) file->private_data; struct dmabuf * dma = (struct dmabuf*) file->private_data;
/* int hwp; */ /* int hwp; ... was unused HP20020201 */
DBG(printk(__FUNCTION__"\n")); DBG(printk(__FUNCTION__"\n"));
COMM ("draining") COMM ("draining")
...@@ -1261,8 +1532,7 @@ static int rme96xx_release(struct inode *in, struct file *file) ...@@ -1261,8 +1532,7 @@ static int rme96xx_release(struct inode *in, struct file *file)
} }
static ssize_t rme96xx_write(struct file *file, const char *buffer, static ssize_t rme96xx_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
size_t count, loff_t *ppos)
{ {
struct dmabuf *dma = (struct dmabuf *)file->private_data; struct dmabuf *dma = (struct dmabuf *)file->private_data;
ssize_t ret = 0; ssize_t ret = 0;
...@@ -1296,7 +1566,6 @@ static ssize_t rme96xx_write(struct file *file, const char *buffer, ...@@ -1296,7 +1566,6 @@ static ssize_t rme96xx_write(struct file *file, const char *buffer,
dma->readptr = hwp; dma->readptr = hwp;
dma->writeptr = hwp; dma->writeptr = hwp;
dma->started = 1; dma->started = 1;
COMM ("first write done")
} }
while (count > 0) { while (count > 0) {
...@@ -1331,11 +1600,11 @@ static ssize_t rme96xx_write(struct file *file, const char *buffer, ...@@ -1331,11 +1600,11 @@ static ssize_t rme96xx_write(struct file *file, const char *buffer,
return ret; return ret;
} }
static ssize_t rme96xx_read(struct file *file, char *buffer,size_t count, loff_t *ppos) static ssize_t rme96xx_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{ {
struct dmabuf *dma = (struct dmabuf *)file->private_data; struct dmabuf *dma = (struct dmabuf *)file->private_data;
ssize_t ret = 0; ssize_t ret = 0;
int cnt; int cnt; /* number of bytes from "buffer" that will/can be used */
int hop = count/dma->inchannels; int hop = count/dma->inchannels;
int hwp; int hwp;
int exact = (file->f_flags & O_NONBLOCK); int exact = (file->f_flags & O_NONBLOCK);
...@@ -1356,9 +1625,6 @@ static ssize_t rme96xx_read(struct file *file, char *buffer,size_t count, loff_t ...@@ -1356,9 +1625,6 @@ static ssize_t rme96xx_read(struct file *file, char *buffer,size_t count, loff_t
if (! (dma->open_mode & FMODE_READ)) if (! (dma->open_mode & FMODE_READ))
return -ENXIO; return -ENXIO;
if (count > ((dma->s->fragsize*dma->inchannels)>>dma->formatshift))
return -EFAULT;
if (!dma->s->started) rme96xx_startcard(dma->s,exact); if (!dma->s->started) rme96xx_startcard(dma->s,exact);
hwp = rme96xx_gethwptr(dma->s,0); hwp = rme96xx_gethwptr(dma->s,0);
...@@ -1444,7 +1710,7 @@ static int rm96xx_mmap(struct file *file, struct vm_area_struct *vma) { ...@@ -1444,7 +1710,7 @@ static int rm96xx_mmap(struct file *file, struct vm_area_struct *vma) {
/* this is the mapping */ /* this is the mapping */
vma->vm_flags &= ~VM_IO;
dma->mmapped = 1; dma->mmapped = 1;
unlock_kernel(); unlock_kernel();
return 0; return 0;
...@@ -1529,8 +1795,12 @@ static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned ...@@ -1529,8 +1795,12 @@ static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned
{ {
rme96xx_info *s = (rme96xx_info *)file->private_data; rme96xx_info *s = (rme96xx_info *)file->private_data;
u32 status; u32 status;
int spdifrate;
status = readl(s->iobase + RME96xx_status_register); status = readl(s->iobase + RME96xx_status_register);
/* hack to convert rev 1.5 SPDIF rate to "crystalrate" format HP 20020201 */
rme96xx_spdif_sample_rate(s,&spdifrate);
status = (status & ~RME96xx_F) | ((spdifrate<<22) & RME96xx_F);
VALIDATE_STATE(s); VALIDATE_STATE(s);
if (cmd == SOUND_MIXER_PRIVATE1) { if (cmd == SOUND_MIXER_PRIVATE1) {
...@@ -1538,9 +1808,21 @@ static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned ...@@ -1538,9 +1808,21 @@ static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned
if (copy_from_user(&mixer,(void*)arg,sizeof(mixer))) if (copy_from_user(&mixer,(void*)arg,sizeof(mixer)))
return -EFAULT; return -EFAULT;
if (file->f_mode & FMODE_WRITE) { mixer.devnr &= RME96xx_MASK_DEVS;
s->dma[mixer.devnr].outoffset = mixer.o_offset; if (mixer.devnr >= devices)
s->dma[mixer.devnr].inoffset = mixer.i_offset; mixer.devnr = devices-1;
if (file->f_mode & FMODE_WRITE && !s->dma[mixer.devnr].opened) {
/* modify only if device not open */
if (mixer.o_offset < 0)
mixer.o_offset = 0;
if (mixer.o_offset >= RME96xx_CHANNELS_PER_CARD)
mixer.o_offset = RME96xx_CHANNELS_PER_CARD-1;
if (mixer.i_offset < 0)
mixer.i_offset = 0;
if (mixer.i_offset >= RME96xx_CHANNELS_PER_CARD)
mixer.i_offset = RME96xx_CHANNELS_PER_CARD-1;
s->dma[mixer.devnr].outoffset = mixer.o_offset;
s->dma[mixer.devnr].inoffset = mixer.i_offset;
} }
mixer.o_offset = s->dma[mixer.devnr].outoffset; mixer.o_offset = s->dma[mixer.devnr].outoffset;
...@@ -1552,13 +1834,14 @@ static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned ...@@ -1552,13 +1834,14 @@ static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned
return put_user(status, (int *)arg); return put_user(status, (int *)arg);
} }
if (cmd == SOUND_MIXER_PRIVATE3) { if (cmd == SOUND_MIXER_PRIVATE3) {
u32 control; u32 control;
if (copy_from_user(&control,(void*)arg,sizeof(control))) if (copy_from_user(&control,(void*)arg,sizeof(control)))
return -EFAULT; return -EFAULT;
if (file->f_mode & FMODE_WRITE) { if (file->f_mode & FMODE_WRITE) {
s->control_register = control; s->control_register &= ~RME96xx_mixer_allowed;
writel(control,s->iobase + RME96xx_control_register); s->control_register |= control & RME96xx_mixer_allowed;
} writel(control,s->iobase + RME96xx_control_register);
}
return put_user(s->control_register, (int *)arg); return put_user(s->control_register, (int *)arg);
} }
......
/* (C) 2000 Guenter Geiger <geiger@debian.org> /* (C) 2000 Guenter Geiger <geiger@debian.org>
with copy/pastes from the driver of Winfried Ritsch <ritsch@iem.kug.ac.at> with copy/pastes from the driver of Winfried Ritsch <ritsch@iem.kug.ac.at>
Modifications - Heiko Purnhagen <purnhage@tnt.uni-hannover.de>
HP20020116 towards REV 1.5 support, based on ALSA's card-rme9652.c
HP20020201 completed?
A text/graphic control panel (rmectrl/xrmectrl) is available from
http://gige.xdv.org/pages/soft/pages/rme
*/ */
...@@ -7,55 +14,65 @@ ...@@ -7,55 +14,65 @@
#define AFMT_S32_BLOCKED 0x0000400 #define AFMT_S32_BLOCKED 0x0000400
#endif #endif
/* AFMT_S16_BLOCKED not yet supported */
#ifndef AFMT_S16_BLOCKED #ifndef AFMT_S16_BLOCKED
#define AFMT_S16_BLOCKED 0x0000800 #define AFMT_S16_BLOCKED 0x0000800
#endif #endif
typedef struct rme_status { typedef struct rme_status {
unsigned int irq:1; /* high or low */ unsigned int irq:1;
unsigned int lockmask:3; /* ADAT1, ADAT2, ADAT3 */ unsigned int lockmask:3; /* ADAT input PLLs locked */
unsigned int sr48:1; /* current sample rate */ /* 100=ADAT1, 010=ADAT2, 001=ADAT3 */
unsigned int wclock:1; /* wordclock used ? */ unsigned int sr48:1; /* sample rate: 0=44.1/88.2 1=48/96 kHz */
unsigned int bufpoint:10; unsigned int wclock:1; /* 1=wordclock used */
unsigned int bufpoint:10;
unsigned int syncmask:3; /* ADAT1, ADAT2, ADAT3 */ unsigned int syncmask:3; /* ADAT input in sync with system clock */
unsigned int doublespeed:1; /* 100=ADAT1, 010=ADAT2, 001=ADAT3 */
unsigned int tc_busy:1; unsigned int doublespeed:1; /* sample rate: 0=44.1/48 1=88.2/96 kHz */
unsigned int tc_out:1; unsigned int tc_busy:1;
unsigned int crystalrate:3; unsigned int tc_out:1;
unsigned int spdif_error:1; unsigned int crystalrate:3; /* spdif input sample rate: */
unsigned int bufid:1; /* 000=64kHz, 100=88.2kHz, 011=96kHz */
unsigned int tc_valid:1; /* 111=32kHz, 110=44.1kHz, 101=48kHz */
unsigned int spdif_error:1; /* 1=no spdif lock */
unsigned int bufid:1;
unsigned int tc_valid:1; /* 1=timecode input detected */
unsigned int spdif_read:1;
} rme_status_t; } rme_status_t;
/* only fields marked W: can be modified by writing to SOUND_MIXER_PRIVATE3 */
typedef struct rme_control { typedef struct rme_control {
unsigned int start:1; unsigned int start:1;
unsigned int latency:3; unsigned int latency:3; /* buffer size / latency [samples]: */
/* 0=64 ... 7=8192 */
unsigned int master:1; unsigned int master:1; /* W: clock mode: 1=master 0=slave/auto */
unsigned int ie:1; unsigned int ie:1;
unsigned int sr48:1; unsigned int sr48:1; /* samplerate 0=44.1/88.2, 1=48/96 kHz */
unsigned int spare:1; unsigned int spare:1;
unsigned int doublespeed:1; /* double speed 0=44.1/48, 1=88.2/96 Khz */
unsigned int doublespeed:1; unsigned int pro:1; /* W: SPDIF-OUT 0=consumer, 1=professional */
unsigned int pro:1; unsigned int emphasis:1; /* W: SPDIF-OUT emphasis 0=off, 1=on */
unsigned int emphasis:1; unsigned int dolby:1; /* W: SPDIF-OUT non-audio bit 1=set, 0=unset */
unsigned int dolby:1; unsigned int opt_out:1; /* W: use 1st optical OUT as SPDIF: 1=yes, 0=no */
unsigned int wordclock:1; /* W: use Wordclock as sync (overwrites master) */
unsigned int opt_out:1; unsigned int spdif_in:2; /* W: SPDIF-IN: */
unsigned int wordclock:1; /* 00=optical (ADAT1), 01=coaxial (Cinch), 10=internal CDROM */
unsigned int spdif_in:2; unsigned int sync_ref:2; /* W: preferred sync-source in autosync */
/* 00=ADAT1, 01=ADAT2, 10=ADAT3, 11=SPDIF */
unsigned int sync_ref:2; unsigned int spdif_reset:1;
unsigned int spdif_select:1;
unsigned int spdif_clock:1;
unsigned int spdif_write:1;
unsigned int adat1_cd:1; /* W: Rev 1.5+: if set, internal CD connector carries ADAT */
} rme_ctrl_t; } rme_ctrl_t;
typedef struct _rme_mixer { typedef struct _rme_mixer {
int i_offset; int i_offset;
int o_offset; int o_offset;
int devnr; int devnr;
int spare[8]; int spare[8];
} rme_mixer; } rme_mixer;
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment