18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2008 Advanced Micro Devices, Inc. 38c2ecf20Sopenharmony_ci * Copyright 2008 Red Hat Inc. 48c2ecf20Sopenharmony_ci * Copyright 2009 Christian König. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 88c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 98c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 108c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 118c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 148c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 178c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 188c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 198c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 208c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 218c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 228c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Authors: Christian König 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci#include <linux/hdmi.h> 278c2ecf20Sopenharmony_ci#include <linux/gcd.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <drm/radeon_drm.h> 308c2ecf20Sopenharmony_ci#include "radeon.h" 318c2ecf20Sopenharmony_ci#include "radeon_asic.h" 328c2ecf20Sopenharmony_ci#include "radeon_audio.h" 338c2ecf20Sopenharmony_ci#include "r600d.h" 348c2ecf20Sopenharmony_ci#include "atom.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * HDMI color format 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cienum r600_hdmi_color_format { 408c2ecf20Sopenharmony_ci RGB = 0, 418c2ecf20Sopenharmony_ci YCC_422 = 1, 428c2ecf20Sopenharmony_ci YCC_444 = 2 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * IEC60958 status bits 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cienum r600_hdmi_iec_status_bits { 498c2ecf20Sopenharmony_ci AUDIO_STATUS_DIG_ENABLE = 0x01, 508c2ecf20Sopenharmony_ci AUDIO_STATUS_V = 0x02, 518c2ecf20Sopenharmony_ci AUDIO_STATUS_VCFG = 0x04, 528c2ecf20Sopenharmony_ci AUDIO_STATUS_EMPHASIS = 0x08, 538c2ecf20Sopenharmony_ci AUDIO_STATUS_COPYRIGHT = 0x10, 548c2ecf20Sopenharmony_ci AUDIO_STATUS_NONAUDIO = 0x20, 558c2ecf20Sopenharmony_ci AUDIO_STATUS_PROFESSIONAL = 0x40, 568c2ecf20Sopenharmony_ci AUDIO_STATUS_LEVEL = 0x80 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic struct r600_audio_pin r600_audio_status(struct radeon_device *rdev) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct r600_audio_pin status = {}; 628c2ecf20Sopenharmony_ci uint32_t value; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* number of channels */ 678c2ecf20Sopenharmony_ci status.channels = (value & 0x7) + 1; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* bits per sample */ 708c2ecf20Sopenharmony_ci switch ((value & 0xF0) >> 4) { 718c2ecf20Sopenharmony_ci case 0x0: 728c2ecf20Sopenharmony_ci status.bits_per_sample = 8; 738c2ecf20Sopenharmony_ci break; 748c2ecf20Sopenharmony_ci case 0x1: 758c2ecf20Sopenharmony_ci status.bits_per_sample = 16; 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci case 0x2: 788c2ecf20Sopenharmony_ci status.bits_per_sample = 20; 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci case 0x3: 818c2ecf20Sopenharmony_ci status.bits_per_sample = 24; 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci case 0x4: 848c2ecf20Sopenharmony_ci status.bits_per_sample = 32; 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci default: 878c2ecf20Sopenharmony_ci dev_err(rdev->dev, "Unknown bits per sample 0x%x, using 16\n", 888c2ecf20Sopenharmony_ci (int)value); 898c2ecf20Sopenharmony_ci status.bits_per_sample = 16; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* current sampling rate in HZ */ 938c2ecf20Sopenharmony_ci if (value & 0x4000) 948c2ecf20Sopenharmony_ci status.rate = 44100; 958c2ecf20Sopenharmony_ci else 968c2ecf20Sopenharmony_ci status.rate = 48000; 978c2ecf20Sopenharmony_ci status.rate *= ((value >> 11) & 0x7) + 1; 988c2ecf20Sopenharmony_ci status.rate /= ((value >> 8) & 0x7) + 1; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci value = RREG32(R600_AUDIO_STATUS_BITS); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* iec 60958 status bits */ 1038c2ecf20Sopenharmony_ci status.status_bits = value & 0xff; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* iec 60958 category code */ 1068c2ecf20Sopenharmony_ci status.category_code = (value >> 8) & 0xff; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return status; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * update all hdmi interfaces with current audio parameters 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_civoid r600_audio_update_hdmi(struct work_struct *work) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct radeon_device *rdev = container_of(work, struct radeon_device, 1178c2ecf20Sopenharmony_ci audio_work); 1188c2ecf20Sopenharmony_ci struct drm_device *dev = rdev->ddev; 1198c2ecf20Sopenharmony_ci struct r600_audio_pin audio_status = r600_audio_status(rdev); 1208c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 1218c2ecf20Sopenharmony_ci bool changed = false; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (rdev->audio.pin[0].channels != audio_status.channels || 1248c2ecf20Sopenharmony_ci rdev->audio.pin[0].rate != audio_status.rate || 1258c2ecf20Sopenharmony_ci rdev->audio.pin[0].bits_per_sample != audio_status.bits_per_sample || 1268c2ecf20Sopenharmony_ci rdev->audio.pin[0].status_bits != audio_status.status_bits || 1278c2ecf20Sopenharmony_ci rdev->audio.pin[0].category_code != audio_status.category_code) { 1288c2ecf20Sopenharmony_ci rdev->audio.pin[0] = audio_status; 1298c2ecf20Sopenharmony_ci changed = true; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 1338c2ecf20Sopenharmony_ci if (!radeon_encoder_is_digital(encoder)) 1348c2ecf20Sopenharmony_ci continue; 1358c2ecf20Sopenharmony_ci if (changed || r600_hdmi_buffer_status_changed(encoder)) 1368c2ecf20Sopenharmony_ci r600_hdmi_update_audio_settings(encoder); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* enable the audio stream */ 1418c2ecf20Sopenharmony_civoid r600_audio_enable(struct radeon_device *rdev, 1428c2ecf20Sopenharmony_ci struct r600_audio_pin *pin, 1438c2ecf20Sopenharmony_ci u8 enable_mask) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci u32 tmp = RREG32(AZ_HOT_PLUG_CONTROL); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (!pin) 1488c2ecf20Sopenharmony_ci return; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (enable_mask) { 1518c2ecf20Sopenharmony_ci tmp |= AUDIO_ENABLED; 1528c2ecf20Sopenharmony_ci if (enable_mask & 1) 1538c2ecf20Sopenharmony_ci tmp |= PIN0_AUDIO_ENABLED; 1548c2ecf20Sopenharmony_ci if (enable_mask & 2) 1558c2ecf20Sopenharmony_ci tmp |= PIN1_AUDIO_ENABLED; 1568c2ecf20Sopenharmony_ci if (enable_mask & 4) 1578c2ecf20Sopenharmony_ci tmp |= PIN2_AUDIO_ENABLED; 1588c2ecf20Sopenharmony_ci if (enable_mask & 8) 1598c2ecf20Sopenharmony_ci tmp |= PIN3_AUDIO_ENABLED; 1608c2ecf20Sopenharmony_ci } else { 1618c2ecf20Sopenharmony_ci tmp &= ~(AUDIO_ENABLED | 1628c2ecf20Sopenharmony_ci PIN0_AUDIO_ENABLED | 1638c2ecf20Sopenharmony_ci PIN1_AUDIO_ENABLED | 1648c2ecf20Sopenharmony_ci PIN2_AUDIO_ENABLED | 1658c2ecf20Sopenharmony_ci PIN3_AUDIO_ENABLED); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci WREG32(AZ_HOT_PLUG_CONTROL, tmp); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistruct r600_audio_pin *r600_audio_get_pin(struct radeon_device *rdev) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci /* only one pin on 6xx-NI */ 1748c2ecf20Sopenharmony_ci return &rdev->audio.pin[0]; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_civoid r600_hdmi_update_acr(struct drm_encoder *encoder, long offset, 1788c2ecf20Sopenharmony_ci const struct radeon_hdmi_acr *acr) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 1818c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* DCE 3.0 uses register that's normally for CRC_CONTROL */ 1848c2ecf20Sopenharmony_ci uint32_t acr_ctl = ASIC_IS_DCE3(rdev) ? DCE3_HDMI0_ACR_PACKET_CONTROL : 1858c2ecf20Sopenharmony_ci HDMI0_ACR_PACKET_CONTROL; 1868c2ecf20Sopenharmony_ci WREG32_P(acr_ctl + offset, 1878c2ecf20Sopenharmony_ci HDMI0_ACR_SOURCE | /* select SW CTS value */ 1888c2ecf20Sopenharmony_ci HDMI0_ACR_AUTO_SEND, /* allow hw to sent ACR packets when required */ 1898c2ecf20Sopenharmony_ci ~(HDMI0_ACR_SOURCE | 1908c2ecf20Sopenharmony_ci HDMI0_ACR_AUTO_SEND)); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci WREG32_P(HDMI0_ACR_32_0 + offset, 1938c2ecf20Sopenharmony_ci HDMI0_ACR_CTS_32(acr->cts_32khz), 1948c2ecf20Sopenharmony_ci ~HDMI0_ACR_CTS_32_MASK); 1958c2ecf20Sopenharmony_ci WREG32_P(HDMI0_ACR_32_1 + offset, 1968c2ecf20Sopenharmony_ci HDMI0_ACR_N_32(acr->n_32khz), 1978c2ecf20Sopenharmony_ci ~HDMI0_ACR_N_32_MASK); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci WREG32_P(HDMI0_ACR_44_0 + offset, 2008c2ecf20Sopenharmony_ci HDMI0_ACR_CTS_44(acr->cts_44_1khz), 2018c2ecf20Sopenharmony_ci ~HDMI0_ACR_CTS_44_MASK); 2028c2ecf20Sopenharmony_ci WREG32_P(HDMI0_ACR_44_1 + offset, 2038c2ecf20Sopenharmony_ci HDMI0_ACR_N_44(acr->n_44_1khz), 2048c2ecf20Sopenharmony_ci ~HDMI0_ACR_N_44_MASK); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci WREG32_P(HDMI0_ACR_48_0 + offset, 2078c2ecf20Sopenharmony_ci HDMI0_ACR_CTS_48(acr->cts_48khz), 2088c2ecf20Sopenharmony_ci ~HDMI0_ACR_CTS_48_MASK); 2098c2ecf20Sopenharmony_ci WREG32_P(HDMI0_ACR_48_1 + offset, 2108c2ecf20Sopenharmony_ci HDMI0_ACR_N_48(acr->n_48khz), 2118c2ecf20Sopenharmony_ci ~HDMI0_ACR_N_48_MASK); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/* 2158c2ecf20Sopenharmony_ci * build a HDMI Video Info Frame 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_civoid r600_set_avi_packet(struct radeon_device *rdev, u32 offset, 2188c2ecf20Sopenharmony_ci unsigned char *buffer, size_t size) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci uint8_t *frame = buffer + 3; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci WREG32(HDMI0_AVI_INFO0 + offset, 2238c2ecf20Sopenharmony_ci frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); 2248c2ecf20Sopenharmony_ci WREG32(HDMI0_AVI_INFO1 + offset, 2258c2ecf20Sopenharmony_ci frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x7] << 24)); 2268c2ecf20Sopenharmony_ci WREG32(HDMI0_AVI_INFO2 + offset, 2278c2ecf20Sopenharmony_ci frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24)); 2288c2ecf20Sopenharmony_ci WREG32(HDMI0_AVI_INFO3 + offset, 2298c2ecf20Sopenharmony_ci frame[0xC] | (frame[0xD] << 8) | (buffer[1] << 24)); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci WREG32_OR(HDMI0_INFOFRAME_CONTROL1 + offset, 2328c2ecf20Sopenharmony_ci HDMI0_AVI_INFO_LINE(2)); /* anything other than 0 */ 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset, 2358c2ecf20Sopenharmony_ci HDMI0_AVI_INFO_SEND | /* enable AVI info frames */ 2368c2ecf20Sopenharmony_ci HDMI0_AVI_INFO_CONT); /* send AVI info frames every frame/field */ 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/* 2418c2ecf20Sopenharmony_ci * build a Audio Info Frame 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_cistatic void r600_hdmi_update_audio_infoframe(struct drm_encoder *encoder, 2448c2ecf20Sopenharmony_ci const void *buffer, size_t size) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 2478c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 2488c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 2498c2ecf20Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 2508c2ecf20Sopenharmony_ci uint32_t offset = dig->afmt->offset; 2518c2ecf20Sopenharmony_ci const u8 *frame = buffer + 3; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci WREG32(HDMI0_AUDIO_INFO0 + offset, 2548c2ecf20Sopenharmony_ci frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); 2558c2ecf20Sopenharmony_ci WREG32(HDMI0_AUDIO_INFO1 + offset, 2568c2ecf20Sopenharmony_ci frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x8] << 24)); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/* 2608c2ecf20Sopenharmony_ci * test if audio buffer is filled enough to start playing 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_cistatic bool r600_hdmi_is_audio_buffer_filled(struct drm_encoder *encoder) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 2658c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 2668c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 2678c2ecf20Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 2688c2ecf20Sopenharmony_ci uint32_t offset = dig->afmt->offset; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return (RREG32(HDMI0_STATUS + offset) & 0x10) != 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* 2748c2ecf20Sopenharmony_ci * have buffer status changed since last call? 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ciint r600_hdmi_buffer_status_changed(struct drm_encoder *encoder) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 2798c2ecf20Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 2808c2ecf20Sopenharmony_ci int status, result; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (!dig->afmt || !dig->afmt->enabled) 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci status = r600_hdmi_is_audio_buffer_filled(encoder); 2868c2ecf20Sopenharmony_ci result = dig->afmt->last_buffer_filled_status != status; 2878c2ecf20Sopenharmony_ci dig->afmt->last_buffer_filled_status = status; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return result; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* 2938c2ecf20Sopenharmony_ci * write the audio workaround status to the hardware 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_civoid r600_hdmi_audio_workaround(struct drm_encoder *encoder) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 2988c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 2998c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 3008c2ecf20Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 3018c2ecf20Sopenharmony_ci uint32_t offset = dig->afmt->offset; 3028c2ecf20Sopenharmony_ci bool hdmi_audio_workaround = false; /* FIXME */ 3038c2ecf20Sopenharmony_ci u32 value; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (!hdmi_audio_workaround || 3068c2ecf20Sopenharmony_ci r600_hdmi_is_audio_buffer_filled(encoder)) 3078c2ecf20Sopenharmony_ci value = 0; /* disable workaround */ 3088c2ecf20Sopenharmony_ci else 3098c2ecf20Sopenharmony_ci value = HDMI0_AUDIO_TEST_EN; /* enable workaround */ 3108c2ecf20Sopenharmony_ci WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset, 3118c2ecf20Sopenharmony_ci value, ~HDMI0_AUDIO_TEST_EN); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_civoid r600_hdmi_audio_set_dto(struct radeon_device *rdev, 3158c2ecf20Sopenharmony_ci struct radeon_crtc *crtc, unsigned int clock) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder; 3188c2ecf20Sopenharmony_ci struct radeon_encoder_atom_dig *dig; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (!crtc) 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci radeon_encoder = to_radeon_encoder(crtc->encoder); 3248c2ecf20Sopenharmony_ci dig = radeon_encoder->enc_priv; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!dig) 3278c2ecf20Sopenharmony_ci return; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (dig->dig_encoder == 0) { 3308c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO0_PHASE, 24000 * 100); 3318c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO0_MODULE, clock * 100); 3328c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO_SELECT, 0); /* select DTO0 */ 3338c2ecf20Sopenharmony_ci } else { 3348c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO1_PHASE, 24000 * 100); 3358c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO1_MODULE, clock * 100); 3368c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO_SELECT, 1); /* select DTO1 */ 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_civoid r600_set_vbi_packet(struct drm_encoder *encoder, u32 offset) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 3438c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci WREG32_OR(HDMI0_VBI_PACKET_CONTROL + offset, 3468c2ecf20Sopenharmony_ci HDMI0_NULL_SEND | /* send null packets when required */ 3478c2ecf20Sopenharmony_ci HDMI0_GC_SEND | /* send general control packets */ 3488c2ecf20Sopenharmony_ci HDMI0_GC_CONT); /* send general control packets every frame */ 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_civoid r600_set_audio_packet(struct drm_encoder *encoder, u32 offset) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 3548c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset, 3578c2ecf20Sopenharmony_ci HDMI0_AUDIO_SAMPLE_SEND | /* send audio packets */ 3588c2ecf20Sopenharmony_ci HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */ 3598c2ecf20Sopenharmony_ci HDMI0_AUDIO_PACKETS_PER_LINE(3) | /* should be suffient for all audio modes and small enough for all hblanks */ 3608c2ecf20Sopenharmony_ci HDMI0_60958_CS_UPDATE, /* allow 60958 channel status fields to be updated */ 3618c2ecf20Sopenharmony_ci ~(HDMI0_AUDIO_SAMPLE_SEND | 3628c2ecf20Sopenharmony_ci HDMI0_AUDIO_DELAY_EN_MASK | 3638c2ecf20Sopenharmony_ci HDMI0_AUDIO_PACKETS_PER_LINE_MASK | 3648c2ecf20Sopenharmony_ci HDMI0_60958_CS_UPDATE)); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset, 3678c2ecf20Sopenharmony_ci HDMI0_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */ 3688c2ecf20Sopenharmony_ci HDMI0_AUDIO_INFO_UPDATE); /* required for audio info values to be updated */ 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci WREG32_P(HDMI0_INFOFRAME_CONTROL1 + offset, 3718c2ecf20Sopenharmony_ci HDMI0_AUDIO_INFO_LINE(2), /* anything other than 0 */ 3728c2ecf20Sopenharmony_ci ~HDMI0_AUDIO_INFO_LINE_MASK); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci WREG32_AND(HDMI0_GENERIC_PACKET_CONTROL + offset, 3758c2ecf20Sopenharmony_ci ~(HDMI0_GENERIC0_SEND | 3768c2ecf20Sopenharmony_ci HDMI0_GENERIC0_CONT | 3778c2ecf20Sopenharmony_ci HDMI0_GENERIC0_UPDATE | 3788c2ecf20Sopenharmony_ci HDMI0_GENERIC1_SEND | 3798c2ecf20Sopenharmony_ci HDMI0_GENERIC1_CONT | 3808c2ecf20Sopenharmony_ci HDMI0_GENERIC0_LINE_MASK | 3818c2ecf20Sopenharmony_ci HDMI0_GENERIC1_LINE_MASK)); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci WREG32_P(HDMI0_60958_0 + offset, 3848c2ecf20Sopenharmony_ci HDMI0_60958_CS_CHANNEL_NUMBER_L(1), 3858c2ecf20Sopenharmony_ci ~(HDMI0_60958_CS_CHANNEL_NUMBER_L_MASK | 3868c2ecf20Sopenharmony_ci HDMI0_60958_CS_CLOCK_ACCURACY_MASK)); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci WREG32_P(HDMI0_60958_1 + offset, 3898c2ecf20Sopenharmony_ci HDMI0_60958_CS_CHANNEL_NUMBER_R(2), 3908c2ecf20Sopenharmony_ci ~HDMI0_60958_CS_CHANNEL_NUMBER_R_MASK); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_civoid r600_set_mute(struct drm_encoder *encoder, u32 offset, bool mute) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 3968c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (mute) 3998c2ecf20Sopenharmony_ci WREG32_OR(HDMI0_GC + offset, HDMI0_GC_AVMUTE); 4008c2ecf20Sopenharmony_ci else 4018c2ecf20Sopenharmony_ci WREG32_AND(HDMI0_GC + offset, ~HDMI0_GC_AVMUTE); 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/** 4058c2ecf20Sopenharmony_ci * r600_hdmi_update_audio_settings - Update audio infoframe 4068c2ecf20Sopenharmony_ci * 4078c2ecf20Sopenharmony_ci * @encoder: drm encoder 4088c2ecf20Sopenharmony_ci * 4098c2ecf20Sopenharmony_ci * Gets info about current audio stream and updates audio infoframe. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_civoid r600_hdmi_update_audio_settings(struct drm_encoder *encoder) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 4148c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 4158c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 4168c2ecf20Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 4178c2ecf20Sopenharmony_ci struct r600_audio_pin audio = r600_audio_status(rdev); 4188c2ecf20Sopenharmony_ci uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; 4198c2ecf20Sopenharmony_ci struct hdmi_audio_infoframe frame; 4208c2ecf20Sopenharmony_ci uint32_t offset; 4218c2ecf20Sopenharmony_ci uint32_t value; 4228c2ecf20Sopenharmony_ci ssize_t err; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (!dig->afmt || !dig->afmt->enabled) 4258c2ecf20Sopenharmony_ci return; 4268c2ecf20Sopenharmony_ci offset = dig->afmt->offset; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci DRM_DEBUG("%s with %d channels, %d Hz sampling rate, %d bits per sample,\n", 4298c2ecf20Sopenharmony_ci r600_hdmi_is_audio_buffer_filled(encoder) ? "playing" : "stopped", 4308c2ecf20Sopenharmony_ci audio.channels, audio.rate, audio.bits_per_sample); 4318c2ecf20Sopenharmony_ci DRM_DEBUG("0x%02X IEC60958 status bits and 0x%02X category code\n", 4328c2ecf20Sopenharmony_ci (int)audio.status_bits, (int)audio.category_code); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci err = hdmi_audio_infoframe_init(&frame); 4358c2ecf20Sopenharmony_ci if (err < 0) { 4368c2ecf20Sopenharmony_ci DRM_ERROR("failed to setup audio infoframe\n"); 4378c2ecf20Sopenharmony_ci return; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci frame.channels = audio.channels; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); 4438c2ecf20Sopenharmony_ci if (err < 0) { 4448c2ecf20Sopenharmony_ci DRM_ERROR("failed to pack audio infoframe\n"); 4458c2ecf20Sopenharmony_ci return; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci value = RREG32(HDMI0_AUDIO_PACKET_CONTROL + offset); 4498c2ecf20Sopenharmony_ci if (value & HDMI0_AUDIO_TEST_EN) 4508c2ecf20Sopenharmony_ci WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset, 4518c2ecf20Sopenharmony_ci value & ~HDMI0_AUDIO_TEST_EN); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci WREG32_OR(HDMI0_CONTROL + offset, 4548c2ecf20Sopenharmony_ci HDMI0_ERROR_ACK); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci WREG32_AND(HDMI0_INFOFRAME_CONTROL0 + offset, 4578c2ecf20Sopenharmony_ci ~HDMI0_AUDIO_INFO_SOURCE); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci r600_hdmi_update_audio_infoframe(encoder, buffer, sizeof(buffer)); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset, 4628c2ecf20Sopenharmony_ci HDMI0_AUDIO_INFO_CONT | 4638c2ecf20Sopenharmony_ci HDMI0_AUDIO_INFO_UPDATE); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/* 4678c2ecf20Sopenharmony_ci * enable the HDMI engine 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_civoid r600_hdmi_enable(struct drm_encoder *encoder, bool enable) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 4728c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 4738c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 4748c2ecf20Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 4758c2ecf20Sopenharmony_ci u32 hdmi = HDMI0_ERROR_ACK; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (!dig || !dig->afmt) 4788c2ecf20Sopenharmony_ci return; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* Older chipsets require setting HDMI and routing manually */ 4818c2ecf20Sopenharmony_ci if (!ASIC_IS_DCE3(rdev)) { 4828c2ecf20Sopenharmony_ci if (enable) 4838c2ecf20Sopenharmony_ci hdmi |= HDMI0_ENABLE; 4848c2ecf20Sopenharmony_ci switch (radeon_encoder->encoder_id) { 4858c2ecf20Sopenharmony_ci case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: 4868c2ecf20Sopenharmony_ci if (enable) { 4878c2ecf20Sopenharmony_ci WREG32_OR(AVIVO_TMDSA_CNTL, AVIVO_TMDSA_CNTL_HDMI_EN); 4888c2ecf20Sopenharmony_ci hdmi |= HDMI0_STREAM(HDMI0_STREAM_TMDSA); 4898c2ecf20Sopenharmony_ci } else { 4908c2ecf20Sopenharmony_ci WREG32_AND(AVIVO_TMDSA_CNTL, ~AVIVO_TMDSA_CNTL_HDMI_EN); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci case ENCODER_OBJECT_ID_INTERNAL_LVTM1: 4948c2ecf20Sopenharmony_ci if (enable) { 4958c2ecf20Sopenharmony_ci WREG32_OR(AVIVO_LVTMA_CNTL, AVIVO_LVTMA_CNTL_HDMI_EN); 4968c2ecf20Sopenharmony_ci hdmi |= HDMI0_STREAM(HDMI0_STREAM_LVTMA); 4978c2ecf20Sopenharmony_ci } else { 4988c2ecf20Sopenharmony_ci WREG32_AND(AVIVO_LVTMA_CNTL, ~AVIVO_LVTMA_CNTL_HDMI_EN); 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci case ENCODER_OBJECT_ID_INTERNAL_DDI: 5028c2ecf20Sopenharmony_ci if (enable) { 5038c2ecf20Sopenharmony_ci WREG32_OR(DDIA_CNTL, DDIA_HDMI_EN); 5048c2ecf20Sopenharmony_ci hdmi |= HDMI0_STREAM(HDMI0_STREAM_DDIA); 5058c2ecf20Sopenharmony_ci } else { 5068c2ecf20Sopenharmony_ci WREG32_AND(DDIA_CNTL, ~DDIA_HDMI_EN); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: 5108c2ecf20Sopenharmony_ci if (enable) 5118c2ecf20Sopenharmony_ci hdmi |= HDMI0_STREAM(HDMI0_STREAM_DVOA); 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci default: 5148c2ecf20Sopenharmony_ci dev_err(rdev->dev, "Invalid encoder for HDMI: 0x%X\n", 5158c2ecf20Sopenharmony_ci radeon_encoder->encoder_id); 5168c2ecf20Sopenharmony_ci break; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci WREG32(HDMI0_CONTROL + dig->afmt->offset, hdmi); 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (rdev->irq.installed) { 5228c2ecf20Sopenharmony_ci /* if irq is available use it */ 5238c2ecf20Sopenharmony_ci /* XXX: shouldn't need this on any asics. Double check DCE2/3 */ 5248c2ecf20Sopenharmony_ci if (enable) 5258c2ecf20Sopenharmony_ci radeon_irq_kms_enable_afmt(rdev, dig->afmt->id); 5268c2ecf20Sopenharmony_ci else 5278c2ecf20Sopenharmony_ci radeon_irq_kms_disable_afmt(rdev, dig->afmt->id); 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci dig->afmt->enabled = enable; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci DRM_DEBUG("%sabling HDMI interface @ 0x%04X for encoder 0x%x\n", 5338c2ecf20Sopenharmony_ci enable ? "En" : "Dis", dig->afmt->offset, radeon_encoder->encoder_id); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 536