summaryrefslogtreecommitdiff
path: root/driver/xf86-video-radeonhd/src/rhd_hdmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/xf86-video-radeonhd/src/rhd_hdmi.c')
-rw-r--r--driver/xf86-video-radeonhd/src/rhd_hdmi.c529
1 files changed, 529 insertions, 0 deletions
diff --git a/driver/xf86-video-radeonhd/src/rhd_hdmi.c b/driver/xf86-video-radeonhd/src/rhd_hdmi.c
new file mode 100644
index 000000000..e342ef46b
--- /dev/null
+++ b/driver/xf86-video-radeonhd/src/rhd_hdmi.c
@@ -0,0 +1,529 @@
+/*
+ * Copyright 2008 Christian König <deathsimple@vodafone.de>
+ * Copyright 2007 Luc Verhaegen <lverhaegen@novell.com>
+ * Copyright 2007 Matthias Hopf <mhopf@novell.com>
+ * Copyright 2007 Egbert Eich <eich@novell.com>
+ * Copyright 2007 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "xf86.h"
+
+#include "rhd.h"
+#include "rhd_audio.h"
+#include "rhd_connector.h"
+#include "rhd_output.h"
+#include "rhd_hdmi.h"
+#include "rhd_regs.h"
+
+enum HdmiColorFormat {
+ RGB = 0,
+ YCC_422 = 1,
+ YCC_444 = 2
+};
+
+struct {
+ CARD32 Clock;
+
+ int N_32kHz;
+ int CTS_32kHz;
+
+ int N_44_1kHz;
+ int CTS_44_1kHz;
+
+ int N_48kHz;
+ int CTS_48kHz;
+
+} AudioClockRegeneration[] = {
+ /* 32kHz 44.1kHz 48kHz */
+ /* Clock N CTS N CTS N CTS */
+ { 25174, 4576, 28125, 7007, 31250, 6864, 28125 }, /* 25,20/1.001 MHz */
+ { 25200, 4096, 25200, 6272, 28000, 6144, 25200 }, /* 25.20 MHz */
+ { 27000, 4096, 27000, 6272, 30000, 6144, 27000 }, /* 27.00 MHz */
+ { 27027, 4096, 27027, 6272, 30030, 6144, 27027 }, /* 27.00*1.001 MHz */
+ { 54000, 4096, 54000, 6272, 60000, 6144, 54000 }, /* 54.00 MHz */
+ { 54054, 4096, 54054, 6272, 60060, 6144, 54054 }, /* 54.00*1.001 MHz */
+ { 74175, 11648, 210937, 17836, 234375, 11648, 140625 }, /* 74.25/1.001 MHz */
+ { 74250, 4096, 74250, 6272, 82500, 6144, 74250 }, /* 74.25 MHz */
+ { 148351, 11648, 421875, 8918, 234375, 5824, 140625 }, /* 148.50/1.001 MHz */
+ { 148500, 4096, 148500, 6272, 165000, 6144, 148500 }, /* 148.50 MHz */
+ { 0, 4096, 0, 6272, 0, 6144, 0 } /* Other */
+};
+
+/*
+ * calculate CTS value if it's not found in the table
+ */
+static void
+HdmiCalcCTS(struct rhdHdmi *hdmi, CARD32 Clock, int* CTS, int N, int freq)
+{
+ if(*CTS == 0) *CTS = (long long)Clock*1000*N/(128*freq);
+ xf86DrvMsg(hdmi->scrnIndex, X_INFO, "Using ACR timing N=%d CTS=%d for frequency %d\n",N,*CTS,freq);
+}
+
+/*
+ * update the N and CTS parameters for a given clock rate
+ */
+static void
+HdmiAudioClockRegeneration(struct rhdHdmi *hdmi, CARD32 Clock)
+{
+ int CTS;
+ int N;
+ int i;
+ for(i=0; AudioClockRegeneration[i].Clock != Clock && AudioClockRegeneration[i].Clock != 0; i++);
+
+ CTS = AudioClockRegeneration[i].CTS_32kHz;
+ N = AudioClockRegeneration[i].N_32kHz;
+ HdmiCalcCTS(hdmi, Clock, &CTS, N, 32000);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_32kHz_CTS, CTS << 12);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_32kHz_N, N);
+
+ CTS = AudioClockRegeneration[i].CTS_44_1kHz;
+ N = AudioClockRegeneration[i].N_44_1kHz;
+ HdmiCalcCTS(hdmi, Clock, &CTS, N, 44100);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_44_1kHz_CTS, CTS << 12);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_44_1kHz_N, N);
+
+ CTS = AudioClockRegeneration[i].CTS_48kHz;
+ N = AudioClockRegeneration[i].N_48kHz;
+ HdmiCalcCTS(hdmi, Clock, &CTS, N, 48000);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_48kHz_CTS, CTS << 12);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_48kHz_N, N);
+}
+
+/*
+ * calculate the crc for a given info frame
+ */
+static void
+HdmiInfoFrameChecksum(CARD8 packetType, CARD8 versionNumber, CARD8 length, CARD8* frame)
+{
+ int i;
+ frame[0] = packetType + versionNumber + length;
+ for(i=1;i<=length;i++)
+ frame[0] += frame[i];
+ frame[0] = 0x100 - frame[0];
+}
+
+/*
+ * build a HDMI Video Info Frame
+ */
+static void
+HdmiVideoInfoFrame(
+ struct rhdHdmi *hdmi,
+ enum HdmiColorFormat ColorFormat,
+ Bool ActiveInformationPresent,
+ CARD8 ActiveFormatAspectRatio,
+ CARD8 ScanInformation,
+ CARD8 Colorimetry,
+ CARD8 ExColorimetry,
+ CARD8 Quantization,
+ Bool ITC,
+ CARD8 PictureAspectRatio,
+ CARD8 VideoFormatIdentification,
+ CARD8 PixelRepetition,
+ CARD8 NonUniformPictureScaling,
+ CARD8 BarInfoDataValid,
+ CARD16 TopBar,
+ CARD16 BottomBar,
+ CARD16 LeftBar,
+ CARD16 RightBar
+)
+{
+ CARD8 frame[14];
+
+ frame[0x0] = 0;
+ frame[0x1] =
+ (ScanInformation & 0x3) |
+ ((BarInfoDataValid & 0x3) << 2) |
+ ((ActiveInformationPresent & 0x1) << 4) |
+ ((ColorFormat & 0x3) << 5);
+ frame[0x2] =
+ (ActiveFormatAspectRatio & 0xF) |
+ ((PictureAspectRatio & 0x3) << 4) |
+ ((Colorimetry & 0x3) << 6);
+ frame[0x3] =
+ (NonUniformPictureScaling & 0x3) |
+ ((Quantization & 0x3) << 2) |
+ ((ExColorimetry & 0x7) << 4) |
+ ((ITC & 0x1) << 7);
+ frame[0x4] = (VideoFormatIdentification & 0x7F);
+ frame[0x5] = (PixelRepetition & 0xF);
+ frame[0x6] = (TopBar & 0xFF);
+ frame[0x7] = (TopBar >> 8);
+ frame[0x8] = (BottomBar & 0xFF);
+ frame[0x9] = (BottomBar >> 8);
+ frame[0xA] = (LeftBar & 0xFF);
+ frame[0xB] = (LeftBar >> 8);
+ frame[0xC] = (RightBar & 0xFF);
+ frame[0xD] = (RightBar >> 8);
+
+ HdmiInfoFrameChecksum(0x82, 0x02, 0x0D, frame);
+
+
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_0,
+ frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_1,
+ frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x7] << 24));
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_2,
+ frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24));
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_3,
+ frame[0xC] | (frame[0xD] << 8));
+}
+
+/*
+ * build a Audio Info Frame
+ */
+static void
+HdmiAudioInfoFrame(
+ struct rhdHdmi *hdmi,
+ CARD8 ChannelCount,
+ CARD8 CodingType,
+ CARD8 SampleSize,
+ CARD8 SampleFrequency,
+ CARD8 Format,
+ CARD8 ChannelAllocation,
+ CARD8 LevelShift,
+ Bool DownmixInhibit
+)
+{
+ CARD8 frame[11];
+
+ frame[0x0] = 0;
+ frame[0x1] = (ChannelCount & 0x7) | ((CodingType & 0xF) << 4);
+ frame[0x2] = (SampleSize & 0x3) | ((SampleFrequency & 0x7) << 2);
+ frame[0x3] = Format;
+ frame[0x4] = ChannelAllocation;
+ frame[0x5] = ((LevelShift & 0xF) << 3) | ((DownmixInhibit & 0x1) << 7);
+ frame[0x6] = 0;
+ frame[0x7] = 0;
+ frame[0x8] = 0;
+ frame[0x9] = 0;
+ frame[0xA] = 0;
+
+ HdmiInfoFrameChecksum(0x84, 0x01, 0x0A, frame);
+
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIOINFOFRAME_0,
+ frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIOINFOFRAME_1,
+ frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x8] << 24));
+}
+
+/*
+ * it's unknown what these bits do excatly, but it's indeed quite usefull for debugging
+ */
+static void
+HdmiAudioDebugWorkaround(struct rhdHdmi* hdmi, Bool Enable)
+{
+ if(Enable) {
+ RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0x1000, 0x1000);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIO_DEBUG, 0xffffff);
+ } else {
+ RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0, 0x1000);
+ }
+}
+
+/*
+ * allocate/initialize the HDMI structure
+ * and register with audio engine
+ * output selects which engine is used
+ */
+struct rhdHdmi*
+RHDHdmiInit(RHDPtr rhdPtr, struct rhdOutput* Output)
+{
+ struct rhdHdmi *hdmi;
+ RHDFUNC(rhdPtr);
+
+ if(rhdPtr->ChipSet >= RHD_R600) {
+ 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;
+ }
+ hdmi->Stored = FALSE;
+ RHDAudioRegisterHdmi(rhdPtr, hdmi);
+ return hdmi;
+ } else
+ return NULL;
+}
+
+/*
+ * update the info frames with the data from the current display mode
+ */
+void
+RHDHdmiSetMode(struct rhdHdmi *hdmi, DisplayModePtr Mode)
+{
+ if(!hdmi) return;
+ RHDFUNC(hdmi);
+
+ RHDAudioSetClock(RHDPTRI(hdmi), hdmi->Output, Mode->Clock);
+
+ 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);
+
+ HdmiAudioClockRegeneration(hdmi, Mode->Clock);
+
+ 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);
+
+ /* 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 settings whith current parameters from audio engine
+ */
+void
+RHDHdmiUpdateAudioSettings(
+ struct rhdHdmi* hdmi,
+ Bool playing,
+ int channels,
+ int rate,
+ int bps,
+ CARD8 status_bits,
+ CARD8 category_code
+)
+{
+ CARD32 iec;
+
+ if(!hdmi) return;
+ RHDFUNC(hdmi);
+
+ xf86DrvMsg(hdmi->scrnIndex, X_INFO, "%s: %s with "
+ "%d channels, %d Hz sampling rate, %d bits per sample,\n",
+ __func__, playing ? "playing" : "stoped", channels, rate, bps);
+ xf86DrvMsg(hdmi->scrnIndex, X_INFO, "%s: "
+ "0x%02x IEC60958 status bits and 0x%02x category code\n",
+ __func__, (int)status_bits, (int)category_code);
+
+ /* start delivering audio frames */
+ RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, playing ? 1 : 0, 0x1);
+
+ iec = 0;
+ if(status_bits & AUDIO_STATUS_PROFESSIONAL) iec |= 1 << 0;
+ if(status_bits & AUDIO_STATUS_NONAUDIO) iec |= 1 << 1;
+ if(status_bits & AUDIO_STATUS_COPYRIGHT) iec |= 1 << 2;
+ if(status_bits & AUDIO_STATUS_EMPHASIS) iec |= 1 << 3;
+
+ iec |= category_code << 8;
+
+ switch(rate)
+ {
+ case 32000: iec |= 0x3 << 24; break;
+ case 44100: iec |= 0x0 << 24; break;
+ case 88200: iec |= 0x8 << 24; break;
+ case 176400: iec |= 0xc << 24; break;
+ case 48000: iec |= 0x2 << 24; break;
+ case 96000: iec |= 0xa << 24; break;
+ case 192000: iec |= 0xe << 24; break;
+ }
+
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_IEC60958_1, iec);
+
+ iec = 0;
+ switch(bps)
+ {
+ case 16: iec |= 0x2; break;
+ case 20: iec |= 0x3; break;
+ case 24: iec |= 0xb; break;
+ }
+ if(status_bits & AUDIO_STATUS_V) iec |= 0x5 << 16;
+
+ 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);
+
+ RHDRegMask(hdmi, hdmi->Offset+HDMI_CNTL, 0x400000, 0x400000);
+}
+
+/*
+ * enable/disable the HDMI engine
+ */
+void
+RHDHdmiEnable(struct rhdHdmi *hdmi, Bool Enable)
+{
+ if(!hdmi) return;
+ RHDFUNC(hdmi);
+
+ /* 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;
+ }
+}
+
+/*
+ * save the current config of HDMI engine
+ */
+void
+RHDHdmiSave(struct rhdHdmi *hdmi)
+{
+ if(!hdmi) return;
+ RHDFUNC(hdmi);
+
+ 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->StoredFrameVersion = RHDRegRead(hdmi, hdmi->Offset+HDMI_VERSION);
+
+ hdmi->StoredVideoControl = RHDRegRead(hdmi, hdmi->Offset+HDMI_VIDEOCNTL);
+ hdmi->StoreVideoInfoFrame[0x0] = RHDRegRead(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_0);
+ hdmi->StoreVideoInfoFrame[0x1] = RHDRegRead(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_1);
+ hdmi->StoreVideoInfoFrame[0x2] = RHDRegRead(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_2);
+ hdmi->StoreVideoInfoFrame[0x3] = RHDRegRead(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_3);
+
+ hdmi->StoredAudioControl = RHDRegRead(hdmi, hdmi->Offset+HDMI_AUDIOCNTL);
+ hdmi->StoreAudioInfoFrame[0x0] = RHDRegRead(hdmi, hdmi->Offset+HDMI_AUDIOINFOFRAME_0);
+ hdmi->StoreAudioInfoFrame[0x1] = RHDRegRead(hdmi, hdmi->Offset+HDMI_AUDIOINFOFRAME_1);
+
+ hdmi->Store_32kHz_N = RHDRegRead(hdmi, hdmi->Offset+HDMI_32kHz_N);
+ hdmi->Store_32kHz_CTS = RHDRegRead(hdmi, hdmi->Offset+HDMI_32kHz_CTS);
+
+ hdmi->Store_44_1kHz_N = RHDRegRead(hdmi, hdmi->Offset+HDMI_44_1kHz_N);
+ hdmi->Store_44_1kHz_CTS = RHDRegRead(hdmi, hdmi->Offset+HDMI_44_1kHz_CTS);
+
+ hdmi->Store_48kHz_N = RHDRegRead(hdmi, hdmi->Offset+HDMI_48kHz_N);
+ hdmi->Store_48kHz_CTS = RHDRegRead(hdmi, hdmi->Offset+HDMI_48kHz_CTS);
+
+ hdmi->StoreIEC60958[0] = RHDRegRead(hdmi, hdmi->Offset+HDMI_IEC60958_1);
+ hdmi->StoreIEC60958[1] = RHDRegRead(hdmi, hdmi->Offset+HDMI_IEC60958_2);
+
+ hdmi->StoreUnknown[0x0] = RHDRegRead(hdmi, hdmi->Offset+HDMI_UNKNOWN_0);
+ hdmi->StoreUnknown[0x1] = RHDRegRead(hdmi, hdmi->Offset+HDMI_UNKNOWN_1);
+ hdmi->StoreUnknown[0x2] = RHDRegRead(hdmi, hdmi->Offset+HDMI_UNKNOWN_2);
+
+ hdmi->Stored = TRUE;
+}
+
+/*
+ * restore the saved config of HDMI engine
+ */
+void
+RHDHdmiRestore(struct rhdHdmi *hdmi)
+{
+ if(!hdmi) return;
+ RHDFUNC(hdmi);
+
+ if (!hdmi->Stored) {
+ xf86DrvMsg(hdmi->scrnIndex, X_ERROR, "%s: trying to restore "
+ "uninitialized values.\n", __func__);
+ return;
+ }
+
+ 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_VERSION, hdmi->StoredFrameVersion);
+
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_VIDEOCNTL, hdmi->StoredVideoControl);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_0, hdmi->StoreVideoInfoFrame[0x0]);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_1, hdmi->StoreVideoInfoFrame[0x1]);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_2, hdmi->StoreVideoInfoFrame[0x2]);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_VIDEOINFOFRAME_3, hdmi->StoreVideoInfoFrame[0x3]);
+
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIOCNTL, hdmi->StoredAudioControl);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIOINFOFRAME_0, hdmi->StoreAudioInfoFrame[0x0]);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_AUDIOINFOFRAME_1, hdmi->StoreAudioInfoFrame[0x1]);
+
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_32kHz_N, hdmi->Store_32kHz_N);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_32kHz_CTS, hdmi->Store_32kHz_CTS);
+
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_44_1kHz_N, hdmi->Store_44_1kHz_N);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_44_1kHz_CTS, hdmi->Store_44_1kHz_CTS);
+
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_48kHz_N, hdmi->Store_48kHz_N);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_48kHz_CTS, hdmi->Store_48kHz_CTS);
+
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_IEC60958_1, hdmi->StoreIEC60958[0]);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_IEC60958_2, hdmi->StoreIEC60958[1]);
+
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_UNKNOWN_0, hdmi->StoreUnknown[0x0]);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_UNKNOWN_1, hdmi->StoreUnknown[0x1]);
+ RHDRegWrite(hdmi, hdmi->Offset+HDMI_UNKNOWN_2, hdmi->StoreUnknown[0x2]);
+}
+
+/*
+ * unregister with audio engine and release memory
+ */
+void
+RHDHdmiDestroy(struct rhdHdmi *hdmi)
+{
+ if(!hdmi) return;
+ RHDFUNC(hdmi);
+
+ RHDAudioUnregisterHdmi(RHDPTRI(hdmi), hdmi);
+
+ xfree(hdmi);
+}