Hi,
After an afternoon and morning of poring over disassembly of the
Thinkpad 755/755c audio driver, I've found a solution to get it to work.
This information applies to Thinkpad 360, Thinkpad 750, Thinkpad 755C,
and Thinkpad 755CS with the Crystal CS4248 WSS codec.
The CS4248 is a full duplex stereo codec which is pin and command
compatible with an AD1848. It is supported in the ALSA and OSS drivers.
However, on these laptops, any attempt to get the sound running with the
existing drivers has met with failure; the chip is forever in the busy
(0x80) state and never responds, so the driver's detection routine
bails out with ENODEV.
Getting tired of this problem finally, I wrote a little "catport"
program that lets me read and write to arbitrary hardware ports easily,
so I could prod a port and check its status immediately.
--------------------------------------------------------------------------------
catport.c
--------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/io.h>
int main(int argc, char **argv) {
unsigned int port, temp;
unsigned char val;
if (argc < 3) { printf("need at least 3 arguments, exiting\n"); return 1; }
if (getuid()) { printf("need root privileges\n"); return 1; }
iopl(3);
sscanf(argv[2], "%x", &port);
switch (*(argv[1])) {
case 'w':
sscanf(argv[3], "%x", &temp);
temp &= 0xff;
val = (unsigned char)temp;
outb(val, port);
printf("Output %x (%c) to port %x\n", val, (unsigned char)val,
port);
break;
case 'r':
val = inb(port);
printf("Received %x (%c) from port %x\n", val, (unsigned
char)val, port);
break;
default:
break;
}
return 0;
}
/* gcc ./catport.c -o catport -g -O2 -Wall */
--------------------------------------------------------------------------------
This allowed me to determine where the proper port ranges were and verify that
the problem was what I thought.
So I use catport to check the ports 0x4e30-0x4e39. Sure enough, they all return
0x80, the AD1848 busy. Other ports return 0xff, so I assume that the CS4248 is
actually on these ports. Any writes to these ports are ignored as per the AD1848
data sheet when the chip is in "reset" or "not ready" mode.
The DOS driver tpauddd.sys initalizes the chip and allows sound to be played to
it as a Windows Sound System compatible codec device. Next step, I took a
copy of the tpauddd.sys off IBM's Audio Features diskette, and run it through
a disassembler. After a day or so of looking over the disassembled code and
translating whatever I could find that hits the hardware into pseudo C code
so it was readable, I run across the following function which I wish I ran
across in the first place (the function names are filled in by myself while
analysing the code):
<mode dmca-violation="on">
----------------------------------------------------------------------------
Iseg000:ADC6 push bp
Iseg000:ADC7 mov bp, sp
Iseg000:ADC9 sub sp, 4
Iseg000:ADCC mov ax, 1Ch ; hmm 0x1c
Iseg000:ADCF push ax
Iseg000:ADD0 mov ax, 15E8h ; goes to port 0x15e8
Iseg000:ADD3 push ax
Iseg000:ADD4 call outb__
Iseg000:ADD7 add sp, 4
Iseg000:ADDA mov ax, 15E9h ; check whats in 0x15e9
Iseg000:ADDD push ax
Iseg000:ADDE call inb__
Iseg000:ADE1 pop bx
Iseg000:ADE2 mov ds:4D45h, al ; save the value for later
Iseg000:ADE5 test byte ptr ds:4D45h, 2 ; is bit 2 set?
Iseg000:ADEA jnz loc_0_AE3F ; if not, go away
Iseg000:ADEC call damnit2 ; do some other stuff
Iseg000:ADEF mov al, ds:4D45h
Iseg000:ADF2 or al, 2 ; or the value from before with 2??
Iseg000:ADF4 sub ah, ah
Iseg000:ADF6 push ax
Iseg000:ADF7 mov ax, 15E9h ; put it back into 0x15e9
Iseg000:ADFA push ax
Iseg000:ADFB call outb__
Iseg000:ADFE add sp, 4
Iseg000:AE01 push word ptr ds:4D0Ch ; $4D0C stores one of the codec's address
Iseg000:AE05 call inb__ ; read from it
Iseg000:AE08 pop bx
Iseg000:AE09 cmp al, 80h ; '�' ; does it == 0x80?
Iseg000:AE0B jnz loc_0_AE38 ; if not, we are done, return 0
Iseg000:AE0D sub ax, ax
Iseg000:AE0F mov [bp+var_2], ax ; i = 0
Iseg000:AE12 mov [bp+var_4], ax ; j = 0
Iseg000:AE15 loc_0_AE15:
Iseg000:AE15 push word ptr ds:4D0Ch ; read $4D0C
Iseg000:AE19 call inb__
Iseg000:AE1C pop bx
Iseg000:AE1D cmp al, 80h ; '�' ; is it 0x80?
Iseg000:AE1F jnz loc_0_AE3C ; if not, the chip is now ready, jump out
Iseg000:AE21 add [bp+var_4], 1 ; i++
Iseg000:AE25 adc [bp+var_2], 0
Iseg000:AE29 cmp [bp+var_2], 7
Iseg000:AE2D jb loc_0_AE15
Iseg000:AE2F ja loc_0_AE38
Iseg000:AE31 cmp [bp+var_4], 0A120h ; if (i <= big_number) goto tryagain;
Iseg000:AE36 jbe loc_0_AE15
Iseg000:AE38 loc_0_AE38:
Iseg000:AE38 xor ax, ax
Iseg000:AE3A jump short loc_0_AE42
Iseg000:AE3C loc_0_AE3C:
Iseg000:AE3C call do_the_init
Iseg000:AE3F loc_0_AE3F:
Iseg000:AE3F mov ax, 1
Iseg000:AE42 loc_0_AE42:
Iseg000:AE42 mov sp, bp
Iseg000:AE44 pop bp
Iseg000:AE45 retn
----------------------------------------------------------------------------------
Now I'm not exactly certain on what everything is going on, but the one loop
certainly looks like the one ad1848_probe calls. So, on a whim, I try those
two port addresses with my catport.
[EMAIL PROTECTED]:~/debs$ sudo ./catport r 0x4e30
Received 80 () from port 4e30 ; as usual
[EMAIL PROTECTED]:~/debs$ sudo ./catport r 0x15e8
Received 2 () from port 15e8
[EMAIL PROTECTED]:~/debs$ sudo ./catport w 0x15e8 0x1c
Output 0x1c () to port 15e8
[EMAIL PROTECTED]:~/debs$ sudo ./catport r 0x15e8
Received 1c () from port 15e8 ; ok, it took
[EMAIL PROTECTED]:~/debs$ sudo ./catport r 0x15e9
Received f9 () from port 15e8
(f9 |= 2) == fb, so:
[EMAIL PROTECTED]:~/debs$ sudo ./catport w 0x15e9 0xfb
Output fb () to port 15e9
[EMAIL PROTECTED]:~/debs$ sudo ./catport r 0x15e9
Received fb () from port 15e9
[EMAIL PROTECTED]:~/debs$ sudo ./catport r 0x4e30
Received 40 () from port 4e30 ; What the... it's up!!!
that's all it took.
Now, I insert the module against the default resources which are
I/O 0x4e30, IRQ 10, DMA 0,1
$ sudo insmod snd-ad1848 port=0x4e30 irq=10 dma1=0
And the sound is inited! I can use alsamixer and cat something to /dev/dsp
and it exits successfully, no errors in the dmesg.
Now, I can't actually *hear* the sound, because I am working through an ssh; my
IBM laptop is at home 100 miles away in a corner somewhere. But since alsamixer
works, the DSP does not block, and all of the ports read the correct AD1848
values (i.e. index = 0x40, statuc=0xcc, revision/mode = 0x8a, etc), I think we
are in business. I am guessing that the Thinkpad leaves the chip disabled unless
a driver enabled it, for the power consumption reasons.
So, my question to alsa-devel is, what should be the proper way to do this fix?
I could just do something like the following:
... try init ...
else { /* if the chip remained busy, let's see if we have a Thinkpad that needs
the chip powered on */
outb(0x1c, 0x15e8);
temp = inb(0x15e9);
outb(temp|2, 0x15e8);
... try init again ...
}
But that seems a braindead solution -- we don't want to go banging random ports
on a machine which may have other sensitive things attached there. Under
windows 95, the 0x15e8-0x15ef is assigned to "Motherboard Resources" -- is there
a way to determine whether this I/O range is allocated by the system in a useful
manner instead of making guesswork?
Let me know a better way to do that, and I will submit a patch.
Thanks!
--
Ryan Underwood, <nemesis at icequake.net>, icq=10317253
-------------------------------------------------------
This SF.Net email sponsored by: Free pre-built ASP.NET sites including
Data Reports, E-commerce, Portals, and Forums are available now.
Download today and enter to win an XBOX or Visual Studio .NET.
http://aspnet.click-url.com/go/psa00100006ave/direct;at.asp_061203_01/01
_______________________________________________
Alsa-devel mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/alsa-devel