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: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Christian König <christian.koenig@amd.com> Acked-by: Rafał Miłecki <zajec5@gmail.com>
This commit is contained in:
parent
a4d39e6894
commit
b530602fd4
|
@ -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 += \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_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;
|
||||
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;
|
||||
|
||||
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];
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -635,6 +635,54 @@
|
|||
|
||||
#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)
|
||||
|
||||
|
@ -755,6 +803,17 @@
|
|||
/* 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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue