drm/radeon: add audio support for DCE6/8 GPUs (v12)
authorAlex Deucher <alexander.deucher@amd.com>
Wed, 31 Jul 2013 20:51:33 +0000 (16:51 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Fri, 30 Aug 2013 20:30:45 +0000 (16:30 -0400)
Similar to DCE4/5, but supports multiple audio pins
which can be assigned per afmt block.

v2: rework the driver to handle more than one audio
pin.
v3: try different dto reg
v4: properly program dto
v5 (ck): change dto programming order
v6: program speaker allocation block
v7: rebase
v8: rebase on Rafał's changes
v9: integrated Rafał's comments, update to latest
    drm_edid_to_speaker_allocation API
v10: add missing line break in error message
v11: add back audio enabled messages
v12: fix copy paste typo in r600_audio_enable

Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Christian König <christian.koenig@amd.com>
Acked-by: Rafał Miłecki <zajec5@gmail.com>
15 files changed:
drivers/gpu/drm/radeon/Makefile
drivers/gpu/drm/radeon/atombios_encoders.c
drivers/gpu/drm/radeon/cik.c
drivers/gpu/drm/radeon/dce6_afmt.c [new file with mode: 0644]
drivers/gpu/drm/radeon/evergreen_hdmi.c
drivers/gpu/drm/radeon/ni.c
drivers/gpu/drm/radeon/r600_audio.c
drivers/gpu/drm/radeon/r600_hdmi.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_asic.c
drivers/gpu/drm/radeon/radeon_asic.h
drivers/gpu/drm/radeon/radeon_display.c
drivers/gpu/drm/radeon/radeon_mode.h
drivers/gpu/drm/radeon/si.c
drivers/gpu/drm/radeon/sid.h

index da2a8e9..306364a 100644 (file)
@@ -80,7 +80,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
        r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \
        rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \
        trinity_smc.o ni_dpm.o si_smc.o si_dpm.o kv_smc.o kv_dpm.o ci_smc.o \
-       ci_dpm.o
+       ci_dpm.o dce6_afmt.o
 
 # add async DMA block
 radeon-y += \
index 092275d..dfac796 100644 (file)
@@ -682,8 +682,6 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
 int
 atombios_get_encoder_mode(struct drm_encoder *encoder)
 {
-       struct drm_device *dev = encoder->dev;
-       struct radeon_device *rdev = dev->dev_private;
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
        struct drm_connector *connector;
        struct radeon_connector *radeon_connector;
@@ -710,8 +708,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
        case DRM_MODE_CONNECTOR_DVII:
        case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */
                if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                   radeon_audio &&
-                   !ASIC_IS_DCE6(rdev)) /* remove once we support DCE6 */
+                   radeon_audio)
                        return ATOM_ENCODER_MODE_HDMI;
                else if (radeon_connector->use_digital)
                        return ATOM_ENCODER_MODE_DVI;
@@ -722,8 +719,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
        case DRM_MODE_CONNECTOR_HDMIA:
        default:
                if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                   radeon_audio &&
-                   !ASIC_IS_DCE6(rdev)) /* remove once we support DCE6 */
+                   radeon_audio)
                        return ATOM_ENCODER_MODE_HDMI;
                else
                        return ATOM_ENCODER_MODE_DVI;
@@ -737,8 +733,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
                    (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
                        return ATOM_ENCODER_MODE_DP;
                else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                        radeon_audio &&
-                        !ASIC_IS_DCE6(rdev)) /* remove once we support DCE6 */
+                        radeon_audio)
                        return ATOM_ENCODER_MODE_HDMI;
                else
                        return ATOM_ENCODER_MODE_DVI;
index 692e31b..2b6049d 100644 (file)
@@ -7004,6 +7004,10 @@ static int cik_startup(struct radeon_device *rdev)
                return r;
        }
 
+       r = dce6_audio_init(rdev);
+       if (r)
+               return r;
+
        return 0;
 }
 
