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 * Rafał Miłecki 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci#include <linux/hdmi.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 "evergreend.h" 348c2ecf20Sopenharmony_ci#include "atom.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* enable the audio stream */ 378c2ecf20Sopenharmony_civoid dce4_audio_enable(struct radeon_device *rdev, 388c2ecf20Sopenharmony_ci struct r600_audio_pin *pin, 398c2ecf20Sopenharmony_ci u8 enable_mask) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci u32 tmp = RREG32(AZ_HOT_PLUG_CONTROL); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (!pin) 448c2ecf20Sopenharmony_ci return; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (enable_mask) { 478c2ecf20Sopenharmony_ci tmp |= AUDIO_ENABLED; 488c2ecf20Sopenharmony_ci if (enable_mask & 1) 498c2ecf20Sopenharmony_ci tmp |= PIN0_AUDIO_ENABLED; 508c2ecf20Sopenharmony_ci if (enable_mask & 2) 518c2ecf20Sopenharmony_ci tmp |= PIN1_AUDIO_ENABLED; 528c2ecf20Sopenharmony_ci if (enable_mask & 4) 538c2ecf20Sopenharmony_ci tmp |= PIN2_AUDIO_ENABLED; 548c2ecf20Sopenharmony_ci if (enable_mask & 8) 558c2ecf20Sopenharmony_ci tmp |= PIN3_AUDIO_ENABLED; 568c2ecf20Sopenharmony_ci } else { 578c2ecf20Sopenharmony_ci tmp &= ~(AUDIO_ENABLED | 588c2ecf20Sopenharmony_ci PIN0_AUDIO_ENABLED | 598c2ecf20Sopenharmony_ci PIN1_AUDIO_ENABLED | 608c2ecf20Sopenharmony_ci PIN2_AUDIO_ENABLED | 618c2ecf20Sopenharmony_ci PIN3_AUDIO_ENABLED); 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci WREG32(AZ_HOT_PLUG_CONTROL, tmp); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_civoid evergreen_hdmi_update_acr(struct drm_encoder *encoder, long offset, 688c2ecf20Sopenharmony_ci const struct radeon_hdmi_acr *acr) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 718c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 728c2ecf20Sopenharmony_ci int bpc = 8; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (encoder->crtc) { 758c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); 768c2ecf20Sopenharmony_ci bpc = radeon_crtc->bpc; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (bpc > 8) 808c2ecf20Sopenharmony_ci WREG32(HDMI_ACR_PACKET_CONTROL + offset, 818c2ecf20Sopenharmony_ci HDMI_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */ 828c2ecf20Sopenharmony_ci else 838c2ecf20Sopenharmony_ci WREG32(HDMI_ACR_PACKET_CONTROL + offset, 848c2ecf20Sopenharmony_ci HDMI_ACR_SOURCE | /* select SW CTS value */ 858c2ecf20Sopenharmony_ci HDMI_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci WREG32(HDMI_ACR_32_0 + offset, HDMI_ACR_CTS_32(acr->cts_32khz)); 888c2ecf20Sopenharmony_ci WREG32(HDMI_ACR_32_1 + offset, acr->n_32khz); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci WREG32(HDMI_ACR_44_0 + offset, HDMI_ACR_CTS_44(acr->cts_44_1khz)); 918c2ecf20Sopenharmony_ci WREG32(HDMI_ACR_44_1 + offset, acr->n_44_1khz); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci WREG32(HDMI_ACR_48_0 + offset, HDMI_ACR_CTS_48(acr->cts_48khz)); 948c2ecf20Sopenharmony_ci WREG32(HDMI_ACR_48_1 + offset, acr->n_48khz); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_civoid dce4_afmt_write_latency_fields(struct drm_encoder *encoder, 988c2ecf20Sopenharmony_ci struct drm_connector *connector, struct drm_display_mode *mode) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct radeon_device *rdev = encoder->dev->dev_private; 1018c2ecf20Sopenharmony_ci u32 tmp = 0; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 1048c2ecf20Sopenharmony_ci if (connector->latency_present[1]) 1058c2ecf20Sopenharmony_ci tmp = VIDEO_LIPSYNC(connector->video_latency[1]) | 1068c2ecf20Sopenharmony_ci AUDIO_LIPSYNC(connector->audio_latency[1]); 1078c2ecf20Sopenharmony_ci else 1088c2ecf20Sopenharmony_ci tmp = VIDEO_LIPSYNC(255) | AUDIO_LIPSYNC(255); 1098c2ecf20Sopenharmony_ci } else { 1108c2ecf20Sopenharmony_ci if (connector->latency_present[0]) 1118c2ecf20Sopenharmony_ci tmp = VIDEO_LIPSYNC(connector->video_latency[0]) | 1128c2ecf20Sopenharmony_ci AUDIO_LIPSYNC(connector->audio_latency[0]); 1138c2ecf20Sopenharmony_ci else 1148c2ecf20Sopenharmony_ci tmp = VIDEO_LIPSYNC(255) | AUDIO_LIPSYNC(255); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci WREG32_ENDPOINT(0, AZ_F0_CODEC_PIN0_CONTROL_RESPONSE_LIPSYNC, tmp); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_civoid dce4_afmt_hdmi_write_speaker_allocation(struct drm_encoder *encoder, 1208c2ecf20Sopenharmony_ci u8 *sadb, int sad_count) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct radeon_device *rdev = encoder->dev->dev_private; 1238c2ecf20Sopenharmony_ci u32 tmp; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* program the speaker allocation */ 1268c2ecf20Sopenharmony_ci tmp = RREG32_ENDPOINT(0, AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER); 1278c2ecf20Sopenharmony_ci tmp &= ~(DP_CONNECTION | SPEAKER_ALLOCATION_MASK); 1288c2ecf20Sopenharmony_ci /* set HDMI mode */ 1298c2ecf20Sopenharmony_ci tmp |= HDMI_CONNECTION; 1308c2ecf20Sopenharmony_ci if (sad_count) 1318c2ecf20Sopenharmony_ci tmp |= SPEAKER_ALLOCATION(sadb[0]); 1328c2ecf20Sopenharmony_ci else 1338c2ecf20Sopenharmony_ci tmp |= SPEAKER_ALLOCATION(5); /* stereo */ 1348c2ecf20Sopenharmony_ci WREG32_ENDPOINT(0, AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER, tmp); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_civoid dce4_afmt_dp_write_speaker_allocation(struct drm_encoder *encoder, 1388c2ecf20Sopenharmony_ci u8 *sadb, int sad_count) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct radeon_device *rdev = encoder->dev->dev_private; 1418c2ecf20Sopenharmony_ci u32 tmp; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* program the speaker allocation */ 1448c2ecf20Sopenharmony_ci tmp = RREG32_ENDPOINT(0, AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER); 1458c2ecf20Sopenharmony_ci tmp &= ~(HDMI_CONNECTION | SPEAKER_ALLOCATION_MASK); 1468c2ecf20Sopenharmony_ci /* set DP mode */ 1478c2ecf20Sopenharmony_ci tmp |= DP_CONNECTION; 1488c2ecf20Sopenharmony_ci if (sad_count) 1498c2ecf20Sopenharmony_ci tmp |= SPEAKER_ALLOCATION(sadb[0]); 1508c2ecf20Sopenharmony_ci else 1518c2ecf20Sopenharmony_ci tmp |= SPEAKER_ALLOCATION(5); /* stereo */ 1528c2ecf20Sopenharmony_ci WREG32_ENDPOINT(0, AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER, tmp); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_civoid evergreen_hdmi_write_sad_regs(struct drm_encoder *encoder, 1568c2ecf20Sopenharmony_ci struct cea_sad *sads, int sad_count) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci int i; 1598c2ecf20Sopenharmony_ci struct radeon_device *rdev = encoder->dev->dev_private; 1608c2ecf20Sopenharmony_ci static const u16 eld_reg_to_type[][2] = { 1618c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR0, HDMI_AUDIO_CODING_TYPE_PCM }, 1628c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR1, HDMI_AUDIO_CODING_TYPE_AC3 }, 1638c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR2, HDMI_AUDIO_CODING_TYPE_MPEG1 }, 1648c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR3, HDMI_AUDIO_CODING_TYPE_MP3 }, 1658c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR4, HDMI_AUDIO_CODING_TYPE_MPEG2 }, 1668c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR5, HDMI_AUDIO_CODING_TYPE_AAC_LC }, 1678c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR6, HDMI_AUDIO_CODING_TYPE_DTS }, 1688c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR7, HDMI_AUDIO_CODING_TYPE_ATRAC }, 1698c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR9, HDMI_AUDIO_CODING_TYPE_EAC3 }, 1708c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR10, HDMI_AUDIO_CODING_TYPE_DTS_HD }, 1718c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR11, HDMI_AUDIO_CODING_TYPE_MLP }, 1728c2ecf20Sopenharmony_ci { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR13, HDMI_AUDIO_CODING_TYPE_WMA_PRO }, 1738c2ecf20Sopenharmony_ci }; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) { 1768c2ecf20Sopenharmony_ci u32 value = 0; 1778c2ecf20Sopenharmony_ci u8 stereo_freqs = 0; 1788c2ecf20Sopenharmony_ci int max_channels = -1; 1798c2ecf20Sopenharmony_ci int j; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci for (j = 0; j < sad_count; j++) { 1828c2ecf20Sopenharmony_ci struct cea_sad *sad = &sads[j]; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (sad->format == eld_reg_to_type[i][1]) { 1858c2ecf20Sopenharmony_ci if (sad->channels > max_channels) { 1868c2ecf20Sopenharmony_ci value = MAX_CHANNELS(sad->channels) | 1878c2ecf20Sopenharmony_ci DESCRIPTOR_BYTE_2(sad->byte2) | 1888c2ecf20Sopenharmony_ci SUPPORTED_FREQUENCIES(sad->freq); 1898c2ecf20Sopenharmony_ci max_channels = sad->channels; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM) 1938c2ecf20Sopenharmony_ci stereo_freqs |= sad->freq; 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci value |= SUPPORTED_FREQUENCIES_STEREO(stereo_freqs); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci WREG32_ENDPOINT(0, eld_reg_to_type[i][0], value); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* 2068c2ecf20Sopenharmony_ci * build a AVI Info Frame 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_civoid evergreen_set_avi_packet(struct radeon_device *rdev, u32 offset, 2098c2ecf20Sopenharmony_ci unsigned char *buffer, size_t size) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci uint8_t *frame = buffer + 3; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci WREG32(AFMT_AVI_INFO0 + offset, 2148c2ecf20Sopenharmony_ci frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); 2158c2ecf20Sopenharmony_ci WREG32(AFMT_AVI_INFO1 + offset, 2168c2ecf20Sopenharmony_ci frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x7] << 24)); 2178c2ecf20Sopenharmony_ci WREG32(AFMT_AVI_INFO2 + offset, 2188c2ecf20Sopenharmony_ci frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24)); 2198c2ecf20Sopenharmony_ci WREG32(AFMT_AVI_INFO3 + offset, 2208c2ecf20Sopenharmony_ci frame[0xC] | (frame[0xD] << 8) | (buffer[1] << 24)); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci WREG32_P(HDMI_INFOFRAME_CONTROL1 + offset, 2238c2ecf20Sopenharmony_ci HDMI_AVI_INFO_LINE(2), /* anything other than 0 */ 2248c2ecf20Sopenharmony_ci ~HDMI_AVI_INFO_LINE_MASK); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_civoid dce4_hdmi_audio_set_dto(struct radeon_device *rdev, 2288c2ecf20Sopenharmony_ci struct radeon_crtc *crtc, unsigned int clock) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci unsigned int max_ratio = clock / 24000; 2318c2ecf20Sopenharmony_ci u32 dto_phase; 2328c2ecf20Sopenharmony_ci u32 wallclock_ratio; 2338c2ecf20Sopenharmony_ci u32 value; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (max_ratio >= 8) { 2368c2ecf20Sopenharmony_ci dto_phase = 192 * 1000; 2378c2ecf20Sopenharmony_ci wallclock_ratio = 3; 2388c2ecf20Sopenharmony_ci } else if (max_ratio >= 4) { 2398c2ecf20Sopenharmony_ci dto_phase = 96 * 1000; 2408c2ecf20Sopenharmony_ci wallclock_ratio = 2; 2418c2ecf20Sopenharmony_ci } else if (max_ratio >= 2) { 2428c2ecf20Sopenharmony_ci dto_phase = 48 * 1000; 2438c2ecf20Sopenharmony_ci wallclock_ratio = 1; 2448c2ecf20Sopenharmony_ci } else { 2458c2ecf20Sopenharmony_ci dto_phase = 24 * 1000; 2468c2ecf20Sopenharmony_ci wallclock_ratio = 0; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci value = RREG32(DCCG_AUDIO_DTO0_CNTL) & ~DCCG_AUDIO_DTO_WALLCLOCK_RATIO_MASK; 2508c2ecf20Sopenharmony_ci value |= DCCG_AUDIO_DTO_WALLCLOCK_RATIO(wallclock_ratio); 2518c2ecf20Sopenharmony_ci value &= ~DCCG_AUDIO_DTO1_USE_512FBR_DTO; 2528c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO0_CNTL, value); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Two dtos; generally use dto0 for HDMI */ 2558c2ecf20Sopenharmony_ci value = 0; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (crtc) 2588c2ecf20Sopenharmony_ci value |= DCCG_AUDIO_DTO0_SOURCE_SEL(crtc->crtc_id); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO_SOURCE, value); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Express [24MHz / target pixel clock] as an exact rational 2638c2ecf20Sopenharmony_ci * number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE 2648c2ecf20Sopenharmony_ci * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO0_PHASE, dto_phase); 2678c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO0_MODULE, clock); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_civoid dce4_dp_audio_set_dto(struct radeon_device *rdev, 2718c2ecf20Sopenharmony_ci struct radeon_crtc *crtc, unsigned int clock) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci u32 value; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci value = RREG32(DCCG_AUDIO_DTO1_CNTL) & ~DCCG_AUDIO_DTO_WALLCLOCK_RATIO_MASK; 2768c2ecf20Sopenharmony_ci value |= DCCG_AUDIO_DTO1_USE_512FBR_DTO; 2778c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO1_CNTL, value); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* Two dtos; generally use dto1 for DP */ 2808c2ecf20Sopenharmony_ci value = 0; 2818c2ecf20Sopenharmony_ci value |= DCCG_AUDIO_DTO_SEL; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (crtc) 2848c2ecf20Sopenharmony_ci value |= DCCG_AUDIO_DTO0_SOURCE_SEL(crtc->crtc_id); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO_SOURCE, value); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Express [24MHz / target pixel clock] as an exact rational 2898c2ecf20Sopenharmony_ci * number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE 2908c2ecf20Sopenharmony_ci * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci if (ASIC_IS_DCE41(rdev)) { 2938c2ecf20Sopenharmony_ci unsigned int div = (RREG32(DCE41_DENTIST_DISPCLK_CNTL) & 2948c2ecf20Sopenharmony_ci DENTIST_DPREFCLK_WDIVIDER_MASK) >> 2958c2ecf20Sopenharmony_ci DENTIST_DPREFCLK_WDIVIDER_SHIFT; 2968c2ecf20Sopenharmony_ci div = radeon_audio_decode_dfs_div(div); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (div) 2998c2ecf20Sopenharmony_ci clock = 100 * clock / div; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO1_PHASE, 24000); 3038c2ecf20Sopenharmony_ci WREG32(DCCG_AUDIO_DTO1_MODULE, clock); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_civoid dce4_set_vbi_packet(struct drm_encoder *encoder, u32 offset) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 3098c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci WREG32(HDMI_VBI_PACKET_CONTROL + offset, 3128c2ecf20Sopenharmony_ci HDMI_NULL_SEND | /* send null packets when required */ 3138c2ecf20Sopenharmony_ci HDMI_GC_SEND | /* send general control packets */ 3148c2ecf20Sopenharmony_ci HDMI_GC_CONT); /* send general control packets every frame */ 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_civoid dce4_hdmi_set_color_depth(struct drm_encoder *encoder, u32 offset, int bpc) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 3208c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 3218c2ecf20Sopenharmony_ci struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); 3228c2ecf20Sopenharmony_ci uint32_t val; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci val = RREG32(HDMI_CONTROL + offset); 3258c2ecf20Sopenharmony_ci val &= ~HDMI_DEEP_COLOR_ENABLE; 3268c2ecf20Sopenharmony_ci val &= ~HDMI_DEEP_COLOR_DEPTH_MASK; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci switch (bpc) { 3298c2ecf20Sopenharmony_ci case 0: 3308c2ecf20Sopenharmony_ci case 6: 3318c2ecf20Sopenharmony_ci case 8: 3328c2ecf20Sopenharmony_ci case 16: 3338c2ecf20Sopenharmony_ci default: 3348c2ecf20Sopenharmony_ci DRM_DEBUG("%s: Disabling hdmi deep color for %d bpc.\n", 3358c2ecf20Sopenharmony_ci connector->name, bpc); 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci case 10: 3388c2ecf20Sopenharmony_ci val |= HDMI_DEEP_COLOR_ENABLE; 3398c2ecf20Sopenharmony_ci val |= HDMI_DEEP_COLOR_DEPTH(HDMI_30BIT_DEEP_COLOR); 3408c2ecf20Sopenharmony_ci DRM_DEBUG("%s: Enabling hdmi deep color 30 for 10 bpc.\n", 3418c2ecf20Sopenharmony_ci connector->name); 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci case 12: 3448c2ecf20Sopenharmony_ci val |= HDMI_DEEP_COLOR_ENABLE; 3458c2ecf20Sopenharmony_ci val |= HDMI_DEEP_COLOR_DEPTH(HDMI_36BIT_DEEP_COLOR); 3468c2ecf20Sopenharmony_ci DRM_DEBUG("%s: Enabling hdmi deep color 36 for 12 bpc.\n", 3478c2ecf20Sopenharmony_ci connector->name); 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci WREG32(HDMI_CONTROL + offset, val); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_civoid dce4_set_audio_packet(struct drm_encoder *encoder, u32 offset) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 3578c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci WREG32(AFMT_INFOFRAME_CONTROL0 + offset, 3608c2ecf20Sopenharmony_ci AFMT_AUDIO_INFO_UPDATE); /* required for audio info values to be updated */ 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci WREG32(AFMT_60958_0 + offset, 3638c2ecf20Sopenharmony_ci AFMT_60958_CS_CHANNEL_NUMBER_L(1)); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci WREG32(AFMT_60958_1 + offset, 3668c2ecf20Sopenharmony_ci AFMT_60958_CS_CHANNEL_NUMBER_R(2)); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci WREG32(AFMT_60958_2 + offset, 3698c2ecf20Sopenharmony_ci AFMT_60958_CS_CHANNEL_NUMBER_2(3) | 3708c2ecf20Sopenharmony_ci AFMT_60958_CS_CHANNEL_NUMBER_3(4) | 3718c2ecf20Sopenharmony_ci AFMT_60958_CS_CHANNEL_NUMBER_4(5) | 3728c2ecf20Sopenharmony_ci AFMT_60958_CS_CHANNEL_NUMBER_5(6) | 3738c2ecf20Sopenharmony_ci AFMT_60958_CS_CHANNEL_NUMBER_6(7) | 3748c2ecf20Sopenharmony_ci AFMT_60958_CS_CHANNEL_NUMBER_7(8)); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci WREG32(AFMT_AUDIO_PACKET_CONTROL2 + offset, 3778c2ecf20Sopenharmony_ci AFMT_AUDIO_CHANNEL_ENABLE(0xff)); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci WREG32(HDMI_AUDIO_PACKET_CONTROL + offset, 3808c2ecf20Sopenharmony_ci HDMI_AUDIO_DELAY_EN(1) | /* set the default audio delay */ 3818c2ecf20Sopenharmony_ci HDMI_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */ 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* allow 60958 channel status and send audio packets fields to be updated */ 3848c2ecf20Sopenharmony_ci WREG32_OR(AFMT_AUDIO_PACKET_CONTROL + offset, 3858c2ecf20Sopenharmony_ci AFMT_RESET_FIFO_WHEN_AUDIO_DIS | AFMT_60958_CS_UPDATE); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_civoid dce4_set_mute(struct drm_encoder *encoder, u32 offset, bool mute) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 3928c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (mute) 3958c2ecf20Sopenharmony_ci WREG32_OR(HDMI_GC + offset, HDMI_GC_AVMUTE); 3968c2ecf20Sopenharmony_ci else 3978c2ecf20Sopenharmony_ci WREG32_AND(HDMI_GC + offset, ~HDMI_GC_AVMUTE); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_civoid evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 4038c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 4048c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 4058c2ecf20Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (!dig || !dig->afmt) 4088c2ecf20Sopenharmony_ci return; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (enable) { 4118c2ecf20Sopenharmony_ci struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (connector && drm_detect_monitor_audio(radeon_connector_edid(connector))) { 4148c2ecf20Sopenharmony_ci WREG32(HDMI_INFOFRAME_CONTROL0 + dig->afmt->offset, 4158c2ecf20Sopenharmony_ci HDMI_AVI_INFO_SEND | /* enable AVI info frames */ 4168c2ecf20Sopenharmony_ci HDMI_AVI_INFO_CONT | /* required for audio info values to be updated */ 4178c2ecf20Sopenharmony_ci HDMI_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */ 4188c2ecf20Sopenharmony_ci HDMI_AUDIO_INFO_CONT); /* required for audio info values to be updated */ 4198c2ecf20Sopenharmony_ci WREG32_OR(AFMT_AUDIO_PACKET_CONTROL + dig->afmt->offset, 4208c2ecf20Sopenharmony_ci AFMT_AUDIO_SAMPLE_SEND); 4218c2ecf20Sopenharmony_ci } else { 4228c2ecf20Sopenharmony_ci WREG32(HDMI_INFOFRAME_CONTROL0 + dig->afmt->offset, 4238c2ecf20Sopenharmony_ci HDMI_AVI_INFO_SEND | /* enable AVI info frames */ 4248c2ecf20Sopenharmony_ci HDMI_AVI_INFO_CONT); /* required for audio info values to be updated */ 4258c2ecf20Sopenharmony_ci WREG32_AND(AFMT_AUDIO_PACKET_CONTROL + dig->afmt->offset, 4268c2ecf20Sopenharmony_ci ~AFMT_AUDIO_SAMPLE_SEND); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci } else { 4298c2ecf20Sopenharmony_ci WREG32_AND(AFMT_AUDIO_PACKET_CONTROL + dig->afmt->offset, 4308c2ecf20Sopenharmony_ci ~AFMT_AUDIO_SAMPLE_SEND); 4318c2ecf20Sopenharmony_ci WREG32(HDMI_INFOFRAME_CONTROL0 + dig->afmt->offset, 0); 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci dig->afmt->enabled = enable; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci DRM_DEBUG("%sabling HDMI interface @ 0x%04X for encoder 0x%x\n", 4378c2ecf20Sopenharmony_ci enable ? "En" : "Dis", dig->afmt->offset, radeon_encoder->encoder_id); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_civoid evergreen_dp_enable(struct drm_encoder *encoder, bool enable) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 4438c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 4448c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 4458c2ecf20Sopenharmony_ci struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; 4468c2ecf20Sopenharmony_ci struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (!dig || !dig->afmt) 4498c2ecf20Sopenharmony_ci return; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (enable && connector && 4528c2ecf20Sopenharmony_ci drm_detect_monitor_audio(radeon_connector_edid(connector))) { 4538c2ecf20Sopenharmony_ci struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); 4548c2ecf20Sopenharmony_ci struct radeon_connector *radeon_connector = to_radeon_connector(connector); 4558c2ecf20Sopenharmony_ci struct radeon_connector_atom_dig *dig_connector; 4568c2ecf20Sopenharmony_ci uint32_t val; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci WREG32_OR(AFMT_AUDIO_PACKET_CONTROL + dig->afmt->offset, 4598c2ecf20Sopenharmony_ci AFMT_AUDIO_SAMPLE_SEND); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DP_SEC_TIMESTAMP + dig->afmt->offset, 4628c2ecf20Sopenharmony_ci EVERGREEN_DP_SEC_TIMESTAMP_MODE(1)); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (!ASIC_IS_DCE6(rdev) && radeon_connector->con_priv) { 4658c2ecf20Sopenharmony_ci dig_connector = radeon_connector->con_priv; 4668c2ecf20Sopenharmony_ci val = RREG32(EVERGREEN_DP_SEC_AUD_N + dig->afmt->offset); 4678c2ecf20Sopenharmony_ci val &= ~EVERGREEN_DP_SEC_N_BASE_MULTIPLE(0xf); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (dig_connector->dp_clock == 162000) 4708c2ecf20Sopenharmony_ci val |= EVERGREEN_DP_SEC_N_BASE_MULTIPLE(3); 4718c2ecf20Sopenharmony_ci else 4728c2ecf20Sopenharmony_ci val |= EVERGREEN_DP_SEC_N_BASE_MULTIPLE(5); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DP_SEC_AUD_N + dig->afmt->offset, val); 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset, 4788c2ecf20Sopenharmony_ci EVERGREEN_DP_SEC_ASP_ENABLE | /* Audio packet transmission */ 4798c2ecf20Sopenharmony_ci EVERGREEN_DP_SEC_ATP_ENABLE | /* Audio timestamp packet transmission */ 4808c2ecf20Sopenharmony_ci EVERGREEN_DP_SEC_AIP_ENABLE | /* Audio infoframe packet transmission */ 4818c2ecf20Sopenharmony_ci EVERGREEN_DP_SEC_STREAM_ENABLE); /* Master enable for secondary stream engine */ 4828c2ecf20Sopenharmony_ci } else { 4838c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset, 0); 4848c2ecf20Sopenharmony_ci WREG32_AND(AFMT_AUDIO_PACKET_CONTROL + dig->afmt->offset, 4858c2ecf20Sopenharmony_ci ~AFMT_AUDIO_SAMPLE_SEND); 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci dig->afmt->enabled = enable; 4898c2ecf20Sopenharmony_ci} 490