[RFH] Add HDMI audio support

Luis R. Rodriguez lrodriguez at atheros.com
Fri Jul 24 10:24:41 PDT 2009


This is based on radeonhd driver, this is a port to radeon.

Cc: Egbert Eich   <eich at novell.com>
Cc: Matthias Hopf <mhopf at novell.com>
Cc: Luc Verhaegen <libv at exsuse.de>
Cc: Christian König <deathsimple at vodafone.de>
Signed-off-by: Luis R. Rodriguez <mcgrof at gmail.com>
---

RFT - Request For Help/patch takeover

I've taken a stab at porting HDMI support from the radeonhd driver
onto the radeon driver. Mind you this is my first video patch, so
not sure if it was done properly. I kept telling myself I was going
to finish this during my night hours but my night hours are now reserved.
So if it is at least done some-what right was hoping someone could take this
on themselves and complete it... The missing piece should be the
generic HDMI stuff which I thought was not required but in fact is.

Forgot if I compile tested.. definitely didn't try loading yet.

I just rebased this onto today's git HEAD.

I do have an HD4800 which I can test.

Anyway, hope someone with more time can take this over.

 src/atombios_output.c   |    2 +
 src/radeon.h            |   96 ++++++++++
 src/radeon_atombios.c   |   17 ++
 src/radeon_hdmi_audio.c |  442 +++++++++++++++++++++++++++++++++++++++++++++++
 src/radeon_macros.h     |    3 +
 src/radeon_probe.h      |    1 +
 src/radeon_reg.h        |    6 +
 7 files changed, 567 insertions(+), 0 deletions(-)
 create mode 100644 src/radeon_hdmi_audio.c

diff --git a/src/atombios_output.c b/src/atombios_output.c
index 00d17cb..73196c0 100644
--- a/src/atombios_output.c
+++ b/src/atombios_output.c
@@ -1509,6 +1509,8 @@ atombios_output_mode_set(xf86OutputPtr output,
 	atombios_output_dig_encoder_setup(output, ATOM_ENABLE);
 	atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_SETUP);
 	atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_ENABLE);
+	if (atombios_get_encoder_mode(output) == ATOM_ENCODER_MODE_HDMI)
+		radeon_hdmi_audio_register();
 	break;
     case ENCODER_OBJECT_ID_INTERNAL_DDI:
 	atombios_output_ddia_setup(output, ATOM_ENABLE);