@@ -7049,6 +7053,7 @@ int cik_resume(struct radeon_device *rdev)
  */
 int cik_suspend(struct radeon_device *rdev)
 {
+       dce6_audio_fini(rdev);
        radeon_vm_manager_fini(rdev);
        cik_cp_enable(rdev, false);
        cik_sdma_enable(rdev, false);
diff --git a/drivers/gpu/drm/radeon/dce6_afmt.c b/drivers/gpu/drm/radeon/dce6_afmt.c
new file mode 100644 (file)
index 0000000..0d9a6a2
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2013 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 <linux/hdmi.h>
+#include <drm/drmP.h>
+#include "radeon.h"
+#include "sid.h"
+
+static u32 dce6_endpoint_rreg(struct radeon_device *rdev,
+                             u32 block_offset, u32 reg)
+{
+       u32 r;
+
+       WREG32(AZ_F0_CODEC_ENDPOINT_INDEX + block_offset, reg);
+       r = RREG32(AZ_F0_CODEC_ENDPOINT_DATA + block_offset);
+       return r;
+}
+
+static void dce6_endpoint_wreg(struct radeon_device *rdev,
+                              u32 block_offset, u32 reg, u32 v)
+{
+       if (ASIC_IS_DCE8(rdev))
+               WREG32(AZ_F0_CODEC_ENDPOINT_INDEX + block_offset, reg);
+       else
+               WREG32(AZ_F0_CODEC_ENDPOINT_INDEX + block_offset,
+                      AZ_ENDPOINT_REG_WRITE_EN | AZ_ENDPOINT_REG_INDEX(reg));
+       WREG32(AZ_F0_CODEC_ENDPOINT_DATA + block_offset, v);
+}
+
+#define RREG32_ENDPOINT(block, reg) dce6_endpoint_rreg(rdev, (block), (reg))
+#define WREG32_ENDPOINT(block, reg, v) dce6_endpoint_wreg(rdev, (block), (reg), (v))
+
+
+static void dce6_afmt_get_connected_pins(struct radeon_device *rdev)
+{
+       int i;
+       u32 offset, tmp;
+
+       for (i = 0; i < rdev->audio.num_pins; i++) {
+               offset = rdev->audio.pin[i].offset;
+               tmp = RREG32_ENDPOINT(offset,
+                                     AZ_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT);
+               if (((tmp & PORT_CONNECTIVITY_MASK) >> PORT_CONNECTIVITY_SHIFT) == 1)
+                       rdev->audio.pin[i].connected = false;
+               else
+                       rdev->audio.pin[i].connected = true;
+       }
+}
+
+struct r600_audio_pin *dce6_audio_get_pin(struct radeon_device *rdev)
+{
+       int i;
+
+       dce6_afmt_get_connected_pins(rdev);
+
+       for (i = 0; i < rdev->audio.num_pins; i++) {
+               if (rdev->audio.pin[i].connected)
+                       return &rdev->audio.pin[i];
+       }
+       DRM_ERROR("No connected audio pins found!\n");
+       return NULL;
+}
+
+void dce6_afmt_select_pin(struct drm_encoder *encoder)
+{
+       struct radeon_device *rdev = encoder->dev->dev_private;
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+       u32 offset = dig->afmt->offset;
+       u32 id = dig->afmt->pin->id;
+
+       if (!dig->afmt->pin)
+               return;
+
+       WREG32(AFMT_AUDIO_SRC_CONTROL + offset, AFMT_AUDIO_SRC_SELECT(id));
+}
+
+void dce6_afmt_write_sad_regs(struct drm_encoder *encoder)
+{
+       struct radeon_device *rdev = encoder->dev->dev_private;
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+       u32 offset, tmp;
+       struct drm_connector *connector;
+       struct radeon_connector *radeon_connector = NULL;
+       struct cea_sad *sads;
+       int i, sad_count, sadb_count;
+       u8 *sadb;
+
+       static const u16 eld_reg_to_type[][2] = {
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, HDMI_AUDIO_CODING_TYPE_PCM },
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR1, HDMI_AUDIO_CODING_TYPE_AC3 },
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR2, HDMI_AUDIO_CODING_TYPE_MPEG1 },
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR3, HDMI_AUDIO_CODING_TYPE_MP3 },
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR4, HDMI_AUDIO_CODING_TYPE_MPEG2 },
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR5, HDMI_AUDIO_CODING_TYPE_AAC_LC },
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR6, HDMI_AUDIO_CODING_TYPE_DTS },
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR7, HDMI_AUDIO_CODING_TYPE_ATRAC },
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR9, HDMI_AUDIO_CODING_TYPE_EAC3 },
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR10, HDMI_AUDIO_CODING_TYPE_DTS_HD },
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR11, HDMI_AUDIO_CODING_TYPE_MLP },
+               { AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR13, HDMI_AUDIO_CODING_TYPE_WMA_PRO },
+       };
+
+       if (!dig->afmt->pin)
+               return;
+
+       offset = dig->afmt->pin->offset;
+
+       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+               if (connector->encoder == encoder)
+                       radeon_connector = to_radeon_connector(connector);
+       }
+
+       if (!radeon_connector) {
+               DRM_ERROR("Couldn't find encoder's connector\n");
+               return;
+       }
+
+       sad_count = drm_edid_to_sad(radeon_connector->edid, &sads);
+       if (sad_count < 0) {
+               DRM_ERROR("Couldn't read SADs: %d\n", sad_count);
+               return;
+       }
+       BUG_ON(!sads);
+
+       sadb_count = drm_edid_to_speaker_allocation(radeon_connector->edid, &sadb);
+       if (sadb_count < 0) {
+               DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sadb_count);
+               return;
+       }
+
+       /* program the speaker allocation */
+       tmp = RREG32_ENDPOINT(offset, AZ_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER);
+       tmp &= ~(DP_CONNECTION | SPEAKER_ALLOCATION_MASK);
+       /* set HDMI mode */
+       tmp |= HDMI_CONNECTION;
+       if (sadb_count)
+               tmp |= SPEAKER_ALLOCATION(sadb[0]);
+       else
+               tmp |= SPEAKER_ALLOCATION(5); /* stereo */
+       WREG32_ENDPOINT(offset, AZ_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, tmp);
+
+       for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) {
+               u32 value = 0;
+               int j;
+
+               for (j = 0; j < sad_count; j++) {
+                       struct cea_sad *sad = &sads[j];
+
+                       if (sad->format == eld_reg_to_type[i][1]) {
+                               value = MAX_CHANNELS(sad->channels) |
+                                       DESCRIPTOR_BYTE_2(sad->byte2) |
+                                       SUPPORTED_FREQUENCIES(sad->freq);
+                               if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
+                                       value |= SUPPORTED_FREQUENCIES_STEREO(sad->freq);
+                               break;
+                       }
+               }
+               WREG32_ENDPOINT(offset, eld_reg_to_type[i][0], value);
+       }
+
+       kfree(sads);
+       kfree(sadb);
+}
+
+static int dce6_audio_chipset_supported(struct radeon_device *rdev)
+{
+       return !ASIC_IS_NODCE(rdev);
+}
+
+static void dce6_audio_enable(struct radeon_device *rdev,
+                             struct r600_audio_pin *pin,
+                             bool enable)
+{
+       WREG32_ENDPOINT(pin->offset, AZ_F0_CODEC_PIN_CONTROL_HOTPLUG_CONTROL,
+                       AUDIO_ENABLED);
+       DRM_INFO("%s audio %d support\n", enable ? "Enabling" : "Disabling", pin->id);
+}
+
+static const u32 pin_offsets[7] =
+{
+       (0x5e00 - 0x5e00),
+       (0x5e18 - 0x5e00),
+       (0x5e30 - 0x5e00),
+       (0x5e48 - 0x5e00),
+       (0x5e60 - 0x5e00),
+       (0x5e78 - 0x5e00),
+       (0x5e90 - 0x5e00),
+};
+
+int dce6_audio_init(struct radeon_device *rdev)
+{
+       int i;
+
+       if (!radeon_audio || !dce6_audio_chipset_supported(rdev))
+               return 0;
+
+       rdev->audio.enabled = true;
+
+       if (ASIC_IS_DCE8(rdev))
+               rdev->audio.num_pins = 7;
+       else
+               rdev->audio.num_pins = 6;
+
+       for (i = 0; i < rdev->audio.num_pins; i++) {
+               rdev->audio.pin[i].channels = -1;
+               rdev->audio.pin[i].rate = -1;
+               rdev->audio.pin[i].bits_per_sample = -1;
+               rdev->audio.pin[i].status_bits = 0;
+               rdev->audio.pin[i].category_code = 0;
+               rdev->audio.pin[i].connected = false;
+               rdev->audio.pin[i].offset = pin_offsets[i];
+               rdev->audio.pin[i].id = i;
+               dce6_audio_enable(rdev, &rdev->audio.pin[i], true);
+       }
+
+       return 0;
+}
+
+void dce6_audio_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       if (!rdev->audio.enabled)
+               return;
+
+       for (i = 0; i < rdev->audio.num_pins; i++)
+               dce6_audio_enable(rdev, &rdev->audio.pin[i], false);
+
+       rdev->audio.enabled = false;
+}
index b0e2800..c5acdf0 100644 (file)
@@ -32,6 +32,9 @@
 #include "evergreend.h"
 #include "atom.h"
 
