Hi, I noticed some messages on the HDMI audio part for an RS690. I have also been experimenting with this feature and got a working setup after quite some experimenting and puzzling. I think the patch does not break support for R600 and up cards but no guarantee here. Right now audio works, however AC3 or DTS pass through is not working yet. Have to do some more puzzling here. Another challenge I'm working on is tear-free video playback on a full-HD screen. Right now I have a working setup using the AVIVO overlay option with a double buffering setup. This however needs a lot more work and can be best called a proof of concept at the moment. Hope this helps, Christiaan van Dijk. diff -u -r xf86-video-radeonhd/src/rhd_audio.c xf86-video-radeonhd-work/src/rhd_audio.c --- xf86-video-radeonhd/src/rhd_audio.c 2009-03-24 18:35:53.000000000 +0100 +++ xf86-video-radeonhd-work/src/rhd_audio.c 2009-04-06 19:06:02.000000000 +0200 @@ -228,41 +228,49 @@ xf86DrvMsg(Audio->scrnIndex, X_INFO, "%s: using %s as clock source with %d khz\n", __func__, Output->Name, (int)Clock); - switch(Output->Id) { - case RHD_OUTPUT_TMDSA: - case RHD_OUTPUT_LVTMA: - RHDRegMask(Audio, AUDIO_TIMING, 0, 0x301); - break; - - case RHD_OUTPUT_UNIPHYA: - case RHD_OUTPUT_UNIPHYB: - case RHD_OUTPUT_KLDSKP_LVTMA: - RHDRegMask(Audio, AUDIO_TIMING, 0x100, 0x301); - break; + if (rhdPtr->ChipSet > RHD_RS740) { + switch(Output->Id) { + case RHD_OUTPUT_TMDSA: + case RHD_OUTPUT_LVTMA: + RHDRegMask(Audio, AUDIO_TIMING, 0, 0x301); + break; + + case RHD_OUTPUT_UNIPHYA: + case RHD_OUTPUT_UNIPHYB: + case RHD_OUTPUT_KLDSKP_LVTMA: + RHDRegMask(Audio, AUDIO_TIMING, 0x100, 0x301); + break; - default: - break; - } + default: + break; + } - switch(Output->Id) { - case RHD_OUTPUT_TMDSA: - case RHD_OUTPUT_UNIPHYA: - RHDRegWrite(Audio, AUDIO_PLL1_MUL, Rate*50); - RHDRegWrite(Audio, AUDIO_PLL1_DIV, Clock*100); - RHDRegWrite(Audio, AUDIO_CLK_SRCSEL, 0); - break; - - case RHD_OUTPUT_LVTMA: - case RHD_OUTPUT_UNIPHYB: - case RHD_OUTPUT_KLDSKP_LVTMA: - RHDRegWrite(Audio, AUDIO_PLL2_MUL, Rate*50); - RHDRegWrite(Audio, AUDIO_PLL2_DIV, Clock*100); - RHDRegWrite(Audio, AUDIO_CLK_SRCSEL, 1); - break; - - default: - xf86DrvMsg(Audio->scrnIndex, X_ERROR, "%s: unsupported output type\n", __func__); - break; + switch(Output->Id) { + case RHD_OUTPUT_TMDSA: + case RHD_OUTPUT_UNIPHYA: + RHDRegWrite(Audio, AUDIO_PLL1_MUL, Rate*50); + RHDRegWrite(Audio, AUDIO_PLL1_DIV, Clock*100); + RHDRegWrite(Audio, AUDIO_CLK_SRCSEL, 0); + break; + + case RHD_OUTPUT_LVTMA: + case RHD_OUTPUT_UNIPHYB: + case RHD_OUTPUT_KLDSKP_LVTMA: + RHDRegWrite(Audio, AUDIO_PLL2_MUL, Rate*50); + RHDRegWrite(Audio, AUDIO_PLL2_DIV, Clock*100); + RHDRegWrite(Audio, AUDIO_CLK_SRCSEL, 1); + break; + + default: + xf86DrvMsg(Audio->scrnIndex, X_ERROR, "%s: unsupported output type\n", __func__); + break; + } + } else { + /* + * RS600, RS690, RS730? + */ + RHDRegWrite(Audio, AUDIO_PLL1_MUL, Rate * 50); + RHDRegWrite(Audio, AUDIO_PLL1_DIV, Clock * 100); } } @@ -287,12 +295,22 @@ xf86DrvMsg(Audio->scrnIndex, X_WARNING, "%s: reserved codec bits set 0x%x\n", __func__, (int) codec); - if(clear) { - RHDRegWrite(Audio, AUDIO_SUPPORTED_SIZE_RATE, config); - RHDRegWrite(Audio, AUDIO_SUPPORTED_CODEC, codec); + if (rhdPtr->ChipSet > RHD_RS740) { + if(clear) { + RHDRegWrite(Audio, AUDIO_SUPPORTED_SIZE_RATE, config); + RHDRegWrite(Audio, AUDIO_SUPPORTED_CODEC, codec); + } else { + RHDRegMask(Audio, AUDIO_SUPPORTED_SIZE_RATE, config, config); + RHDRegMask(Audio, AUDIO_SUPPORTED_CODEC, codec, codec); + } } else { - RHDRegMask(Audio, AUDIO_SUPPORTED_SIZE_RATE, config, config); - RHDRegMask(Audio, AUDIO_SUPPORTED_CODEC, codec, codec); + /* + * RS600, RS690, RS730? + */ + if(clear) + RHDRegWrite(Audio, AUDIO_SUPPORTED_CODEC, codec); + else + RHDRegMask(Audio, AUDIO_SUPPORTED_CODEC, codec, codec); } } @@ -375,7 +393,9 @@ return; } - /* shoutdown the audio engine before doing anything else */ + /* + * Shutdown the audio engine before doing anything else. + */ RHDAudioSetEnable(rhdPtr, FALSE); RHDRegWrite(Audio, AUDIO_TIMING, Audio->StoreTiming); diff -u -r xf86-video-radeonhd/src/rhd_hdmi.c xf86-video-radeonhd-work/src/rhd_hdmi.c --- xf86-video-radeonhd/src/rhd_hdmi.c 2009-03-24 18:35:54.000000000 +0100 +++ xf86-video-radeonhd-work/src/rhd_hdmi.c 2009-04-06 19:09:26.000000000 +0200 @@ -236,7 +236,7 @@ { if(Enable) { RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0x1000, 0x1000); - RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG, 0xffffff); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_0, 0xffffff); } else { RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0, 0x1000); } @@ -253,34 +253,46 @@ struct rhdHdmi *hdmi; RHDFUNC(rhdPtr); - if(rhdPtr->ChipSet >= RHD_R600) { + if(rhdPtr->ChipSet >= RHD_RS600) { hdmi = (struct rhdHdmi *) xnfcalloc(sizeof(struct rhdHdmi), 1); hdmi->scrnIndex = rhdPtr->scrnIndex; hdmi->Output = Output; - switch(Output->Id) { - case RHD_OUTPUT_TMDSA: - hdmi->Offset = HDMI_TMDS; - break; - - case RHD_OUTPUT_LVTMA: - hdmi->Offset = HDMI_LVTMA; - break; - - case RHD_OUTPUT_UNIPHYA: - hdmi->Offset = HDMI_TMDS; - break; - - case RHD_OUTPUT_KLDSKP_LVTMA: - hdmi->Offset = HDMI_DIG; - break; - - /*case RHD_OUTPUT_UNIPHYB: */ - - default: - xf86DrvMsg(hdmi->scrnIndex, X_ERROR, "%s: unknown HDMI output type\n", __func__); - xfree(hdmi); - return NULL; - break; + /* I really don't like this... */ + hdmi->ChipSet = rhdPtr->ChipSet; + if (rhdPtr->ChipSet > RHD_RS740) { + /* + * Above RS740. + */ + switch(Output->Id) { + case RHD_OUTPUT_TMDSA: + hdmi->Offset = HDMI_TMDS; + break; + + case RHD_OUTPUT_LVTMA: + hdmi->Offset = HDMI_LVTMA; + break; + + case RHD_OUTPUT_UNIPHYA: + hdmi->Offset = HDMI_TMDS; + break; + + case RHD_OUTPUT_KLDSKP_LVTMA: + hdmi->Offset = HDMI_DIG; + break; + + /*case RHD_OUTPUT_UNIPHYB: */ + + default: + xf86DrvMsg(hdmi->scrnIndex, X_ERROR, "%s: unknown HDMI output type\n", __func__); + xfree(hdmi); + return NULL; + break; + } + } else { + /* + * RS600, RS690, RS740? + */ + hdmi->Offset = HDMI_TMDS; } hdmi->Stored = FALSE; RHDAudioRegisterHdmi(rhdPtr, hdmi); @@ -298,31 +310,60 @@ if(!hdmi) return; RHDFUNC(hdmi); - RHDAudioSetClock(RHDPTRI(hdmi), hdmi->Output, Mode->Clock); + if (hdmi->ChipSet > RHD_RS740) { + /* + * Above RS740. + */ + RHDAudioSetClock(RHDPTRI(hdmi), hdmi->Output, Mode->Clock); - HdmiAudioDebugWorkaround(hdmi, FALSE); + HdmiAudioDebugWorkaround(hdmi, FALSE); - RHDRegWrite(hdmi, hdmi->Offset+HDMI_UNKNOWN_0, 0x1000); - RHDRegWrite(hdmi, hdmi->Offset+HDMI_UNKNOWN_1, 0x0); - RHDRegWrite(hdmi, hdmi->Offset+HDMI_UNKNOWN_2, 0x1000); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_UNKNOWN_0, 0x1000); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_UNKNOWN_1, 0x0); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_UNKNOWN_2, 0x1000); - HdmiAudioClockRegeneration(hdmi, Mode->Clock); + HdmiAudioClockRegeneration(hdmi, Mode->Clock); - RHDRegWrite(hdmi, hdmi->Offset+HDMI_VIDEOCNTL, 0x13); - RHDRegWrite(hdmi, hdmi->Offset+HDMI_VERSION, 0x202); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_VIDEOCNTL, 0x13); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_VERSION, 0x202); - HdmiVideoInfoFrame(hdmi, RGB, FALSE, 0, 0, 0, - 0, 0, FALSE, 0, 0, 0, 0, 0, 0, 0, 0, 0); + HdmiVideoInfoFrame(hdmi, RGB, FALSE, 0, 0, 0, + 0, 0, FALSE, 0, 0, 0, 0, 0, 0, 0, 0, 0); - /* audio packets per line, does anyone know how to calc this ? */ - RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0x020000, 0x1F0000); + /* audio packets per line, does anyone know how to calc this ? */ + RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0x020000, 0x1F0000); - /* update? reset? don't realy know */ - RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0x14000000, 0x14000000); + /* update? reset? don't realy know */ + RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0x14000000, 0x14000000); + } else { + /* + * RS600, RS690, RS740? + */ + RHDAudioSetClock(RHDPTRI(hdmi), hdmi->Output, Mode->Clock); + + /* Debug stuff */ + RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_0, 0x00FFFFFF); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_1, 0x007FFFFF); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_2, 0x00000001); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_3, 0x00000001); + HdmiAudioDebugWorkaround(hdmi, FALSE); + + /* Setup video info frame. */ + RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0x04020011, 0x04070111); + RHDRegMask(hdmi, hdmi->Offset+HDMI_UNKNOWN_2, 0x00001100, 0x00001100); + RHDRegMask(hdmi, hdmi->Offset+HDMI_VIDEOCNTL, 0x000000B3, 0x000000B3); + RHDRegMask(hdmi, hdmi->Offset+HDMI_VERSION, 0x00000202, 0x00003F3F); + RHDRegMask(hdmi, hdmi->Offset+HDMI_UNKNOWN_1, 0x00000000, 0x00000001); + + HdmiAudioClockRegeneration(hdmi, Mode->Clock); + + HdmiVideoInfoFrame(hdmi, RGB, FALSE, 0, 0, 0, + 0, 0, FALSE, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } } /* - * update settings whith current parameters from audio engine + * update settings with current parameters from audio engine */ void RHDHdmiUpdateAudioSettings( @@ -382,10 +423,27 @@ RHDRegMask(hdmi, hdmi->Offset+HDMI_IEC60958_2, iec, 0x5000f); - RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIOCNTL, 0x31); - HdmiAudioInfoFrame(hdmi, channels-1, 0, 0, 0, 0, 0, 0, FALSE); + if (hdmi->ChipSet > RHD_RS740) + { + /* + * Above RS740. + */ + RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIOCNTL, 0x31); + HdmiAudioInfoFrame(hdmi, channels-1, 0, 0, 0, 0, 0, 0, FALSE); + + /* is this OK? Not 0x4000000 ?? */ + RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0x400000, 0x400000); + } else { + /* + * RS600, RS690, RS740? + */ + /* 0x021 or 0x031 sets the audio frame length */ + RHDRegMask(hdmi, hdmi->Offset+HDMI_AUDIOCNTL, 0x00000031, 0x00000031); + HdmiAudioInfoFrame(hdmi, channels-1, 0, 0, 0, 0, 0, 0, FALSE); + RHDRegMask(hdmi, hdmi->Offset+HDMI_VIDEOCNTL, 0xC0, 0xC0); - RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0x400000, 0x400000); + RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0x4000000, 0x4000000); + } } /* @@ -399,26 +457,57 @@ /* some version of atombios ignore the enable HDMI flag * so enabling/disabling HDMI was moved here for TMDSA and LVTMA */ - switch(hdmi->Output->Id) { - case RHD_OUTPUT_TMDSA: - RHDRegMask(hdmi, TMDSA_CNTL, Enable ? 0x4 : 0x0, 0x4); - RHDRegWrite(hdmi, hdmi->Offset+HDMI_ENABLE, Enable ? 0x101 : 0x0); - break; - - case RHD_OUTPUT_LVTMA: - RHDRegMask(hdmi, LVTMA_CNTL, Enable ? 0x4 : 0x0, 0x4); - RHDRegWrite(hdmi, hdmi->Offset+HDMI_ENABLE, Enable ? 0x105 : 0x0); - break; - - case RHD_OUTPUT_UNIPHYA: - case RHD_OUTPUT_UNIPHYB: - case RHD_OUTPUT_KLDSKP_LVTMA: - RHDRegWrite(hdmi, hdmi->Offset+HDMI_ENABLE, Enable ? 0x110 : 0x0); - break; - - default: - xf86DrvMsg(hdmi->scrnIndex, X_ERROR, "%s: unknown HDMI output type\n", __func__); - break; + if(hdmi->ChipSet > RHD_RS740) { + /* + * Above RS740. + */ + switch(hdmi->Output->Id) { + case RHD_OUTPUT_TMDSA: + RHDRegMask(hdmi, TMDSA_CNTL, Enable ? 0x4 : 0x0, 0x4); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_ENABLE, Enable ? 0x101 : 0x0); + break; + + case RHD_OUTPUT_LVTMA: + RHDRegMask(hdmi, LVTMA_CNTL, Enable ? 0x4 : 0x0, 0x4); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_ENABLE, Enable ? 0x105 : 0x0); + break; + + case RHD_OUTPUT_UNIPHYA: + case RHD_OUTPUT_UNIPHYB: + case RHD_OUTPUT_KLDSKP_LVTMA: + /* This part is doubtfull in my opinion */ + RHDRegWrite(hdmi, hdmi->Offset+HDMI_ENABLE, Enable ? 0x110 : 0x0); + break; + + default: + xf86DrvMsg(hdmi->scrnIndex, X_ERROR, "%s: unknown HDMI output type\n", __func__); + break; + } + } else { + /* + * RS600?, RS690, and RS740? + */ + switch(hdmi->Output->Id) { + case RHD_OUTPUT_TMDSA: + RHDRegMask(hdmi, HDMI_TMDS+HDMI_ENABLE, Enable ? 0x01 : 0x00, 0x0D); + break; + + case RHD_OUTPUT_LVTMA: + RHDRegMask(hdmi, HDMI_TMDS+HDMI_ENABLE, Enable ? 0x05 : 0x00, 0x0D); + RHDRegMask(hdmi, LVTMA_CNTL, Enable ? 0x04 : 0x00, 0x04); + break; + + case RHD_OUTPUT_UNIPHYA: + case RHD_OUTPUT_UNIPHYB: + case RHD_OUTPUT_KLDSKP_LVTMA: + RHDRegMask(hdmi, HDMI_TMDS+HDMI_ENABLE, Enable ? 0x0D : 0x00, 0x0D); + RHDRegMask(hdmi, RS69_DDIA_CNTL, Enable ? 0x04 : 0x00, 0x04); + break; + + default: + xf86DrvMsg(hdmi->scrnIndex, X_ERROR, "%s: unknown HDMI output type\n", __func__); + break; + } } } @@ -433,7 +522,10 @@ hdmi->StoreEnable = RHDRegRead(hdmi, hdmi->Offset+HDMI_ENABLE); hdmi->StoreControl = RHDRegRead(hdmi, hdmi->Offset+HDMI_CNTL); - hdmi->StoredAudioDebugWorkaround = RHDRegRead(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG); + hdmi->StoredAudioDebugWorkaround[0x0] = RHDRegRead(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_0); + hdmi->StoredAudioDebugWorkaround[0x1] = RHDRegRead(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_1); + hdmi->StoredAudioDebugWorkaround[0x2] = RHDRegRead(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_2); + hdmi->StoredAudioDebugWorkaround[0x3] = RHDRegRead(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_3); hdmi->StoredFrameVersion = RHDRegRead(hdmi, hdmi->Offset+HDMI_VERSION); @@ -483,7 +575,10 @@ RHDRegWrite(hdmi, hdmi->Offset+HDMI_ENABLE, hdmi->StoreEnable); RHDRegWrite(hdmi, hdmi->Offset+HDMI_CNTL, hdmi->StoreControl); - RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG, hdmi->StoredAudioDebugWorkaround); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_0, hdmi->StoredAudioDebugWorkaround[0x0]); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_1, hdmi->StoredAudioDebugWorkaround[0x1]); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_2, hdmi->StoredAudioDebugWorkaround[0x2]); + RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG_3, hdmi->StoredAudioDebugWorkaround[0x3]); RHDRegWrite(hdmi, hdmi->Offset+HDMI_VERSION, hdmi->StoredFrameVersion); diff -u -r xf86-video-radeonhd/src/rhd_hdmi.h xf86-video-radeonhd-work/src/rhd_hdmi.h --- xf86-video-radeonhd/src/rhd_hdmi.h 2009-03-24 18:35:54.000000000 +0100 +++ xf86-video-radeonhd-work/src/rhd_hdmi.h 2009-04-05 17:31:57.000000000 +0200 @@ -29,6 +29,7 @@ struct rhdHdmi { struct rhdHdmi* Next; + enum RHD_CHIPSETS ChipSet; int scrnIndex; @@ -39,7 +40,7 @@ CARD32 StoreEnable; CARD32 StoreControl; CARD32 StoreUnknown[0x3]; - CARD32 StoredAudioDebugWorkaround; + CARD32 StoredAudioDebugWorkaround[0x4]; CARD32 StoredFrameVersion; CARD32 StoredVideoControl; diff -u -r xf86-video-radeonhd/src/rhd_regs.h xf86-video-radeonhd-work/src/rhd_regs.h --- xf86-video-radeonhd/src/rhd_regs.h 2009-03-24 18:35:54.000000000 +0100 +++ xf86-video-radeonhd-work/src/rhd_regs.h 2009-04-05 17:13:51.000000000 +0200 @@ -1113,7 +1113,10 @@ HDMI_IEC60958_1 = 0xd4, HDMI_IEC60958_2 = 0xd8, HDMI_UNKNOWN_2 = 0xdc, - HDMI_AUDIO_DEBUG = 0xe0 + HDMI_AUDIO_DEBUG_0 = 0xe0, + HDMI_AUDIO_DEBUG_1 = 0xe4, + HDMI_AUDIO_DEBUG_2 = 0xe8, + HDMI_AUDIO_DEBUG_3 = 0xec }; #endif /* _RHD_REGS_H */