提交 b530602f 编写于 作者: A Alex Deucher

drm/radeon: add audio support for DCE6/8 GPUs (v12)

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: NAlex Deucher <alexander.deucher@amd.com>
Signed-off-by: NChristian König <christian.koenig@amd.com>
Acked-by: NRafał Miłecki <zajec5@gmail.com>
上级 a4d39e68
...@@ -80,7 +80,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ ...@@ -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 \ 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 \ 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 \ 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 # add async DMA block
radeon-y += \ radeon-y += \
......
...@@ -682,8 +682,6 @@ atombios_digital_setup(struct drm_encoder *encoder, int action) ...@@ -682,8 +682,6 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
int int
atombios_get_encoder_mode(struct drm_encoder *encoder) 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 radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct drm_connector *connector; struct drm_connector *connector;
struct radeon_connector *radeon_connector; struct radeon_connector *radeon_connector;
...@@ -710,8 +708,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) ...@@ -710,8 +708,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
case DRM_MODE_CONNECTOR_DVII: case DRM_MODE_CONNECTOR_DVII:
case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */ case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */
if (drm_detect_hdmi_monitor(radeon_connector->edid) && if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
radeon_audio && radeon_audio)
!ASIC_IS_DCE6(rdev)) /* remove once we support DCE6 */
return ATOM_ENCODER_MODE_HDMI; return ATOM_ENCODER_MODE_HDMI;
else if (radeon_connector->use_digital) else if (radeon_connector->use_digital)
return ATOM_ENCODER_MODE_DVI; return ATOM_ENCODER_MODE_DVI;
...@@ -722,8 +719,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) ...@@ -722,8 +719,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
case DRM_MODE_CONNECTOR_HDMIA: case DRM_MODE_CONNECTOR_HDMIA:
default: default:
if (drm_detect_hdmi_monitor(radeon_connector->edid) && if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
radeon_audio && radeon_audio)
!ASIC_IS_DCE6(rdev)) /* remove once we support DCE6 */
return ATOM_ENCODER_MODE_HDMI; return ATOM_ENCODER_MODE_HDMI;
else else
return ATOM_ENCODER_MODE_DVI; return ATOM_ENCODER_MODE_DVI;
...@@ -737,8 +733,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) ...@@ -737,8 +733,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
(dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
return ATOM_ENCODER_MODE_DP; return ATOM_ENCODER_MODE_DP;
else if (drm_detect_hdmi_monitor(radeon_connector->edid) && else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
radeon_audio && radeon_audio)
!ASIC_IS_DCE6(rdev)) /* remove once we support DCE6 */
return ATOM_ENCODER_MODE_HDMI; return ATOM_ENCODER_MODE_HDMI;
else else
return ATOM_ENCODER_MODE_DVI; return ATOM_ENCODER_MODE_DVI;
......
...@@ -7004,6 +7004,10 @@ static int cik_startup(struct radeon_device *rdev) ...@@ -7004,6 +7004,10 @@ static int cik_startup(struct radeon_device *rdev)
return r; return r;
} }
r = dce6_audio_init(rdev);
if (r)
return r;
return 0; return 0;
} }
...@@ -7049,6 +7053,7 @@ int cik_resume(struct radeon_device *rdev) ...@@ -7049,6 +7053,7 @@ int cik_resume(struct radeon_device *rdev)
*/ */
int cik_suspend(struct radeon_device *rdev) int cik_suspend(struct radeon_device *rdev)
{ {
dce6_audio_fini(rdev);
radeon_vm_manager_fini(rdev); radeon_vm_manager_fini(rdev);
cik_cp_enable(rdev, false); cik_cp_enable(rdev, false);
cik_sdma_enable(rdev, false); cik_sdma_enable(rdev, false);
......
/*
* 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;
}
...@@ -32,6 +32,9 @@ ...@@ -32,6 +32,9 @@
#include "evergreend.h" #include "evergreend.h"
#include "atom.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 * 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) ...@@ -157,22 +160,26 @@ static void evergreen_audio_set_dto(struct drm_encoder *encoder, u32 clock)
if (!dig || !dig->afmt) if (!dig || !dig->afmt)
return; return;
if (max_ratio >= 8) { if (ASIC_IS_DCE6(rdev)) {
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; 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 */ /* XXX two dtos; generally use dto0 for hdmi */
/* Express [24MHz / target pixel clock] as an exact rational /* 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 ...@@ -266,7 +273,13 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
AFMT_AUDIO_CHANNEL_ENABLE(0xff)); AFMT_AUDIO_CHANNEL_ENABLE(0xff));
/* fglrx sets 0x40 in 0x5f80 here */ /* 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); err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
if (err < 0) { if (err < 0) {
...@@ -302,6 +315,8 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode ...@@ -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) 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 *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
...@@ -314,6 +329,15 @@ void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable) ...@@ -314,6 +329,15 @@ void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable)
if (!enable && !dig->afmt->enabled) if (!enable && !dig->afmt->enabled)
return; 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; dig->afmt->enabled = enable;
DRM_DEBUG("%sabling HDMI interface @ 0x%04X for encoder 0x%x\n", DRM_DEBUG("%sabling HDMI interface @ 0x%04X for encoder 0x%x\n",
......
...@@ -2027,9 +2027,15 @@ static int cayman_startup(struct radeon_device *rdev) ...@@ -2027,9 +2027,15 @@ static int cayman_startup(struct radeon_device *rdev)
return r; return r;
} }
r = r600_audio_init(rdev); if (ASIC_IS_DCE6(rdev)) {
if (r) r = dce6_audio_init(rdev);
return r; if (r)
return r;
} else {
r = r600_audio_init(rdev);
if (r)
return r;
}
return 0; return 0;
} }
...@@ -2060,7 +2066,10 @@ int cayman_resume(struct radeon_device *rdev) ...@@ -2060,7 +2066,10 @@ int cayman_resume(struct radeon_device *rdev)
int cayman_suspend(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); radeon_vm_manager_fini(rdev);
cayman_cp_enable(rdev, false); cayman_cp_enable(rdev, false);
cayman_dma_stop(rdev); cayman_dma_stop(rdev);
......
...@@ -57,12 +57,12 @@ static bool radeon_dig_encoder(struct drm_encoder *encoder) ...@@ -57,12 +57,12 @@ static bool radeon_dig_encoder(struct drm_encoder *encoder)
*/ */
static int r600_audio_chipset_supported(struct radeon_device *rdev) 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; uint32_t value;
value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL); value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL);
...@@ -120,16 +120,16 @@ void r600_audio_update_hdmi(struct work_struct *work) ...@@ -120,16 +120,16 @@ void r600_audio_update_hdmi(struct work_struct *work)
struct radeon_device *rdev = container_of(work, struct radeon_device, struct radeon_device *rdev = container_of(work, struct radeon_device,
audio_work); audio_work);
struct drm_device *dev = rdev->ddev; 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; struct drm_encoder *encoder;
bool changed = false; bool changed = false;
if (rdev->audio_status.channels != audio_status.channels || if (rdev->audio.pin[0].channels != audio_status.channels ||
rdev->audio_status.rate != audio_status.rate || rdev->audio.pin[0].rate != audio_status.rate ||
rdev->audio_status.bits_per_sample != audio_status.bits_per_sample || rdev->audio.pin[0].bits_per_sample != audio_status.bits_per_sample ||
rdev->audio_status.status_bits != audio_status.status_bits || rdev->audio.pin[0].status_bits != audio_status.status_bits ||
rdev->audio_status.category_code != audio_status.category_code) { rdev->audio.pin[0].category_code != audio_status.category_code) {
rdev->audio_status = audio_status; rdev->audio.pin[0] = audio_status;
changed = true; changed = true;
} }
...@@ -141,13 +141,13 @@ void r600_audio_update_hdmi(struct work_struct *work) ...@@ -141,13 +141,13 @@ void r600_audio_update_hdmi(struct work_struct *work)
} }
} }
/* /* enable the audio stream */
* turn on/off audio engine static void r600_audio_enable(struct radeon_device *rdev,
*/ struct r600_audio_pin *pin,
static void r600_audio_engine_enable(struct radeon_device *rdev, bool enable) bool enable)
{ {
u32 value = 0; u32 value = 0;
DRM_INFO("%s audio support\n", enable ? "Enabling" : "Disabling");
if (ASIC_IS_DCE4(rdev)) { if (ASIC_IS_DCE4(rdev)) {
if (enable) { if (enable) {
value |= 0x81000000; /* Required to enable audio */ value |= 0x81000000; /* Required to enable audio */
...@@ -158,7 +158,7 @@ static void r600_audio_engine_enable(struct radeon_device *rdev, bool enable) ...@@ -158,7 +158,7 @@ static void r600_audio_engine_enable(struct radeon_device *rdev, bool enable)
WREG32_P(R600_AUDIO_ENABLE, WREG32_P(R600_AUDIO_ENABLE,
enable ? 0x81000000 : 0x0, ~0x81000000); 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) ...@@ -169,13 +169,17 @@ int r600_audio_init(struct radeon_device *rdev)
if (!radeon_audio || !r600_audio_chipset_supported(rdev)) if (!radeon_audio || !r600_audio_chipset_supported(rdev))
return 0; 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; r600_audio_enable(rdev, &rdev->audio.pin[0], true);
rdev->audio_status.rate = -1;
rdev->audio_status.bits_per_sample = -1;
rdev->audio_status.status_bits = 0;
rdev->audio_status.category_code = 0;
return 0; return 0;
} }
...@@ -186,8 +190,16 @@ int r600_audio_init(struct radeon_device *rdev) ...@@ -186,8 +190,16 @@ int r600_audio_init(struct radeon_device *rdev)
*/ */
void r600_audio_fini(struct radeon_device *rdev) void r600_audio_fini(struct radeon_device *rdev)
{ {
if (!rdev->audio_enabled) if (!rdev->audio.enabled)
return; 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];
} }
...@@ -382,7 +382,7 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder) ...@@ -382,7 +382,7 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
struct radeon_device *rdev = dev->dev_private; struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 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]; uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
struct hdmi_audio_infoframe frame; struct hdmi_audio_infoframe frame;
uint32_t offset; uint32_t offset;
...@@ -491,6 +491,11 @@ void r600_hdmi_enable(struct drm_encoder *encoder, bool enable) ...@@ -491,6 +491,11 @@ void r600_hdmi_enable(struct drm_encoder *encoder, bool enable)
if (!enable && !dig->afmt->enabled) if (!enable && !dig->afmt->enabled)
return; return;
if (enable)
dig->afmt->pin = r600_audio_get_pin(rdev);
else
dig->afmt->pin = NULL;
/* Older chipsets require setting HDMI and routing manually */ /* Older chipsets require setting HDMI and routing manually */
if (!ASIC_IS_DCE3(rdev)) { if (!ASIC_IS_DCE3(rdev)) {
if (enable) if (enable)
......
...@@ -696,7 +696,7 @@ union radeon_irq_stat_regs { ...@@ -696,7 +696,7 @@ union radeon_irq_stat_regs {
#define RADEON_MAX_HPD_PINS 6 #define RADEON_MAX_HPD_PINS 6
#define RADEON_MAX_CRTCS 6 #define RADEON_MAX_CRTCS 6
#define RADEON_MAX_AFMT_BLOCKS 6 #define RADEON_MAX_AFMT_BLOCKS 7
struct radeon_irq { struct radeon_irq {
bool installed; bool installed;
...@@ -1537,12 +1537,21 @@ int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev, ...@@ -1537,12 +1537,21 @@ int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev,
int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev, int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev,
unsigned cg_upll_func_cntl); unsigned cg_upll_func_cntl);
struct r600_audio { struct r600_audio_pin {
int channels; int channels;
int rate; int rate;
int bits_per_sample; int bits_per_sample;
u8 status_bits; u8 status_bits;
u8 category_code; 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 { ...@@ -2128,9 +2137,8 @@ struct radeon_device {
struct work_struct reset_work; struct work_struct reset_work;
int num_crtc; /* number of crtcs */ int num_crtc; /* number of crtcs */
struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */ struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */
bool audio_enabled;
bool has_uvd; bool has_uvd;
struct r600_audio audio_status; /* audio stuff */ struct r600_audio audio; /* audio stuff */
struct notifier_block acpi_nb; struct notifier_block acpi_nb;
/* only one userspace can use Hyperz features or CMASK at a time */ /* only one userspace can use Hyperz features or CMASK at a time */
struct drm_file *hyperz_filp; struct drm_file *hyperz_filp;
...@@ -2594,6 +2602,8 @@ int radeon_vm_bo_rmv(struct radeon_device *rdev, ...@@ -2594,6 +2602,8 @@ int radeon_vm_bo_rmv(struct radeon_device *rdev,
/* audio */ /* audio */
void r600_audio_update_hdmi(struct work_struct *work); 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 * R600 vram scratch functions
......
...@@ -1739,6 +1739,8 @@ static struct radeon_asic trinity_asic = { ...@@ -1739,6 +1739,8 @@ static struct radeon_asic trinity_asic = {
.wait_for_vblank = &dce4_wait_for_vblank, .wait_for_vblank = &dce4_wait_for_vblank,
.set_backlight_level = &atombios_set_backlight_level, .set_backlight_level = &atombios_set_backlight_level,
.get_backlight_level = &atombios_get_backlight_level, .get_backlight_level = &atombios_get_backlight_level,
.hdmi_enable = &evergreen_hdmi_enable,
.hdmi_setmode = &evergreen_hdmi_setmode,
}, },
.copy = { .copy = {
.blit = &r600_copy_cpdma, .blit = &r600_copy_cpdma,
...@@ -1867,6 +1869,8 @@ static struct radeon_asic si_asic = { ...@@ -1867,6 +1869,8 @@ static struct radeon_asic si_asic = {
.wait_for_vblank = &dce4_wait_for_vblank, .wait_for_vblank = &dce4_wait_for_vblank,
.set_backlight_level = &atombios_set_backlight_level, .set_backlight_level = &atombios_set_backlight_level,
.get_backlight_level = &atombios_get_backlight_level, .get_backlight_level = &atombios_get_backlight_level,
.hdmi_enable = &evergreen_hdmi_enable,
.hdmi_setmode = &evergreen_hdmi_setmode,
}, },
.copy = { .copy = {
.blit = NULL, .blit = NULL,
...@@ -2009,6 +2013,8 @@ static struct radeon_asic ci_asic = { ...@@ -2009,6 +2013,8 @@ static struct radeon_asic ci_asic = {
.bandwidth_update = &dce8_bandwidth_update, .bandwidth_update = &dce8_bandwidth_update,
.get_vblank_counter = &evergreen_get_vblank_counter, .get_vblank_counter = &evergreen_get_vblank_counter,
.wait_for_vblank = &dce4_wait_for_vblank, .wait_for_vblank = &dce4_wait_for_vblank,
.hdmi_enable = &evergreen_hdmi_enable,
.hdmi_setmode = &evergreen_hdmi_setmode,
}, },
.copy = { .copy = {
.blit = NULL, .blit = NULL,
...@@ -2107,6 +2113,8 @@ static struct radeon_asic kv_asic = { ...@@ -2107,6 +2113,8 @@ static struct radeon_asic kv_asic = {
.bandwidth_update = &dce8_bandwidth_update, .bandwidth_update = &dce8_bandwidth_update,
.get_vblank_counter = &evergreen_get_vblank_counter, .get_vblank_counter = &evergreen_get_vblank_counter,
.wait_for_vblank = &dce4_wait_for_vblank, .wait_for_vblank = &dce4_wait_for_vblank,
.hdmi_enable = &evergreen_hdmi_enable,
.hdmi_setmode = &evergreen_hdmi_setmode,
}, },
.copy = { .copy = {
.blit = NULL, .blit = NULL,
......
...@@ -379,7 +379,7 @@ void r600_disable_interrupts(struct radeon_device *rdev); ...@@ -379,7 +379,7 @@ void r600_disable_interrupts(struct radeon_device *rdev);
void r600_rlc_stop(struct radeon_device *rdev); void r600_rlc_stop(struct radeon_device *rdev);
/* r600 audio */ /* r600 audio */
int r600_audio_init(struct radeon_device *rdev); 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); void r600_audio_fini(struct radeon_device *rdev);
int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder); int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder);
void r600_hdmi_update_audio_settings(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, ...@@ -628,6 +628,8 @@ int trinity_dpm_force_performance_level(struct radeon_device *rdev,
/* DCE6 - SI */ /* DCE6 - SI */
void dce6_bandwidth_update(struct radeon_device *rdev); 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 * si
......
...@@ -1254,8 +1254,8 @@ static void radeon_afmt_init(struct radeon_device *rdev) ...@@ -1254,8 +1254,8 @@ static void radeon_afmt_init(struct radeon_device *rdev)
for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++) for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++)
rdev->mode_info.afmt[i] = NULL; rdev->mode_info.afmt[i] = NULL;
if (ASIC_IS_DCE6(rdev)) { if (ASIC_IS_NODCE(rdev)) {
/* todo */ /* nothing to do */
} else if (ASIC_IS_DCE4(rdev)) { } else if (ASIC_IS_DCE4(rdev)) {
static uint32_t eg_offsets[] = { static uint32_t eg_offsets[] = {
EVERGREEN_CRTC0_REGISTER_OFFSET, EVERGREEN_CRTC0_REGISTER_OFFSET,
...@@ -1264,12 +1264,19 @@ static void radeon_afmt_init(struct radeon_device *rdev) ...@@ -1264,12 +1264,19 @@ static void radeon_afmt_init(struct radeon_device *rdev)
EVERGREEN_CRTC3_REGISTER_OFFSET, EVERGREEN_CRTC3_REGISTER_OFFSET,
EVERGREEN_CRTC4_REGISTER_OFFSET, EVERGREEN_CRTC4_REGISTER_OFFSET,
EVERGREEN_CRTC5_REGISTER_OFFSET, EVERGREEN_CRTC5_REGISTER_OFFSET,
0x13830 - 0x7030,
}; };
int num_afmt; 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/5 has 6 audio blocks tied to DIG encoders */
/* DCE4.1 has 2 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; num_afmt = 6;
else if (ASIC_IS_DCE41(rdev)) else if (ASIC_IS_DCE41(rdev))
num_afmt = 2; num_afmt = 2;
......
...@@ -225,6 +225,7 @@ struct radeon_afmt { ...@@ -225,6 +225,7 @@ struct radeon_afmt {
int offset; int offset;
bool last_buffer_filled_status; bool last_buffer_filled_status;
int id; int id;
struct r600_audio_pin *pin;
}; };
struct radeon_mode_info { struct radeon_mode_info {
...@@ -233,7 +234,7 @@ struct radeon_mode_info { ...@@ -233,7 +234,7 @@ struct radeon_mode_info {
enum radeon_connector_table connector_table; enum radeon_connector_table connector_table;
bool mode_config_initialized; bool mode_config_initialized;
struct radeon_crtc *crtcs[6]; struct radeon_crtc *crtcs[6];
struct radeon_afmt *afmt[6]; struct radeon_afmt *afmt[7];
/* DVI-I properties */ /* DVI-I properties */
struct drm_property *coherent_mode_property; struct drm_property *coherent_mode_property;
/* DAC enable load detect */ /* DAC enable load detect */
......
...@@ -6264,6 +6264,10 @@ static int si_startup(struct radeon_device *rdev) ...@@ -6264,6 +6264,10 @@ static int si_startup(struct radeon_device *rdev)
return r; return r;
} }
r = dce6_audio_init(rdev);
if (r)
return r;
return 0; return 0;
} }
...@@ -6295,6 +6299,7 @@ int si_resume(struct radeon_device *rdev) ...@@ -6295,6 +6299,7 @@ int si_resume(struct radeon_device *rdev)
int si_suspend(struct radeon_device *rdev) int si_suspend(struct radeon_device *rdev)
{ {
dce6_audio_fini(rdev);
radeon_vm_manager_fini(rdev); radeon_vm_manager_fini(rdev);
si_cp_enable(rdev, false); si_cp_enable(rdev, false);
cayman_dma_stop(rdev); cayman_dma_stop(rdev);
......
...@@ -635,6 +635,54 @@ ...@@ -635,6 +635,54 @@
#define HDP_REG_COHERENCY_FLUSH_CNTL 0x54A0 #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_SPLIT 0x6b0c
#define DC_LB_MEMORY_CONFIG(x) ((x) << 20) #define DC_LB_MEMORY_CONFIG(x) ((x) << 20)
...@@ -755,6 +803,17 @@ ...@@ -755,6 +803,17 @@
/* 0x6e98, 0x7a98, 0x10698, 0x11298, 0x11e98, 0x12a98 */ /* 0x6e98, 0x7a98, 0x10698, 0x11298, 0x11e98, 0x12a98 */
#define CRTC_STATUS_FRAME_COUNT 0x6e98 #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_CNTL 0x8000
#define GRBM_READ_TIMEOUT(x) ((x) << 0) #define GRBM_READ_TIMEOUT(x) ((x) << 0)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册