+extern void dce6_afmt_write_sad_regs(struct drm_encoder *encoder);
+extern void dce6_afmt_select_pin(struct drm_encoder *encoder);
+
 /*
  * update the N and CTS parameters for a given pixel clock rate
  */
@@ -157,22 +160,26 @@ static void evergreen_audio_set_dto(struct drm_encoder *encoder, u32 clock)
        if (!dig || !dig->afmt)
                return;
 
-       if (max_ratio >= 8) {
-               dto_phase = 192 * 1000;
-               wallclock_ratio = 3;
-       } else if (max_ratio >= 4) {
-               dto_phase = 96 * 1000;
-               wallclock_ratio = 2;
-       } else if (max_ratio >= 2) {
-               dto_phase = 48 * 1000;
-               wallclock_ratio = 1;
-       } else {
+       if (ASIC_IS_DCE6(rdev)) {
                dto_phase = 24 * 1000;
-               wallclock_ratio = 0;
+       } else {
+               if (max_ratio >= 8) {
+                       dto_phase = 192 * 1000;
+                       wallclock_ratio = 3;
+               } else if (max_ratio >= 4) {
+                       dto_phase = 96 * 1000;
+                       wallclock_ratio = 2;
+               } else if (max_ratio >= 2) {
+                       dto_phase = 48 * 1000;
+                       wallclock_ratio = 1;
+               } else {
+                       dto_phase = 24 * 1000;
+                       wallclock_ratio = 0;
+               }
+               dto_cntl = RREG32(DCCG_AUDIO_DTO0_CNTL) & ~DCCG_AUDIO_DTO_WALLCLOCK_RATIO_MASK;
+               dto_cntl |= DCCG_AUDIO_DTO_WALLCLOCK_RATIO(wallclock_ratio);
+               WREG32(DCCG_AUDIO_DTO0_CNTL, dto_cntl);
        }
-       dto_cntl = RREG32(DCCG_AUDIO_DTO0_CNTL) & ~DCCG_AUDIO_DTO_WALLCLOCK_RATIO_MASK;
-       dto_cntl |= DCCG_AUDIO_DTO_WALLCLOCK_RATIO(wallclock_ratio);
-       WREG32(DCCG_AUDIO_DTO0_CNTL, dto_cntl);
 
        /* XXX two dtos; generally use dto0 for hdmi */
        /* Express [24MHz / target pixel clock] as an exact rational
@@ -266,7 +273,13 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
               AFMT_AUDIO_CHANNEL_ENABLE(0xff));
 
        /* fglrx sets 0x40 in 0x5f80 here */
-       evergreen_hdmi_write_sad_regs(encoder);
+
+       if (ASIC_IS_DCE6(rdev)) {
+               dce6_afmt_select_pin(encoder);
+               dce6_afmt_write_sad_regs(encoder);
+       } else {
+               evergreen_hdmi_write_sad_regs(encoder);
+       }
 
        err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
        if (err < 0) {
@@ -302,6 +315,8 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
 
 void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable)
 {
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
        struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
 
@@ -314,6 +329,15 @@ void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable)
        if (!enable && !dig->afmt->enabled)
                return;
 
+       if (enable) {
+               if (ASIC_IS_DCE6(rdev))
+                       dig->afmt->pin = dce6_audio_get_pin(rdev);
+               else
+                       dig->afmt->pin = r600_audio_get_pin(rdev);
+       } else {
+               dig->afmt->pin = NULL;
+       }
+
        dig->afmt->enabled = enable;
 
        DRM_DEBUG("%sabling HDMI interface @ 0x%04X for encoder 0x%x\n",
index 2db8ce0..69499ff 100644 (file)
@@ -2027,9 +2027,15 @@ static int cayman_startup(struct radeon_device *rdev)
                return r;
        }
 
-       r = r600_audio_init(rdev);
-       if (r)
-               return r;
+       if (ASIC_IS_DCE6(rdev)) {
+               r = dce6_audio_init(rdev);
+               if (r)
+                       return r;
+       } else {
+               r = r600_audio_init(rdev);
+               if (r)
+                       return r;
+       }
 
        return 0;
 }
