162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2008 Advanced Micro Devices, Inc. 362306a36Sopenharmony_ci * Copyright 2008 Red Hat Inc. 462306a36Sopenharmony_ci * Copyright 2009 Christian König. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 762306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 862306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 962306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1062306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1162306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1462306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1762306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1862306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1962306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 2062306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 2162306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2262306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Authors: Christian König 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci#include <linux/hdmi.h> 2762306a36Sopenharmony_ci#include <linux/gcd.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <drm/radeon_drm.h> 3062306a36Sopenharmony_ci#include "radeon.h" 3162306a36Sopenharmony_ci#include "radeon_asic.h" 3262306a36Sopenharmony_ci#include "radeon_audio.h" 3362306a36Sopenharmony_ci#include "r600.h" 3462306a36Sopenharmony_ci#include "r600d.h" 3562306a36Sopenharmony_ci#include "atom.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * HDMI color format 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cienum r600_hdmi_color_format { 4162306a36Sopenharmony_ci RGB = 0, 4262306a36Sopenharmony_ci YCC_422 = 1, 4362306a36Sopenharmony_ci YCC_444 = 2 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * IEC60958 status bits 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cienum r600_hdmi_iec_status_bits { 5062306a36Sopenharmony_ci AUDIO_STATUS_DIG_ENABLE = 0x01, 5162306a36Sopenharmony_ci AUDIO_STATUS_V = 0x02, 5262306a36Sopenharmony_ci AUDIO_STATUS_VCFG = 0x04, 5362306a36Sopenharmony_ci AUDIO_STATUS_EMPHASIS = 0x08, 5462306a36Sopenharmony_ci AUDIO_STATUS_COPYRIGHT = 0x10, 5562306a36Sopenharmony_ci AUDIO_STATUS_NONAUDIO = 0x20, 5662306a36Sopenharmony_ci AUDIO_STATUS_PROFESSIONAL = 0x40, 5762306a36Sopenharmony_ci AUDIO_STATUS_LEVEL = 0x80 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic struct r600_audio_pin r600_audio_status(struct radeon_device *rdev) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct r600_audio_pin status = {}; 6362306a36Sopenharmony_ci uint32_t value; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* number of channels */ 6862306a36Sopenharmony_ci status.channels = (value & 0x7) + 1; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* bits per sample */ 7162306a36Sopenharmony_ci switch ((value & 0xF0) >> 4) { 7262306a36Sopenharmony_ci case 0x0: 7362306a36Sopenharmony_ci status.bits_per_sample = 8; 7462306a36Sopenharmony_ci break; 7562306a36Sopenharmony_ci case 0x1: 7662306a36Sopenharmony_ci status.bits_per_sample = 16; 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci case 0x2: 7962306a36Sopenharmony_ci status.bits_per_sample = 20; 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci case 0x3: 8262306a36Sopenharmony_ci status.bits_per_sample = 24; 8362306a36Sopenharmony_ci break; 8462306a36Sopenharmony_ci case 0x4: 8562306a36Sopenharmony_ci status.bits_per_sample = 32; 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci default: 8862306a36Sopenharmony_ci dev_err(rdev->dev, "Unknown bits per sample 0x%x, using 16\n", 8962306a36Sopenharmony_ci (int)value); 9062306a36Sopenharmony_ci status.bits_per_sample = 16; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* current sampling rate in HZ */ 9462306a36Sopenharmony_ci if (value & 0x4000) 9562306a36Sopenharmony_ci status.rate = 44100; 9662306a36Sopenharmony_ci else 9762306a36Sopenharmony_ci status.rate = 48000; 9862306a36Sopenharmony_ci status.rate *= ((value >> 11) & 0x7) + 1; 9962306a36Sopenharmony_ci status.rate /= ((value >> 8) & 0x7) + 1; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci value = RREG32(R600_AUDIO_STATUS_BITS); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* iec 60958 status bits */ 10462306a36Sopenharmony_ci status.status_bits = value & 0xff; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* iec 60958 category code */ 10762306a36Sopenharmony_ci status.category_code = (value >> 8) & 0xff; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return status; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * update all hdmi interfaces with current audio parameters 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_civoid r600_audio_update_hdmi(struct work_struct *work) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct radeon_device *rdev = container_of(work, struct radeon_device, 11862306a36Sopenharmony_ci audio_work); 11962306a36Sopenharmony_ci struct drm_device *dev = rdev->ddev; 12062306a36Sopenharmony_ci struct r600_audio_pin audio_status = r600_audio_status(rdev); 12162306a36Sopenharmony_ci struct drm_encoder *encoder; 12262306a36Sopenharmony_ci bool changed = false; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (rdev->audio.pin[0].channels != audio_status.channels || 12562306a36Sopenharmony_ci rdev->audio.pin[0].rate != audio_status.rate || 12662306a36Sopenharmony_ci rdev->audio.pin[0].bits_per_sample != audio_status.bits_per_sample || 12762306a36Sopenharmony_ci rdev->audio.pin[0].status_bits != audio_status.status_bits || 12862306a36Sopenharmony_ci rdev->audio.pin[0].category_code != audio_status.category_code) { 12962306a36Sopenharmony_ci rdev->audio.pin[0] = audio_status; 13062306a36Sopenharmony_ci changed = true; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 13462306a36Sopenharmony_ci if (!radeon_encoder_is_digital(encoder)) 13562306a36Sopenharmony_ci continue; 13662306a36Sopenharmony_ci if (changed || r600_hdmi_buffer_status_changed(encoder)) 13762306a36Sopenharmony_ci r600_hdmi_update_audio_settings(encoder); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* enable the audio stream */ 14262306a36Sopenharmony_civoid r600_audio_enable(struct radeon_device *rdev, 14362306a36Sopenharmony_ci struct r600_audio_pin *pin, 14462306a36Sopenharmony_ci u8 enable_mask) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci u32 tmp = RREG32(AZ_HOT_PLUG_CONTROL); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (!pin) 14962306a36Sopenharmony_ci return; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (enable_mask) { 15262306a36Sopenharmony_ci tmp |= AUDIO_ENABLED; 15362306a36Sopenharmony_ci if (enable_mask & 1) 15462306a36Sopenharmony_ci tmp |= PIN0_AUDIO_ENABLED; 15562306a36Sopenharmony_ci if (enable_mask & 2) 15662306a36Sopenharmony_ci tmp |= PIN1_AUDIO_ENABLED; 15762306a36Sopenharmony_ci if (enable_mask & 4) 15862306a36Sopenharmony_ci tmp |= PIN2_AUDIO_ENABLED; 15962306a36Sopenharmony_ci if (enable_mask & 8) 16062306a36Sopenharmony_ci tmp |= PIN3_AUDIO_ENABLED; 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci tmp &= ~(AUDIO_ENABLED | 16362306a36Sopenharmony_ci PIN0_AUDIO_ENABLED | 16462306a36Sopenharmony_ci PIN1_AUDIO_ENABLED | 16562306a36Sopenharmony_ci PIN2_AUDIO_ENABLED | 16662306a36Sopenharmony_ci PIN3_AUDIO_ENABLED); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci WREG32(AZ_HOT_PLUG_CONTROL, tmp); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistruct r600_audio_pin *r600_audio_get_pin(struct radeon_device *rdev) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci /* only one pin on 6xx-NI */ 17562306a36Sopenharmony_ci return &rdev->audio.pin[0]; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_civoid r600_hdmi_update_acr(struct drm_encoder *encoder, long offset, 17962306a36Sopenharmony_ci const struct radeon_hdmi_acr *acr) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 18262306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* DCE 3.0 uses register that's normally for CRC_CONTROL */ 18562306a36Sopenharmony_ci uint32_t acr_ctl = ASIC_IS_DCE3(rdev) ? DCE3_HDMI0_ACR_PACKET_CONTROL : 18662306a36Sopenharmony_ci HDMI0_ACR_PACKET_CONTROL; 18762306a36Sopenharmony_ci WREG32_P(acr_ctl + offset, 18862306a36Sopenharmony_ci HDMI0_ACR_SOURCE | /* select SW CTS value */ 18962306a36Sopenharmony_ci HDMI0_ACR_AUTO_SEND, /* allow hw to sent ACR packets when required */ 19062306a36Sopenharmony_ci ~(HDMI0_ACR_SOURCE | 19162306a36Sopenharmony_ci HDMI0_ACR_AUTO_SEND)); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci WREG32_P(HDMI0_ACR_32_0 + offset, 19462306a36Sopenharmony_ci HDMI0_ACR_CTS_32(acr->cts_32khz), 19562306a36Sopenharmony_ci ~HDMI0_ACR_CTS_32_MASK); 19662306a36Sopenharmony_ci WREG32_P(HDMI0_ACR_32_1 + offset, 19762306a36Sopenharmony_ci HDMI0_ACR_N_32(acr->n_32khz), 19862306a36Sopenharmony_ci ~HDMI0_ACR_N_32_MASK); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci WREG32_P(HDMI0_ACR_44_0 + offset, 20162306a36Sopenharmony_ci HDMI0_ACR_CTS_44(acr->cts_44_1khz), 20262306a36Sopenharmony_ci ~HDMI0_ACR_CTS_44_MASK); 20362306a36Sopenharmony_ci WREG32_P(HDMI0_ACR_44_1 + offset, 20462306a36Sopenharmony_ci HDMI0_ACR_N_44(acr->n_44_1khz), 20562306a36Sopenharmony_ci ~HDMI0_ACR_N_44_MASK); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci WREG32_P(HDMI0_ACR_48_0 + offset, 20862306a36Sopenharmony_ci HDMI0_ACR_CTS_48(acr->cts_48khz), 20962306a36Sopenharmony_ci ~HDMI0_ACR_CTS_48_MASK); 21062306a36Sopenharmony_ci WREG32_P(HDMI0_ACR_48_1 + offset, 21162306a36Sopenharmony_ci HDMI0_ACR_N_48(acr->n_48khz), 21262306a36Sopenharmony_ci ~HDMI0_ACR_N_48_MASK); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/* 21662306a36Sopenharmony_ci * build a HDMI Video Info Frame 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_civoid r600_set_avi_packet(struct radeon_device *rdev, u32 offset, 21962306a36Sopenharmony_ci unsigned char *buffer, size_t size) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci uint8_t *frame = buffer + 3; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci WREG32(HDMI0_AVI_INFO0 + offset, 22462306a36Sopenharmony_ci frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); 22562306a36Sopenharmony_ci WREG32(HDMI0_AVI_INFO1 + offset, 22662306a36Sopenharmony_ci frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x7] << 24)); 22762306a36Sopenharmony_ci WREG32(HDMI0_AVI_INFO2 + offset, 22862306a36Sopenharmony_ci frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24)); 22962306a36Sopenharmony_ci WREG32(HDMI0_AVI_INFO3 + offset, 23062306a36Sopenharmony_ci frame[0xC] | (frame[0xD] << 8) | (buffer[1] << 24)); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci WREG32_OR(HDMI0_INFOFRAME_CONTROL1 + offset, 23362306a36Sopenharmony_ci HDMI0_AVI_INFO_LINE(2)); /* anything other than 0 */ 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset, 23662306a36Sopenharmony_ci HDMI0_AVI_INFO_SEND | /* enable AVI info frames */ 23762306a36Sopenharmony_ci HDMI0_AVI_INFO_CONT); /* send AVI info frames every frame/field */ 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* 24262306a36Sopenharmony_ci * build a Audio Info Frame 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_cistatic void r600_hdmi_update_audio_infoframe(struct drm_encoder *encoder, 24562306a36Sopenharmony_ci const void *buffer, size_t size) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 24862306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 24962306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 25062306a36Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 25162306a36Sopenharmony_ci uint32_t offset = dig->afmt->offset; 25262306a36Sopenharmony_ci const u8 *frame = buffer + 3; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci WREG32(HDMI0_AUDIO_INFO0 + offset, 25562306a36Sopenharmony_ci frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); 25662306a36Sopenharmony_ci WREG32(HDMI0_AUDIO_INFO1 + offset, 25762306a36Sopenharmony_ci frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x8] << 24)); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* 26162306a36Sopenharmony_ci * test if audio buffer is filled enough to start playing 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic bool r600_hdmi_is_audio_buffer_filled(struct drm_encoder *encoder) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 26662306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 26762306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 26862306a36Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 26962306a36Sopenharmony_ci uint32_t offset = dig->afmt->offset; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return (RREG32(HDMI0_STATUS + offset) & 0x10) != 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/* 27562306a36Sopenharmony_ci * have buffer status changed since last call? 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ciint r600_hdmi_buffer_status_changed(struct drm_encoder *encoder) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 28062306a36Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 28162306a36Sopenharmony_ci int status, result; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (!dig->afmt || !dig->afmt->enabled) 28462306a36Sopenharmony_ci return 0; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci status = r600_hdmi_is_audio_buffer_filled(encoder); 28762306a36Sopenharmony_ci result = dig->afmt->last_buffer_filled_status != status; 28862306a36Sopenharmony_ci dig->afmt->last_buffer_filled_status = status; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return result; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci/* 29462306a36Sopenharmony_ci * write the audio workaround status to the hardware 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_civoid r600_hdmi_audio_workaround(struct drm_encoder *encoder) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 29962306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 30062306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 30162306a36Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 30262306a36Sopenharmony_ci uint32_t offset = dig->afmt->offset; 30362306a36Sopenharmony_ci bool hdmi_audio_workaround = false; /* FIXME */ 30462306a36Sopenharmony_ci u32 value; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!hdmi_audio_workaround || 30762306a36Sopenharmony_ci r600_hdmi_is_audio_buffer_filled(encoder)) 30862306a36Sopenharmony_ci value = 0; /* disable workaround */ 30962306a36Sopenharmony_ci else 31062306a36Sopenharmony_ci value = HDMI0_AUDIO_TEST_EN; /* enable workaround */ 31162306a36Sopenharmony_ci WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset, 31262306a36Sopenharmony_ci value, ~HDMI0_AUDIO_TEST_EN); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_civoid r600_hdmi_audio_set_dto(struct radeon_device *rdev, 31662306a36Sopenharmony_ci struct radeon_crtc *crtc, unsigned int clock) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder; 31962306a36Sopenharmony_ci struct radeon_encoder_atom_dig *dig; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (!crtc) 32262306a36Sopenharmony_ci return; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci radeon_encoder = to_radeon_encoder(crtc->encoder); 32562306a36Sopenharmony_ci dig = radeon_encoder->enc_priv; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (!dig) 32862306a36Sopenharmony_ci return; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (dig->dig_encoder == 0) { 33162306a36Sopenharmony_ci WREG32(DCCG_AUDIO_DTO0_PHASE, 24000 * 100); 33262306a36Sopenharmony_ci WREG32(DCCG_AUDIO_DTO0_MODULE, clock * 100); 33362306a36Sopenharmony_ci WREG32(DCCG_AUDIO_DTO_SELECT, 0); /* select DTO0 */ 33462306a36Sopenharmony_ci } else { 33562306a36Sopenharmony_ci WREG32(DCCG_AUDIO_DTO1_PHASE, 24000 * 100); 33662306a36Sopenharmony_ci WREG32(DCCG_AUDIO_DTO1_MODULE, clock * 100); 33762306a36Sopenharmony_ci WREG32(DCCG_AUDIO_DTO_SELECT, 1); /* select DTO1 */ 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_civoid r600_set_vbi_packet(struct drm_encoder *encoder, u32 offset) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 34462306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci WREG32_OR(HDMI0_VBI_PACKET_CONTROL + offset, 34762306a36Sopenharmony_ci HDMI0_NULL_SEND | /* send null packets when required */ 34862306a36Sopenharmony_ci HDMI0_GC_SEND | /* send general control packets */ 34962306a36Sopenharmony_ci HDMI0_GC_CONT); /* send general control packets every frame */ 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_civoid r600_set_audio_packet(struct drm_encoder *encoder, u32 offset) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 35562306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset, 35862306a36Sopenharmony_ci HDMI0_AUDIO_SAMPLE_SEND | /* send audio packets */ 35962306a36Sopenharmony_ci HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */ 36062306a36Sopenharmony_ci HDMI0_AUDIO_PACKETS_PER_LINE(3) | /* should be suffient for all audio modes and small enough for all hblanks */ 36162306a36Sopenharmony_ci HDMI0_60958_CS_UPDATE, /* allow 60958 channel status fields to be updated */ 36262306a36Sopenharmony_ci ~(HDMI0_AUDIO_SAMPLE_SEND | 36362306a36Sopenharmony_ci HDMI0_AUDIO_DELAY_EN_MASK | 36462306a36Sopenharmony_ci HDMI0_AUDIO_PACKETS_PER_LINE_MASK | 36562306a36Sopenharmony_ci HDMI0_60958_CS_UPDATE)); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset, 36862306a36Sopenharmony_ci HDMI0_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */ 36962306a36Sopenharmony_ci HDMI0_AUDIO_INFO_UPDATE); /* required for audio info values to be updated */ 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci WREG32_P(HDMI0_INFOFRAME_CONTROL1 + offset, 37262306a36Sopenharmony_ci HDMI0_AUDIO_INFO_LINE(2), /* anything other than 0 */ 37362306a36Sopenharmony_ci ~HDMI0_AUDIO_INFO_LINE_MASK); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci WREG32_AND(HDMI0_GENERIC_PACKET_CONTROL + offset, 37662306a36Sopenharmony_ci ~(HDMI0_GENERIC0_SEND | 37762306a36Sopenharmony_ci HDMI0_GENERIC0_CONT | 37862306a36Sopenharmony_ci HDMI0_GENERIC0_UPDATE | 37962306a36Sopenharmony_ci HDMI0_GENERIC1_SEND | 38062306a36Sopenharmony_ci HDMI0_GENERIC1_CONT | 38162306a36Sopenharmony_ci HDMI0_GENERIC0_LINE_MASK | 38262306a36Sopenharmony_ci HDMI0_GENERIC1_LINE_MASK)); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci WREG32_P(HDMI0_60958_0 + offset, 38562306a36Sopenharmony_ci HDMI0_60958_CS_CHANNEL_NUMBER_L(1), 38662306a36Sopenharmony_ci ~(HDMI0_60958_CS_CHANNEL_NUMBER_L_MASK | 38762306a36Sopenharmony_ci HDMI0_60958_CS_CLOCK_ACCURACY_MASK)); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci WREG32_P(HDMI0_60958_1 + offset, 39062306a36Sopenharmony_ci HDMI0_60958_CS_CHANNEL_NUMBER_R(2), 39162306a36Sopenharmony_ci ~HDMI0_60958_CS_CHANNEL_NUMBER_R_MASK); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_civoid r600_set_mute(struct drm_encoder *encoder, u32 offset, bool mute) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 39762306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (mute) 40062306a36Sopenharmony_ci WREG32_OR(HDMI0_GC + offset, HDMI0_GC_AVMUTE); 40162306a36Sopenharmony_ci else 40262306a36Sopenharmony_ci WREG32_AND(HDMI0_GC + offset, ~HDMI0_GC_AVMUTE); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci/** 40662306a36Sopenharmony_ci * r600_hdmi_update_audio_settings - Update audio infoframe 40762306a36Sopenharmony_ci * 40862306a36Sopenharmony_ci * @encoder: drm encoder 40962306a36Sopenharmony_ci * 41062306a36Sopenharmony_ci * Gets info about current audio stream and updates audio infoframe. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_civoid r600_hdmi_update_audio_settings(struct drm_encoder *encoder) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 41562306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 41662306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 41762306a36Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 41862306a36Sopenharmony_ci struct r600_audio_pin audio = r600_audio_status(rdev); 41962306a36Sopenharmony_ci uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; 42062306a36Sopenharmony_ci struct hdmi_audio_infoframe frame; 42162306a36Sopenharmony_ci uint32_t offset; 42262306a36Sopenharmony_ci uint32_t value; 42362306a36Sopenharmony_ci ssize_t err; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (!dig->afmt || !dig->afmt->enabled) 42662306a36Sopenharmony_ci return; 42762306a36Sopenharmony_ci offset = dig->afmt->offset; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci DRM_DEBUG("%s with %d channels, %d Hz sampling rate, %d bits per sample,\n", 43062306a36Sopenharmony_ci r600_hdmi_is_audio_buffer_filled(encoder) ? "playing" : "stopped", 43162306a36Sopenharmony_ci audio.channels, audio.rate, audio.bits_per_sample); 43262306a36Sopenharmony_ci DRM_DEBUG("0x%02X IEC60958 status bits and 0x%02X category code\n", 43362306a36Sopenharmony_ci (int)audio.status_bits, (int)audio.category_code); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci err = hdmi_audio_infoframe_init(&frame); 43662306a36Sopenharmony_ci if (err < 0) { 43762306a36Sopenharmony_ci DRM_ERROR("failed to setup audio infoframe\n"); 43862306a36Sopenharmony_ci return; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci frame.channels = audio.channels; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); 44462306a36Sopenharmony_ci if (err < 0) { 44562306a36Sopenharmony_ci DRM_ERROR("failed to pack audio infoframe\n"); 44662306a36Sopenharmony_ci return; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci value = RREG32(HDMI0_AUDIO_PACKET_CONTROL + offset); 45062306a36Sopenharmony_ci if (value & HDMI0_AUDIO_TEST_EN) 45162306a36Sopenharmony_ci WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset, 45262306a36Sopenharmony_ci value & ~HDMI0_AUDIO_TEST_EN); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci WREG32_OR(HDMI0_CONTROL + offset, 45562306a36Sopenharmony_ci HDMI0_ERROR_ACK); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci WREG32_AND(HDMI0_INFOFRAME_CONTROL0 + offset, 45862306a36Sopenharmony_ci ~HDMI0_AUDIO_INFO_SOURCE); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci r600_hdmi_update_audio_infoframe(encoder, buffer, sizeof(buffer)); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset, 46362306a36Sopenharmony_ci HDMI0_AUDIO_INFO_CONT | 46462306a36Sopenharmony_ci HDMI0_AUDIO_INFO_UPDATE); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci/* 46862306a36Sopenharmony_ci * enable the HDMI engine 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_civoid r600_hdmi_enable(struct drm_encoder *encoder, bool enable) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 47362306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 47462306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 47562306a36Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 47662306a36Sopenharmony_ci u32 hdmi = HDMI0_ERROR_ACK; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (!dig || !dig->afmt) 47962306a36Sopenharmony_ci return; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* Older chipsets require setting HDMI and routing manually */ 48262306a36Sopenharmony_ci if (!ASIC_IS_DCE3(rdev)) { 48362306a36Sopenharmony_ci if (enable) 48462306a36Sopenharmony_ci hdmi |= HDMI0_ENABLE; 48562306a36Sopenharmony_ci switch (radeon_encoder->encoder_id) { 48662306a36Sopenharmony_ci case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: 48762306a36Sopenharmony_ci if (enable) { 48862306a36Sopenharmony_ci WREG32_OR(AVIVO_TMDSA_CNTL, AVIVO_TMDSA_CNTL_HDMI_EN); 48962306a36Sopenharmony_ci hdmi |= HDMI0_STREAM(HDMI0_STREAM_TMDSA); 49062306a36Sopenharmony_ci } else { 49162306a36Sopenharmony_ci WREG32_AND(AVIVO_TMDSA_CNTL, ~AVIVO_TMDSA_CNTL_HDMI_EN); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci case ENCODER_OBJECT_ID_INTERNAL_LVTM1: 49562306a36Sopenharmony_ci if (enable) { 49662306a36Sopenharmony_ci WREG32_OR(AVIVO_LVTMA_CNTL, AVIVO_LVTMA_CNTL_HDMI_EN); 49762306a36Sopenharmony_ci hdmi |= HDMI0_STREAM(HDMI0_STREAM_LVTMA); 49862306a36Sopenharmony_ci } else { 49962306a36Sopenharmony_ci WREG32_AND(AVIVO_LVTMA_CNTL, ~AVIVO_LVTMA_CNTL_HDMI_EN); 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci case ENCODER_OBJECT_ID_INTERNAL_DDI: 50362306a36Sopenharmony_ci if (enable) { 50462306a36Sopenharmony_ci WREG32_OR(DDIA_CNTL, DDIA_HDMI_EN); 50562306a36Sopenharmony_ci hdmi |= HDMI0_STREAM(HDMI0_STREAM_DDIA); 50662306a36Sopenharmony_ci } else { 50762306a36Sopenharmony_ci WREG32_AND(DDIA_CNTL, ~DDIA_HDMI_EN); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: 51162306a36Sopenharmony_ci if (enable) 51262306a36Sopenharmony_ci hdmi |= HDMI0_STREAM(HDMI0_STREAM_DVOA); 51362306a36Sopenharmony_ci break; 51462306a36Sopenharmony_ci default: 51562306a36Sopenharmony_ci dev_err(rdev->dev, "Invalid encoder for HDMI: 0x%X\n", 51662306a36Sopenharmony_ci radeon_encoder->encoder_id); 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci WREG32(HDMI0_CONTROL + dig->afmt->offset, hdmi); 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (rdev->irq.installed) { 52362306a36Sopenharmony_ci /* if irq is available use it */ 52462306a36Sopenharmony_ci /* XXX: shouldn't need this on any asics. Double check DCE2/3 */ 52562306a36Sopenharmony_ci if (enable) 52662306a36Sopenharmony_ci radeon_irq_kms_enable_afmt(rdev, dig->afmt->id); 52762306a36Sopenharmony_ci else 52862306a36Sopenharmony_ci radeon_irq_kms_disable_afmt(rdev, dig->afmt->id); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci dig->afmt->enabled = enable; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci DRM_DEBUG("%sabling HDMI interface @ 0x%04X for encoder 0x%x\n", 53462306a36Sopenharmony_ci enable ? "En" : "Dis", dig->afmt->offset, radeon_encoder->encoder_id); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 537