diff --git a/src/radeon.h b/src/radeon.h
index 3c62fd9..7d280e5 100644
--- a/src/radeon.h
+++ b/src/radeon.h
@@ -1,6 +1,11 @@
 /*
  * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and
  *                VA Linux Systems Inc., Fremont, California.
+ * Copyright 2008  Christian König <deathsimple at vodafone.de>
+ * Copyright 2007  Luc Verhaegen <libv at exsuse.de>
+ * Copyright 2007  Matthias Hopf <mhopf at novell.com>
+ * Copyright 2007  Egbert Eich   <eich at novell.com>
+ * Copyright 2007  Advanced Micro Devices, Inc.
  *
  * All Rights Reserved.
  *
@@ -775,6 +780,34 @@ struct radeon_accel_state {
 
 };
 
+struct radeon_hdmi_audio {
+
+	int scrnIndex;
+
+	//struct rhdHdmi* Registered;
+	OsTimerPtr 	Timer;
+
+	Bool	SavedPlaying;
+	int	SavedChannels;
+	int	SavedRate;
+	int	SavedBitsPerSample;
+	CARD8	SavedStatusBits;
+	CARD8	SavedCategoryCode;
+
+	Bool Stored;
+
+	CARD32 StoreEnabled;
+	CARD32 StoreTiming;
+	CARD32 StoreSupportedSizeRate;
+	CARD32 StoreSupportedCodec;
+
+	CARD32 StorePll1Mul;
+	CARD32 StorePll1Div;
+	CARD32 StorePll2Mul;
+	CARD32 StorePll2Div;
+	CARD32 StoreClockSrcSel;
+};
+
 typedef struct {
     EntityInfoPtr     pEnt;
     pciVideoPtr       PciInfo;
@@ -979,6 +1012,7 @@ typedef struct {
     float igp_system_mclk;
     float igp_ht_link_clk;
     float igp_ht_link_width;
+    struct radeon_hdmi_audio *hdmi_audio;
 
     int can_resize;
     void (*reemit_current2d)(ScrnInfoPtr pScrn, int op); // emit the current 2D state into the IB 
@@ -1005,6 +1039,53 @@ typedef struct {
     int               bicubic_offset;
 } RADEONInfoRec, *RADEONInfoPtr;
 
+/*
+ * used for config value of radeon_hmdi_audio_set_supported
+ */
+enum {
+	AUDIO_RATE_8000_HZ   = 0x00000001,
+	AUDIO_RATE_11025_HZ  = 0x00000002,
+	AUDIO_RATE_16000_HZ  = 0x00000004,
+	AUDIO_RATE_22050_HZ  = 0x00000008,
+	AUDIO_RATE_32000_HZ  = 0x00000010,
+	AUDIO_RATE_44100_HZ  = 0x00000020,
+	AUDIO_RATE_48000_HZ  = 0x00000040,
+	AUDIO_RATE_88200_HZ  = 0x00000080,
+	AUDIO_RATE_96000_HZ  = 0x00000100,
+	AUDIO_RATE_176400_HZ = 0x00000200,
+	AUDIO_RATE_192000_HZ = 0x00000400,
+	AUDIO_RATE_384000_HZ = 0x00000800,
+
+	AUDIO_BPS_8  = 0x00010000,
+	AUDIO_BPS_16 = 0x00020000,
+	AUDIO_BPS_20 = 0x00040000,
+	AUDIO_BPS_24 = 0x00080000,
+	AUDIO_BPS_32 = 0x00100000
+};
+
+/*
+ * used for codec value of radeon_hmdi_audio_set_supported
+ */
+enum {
+	AUDIO_CODEC_PCM      = 0x00000001,
+	AUDIO_CODEC_FLOAT32  = 0x00000002,
+	AUDIO_CODEC_AC3      = 0x00000004
+};
+
+/*
+ * used for status bist value in radeon_update_hdmi_audio
+ */
+enum {
+	AUDIO_STATUS_DIG_ENABLE   = 0x01,
+	AUDIO_STATUS_V            = 0x02,
+	AUDIO_STATUS_VCFG         = 0x04,
+	AUDIO_STATUS_EMPHASIS     = 0x08,
+	AUDIO_STATUS_COPYRIGHT    = 0x10,
+	AUDIO_STATUS_NONAUDIO     = 0x20,
+	AUDIO_STATUS_PROFESSIONAL = 0x40,
+	AUDIO_STATUS_LEVEL        = 0x80
+};
+
 #define RADEONWaitForFifo(pScrn, entries)				\
 do {									\
     if (info->accel_state->fifo_slots < entries)			\
@@ -1284,6 +1365,21 @@ extern void radeon_ddx_cs_start(ScrnInfoPtr pScrn,
 struct radeon_bo *radeon_get_pixmap_bo(PixmapPtr pPix);
 void radeon_set_pixmap_bo(PixmapPtr pPix, struct radeon_bo *bo);
 
+/* radeon_hdmi_audio.c */
+extern void radeon_hdmi_audio_init(RHDPtr rhdPtr);
+
+extern void radeon_hdmi_audio_set_supported(RHDPtr rhdPtr, Bool clear, CARD32 config, CARD32 codec);
+extern void radeon_hdmi_audio_set_enable(RHDPtr rhdPtr, Bool Enable);
+extern void radeon_hdmi_audio_set_clock(RHDPtr rhdPtr, struct rhdOutput* Output, CARD32 Clock);
+
+extern void radeon_hdmi_audio_register(RHDPtr rhdPtr, struct rhdHdmi* rhdHdmi);
+extern void radeon_hdmi_audio_unregister(RHDPtr rhdPtr, struct rhdHdmi* rhdHdmi);
+
+extern void radeon_hdmi_audio_save(RHDPtr rhdPtr);
+extern void radeon_hdmi_audio_restore(RHDPtr rhdPtr);
+
+extern void radeon_hdmi_audio_destroy(RHDPtr rhdPtr);
+
 #ifdef XF86DRI
 #  ifdef USE_XAA
 /* radeon_accelfuncs.c */
diff --git a/src/radeon_atombios.c b/src/radeon_atombios.c
index f590d5b..ff8c11d 100644
--- a/src/radeon_atombios.c
+++ b/src/radeon_atombios.c
@@ -1771,6 +1771,23 @@ radeon_add_encoder(ScrnInfoPtr pScrn, uint32_t encoder_id, uint32_t device_suppo
 		}
 		break;
 	    }
+
+	    /* This time around we'll just set the HDMI offset */
+	    switch (encoder_id) {
+	    case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+	    case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+		    info->encoders[device_index]->hdmi_offset = RADEON_HDMI_TMDS;
+		    break;
+	    case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+		    info->encoders[device_index]->hdmi_offset = RADEON_HDMI_LVTMA;
+		    break;
+	    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+		    info->encoders[device_index]->hdmi_offset = RADEON_HDMI_DIG;
+		    break;
+	    default:
+		    info->encoders[device_index]->hdmi_offset = 0;
+		    break;
+	    }
 	    return TRUE;
 	} else {
 	    ErrorF("xalloc failed\n");
diff --git a/src/radeon_hdmi_audio.c b/src/radeon_hdmi_audio.c
new file mode 100644
index 0000000..931cb6d
--- /dev/null
+++ b/src/radeon_hdmi_audio.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright 2009  Luis R. Rodriguez <mcgrof at gmail.com>
+ * Copyright 2008  Christian König <deathsimple at vodafone.de>
+ * Copyright 2007  Luc Verhaegen <libv at exsuse.de>
+ * Copyright 2007  Matthias Hopf <mhopf at novell.com>
+ * Copyright 2007  Egbert Eich   <eich at 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.
+ */
+
+#include "radeon.h"
+
+#define RADEON_HDMI_AUDIO_TIMER_INTERVALL 100 /* 1/10 sekund should be enough */
+
+/*
+ * current number of channels
+ */
+static int radeon_hdmi_audio_channels(xf86OutputPtr output)
+{
+    RADEONInfoPtr info = RADEONPTR(output->scrn);
+    unsigned char *RADEONMMIO = info->MMIO;
+    return (INREG(AUDIO_RATE_BPS_CHANNEL) & 0x7) + 1;
+}
+
+/*
+ * current bits per sample
+ */
+static int radeon_hdmi_audio_bits_per_sample(xf86OutputPtr output)
+{
+    RADEONOutputPrivatePtr radeon_output = output->driver_private;
+    struct radeon_hdmi_audio *Audio = radeon_output->hdmi_audio;
+    RADEONInfoPtr info = RADEONPTR(output->scrn);
+    unsigned char *RADEONMMIO = info->MMIO;
+
+    CARD32 value = (INREG(AUDIO_RATE_BPS_CHANNEL) & 0xF0) >> 4;
+    switch(value)
+    {
+	case 0x0: return  8;
+	case 0x1: return 16;
+	case 0x2: return 20;
+	case 0x3: return 24;
+	case 0x4: return 32;
+    }
+
+    xf86DrvMsg(Audio->scrnIndex, X_WARNING, "%s: unknown bits per sample 0x%x "
+               "using 16 instead.\n", __func__, (int) value);
+
+    return 16;
+}
+
+/*
+ * current sampling rate in HZ
+ */
+static int radeon_hdmi_audio_rate(xf86OutputPtr output)
+{
+    RADEONInfoPtr info = RADEONPTR(output->scrn);
+    unsigned char *RADEONMMIO = info->MMIO;
+
+    CARD32 value = INREG(AUDIO_RATE_BPS_CHANNEL);
+    CARD32 result;
+
+    if(value & 0x4000)
+	result = 44100;
+    else
+	result = 48000;
+
+    result *= ((value >> 11) & 0x7) + 1;
+    result /= ((value >> 8) & 0x7) + 1;
+
+    return result;
+}
+
+/*
+ * something playing ?
+ */
+static Bool radeon_hdmi_audio_playing(xf86OutputPtr output)
+{
+    RADEONInfoPtr info = RADEONPTR(output->scrn);
+    unsigned char *RADEONMMIO = info->MMIO;
+    return (INREG(AUDIO_PLAYING) >> 4) & 1;
+}
+
+/*
+ * iec 60958 status bits
+ */
+static CARD8 radeon_hdmi_audio_status_bits(xf86OutputPtr output)
+{
+    RADEONInfoPtr info = RADEONPTR(output->scrn);
+    unsigned char *RADEONMMIO = info->MMIO;
+    return INREG(AUDIO_STATUS_BITS) & 0xff;
+}
+
+/*
+ * iec 60958 category code
+ */
+static CARD8 radeon_hdmi_audio_category_code(xf86OutputPtr output)
+{
+    RADEONInfoPtr info = RADEONPTR(output->scrn);
+    unsigned char *RADEONMMIO = info->MMIO;
+    return (INREG(AUDIO_STATUS_BITS) >> 8) & 0xff;
+}
+
+/*
+ * update all registered hdmi interfaces with current audio parameters
+ */
+static CARD32 radeon_timer_hdmi_audio(OsTimerPtr timer, CARD32 time, pointer ptr)
+{
+    struct radeon_hdmi_audio *Audio = (struct radeon_hdmi_audio*)ptr;
+    Bool playing = radeon_hdmi_audio_playing(Audio);
+    int channels = radeon_hdmi_audio_hannels(Audio);
+    int rate = radeon_hdmi_audio_rate(Audio);
+    int bps = radeon_hdmi_audio_bits_per_sample(Audio);
+    CARD8 status_bits = radeon_audio_status_bits(Audio);
+    CARD8 category_code = radeon_audio_category_code(Audio);
+
+    struct rhdHdmi* hdmi;
+
+    if (playing != Audio->SavedPlaying ||
+	channels != Audio->SavedChannels ||
+	rate != Audio->SavedRate ||
+	bps != Audio->SavedBitsPerSample ||
+	status_bits != Audio->SavedStatusBits ||
+	category_code != Audio->SavedCategoryCode) {
+
+	Audio->SavedPlaying = playing;
+	Audio->SavedChannels = channels;
+	Audio->SavedRate = rate;
+	Audio->SavedBitsPerSample = bps;
+	Audio->SavedStatusBits = status_bits;
+	Audio->SavedCategoryCode = category_code;
+
+	/* XXX: port hdmi struct usage */
+	for (hdmi=Audio->Registered; hdmi != NULL; hdmi=hdmi->Next)
+	    radeon_hdmi_update_audio_settings(
+		hdmi, playing, channels,
+		rate, bps, status_bits,
+		category_code);
+    }
+
+    return RADEON_HDMI_AUDIO_TIMER_INTERVALL;
+}
+
+/*
+ * allocate and init the audio structure
+ */
+void
+radeon_hdmi_audio_init(xf86OutputPtr output)
+{
+    RADEONInfoPtr info = RADEONPTR(output->scrn);
+    RADEONOutputPrivatePtr radeon_output = output->driver_private;
+    struct radeon_hdmi_audio *Audio;
+
+    if (info->ChipFamily < CHIP_FAMILY_RS600) {
+	radeon_output->Audio = NULL;
+	return;
+    }
+
+    Audio = (struct radeon_hdmi_audio *) xnfcalloc(sizeof(struct radeon_hdmi_audio), 1);
+    Audio->scrnIndex = info->atomBIOS->scrnIndex;
+    Audio->Registered = NULL;
+    Audio->Stored = FALSE;
+
+    radeon_output->Audio = Audio;
+}
+
+/*
+ * enable or disable the complete audio engine
+ */
+void
+radeon_hdmi_audio_set_enable(xf86OutputPtr output, Bool Enable)
+{
+    RADEONOutputPrivatePtr radeon_output = output->driver_private;
+    struct radeon_hdmi_audio *Audio = radeon_output->hdmi_audio;
+
+    if (!Audio)
+	return;
+
+    REG_RMW(AUDIO_ENABLE, Enable ? 0x80000000 : 0x0, 0x80000000);
+    if (!Enable) {
+	TimerFree(Audio->Timer);
+	Audio->Timer = NULL;
+	return;
+    }
+
+   /*
+    * the hardware generates an interrupt if audio starts/stops playing,
+    * but since drm doesn't support this interrupt, we check
+    * every RADEON_HDMI_AUDIO_TIMER_INTERVALL ms if something has changed
+    */
+
+    Audio->SavedChannels = -1;
+    Audio->SavedRate = -1;
+    Audio->SavedBitsPerSample = -1;
+    Audio->SavedStatusBits = 0;
+    Audio->SavedCategoryCode = 0;
+    Audio->Timer = TimerSet(NULL, 0, RADEON_HDMI_AUDIO_TIMER_INTERVALL,
+			    radeon_hdmi_audio_update, Audio);
+
+    /* 48kHz and 16/20 bits per sample are always supported */
+    radeon_hdmi_audio_set_supported(output, TRUE,
+    	AUDIO_RATE_48000_HZ |
+    	AUDIO_BPS_16 |
+	AUDIO_BPS_20,
+    	AUDIO_CODEC_PCM);
+}
+
+/*
+ * programm the audio clock and timing registers
+ */
+void
+radeon_hdmi_audio_set_clock(xf86OutputPtr output, CARD32 Clock)
+{
+    RADEONOutputPrivatePtr radeon_output = output->driver_private;
+    struct radeon_hdmi_audio *Audio = radeon_output->hdmi_audio;
+    radeon_encoder_ptr radeon_encoder;
+    RADEONInfoPtr info = RADEONPTR(output->scrn);
+    unsigned char *RADEONMMIO = info->MMIO;
+
+    int Rate = 48000;
+
+    if (!Audio)
+	return;
+
+    radeon_encoder = radeon_get_encoder(output);
+
+    xf86DrvMsg(Audio->scrnIndex, X_INFO, "%s: using %s as clock source with %d khz\n",
+	       __func__,
+	       device_name[radeon_get_device_index(radeon_output->active_device)],
+	       (int) Clock);
+
+    switch(radeon_encoder->encoder_id) {
+	case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+	case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+	    REG_RMW(AUDIO_TIMING, 0, 0x301);
+	    break;
+	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+	    REG_RMW(AUDIO_TIMING, 0x100, 0x301);
+	    break;
+
+	default:
+	    break;
+    }
+
+    switch(radeon_encoder->encoder_id) {
+	case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+	    OUTREG(AUDIO_PLL1_MUL, Rate*50);
+	    OUTREG(AUDIO_PLL1_DIV, Clock*100);
+	    OUTREG(AUDIO_CLK_SRCSEL, 0);
+	    break;
+
+	case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+	    OUTREG(AUDIO_PLL2_MUL, Rate*50);
+	    OUTREG(AUDIO_PLL2_DIV, Clock*100);
+	    OUTREG(AUDIO_CLK_SRCSEL, 1);
+	    break;
+
+	default:
+	    xf86DrvMsg(Audio->scrnIndex, X_ERROR, "%s: unsupported output type\n", __func__);
+	    break;
+    }
+}
+
+/*
+ * set the supported audio rates, bits per sample and codecs
+ */
+void
+radeon_hdmi_audio_set_supported(xf86OutputPtr output, Bool clear, CARD32 config, CARD32 codec)
+{
+    RADEONOutputPrivatePtr radeon_output = output->driver_private;
+    struct radeon_hdmi_audio *Audio = radeon_output->hdmi_audio;
+    RADEONInfoPtr info = RADEONPTR(output->scrn);
+    unsigned char *RADEONMMIO = info->MMIO;
+
+    if (!Audio)
+	return;
+
+    xf86DrvMsg(Audio->scrnIndex, X_INFO, "%s: config 0x%x codec 0x%x\n",
+	       __func__, (int) config, (int) codec);
+
+    if(config & 0xFFE0F000)
+	xf86DrvMsg(Audio->scrnIndex, X_WARNING, "%s: reserved config bits set 0x%x\n",
+		   __func__, (int) config);
+
+    if(codec & 0xFFFFFFF8)
+	xf86DrvMsg(Audio->scrnIndex, X_WARNING, "%s: reserved codec bits set 0x%x\n",
+		   __func__, (int) codec);
+
+    if (clear) {
+	OUTREG(AUDIO_SUPPORTED_SIZE_RATE, config);
+	OUTREG(AUDIO_SUPPORTED_CODEC, codec);
+    } else {
+	OUTREG(AUDIO_SUPPORTED_SIZE_RATE, config, config);
+	OUTREG(AUDIO_SUPPORTED_CODEC, codec, codec);
+    }
+}
+
+/*
+ * register and hdmi interface for getting updates when audio parameters change.
+ * The right approach here is to use DRM interrupts for Audio updates but until
+ * DRM does not suppor that we use a timer to update it regularly.
+ */
+void radeon_hdmi_audio_register(xf86OutputPtr output, struct rhdHdmi* rhdHdmi)
+{
+    RADEONOutputPrivatePtr radeon_output = output->driver_private;
+    struct radeon_hdmi_audio *Audio = radeon_output->hdmi_audio;
+
+    if (!Audio)
+	return;
+
+	/* XXX: How to port rhdHmdi to ati driver from radeonhd */
+    if(!rhdHdmi)
+	return;
+
+    rhdHdmi->Next = Audio->Registered;
+    Audio->Registered = rhdHdmi;
+}
+
+
+/*
+ * unregister the hdmi interface
+ */
+void radeon_hdmi_audio_unregister(xf86OutputPtr output, struct rhdHdmi* rhdHdmi)
+{
+    RADEONOutputPrivatePtr radeon_output = output->driver_private;
+    struct radeon_hdmi_audio *Audio = radeon_output->hdmi_audio;
+    struct rhdHdmi** hdmiPtr;
+
+    if (!Audio)
+	return;
+
+    for (hdmiPtr=&Audio->Registered; hdmiPtr!=NULL; hdmiPtr=&(*hdmiPtr)->Next) {
+	if(*hdmiPtr != rhdHdmi)
+		continue;
+	*hdmiPtr = rhdHdmi->Next;
+	rhdHdmi->Next = NULL;
+	return;
+    }
+	
+}
+
+/*
+ * save the current config of audio engine
+ */
+void
+radeon_hdmi_audio_save(xf86OutputPtr output)
+{
+    RADEONOutputPrivatePtr radeon_output = output->driver_private;
+    struct radeon_hdmi_audio *Audio = radeon_output->hdmi_audio;
+    unsigned char *RADEONMMIO = info->MMIO;
+
+    if (!Audio)
+	return;
+
+    Audio->StoreEnabled = INREG(AUDIO_ENABLE);
+    Audio->StoreTiming = INREG(AUDIO_TIMING);
+
+    Audio->StoreSupportedSizeRate = INREG(AUDIO_SUPPORTED_SIZE_RATE);
+    Audio->StoreSupportedCodec = INREG(AUDIO_SUPPORTED_CODEC);
+
+    Audio->StorePll1Mul     = INREG(AUDIO_PLL1_MUL);
+    Audio->StorePll1Div     = INREG(AUDIO_PLL1_DIV);
+    Audio->StorePll2Mul     = INREG(AUDIO_PLL2_MUL);
+    Audio->StorePll2Div     = INREG(AUDIO_PLL2_DIV);
+    Audio->StoreClockSrcSel = INREG(AUDIO_CLK_SRCSEL);
+
+    Audio->Stored = TRUE;
+}
+
+/*
+ * restore the saved config of audio engine
+ */
+void
+radeon_hdmi_audio_restore(xf86OutputPtr output)
+{
+    RADEONOutputPrivatePtr radeon_output = output->driver_private;
+    struct radeon_hdmi_audio *Audio = radeon_output->hdmi_audio;
+    RADEONInfoPtr info = RADEONPTR(output->scrn);
+    unsigned char *RADEONMMIO = info->MMIO;
+
+    if (!Audio)
+	return;
+
+    if (!Audio->Stored) {
+        xf86DrvMsg(Audio->scrnIndex, X_ERROR, "%s: trying to restore "
+                   "uninitialized values.\n", __func__);
+        return;
+    }
+
+    /* shoutdown the audio engine before doing anything else */
+    radeon_hdmi_audio_set_enable(output, FALSE);
+
+    OUTREG(AUDIO_TIMING, Audio->StoreTiming);
+    OUTREG(AUDIO_SUPPORTED_SIZE_RATE, Audio->StoreSupportedSizeRate);
+    OUTREG(AUDIO_SUPPORTED_CODEC, Audio->StoreSupportedCodec);
+
+    OUTREG(AUDIO_PLL1_MUL, Audio->StorePll1Mul);
+    OUTREG(AUDIO_PLL1_DIV, Audio->StorePll1Div);
+    OUTREG(AUDIO_PLL2_MUL, Audio->StorePll2Mul);
+    OUTREG(AUDIO_PLL2_DIV, Audio->StorePll2Div);
+    OUTREG(AUDIO_CLK_SRCSEL, Audio->StoreClockSrcSel);
+    OUTREG(AUDIO_ENABLE, Audio->StoreEnabled);
+}
+
+/*
+ * release the allocated memory
+ */
+void
+radeon_hdmi_audio_destroy(xf86OutputPtr output)
+{
+    RADEONOutputPrivatePtr radeon_output = output->driver_private;
+
+    if (!radeon_output->hdmi_audio)
+	return;
+
+    if(radeon_output->hdmi_audio->Timer)
+	TimerFree(radeon_output->hdmi_audio->Timer);
+
+    xfree(radeon_output->hdmi_audio);
+    radeon_output->hdmi_audio = NULL;
+}
diff --git a/src/radeon_macros.h b/src/radeon_macros.h
index 26d9825..a51f2f4 100644
--- a/src/radeon_macros.h
+++ b/src/radeon_macros.h
@@ -69,6 +69,9 @@
 
 #define ADDRREG(addr)       ((volatile uint32_t *)(pointer)(RADEONMMIO + (addr)))
 
+#define REG_RMW(_r, _m, _v) \
+	OUTREG((_r), \
+	(INREG(_r) & ~_m) | ((_v) & _m))
 
 #define OUTREGP(addr, val, mask)					\
 do {									\
diff --git a/src/radeon_probe.h b/src/radeon_probe.h
index 9cac15c..b62f019 100644
--- a/src/radeon_probe.h
+++ b/src/radeon_probe.h
@@ -167,6 +167,7 @@ typedef struct _RADEONCrtcPrivateRec {
 typedef struct _radeon_encoder {
     uint16_t encoder_id;
     int devices;
+    uint32_t hdmi_offset;
     void *dev_priv;
 } radeon_encoder_rec, *radeon_encoder_ptr;
 
diff --git a/src/radeon_reg.h b/src/radeon_reg.h
index 9df7fff..0e1e9a4 100644
--- a/src/radeon_reg.h
+++ b/src/radeon_reg.h
@@ -3833,6 +3833,12 @@
 #define AVIVO_D2SCL_SCALER_TAP_CONTROL	 	0x6d94
 #define AVIVO_D2SCL_UPDATE                      0x6dcc
 
+/* HDMI offsets */
+#define	RADEON_HDMI_TMDS			0x7400
+#define RADEON_HDMI_LVTMA			0x7700
+#define RADEON_HDMI_DIG				0x7800
+
+
 #define AVIVO_DDIA_BIT_DEPTH_CONTROL				0x7214
 
 #define AVIVO_DACA_ENABLE					0x7800
-- 
1.6.3.3



More information about the xorg-driver-ati mailing list