@@ -2060,7 +2066,10 @@ int cayman_resume(struct radeon_device *rdev)
 
 int cayman_suspend(struct radeon_device *rdev)
 {
-       r600_audio_fini(rdev);
+       if (ASIC_IS_DCE6(rdev))
+               dce6_audio_fini(rdev);
+       else
+               r600_audio_fini(rdev);
        radeon_vm_manager_fini(rdev);
        cayman_cp_enable(rdev, false);
        cayman_dma_stop(rdev);
index c92eb86..47fc2b8 100644 (file)
@@ -57,12 +57,12 @@ static bool radeon_dig_encoder(struct drm_encoder *encoder)
  */
 static int r600_audio_chipset_supported(struct radeon_device *rdev)
 {
-       return ASIC_IS_DCE2(rdev) && !ASIC_IS_DCE6(rdev);
+       return ASIC_IS_DCE2(rdev) && !ASIC_IS_NODCE(rdev);
 }
 
-struct r600_audio r600_audio_status(struct radeon_device *rdev)
+struct r600_audio_pin r600_audio_status(struct radeon_device *rdev)
 {
-       struct r600_audio status;
+       struct r600_audio_pin status;
        uint32_t value;
 
        value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL);
@@ -120,16 +120,16 @@ void r600_audio_update_hdmi(struct work_struct *work)
        struct radeon_device *rdev = container_of(work, struct radeon_device,
                                                  audio_work);
        struct drm_device *dev = rdev->ddev;
-       struct r600_audio audio_status = r600_audio_status(rdev);
+       struct r600_audio_pin audio_status = r600_audio_status(rdev);
        struct drm_encoder *encoder;
        bool changed = false;
 
-       if (rdev->audio_status.channels != audio_status.channels ||
-           rdev->audio_status.rate != audio_status.rate ||
-           rdev->audio_status.bits_per_sample != audio_status.bits_per_sample ||
-           rdev->audio_status.status_bits != audio_status.status_bits ||
-           rdev->audio_status.category_code != audio_status.category_code) {
-               rdev->audio_status = audio_status;
+       if (rdev->audio.pin[0].channels != audio_status.channels ||
+           rdev->audio.pin[0].rate != audio_status.rate ||
+           rdev->audio.pin[0].bits_per_sample != audio_status.bits_per_sample ||
+           rdev->audio.pin[0].status_bits != audio_status.status_bits ||
+           rdev->audio.pin[0].category_code != audio_status.category_code) {
+               rdev->audio.pin[0] = audio_status;
                changed = true;
        }
 
@@ -141,13 +141,13 @@ void r600_audio_update_hdmi(struct work_struct *work)
        }
 }
 
-/*
- * turn on/off audio engine
- */
-static void r600_audio_engine_enable(struct radeon_device *rdev, bool enable)
+/* enable the audio stream */
+static void r600_audio_enable(struct radeon_device *rdev,
+                             struct r600_audio_pin *pin,
+                             bool enable)
 {
        u32 value = 0;
-       DRM_INFO("%s audio support\n", enable ? "Enabling" : "Disabling");
+
        if (ASIC_IS_DCE4(rdev)) {
                if (enable) {
                        value |= 0x81000000; /* Required to enable audio */
@@ -158,7 +158,7 @@ static void r600_audio_engine_enable(struct radeon_device *rdev, bool enable)
                WREG32_P(R600_AUDIO_ENABLE,
                         enable ? 0x81000000 : 0x0, ~0x81000000);
        }
-       rdev->audio_enabled = enable;
+       DRM_INFO("%s audio %d support\n", enable ? "Enabling" : "Disabling", pin->id);
 }
 
 /*
@@ -169,13 +169,17 @@ int r600_audio_init(struct radeon_device *rdev)
        if (!radeon_audio || !r600_audio_chipset_supported(rdev))
                return 0;
 
-       r600_audio_engine_enable(rdev, true);
+       rdev->audio.enabled = true;
+
+       rdev->audio.num_pins = 1;
+       rdev->audio.pin[0].channels = -1;
+       rdev->audio.pin[0].rate = -1;
+       rdev->audio.pin[0].bits_per_sample = -1;
+       rdev->audio.pin[0].status_bits = 0;
+       rdev->audio.pin[0].category_code = 0;
+       rdev->audio.pin[0].id = 0;
 
-       rdev->audio_status.channels = -1;
-       rdev->audio_status.rate = -1;
-       rdev->audio_status.bits_per_sample = -1;
-       rdev->audio_status.status_bits = 0;
-       rdev->audio_status.category_code = 0;
+       r600_audio_enable(rdev, &rdev->audio.pin[0], true);
 
        return 0;
 }
@@ -186,8 +190,16 @@ int r600_audio_init(struct radeon_device *rdev)
  */
 void r600_audio_fini(struct radeon_device *rdev)
 {
-       if (!rdev->audio_enabled)
+       if (!rdev->audio.enabled)
                return;
 
-       r600_audio_engine_enable(rdev, false);
+       r600_audio_enable(rdev, &rdev->audio.pin[0], false);
+
+       rdev->audio.enabled = false;
+}
+
+struct r600_audio_pin *r600_audio_get_pin(struct radeon_device *rdev)
+{
+       /* only one pin on 6xx-NI */
+       return &rdev->audio.pin[0];
 }
index f264df5..e1dec13 100644 (file)
@@ -382,7 +382,7 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
        struct radeon_device *rdev = dev->dev_private;
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
        struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
-       struct r600_audio audio = r600_audio_status(rdev);
+       struct r600_audio_pin audio = r600_audio_status(rdev);
        uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
        struct hdmi_audio_infoframe frame;
        uint32_t offset;
@@ -491,6 +491,11 @@ void r600_hdmi_enable(struct drm_encoder *encoder, bool enable)
        if (!enable && !dig->afmt->enabled)
                return;
 
+       if (enable)
+               dig->afmt->pin = r600_audio_get_pin(rdev);
+       else
+               dig->afmt->pin = NULL;
+
        /* Older chipsets require setting HDMI and routing manually */
        if (!ASIC_IS_DCE3(rdev)) {
                if (enable)
index 791cc8d..82fef85 100644 (file)
@@ -696,7 +696,7 @@ union radeon_irq_stat_regs {
 
 #define RADEON_MAX_HPD_PINS 6
 #define RADEON_MAX_CRTCS 6
-#define RADEON_MAX_AFMT_BLOCKS 6
+#define RADEON_MAX_AFMT_BLOCKS 7
 
 struct radeon_irq {
        bool                            installed;
@@ -1537,12 +1537,21 @@ int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev,
 int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev,
                                 unsigned cg_upll_func_cntl);
 
-struct r600_audio {
+struct r600_audio_pin {
        int                     channels;
        int                     rate;
        int                     bits_per_sample;
        u8                      status_bits;
        u8                      category_code;
+       u32                     offset;
+       bool                    connected;
+       u32                     id;
+};
+
+struct r600_audio {
+       bool enabled;
+       struct r600_audio_pin pin[RADEON_MAX_AFMT_BLOCKS];
+       int num_pins;
 };
 
 /*
@@ -2128,9 +2137,8 @@ struct radeon_device {
        struct work_struct reset_work;
        int num_crtc; /* number of crtcs */
        struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */
-       bool audio_enabled;
        bool has_uvd;
-       struct r600_audio audio_status; /* audio stuff */
+       struct r600_audio audio; /* audio stuff */
        struct notifier_block acpi_nb;
        /* only one userspace can use Hyperz features or CMASK at a time */
        struct drm_file *hyperz_filp;
@@ -2594,6 +2602,8 @@ int radeon_vm_bo_rmv(struct radeon_device *rdev,
 
 /* audio */
 void r600_audio_update_hdmi(struct work_struct *work);
+struct r600_audio_pin *r600_audio_get_pin(struct radeon_device *rdev);
+struct r600_audio_pin *dce6_audio_get_pin(struct radeon_device *rdev);
 
 /*
  * R600 vram scratch functions
index da755bf..69f3122 100644 (file)
@@ -1739,6 +1739,8 @@ static struct radeon_asic trinity_asic = {
                .wait_for_vblank = &dce4_wait_for_vblank,
                .set_backlight_level = &atombios_set_backlight_level,
                .get_backlight_level = &atombios_get_backlight_level,
+               .hdmi_enable = &evergreen_hdmi_enable,
+               .hdmi_setmode = &evergreen_hdmi_setmode,
        },
        .copy = {
                .blit = &r600_copy_cpdma,
@@ -1867,6 +1869,8 @@ static struct radeon_asic si_asic = {
                .wait_for_vblank = &dce4_wait_for_vblank,
                .set_backlight_level = &atombios_set_backlight_level,
                .get_backlight_level = &atombios_get_backlight_level,
+               .hdmi_enable = &evergreen_hdmi_enable,
+               .hdmi_setmode = &evergreen_hdmi_setmode,
        },
        .copy = {
                .blit = NULL,
@@ -2009,6 +2013,8 @@ static struct radeon_asic ci_asic = {
                .bandwidth_update = &dce8_bandwidth_update,
                .get_vblank_counter = &evergreen_get_vblank_counter,
                .wait_for_vblank = &dce4_wait_for_vblank,
+               .hdmi_enable = &evergreen_hdmi_enable,
+               .hdmi_setmode = &evergreen_hdmi_setmode,
        },
        .copy = {
                .blit = NULL,
@@ -2107,6 +2113,8 @@ static struct radeon_asic kv_asic = {
                .bandwidth_update = &dce8_bandwidth_update,
                .get_vblank_counter = &evergreen_get_vblank_counter,
                .wait_for_vblank = &dce4_wait_for_vblank,
+               .hdmi_enable = &evergreen_hdmi_enable,
+               .hdmi_setmode = &evergreen_hdmi_setmode,
        },
        .copy = {
                .blit = NULL,
index e69f00a..818bbe6 100644 (file)
@@ -379,7 +379,7 @@ void r600_disable_interrupts(struct radeon_device *rdev);
 void r600_rlc_stop(struct radeon_device *rdev);
 /* r600 audio */
 int r600_audio_init(struct radeon_device *rdev);
-struct r600_audio r600_audio_status(struct radeon_device *rdev);
+struct r600_audio_pin r600_audio_status(struct radeon_device *rdev);
 void r600_audio_fini(struct radeon_device *rdev);
 int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder);
 void r600_hdmi_update_audio_settings(struct drm_encoder *encoder);
@@ -628,6 +628,8 @@ int trinity_dpm_force_performance_level(struct radeon_device *rdev,
 
 /* DCE6 - SI */
 void dce6_bandwidth_update(struct radeon_device *rdev);
+int dce6_audio_init(struct radeon_device *rdev);
+void dce6_audio_fini(struct radeon_device *rdev);
 
 /*
  * si
index 31d9fbe..af9cd6a 100644 (file)
@@ -1254,8 +1254,8 @@ static void radeon_afmt_init(struct radeon_device *rdev)
        for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++)
                rdev->mode_info.afmt[i] = NULL;
 
-       if (ASIC_IS_DCE6(rdev)) {
-               /* todo */
+       if (ASIC_IS_NODCE(rdev)) {
+               /* nothing to do */
        } else if (ASIC_IS_DCE4(rdev)) {
                static uint32_t eg_offsets[] = {
                        EVERGREEN_CRTC0_REGISTER_OFFSET,
@@ -1264,12 +1264,19 @@ static void radeon_afmt_init(struct radeon_device *rdev)
                        EVERGREEN_CRTC3_REGISTER_OFFSET,
                        EVERGREEN_CRTC4_REGISTER_OFFSET,
                        EVERGREEN_CRTC5_REGISTER_OFFSET,
+                       0x13830 - 0x7030,
                };
                int num_afmt;
 
+               /* DCE8 has 7 audio blocks tied to DIG encoders */
+               /* DCE6 has 6 audio blocks tied to DIG encoders */
                /* DCE4/5 has 6 audio blocks tied to DIG encoders */
                /* DCE4.1 has 2 audio blocks tied to DIG encoders */
-               if (ASIC_IS_DCE5(rdev))
+               if (ASIC_IS_DCE8(rdev))
+                       num_afmt = 7;
+               else if (ASIC_IS_DCE6(rdev))
+                       num_afmt = 6;
+               else if (ASIC_IS_DCE5(rdev))
                        num_afmt = 6;
                else if (ASIC_IS_DCE41(rdev))
                        num_afmt = 2;
index 8296632..d908d8d 100644 (file)
@@ -225,6 +225,7 @@ struct radeon_afmt {
        int offset;
        bool last_buffer_filled_status;
        int id;
+       struct r600_audio_pin *pin;
 };
 
 struct radeon_mode_info {
@@ -233,7 +234,7 @@ struct radeon_mode_info {
        enum radeon_connector_table connector_table;
        bool mode_config_initialized;
        struct radeon_crtc *crtcs[6];
-       struct radeon_afmt *afmt[6];
+       struct radeon_afmt *afmt[7];
        /* DVI-I properties */
        struct drm_property *coherent_mode_property;
        /* DAC enable load detect */
index f5307e6..fb2058c 100644 (file)
@@ -6264,6 +6264,10 @@ static int si_startup(struct radeon_device *rdev)
                return r;
        }
 
+       r = dce6_audio_init(rdev);
+       if (r)
+               return r;
+
        return 0;
 }
 
@@ -6295,6 +6299,7 @@ int si_resume(struct radeon_device *rdev)
 
 int si_suspend(struct radeon_device *rdev)
 {
+       dce6_audio_fini(rdev);
        radeon_vm_manager_fini(rdev);
        si_cp_enable(rdev, false);
        cayman_dma_stop(rdev);
index 2c8da27..968cf69 100644 (file)
 
 #define HDP_REG_COHERENCY_FLUSH_CNTL                   0x54A0
 
+/* DCE6 ELD audio interface */
+#define AZ_F0_CODEC_ENDPOINT_INDEX                       0x5E00
+#       define AZ_ENDPOINT_REG_INDEX(x)                  (((x) & 0xff) << 0)
+#       define AZ_ENDPOINT_REG_WRITE_EN                  (1 << 8)
+#define AZ_F0_CODEC_ENDPOINT_DATA                        0x5E04
+
+#define AZ_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER          0x25
+#define                SPEAKER_ALLOCATION(x)                   (((x) & 0x7f) << 0)
+#define                SPEAKER_ALLOCATION_MASK                 (0x7f << 0)
+#define                SPEAKER_ALLOCATION_SHIFT                0
+#define                HDMI_CONNECTION                         (1 << 16)
+#define                DP_CONNECTION                           (1 << 17)
+
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0        0x28 /* LPCM */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR1        0x29 /* AC3 */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR2        0x2A /* MPEG1 */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR3        0x2B /* MP3 */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR4        0x2C /* MPEG2 */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR5        0x2D /* AAC */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR6        0x2E /* DTS */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR7        0x2F /* ATRAC */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR8        0x30 /* one bit audio - leave at 0 (default) */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR9        0x31 /* Dolby Digital */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR10       0x32 /* DTS-HD */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR11       0x33 /* MAT-MLP */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR12       0x34 /* DTS */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR13       0x35 /* WMA Pro */
+#       define MAX_CHANNELS(x)                            (((x) & 0x7) << 0)
+/* max channels minus one.  7 = 8 channels */
+#       define SUPPORTED_FREQUENCIES(x)                   (((x) & 0xff) << 8)
+#       define DESCRIPTOR_BYTE_2(x)                       (((x) & 0xff) << 16)
+#       define SUPPORTED_FREQUENCIES_STEREO(x)            (((x) & 0xff) << 24) /* LPCM only */
+/* SUPPORTED_FREQUENCIES, SUPPORTED_FREQUENCIES_STEREO
+ * bit0 = 32 kHz
+ * bit1 = 44.1 kHz
+ * bit2 = 48 kHz
+ * bit3 = 88.2 kHz
+ * bit4 = 96 kHz
+ * bit5 = 176.4 kHz
+ * bit6 = 192 kHz
+ */
+#define AZ_F0_CODEC_PIN_CONTROL_HOTPLUG_CONTROL          0x54
+#       define AUDIO_ENABLED                             (1 << 31)
+
+#define AZ_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT  0x56
+#define                PORT_CONNECTIVITY_MASK                          (3 << 30)
+#define                PORT_CONNECTIVITY_SHIFT                         30
+
 #define        DC_LB_MEMORY_SPLIT                                      0x6b0c
 #define                DC_LB_MEMORY_CONFIG(x)                          ((x) << 20)
 
 /* 0x6e98, 0x7a98, 0x10698, 0x11298, 0x11e98, 0x12a98 */
 #define CRTC_STATUS_FRAME_COUNT                         0x6e98
 
+#define AFMT_AUDIO_SRC_CONTROL                          0x713c
+#define                AFMT_AUDIO_SRC_SELECT(x)                (((x) & 7) << 0)
+/* AFMT_AUDIO_SRC_SELECT
+ * 0 = stream0
+ * 1 = stream1
+ * 2 = stream2
+ * 3 = stream3
+ * 4 = stream4
+ * 5 = stream5
+ */
+
 #define        GRBM_CNTL                                       0x8000
 #define                GRBM_READ_TIMEOUT(x)                            ((x) << 0)