diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/dce')
16 files changed, 9719 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/dce/Makefile b/drivers/gpu/drm/amd/display/dc/dce/Makefile new file mode 100644 index 000000000000..bfca38170329 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for common 'dce' logic +# HW object file under this folder follow similar pattern for HW programming +# - register offset and/or shift + mask stored in the dec_hw struct +# - register programming through common macros that look up register +# offset/shift/mask stored in dce_hw struct + +DCE = dce_audio.o dce_stream_encoder.o dce_link_encoder.o dce_hwseq.o \ +dce_mem_input.o dce_clock_source.o dce_scl_filters.o dce_transform.o + + +AMD_DAL_DCE = $(addprefix $(AMDDALPATH)/dc/dce/,$(DCE)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c new file mode 100644 index 000000000000..dc44053e8575 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c @@ -0,0 +1,920 @@ +/* + * Copyright 2012-15 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. + * + * Authors: AMD + * + */ + +#include "reg_helper.h" +#include "dce_audio.h" +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#define DCE_AUD(audio)\ + container_of(audio, struct dce_audio, base) + +#define CTX \ + aud->base.ctx +#define REG(reg)\ + (aud->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + aud->shifts->field_name, aud->masks->field_name + +#define IX_REG(reg)\ + ix ## reg + +#define AZ_REG_READ(reg_name) \ + read_indirect_azalia_reg(audio, IX_REG(reg_name)) + +#define AZ_REG_WRITE(reg_name, value) \ + write_indirect_azalia_reg(audio, IX_REG(reg_name), value) + +static void write_indirect_azalia_reg(struct audio *audio, + uint32_t reg_index, + uint32_t reg_data) +{ + struct dce_audio *aud = DCE_AUD(audio); + + /* AZALIA_F0_CODEC_ENDPOINT_INDEX endpoint index */ + REG_SET(AZALIA_F0_CODEC_ENDPOINT_INDEX, 0, + AZALIA_ENDPOINT_REG_INDEX, reg_index); + + /* AZALIA_F0_CODEC_ENDPOINT_DATA endpoint data */ + REG_SET(AZALIA_F0_CODEC_ENDPOINT_DATA, 0, + AZALIA_ENDPOINT_REG_DATA, reg_data); + + dm_logger_write(CTX->logger, LOG_HW_AUDIO, + "AUDIO:write_indirect_azalia_reg: index: %u data: %u\n", + reg_index, reg_data); +} + +static uint32_t read_indirect_azalia_reg(struct audio *audio, uint32_t reg_index) +{ + struct dce_audio *aud = DCE_AUD(audio); + + uint32_t value = 0; + + /* AZALIA_F0_CODEC_ENDPOINT_INDEX endpoint index */ + REG_SET(AZALIA_F0_CODEC_ENDPOINT_INDEX, 0, + AZALIA_ENDPOINT_REG_INDEX, reg_index); + + /* AZALIA_F0_CODEC_ENDPOINT_DATA endpoint data */ + value = REG_READ(AZALIA_F0_CODEC_ENDPOINT_DATA); + + dm_logger_write(CTX->logger, LOG_HW_AUDIO, + "AUDIO:read_indirect_azalia_reg: index: %u data: %u\n", + reg_index, value); + + return value; +} + +static bool is_audio_format_supported( + const struct audio_info *audio_info, + enum audio_format_code audio_format_code, + uint32_t *format_index) +{ + uint32_t index; + uint32_t max_channe_index = 0; + bool found = false; + + if (audio_info == NULL) + return found; + + /* pass through whole array */ + for (index = 0; index < audio_info->mode_count; index++) { + if (audio_info->modes[index].format_code == audio_format_code) { + if (found) { + /* format has multiply entries, choose one with + * highst number of channels */ + if (audio_info->modes[index].channel_count > + audio_info->modes[max_channe_index].channel_count) { + max_channe_index = index; + } + } else { + /* format found, save it's index */ + found = true; + max_channe_index = index; + } + } + } + + /* return index */ + if (found && format_index != NULL) + *format_index = max_channe_index; + + return found; +} + +/*For HDMI, calculate if specified sample rates can fit into a given timing */ +static void check_audio_bandwidth_hdmi( + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates) +{ + uint32_t samples; + uint32_t h_blank; + bool limit_freq_to_48_khz = false; + bool limit_freq_to_88_2_khz = false; + bool limit_freq_to_96_khz = false; + bool limit_freq_to_174_4_khz = false; + + /* For two channels supported return whatever sink support,unmodified*/ + if (channel_count > 2) { + + /* Based on HDMI spec 1.3 Table 7.5 */ + if ((crtc_info->requested_pixel_clock <= 27000) && + (crtc_info->v_active <= 576) && + !(crtc_info->interlaced) && + !(crtc_info->pixel_repetition == 2 || + crtc_info->pixel_repetition == 4)) { + limit_freq_to_48_khz = true; + + } else if ((crtc_info->requested_pixel_clock <= 27000) && + (crtc_info->v_active <= 576) && + (crtc_info->interlaced) && + (crtc_info->pixel_repetition == 2)) { + limit_freq_to_88_2_khz = true; + + } else if ((crtc_info->requested_pixel_clock <= 54000) && + (crtc_info->v_active <= 576) && + !(crtc_info->interlaced)) { + limit_freq_to_174_4_khz = true; + } + } + + /* Also do some calculation for the available Audio Bandwidth for the + * 8 ch (i.e. for the Layout 1 => ch > 2) + */ + h_blank = crtc_info->h_total - crtc_info->h_active; + + if (crtc_info->pixel_repetition) + h_blank *= crtc_info->pixel_repetition; + + /*based on HDMI spec 1.3 Table 7.5 */ + h_blank -= 58; + /*for Control Period */ + h_blank -= 16; + + samples = h_blank * 10; + /* Number of Audio Packets (multiplied by 10) per Line (for 8 ch number + * of Audio samples per line multiplied by 10 - Layout 1) + */ + samples /= 32; + samples *= crtc_info->v_active; + /*Number of samples multiplied by 10, per second */ + samples *= crtc_info->refresh_rate; + /*Number of Audio samples per second */ + samples /= 10; + + /* @todo do it after deep color is implemented + * 8xx - deep color bandwidth scaling + * Extra bandwidth is avaliable in deep color b/c link runs faster than + * pixel rate. This has the effect of allowing more tmds characters to + * be transmitted during blank + */ + + switch (crtc_info->color_depth) { + case COLOR_DEPTH_888: + samples *= 4; + break; + case COLOR_DEPTH_101010: + samples *= 5; + break; + case COLOR_DEPTH_121212: + samples *= 6; + break; + default: + samples *= 4; + break; + } + + samples /= 4; + + /*check limitation*/ + if (samples < 88200) + limit_freq_to_48_khz = true; + else if (samples < 96000) + limit_freq_to_88_2_khz = true; + else if (samples < 176400) + limit_freq_to_96_khz = true; + else if (samples < 192000) + limit_freq_to_174_4_khz = true; + + if (sample_rates != NULL) { + /* limit frequencies */ + if (limit_freq_to_174_4_khz) + sample_rates->rate.RATE_192 = 0; + + if (limit_freq_to_96_khz) { + sample_rates->rate.RATE_192 = 0; + sample_rates->rate.RATE_176_4 = 0; + } + if (limit_freq_to_88_2_khz) { + sample_rates->rate.RATE_192 = 0; + sample_rates->rate.RATE_176_4 = 0; + sample_rates->rate.RATE_96 = 0; + } + if (limit_freq_to_48_khz) { + sample_rates->rate.RATE_192 = 0; + sample_rates->rate.RATE_176_4 = 0; + sample_rates->rate.RATE_96 = 0; + sample_rates->rate.RATE_88_2 = 0; + } + } +} + +/*For DP SST, calculate if specified sample rates can fit into a given timing */ +static void check_audio_bandwidth_dpsst( + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates) +{ + /* do nothing */ +} + +/*For DP MST, calculate if specified sample rates can fit into a given timing */ +static void check_audio_bandwidth_dpmst( + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates) +{ + /* do nothing */ +} + +static void check_audio_bandwidth( + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + enum signal_type signal, + union audio_sample_rates *sample_rates) +{ + switch (signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + check_audio_bandwidth_hdmi( + crtc_info, channel_count, sample_rates); + break; + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT: + check_audio_bandwidth_dpsst( + crtc_info, channel_count, sample_rates); + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + check_audio_bandwidth_dpmst( + crtc_info, channel_count, sample_rates); + break; + default: + break; + } +} + +/* expose/not expose HBR capability to Audio driver */ +static void set_high_bit_rate_capable( + struct audio *audio, + bool capable) +{ + uint32_t value = 0; + + /* set high bit rate audio capable*/ + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR); + + set_reg_field_value(value, capable, + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR, + HBR_CAPABLE); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR, value); +} + +/* set video latency in in ms/2+1 */ +static void set_video_latency( + struct audio *audio, + int latency_in_ms) +{ + uint32_t value = 0; + + if ((latency_in_ms < 0) || (latency_in_ms > 255)) + return; + + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC); + + set_reg_field_value(value, latency_in_ms, + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + VIDEO_LIPSYNC); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + value); +} + +/* set audio latency in in ms/2+1 */ +static void set_audio_latency( + struct audio *audio, + int latency_in_ms) +{ + uint32_t value = 0; + + if (latency_in_ms < 0) + latency_in_ms = 0; + + if (latency_in_ms > 255) + latency_in_ms = 255; + + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC); + + set_reg_field_value(value, latency_in_ms, + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + AUDIO_LIPSYNC); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + value); +} + +void dce_aud_az_enable(struct audio *audio) +{ + uint32_t value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + + if (get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + AUDIO_ENABLED) != 1) + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + AUDIO_ENABLED); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); +} + +void dce_aud_az_disable(struct audio *audio) +{ + uint32_t value; + + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + + set_reg_field_value(value, 0, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + AUDIO_ENABLED); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); +} + +void dce_aud_az_configure( + struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_info *audio_info) +{ + struct dce_audio *aud = DCE_AUD(audio); + + uint32_t speakers = audio_info->flags.info.ALLSPEAKERS; + uint32_t value; + uint32_t field = 0; + enum audio_format_code audio_format_code; + uint32_t format_index; + uint32_t index; + bool is_ac3_supported = false; + union audio_sample_rates sample_rate; + uint32_t strlen = 0; + + /* Speaker Allocation */ + /* + uint32_t value; + uint32_t field = 0;*/ + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER); + + set_reg_field_value(value, + speakers, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + SPEAKER_ALLOCATION); + + /* LFE_PLAYBACK_LEVEL = LFEPBL + * LFEPBL = 0 : Unknown or refer to other information + * LFEPBL = 1 : 0dB playback + * LFEPBL = 2 : +10dB playback + * LFE_BL = 3 : Reserved + */ + set_reg_field_value(value, + 0, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + LFE_PLAYBACK_LEVEL); + /* todo: according to reg spec LFE_PLAYBACK_LEVEL is read only. + * why are we writing to it? DCE8 does not write this */ + + + set_reg_field_value(value, + 0, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + HDMI_CONNECTION); + + set_reg_field_value(value, + 0, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + DP_CONNECTION); + + field = get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + EXTRA_CONNECTION_INFO); + + field &= ~0x1; + + set_reg_field_value(value, + field, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + EXTRA_CONNECTION_INFO); + + /* set audio for output signal */ + switch (signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + set_reg_field_value(value, + 1, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + HDMI_CONNECTION); + + break; + + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + set_reg_field_value(value, + 1, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + DP_CONNECTION); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, value); + + /* Audio Descriptors */ + /* pass through all formats */ + for (format_index = 0; format_index < AUDIO_FORMAT_CODE_COUNT; + format_index++) { + audio_format_code = + (AUDIO_FORMAT_CODE_FIRST + format_index); + + /* those are unsupported, skip programming */ + if (audio_format_code == AUDIO_FORMAT_CODE_1BITAUDIO || + audio_format_code == AUDIO_FORMAT_CODE_DST) + continue; + + value = 0; + + /* check if supported */ + if (is_audio_format_supported( + audio_info, audio_format_code, &index)) { + const struct audio_mode *audio_mode = + &audio_info->modes[index]; + union audio_sample_rates sample_rates = + audio_mode->sample_rates; + uint8_t byte2 = audio_mode->max_bit_rate; + + /* adjust specific properties */ + switch (audio_format_code) { + case AUDIO_FORMAT_CODE_LINEARPCM: { + check_audio_bandwidth( + crtc_info, + audio_mode->channel_count, + signal, + &sample_rates); + + byte2 = audio_mode->sample_size; + + set_reg_field_value(value, + sample_rates.all, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + SUPPORTED_FREQUENCIES_STEREO); + } + break; + case AUDIO_FORMAT_CODE_AC3: + is_ac3_supported = true; + break; + case AUDIO_FORMAT_CODE_DOLBYDIGITALPLUS: + case AUDIO_FORMAT_CODE_DTS_HD: + case AUDIO_FORMAT_CODE_MAT_MLP: + case AUDIO_FORMAT_CODE_DST: + case AUDIO_FORMAT_CODE_WMAPRO: + byte2 = audio_mode->vendor_specific; + break; + default: + break; + } + + /* fill audio format data */ + set_reg_field_value(value, + audio_mode->channel_count - 1, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + MAX_CHANNELS); + + set_reg_field_value(value, + sample_rates.all, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + SUPPORTED_FREQUENCIES); + + set_reg_field_value(value, + byte2, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + DESCRIPTOR_BYTE_2); + } /* if */ + + AZ_REG_WRITE( + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0 + format_index, + value); + } /* for */ + + if (is_ac3_supported) + /* todo: this reg global. why program global register? */ + REG_WRITE(AZALIA_F0_CODEC_FUNCTION_PARAMETER_STREAM_FORMATS, + 0x05); + + /* check for 192khz/8-Ch support for HBR requirements */ + sample_rate.all = 0; + sample_rate.rate.RATE_192 = 1; + + check_audio_bandwidth( + crtc_info, + 8, + signal, + &sample_rate); + + set_high_bit_rate_capable(audio, sample_rate.rate.RATE_192); + + /* Audio and Video Lipsync */ + set_video_latency(audio, audio_info->video_latency); + set_audio_latency(audio, audio_info->audio_latency); + + value = 0; + set_reg_field_value(value, audio_info->manufacture_id, + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, + MANUFACTURER_ID); + + set_reg_field_value(value, audio_info->product_id, + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, + PRODUCT_ID); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, + value); + + value = 0; + + /*get display name string length */ + while (audio_info->display_name[strlen++] != '\0') { + if (strlen >= + MAX_HW_AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS) + break; + } + set_reg_field_value(value, strlen, + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO1, + SINK_DESCRIPTION_LEN); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO1, + value); + + /* + *write the port ID: + *PORT_ID0 = display index + *PORT_ID1 = 16bit BDF + *(format MSB->LSB: 8bit Bus, 5bit Device, 3bit Function) + */ + + value = 0; + + set_reg_field_value(value, audio_info->port_id[0], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO2, + PORT_ID0); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO2, value); + + value = 0; + set_reg_field_value(value, audio_info->port_id[1], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO3, + PORT_ID1); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO3, value); + + /*write the 18 char monitor string */ + + value = 0; + set_reg_field_value(value, audio_info->display_name[0], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION0); + + set_reg_field_value(value, audio_info->display_name[1], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION1); + + set_reg_field_value(value, audio_info->display_name[2], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION2); + + set_reg_field_value(value, audio_info->display_name[3], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION3); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, value); + + value = 0; + set_reg_field_value(value, audio_info->display_name[4], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION4); + + set_reg_field_value(value, audio_info->display_name[5], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION5); + + set_reg_field_value(value, audio_info->display_name[6], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION6); + + set_reg_field_value(value, audio_info->display_name[7], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION7); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, value); + + value = 0; + set_reg_field_value(value, audio_info->display_name[8], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION8); + + set_reg_field_value(value, audio_info->display_name[9], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION9); + + set_reg_field_value(value, audio_info->display_name[10], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION10); + + set_reg_field_value(value, audio_info->display_name[11], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION11); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, value); + + value = 0; + set_reg_field_value(value, audio_info->display_name[12], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION12); + + set_reg_field_value(value, audio_info->display_name[13], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION13); + + set_reg_field_value(value, audio_info->display_name[14], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION14); + + set_reg_field_value(value, audio_info->display_name[15], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION15); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, value); + + value = 0; + set_reg_field_value(value, audio_info->display_name[16], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, + DESCRIPTION16); + + set_reg_field_value(value, audio_info->display_name[17], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, + DESCRIPTION17); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, value); +} + +/* +* todo: wall clk related functionality probably belong to clock_src. +*/ + +/* search pixel clock value for Azalia HDMI Audio */ +static bool get_azalia_clock_info_hdmi( + uint32_t crtc_pixel_clock_in_khz, + uint32_t actual_pixel_clock_in_khz, + struct azalia_clock_info *azalia_clock_info) +{ + if (azalia_clock_info == NULL) + return false; + + /* audio_dto_phase= 24 * 10,000; + * 24MHz in [100Hz] units */ + azalia_clock_info->audio_dto_phase = + 24 * 10000; + + /* audio_dto_module = PCLKFrequency * 10,000; + * [khz] -> [100Hz] */ + azalia_clock_info->audio_dto_module = + actual_pixel_clock_in_khz * 10; + + return true; +} + +static bool get_azalia_clock_info_dp( + uint32_t requested_pixel_clock_in_khz, + const struct audio_pll_info *pll_info, + struct azalia_clock_info *azalia_clock_info) +{ + if (pll_info == NULL || azalia_clock_info == NULL) + return false; + + /* Reported dpDtoSourceClockInkhz value for + * DCE8 already adjusted for SS, do not need any + * adjustment here anymore + */ + + /*audio_dto_phase = 24 * 10,000; + * 24MHz in [100Hz] units */ + azalia_clock_info->audio_dto_phase = 24 * 10000; + + /*audio_dto_module = dpDtoSourceClockInkhz * 10,000; + * [khz] ->[100Hz] */ + azalia_clock_info->audio_dto_module = + pll_info->dp_dto_source_clock_in_khz * 10; + + return true; +} + +void dce_aud_wall_dto_setup( + struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info) +{ + struct dce_audio *aud = DCE_AUD(audio); + + struct azalia_clock_info clock_info = { 0 }; + + if (dc_is_hdmi_signal(signal)) { + uint32_t src_sel; + + /*DTO0 Programming goal: + -generate 24MHz, 128*Fs from 24MHz + -use DTO0 when an active HDMI port is connected + (optionally a DP is connected) */ + + /* calculate DTO settings */ + get_azalia_clock_info_hdmi( + crtc_info->requested_pixel_clock, + crtc_info->calculated_pixel_clock, + &clock_info); + + /* On TN/SI, Program DTO source select and DTO select before + programming DTO modulo and DTO phase. These bits must be + programmed first, otherwise there will be no HDMI audio at boot + up. This is a HW sequence change (different from old ASICs). + Caution when changing this programming sequence. + + HDMI enabled, using DTO0 + program master CRTC for DTO0 */ + src_sel = pll_info->dto_source - DTO_SOURCE_ID0; + REG_UPDATE_2(DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO0_SOURCE_SEL, src_sel, + DCCG_AUDIO_DTO_SEL, 0); + + /* module */ + REG_UPDATE(DCCG_AUDIO_DTO0_MODULE, + DCCG_AUDIO_DTO0_MODULE, clock_info.audio_dto_module); + + /* phase */ + REG_UPDATE(DCCG_AUDIO_DTO0_PHASE, + DCCG_AUDIO_DTO0_PHASE, clock_info.audio_dto_phase); + } else { + /*DTO1 Programming goal: + -generate 24MHz, 512*Fs, 128*Fs from 24MHz + -default is to used DTO1, and switch to DTO0 when an audio + master HDMI port is connected + -use as default for DP + + calculate DTO settings */ + get_azalia_clock_info_dp( + crtc_info->requested_pixel_clock, + pll_info, + &clock_info); + + /* Program DTO select before programming DTO modulo and DTO + phase. default to use DTO1 */ + + REG_UPDATE(DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO_SEL, 1); + + REG_UPDATE(DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO_SEL, 1); + /* DCCG_AUDIO_DTO2_USE_512FBR_DTO, 1) + * Select 512fs for DP TODO: web register definition + * does not match register header file + * DCE11 version it's commented out while DCE8 it's set to 1 + */ + + /* module */ + REG_UPDATE(DCCG_AUDIO_DTO1_MODULE, + DCCG_AUDIO_DTO1_MODULE, clock_info.audio_dto_module); + + /* phase */ + REG_UPDATE(DCCG_AUDIO_DTO1_PHASE, + DCCG_AUDIO_DTO1_PHASE, clock_info.audio_dto_phase); + + /* DAL2 code separate DCCG_AUDIO_DTO_SEL and + DCCG_AUDIO_DTO2_USE_512FBR_DTO programming into two different + location. merge together should not hurt */ + /*value.bits.DCCG_AUDIO_DTO2_USE_512FBR_DTO = 1; + dal_write_reg(mmDCCG_AUDIO_DTO_SOURCE, value);*/ + } +} + +bool dce_aud_endpoint_valid( + struct audio *audio) +{ + uint32_t value; + uint32_t port_connectivity; + + value = AZ_REG_READ( + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT); + + port_connectivity = get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT, + PORT_CONNECTIVITY); + + return !(port_connectivity == 1); +} + +/* initialize HW state */ +void dce_aud_hw_init( + struct audio *audio) +{ + struct dce_audio *aud = DCE_AUD(audio); + + /* we only need to program the following registers once, so we only do + it for the inst 0*/ + if (audio->inst != 0) + return; + + /* Suport R5 - 32khz + * Suport R6 - 44.1khz + * Suport R7 - 48khz + */ + REG_UPDATE(AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES, + AUDIO_RATE_CAPABILITIES, 0x70); + + /*Keep alive bit to verify HW block in BU. */ + REG_UPDATE_2(AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, + CLKSTOP, 1, + EPSS, 1); +} + +static const struct audio_funcs funcs = { + .endpoint_valid = dce_aud_endpoint_valid, + .hw_init = dce_aud_hw_init, + .wall_dto_setup = dce_aud_wall_dto_setup, + .az_enable = dce_aud_az_enable, + .az_disable = dce_aud_az_disable, + .az_configure = dce_aud_az_configure, + .destroy = dce_aud_destroy, +}; + +void dce_aud_destroy(struct audio **audio) +{ + dm_free(*audio); + *audio = NULL; +} + +struct audio *dce_audio_create( + struct dc_context *ctx, + unsigned int inst, + const struct dce_audio_registers *reg, + const struct dce_audio_shift *shifts, + const struct dce_aduio_mask *masks + ) +{ + struct dce_audio *audio = dm_alloc(sizeof(*audio)); + + if (audio == NULL) { + ASSERT_CRITICAL(audio); + return NULL; + } + + audio->base.ctx = ctx; + audio->base.inst = inst; + audio->base.funcs = &funcs; + + audio->regs = reg; + audio->shifts = shifts; + audio->masks = masks; + + return &audio->base; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.h b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.h new file mode 100644 index 000000000000..bf97cd8c8221 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.h @@ -0,0 +1,145 @@ +/* + * Copyright 2012-15 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. + * + * Authors: AMD + * + */ +#ifndef __DAL_AUDIO_DCE_110_H__ +#define __DAL_AUDIO_DCE_110_H__ + +#include "audio.h" + +#define AUD_COMMON_REG_LIST(id)\ + SRI(AZALIA_F0_CODEC_ENDPOINT_INDEX, AZF0ENDPOINT, id),\ + SRI(AZALIA_F0_CODEC_ENDPOINT_DATA, AZF0ENDPOINT, id),\ + SR(AZALIA_F0_CODEC_FUNCTION_PARAMETER_STREAM_FORMATS),\ + SR(AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES),\ + SR(AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES),\ + SR(DCCG_AUDIO_DTO_SOURCE),\ + SR(DCCG_AUDIO_DTO0_MODULE),\ + SR(DCCG_AUDIO_DTO0_PHASE),\ + SR(DCCG_AUDIO_DTO1_MODULE),\ + SR(DCCG_AUDIO_DTO1_PHASE) + + + /* set field name */ +#define SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + + +#define AUD_COMMON_MASK_SH_LIST_BASE(mask_sh)\ + SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO0_SOURCE_SEL, mask_sh),\ + SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO_SEL, mask_sh),\ + SF(DCCG_AUDIO_DTO0_MODULE, DCCG_AUDIO_DTO0_MODULE, mask_sh),\ + SF(DCCG_AUDIO_DTO0_PHASE, DCCG_AUDIO_DTO0_PHASE, mask_sh),\ + SF(DCCG_AUDIO_DTO0_MODULE, DCCG_AUDIO_DTO0_MODULE, mask_sh),\ + SF(DCCG_AUDIO_DTO0_PHASE, DCCG_AUDIO_DTO0_PHASE, mask_sh),\ + SF(AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES, AUDIO_RATE_CAPABILITIES, mask_sh),\ + SF(AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, CLKSTOP, mask_sh),\ + SF(AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, EPSS, mask_sh) + +#define AUD_COMMON_MASK_SH_LIST(mask_sh)\ + AUD_COMMON_MASK_SH_LIST_BASE(mask_sh),\ + SF(AZALIA_F0_CODEC_ENDPOINT_INDEX, AZALIA_ENDPOINT_REG_INDEX, mask_sh),\ + SF(AZALIA_F0_CODEC_ENDPOINT_DATA, AZALIA_ENDPOINT_REG_DATA, mask_sh) + + +struct dce_audio_registers { + uint32_t AZALIA_F0_CODEC_ENDPOINT_INDEX; + uint32_t AZALIA_F0_CODEC_ENDPOINT_DATA; + + uint32_t AZALIA_F0_CODEC_FUNCTION_PARAMETER_STREAM_FORMATS; + uint32_t AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES; + uint32_t AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES; + + uint32_t DCCG_AUDIO_DTO_SOURCE; + uint32_t DCCG_AUDIO_DTO0_MODULE; + uint32_t DCCG_AUDIO_DTO0_PHASE; + uint32_t DCCG_AUDIO_DTO1_MODULE; + uint32_t DCCG_AUDIO_DTO1_PHASE; + + uint32_t AUDIO_RATE_CAPABILITIES; +}; + +struct dce_audio_shift { + uint8_t AZALIA_ENDPOINT_REG_INDEX; + uint8_t AZALIA_ENDPOINT_REG_DATA; + + uint8_t AUDIO_RATE_CAPABILITIES; + uint8_t CLKSTOP; + uint8_t EPSS; + + uint8_t DCCG_AUDIO_DTO0_SOURCE_SEL; + uint8_t DCCG_AUDIO_DTO_SEL; + uint8_t DCCG_AUDIO_DTO0_MODULE; + uint8_t DCCG_AUDIO_DTO0_PHASE; + uint8_t DCCG_AUDIO_DTO1_MODULE; + uint8_t DCCG_AUDIO_DTO1_PHASE; +}; + +struct dce_aduio_mask { + uint32_t AZALIA_ENDPOINT_REG_INDEX; + uint32_t AZALIA_ENDPOINT_REG_DATA; + + uint32_t AUDIO_RATE_CAPABILITIES; + uint32_t CLKSTOP; + uint32_t EPSS; + + uint32_t DCCG_AUDIO_DTO0_SOURCE_SEL; + uint32_t DCCG_AUDIO_DTO_SEL; + uint32_t DCCG_AUDIO_DTO0_MODULE; + uint32_t DCCG_AUDIO_DTO0_PHASE; + uint32_t DCCG_AUDIO_DTO1_MODULE; + uint32_t DCCG_AUDIO_DTO1_PHASE; +}; + +struct dce_audio { + struct audio base; + const struct dce_audio_registers *regs; + const struct dce_audio_shift *shifts; + const struct dce_aduio_mask *masks; +}; + +struct audio *dce_audio_create( + struct dc_context *ctx, + unsigned int inst, + const struct dce_audio_registers *reg, + const struct dce_audio_shift *shifts, + const struct dce_aduio_mask *masks); + +void dce_aud_destroy(struct audio **audio); + +void dce_aud_hw_init(struct audio *audio); + +void dce_aud_az_enable(struct audio *audio); +void dce_aud_az_disable(struct audio *audio); + +void dce_aud_az_configure(struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_info *audio_info); + +void dce_aud_wall_dto_setup(struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info); + +#endif /*__DAL_AUDIO_DCE_110_H__*/ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c new file mode 100644 index 000000000000..80ac5d9efa71 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -0,0 +1,1264 @@ +/* + * Copyright 2012-15 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. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + + +#include "dc_types.h" +#include "core_types.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" + +#include "dce_clock_source.h" + +#include "reg_helper.h" + +#define REG(reg)\ + (clk_src->regs->reg) + +#define CTX \ + clk_src->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + clk_src->cs_shift->field_name, clk_src->cs_mask->field_name + +#define FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM 6 +#define CALC_PLL_CLK_SRC_ERR_TOLERANCE 1 +#define MAX_PLL_CALC_ERROR 0xFFFFFFFF + +static const struct spread_spectrum_data *get_ss_data_entry( + struct dce110_clk_src *clk_src, + enum signal_type signal, + uint32_t pix_clk_khz) +{ + + uint32_t entrys_num; + uint32_t i; + struct spread_spectrum_data *ss_parm = NULL; + struct spread_spectrum_data *ret = NULL; + + switch (signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + ss_parm = clk_src->dvi_ss_params; + entrys_num = clk_src->dvi_ss_params_cnt; + break; + + case SIGNAL_TYPE_HDMI_TYPE_A: + ss_parm = clk_src->hdmi_ss_params; + entrys_num = clk_src->hdmi_ss_params_cnt; + break; + + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_VIRTUAL: + ss_parm = clk_src->dp_ss_params; + entrys_num = clk_src->dp_ss_params_cnt; + break; + + default: + ss_parm = NULL; + entrys_num = 0; + break; + } + + if (ss_parm == NULL) + return ret; + + for (i = 0; i < entrys_num; ++i, ++ss_parm) { + if (ss_parm->freq_range_khz >= pix_clk_khz) { + ret = ss_parm; + break; + } + } + + return ret; +} + +/** +* Function: calculate_fb_and_fractional_fb_divider +* +* * DESCRIPTION: Calculates feedback and fractional feedback dividers values +* +*PARAMETERS: +* targetPixelClock Desired frequency in 10 KHz +* ref_divider Reference divider (already known) +* postDivider Post Divider (already known) +* feedback_divider_param Pointer where to store +* calculated feedback divider value +* fract_feedback_divider_param Pointer where to store +* calculated fract feedback divider value +* +*RETURNS: +* It fills the locations pointed by feedback_divider_param +* and fract_feedback_divider_param +* It returns - true if feedback divider not 0 +* - false should never happen) +*/ +static bool calculate_fb_and_fractional_fb_divider( + struct calc_pll_clock_source *calc_pll_cs, + uint32_t target_pix_clk_khz, + uint32_t ref_divider, + uint32_t post_divider, + uint32_t *feedback_divider_param, + uint32_t *fract_feedback_divider_param) +{ + uint64_t feedback_divider; + + feedback_divider = + (uint64_t)(target_pix_clk_khz * ref_divider * post_divider); + feedback_divider *= 10; + /* additional factor, since we divide by 10 afterwards */ + feedback_divider *= (uint64_t)(calc_pll_cs->fract_fb_divider_factor); + feedback_divider = div_u64(feedback_divider, calc_pll_cs->ref_freq_khz); + +/*Round to the number of precision + * The following code replace the old code (ullfeedbackDivider + 5)/10 + * for example if the difference between the number + * of fractional feedback decimal point and the fractional FB Divider precision + * is 2 then the equation becomes (ullfeedbackDivider + 5*100) / (10*100))*/ + + feedback_divider += (uint64_t) + (5 * calc_pll_cs->fract_fb_divider_precision_factor); + feedback_divider = + div_u64(feedback_divider, + calc_pll_cs->fract_fb_divider_precision_factor * 10); + feedback_divider *= (uint64_t) + (calc_pll_cs->fract_fb_divider_precision_factor); + + *feedback_divider_param = + div_u64_rem( + feedback_divider, + calc_pll_cs->fract_fb_divider_factor, + fract_feedback_divider_param); + + if (*feedback_divider_param != 0) + return true; + return false; +} + +/** +*calc_fb_divider_checking_tolerance +* +*DESCRIPTION: Calculates Feedback and Fractional Feedback divider values +* for passed Reference and Post divider, checking for tolerance. +*PARAMETERS: +* pll_settings Pointer to structure +* ref_divider Reference divider (already known) +* postDivider Post Divider (already known) +* tolerance Tolerance for Calculated Pixel Clock to be within +* +*RETURNS: +* It fills the PLLSettings structure with PLL Dividers values +* if calculated values are within required tolerance +* It returns - true if eror is within tolerance +* - false if eror is not within tolerance +*/ +static bool calc_fb_divider_checking_tolerance( + struct calc_pll_clock_source *calc_pll_cs, + struct pll_settings *pll_settings, + uint32_t ref_divider, + uint32_t post_divider, + uint32_t tolerance) +{ + uint32_t feedback_divider; + uint32_t fract_feedback_divider; + uint32_t actual_calculated_clock_khz; + uint32_t abs_err; + uint64_t actual_calc_clk_khz; + + calculate_fb_and_fractional_fb_divider( + calc_pll_cs, + pll_settings->adjusted_pix_clk, + ref_divider, + post_divider, + &feedback_divider, + &fract_feedback_divider); + + /*Actual calculated value*/ + actual_calc_clk_khz = (uint64_t)(feedback_divider * + calc_pll_cs->fract_fb_divider_factor) + + fract_feedback_divider; + actual_calc_clk_khz *= calc_pll_cs->ref_freq_khz; + actual_calc_clk_khz = + div_u64(actual_calc_clk_khz, + ref_divider * post_divider * + calc_pll_cs->fract_fb_divider_factor); + + actual_calculated_clock_khz = (uint32_t)(actual_calc_clk_khz); + + abs_err = (actual_calculated_clock_khz > + pll_settings->adjusted_pix_clk) + ? actual_calculated_clock_khz - + pll_settings->adjusted_pix_clk + : pll_settings->adjusted_pix_clk - + actual_calculated_clock_khz; + + if (abs_err <= tolerance) { + /*found good values*/ + pll_settings->reference_freq = calc_pll_cs->ref_freq_khz; + pll_settings->reference_divider = ref_divider; + pll_settings->feedback_divider = feedback_divider; + pll_settings->fract_feedback_divider = fract_feedback_divider; + pll_settings->pix_clk_post_divider = post_divider; + pll_settings->calculated_pix_clk = + actual_calculated_clock_khz; + pll_settings->vco_freq = + actual_calculated_clock_khz * post_divider; + return true; + } + return false; +} + +static bool calc_pll_dividers_in_range( + struct calc_pll_clock_source *calc_pll_cs, + struct pll_settings *pll_settings, + uint32_t min_ref_divider, + uint32_t max_ref_divider, + uint32_t min_post_divider, + uint32_t max_post_divider, + uint32_t err_tolerance) +{ + uint32_t ref_divider; + uint32_t post_divider; + uint32_t tolerance; + +/* This is err_tolerance / 10000 = 0.0025 - acceptable error of 0.25% + * This is errorTolerance / 10000 = 0.0001 - acceptable error of 0.01%*/ + tolerance = (pll_settings->adjusted_pix_clk * err_tolerance) / + 10000; + if (tolerance < CALC_PLL_CLK_SRC_ERR_TOLERANCE) + tolerance = CALC_PLL_CLK_SRC_ERR_TOLERANCE; + + for ( + post_divider = max_post_divider; + post_divider >= min_post_divider; + --post_divider) { + for ( + ref_divider = min_ref_divider; + ref_divider <= max_ref_divider; + ++ref_divider) { + if (calc_fb_divider_checking_tolerance( + calc_pll_cs, + pll_settings, + ref_divider, + post_divider, + tolerance)) { + return true; + } + } + } + + return false; +} + +static uint32_t calculate_pixel_clock_pll_dividers( + struct calc_pll_clock_source *calc_pll_cs, + struct pll_settings *pll_settings) +{ + uint32_t err_tolerance; + uint32_t min_post_divider; + uint32_t max_post_divider; + uint32_t min_ref_divider; + uint32_t max_ref_divider; + + if (pll_settings->adjusted_pix_clk == 0) { + dm_logger_write(calc_pll_cs->ctx->logger, LOG_ERROR, + "%s Bad requested pixel clock", __func__); + return MAX_PLL_CALC_ERROR; + } + +/* 1) Find Post divider ranges */ + if (pll_settings->pix_clk_post_divider) { + min_post_divider = pll_settings->pix_clk_post_divider; + max_post_divider = pll_settings->pix_clk_post_divider; + } else { + min_post_divider = calc_pll_cs->min_pix_clock_pll_post_divider; + if (min_post_divider * pll_settings->adjusted_pix_clk < + calc_pll_cs->min_vco_khz) { + min_post_divider = calc_pll_cs->min_vco_khz / + pll_settings->adjusted_pix_clk; + if ((min_post_divider * + pll_settings->adjusted_pix_clk) < + calc_pll_cs->min_vco_khz) + min_post_divider++; + } + + max_post_divider = calc_pll_cs->max_pix_clock_pll_post_divider; + if (max_post_divider * pll_settings->adjusted_pix_clk + > calc_pll_cs->max_vco_khz) + max_post_divider = calc_pll_cs->max_vco_khz / + pll_settings->adjusted_pix_clk; + } + +/* 2) Find Reference divider ranges + * When SS is enabled, or for Display Port even without SS, + * pll_settings->referenceDivider is not zero. + * So calculate PPLL FB and fractional FB divider + * using the passed reference divider*/ + + if (pll_settings->reference_divider) { + min_ref_divider = pll_settings->reference_divider; + max_ref_divider = pll_settings->reference_divider; + } else { + min_ref_divider = ((calc_pll_cs->ref_freq_khz + / calc_pll_cs->max_pll_input_freq_khz) + > calc_pll_cs->min_pll_ref_divider) + ? calc_pll_cs->ref_freq_khz + / calc_pll_cs->max_pll_input_freq_khz + : calc_pll_cs->min_pll_ref_divider; + + max_ref_divider = ((calc_pll_cs->ref_freq_khz + / calc_pll_cs->min_pll_input_freq_khz) + < calc_pll_cs->max_pll_ref_divider) + ? calc_pll_cs->ref_freq_khz / + calc_pll_cs->min_pll_input_freq_khz + : calc_pll_cs->max_pll_ref_divider; + } + +/* If some parameters are invalid we could have scenario when "min">"max" + * which produced endless loop later. + * We should investigate why we get the wrong parameters. + * But to follow the similar logic when "adjustedPixelClock" is set to be 0 + * it is better to return here than cause system hang/watchdog timeout later. + * ## SVS Wed 15 Jul 2009 */ + + if (min_post_divider > max_post_divider) { + dm_logger_write(calc_pll_cs->ctx->logger, LOG_ERROR, + "%s Post divider range is invalid", __func__); + return MAX_PLL_CALC_ERROR; + } + + if (min_ref_divider > max_ref_divider) { + dm_logger_write(calc_pll_cs->ctx->logger, LOG_ERROR, + "%s Reference divider range is invalid", __func__); + return MAX_PLL_CALC_ERROR; + } + +/* 3) Try to find PLL dividers given ranges + * starting with minimal error tolerance. + * Increase error tolerance until PLL dividers found*/ + err_tolerance = MAX_PLL_CALC_ERROR; + + while (!calc_pll_dividers_in_range( + calc_pll_cs, + pll_settings, + min_ref_divider, + max_ref_divider, + min_post_divider, + max_post_divider, + err_tolerance)) + err_tolerance += (err_tolerance > 10) + ? (err_tolerance / 10) + : 1; + + return err_tolerance; +} + +static bool pll_adjust_pix_clk( + struct dce110_clk_src *clk_src, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + uint32_t actual_pix_clk_khz = 0; + uint32_t requested_clk_khz = 0; + struct bp_adjust_pixel_clock_parameters bp_adjust_pixel_clock_params = { + 0 }; + enum bp_result bp_result; + + switch (pix_clk_params->signal_type) { + case SIGNAL_TYPE_HDMI_TYPE_A: { + requested_clk_khz = pix_clk_params->requested_pix_clk; + + switch (pix_clk_params->color_depth) { + case COLOR_DEPTH_101010: + requested_clk_khz = (requested_clk_khz * 5) >> 2; + break; /* x1.25*/ + case COLOR_DEPTH_121212: + requested_clk_khz = (requested_clk_khz * 6) >> 2; + break; /* x1.5*/ + case COLOR_DEPTH_161616: + requested_clk_khz = requested_clk_khz * 2; + break; /* x2.0*/ + default: + break; + } + + actual_pix_clk_khz = requested_clk_khz; + } + break; + + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + requested_clk_khz = pix_clk_params->requested_sym_clk; + actual_pix_clk_khz = pix_clk_params->requested_pix_clk; + break; + + default: + requested_clk_khz = pix_clk_params->requested_pix_clk; + actual_pix_clk_khz = pix_clk_params->requested_pix_clk; + break; + } + + bp_adjust_pixel_clock_params.pixel_clock = requested_clk_khz; + bp_adjust_pixel_clock_params. + encoder_object_id = pix_clk_params->encoder_object_id; + bp_adjust_pixel_clock_params.signal_type = pix_clk_params->signal_type; + bp_adjust_pixel_clock_params. + ss_enable = pix_clk_params->flags.ENABLE_SS; + bp_result = clk_src->bios->funcs->adjust_pixel_clock( + clk_src->bios, &bp_adjust_pixel_clock_params); + if (bp_result == BP_RESULT_OK) { + pll_settings->actual_pix_clk = actual_pix_clk_khz; + pll_settings->adjusted_pix_clk = + bp_adjust_pixel_clock_params.adjusted_pixel_clock; + pll_settings->reference_divider = + bp_adjust_pixel_clock_params.reference_divider; + pll_settings->pix_clk_post_divider = + bp_adjust_pixel_clock_params.pixel_clock_post_divider; + + return true; + } + + return false; +} + +/** + * Calculate PLL Dividers for given Clock Value. + * First will call VBIOS Adjust Exec table to check if requested Pixel clock + * will be Adjusted based on usage. + * Then it will calculate PLL Dividers for this Adjusted clock using preferred + * method (Maximum VCO frequency). + * + * \return + * Calculation error in units of 0.01% + */ + +static uint32_t dce110_get_pix_clk_dividers_helper ( + struct dce110_clk_src *clk_src, + struct pll_settings *pll_settings, + struct pixel_clk_params *pix_clk_params) +{ + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; + + /* Check if reference clock is external (not pcie/xtalin) + * HW Dce80 spec: + * 00 - PCIE_REFCLK, 01 - XTALIN, 02 - GENERICA, 03 - GENERICB + * 04 - HSYNCA, 05 - GENLK_CLK, 06 - PCIE_REFCLK, 07 - DVOCLK0 */ + value = REG_READ(PLL_CNTL); + REG_GET(PLL_CNTL, PLL_REF_DIV_SRC, &field); + pll_settings->use_external_clk = (field > 1); + + /* VBIOS by default enables DP SS (spread on IDCLK) for DCE 8.0 always + * (we do not care any more from SI for some older DP Sink which + * does not report SS support, no known issues) */ + if ((pix_clk_params->flags.ENABLE_SS) || + (dc_is_dp_signal(pix_clk_params->signal_type))) { + + const struct spread_spectrum_data *ss_data = get_ss_data_entry( + clk_src, + pix_clk_params->signal_type, + pll_settings->adjusted_pix_clk); + + if (NULL != ss_data) + pll_settings->ss_percentage = ss_data->percentage; + } + + /* Check VBIOS AdjustPixelClock Exec table */ + if (!pll_adjust_pix_clk(clk_src, pix_clk_params, pll_settings)) { + /* Should never happen, ASSERT and fill up values to be able + * to continue. */ + dm_logger_write(clk_src->base.ctx->logger, LOG_ERROR, + "%s: Failed to adjust pixel clock!!", __func__); + pll_settings->actual_pix_clk = + pix_clk_params->requested_pix_clk; + pll_settings->adjusted_pix_clk = + pix_clk_params->requested_pix_clk; + + if (dc_is_dp_signal(pix_clk_params->signal_type)) + pll_settings->adjusted_pix_clk = 100000; + } + + /* Calculate Dividers */ + if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) + /*Calculate Dividers by HDMI object, no SS case or SS case */ + pll_calc_error = + calculate_pixel_clock_pll_dividers( + &clk_src->calc_pll_hdmi, + pll_settings); + else + /*Calculate Dividers by default object, no SS case or SS case */ + pll_calc_error = + calculate_pixel_clock_pll_dividers( + &clk_src->calc_pll, + pll_settings); + + return pll_calc_error; +} + +static void dce112_get_pix_clk_dividers_helper ( + struct dce110_clk_src *clk_src, + struct pll_settings *pll_settings, + struct pixel_clk_params *pix_clk_params) +{ + uint32_t actualPixelClockInKHz; + + actualPixelClockInKHz = pix_clk_params->requested_pix_clk; + /* Calculate Dividers */ + if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) { + switch (pix_clk_params->color_depth) { + case COLOR_DEPTH_101010: + actualPixelClockInKHz = (actualPixelClockInKHz * 5) >> 2; + break; + case COLOR_DEPTH_121212: + actualPixelClockInKHz = (actualPixelClockInKHz * 6) >> 2; + break; + case COLOR_DEPTH_161616: + actualPixelClockInKHz = actualPixelClockInKHz * 2; + break; + default: + break; + } + } + pll_settings->actual_pix_clk = actualPixelClockInKHz; + pll_settings->adjusted_pix_clk = actualPixelClockInKHz; + pll_settings->calculated_pix_clk = pix_clk_params->requested_pix_clk; +} + +static uint32_t dce110_get_pix_clk_dividers( + struct clock_source *cs, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); + uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; + + if (pix_clk_params == NULL || pll_settings == NULL + || pix_clk_params->requested_pix_clk == 0) { + dm_logger_write(clk_src->base.ctx->logger, LOG_ERROR, + "%s: Invalid parameters!!\n", __func__); + return pll_calc_error; + } + + memset(pll_settings, 0, sizeof(*pll_settings)); + + if (cs->id == CLOCK_SOURCE_ID_DP_DTO || + cs->id == CLOCK_SOURCE_ID_EXTERNAL) { + pll_settings->adjusted_pix_clk = clk_src->ext_clk_khz; + pll_settings->calculated_pix_clk = clk_src->ext_clk_khz; + pll_settings->actual_pix_clk = + pix_clk_params->requested_pix_clk; + return 0; + } + + switch (cs->ctx->dce_version) { + case DCE_VERSION_8_0: + case DCE_VERSION_10_0: + case DCE_VERSION_11_0: + pll_calc_error = + dce110_get_pix_clk_dividers_helper(clk_src, + pll_settings, pix_clk_params); + break; + case DCE_VERSION_11_2: + dce112_get_pix_clk_dividers_helper(clk_src, + pll_settings, pix_clk_params); + break; + default: + break; + } + + return pll_calc_error; +} + +static bool disable_spread_spectrum(struct dce110_clk_src *clk_src) +{ + enum bp_result result; + struct bp_spread_spectrum_parameters bp_ss_params = {0}; + + bp_ss_params.pll_id = clk_src->base.id; + + /*Call ASICControl to process ATOMBIOS Exec table*/ + result = clk_src->bios->funcs->enable_spread_spectrum_on_ppll( + clk_src->bios, + &bp_ss_params, + false); + + return result == BP_RESULT_OK; +} + +static bool calculate_ss( + const struct pll_settings *pll_settings, + const struct spread_spectrum_data *ss_data, + struct delta_sigma_data *ds_data) +{ + struct fixed32_32 fb_div; + struct fixed32_32 ss_amount; + struct fixed32_32 ss_nslip_amount; + struct fixed32_32 ss_ds_frac_amount; + struct fixed32_32 ss_step_size; + struct fixed32_32 modulation_time; + + if (ds_data == NULL) + return false; + if (ss_data == NULL) + return false; + if (ss_data->percentage == 0) + return false; + if (pll_settings == NULL) + return false; + + memset(ds_data, 0, sizeof(struct delta_sigma_data)); + + /* compute SS_AMOUNT_FBDIV & SS_AMOUNT_NFRAC_SLIP & SS_AMOUNT_DSFRAC*/ + /* 6 decimal point support in fractional feedback divider */ + fb_div = dal_fixed32_32_from_fraction( + pll_settings->fract_feedback_divider, 1000000); + fb_div = dal_fixed32_32_add_int(fb_div, pll_settings->feedback_divider); + + ds_data->ds_frac_amount = 0; + /*spreadSpectrumPercentage is in the unit of .01%, + * so have to divided by 100 * 100*/ + ss_amount = dal_fixed32_32_mul( + fb_div, dal_fixed32_32_from_fraction(ss_data->percentage, + 100 * ss_data->percentage_divider)); + ds_data->feedback_amount = dal_fixed32_32_floor(ss_amount); + + ss_nslip_amount = dal_fixed32_32_sub(ss_amount, + dal_fixed32_32_from_int(ds_data->feedback_amount)); + ss_nslip_amount = dal_fixed32_32_mul_int(ss_nslip_amount, 10); + ds_data->nfrac_amount = dal_fixed32_32_floor(ss_nslip_amount); + + ss_ds_frac_amount = dal_fixed32_32_sub(ss_nslip_amount, + dal_fixed32_32_from_int(ds_data->nfrac_amount)); + ss_ds_frac_amount = dal_fixed32_32_mul_int(ss_ds_frac_amount, 65536); + ds_data->ds_frac_amount = dal_fixed32_32_floor(ss_ds_frac_amount); + + /* compute SS_STEP_SIZE_DSFRAC */ + modulation_time = dal_fixed32_32_from_fraction( + pll_settings->reference_freq * 1000, + pll_settings->reference_divider * ss_data->modulation_freq_hz); + + if (ss_data->flags.CENTER_SPREAD) + modulation_time = dal_fixed32_32_div_int(modulation_time, 4); + else + modulation_time = dal_fixed32_32_div_int(modulation_time, 2); + + ss_step_size = dal_fixed32_32_div(ss_amount, modulation_time); + /* SS_STEP_SIZE_DSFRAC_DEC = Int(SS_STEP_SIZE * 2 ^ 16 * 10)*/ + ss_step_size = dal_fixed32_32_mul_int(ss_step_size, 65536 * 10); + ds_data->ds_frac_size = dal_fixed32_32_floor(ss_step_size); + + return true; +} + +static bool enable_spread_spectrum( + struct dce110_clk_src *clk_src, + enum signal_type signal, struct pll_settings *pll_settings) +{ + struct bp_spread_spectrum_parameters bp_params = {0}; + struct delta_sigma_data d_s_data; + const struct spread_spectrum_data *ss_data = NULL; + + ss_data = get_ss_data_entry( + clk_src, + signal, + pll_settings->calculated_pix_clk); + +/* Pixel clock PLL has been programmed to generate desired pixel clock, + * now enable SS on pixel clock */ +/* TODO is it OK to return true not doing anything ??*/ + if (ss_data != NULL && pll_settings->ss_percentage != 0) { + if (calculate_ss(pll_settings, ss_data, &d_s_data)) { + bp_params.ds.feedback_amount = + d_s_data.feedback_amount; + bp_params.ds.nfrac_amount = + d_s_data.nfrac_amount; + bp_params.ds.ds_frac_size = d_s_data.ds_frac_size; + bp_params.ds_frac_amount = + d_s_data.ds_frac_amount; + bp_params.flags.DS_TYPE = 1; + bp_params.pll_id = clk_src->base.id; + bp_params.percentage = ss_data->percentage; + if (ss_data->flags.CENTER_SPREAD) + bp_params.flags.CENTER_SPREAD = 1; + if (ss_data->flags.EXTERNAL_SS) + bp_params.flags.EXTERNAL_SS = 1; + + if (BP_RESULT_OK != + clk_src->bios->funcs-> + enable_spread_spectrum_on_ppll( + clk_src->bios, + &bp_params, + true)) + return false; + } else + return false; + } + return true; +} + +static void dce110_program_pixel_clk_resync( + struct dce110_clk_src *clk_src, + enum signal_type signal_type, + enum dc_color_depth colordepth) +{ + uint32_t value = 0; + + REG_UPDATE(RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1, 0); + /* + 24 bit mode: TMDS clock = 1.0 x pixel clock (1:1) + 30 bit mode: TMDS clock = 1.25 x pixel clock (5:4) + 36 bit mode: TMDS clock = 1.5 x pixel clock (3:2) + 48 bit mode: TMDS clock = 2 x pixel clock (2:1) + */ + if (signal_type != SIGNAL_TYPE_HDMI_TYPE_A) + return; + + switch (colordepth) { + case COLOR_DEPTH_888: + REG_UPDATE(RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1, 0); + break; + case COLOR_DEPTH_101010: + REG_UPDATE(RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1, 1); + break; + case COLOR_DEPTH_121212: + REG_UPDATE(RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1, 2); + break; + case COLOR_DEPTH_161616: + REG_UPDATE(RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1, 3); + break; + default: + break; + } +} + +static void dce112_program_pixel_clk_resync( + struct dce110_clk_src *clk_src, + enum signal_type signal_type, + enum dc_color_depth colordepth, + bool enable_ycbcr420) +{ + uint32_t value = 0; + + REG_UPDATE(PIXCLK_RESYNC_CNTL, + PHYPLLA_DCCG_DEEP_COLOR_CNTL, 0); + /* + 24 bit mode: TMDS clock = 1.0 x pixel clock (1:1) + 30 bit mode: TMDS clock = 1.25 x pixel clock (5:4) + 36 bit mode: TMDS clock = 1.5 x pixel clock (3:2) + 48 bit mode: TMDS clock = 2 x pixel clock (2:1) + */ + if (signal_type != SIGNAL_TYPE_HDMI_TYPE_A) + return; + + switch (colordepth) { + case COLOR_DEPTH_888: + REG_UPDATE_2(PIXCLK_RESYNC_CNTL, + PHYPLLA_DCCG_DEEP_COLOR_CNTL, 0, + PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE, enable_ycbcr420); + break; + case COLOR_DEPTH_101010: + REG_UPDATE_2(PIXCLK_RESYNC_CNTL, + PHYPLLA_DCCG_DEEP_COLOR_CNTL, 1, + PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE, enable_ycbcr420); + break; + case COLOR_DEPTH_121212: + REG_UPDATE_2(PIXCLK_RESYNC_CNTL, + PHYPLLA_DCCG_DEEP_COLOR_CNTL, 2, + PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE, enable_ycbcr420); + break; + case COLOR_DEPTH_161616: + REG_UPDATE_2(PIXCLK_RESYNC_CNTL, + PHYPLLA_DCCG_DEEP_COLOR_CNTL, 3, + PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE, enable_ycbcr420); + break; + default: + break; + } +} + +static bool dce110_program_pix_clk( + struct clock_source *clk_src, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *dce110_clk_src = TO_DCE110_CLK_SRC(clk_src); + struct bp_pixel_clock_parameters bp_pc_params = {0}; + + /* First disable SS + * ATOMBIOS will enable by default SS on PLL for DP, + * do not disable it here + */ + if (clk_src->id != CLOCK_SOURCE_ID_EXTERNAL && + !dc_is_dp_signal(pix_clk_params->signal_type) && + clk_src->ctx->dce_version <= DCE_VERSION_11_0) + disable_spread_spectrum(dce110_clk_src); + + /*ATOMBIOS expects pixel rate adjusted by deep color ratio)*/ + bp_pc_params.controller_id = pix_clk_params->controller_id; + bp_pc_params.pll_id = clk_src->id; + bp_pc_params.target_pixel_clock = pll_settings->actual_pix_clk; + bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id; + bp_pc_params.signal_type = pix_clk_params->signal_type; + + switch (clk_src->ctx->dce_version) { + case DCE_VERSION_11_2: + if (clk_src->id != CLOCK_SOURCE_ID_DP_DTO) { + bp_pc_params.flags.SET_GENLOCK_REF_DIV_SRC = + pll_settings->use_external_clk; + bp_pc_params.flags.SET_XTALIN_REF_SRC = + !pll_settings->use_external_clk; + if (pix_clk_params->flags.SUPPORT_YCBCR420) { + bp_pc_params.target_pixel_clock = pll_settings->actual_pix_clk / 2; + bp_pc_params.flags.SUPPORT_YUV_420 = 1; + } + } + if (dce110_clk_src->bios->funcs->set_pixel_clock( + dce110_clk_src->bios, &bp_pc_params) != BP_RESULT_OK) + return false; + /* Resync deep color DTO */ + if (clk_src->id != CLOCK_SOURCE_ID_DP_DTO) + dce112_program_pixel_clk_resync(dce110_clk_src, + pix_clk_params->signal_type, + pix_clk_params->color_depth, + pix_clk_params->flags.SUPPORT_YCBCR420); + break; + case DCE_VERSION_8_0: + case DCE_VERSION_10_0: + case DCE_VERSION_11_0: + bp_pc_params.reference_divider = pll_settings->reference_divider; + bp_pc_params.feedback_divider = pll_settings->feedback_divider; + bp_pc_params.fractional_feedback_divider = + pll_settings->fract_feedback_divider; + bp_pc_params.pixel_clock_post_divider = + pll_settings->pix_clk_post_divider; + bp_pc_params.flags.SET_EXTERNAL_REF_DIV_SRC = + pll_settings->use_external_clk; + + if (dce110_clk_src->bios->funcs->set_pixel_clock( + dce110_clk_src->bios, &bp_pc_params) != BP_RESULT_OK) + return false; + /* Enable SS + * ATOMBIOS will enable by default SS for DP on PLL ( DP ID clock), + * based on HW display PLL team, SS control settings should be programmed + * during PLL Reset, but they do not have effect + * until SS_EN is asserted.*/ + if (clk_src->id != CLOCK_SOURCE_ID_EXTERNAL + && pix_clk_params->flags.ENABLE_SS && !dc_is_dp_signal( + pix_clk_params->signal_type)) { + + if (!enable_spread_spectrum(dce110_clk_src, + pix_clk_params->signal_type, + pll_settings)) + return false; + /* Resync deep color DTO */ + dce110_program_pixel_clk_resync(dce110_clk_src, + pix_clk_params->signal_type, + pix_clk_params->color_depth); + } + break; + default: + break; + } + + return true; +} + +static bool dce110_clock_source_power_down( + struct clock_source *clk_src) +{ + struct dce110_clk_src *dce110_clk_src = TO_DCE110_CLK_SRC(clk_src); + enum bp_result bp_result; + struct bp_pixel_clock_parameters bp_pixel_clock_params = {0}; + + if (clk_src->dp_clk_src) + return true; + + /* If Pixel Clock is 0 it means Power Down Pll*/ + bp_pixel_clock_params.controller_id = CONTROLLER_ID_UNDEFINED; + bp_pixel_clock_params.pll_id = clk_src->id; + bp_pixel_clock_params.flags.FORCE_PROGRAMMING_OF_PLL = 1; + + /*Call ASICControl to process ATOMBIOS Exec table*/ + bp_result = dce110_clk_src->bios->funcs->set_pixel_clock( + dce110_clk_src->bios, + &bp_pixel_clock_params); + + return bp_result == BP_RESULT_OK; +} + +/*****************************************/ +/* Constructor */ +/*****************************************/ +static const struct clock_source_funcs dce110_clk_src_funcs = { + .cs_power_down = dce110_clock_source_power_down, + .program_pix_clk = dce110_program_pix_clk, + .get_pix_clk_dividers = dce110_get_pix_clk_dividers +}; + +static void get_ss_info_from_atombios( + struct dce110_clk_src *clk_src, + enum as_signal_type as_signal, + struct spread_spectrum_data *spread_spectrum_data[], + uint32_t *ss_entries_num) +{ + enum bp_result bp_result = BP_RESULT_FAILURE; + struct spread_spectrum_info *ss_info; + struct spread_spectrum_data *ss_data; + struct spread_spectrum_info *ss_info_cur; + struct spread_spectrum_data *ss_data_cur; + uint32_t i; + + if (ss_entries_num == NULL) { + dm_logger_write(clk_src->base.ctx->logger, LOG_SYNC, + "Invalid entry !!!\n"); + return; + } + if (spread_spectrum_data == NULL) { + dm_logger_write(clk_src->base.ctx->logger, LOG_SYNC, + "Invalid array pointer!!!\n"); + return; + } + + spread_spectrum_data[0] = NULL; + *ss_entries_num = 0; + + *ss_entries_num = clk_src->bios->funcs->get_ss_entry_number( + clk_src->bios, + as_signal); + + if (*ss_entries_num == 0) + return; + + ss_info = dm_alloc(sizeof(struct spread_spectrum_info) * (*ss_entries_num)); + ss_info_cur = ss_info; + if (ss_info == NULL) + return; + + ss_data = dm_alloc(sizeof(struct spread_spectrum_data) * (*ss_entries_num)); + if (ss_data == NULL) + goto out_free_info; + + for (i = 0, ss_info_cur = ss_info; + i < (*ss_entries_num); + ++i, ++ss_info_cur) { + + bp_result = clk_src->bios->funcs->get_spread_spectrum_info( + clk_src->bios, + as_signal, + i, + ss_info_cur); + + if (bp_result != BP_RESULT_OK) + goto out_free_data; + } + + for (i = 0, ss_info_cur = ss_info, ss_data_cur = ss_data; + i < (*ss_entries_num); + ++i, ++ss_info_cur, ++ss_data_cur) { + + if (ss_info_cur->type.STEP_AND_DELAY_INFO != false) { + dm_logger_write(clk_src->base.ctx->logger, LOG_SYNC, + "Invalid ATOMBIOS SS Table!!!\n"); + goto out_free_data; + } + + /* for HDMI check SS percentage, + * if it is > 6 (0.06%), the ATOMBIOS table info is invalid*/ + if (as_signal == AS_SIGNAL_TYPE_HDMI + && ss_info_cur->spread_spectrum_percentage > 6){ + /* invalid input, do nothing */ + dm_logger_write(clk_src->base.ctx->logger, LOG_SYNC, + "Invalid SS percentage "); + dm_logger_write(clk_src->base.ctx->logger, LOG_SYNC, + "for HDMI in ATOMBIOS info Table!!!\n"); + continue; + } + if (ss_info_cur->spread_percentage_divider == 1000) { + /* Keep previous precision from ATOMBIOS for these + * in case new precision set by ATOMBIOS for these + * (otherwise all code in DCE specific classes + * for all previous ASICs would need + * to be updated for SS calculations, + * Audio SS compensation and DP DTO SS compensation + * which assumes fixed SS percentage Divider = 100)*/ + ss_info_cur->spread_spectrum_percentage /= 10; + ss_info_cur->spread_percentage_divider = 100; + } + + ss_data_cur->freq_range_khz = ss_info_cur->target_clock_range; + ss_data_cur->percentage = + ss_info_cur->spread_spectrum_percentage; + ss_data_cur->percentage_divider = + ss_info_cur->spread_percentage_divider; + ss_data_cur->modulation_freq_hz = + ss_info_cur->spread_spectrum_range; + + if (ss_info_cur->type.CENTER_MODE) + ss_data_cur->flags.CENTER_SPREAD = 1; + + if (ss_info_cur->type.EXTERNAL) + ss_data_cur->flags.EXTERNAL_SS = 1; + + } + + *spread_spectrum_data = ss_data; + dm_free(ss_info); + return; + +out_free_data: + dm_free(ss_data); + *ss_entries_num = 0; +out_free_info: + dm_free(ss_info); +} + +static void ss_info_from_atombios_create( + struct dce110_clk_src *clk_src) +{ + get_ss_info_from_atombios( + clk_src, + AS_SIGNAL_TYPE_DISPLAY_PORT, + &clk_src->dp_ss_params, + &clk_src->dp_ss_params_cnt); + get_ss_info_from_atombios( + clk_src, + AS_SIGNAL_TYPE_HDMI, + &clk_src->hdmi_ss_params, + &clk_src->hdmi_ss_params_cnt); + get_ss_info_from_atombios( + clk_src, + AS_SIGNAL_TYPE_DVI, + &clk_src->dvi_ss_params, + &clk_src->dvi_ss_params_cnt); +} + +static bool calc_pll_max_vco_construct( + struct calc_pll_clock_source *calc_pll_cs, + struct calc_pll_clock_source_init_data *init_data) +{ + uint32_t i; + struct firmware_info fw_info = { { 0 } }; + if (calc_pll_cs == NULL || + init_data == NULL || + init_data->bp == NULL) + return false; + + if (init_data->bp->funcs->get_firmware_info( + init_data->bp, + &fw_info) != BP_RESULT_OK) + return false; + + calc_pll_cs->ctx = init_data->ctx; + calc_pll_cs->ref_freq_khz = fw_info.pll_info.crystal_frequency; + calc_pll_cs->min_vco_khz = + fw_info.pll_info.min_output_pxl_clk_pll_frequency; + calc_pll_cs->max_vco_khz = + fw_info.pll_info.max_output_pxl_clk_pll_frequency; + + if (init_data->max_override_input_pxl_clk_pll_freq_khz != 0) + calc_pll_cs->max_pll_input_freq_khz = + init_data->max_override_input_pxl_clk_pll_freq_khz; + else + calc_pll_cs->max_pll_input_freq_khz = + fw_info.pll_info.max_input_pxl_clk_pll_frequency; + + if (init_data->min_override_input_pxl_clk_pll_freq_khz != 0) + calc_pll_cs->min_pll_input_freq_khz = + init_data->min_override_input_pxl_clk_pll_freq_khz; + else + calc_pll_cs->min_pll_input_freq_khz = + fw_info.pll_info.min_input_pxl_clk_pll_frequency; + + calc_pll_cs->min_pix_clock_pll_post_divider = + init_data->min_pix_clk_pll_post_divider; + calc_pll_cs->max_pix_clock_pll_post_divider = + init_data->max_pix_clk_pll_post_divider; + calc_pll_cs->min_pll_ref_divider = + init_data->min_pll_ref_divider; + calc_pll_cs->max_pll_ref_divider = + init_data->max_pll_ref_divider; + + if (init_data->num_fract_fb_divider_decimal_point == 0 || + init_data->num_fract_fb_divider_decimal_point_precision > + init_data->num_fract_fb_divider_decimal_point) { + dm_logger_write(calc_pll_cs->ctx->logger, LOG_ERROR, + "The dec point num or precision is incorrect!"); + return false; + } + if (init_data->num_fract_fb_divider_decimal_point_precision == 0) { + dm_logger_write(calc_pll_cs->ctx->logger, LOG_ERROR, + "Incorrect fract feedback divider precision num!"); + return false; + } + + calc_pll_cs->fract_fb_divider_decimal_points_num = + init_data->num_fract_fb_divider_decimal_point; + calc_pll_cs->fract_fb_divider_precision = + init_data->num_fract_fb_divider_decimal_point_precision; + calc_pll_cs->fract_fb_divider_factor = 1; + for (i = 0; i < calc_pll_cs->fract_fb_divider_decimal_points_num; ++i) + calc_pll_cs->fract_fb_divider_factor *= 10; + + calc_pll_cs->fract_fb_divider_precision_factor = 1; + for ( + i = 0; + i < (calc_pll_cs->fract_fb_divider_decimal_points_num - + calc_pll_cs->fract_fb_divider_precision); + ++i) + calc_pll_cs->fract_fb_divider_precision_factor *= 10; + + return true; +} + +bool dce110_clk_src_construct( + struct dce110_clk_src *clk_src, + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + const struct dce110_clk_src_shift *cs_shift, + const struct dce110_clk_src_mask *cs_mask) +{ + struct firmware_info fw_info = { { 0 } }; + struct calc_pll_clock_source_init_data calc_pll_cs_init_data_hdmi; + struct calc_pll_clock_source_init_data calc_pll_cs_init_data; + + clk_src->base.ctx = ctx; + clk_src->bios = bios; + clk_src->base.id = id; + clk_src->base.funcs = &dce110_clk_src_funcs; + + clk_src->regs = regs; + clk_src->cs_shift = cs_shift; + clk_src->cs_mask = cs_mask; + + if (clk_src->bios->funcs->get_firmware_info( + clk_src->bios, &fw_info) != BP_RESULT_OK) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } + + clk_src->ext_clk_khz = + fw_info.external_clock_source_frequency_for_dp; + + switch (clk_src->base.ctx->dce_version) { + case DCE_VERSION_8_0: + case DCE_VERSION_10_0: + case DCE_VERSION_11_0: + + /* structure normally used with PLL ranges from ATOMBIOS; DS on by default */ + calc_pll_cs_init_data.bp = bios; + calc_pll_cs_init_data.min_pix_clk_pll_post_divider = 1; + calc_pll_cs_init_data.max_pix_clk_pll_post_divider = + clk_src->cs_mask->PLL_POST_DIV_PIXCLK; + calc_pll_cs_init_data.min_pll_ref_divider = 1; + calc_pll_cs_init_data.max_pll_ref_divider = clk_src->cs_mask->PLL_REF_DIV; + /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data.min_override_input_pxl_clk_pll_freq_khz = 0; + /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data.max_override_input_pxl_clk_pll_freq_khz = 0; + /*numberOfFractFBDividerDecimalPoints*/ + calc_pll_cs_init_data.num_fract_fb_divider_decimal_point = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + /*number of decimal point to round off for fractional feedback divider value*/ + calc_pll_cs_init_data.num_fract_fb_divider_decimal_point_precision = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + calc_pll_cs_init_data.ctx = ctx; + + /*structure for HDMI, no SS or SS% <= 0.06% for 27 MHz Ref clock */ + calc_pll_cs_init_data_hdmi.bp = bios; + calc_pll_cs_init_data_hdmi.min_pix_clk_pll_post_divider = 1; + calc_pll_cs_init_data_hdmi.max_pix_clk_pll_post_divider = + clk_src->cs_mask->PLL_POST_DIV_PIXCLK; + calc_pll_cs_init_data_hdmi.min_pll_ref_divider = 1; + calc_pll_cs_init_data_hdmi.max_pll_ref_divider = clk_src->cs_mask->PLL_REF_DIV; + /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data_hdmi.min_override_input_pxl_clk_pll_freq_khz = 13500; + /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data_hdmi.max_override_input_pxl_clk_pll_freq_khz = 27000; + /*numberOfFractFBDividerDecimalPoints*/ + calc_pll_cs_init_data_hdmi.num_fract_fb_divider_decimal_point = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + /*number of decimal point to round off for fractional feedback divider value*/ + calc_pll_cs_init_data_hdmi.num_fract_fb_divider_decimal_point_precision = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + calc_pll_cs_init_data_hdmi.ctx = ctx; + + clk_src->ref_freq_khz = fw_info.pll_info.crystal_frequency; + + if (clk_src->base.id == CLOCK_SOURCE_ID_EXTERNAL) + return true; + + /* PLL only from here on */ + ss_info_from_atombios_create(clk_src); + + if (!calc_pll_max_vco_construct( + &clk_src->calc_pll, + &calc_pll_cs_init_data)) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } + + if (clk_src->ref_freq_khz == 48000) { + calc_pll_cs_init_data_hdmi. + min_override_input_pxl_clk_pll_freq_khz = 24000; + calc_pll_cs_init_data_hdmi. + max_override_input_pxl_clk_pll_freq_khz = 48000; + } else if (clk_src->ref_freq_khz == 100000) { + calc_pll_cs_init_data_hdmi. + min_override_input_pxl_clk_pll_freq_khz = 25000; + calc_pll_cs_init_data_hdmi. + max_override_input_pxl_clk_pll_freq_khz = 50000; + } + + if (!calc_pll_max_vco_construct( + &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } + break; + default: + break; + } + + return true; + +unexpected_failure: + return false; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h new file mode 100644 index 000000000000..067e4ac0e67a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h @@ -0,0 +1,109 @@ +/* Copyright 2012-15 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. + * + * Authors: AMD + * + */ + +#ifndef __DC_CLOCK_SOURCE_DCE_H__ +#define __DC_CLOCK_SOURCE_DCE_H__ + +#include "../inc/clock_source.h" + +#define TO_DCE110_CLK_SRC(clk_src)\ + container_of(clk_src, struct dce110_clk_src, base) + +#define CS_COMMON_REG_LIST_DCE_100_110(id) \ + SRI(RESYNC_CNTL, PIXCLK, id), \ + SRI(PLL_CNTL, BPHYC_PLL, id) + +#define CS_COMMON_REG_LIST_DCE_80(id) \ + SRI(RESYNC_CNTL, PIXCLK, id), \ + SRI(PLL_CNTL, DCCG_PLL, id) + +#define CS_COMMON_REG_LIST_DCE_112(id) \ + SRI(PIXCLK_RESYNC_CNTL, PHYPLL, id) + +#define CS_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh)\ + CS_SF(PLL_CNTL, PLL_REF_DIV_SRC, mask_sh),\ + CS_SF(PIXCLK1_RESYNC_CNTL, DCCG_DEEP_COLOR_CNTL1, mask_sh),\ + CS_SF(PLL_POST_DIV, PLL_POST_DIV_PIXCLK, mask_sh),\ + CS_SF(PLL_REF_DIV, PLL_REF_DIV, mask_sh),\ + +#define CS_COMMON_MASK_SH_LIST_DCE_112(mask_sh)\ + CS_SF(PHYPLLA_PIXCLK_RESYNC_CNTL, PHYPLLA_DCCG_DEEP_COLOR_CNTL, mask_sh),\ + CS_SF(PHYPLLA_PIXCLK_RESYNC_CNTL, PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE, mask_sh),\ + +#define CS_REG_FIELD_LIST(type) \ + type PLL_REF_DIV_SRC; \ + type DCCG_DEEP_COLOR_CNTL1; \ + type PHYPLLA_DCCG_DEEP_COLOR_CNTL; \ + type PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE; \ + type PLL_POST_DIV_PIXCLK; \ + type PLL_REF_DIV; \ + +struct dce110_clk_src_shift { + CS_REG_FIELD_LIST(uint8_t) +}; + +struct dce110_clk_src_mask{ + CS_REG_FIELD_LIST(uint32_t) +}; + +struct dce110_clk_src_regs { + uint32_t RESYNC_CNTL; + uint32_t PIXCLK_RESYNC_CNTL; + uint32_t PLL_CNTL; +}; + +struct dce110_clk_src { + struct clock_source base; + const struct dce110_clk_src_regs *regs; + const struct dce110_clk_src_mask *cs_mask; + const struct dce110_clk_src_shift *cs_shift; + struct dc_bios *bios; + + struct spread_spectrum_data *dp_ss_params; + uint32_t dp_ss_params_cnt; + struct spread_spectrum_data *hdmi_ss_params; + uint32_t hdmi_ss_params_cnt; + struct spread_spectrum_data *dvi_ss_params; + uint32_t dvi_ss_params_cnt; + + uint32_t ext_clk_khz; + uint32_t ref_freq_khz; + + struct calc_pll_clock_source calc_pll; + struct calc_pll_clock_source calc_pll_hdmi; +}; + +bool dce110_clk_src_construct( + struct dce110_clk_src *clk_src, + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id, + const struct dce110_clk_src_regs *regs, + const struct dce110_clk_src_shift *cs_shift, + const struct dce110_clk_src_mask *cs_mask); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.c b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.c new file mode 100644 index 000000000000..dd1cf5e6e949 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.c @@ -0,0 +1,195 @@ +/* + * Copyright 2016 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. + * + * Authors: AMD + * + */ + +#include "dce_hwseq.h" +#include "reg_helper.h" +#include "hw_sequencer.h" + +#define CTX \ + hws->ctx +#define REG(reg)\ + hws->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + hws->shifts->field_name, hws->masks->field_name + +void dce_enable_fe_clock(struct dce_hwseq *hws, + unsigned int fe_inst, bool enable) +{ + REG_UPDATE(DCFE_CLOCK_CONTROL[fe_inst], + DCFE_CLOCK_ENABLE, enable); +} + +void dce_pipe_control_lock(struct dce_hwseq *hws, + unsigned int blnd_inst, + enum pipe_lock_control control_mask, + bool lock) +{ + uint32_t lock_val = lock ? 1 : 0; + uint32_t dcp_grph, scl, dcp_grph_surf, blnd, update_lock_mode; + + uint32_t val = REG_GET_5(BLND_V_UPDATE_LOCK[blnd_inst], + BLND_DCP_GRPH_V_UPDATE_LOCK, &dcp_grph, + BLND_SCL_V_UPDATE_LOCK, &scl, + BLND_DCP_GRPH_SURF_V_UPDATE_LOCK, &dcp_grph_surf, + BLND_BLND_V_UPDATE_LOCK, &blnd, + BLND_V_UPDATE_LOCK_MODE, &update_lock_mode); + + if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS) + dcp_grph = lock_val; + + if (control_mask & PIPE_LOCK_CONTROL_SCL) + scl = lock_val; + + if (control_mask & PIPE_LOCK_CONTROL_SURFACE) + dcp_grph_surf = lock_val; + + if (control_mask & PIPE_LOCK_CONTROL_BLENDER) + blnd = lock_val; + + if (control_mask & PIPE_LOCK_CONTROL_MODE) + update_lock_mode = lock_val; + + REG_SET_5(BLND_V_UPDATE_LOCK[blnd_inst], val, + BLND_DCP_GRPH_V_UPDATE_LOCK, dcp_grph, + BLND_SCL_V_UPDATE_LOCK, scl, + BLND_DCP_GRPH_SURF_V_UPDATE_LOCK, dcp_grph_surf, + BLND_BLND_V_UPDATE_LOCK, blnd, + BLND_V_UPDATE_LOCK_MODE, update_lock_mode); + + if (hws->wa.blnd_crtc_trigger) + if (!lock && (control_mask & PIPE_LOCK_CONTROL_BLENDER)) { + uint32_t value = REG_READ(CRTC_H_BLANK_START_END[blnd_inst]); + REG_WRITE(CRTC_H_BLANK_START_END[blnd_inst], value); + } +} + +void dce_set_blender_mode(struct dce_hwseq *hws, + unsigned int blnd_inst, + enum blnd_mode mode) +{ + uint32_t feedthrough = 1; + uint32_t blnd_mode = 0; + uint32_t multiplied_mode = 0; + uint32_t alpha_mode = 2; + + switch (mode) { + case BLND_MODE_OTHER_PIPE: + feedthrough = 0; + blnd_mode = 1; + alpha_mode = 0; + break; + case BLND_MODE_BLENDING: + feedthrough = 0; + blnd_mode = 2; + alpha_mode = 0; + multiplied_mode = 1; + break; + case BLND_MODE_CURRENT_PIPE: + default: + if (REG(BLND_CONTROL[blnd_inst]) == REG(BLNDV_CONTROL) || + blnd_inst == 0) + feedthrough = 0; + break; + } + + REG_UPDATE_4(BLND_CONTROL[blnd_inst], + BLND_FEEDTHROUGH_EN, feedthrough, + BLND_ALPHA_MODE, alpha_mode, + BLND_MODE, blnd_mode, + BLND_MULTIPLIED_MODE, multiplied_mode); +} + + +static void dce_disable_sram_shut_down(struct dce_hwseq *hws) +{ + if (REG(DC_MEM_GLOBAL_PWR_REQ_CNTL)) + REG_UPDATE(DC_MEM_GLOBAL_PWR_REQ_CNTL, + DC_MEM_GLOBAL_PWR_REQ_DIS, 1); +} + +static void dce_underlay_clock_enable(struct dce_hwseq *hws) +{ + /* todo: why do we need this at boot? is dce_enable_fe_clock enough? */ + if (REG(DCFEV_CLOCK_CONTROL)) + REG_UPDATE(DCFEV_CLOCK_CONTROL, + DCFEV_CLOCK_ENABLE, 1); +} + +static void enable_hw_base_light_sleep(void) +{ + /* TODO: implement */ +} + +static void disable_sw_manual_control_light_sleep(void) +{ + /* TODO: implement */ +} + +void dce_clock_gating_power_up(struct dce_hwseq *hws, + bool enable) +{ + if (enable) { + enable_hw_base_light_sleep(); + disable_sw_manual_control_light_sleep(); + } else { + dce_disable_sram_shut_down(hws); + dce_underlay_clock_enable(hws); + } +} + +void dce_crtc_switch_to_clk_src(struct dce_hwseq *hws, + struct clock_source *clk_src, + unsigned int tg_inst) +{ + if (clk_src->id == CLOCK_SOURCE_ID_DP_DTO) { + REG_UPDATE(PIXEL_RATE_CNTL[tg_inst], + DP_DTO0_ENABLE, 1); + + } else if (clk_src->id >= CLOCK_SOURCE_COMBO_PHY_PLL0) { + uint32_t rate_source = clk_src->id - CLOCK_SOURCE_COMBO_PHY_PLL0; + + REG_UPDATE_2(PHYPLL_PIXEL_RATE_CNTL[tg_inst], + PHYPLL_PIXEL_RATE_SOURCE, rate_source, + PIXEL_RATE_PLL_SOURCE, 0); + + REG_UPDATE(PIXEL_RATE_CNTL[tg_inst], + DP_DTO0_ENABLE, 0); + + } else if (clk_src->id <= CLOCK_SOURCE_ID_PLL2) { + uint32_t rate_source = clk_src->id - CLOCK_SOURCE_ID_PLL0; + + REG_UPDATE_2(PIXEL_RATE_CNTL[tg_inst], + PIXEL_RATE_SOURCE, rate_source, + DP_DTO0_ENABLE, 0); + + if (REG(PHYPLL_PIXEL_RATE_CNTL[tg_inst])) + REG_UPDATE(PHYPLL_PIXEL_RATE_CNTL[tg_inst], + PIXEL_RATE_PLL_SOURCE, 1); + } else { + DC_ERR("unknown clock source"); + } +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h new file mode 100644 index 000000000000..4af8d560a7ee --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h @@ -0,0 +1,250 @@ +/* + * Copyright 2016 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. + * + * Authors: AMD + * + */ +#ifndef __DCE_HWSEQ_H__ +#define __DCE_HWSEQ_H__ + +#include "hw_sequencer.h" + +#define HWSEQ_DCEF_REG_LIST_DCE8() \ + .DCFE_CLOCK_CONTROL[0] = mmCRTC0_CRTC_DCFE_CLOCK_CONTROL, \ + .DCFE_CLOCK_CONTROL[1] = mmCRTC1_CRTC_DCFE_CLOCK_CONTROL, \ + .DCFE_CLOCK_CONTROL[2] = mmCRTC2_CRTC_DCFE_CLOCK_CONTROL, \ + .DCFE_CLOCK_CONTROL[3] = mmCRTC3_CRTC_DCFE_CLOCK_CONTROL, \ + .DCFE_CLOCK_CONTROL[4] = mmCRTC4_CRTC_DCFE_CLOCK_CONTROL, \ + .DCFE_CLOCK_CONTROL[5] = mmCRTC5_CRTC_DCFE_CLOCK_CONTROL + +#define HWSEQ_DCEF_REG_LIST() \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 0), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 1), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 2), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 3), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 4), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 5), \ + SR(DC_MEM_GLOBAL_PWR_REQ_CNTL) + +#define HWSEQ_BLND_REG_LIST() \ + SRII(BLND_V_UPDATE_LOCK, BLND, 0), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 1), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 2), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 3), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 4), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 5), \ + SRII(BLND_CONTROL, BLND, 0), \ + SRII(BLND_CONTROL, BLND, 1), \ + SRII(BLND_CONTROL, BLND, 2), \ + SRII(BLND_CONTROL, BLND, 3), \ + SRII(BLND_CONTROL, BLND, 4), \ + SRII(BLND_CONTROL, BLND, 5) + +#define HWSEQ_PIXEL_RATE_REG_LIST(blk) \ + SRII(PIXEL_RATE_CNTL, blk, 0), \ + SRII(PIXEL_RATE_CNTL, blk, 1), \ + SRII(PIXEL_RATE_CNTL, blk, 2), \ + SRII(PIXEL_RATE_CNTL, blk, 3), \ + SRII(PIXEL_RATE_CNTL, blk, 4), \ + SRII(PIXEL_RATE_CNTL, blk, 5) + +#define HWSEQ_PHYPLL_REG_LIST(blk) \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 0), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 1), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 2), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 3), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 4), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 5) + +#define HWSEQ_DCE11_REG_LIST_BASE() \ + SR(DC_MEM_GLOBAL_PWR_REQ_CNTL), \ + SR(DCFEV_CLOCK_CONTROL), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 0), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 1), \ + SRII(CRTC_H_BLANK_START_END, CRTC, 0),\ + SRII(CRTC_H_BLANK_START_END, CRTC, 1),\ + SRII(BLND_V_UPDATE_LOCK, BLND, 0),\ + SRII(BLND_V_UPDATE_LOCK, BLND, 1),\ + SRII(BLND_CONTROL, BLND, 0),\ + SRII(BLND_CONTROL, BLND, 1),\ + SR(BLNDV_CONTROL),\ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC) + +#define HWSEQ_DCE8_REG_LIST() \ + HWSEQ_DCEF_REG_LIST_DCE8(), \ + HWSEQ_BLND_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC) + +#define HWSEQ_DCE10_REG_LIST() \ + HWSEQ_DCEF_REG_LIST(), \ + HWSEQ_BLND_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC) + +#define HWSEQ_ST_REG_LIST() \ + HWSEQ_DCE11_REG_LIST_BASE(), \ + .DCFE_CLOCK_CONTROL[2] = mmDCFEV_CLOCK_CONTROL, \ + .CRTC_H_BLANK_START_END[2] = mmCRTCV_H_BLANK_START_END, \ + .BLND_V_UPDATE_LOCK[2] = mmBLNDV_V_UPDATE_LOCK, \ + .BLND_CONTROL[2] = mmBLNDV_CONTROL, + +#define HWSEQ_CZ_REG_LIST() \ + HWSEQ_DCE11_REG_LIST_BASE(), \ + SRII(DCFE_CLOCK_CONTROL, DCFE, 2), \ + SRII(CRTC_H_BLANK_START_END, CRTC, 2), \ + SRII(BLND_V_UPDATE_LOCK, BLND, 2), \ + SRII(BLND_CONTROL, BLND, 2), \ + .DCFE_CLOCK_CONTROL[3] = mmDCFEV_CLOCK_CONTROL, \ + .CRTC_H_BLANK_START_END[3] = mmCRTCV_H_BLANK_START_END, \ + .BLND_V_UPDATE_LOCK[3] = mmBLNDV_V_UPDATE_LOCK, \ + .BLND_CONTROL[3] = mmBLNDV_CONTROL + +#define HWSEQ_DCE112_REG_LIST() \ + HWSEQ_DCE10_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC), \ + HWSEQ_PHYPLL_REG_LIST(CRTC) + +struct dce_hwseq_registers { + uint32_t DCFE_CLOCK_CONTROL[6]; + uint32_t DCFEV_CLOCK_CONTROL; + uint32_t DC_MEM_GLOBAL_PWR_REQ_CNTL; + uint32_t BLND_V_UPDATE_LOCK[6]; + uint32_t BLND_CONTROL[6]; + uint32_t BLNDV_CONTROL; + + uint32_t CRTC_H_BLANK_START_END[6]; + uint32_t PIXEL_RATE_CNTL[6]; + uint32_t PHYPLL_PIXEL_RATE_CNTL[6]; +}; + /* set field name */ +#define HWS_SF(blk_name, reg_name, field_name, post_fix)\ + .field_name = blk_name ## reg_name ## __ ## field_name ## post_fix + +#define HWS_SF1(blk_name, reg_name, field_name, post_fix)\ + .field_name = blk_name ## reg_name ## __ ## blk_name ## field_name ## post_fix + + +#define HWSEQ_DCEF_MASK_SH_LIST(mask_sh, blk)\ + HWS_SF(blk, CLOCK_CONTROL, DCFE_CLOCK_ENABLE, mask_sh),\ + SF(DC_MEM_GLOBAL_PWR_REQ_CNTL, DC_MEM_GLOBAL_PWR_REQ_DIS, mask_sh) + +#define HWSEQ_BLND_MASK_SH_LIST(mask_sh, blk)\ + HWS_SF(blk, V_UPDATE_LOCK, BLND_DCP_GRPH_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(blk, V_UPDATE_LOCK, BLND_SCL_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(blk, V_UPDATE_LOCK, BLND_DCP_GRPH_SURF_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(blk, V_UPDATE_LOCK, BLND_BLND_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(blk, V_UPDATE_LOCK, BLND_V_UPDATE_LOCK_MODE, mask_sh),\ + HWS_SF(blk, CONTROL, BLND_FEEDTHROUGH_EN, mask_sh),\ + HWS_SF(blk, CONTROL, BLND_ALPHA_MODE, mask_sh),\ + HWS_SF(blk, CONTROL, BLND_MODE, mask_sh),\ + HWS_SF(blk, CONTROL, BLND_MULTIPLIED_MODE, mask_sh) + +#define HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, blk)\ + HWS_SF1(blk, PIXEL_RATE_CNTL, PIXEL_RATE_SOURCE, mask_sh),\ + HWS_SF(blk, PIXEL_RATE_CNTL, DP_DTO0_ENABLE, mask_sh) + +#define HWSEQ_PHYPLL_MASK_SH_LIST(mask_sh, blk)\ + HWS_SF1(blk, PHYPLL_PIXEL_RATE_CNTL, PHYPLL_PIXEL_RATE_SOURCE, mask_sh),\ + HWS_SF1(blk, PHYPLL_PIXEL_RATE_CNTL, PIXEL_RATE_PLL_SOURCE, mask_sh) + +#define HWSEQ_DCE8_MASK_SH_LIST(mask_sh)\ + .DCFE_CLOCK_ENABLE = CRTC_DCFE_CLOCK_CONTROL__CRTC_DCFE_CLOCK_ENABLE ## mask_sh, \ + HWS_SF(BLND_, V_UPDATE_LOCK, BLND_DCP_GRPH_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(BLND_, V_UPDATE_LOCK, BLND_SCL_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(BLND_, V_UPDATE_LOCK, BLND_DCP_GRPH_SURF_V_UPDATE_LOCK, mask_sh),\ + HWS_SF(BLND_, CONTROL, BLND_MODE, mask_sh),\ + HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, CRTC0_) + +#define HWSEQ_DCE10_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCEF_MASK_SH_LIST(mask_sh, DCFE_),\ + HWSEQ_BLND_MASK_SH_LIST(mask_sh, BLND_),\ + HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, CRTC0_) + +#define HWSEQ_DCE11_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCE10_MASK_SH_LIST(mask_sh),\ + SF(DCFEV_CLOCK_CONTROL, DCFEV_CLOCK_ENABLE, mask_sh),\ + HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, CRTC0_) + +#define HWSEQ_DCE112_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCE10_MASK_SH_LIST(mask_sh),\ + HWSEQ_PHYPLL_MASK_SH_LIST(mask_sh, CRTC0_) + +#define HWSEQ_REG_FIED_LIST(type) \ + type DCFE_CLOCK_ENABLE; \ + type DCFEV_CLOCK_ENABLE; \ + type DC_MEM_GLOBAL_PWR_REQ_DIS; \ + type BLND_DCP_GRPH_V_UPDATE_LOCK; \ + type BLND_SCL_V_UPDATE_LOCK; \ + type BLND_DCP_GRPH_SURF_V_UPDATE_LOCK; \ + type BLND_BLND_V_UPDATE_LOCK; \ + type BLND_V_UPDATE_LOCK_MODE; \ + type BLND_FEEDTHROUGH_EN; \ + type BLND_ALPHA_MODE; \ + type BLND_MODE; \ + type BLND_MULTIPLIED_MODE; \ + type DP_DTO0_ENABLE; \ + type PIXEL_RATE_SOURCE; \ + type PHYPLL_PIXEL_RATE_SOURCE; \ + type PIXEL_RATE_PLL_SOURCE; \ + +struct dce_hwseq_shift { + HWSEQ_REG_FIED_LIST(uint8_t) +}; + +struct dce_hwseq_mask { + HWSEQ_REG_FIED_LIST(uint32_t) +}; + +struct dce_hwseq_wa { + bool blnd_crtc_trigger; +}; + +struct dce_hwseq { + struct dc_context *ctx; + const struct dce_hwseq_registers *regs; + const struct dce_hwseq_shift *shifts; + const struct dce_hwseq_mask *masks; + struct dce_hwseq_wa wa; +}; + +enum blnd_mode { + BLND_MODE_CURRENT_PIPE = 0,/* Data from current pipe only */ + BLND_MODE_OTHER_PIPE, /* Data from other pipe only */ + BLND_MODE_BLENDING,/* Alpha blending - blend 'current' and 'other' */ +}; + +void dce_enable_fe_clock(struct dce_hwseq *hwss, + unsigned int inst, bool enable); + +void dce_pipe_control_lock(struct dce_hwseq *hws, + unsigned int blnd_inst, + enum pipe_lock_control control_mask, + bool lock); + +void dce_set_blender_mode(struct dce_hwseq *hws, + unsigned int blnd_inst, enum blnd_mode mode); + +void dce_clock_gating_power_up(struct dce_hwseq *hws, + bool enable); + +void dce_crtc_switch_to_clk_src(struct dce_hwseq *hws, + struct clock_source *clk_src, + unsigned int tg_inst); +#endif /*__DCE_HWSEQ_H__*/ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c new file mode 100644 index 000000000000..86e55d028cbf --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c @@ -0,0 +1,2176 @@ +/* + * Copyright 2012-15 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. + * + * Authors: AMD + * + */ + +#include "reg_helper.h" + +#include "core_types.h" +#include "link_encoder.h" +#include "dce_link_encoder.h" +#include "stream_encoder.h" +#include "i2caux_interface.h" +#include "dc_bios_types.h" + +#include "gpio_service_interface.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "dce/dce_11_0_enum.h" + +#ifndef ATOM_S2_CURRENT_BL_LEVEL_MASK +#define ATOM_S2_CURRENT_BL_LEVEL_MASK 0x0000FF00L +#define ATOM_S2_VRI_BRIGHT_ENABLE 0x20000000L +#endif + +#ifndef ATOM_S2_CURRENT_BL_LEVEL_SHIFT +#define ATOM_S2_CURRENT_BL_LEVEL_SHIFT 8 +#endif + +#ifndef HPD0_DC_HPD_CONTROL__DC_HPD_EN_MASK +#define HPD0_DC_HPD_CONTROL__DC_HPD_EN_MASK 0x10000000L +#endif + +#ifndef HPD0_DC_HPD_CONTROL__DC_HPD_EN__SHIFT +#define HPD0_DC_HPD_CONTROL__DC_HPD_EN__SHIFT 0x1c +#endif + +#define CTX \ + enc110->base.ctx + +#define REG(reg)\ + (enc110->link_regs->reg) + +#define AUX_REG(reg)\ + (enc110->aux_regs->reg) + +#define HPD_REG(reg)\ + (enc110->hpd_regs->reg) + +/* For current ASICs pixel clock - 600MHz */ +#define MAX_ENCODER_CLK 600000 + +#define DCE11_UNIPHY_MAX_PIXEL_CLK_IN_KHZ 594000 + +#define DEFAULT_AUX_MAX_DATA_SIZE 16 +#define AUX_MAX_DEFER_WRITE_RETRY 20 +/* + * @brief + * Trigger Source Select + * ASIC-dependent, actual values for register programming + */ +#define DCE110_DIG_FE_SOURCE_SELECT_INVALID 0x0 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGA 0x1 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGB 0x2 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGC 0x4 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGD 0x08 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGE 0x10 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGF 0x20 + +/* all values are in milliseconds */ +/* For eDP, after power-up/power/down, + * 300/500 msec max. delay from LCDVCC to black video generation */ +#define PANEL_POWER_UP_TIMEOUT 300 +#define PANEL_POWER_DOWN_TIMEOUT 500 +#define HPD_CHECK_INTERVAL 10 + +/* Minimum pixel clock, in KHz. For TMDS signal is 25.00 MHz */ +#define TMDS_MIN_PIXEL_CLOCK 25000 +/* Maximum pixel clock, in KHz. For TMDS signal is 165.00 MHz */ +#define TMDS_MAX_PIXEL_CLOCK 165000 +/* For current ASICs pixel clock - 600MHz */ +#define MAX_ENCODER_CLOCK 600000 + +/* Set the ABM Pipe */ +#define MCP_ABM_PIPE_SET 0x66 +/* Set the ABM level */ +#define MCP_ABM_LEVEL_SET 0x65 +/* Set backlight level */ +#define MCP_BL_SET 0x67 + +/* PSR related commands */ +#define PSR_ENABLE 0x20 +#define PSR_EXIT 0x21 +#define PSR_SET 0x23 + +/*TODO: Used for psr wakeup for set backlight level*/ +static unsigned int psr_crtc_offset; + +/* registers setting needs to be save and restored used at InitBacklight */ +static struct dce110_abm_backlight_registers stored_backlight_registers; + +enum { + DP_MST_UPDATE_MAX_RETRY = 50 +}; + +#define DIG_REG(reg)\ + (reg + enc110->offsets.dig) + +#define DP_REG(reg)\ + (reg + enc110->offsets.dp) + +static const struct link_encoder_funcs dce110_lnk_enc_funcs = { + .validate_output_with_stream = + dce110_link_encoder_validate_output_with_stream, + .hw_init = dce110_link_encoder_hw_init, + .setup = dce110_link_encoder_setup, + .enable_tmds_output = dce110_link_encoder_enable_tmds_output, + .enable_dp_output = dce110_link_encoder_enable_dp_output, + .enable_dp_mst_output = dce110_link_encoder_enable_dp_mst_output, + .disable_output = dce110_link_encoder_disable_output, + .dp_set_lane_settings = dce110_link_encoder_dp_set_lane_settings, + .dp_set_phy_pattern = dce110_link_encoder_dp_set_phy_pattern, + .update_mst_stream_allocation_table = + dce110_link_encoder_update_mst_stream_allocation_table, + .set_lcd_backlight_level = dce110_link_encoder_set_lcd_backlight_level, + .set_dmcu_backlight_level = + dce110_link_encoder_set_dmcu_backlight_level, + .init_dmcu_backlight_settings = + dce110_link_encoder_init_dmcu_backlight_settings, + .set_dmcu_abm_level = dce110_link_encoder_set_dmcu_abm_level, + .set_dmcu_psr_enable = dce110_link_encoder_set_dmcu_psr_enable, + .setup_dmcu_psr = dce110_link_encoder_setup_dmcu_psr, + .backlight_control = dce110_link_encoder_edp_backlight_control, + .power_control = dce110_link_encoder_edp_power_control, + .connect_dig_be_to_fe = dce110_link_encoder_connect_dig_be_to_fe, + .enable_hpd = dce110_link_encoder_enable_hpd, + .disable_hpd = dce110_link_encoder_disable_hpd, + .destroy = dce110_link_encoder_destroy +}; + + +static enum bp_result link_transmitter_control( + struct dce110_link_encoder *enc110, + struct bp_transmitter_control *cntl) +{ + enum bp_result result; + struct dc_bios *bp = enc110->base.ctx->dc_bios; + + result = bp->funcs->transmitter_control(bp, cntl); + + return result; +} + +static void enable_phy_bypass_mode( + struct dce110_link_encoder *enc110, + bool enable) +{ + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_UPDATE(DP_DPHY_CNTL, DPHY_BYPASS, enable); + +} + +static void disable_prbs_symbols( + struct dce110_link_encoder *enc110, + bool disable) +{ + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_UPDATE_4(DP_DPHY_CNTL, + DPHY_ATEST_SEL_LANE0, disable, + DPHY_ATEST_SEL_LANE1, disable, + DPHY_ATEST_SEL_LANE2, disable, + DPHY_ATEST_SEL_LANE3, disable); +} + +static void disable_prbs_mode( + struct dce110_link_encoder *enc110) +{ + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_UPDATE(DP_DPHY_PRBS_CNTL, DPHY_PRBS_EN, 0); +} + +static void program_pattern_symbols( + struct dce110_link_encoder *enc110, + uint16_t pattern_symbols[8]) +{ + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_SET_3(DP_DPHY_SYM0, 0, + DPHY_SYM1, pattern_symbols[0], + DPHY_SYM2, pattern_symbols[1], + DPHY_SYM3, pattern_symbols[2]); + + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_SET_3(DP_DPHY_SYM1, 0, + DPHY_SYM4, pattern_symbols[3], + DPHY_SYM5, pattern_symbols[4], + DPHY_SYM6, pattern_symbols[5]); + + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_SET_2(DP_DPHY_SYM2, 0, + DPHY_SYM7, pattern_symbols[6], + DPHY_SYM8, pattern_symbols[7]); +} + +static void set_dp_phy_pattern_d102( + struct dce110_link_encoder *enc110) +{ + /* Disable PHY Bypass mode to setup the test pattern */ + enable_phy_bypass_mode(enc110, false); + + /* For 10-bit PRBS or debug symbols + * please use the following sequence: */ + + /* Enable debug symbols on the lanes */ + + disable_prbs_symbols(enc110, true); + + /* Disable PRBS mode, + * make sure DPHY_PRBS_CNTL.DPHY_PRBS_EN=0 */ + + disable_prbs_mode(enc110); + + /* Program debug symbols to be output */ + { + uint16_t pattern_symbols[8] = { + 0x2AA, 0x2AA, 0x2AA, 0x2AA, + 0x2AA, 0x2AA, 0x2AA, 0x2AA + }; + + program_pattern_symbols(enc110, pattern_symbols); + } + + /* Enable phy bypass mode to enable the test pattern */ + + enable_phy_bypass_mode(enc110, true); +} + +static void set_link_training_complete( + struct dce110_link_encoder *enc110, + bool complete) +{ + /* This register resides in DP back end block; + * transmitter is used for the offset */ + + REG_UPDATE(DP_LINK_CNTL, DP_LINK_TRAINING_COMPLETE, complete); + +} + +void dce110_link_encoder_set_dp_phy_pattern_training_pattern( + struct link_encoder *enc, + uint32_t index) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + /* Write Training Pattern */ + + REG_WRITE(DP_DPHY_TRAINING_PATTERN_SEL, index); + + /* Set HW Register Training Complete to false */ + + set_link_training_complete(enc110, false); + + /* Disable PHY Bypass mode to output Training Pattern */ + + enable_phy_bypass_mode(enc110, false); + + /* Disable PRBS mode, + * make sure DPHY_PRBS_CNTL.DPHY_PRBS_EN=0 */ + + disable_prbs_mode(enc110); +} + +static void set_dp_phy_pattern_symbol_error( + struct dce110_link_encoder *enc110) +{ + /* Disable PHY Bypass mode to setup the test pattern */ + uint32_t value = 0x0; + + enable_phy_bypass_mode(enc110, false); + + /* program correct panel mode*/ + { + ASSERT(REG(DP_DPHY_INTERNAL_CTRL)); + /*DCE 120 does not have this reg*/ + + REG_WRITE(DP_DPHY_INTERNAL_CTRL, value); + } + + /* A PRBS23 pattern is used for most DP electrical measurements. */ + + /* Enable PRBS symbols on the lanes */ + + disable_prbs_symbols(enc110, false); + + /* For PRBS23 Set bit DPHY_PRBS_SEL=1 and Set bit DPHY_PRBS_EN=1 */ + { + REG_UPDATE_2(DP_DPHY_PRBS_CNTL, + DPHY_PRBS_SEL, 1, + DPHY_PRBS_EN, 1); + } + + /* Enable phy bypass mode to enable the test pattern */ + + enable_phy_bypass_mode(enc110, true); +} + +static void set_dp_phy_pattern_prbs7( + struct dce110_link_encoder *enc110) +{ + /* Disable PHY Bypass mode to setup the test pattern */ + + enable_phy_bypass_mode(enc110, false); + + /* A PRBS7 pattern is used for most DP electrical measurements. */ + + /* Enable PRBS symbols on the lanes */ + + disable_prbs_symbols(enc110, false); + + /* For PRBS7 Set bit DPHY_PRBS_SEL=0 and Set bit DPHY_PRBS_EN=1 */ + { + REG_UPDATE_2(DP_DPHY_PRBS_CNTL, + DPHY_PRBS_SEL, 0, + DPHY_PRBS_EN, 1); + } + + /* Enable phy bypass mode to enable the test pattern */ + + enable_phy_bypass_mode(enc110, true); +} + +static void set_dp_phy_pattern_80bit_custom( + struct dce110_link_encoder *enc110, + const uint8_t *pattern) +{ + /* Disable PHY Bypass mode to setup the test pattern */ + enable_phy_bypass_mode(enc110, false); + + /* Enable debug symbols on the lanes */ + + disable_prbs_symbols(enc110, true); + + /* Enable PHY bypass mode to enable the test pattern */ + /* TODO is it really needed ? */ + + enable_phy_bypass_mode(enc110, true); + + /* Program 80 bit custom pattern */ + { + uint16_t pattern_symbols[8]; + + pattern_symbols[0] = + ((pattern[1] & 0x03) << 8) | pattern[0]; + pattern_symbols[1] = + ((pattern[2] & 0x0f) << 6) | ((pattern[1] >> 2) & 0x3f); + pattern_symbols[2] = + ((pattern[3] & 0x3f) << 4) | ((pattern[2] >> 4) & 0x0f); + pattern_symbols[3] = + (pattern[4] << 2) | ((pattern[3] >> 6) & 0x03); + pattern_symbols[4] = + ((pattern[6] & 0x03) << 8) | pattern[5]; + pattern_symbols[5] = + ((pattern[7] & 0x0f) << 6) | ((pattern[6] >> 2) & 0x3f); + pattern_symbols[6] = + ((pattern[8] & 0x3f) << 4) | ((pattern[7] >> 4) & 0x0f); + pattern_symbols[7] = + (pattern[9] << 2) | ((pattern[8] >> 6) & 0x03); + + program_pattern_symbols(enc110, pattern_symbols); + } + + /* Enable phy bypass mode to enable the test pattern */ + + enable_phy_bypass_mode(enc110, true); +} + +static void set_dp_phy_pattern_hbr2_compliance( + struct dce110_link_encoder *enc110) +{ + + /* previously there is a register DP_HBR2_EYE_PATTERN + * that is enabled to get the pattern. + * But it does not work with the latest spec change, + * so we are programming the following registers manually. + * + * The following settings have been confirmed + * by Nick Chorney and Sandra Liu */ + + /* Disable PHY Bypass mode to setup the test pattern */ + + enable_phy_bypass_mode(enc110, false); + + /* Setup DIG encoder in DP SST mode */ + + enc110->base.funcs->setup(&enc110->base, SIGNAL_TYPE_DISPLAY_PORT); + + /* program correct panel mode*/ + { + ASSERT(REG(DP_DPHY_INTERNAL_CTRL)); + + REG_WRITE(DP_DPHY_INTERNAL_CTRL, 0x0); + } + + /* no vbid after BS (SR) + * DP_LINK_FRAMING_CNTL changed history Sandra Liu + * 11000260 / 11000104 / 110000FC */ + + /* TODO DP_LINK_FRAMING_CNTL should always use hardware default value + * output except output hbr2_compliance pattern for physical PHY + * measurement. This is not normal usage case. SW should reset this + * register to hardware default value after end use of HBR2 eye + */ + BREAK_TO_DEBUGGER(); + /* TODO: do we still need this, find out at compliance test + addr = mmDP_LINK_FRAMING_CNTL + fe_addr_offset; + + value = dal_read_reg(ctx, addr); + + set_reg_field_value(value, 0xFC, + DP_LINK_FRAMING_CNTL, DP_IDLE_BS_INTERVAL); + set_reg_field_value(value, 1, + DP_LINK_FRAMING_CNTL, DP_VBID_DISABLE); + set_reg_field_value(value, 1, + DP_LINK_FRAMING_CNTL, DP_VID_ENHANCED_FRAME_MODE); + + dal_write_reg(ctx, addr, value); + */ + /* swap every BS with SR */ + + REG_UPDATE(DP_DPHY_SCRAM_CNTL, DPHY_SCRAMBLER_BS_COUNT, 0); + + /*TODO add support for this test pattern + * support_dp_hbr2_eye_pattern + */ + + /* set link training complete */ + set_link_training_complete(enc110, true); + /* do not enable video stream */ + + REG_UPDATE(DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, 0); + + /* Disable PHY Bypass mode to setup the test pattern */ + + enable_phy_bypass_mode(enc110, false); +} + +static void set_dp_phy_pattern_passthrough_mode( + struct dce110_link_encoder *enc110, + enum dp_panel_mode panel_mode) +{ + uint32_t value; + + /* program correct panel mode */ + { + ASSERT(REG(DP_DPHY_INTERNAL_CTRL)); + value = REG_READ(DP_DPHY_INTERNAL_CTRL); + + switch (panel_mode) { + case DP_PANEL_MODE_EDP: + value = 0x1; + break; + case DP_PANEL_MODE_SPECIAL: + value = 0x11; + break; + default: + value = 0x0; + break; + } + + REG_WRITE(DP_DPHY_INTERNAL_CTRL, value); + } + + REG_UPDATE(DP_DPHY_SCRAM_CNTL, DPHY_SCRAMBLER_BS_COUNT, 0x1FF); + + /* set link training complete */ + + set_link_training_complete(enc110, true); + + /* Disable PHY Bypass mode to setup the test pattern */ + + enable_phy_bypass_mode(enc110, false); + + /* Disable PRBS mode, + * make sure DPHY_PRBS_CNTL.DPHY_PRBS_EN=0 */ + + disable_prbs_mode(enc110); +} + +/* return value is bit-vector */ +static uint8_t get_frontend_source( + enum engine_id engine) +{ + switch (engine) { + case ENGINE_ID_DIGA: + return DCE110_DIG_FE_SOURCE_SELECT_DIGA; + case ENGINE_ID_DIGB: + return DCE110_DIG_FE_SOURCE_SELECT_DIGB; + case ENGINE_ID_DIGC: + return DCE110_DIG_FE_SOURCE_SELECT_DIGC; + case ENGINE_ID_DIGD: + return DCE110_DIG_FE_SOURCE_SELECT_DIGD; + case ENGINE_ID_DIGE: + return DCE110_DIG_FE_SOURCE_SELECT_DIGE; + case ENGINE_ID_DIGF: + return DCE110_DIG_FE_SOURCE_SELECT_DIGF; + default: + ASSERT_CRITICAL(false); + return DCE110_DIG_FE_SOURCE_SELECT_INVALID; + } +} + +static void configure_encoder( + struct dce110_link_encoder *enc110, + const struct dc_link_settings *link_settings) +{ + /* set number of lanes */ + + REG_SET(DP_CONFIG, 0, + DP_UDI_LANES, link_settings->lane_count - LANE_COUNT_ONE); + + /* setup scrambler */ + REG_UPDATE(DP_DPHY_SCRAM_CNTL, DPHY_SCRAMBLER_ADVANCE, 1); +} + +static bool is_panel_powered_on(struct dce110_link_encoder *enc110) +{ + bool ret; + uint32_t value; + + REG_GET(LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, &value); + ret = value; + + return ret == 1; +} + + +/* TODO duplicate of dc_link.c version */ +static struct gpio *get_hpd_gpio(const struct link_encoder *enc) +{ + enum bp_result bp_result; + struct dc_bios *dcb = enc->ctx->dc_bios; + struct graphics_object_hpd_info hpd_info; + struct gpio_pin_info pin_info; + + if (dcb->funcs->get_hpd_info(dcb, enc->connector, &hpd_info) != BP_RESULT_OK) + return NULL; + + bp_result = dcb->funcs->get_gpio_pin_info(dcb, + hpd_info.hpd_int_gpio_uid, &pin_info); + + if (bp_result != BP_RESULT_OK) { + ASSERT(bp_result == BP_RESULT_NORECORD); + return NULL; + } + + return dal_gpio_service_create_irq( + enc->ctx->gpio_service, + pin_info.offset, + pin_info.mask); +} + +/* + * @brief + * eDP only. + */ +static void link_encoder_edp_wait_for_hpd_ready( + struct dce110_link_encoder *enc110, + bool power_up) +{ + struct dc_context *ctx = enc110->base.ctx; + struct graphics_object_id connector = enc110->base.connector; + struct gpio *hpd; + bool edp_hpd_high = false; + uint32_t time_elapsed = 0; + uint32_t timeout = power_up ? + PANEL_POWER_UP_TIMEOUT : PANEL_POWER_DOWN_TIMEOUT; + + if (dal_graphics_object_id_get_connector_id(connector) != + CONNECTOR_ID_EDP) { + BREAK_TO_DEBUGGER(); + return; + } + + if (!power_up) + /* from KV, we will not HPD low after turning off VCC - + * instead, we will check the SW timer in power_up(). */ + return; + + /* when we power on/off the eDP panel, + * we need to wait until SENSE bit is high/low */ + + /* obtain HPD */ + /* TODO what to do with this? */ + hpd = get_hpd_gpio(&enc110->base); + + if (!hpd) { + BREAK_TO_DEBUGGER(); + return; + } + + dal_gpio_open(hpd, GPIO_MODE_INTERRUPT); + + /* wait until timeout or panel detected */ + + do { + uint32_t detected = 0; + + dal_gpio_get_value(hpd, &detected); + + if (!(detected ^ power_up)) { + edp_hpd_high = true; + break; + } + + msleep(HPD_CHECK_INTERVAL); + + time_elapsed += HPD_CHECK_INTERVAL; + } while (time_elapsed < timeout); + + dal_gpio_close(hpd); + + dal_gpio_destroy_irq(&hpd); + + if (false == edp_hpd_high) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: wait timed out!\n", __func__); + } +} + +/* + * @brief + * eDP only. Control the power of the eDP panel. + */ +void dce110_link_encoder_edp_power_control( + struct link_encoder *enc, + bool power_up) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result bp_result; + + if (dal_graphics_object_id_get_connector_id(enc110->base.connector) != + CONNECTOR_ID_EDP) { + BREAK_TO_DEBUGGER(); + return; + } + + if ((power_up && !is_panel_powered_on(enc110)) || + (!power_up && is_panel_powered_on(enc110))) { + + /* Send VBIOS command to prompt eDP panel power */ + + dm_logger_write(ctx->logger, LOG_HW_RESUME_S3, + "%s: Panel Power action: %s\n", + __func__, (power_up ? "On":"Off")); + + cntl.action = power_up ? + TRANSMITTER_CONTROL_POWER_ON : + TRANSMITTER_CONTROL_POWER_OFF; + cntl.transmitter = enc110->base.transmitter; + cntl.connector_obj_id = enc110->base.connector; + cntl.coherent = false; + cntl.lanes_number = LANE_COUNT_FOUR; + cntl.hpd_sel = enc110->base.hpd_source; + + bp_result = link_transmitter_control(enc110, &cntl); + + if (BP_RESULT_OK != bp_result) { + + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Panel Power bp_result: %d\n", + __func__, bp_result); + } + } else { + dm_logger_write(ctx->logger, LOG_HW_RESUME_S3, + "%s: Skipping Panel Power action: %s\n", + __func__, (power_up ? "On":"Off")); + } + + link_encoder_edp_wait_for_hpd_ready(enc110, true); +} + +static void aux_initialize( + struct dce110_link_encoder *enc110) +{ + struct dc_context *ctx = enc110->base.ctx; + enum hpd_source_id hpd_source = enc110->base.hpd_source; + uint32_t addr = AUX_REG(AUX_CONTROL); + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value(value, hpd_source, AUX_CONTROL, AUX_HPD_SEL); + set_reg_field_value(value, 0, AUX_CONTROL, AUX_LS_READ_EN); + dm_write_reg(ctx, addr, value); + + addr = AUX_REG(AUX_DPHY_RX_CONTROL0); + value = dm_read_reg(ctx, addr); + + /* 1/4 window (the maximum allowed) */ + set_reg_field_value(value, 1, + AUX_DPHY_RX_CONTROL0, AUX_RX_RECEIVE_WINDOW); + dm_write_reg(ctx, addr, value); + +} + +/*todo: cloned in stream enc, fix*/ +static bool is_panel_backlight_on(struct dce110_link_encoder *enc110) +{ + uint32_t value; + + REG_GET(LVTMA_PWRSEQ_CNTL, LVTMA_BLON, &value); + + return value; +} + +/*todo: cloned in stream enc, fix*/ +/* + * @brief + * eDP only. Control the backlight of the eDP panel + */ +void dce110_link_encoder_edp_backlight_control( + struct link_encoder *enc, + bool enable) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + + if (dal_graphics_object_id_get_connector_id(enc110->base.connector) + != CONNECTOR_ID_EDP) { + BREAK_TO_DEBUGGER(); + return; + } + + if (enable && is_panel_backlight_on(enc110)) { + dm_logger_write(ctx->logger, LOG_HW_RESUME_S3, + "%s: panel already powered up. Do nothing.\n", + __func__); + return; + } + + if (!enable && !is_panel_powered_on(enc110)) { + dm_logger_write(ctx->logger, LOG_HW_RESUME_S3, + "%s: panel already powered down. Do nothing.\n", + __func__); + return; + } + + /* Send VBIOS command to control eDP panel backlight */ + + dm_logger_write(ctx->logger, LOG_HW_RESUME_S3, + "%s: backlight action: %s\n", + __func__, (enable ? "On":"Off")); + + cntl.action = enable ? + TRANSMITTER_CONTROL_BACKLIGHT_ON : + TRANSMITTER_CONTROL_BACKLIGHT_OFF; + /*cntl.engine_id = ctx->engine;*/ + cntl.transmitter = enc110->base.transmitter; + cntl.connector_obj_id = enc110->base.connector; + /*todo: unhardcode*/ + cntl.lanes_number = LANE_COUNT_FOUR; + cntl.hpd_sel = enc110->base.hpd_source; + + /* For eDP, the following delays might need to be considered + * after link training completed: + * idle period - min. accounts for required BS-Idle pattern, + * max. allows for source frame synchronization); + * 50 msec max. delay from valid video data from source + * to video on dislpay or backlight enable. + * + * Disable the delay for now. + * Enable it in the future if necessary. + */ + /* dc_service_sleep_in_milliseconds(50); */ + link_transmitter_control(enc110, &cntl); +} + +static bool is_dig_enabled(const struct dce110_link_encoder *enc110) +{ + uint32_t value; + + REG_GET(DIG_BE_EN_CNTL, DIG_ENABLE, &value); + return value; +} + +static void link_encoder_disable(struct dce110_link_encoder *enc110) +{ + /* reset training pattern */ + REG_SET(DP_DPHY_TRAINING_PATTERN_SEL, 0, + DPHY_TRAINING_PATTERN_SEL, 0); + + /* reset training complete */ + REG_UPDATE(DP_LINK_CNTL, DP_LINK_TRAINING_COMPLETE, 0); + + /* reset panel mode */ + ASSERT(REG(DP_DPHY_INTERNAL_CTRL)); + REG_WRITE(DP_DPHY_INTERNAL_CTRL, 0); +} + +static void hpd_initialize( + struct dce110_link_encoder *enc110) +{ + /* Associate HPD with DIG_BE */ + enum hpd_source_id hpd_source = enc110->base.hpd_source; + + REG_UPDATE(DIG_BE_CNTL, DIG_HPD_SELECT, hpd_source); +} + +bool dce110_link_encoder_validate_dvi_output( + const struct dce110_link_encoder *enc110, + enum signal_type connector_signal, + enum signal_type signal, + const struct dc_crtc_timing *crtc_timing) +{ + uint32_t max_pixel_clock = TMDS_MAX_PIXEL_CLOCK; + + if (enc110->base.features.max_pixel_clock < TMDS_MAX_PIXEL_CLOCK) + max_pixel_clock = enc110->base.features.max_pixel_clock; + + if (signal == SIGNAL_TYPE_DVI_DUAL_LINK) + max_pixel_clock <<= 1; + + /* This handles the case of HDMI downgrade to DVI we don't want to + * we don't want to cap the pixel clock if the DDI is not DVI. + */ + if (connector_signal != SIGNAL_TYPE_DVI_DUAL_LINK && + connector_signal != SIGNAL_TYPE_DVI_SINGLE_LINK) + max_pixel_clock = enc110->base.features.max_pixel_clock; + + /* DVI only support RGB pixel encoding */ + if (crtc_timing->pixel_encoding != PIXEL_ENCODING_RGB) + return false; + + if (crtc_timing->pix_clk_khz < TMDS_MIN_PIXEL_CLOCK) + return false; + + if (crtc_timing->pix_clk_khz > max_pixel_clock) + return false; + + /* DVI supports 6/8bpp single-link and 10/16bpp dual-link */ + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_666: + case COLOR_DEPTH_888: + break; + case COLOR_DEPTH_101010: + case COLOR_DEPTH_161616: + if (signal != SIGNAL_TYPE_DVI_DUAL_LINK) + return false; + break; + default: + return false; + } + + return true; +} + +static bool dce110_link_encoder_validate_hdmi_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing, + int adjusted_pix_clk_khz) +{ + enum dc_color_depth max_deep_color = + enc110->base.features.max_hdmi_deep_color; + + if (max_deep_color > enc110->base.features.max_deep_color) + max_deep_color = enc110->base.features.max_deep_color; + + if (max_deep_color < crtc_timing->display_color_depth) + return false; + + if (adjusted_pix_clk_khz < TMDS_MIN_PIXEL_CLOCK) + return false; + + if ((adjusted_pix_clk_khz == 0) || + (adjusted_pix_clk_khz > enc110->base.features.max_hdmi_pixel_clock) || + (adjusted_pix_clk_khz > enc110->base.features.max_pixel_clock)) + return false; + + /* DCE11 HW does not support 420 */ + if (!enc110->base.features.ycbcr420_supported && + crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) + return false; + + return true; +} + +bool dce110_link_encoder_validate_rgb_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing) +{ + if (crtc_timing->pix_clk_khz > enc110->base.features.max_pixel_clock) + return false; + + if (crtc_timing->pixel_encoding != PIXEL_ENCODING_RGB) + return false; + + return true; +} + +bool dce110_link_encoder_validate_dp_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing) +{ + /* default RGB only */ + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) + return true; + + if (enc110->base.features.flags.bits.IS_YCBCR_CAPABLE) + return true; + + /* for DCE 8.x or later DP Y-only feature, + * we need ASIC cap + FeatureSupportDPYonly, not support 666 */ + if (crtc_timing->flags.Y_ONLY && + enc110->base.features.flags.bits.IS_YCBCR_CAPABLE && + crtc_timing->display_color_depth != COLOR_DEPTH_666) + return true; + + return false; +} + +bool dce110_link_encoder_validate_wireless_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing) +{ + if (crtc_timing->pix_clk_khz > enc110->base.features.max_pixel_clock) + return false; + + /* Wireless only supports YCbCr444 */ + if (crtc_timing->pixel_encoding == + PIXEL_ENCODING_YCBCR444) + return true; + + return false; +} + +bool dce110_link_encoder_construct( + struct dce110_link_encoder *enc110, + const struct encoder_init_data *init_data, + const struct dce110_link_enc_registers *link_regs, + const struct dce110_link_enc_aux_registers *aux_regs, + const struct dce110_link_enc_hpd_registers *hpd_regs) +{ + enc110->base.funcs = &dce110_lnk_enc_funcs; + enc110->base.ctx = init_data->ctx; + enc110->base.id = init_data->encoder; + + enc110->base.hpd_source = init_data->hpd_source; + enc110->base.connector = init_data->connector; + enc110->base.input_signals = SIGNAL_TYPE_ALL; + + enc110->base.preferred_engine = ENGINE_ID_UNKNOWN; + + enc110->base.features.flags.raw = 0; + + enc110->base.transmitter = init_data->transmitter; + + enc110->base.features.flags.bits.IS_AUDIO_CAPABLE = true; + + enc110->base.features.max_pixel_clock = + MAX_ENCODER_CLK; + + enc110->base.features.max_deep_color = COLOR_DEPTH_121212; + enc110->base.features.max_hdmi_deep_color = COLOR_DEPTH_121212; + + /* set the flag to indicate whether driver poll the I2C data pin + * while doing the DP sink detect + */ + +/* if (dal_adapter_service_is_feature_supported(as, + FEATURE_DP_SINK_DETECT_POLL_DATA_PIN)) + enc110->base.features.flags.bits. + DP_SINK_DETECT_POLL_DATA_PIN = true;*/ + + enc110->base.output_signals = + SIGNAL_TYPE_DVI_SINGLE_LINK | + SIGNAL_TYPE_DVI_DUAL_LINK | + SIGNAL_TYPE_LVDS | + SIGNAL_TYPE_DISPLAY_PORT | + SIGNAL_TYPE_DISPLAY_PORT_MST | + SIGNAL_TYPE_EDP | + SIGNAL_TYPE_HDMI_TYPE_A; + + /* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE. + * SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY. + * SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer + * DIG is per UNIPHY and used by SST DP, eDP, HDMI, DVI and LVDS. + * Prefer DIG assignment is decided by board design. + * For DCE 8.0, there are only max 6 UNIPHYs, we assume board design + * and VBIOS will filter out 7 UNIPHY for DCE 8.0. + * By this, adding DIGG should not hurt DCE 8.0. + * This will let DCE 8.1 share DCE 8.0 as much as possible + */ + + enc110->link_regs = link_regs; + enc110->aux_regs = aux_regs; + enc110->hpd_regs = hpd_regs; + + switch (enc110->base.transmitter) { + case TRANSMITTER_UNIPHY_A: + enc110->base.preferred_engine = ENGINE_ID_DIGA; + break; + case TRANSMITTER_UNIPHY_B: + enc110->base.preferred_engine = ENGINE_ID_DIGB; + break; + case TRANSMITTER_UNIPHY_C: + enc110->base.preferred_engine = ENGINE_ID_DIGC; + break; + case TRANSMITTER_UNIPHY_D: + enc110->base.preferred_engine = ENGINE_ID_DIGD; + break; + case TRANSMITTER_UNIPHY_E: + enc110->base.preferred_engine = ENGINE_ID_DIGE; + break; + case TRANSMITTER_UNIPHY_F: + enc110->base.preferred_engine = ENGINE_ID_DIGF; + break; + default: + ASSERT_CRITICAL(false); + enc110->base.preferred_engine = ENGINE_ID_UNKNOWN; + } + + dm_logger_write(init_data->ctx->logger, LOG_I2C_AUX, + "Using channel: %s [%d]\n", + DECODE_CHANNEL_ID(init_data->channel), + init_data->channel); + + /* Override features with DCE-specific values */ + { + struct bp_encoder_cap_info bp_cap_info = {0}; + const struct dc_vbios_funcs *bp_funcs = enc110->base.ctx->dc_bios->funcs; + + if (BP_RESULT_OK == bp_funcs->get_encoder_cap_info( + enc110->base.ctx->dc_bios, enc110->base.id, + &bp_cap_info)) + enc110->base.features.flags.bits.IS_HBR2_CAPABLE = + bp_cap_info.DP_HBR2_CAP; + } + /* test pattern 3 support */ + enc110->base.features.flags.bits.IS_TPS3_CAPABLE = true; + + enc110->base.features.flags.bits.IS_Y_ONLY_CAPABLE = false; + /* + dal_adapter_service_is_feature_supported(as, + FEATURE_SUPPORT_DP_Y_ONLY); +*/ + enc110->base.features.flags.bits.IS_YCBCR_CAPABLE = true; + /* + dal_adapter_service_is_feature_supported(as, + FEATURE_SUPPORT_DP_YUV); + */ + return true; +} + +bool dce110_link_encoder_validate_output_with_stream( + struct link_encoder *enc, + struct pipe_ctx *pipe_ctx) +{ + struct core_stream *stream = pipe_ctx->stream; + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + bool is_valid; + + switch (pipe_ctx->stream->signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + is_valid = dce110_link_encoder_validate_dvi_output( + enc110, + stream->sink->link->public.connector_signal, + pipe_ctx->stream->signal, + &stream->public.timing); + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + is_valid = dce110_link_encoder_validate_hdmi_output( + enc110, + &stream->public.timing, + stream->phy_pix_clk); + break; + case SIGNAL_TYPE_RGB: + is_valid = dce110_link_encoder_validate_rgb_output( + enc110, &stream->public.timing); + break; + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + is_valid = dce110_link_encoder_validate_dp_output( + enc110, &stream->public.timing); + break; + case SIGNAL_TYPE_WIRELESS: + is_valid = dce110_link_encoder_validate_wireless_output( + enc110, &stream->public.timing); + break; + default: + is_valid = true; + break; + } + + return is_valid; +} + +void dce110_link_encoder_hw_init( + struct link_encoder *enc) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + cntl.action = TRANSMITTER_CONTROL_INIT; + cntl.engine_id = ENGINE_ID_UNKNOWN; + cntl.transmitter = enc110->base.transmitter; + cntl.connector_obj_id = enc110->base.connector; + cntl.lanes_number = LANE_COUNT_FOUR; + cntl.coherent = false; + cntl.hpd_sel = enc110->base.hpd_source; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + return; + } + + if (enc110->base.connector.id == CONNECTOR_ID_LVDS) { + cntl.action = TRANSMITTER_CONTROL_BACKLIGHT_BRIGHTNESS; + + result = link_transmitter_control(enc110, &cntl); + + ASSERT(result == BP_RESULT_OK); + + } else if (enc110->base.connector.id == CONNECTOR_ID_EDP) { + enc->funcs->power_control(&enc110->base, true); + } + aux_initialize(enc110); + + /* reinitialize HPD. + * hpd_initialize() will pass DIG_FE id to HW context. + * All other routine within HW context will use fe_engine_offset + * as DIG_FE id even caller pass DIG_FE id. + * So this routine must be called first. */ + hpd_initialize(enc110); +} + +void dce110_link_encoder_destroy(struct link_encoder **enc) +{ + dm_free(TO_DCE110_LINK_ENC(*enc)); + *enc = NULL; +} + +void dce110_link_encoder_setup( + struct link_encoder *enc, + enum signal_type signal) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + + switch (signal) { + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT: + /* DP SST */ + REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 0); + break; + case SIGNAL_TYPE_LVDS: + /* LVDS */ + REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 1); + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + /* TMDS-DVI */ + REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 2); + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + /* TMDS-HDMI */ + REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 3); + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + /* DP MST */ + REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 5); + break; + default: + ASSERT_CRITICAL(false); + /* invalid mode ! */ + break; + } + +} + +/* TODO: still need depth or just pass in adjusted pixel clock? */ +void dce110_link_encoder_enable_tmds_output( + struct link_encoder *enc, + enum clock_source_id clock_source, + enum dc_color_depth color_depth, + bool hdmi, + bool dual_link, + uint32_t pixel_clock) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = ENGINE_ID_UNKNOWN; + cntl.transmitter = enc110->base.transmitter; + cntl.pll_id = clock_source; + if (hdmi) { + cntl.signal = SIGNAL_TYPE_HDMI_TYPE_A; + cntl.lanes_number = 4; + } else if (dual_link) { + cntl.signal = SIGNAL_TYPE_DVI_DUAL_LINK; + cntl.lanes_number = 8; + } else { + cntl.signal = SIGNAL_TYPE_DVI_SINGLE_LINK; + cntl.lanes_number = 4; + } + cntl.hpd_sel = enc110->base.hpd_source; + + cntl.pixel_clock = pixel_clock; + cntl.color_depth = color_depth; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} + +/* enables DP PHY output */ +void dce110_link_encoder_enable_dp_output( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + + /* number_of_lanes is used for pixel clock adjust, + * but it's not passed to asic_control. + * We need to set number of lanes manually. + */ + configure_encoder(enc110, link_settings); + + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = ENGINE_ID_UNKNOWN; + cntl.transmitter = enc110->base.transmitter; + cntl.pll_id = clock_source; + cntl.signal = SIGNAL_TYPE_DISPLAY_PORT; + cntl.lanes_number = link_settings->lane_count; + cntl.hpd_sel = enc110->base.hpd_source; + cntl.pixel_clock = link_settings->link_rate + * LINK_RATE_REF_FREQ_IN_KHZ; + /* TODO: check if undefined works */ + cntl.color_depth = COLOR_DEPTH_UNDEFINED; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} + +/* enables DP PHY output in MST mode */ +void dce110_link_encoder_enable_dp_mst_output( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + + /* number_of_lanes is used for pixel clock adjust, + * but it's not passed to asic_control. + * We need to set number of lanes manually. + */ + configure_encoder(enc110, link_settings); + + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = ENGINE_ID_UNKNOWN; + cntl.transmitter = enc110->base.transmitter; + cntl.pll_id = clock_source; + cntl.signal = SIGNAL_TYPE_DISPLAY_PORT_MST; + cntl.lanes_number = link_settings->lane_count; + cntl.hpd_sel = enc110->base.hpd_source; + cntl.pixel_clock = link_settings->link_rate + * LINK_RATE_REF_FREQ_IN_KHZ; + /* TODO: check if undefined works */ + cntl.color_depth = COLOR_DEPTH_UNDEFINED; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} +/* + * @brief + * Disable transmitter and its encoder + */ +void dce110_link_encoder_disable_output( + struct link_encoder *enc, + enum signal_type signal) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + if (!is_dig_enabled(enc110)) { + /* OF_SKIP_POWER_DOWN_INACTIVE_ENCODER */ + return; + } + /* Power-down RX and disable GPU PHY should be paired. + * Disabling PHY without powering down RX may cause + * symbol lock loss, on which we will get DP Sink interrupt. */ + + /* There is a case for the DP active dongles + * where we want to disable the PHY but keep RX powered, + * for those we need to ignore DP Sink interrupt + * by checking lane count that has been set + * on the last do_enable_output(). */ + + /* disable transmitter */ + cntl.action = TRANSMITTER_CONTROL_DISABLE; + cntl.transmitter = enc110->base.transmitter; + cntl.hpd_sel = enc110->base.hpd_source; + cntl.signal = signal; + cntl.connector_obj_id = enc110->base.connector; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + dm_logger_write(ctx->logger, LOG_ERROR, + "%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + return; + } + + /* disable encoder */ + if (dc_is_dp_signal(signal)) + link_encoder_disable(enc110); + + if (enc110->base.connector.id == CONNECTOR_ID_EDP) { + /* power down eDP panel */ + /* TODO: Power control cause regression, we should implement + * it properly, for now just comment it. + * + * link_encoder_edp_wait_for_hpd_ready( + link_enc, + link_enc->connector, + false); + + * link_encoder_edp_power_control( + link_enc, false); */ + } +} + +void dce110_link_encoder_dp_set_lane_settings( + struct link_encoder *enc, + const struct link_training_settings *link_settings) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + union dpcd_training_lane_set training_lane_set = { { 0 } }; + int32_t lane = 0; + struct bp_transmitter_control cntl = { 0 }; + + if (!link_settings) { + BREAK_TO_DEBUGGER(); + return; + } + + cntl.action = TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS; + cntl.transmitter = enc110->base.transmitter; + cntl.connector_obj_id = enc110->base.connector; + cntl.lanes_number = link_settings->link_settings.lane_count; + cntl.hpd_sel = enc110->base.hpd_source; + cntl.pixel_clock = link_settings->link_settings.link_rate * + LINK_RATE_REF_FREQ_IN_KHZ; + + for (lane = 0; lane < link_settings->link_settings.lane_count; ++lane) { + /* translate lane settings */ + + training_lane_set.bits.VOLTAGE_SWING_SET = + link_settings->lane_settings[lane].VOLTAGE_SWING; + training_lane_set.bits.PRE_EMPHASIS_SET = + link_settings->lane_settings[lane].PRE_EMPHASIS; + + /* post cursor 2 setting only applies to HBR2 link rate */ + if (link_settings->link_settings.link_rate == LINK_RATE_HIGH2) { + /* this is passed to VBIOS + * to program post cursor 2 level */ + + training_lane_set.bits.POST_CURSOR2_SET = + link_settings->lane_settings[lane].POST_CURSOR2; + } + + cntl.lane_select = lane; + cntl.lane_settings = training_lane_set.raw; + + /* call VBIOS table to set voltage swing and pre-emphasis */ + link_transmitter_control(enc110, &cntl); + } +} + +/* set DP PHY test and training patterns */ +void dce110_link_encoder_dp_set_phy_pattern( + struct link_encoder *enc, + const struct encoder_set_dp_phy_pattern_param *param) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + + switch (param->dp_phy_pattern) { + case DP_TEST_PATTERN_TRAINING_PATTERN1: + dce110_link_encoder_set_dp_phy_pattern_training_pattern(enc, 0); + break; + case DP_TEST_PATTERN_TRAINING_PATTERN2: + dce110_link_encoder_set_dp_phy_pattern_training_pattern(enc, 1); + break; + case DP_TEST_PATTERN_TRAINING_PATTERN3: + dce110_link_encoder_set_dp_phy_pattern_training_pattern(enc, 2); + break; + case DP_TEST_PATTERN_D102: + set_dp_phy_pattern_d102(enc110); + break; + case DP_TEST_PATTERN_SYMBOL_ERROR: + set_dp_phy_pattern_symbol_error(enc110); + break; + case DP_TEST_PATTERN_PRBS7: + set_dp_phy_pattern_prbs7(enc110); + break; + case DP_TEST_PATTERN_80BIT_CUSTOM: + set_dp_phy_pattern_80bit_custom( + enc110, param->custom_pattern); + break; + case DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE: + set_dp_phy_pattern_hbr2_compliance(enc110); + break; + case DP_TEST_PATTERN_VIDEO_MODE: { + set_dp_phy_pattern_passthrough_mode( + enc110, param->dp_panel_mode); + break; + } + + default: + /* invalid phy pattern */ + ASSERT_CRITICAL(false); + break; + } +} + +static void fill_stream_allocation_row_info( + const struct link_mst_stream_allocation *stream_allocation, + uint32_t *src, + uint32_t *slots) +{ + const struct stream_encoder *stream_enc = stream_allocation->stream_enc; + + if (stream_enc) { + *src = stream_enc->id; + *slots = stream_allocation->slot_count; + } else { + *src = 0; + *slots = 0; + } +} + +/* programs DP MST VC payload allocation */ +void dce110_link_encoder_update_mst_stream_allocation_table( + struct link_encoder *enc, + const struct link_mst_stream_allocation_table *table) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + uint32_t value0 = 0; + uint32_t value1 = 0; + uint32_t value2 = 0; + uint32_t slots = 0; + uint32_t src = 0; + uint32_t retries = 0; + + /* For CZ, there are only 3 pipes. So Virtual channel is up 3.*/ + + /* --- Set MSE Stream Attribute - + * Setup VC Payload Table on Tx Side, + * Issue allocation change trigger + * to commit payload on both tx and rx side */ + + /* we should clean-up table each time */ + + if (table->stream_count >= 1) { + fill_stream_allocation_row_info( + &table->stream_allocations[0], + &src, + &slots); + } else { + src = 0; + slots = 0; + } + + REG_UPDATE_2(DP_MSE_SAT0, + DP_MSE_SAT_SRC0, src, + DP_MSE_SAT_SLOT_COUNT0, slots); + + if (table->stream_count >= 2) { + fill_stream_allocation_row_info( + &table->stream_allocations[1], + &src, + &slots); + } else { + src = 0; + slots = 0; + } + + REG_UPDATE_2(DP_MSE_SAT0, + DP_MSE_SAT_SRC1, src, + DP_MSE_SAT_SLOT_COUNT1, slots); + + if (table->stream_count >= 3) { + fill_stream_allocation_row_info( + &table->stream_allocations[2], + &src, + &slots); + } else { + src = 0; + slots = 0; + } + + REG_UPDATE_2(DP_MSE_SAT1, + DP_MSE_SAT_SRC2, src, + DP_MSE_SAT_SLOT_COUNT2, slots); + + if (table->stream_count >= 4) { + fill_stream_allocation_row_info( + &table->stream_allocations[3], + &src, + &slots); + } else { + src = 0; + slots = 0; + } + + REG_UPDATE_2(DP_MSE_SAT1, + DP_MSE_SAT_SRC3, src, + DP_MSE_SAT_SLOT_COUNT3, slots); + + /* --- wait for transaction finish */ + + /* send allocation change trigger (ACT) ? + * this step first sends the ACT, + * then double buffers the SAT into the hardware + * making the new allocation active on the DP MST mode link */ + + + /* DP_MSE_SAT_UPDATE: + * 0 - No Action + * 1 - Update SAT with trigger + * 2 - Update SAT without trigger */ + + REG_UPDATE(DP_MSE_SAT_UPDATE, + DP_MSE_SAT_UPDATE, 1); + + /* wait for update to complete + * (i.e. DP_MSE_SAT_UPDATE field is reset to 0) + * then wait for the transmission + * of at least 16 MTP headers on immediate local link. + * i.e. DP_MSE_16_MTP_KEEPOUT field (read only) is reset to 0 + * a value of 1 indicates that DP MST mode + * is in the 16 MTP keepout region after a VC has been added. + * MST stream bandwidth (VC rate) can be configured + * after this bit is cleared */ + + do { + udelay(10); + + value0 = REG_READ(DP_MSE_SAT_UPDATE); + + REG_GET(DP_MSE_SAT_UPDATE, + DP_MSE_SAT_UPDATE, &value1); + + REG_GET(DP_MSE_SAT_UPDATE, + DP_MSE_16_MTP_KEEPOUT, &value2); + + /* bit field DP_MSE_SAT_UPDATE is set to 1 already */ + if (!value1 && !value2) + break; + ++retries; + } while (retries < DP_MST_UPDATE_MAX_RETRY); +} + +void dce110_link_encoder_set_lcd_backlight_level( + struct link_encoder *enc, + uint32_t level) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + + const uint32_t backlight_update_pending_max_retry = 1000; + + uint32_t backlight_lock; + + uint32_t i; + uint32_t backlight_24bit; + uint32_t backlight_17bit; + uint32_t backlight_16bit; + uint32_t masked_pwm_period; + uint8_t rounding_bit; + uint8_t bit_count; + uint64_t active_duty_cycle; + uint32_t pwm_period_bitcnt; + + backlight_lock = REG_READ(BL_PWM_GRP1_REG_LOCK); + + /* + * 1. Convert 8-bit value to 17 bit U1.16 format + * (1 integer, 16 fractional bits) + */ + + /* 1.1 multiply 8 bit value by 0x10101 to get a 24 bit value, + * effectively multiplying value by 256/255 + * eg. for a level of 0xEF, backlight_24bit = 0xEF * 0x10101 = 0xEFEFEF + */ + backlight_24bit = level * 0x10101; + + /* 1.2 The upper 16 bits of the 24 bit value is the fraction, lower 8 + * used for rounding, take most significant bit of fraction for + * rounding, e.g. for 0xEFEFEF, rounding bit is 1 + */ + rounding_bit = (backlight_24bit >> 7) & 1; + + /* 1.3 Add the upper 16 bits of the 24 bit value with the rounding bit + * resulting in a 17 bit value e.g. 0xEFF0 = (0xEFEFEF >> 8) + 1 + */ + backlight_17bit = (backlight_24bit >> 8) + rounding_bit; + + /* + * 2. Find 16 bit backlight active duty cycle, where 0 <= backlight + * active duty cycle <= backlight period + */ + + /* 2.1 Apply bitmask for backlight period value based on value of BITCNT + */ + { + REG_GET(BL_PWM_PERIOD_CNTL, + BL_PWM_PERIOD_BITCNT, &pwm_period_bitcnt); + + if (pwm_period_bitcnt == 0) + bit_count = 16; + else + bit_count = pwm_period_bitcnt; + } + + /* e.g. maskedPwmPeriod = 0x24 when bitCount is 6 */ + masked_pwm_period = + REG_GET(BL_PWM_PERIOD_CNTL, + BL_PWM_PERIOD, &masked_pwm_period) + & ((1 << bit_count) - 1); + + /* 2.2 Calculate integer active duty cycle required upper 16 bits + * contain integer component, lower 16 bits contain fractional component + * of active duty cycle e.g. 0x21BDC0 = 0xEFF0 * 0x24 + */ + active_duty_cycle = backlight_17bit * masked_pwm_period; + + /* 2.3 Calculate 16 bit active duty cycle from integer and fractional + * components shift by bitCount then mask 16 bits and add rounding bit + * from MSB of fraction e.g. 0x86F7 = ((0x21BDC0 >> 6) & 0xFFF) + 0 + */ + backlight_16bit = active_duty_cycle >> bit_count; + backlight_16bit &= 0xFFFF; + backlight_16bit += (active_duty_cycle >> (bit_count - 1)) & 0x1; + + REG_UPDATE(BL_PWM_CNTL, + BL_ACTIVE_INT_FRAC_CNT, backlight_16bit); + + /* + * 3. Program register with updated value + */ + + /* 3.1 Lock group 2 backlight registers */ + + REG_UPDATE(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_IGNORE_MASTER_LOCK_EN, 1); + + REG_UPDATE(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_REG_LOCK, 1); + + /* 3.3 Unlock group 2 backlight registers */ + REG_UPDATE(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_REG_LOCK, 0); + + /* 5.4.4 Wait for pending bit to be cleared */ + for (i = 0; i < backlight_update_pending_max_retry; ++i) { + REG_GET(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_REG_UPDATE_PENDING, &backlight_lock); + if (!backlight_lock) + break; + + udelay(10); + } +} + +void dce110_link_encoder_set_dmcu_backlight_level( + struct link_encoder *enc, + uint32_t level, + uint32_t frame_ramp, + uint32_t controller_id) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + unsigned int backlight_17bit = level * 0x10101; + unsigned char temp_uchar = + (unsigned char)(((backlight_17bit & 0x80) >> 7) & 1); + unsigned int regValue; + uint32_t rampingBoundary = 0xFFFF; + uint32_t s2; + + backlight_17bit = (backlight_17bit >> 8) + temp_uchar; + + /* set ramping boundary */ + REG_WRITE(MASTER_COMM_DATA_REG1, rampingBoundary); + + /* setDMCUParam_Pipe */ + REG_UPDATE_2(MASTER_COMM_CMD_REG, + MASTER_COMM_CMD_REG_BYTE0, MCP_ABM_PIPE_SET, + MASTER_COMM_CMD_REG_BYTE1, controller_id); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + /* waitDMCUReadyForCmd */ + do { + dm_delay_in_microseconds(ctx, dmcu_wait_reg_ready_interval); + regValue = REG_READ(MASTER_COMM_CNTL_REG); + dmcu_max_retry_on_wait_reg_ready--; + } while + /* expected value is 0, loop while not 0*/ + ((MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK & regValue) && + dmcu_max_retry_on_wait_reg_ready > 0); + + /* setDMCUParam_BL */ + REG_UPDATE(BL1_PWM_USER_LEVEL, BL1_PWM_USER_LEVEL, backlight_17bit); + + /* write ramp */ + REG_WRITE(MASTER_COMM_DATA_REG1, frame_ramp); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, MCP_BL_SET); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + /* UpdateRequestedBacklightLevel */ + s2 = REG_READ(BIOS_SCRATCH_2); + + s2 &= ~ATOM_S2_CURRENT_BL_LEVEL_MASK; + level &= (ATOM_S2_CURRENT_BL_LEVEL_MASK >> + ATOM_S2_CURRENT_BL_LEVEL_SHIFT); + s2 |= (level << ATOM_S2_CURRENT_BL_LEVEL_SHIFT); + + REG_WRITE(BIOS_SCRATCH_2, s2); +} + +void dce110_link_encoder_init_dmcu_backlight_settings( + struct link_encoder *enc) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + uint32_t bl_pwm_cntl; + uint32_t pwmCntl; + uint32_t pwmCntl2; + uint32_t periodCntl; + uint32_t s2; + uint32_t value; + + bl_pwm_cntl = REG_READ(BL_PWM_CNTL); + + /* It must not be 0, so we have to restore them + * Bios bug w/a - period resets to zero, + * restoring to cache values which is always correct + */ + REG_GET(BL_PWM_CNTL, + BL_ACTIVE_INT_FRAC_CNT, &value); + if (value == 0 || bl_pwm_cntl == 1) { + if (stored_backlight_registers.vBL_PWM_CNTL != 0) { + pwmCntl = stored_backlight_registers.vBL_PWM_CNTL; + REG_WRITE(BL_PWM_CNTL, pwmCntl); + + pwmCntl2 = stored_backlight_registers.vBL_PWM_CNTL2; + REG_WRITE(BL_PWM_CNTL2, pwmCntl2); + + periodCntl = + stored_backlight_registers.vBL_PWM_PERIOD_CNTL; + REG_WRITE(BL_PWM_PERIOD_CNTL, periodCntl); + + REG_UPDATE(LVTMA_PWRSEQ_REF_DIV, + BL_PWM_REF_DIV, + stored_backlight_registers. + vLVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV); + } + } else { + stored_backlight_registers.vBL_PWM_CNTL = + REG_READ(BL_PWM_CNTL); + stored_backlight_registers.vBL_PWM_CNTL2 = + REG_READ(BL_PWM_CNTL2); + stored_backlight_registers.vBL_PWM_PERIOD_CNTL = + REG_READ(BL_PWM_PERIOD_CNTL); + + REG_GET(LVTMA_PWRSEQ_REF_DIV, BL_PWM_REF_DIV, + &stored_backlight_registers. + vLVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV); + } + + /* Have driver take backlight control + * TakeBacklightControl(true) + */ + s2 = REG_READ(BIOS_SCRATCH_2); + s2 |= ATOM_S2_VRI_BRIGHT_ENABLE; + REG_WRITE(BIOS_SCRATCH_2, s2); + + /* Enable the backlight output */ + REG_UPDATE(BL_PWM_CNTL, BL_PWM_EN, 1); + +} + +void dce110_link_encoder_set_dmcu_abm_level( + struct link_encoder *enc, uint32_t level) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + unsigned int regValue; + + /* waitDMCUReadyForCmd */ + do { + dm_delay_in_microseconds(ctx, dmcu_wait_reg_ready_interval); + regValue = REG_READ(MASTER_COMM_CNTL_REG); + dmcu_max_retry_on_wait_reg_ready--; + } while + /* expected value is 0, loop while not 0*/ + ((MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK & regValue) && + dmcu_max_retry_on_wait_reg_ready > 0); + + /* setDMCUParam_ABMLevel */ + REG_UPDATE_2(MASTER_COMM_CMD_REG, + MASTER_COMM_CMD_REG_BYTE0, MCP_ABM_LEVEL_SET, + MASTER_COMM_CMD_REG_BYTE2, level); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); +} + +static void get_dmcu_psr_state(struct link_encoder *enc, uint32_t *psr_state) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + + uint32_t count = 0; + uint32_t psrStateOffset = 0xf0; + uint32_t value; + + /* Enable write access to IRAM */ + REG_UPDATE(DMCU_RAM_ACCESS_CTRL, IRAM_HOST_ACCESS_EN, 1); + + do { + dm_delay_in_microseconds(ctx, 2); + REG_GET(DCI_MEM_PWR_STATUS, + DMCU_IRAM_MEM_PWR_STATE, &value); + } while + (value != 0 && count++ < 10); + + /* Write address to IRAM_RD_ADDR in DMCU_IRAM_RD_CTRL */ + REG_WRITE(DMCU_IRAM_RD_CTRL, psrStateOffset); + + /* Read data from IRAM_RD_DATA in DMCU_IRAM_RD_DATA*/ + *psr_state = REG_READ(DMCU_IRAM_RD_DATA); + + /* Disable write access to IRAM after finished using IRAM + * in order to allow dynamic sleep state + */ + REG_UPDATE(DMCU_RAM_ACCESS_CTRL, IRAM_HOST_ACCESS_EN, 0); +} + +void dce110_link_encoder_set_dmcu_psr_enable(struct link_encoder *enc, + bool enable) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + + unsigned int regValue; + + unsigned int retryCount; + uint32_t psr_state = 0; + + /* waitDMCUReadyForCmd */ + do { + dm_delay_in_microseconds(ctx, dmcu_wait_reg_ready_interval); + regValue = REG_READ(MASTER_COMM_CNTL_REG); + dmcu_max_retry_on_wait_reg_ready--; + } while + /* expected value is 0, loop while not 0*/ + ((MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK & regValue) && + dmcu_max_retry_on_wait_reg_ready > 0); + + /* setDMCUParam_Cmd */ + if (enable) + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, PSR_ENABLE); + else + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, PSR_EXIT); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + for (retryCount = 0; retryCount <= 100; retryCount++) { + get_dmcu_psr_state(enc, &psr_state); + if (enable) { + if (psr_state != 0) + break; + } else { + if (psr_state == 0) + break; + } + dm_delay_in_microseconds(ctx, 10); + } +} + +void dce110_link_encoder_setup_dmcu_psr(struct link_encoder *enc, + struct psr_dmcu_context *psr_context) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + unsigned int regValue; + + union dce110_dmcu_psr_config_data_reg1 masterCmdData1; + union dce110_dmcu_psr_config_data_reg2 masterCmdData2; + union dce110_dmcu_psr_config_data_reg3 masterCmdData3; + + if (psr_context->psrExitLinkTrainingRequired) + REG_UPDATE(DP_DPHY_FAST_TRAINING, DPHY_RX_FAST_TRAINING_CAPABLE, 1); + else { + REG_UPDATE(DP_DPHY_FAST_TRAINING, DPHY_RX_FAST_TRAINING_CAPABLE, 0); + /*In DCE 11, we are able to pre-program a Force SR register + * to be able to trigger SR symbol after 5 idle patterns + * transmitted. Upon PSR Exit, DMCU can trigger + * DPHY_LOAD_BS_COUNT_START = 1. Upon writing 1 to + * DPHY_LOAD_BS_COUNT_START and the internal counter + * reaches DPHY_LOAD_BS_COUNT, the next BS symbol will be + * replaced by SR symbol once. + */ + + REG_UPDATE(DP_DPHY_BS_SR_SWAP_CNTL, DPHY_LOAD_BS_COUNT, 0x5); + } + + /* Enable static screen interrupts for PSR supported display */ + /* Disable the interrupt coming from other displays. */ + REG_UPDATE_4(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 0, + STATIC_SCREEN2_INT_TO_UC_EN, 0, + STATIC_SCREEN3_INT_TO_UC_EN, 0, + STATIC_SCREEN4_INT_TO_UC_EN, 0); + + switch (psr_context->controllerId) { + /* Driver uses case 1 for unconfigured */ + case 1: + psr_crtc_offset = mmCRTC0_CRTC_STATIC_SCREEN_CONTROL - + mmCRTC0_CRTC_STATIC_SCREEN_CONTROL; + + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 1); + break; + case 2: + psr_crtc_offset = mmCRTC1_CRTC_STATIC_SCREEN_CONTROL - + mmCRTC0_CRTC_STATIC_SCREEN_CONTROL; + + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN2_INT_TO_UC_EN, 1); + break; + case 3: + psr_crtc_offset = mmCRTC2_CRTC_STATIC_SCREEN_CONTROL - + mmCRTC0_CRTC_STATIC_SCREEN_CONTROL; + + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN3_INT_TO_UC_EN, 1); + break; + case 4: + psr_crtc_offset = mmCRTC3_CRTC_STATIC_SCREEN_CONTROL - + mmCRTC0_CRTC_STATIC_SCREEN_CONTROL; + + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN4_INT_TO_UC_EN, 1); + break; + case 5: + psr_crtc_offset = mmCRTC4_CRTC_STATIC_SCREEN_CONTROL - + mmCRTC0_CRTC_STATIC_SCREEN_CONTROL; + /* CZ/NL only has 4 CRTC!! + * really valid. + * There is no interrupt enable mask for these instances. + */ + break; + case 6: + psr_crtc_offset = mmCRTC5_CRTC_STATIC_SCREEN_CONTROL - + mmCRTC0_CRTC_STATIC_SCREEN_CONTROL; + /* CZ/NL only has 4 CRTC!! + * These are here because they are defined in HW regspec, + * but not really valid. There is no interrupt enable mask + * for these instances. + */ + break; + default: + psr_crtc_offset = mmCRTC0_CRTC_STATIC_SCREEN_CONTROL - + mmCRTC0_CRTC_STATIC_SCREEN_CONTROL; + + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 1); + break; + } + + REG_UPDATE_2(DP_SEC_CNTL1, + DP_SEC_GSP0_LINE_NUM, psr_context->sdpTransmitLineNumDeadline, + DP_SEC_GSP0_PRIORITY, 1); + + if (psr_context->psr_level.bits.SKIP_SMU_NOTIFICATION) { + REG_UPDATE(SMU_INTERRUPT_CONTROL, DC_SMU_INT_ENABLE, 1); + } + + /* waitDMCUReadyForCmd */ + do { + dm_delay_in_microseconds(ctx, dmcu_wait_reg_ready_interval); + regValue = REG_READ(MASTER_COMM_CNTL_REG); + dmcu_max_retry_on_wait_reg_ready--; + } while + /* expected value is 0, loop while not 0*/ + ((MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK & regValue) && + dmcu_max_retry_on_wait_reg_ready > 0); + + /* setDMCUParam_PSRHostConfigData */ + masterCmdData1.u32All = 0; + masterCmdData1.bits.timehyst_frames = psr_context->timehyst_frames; + masterCmdData1.bits.hyst_lines = psr_context->hyst_lines; + masterCmdData1.bits.rfb_update_auto_en = + psr_context->rfb_update_auto_en; + masterCmdData1.bits.dp_port_num = psr_context->transmitterId; + masterCmdData1.bits.dcp_sel = psr_context->controllerId; + masterCmdData1.bits.phy_type = psr_context->phyType; + masterCmdData1.bits.frame_cap_ind = + psr_context->psrFrameCaptureIndicationReq; + masterCmdData1.bits.aux_chan = psr_context->channel; + masterCmdData1.bits.aux_repeat = psr_context->aux_repeats; + dm_write_reg(ctx, REG(MASTER_COMM_DATA_REG1), + masterCmdData1.u32All); + + masterCmdData2.u32All = 0; + masterCmdData2.bits.dig_fe = psr_context->engineId; + masterCmdData2.bits.dig_be = psr_context->transmitterId; + masterCmdData2.bits.skip_wait_for_pll_lock = + psr_context->skipPsrWaitForPllLock; + masterCmdData2.bits.frame_delay = psr_context->frame_delay; + masterCmdData2.bits.smu_phy_id = psr_context->smuPhyId; + masterCmdData2.bits.num_of_controllers = + psr_context->numberOfControllers; + dm_write_reg(ctx, REG(MASTER_COMM_DATA_REG2), + masterCmdData2.u32All); + + masterCmdData3.u32All = 0; + masterCmdData3.bits.psr_level = psr_context->psr_level.u32all; + dm_write_reg(ctx, REG(MASTER_COMM_DATA_REG3), + masterCmdData3.u32All); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, + MASTER_COMM_CMD_REG_BYTE0, PSR_SET); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); +} + +void dce110_link_encoder_connect_dig_be_to_fe( + struct link_encoder *enc, + enum engine_id engine, + bool connect) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + uint32_t field; + + if (engine != ENGINE_ID_UNKNOWN) { + + REG_GET(DIG_BE_CNTL, DIG_FE_SOURCE_SELECT, &field); + + if (connect) + field |= get_frontend_source(engine); + else + field &= ~get_frontend_source(engine); + + REG_UPDATE(DIG_BE_CNTL, DIG_FE_SOURCE_SELECT, field); + } +} + +void dce110_link_encoder_enable_hpd(struct link_encoder *enc) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr = HPD_REG(DC_HPD_CONTROL); + uint32_t hpd_enable = 0; + uint32_t value = dm_read_reg(ctx, addr); + + get_reg_field_value(hpd_enable, DC_HPD_CONTROL, DC_HPD_EN); + + if (hpd_enable == 0) + set_reg_field_value(value, 1, DC_HPD_CONTROL, DC_HPD_EN); +} + +void dce110_link_encoder_disable_hpd(struct link_encoder *enc) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct dc_context *ctx = enc110->base.ctx; + uint32_t addr = HPD_REG(DC_HPD_CONTROL); + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value(value, 0, DC_HPD_CONTROL, DC_HPD_EN); +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h new file mode 100644 index 000000000000..1635b239402f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h @@ -0,0 +1,363 @@ +/* + * Copyright 2012-15 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. + * + * Authors: AMD + * + */ + +#ifndef __DC_LINK_ENCODER__DCE110_H__ +#define __DC_LINK_ENCODER__DCE110_H__ + +#include "link_encoder.h" + +#define TO_DCE110_LINK_ENC(link_encoder)\ + container_of(link_encoder, struct dce110_link_encoder, base) + +#define AUX_REG_LIST(id)\ + SRI(AUX_CONTROL, DP_AUX, id), \ + SRI(AUX_DPHY_RX_CONTROL0, DP_AUX, id) + +#define HPD_REG_LIST(id)\ + SRI(DC_HPD_CONTROL, HPD, id) + +#define LE_COMMON_REG_LIST_BASE(id) \ + SR(BL_PWM_CNTL), \ + SR(BL_PWM_GRP1_REG_LOCK), \ + SR(BL_PWM_PERIOD_CNTL), \ + SR(LVTMA_PWRSEQ_CNTL), \ + SR(LVTMA_PWRSEQ_STATE), \ + SR(BL_PWM_CNTL2), \ + SR(LVTMA_PWRSEQ_REF_DIV), \ + SR(MASTER_COMM_DATA_REG1), \ + SR(MASTER_COMM_DATA_REG2), \ + SR(MASTER_COMM_DATA_REG3), \ + SR(MASTER_COMM_CMD_REG), \ + SR(MASTER_COMM_CNTL_REG), \ + SR(DMCU_RAM_ACCESS_CTRL), \ + SR(DMCU_IRAM_RD_CTRL), \ + SR(DMCU_IRAM_RD_DATA), \ + SR(DMCU_INTERRUPT_TO_UC_EN_MASK), \ + SR(SMU_INTERRUPT_CONTROL), \ + SRI(DIG_BE_CNTL, DIG, id), \ + SRI(DIG_BE_EN_CNTL, DIG, id), \ + SRI(DP_CONFIG, DP, id), \ + SRI(DP_DPHY_CNTL, DP, id), \ + SRI(DP_DPHY_PRBS_CNTL, DP, id), \ + SRI(DP_DPHY_SCRAM_CNTL, DP, id),\ + SRI(DP_DPHY_SYM0, DP, id), \ + SRI(DP_DPHY_SYM1, DP, id), \ + SRI(DP_DPHY_SYM2, DP, id), \ + SRI(DP_DPHY_TRAINING_PATTERN_SEL, DP, id), \ + SRI(DP_LINK_CNTL, DP, id), \ + SRI(DP_LINK_FRAMING_CNTL, DP, id), \ + SRI(DP_MSE_SAT0, DP, id), \ + SRI(DP_MSE_SAT1, DP, id), \ + SRI(DP_MSE_SAT2, DP, id), \ + SRI(DP_MSE_SAT_UPDATE, DP, id), \ + SRI(DP_SEC_CNTL, DP, id), \ + SRI(DP_VID_STREAM_CNTL, DP, id), \ + SRI(DP_DPHY_FAST_TRAINING, DP, id), \ + SRI(DP_SEC_CNTL1, DP, id) + + #define LE_COMMON_REG_LIST(id)\ + LE_COMMON_REG_LIST_BASE(id), \ + SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ + SR(BIOS_SCRATCH_2), \ + SR(BL1_PWM_USER_LEVEL), \ + SR(DCI_MEM_PWR_STATUS) + + #define LE_DCE110_REG_LIST(id)\ + LE_COMMON_REG_LIST_BASE(id), \ + SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ + SR(BIOS_SCRATCH_2), \ + SR(BL1_PWM_USER_LEVEL), \ + SR(DCI_MEM_PWR_STATUS) + + #define LE_DCE80_REG_LIST(id)\ + SR(BIOS_SCRATCH_2), \ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ + SR(BL1_PWM_USER_LEVEL), \ + LE_COMMON_REG_LIST_BASE(id) + + +struct dce110_link_enc_aux_registers { + uint32_t AUX_CONTROL; + uint32_t AUX_DPHY_RX_CONTROL0; +}; + +struct dce110_link_enc_hpd_registers { + uint32_t DC_HPD_CONTROL; +}; + +struct dce110_link_enc_registers { + /* BL registers */ + uint32_t BL_PWM_CNTL; + uint32_t BL_PWM_GRP1_REG_LOCK; + uint32_t BL_PWM_PERIOD_CNTL; + uint32_t LVTMA_PWRSEQ_CNTL; + uint32_t LVTMA_PWRSEQ_STATE; + uint32_t BL_PWM_CNTL2; + uint32_t LVTMA_PWRSEQ_REF_DIV; + + /* DMCU registers */ + uint32_t BL1_PWM_USER_LEVEL; + uint32_t ABM0_BL1_PWM_USER_LEVEL; + uint32_t MASTER_COMM_DATA_REG1; + uint32_t MASTER_COMM_DATA_REG2; + uint32_t MASTER_COMM_DATA_REG3; + uint32_t MASTER_COMM_CMD_REG; + uint32_t MASTER_COMM_CNTL_REG; + uint32_t BIOS_SCRATCH_2; + uint32_t DMCU_RAM_ACCESS_CTRL; + uint32_t DCI_MEM_PWR_STATUS; + uint32_t DMU_MEM_PWR_CNTL; + uint32_t DMCU_IRAM_RD_CTRL; + uint32_t DMCU_IRAM_RD_DATA; + uint32_t DMCU_INTERRUPT_TO_UC_EN_MASK; + uint32_t SMU_INTERRUPT_CONTROL; + + + /* Common DP registers */ + uint32_t DIG_BE_CNTL; + uint32_t DIG_BE_EN_CNTL; + uint32_t DP_CONFIG; + uint32_t DP_DPHY_CNTL; + uint32_t DP_DPHY_INTERNAL_CTRL; + uint32_t DP_DPHY_PRBS_CNTL; + uint32_t DP_DPHY_SCRAM_CNTL; + uint32_t DP_DPHY_SYM0; + uint32_t DP_DPHY_SYM1; + uint32_t DP_DPHY_SYM2; + uint32_t DP_DPHY_TRAINING_PATTERN_SEL; + uint32_t DP_LINK_CNTL; + uint32_t DP_LINK_FRAMING_CNTL; + uint32_t DP_MSE_SAT0; + uint32_t DP_MSE_SAT1; + uint32_t DP_MSE_SAT2; + uint32_t DP_MSE_SAT_UPDATE; + uint32_t DP_SEC_CNTL; + uint32_t DP_VID_STREAM_CNTL; + uint32_t DP_DPHY_FAST_TRAINING; + uint32_t DP_DPHY_BS_SR_SWAP_CNTL; + uint32_t DP_SEC_CNTL1; +}; + +struct dce110_link_encoder { + struct link_encoder base; + const struct dce110_link_enc_registers *link_regs; + const struct dce110_link_enc_aux_registers *aux_regs; + const struct dce110_link_enc_hpd_registers *hpd_regs; +}; + +/******************************************************************* +* MASTER_COMM_DATA_REG1 Bit position Data +* 7:0 hyst_frames[7:0] +* 14:8 hyst_lines[6:0] +* 15 RFB_UPDATE_AUTO_EN +* 18:16 phy_num[2:0] +* 21:19 dcp_sel[2:0] +* 22 phy_type +* 23 frame_cap_ind +* 26:24 aux_chan[2:0] +* 30:27 aux_repeat[3:0] +* 31:31 reserved[31:31] +*******************************************************************/ +union dce110_dmcu_psr_config_data_reg1 { + struct { + unsigned int timehyst_frames:8; /*[7:0]*/ + unsigned int hyst_lines:7; /*[14:8]*/ + unsigned int rfb_update_auto_en:1; /*[15:15]*/ + unsigned int dp_port_num:3; /*[18:16]*/ + unsigned int dcp_sel:3; /*[21:19]*/ + unsigned int phy_type:1; /*[22:22]*/ + unsigned int frame_cap_ind:1; /*[23:23]*/ + unsigned int aux_chan:3; /*[26:24]*/ + unsigned int aux_repeat:4; /*[30:27]*/ + unsigned int reserved:1; /*[31:31]*/ + } bits; + unsigned int u32All; +}; + +/******************************************************************* +* MASTER_COMM_DATA_REG2 +*******************************************************************/ +union dce110_dmcu_psr_config_data_reg2 { + struct { + unsigned int dig_fe:3; /*[2:0]*/ + unsigned int dig_be:3; /*[5:3]*/ + unsigned int skip_wait_for_pll_lock:1; /*[6:6]*/ + unsigned int reserved:9; /*[15:7]*/ + unsigned int frame_delay:8; /*[23:16]*/ + unsigned int smu_phy_id:4; /*[27:24]*/ + unsigned int num_of_controllers:4; /*[31:28]*/ + } bits; + unsigned int u32All; +}; + +/******************************************************************* +* MASTER_COMM_DATA_REG3 +*******************************************************************/ +union dce110_dmcu_psr_config_data_reg3 { + struct { + unsigned int psr_level:16; /*[15:0]*/ + unsigned int link_rate:4; /*[19:16]*/ + unsigned int reserved:12; /*[31:20]*/ + } bits; + unsigned int u32All; +}; + +struct dce110_abm_backlight_registers { + unsigned int vBL_PWM_CNTL; + unsigned int vBL_PWM_CNTL2; + unsigned int vBL_PWM_PERIOD_CNTL; + unsigned int vLVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV; +}; + +bool dce110_link_encoder_construct( + struct dce110_link_encoder *enc110, + const struct encoder_init_data *init_data, + const struct dce110_link_enc_registers *link_regs, + const struct dce110_link_enc_aux_registers *aux_regs, + const struct dce110_link_enc_hpd_registers *hpd_regs); + +bool dce110_link_encoder_validate_dvi_output( + const struct dce110_link_encoder *enc110, + enum signal_type connector_signal, + enum signal_type signal, + const struct dc_crtc_timing *crtc_timing); + +bool dce110_link_encoder_validate_rgb_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing); + +bool dce110_link_encoder_validate_dp_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing); + +bool dce110_link_encoder_validate_wireless_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing); + +bool dce110_link_encoder_validate_output_with_stream( + struct link_encoder *enc, + struct pipe_ctx *pipe_ctx); + +/****************** HW programming ************************/ + +/* initialize HW */ /* why do we initialze aux in here? */ +void dce110_link_encoder_hw_init(struct link_encoder *enc); + +void dce110_link_encoder_destroy(struct link_encoder **enc); + +/* program DIG_MODE in DIG_BE */ +/* TODO can this be combined with enable_output? */ +void dce110_link_encoder_setup( + struct link_encoder *enc, + enum signal_type signal); + +/* enables TMDS PHY output */ +/* TODO: still need depth or just pass in adjusted pixel clock? */ +void dce110_link_encoder_enable_tmds_output( + struct link_encoder *enc, + enum clock_source_id clock_source, + enum dc_color_depth color_depth, + bool hdmi, + bool dual_link, + uint32_t pixel_clock); + +/* enables DP PHY output */ +void dce110_link_encoder_enable_dp_output( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source); + +/* enables DP PHY output in MST mode */ +void dce110_link_encoder_enable_dp_mst_output( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + enum clock_source_id clock_source); + +/* disable PHY output */ +void dce110_link_encoder_disable_output( + struct link_encoder *link_enc, + enum signal_type signal); + +/* set DP lane settings */ +void dce110_link_encoder_dp_set_lane_settings( + struct link_encoder *enc, + const struct link_training_settings *link_settings); + +void dce110_link_encoder_dp_set_phy_pattern( + struct link_encoder *enc, + const struct encoder_set_dp_phy_pattern_param *param); + +/* programs DP MST VC payload allocation */ +void dce110_link_encoder_update_mst_stream_allocation_table( + struct link_encoder *enc, + const struct link_mst_stream_allocation_table *table); + +void dce110_link_encoder_set_lcd_backlight_level( + struct link_encoder *enc, + uint32_t level); + +void dce110_link_encoder_set_dmcu_backlight_level( + struct link_encoder *enc, + uint32_t level, + uint32_t frame_ramp, + uint32_t controller_id); + +void dce110_link_encoder_init_dmcu_backlight_settings( + struct link_encoder *enc); + +void dce110_link_encoder_set_dmcu_abm_level( + struct link_encoder *enc, + uint32_t level); + +void dce110_link_encoder_set_dmcu_psr_enable( + struct link_encoder *enc, bool enable); + +void dce110_link_encoder_setup_dmcu_psr(struct link_encoder *enc, + struct psr_dmcu_context *psr_context); + +void dce110_link_encoder_edp_backlight_control( + struct link_encoder *enc, + bool enable); + +void dce110_link_encoder_edp_power_control( + struct link_encoder *enc, + bool power_up); + +void dce110_link_encoder_connect_dig_be_to_fe( + struct link_encoder *enc, + enum engine_id engine, + bool connect); + +void dce110_link_encoder_set_dp_phy_pattern_training_pattern( + struct link_encoder *enc, + uint32_t index); + +void dce110_link_encoder_enable_hpd(struct link_encoder *enc); + +void dce110_link_encoder_disable_hpd(struct link_encoder *enc); + +#endif /* __DC_LINK_ENCODER__DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c new file mode 100644 index 000000000000..654731cccdcd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c @@ -0,0 +1,384 @@ +/* + * Copyright 2016 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. + * + * Authors: AMD + * + */ + +#include "mem_input.h" +#include "reg_helper.h" + +#define CTX \ + mi->ctx +#define REG(reg)\ + mi->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + mi->shifts->field_name, mi->masks->field_name + + +static void program_urgency_watermark(struct mem_input *mi, + uint32_t wm_select, + uint32_t urgency_low_wm, + uint32_t urgency_high_wm) +{ + REG_UPDATE(DPG_WATERMARK_MASK_CONTROL, + URGENCY_WATERMARK_MASK, wm_select); + + REG_SET_2(DPG_PIPE_URGENCY_CONTROL, 0, + URGENCY_LOW_WATERMARK, urgency_low_wm, + URGENCY_HIGH_WATERMARK, urgency_high_wm); +} + +static void program_nbp_watermark(struct mem_input *mi, + uint32_t wm_select, + uint32_t nbp_wm) +{ + if (REG(DPG_PIPE_NB_PSTATE_CHANGE_CONTROL)) { + REG_UPDATE(DPG_WATERMARK_MASK_CONTROL, + NB_PSTATE_CHANGE_WATERMARK_MASK, wm_select); + + REG_UPDATE_3(DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_ENABLE, 1, + NB_PSTATE_CHANGE_URGENT_DURING_REQUEST, 1, + NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST, 1); + + REG_UPDATE(DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_WATERMARK, nbp_wm); + } +} + +static void program_stutter_watermark(struct mem_input *mi, + uint32_t wm_select, + uint32_t stutter_mark) +{ + REG_UPDATE(DPG_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK, wm_select); + + REG_UPDATE(DPG_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK, stutter_mark); +} + +void dce_mem_input_program_display_marks(struct mem_input *mi, + struct bw_watermarks nbp, + struct bw_watermarks stutter, + struct bw_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + uint32_t stutter_en = mi->ctx->dc->debug.disable_stutter ? 0 : 1; + + program_urgency_watermark(mi, 0, /* set a */ + urgent.a_mark, total_dest_line_time_ns); + program_urgency_watermark(mi, 1, /* set b */ + urgent.b_mark, total_dest_line_time_ns); + program_urgency_watermark(mi, 2, /* set c */ + urgent.c_mark, total_dest_line_time_ns); + program_urgency_watermark(mi, 3, /* set d */ + urgent.d_mark, total_dest_line_time_ns); + + REG_UPDATE_2(DPG_PIPE_STUTTER_CONTROL, + STUTTER_ENABLE, stutter_en, + STUTTER_IGNORE_FBC, 1); + program_nbp_watermark(mi, 0, nbp.a_mark); /* set a */ + program_nbp_watermark(mi, 1, nbp.b_mark); /* set b */ + program_nbp_watermark(mi, 2, nbp.c_mark); /* set c */ + program_nbp_watermark(mi, 3, nbp.d_mark); /* set d */ + + program_stutter_watermark(mi, 0, stutter.a_mark); /* set a */ + program_stutter_watermark(mi, 1, stutter.b_mark); /* set b */ + program_stutter_watermark(mi, 2, stutter.c_mark); /* set c */ + program_stutter_watermark(mi, 3, stutter.d_mark); /* set d */ +} + +static void program_tiling(struct mem_input *mi, + const union dc_tiling_info *info) +{ + if (mi->masks->GRPH_ARRAY_MODE) { /* GFX8 */ + REG_UPDATE_9(GRPH_CONTROL, + GRPH_NUM_BANKS, info->gfx8.num_banks, + GRPH_BANK_WIDTH, info->gfx8.bank_width, + GRPH_BANK_HEIGHT, info->gfx8.bank_height, + GRPH_MACRO_TILE_ASPECT, info->gfx8.tile_aspect, + GRPH_TILE_SPLIT, info->gfx8.tile_split, + GRPH_MICRO_TILE_MODE, info->gfx8.tile_mode, + GRPH_PIPE_CONFIG, info->gfx8.pipe_config, + GRPH_ARRAY_MODE, info->gfx8.array_mode, + GRPH_COLOR_EXPANSION_MODE, 1); + /* 01 - DCP_GRPH_COLOR_EXPANSION_MODE_ZEXP: zero expansion for YCbCr */ + /* + GRPH_Z, 0); + */ + } +} + + +static void program_size_and_rotation( + struct mem_input *mi, + enum dc_rotation_angle rotation, + const union plane_size *plane_size) +{ + const struct rect *in_rect = &plane_size->grph.surface_size; + struct rect hw_rect = plane_size->grph.surface_size; + const uint32_t rotation_angles[ROTATION_ANGLE_COUNT] = { + [ROTATION_ANGLE_0] = 0, + [ROTATION_ANGLE_90] = 1, + [ROTATION_ANGLE_180] = 2, + [ROTATION_ANGLE_270] = 3, + }; + + if (rotation == ROTATION_ANGLE_90 || rotation == ROTATION_ANGLE_270) { + hw_rect.x = in_rect->y; + hw_rect.y = in_rect->x; + + hw_rect.height = in_rect->width; + hw_rect.width = in_rect->height; + } + + REG_SET(GRPH_X_START, 0, + GRPH_X_START, hw_rect.x); + + REG_SET(GRPH_Y_START, 0, + GRPH_Y_START, hw_rect.y); + + REG_SET(GRPH_X_END, 0, + GRPH_X_END, hw_rect.width); + + REG_SET(GRPH_Y_END, 0, + GRPH_Y_END, hw_rect.height); + + REG_SET(GRPH_PITCH, 0, + GRPH_PITCH, plane_size->grph.surface_pitch); + + REG_SET(HW_ROTATION, 0, + GRPH_ROTATION_ANGLE, rotation_angles[rotation]); +} + +static void program_grph_pixel_format( + struct mem_input *mi, + enum surface_pixel_format format) +{ + uint32_t red_xbar = 0, blue_xbar = 0; /* no swap */ + uint32_t grph_depth, grph_format; + uint32_t sign = 0, floating = 0; + + if (format == SURFACE_PIXEL_FORMAT_GRPH_BGRA8888 || + /*todo: doesn't look like we handle BGRA here, + * should problem swap endian*/ + format == SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010 || + format == SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS || + format == SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F) { + /* ABGR formats */ + red_xbar = 2; + blue_xbar = 2; + } + + REG_SET_2(GRPH_SWAP_CNTL, 0, + GRPH_RED_CROSSBAR, red_xbar, + GRPH_BLUE_CROSSBAR, blue_xbar); + + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: + grph_depth = 0; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: + grph_depth = 1; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + grph_depth = 1; + grph_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_BGRA8888: + grph_depth = 2; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + grph_depth = 2; + grph_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + sign = 1; + floating = 1; + /* no break */ + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: /* shouldn't this get float too? */ + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + grph_depth = 3; + grph_format = 0; + break; + default: + DC_ERR("unsupported grph pixel format"); + break; + } + + REG_UPDATE_2(GRPH_CONTROL, + GRPH_DEPTH, grph_depth, + GRPH_FORMAT, grph_format); + + REG_UPDATE_4(PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_SELECT, floating, + GRPH_PRESCALE_R_SIGN, sign, + GRPH_PRESCALE_G_SIGN, sign, + GRPH_PRESCALE_B_SIGN, sign); +} + +bool dce_mem_input_program_surface_config(struct mem_input *mi, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + union plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror) +{ + REG_UPDATE(GRPH_ENABLE, GRPH_ENABLE, 1); + + program_tiling(mi, tiling_info); + program_size_and_rotation(mi, rotation, plane_size); + + if (format >= SURFACE_PIXEL_FORMAT_GRPH_BEGIN && + format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + program_grph_pixel_format(mi, format); + + return true; +} + +static uint32_t get_dmif_switch_time_us( + uint32_t h_total, + uint32_t v_total, + uint32_t pix_clk_khz) +{ + uint32_t frame_time; + uint32_t pixels_per_second; + uint32_t pixels_per_frame; + uint32_t refresh_rate; + const uint32_t us_in_sec = 1000000; + const uint32_t min_single_frame_time_us = 30000; + /*return double of frame time*/ + const uint32_t single_frame_time_multiplier = 2; + + if (!h_total || v_total || !pix_clk_khz) + return single_frame_time_multiplier * min_single_frame_time_us; + + /*TODO: should we use pixel format normalized pixel clock here?*/ + pixels_per_second = pix_clk_khz * 1000; + pixels_per_frame = h_total * v_total; + + if (!pixels_per_second || !pixels_per_frame) { + /* avoid division by zero */ + ASSERT(pixels_per_frame); + ASSERT(pixels_per_second); + return single_frame_time_multiplier * min_single_frame_time_us; + } + + refresh_rate = pixels_per_second / pixels_per_frame; + + if (!refresh_rate) { + /* avoid division by zero*/ + ASSERT(refresh_rate); + return single_frame_time_multiplier * min_single_frame_time_us; + } + + frame_time = us_in_sec / refresh_rate; + + if (frame_time < min_single_frame_time_us) + frame_time = min_single_frame_time_us; + + frame_time *= single_frame_time_multiplier; + + return frame_time; +} + +void dce_mem_input_allocate_dmif(struct mem_input *mi, + uint32_t h_total, + uint32_t v_total, + uint32_t pix_clk_khz, + uint32_t total_stream_num) +{ + const uint32_t retry_delay = 10; + uint32_t retry_count = get_dmif_switch_time_us( + h_total, + v_total, + pix_clk_khz) / retry_delay; + + uint32_t pix_dur; + uint32_t buffers_allocated; + uint32_t dmif_buffer_control; + + dmif_buffer_control = REG_GET(DMIF_BUFFER_CONTROL, + DMIF_BUFFERS_ALLOCATED, &buffers_allocated); + + if (buffers_allocated == 2) + return; + + REG_SET(DMIF_BUFFER_CONTROL, dmif_buffer_control, + DMIF_BUFFERS_ALLOCATED, 2); + + REG_WAIT(DMIF_BUFFER_CONTROL, + DMIF_BUFFERS_ALLOCATION_COMPLETED, 1, + retry_delay, retry_count); + + if (pix_clk_khz != 0) { + pix_dur = 1000000000ULL / pix_clk_khz; + + REG_UPDATE(DPG_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION, pix_dur); + } + + if (mi->wa.single_head_rdreq_dmif_limit) { + uint32_t eanble = (total_stream_num > 1) ? 0 : + mi->wa.single_head_rdreq_dmif_limit; + + REG_UPDATE(MC_HUB_RDREQ_DMIF_LIMIT, + ENABLE, eanble); + } +} + +void dce_mem_input_free_dmif(struct mem_input *mi, + uint32_t total_stream_num) +{ + uint32_t buffers_allocated; + uint32_t dmif_buffer_control; + + dmif_buffer_control = REG_GET(DMIF_BUFFER_CONTROL, + DMIF_BUFFERS_ALLOCATED, &buffers_allocated); + + if (buffers_allocated == 0) + return; + + REG_SET(DMIF_BUFFER_CONTROL, dmif_buffer_control, + DMIF_BUFFERS_ALLOCATED, 0); + + REG_WAIT(DMIF_BUFFER_CONTROL, + DMIF_BUFFERS_ALLOCATION_COMPLETED, 1, + 10, 0xBB8); + + if (mi->wa.single_head_rdreq_dmif_limit) { + uint32_t eanble = (total_stream_num > 1) ? 0 : + mi->wa.single_head_rdreq_dmif_limit; + + REG_UPDATE(MC_HUB_RDREQ_DMIF_LIMIT, + ENABLE, eanble); + } +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.h b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.h new file mode 100644 index 000000000000..d5930a925fcb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.h @@ -0,0 +1,217 @@ +/* + * Copyright 2016 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. + * + * Authors: AMD + * + */ +#ifndef __DCE_MEM_INPUT_H__ +#define __DCE_MEM_INPUT_H__ + +#define MI_DCE_BASE_REG_LIST(id)\ + SRI(GRPH_ENABLE, DCP, id),\ + SRI(GRPH_CONTROL, DCP, id),\ + SRI(GRPH_X_START, DCP, id),\ + SRI(GRPH_Y_START, DCP, id),\ + SRI(GRPH_X_END, DCP, id),\ + SRI(GRPH_Y_END, DCP, id),\ + SRI(GRPH_PITCH, DCP, id),\ + SRI(HW_ROTATION, DCP, id),\ + SRI(GRPH_SWAP_CNTL, DCP, id),\ + SRI(PRESCALE_GRPH_CONTROL, DCP, id),\ + SRI(DPG_PIPE_ARBITRATION_CONTROL1, DMIF_PG, id),\ + SRI(DPG_WATERMARK_MASK_CONTROL, DMIF_PG, id),\ + SRI(DPG_PIPE_URGENCY_CONTROL, DMIF_PG, id),\ + SRI(DPG_PIPE_STUTTER_CONTROL, DMIF_PG, id),\ + SRI(DMIF_BUFFER_CONTROL, PIPE, id) + +#define MI_REG_LIST(id)\ + MI_DCE_BASE_REG_LIST(id),\ + SRI(DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, DMIF_PG, id) + +struct dce_mem_input_registers { + /* DCP */ + uint32_t GRPH_ENABLE; + uint32_t GRPH_CONTROL; + uint32_t GRPH_X_START; + uint32_t GRPH_Y_START; + uint32_t GRPH_X_END; + uint32_t GRPH_Y_END; + uint32_t GRPH_PITCH; + uint32_t HW_ROTATION; + uint32_t GRPH_SWAP_CNTL; + uint32_t PRESCALE_GRPH_CONTROL; + /* DMIF_PG */ + uint32_t DPG_PIPE_ARBITRATION_CONTROL1; + uint32_t DPG_WATERMARK_MASK_CONTROL; + uint32_t DPG_PIPE_URGENCY_CONTROL; + uint32_t DPG_PIPE_NB_PSTATE_CHANGE_CONTROL; + uint32_t DPG_PIPE_LOW_POWER_CONTROL; + uint32_t DPG_PIPE_STUTTER_CONTROL; + uint32_t DPG_PIPE_STUTTER_CONTROL2; + /* DCI */ + uint32_t DMIF_BUFFER_CONTROL; + /* MC_HUB */ + uint32_t MC_HUB_RDREQ_DMIF_LIMIT; +}; + +/* Set_Filed_for_Block */ +#define SFB(blk_name, reg_name, field_name, post_fix)\ + .field_name = blk_name ## reg_name ## __ ## field_name ## post_fix + +#define MI_GFX8_TILE_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, GRPH_CONTROL, GRPH_BANK_HEIGHT, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_MACRO_TILE_ASPECT, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_TILE_SPLIT, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_MICRO_TILE_MODE, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_PIPE_CONFIG, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_ARRAY_MODE, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE, mask_sh) + +#define MI_DCP_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, GRPH_ENABLE, GRPH_ENABLE, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_DEPTH, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_FORMAT, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_NUM_BANKS, mask_sh),\ + SFB(blk, GRPH_X_START, GRPH_X_START, mask_sh),\ + SFB(blk, GRPH_Y_START, GRPH_Y_START, mask_sh),\ + SFB(blk, GRPH_X_END, GRPH_X_END, mask_sh),\ + SFB(blk, GRPH_Y_END, GRPH_Y_END, mask_sh),\ + SFB(blk, GRPH_PITCH, GRPH_PITCH, mask_sh),\ + SFB(blk, HW_ROTATION, GRPH_ROTATION_ANGLE, mask_sh),\ + SFB(blk, GRPH_SWAP_CNTL, GRPH_RED_CROSSBAR, mask_sh),\ + SFB(blk, GRPH_SWAP_CNTL, GRPH_BLUE_CROSSBAR, mask_sh),\ + SFB(blk, PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_SELECT, mask_sh),\ + SFB(blk, PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_R_SIGN, mask_sh),\ + SFB(blk, PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_G_SIGN, mask_sh),\ + SFB(blk, PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_B_SIGN, mask_sh) + +#define MI_DMIF_PG_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, DPG_PIPE_ARBITRATION_CONTROL1, PIXEL_DURATION, mask_sh),\ + SFB(blk, DPG_WATERMARK_MASK_CONTROL, URGENCY_WATERMARK_MASK, mask_sh),\ + SFB(blk, DPG_WATERMARK_MASK_CONTROL, STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK, mask_sh),\ + SFB(blk, DPG_PIPE_URGENCY_CONTROL, URGENCY_LOW_WATERMARK, mask_sh),\ + SFB(blk, DPG_PIPE_URGENCY_CONTROL, URGENCY_HIGH_WATERMARK, mask_sh),\ + SFB(blk, DPG_PIPE_STUTTER_CONTROL, STUTTER_ENABLE, mask_sh),\ + SFB(blk, DPG_PIPE_STUTTER_CONTROL, STUTTER_IGNORE_FBC, mask_sh),\ + SF(PIPE0_DMIF_BUFFER_CONTROL, DMIF_BUFFERS_ALLOCATED, mask_sh),\ + SF(PIPE0_DMIF_BUFFER_CONTROL, DMIF_BUFFERS_ALLOCATION_COMPLETED, mask_sh) + +#define MI_DMIF_PG_MASK_SH_DCE(mask_sh, blk)\ + SFB(blk, DPG_PIPE_STUTTER_CONTROL, STUTTER_EXIT_SELF_REFRESH_WATERMARK, mask_sh),\ + SFB(blk, DPG_WATERMARK_MASK_CONTROL, NB_PSTATE_CHANGE_WATERMARK_MASK, mask_sh),\ + SFB(blk, DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, NB_PSTATE_CHANGE_ENABLE, mask_sh),\ + SFB(blk, DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, NB_PSTATE_CHANGE_URGENT_DURING_REQUEST, mask_sh),\ + SFB(blk, DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST, mask_sh),\ + SFB(blk, DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, NB_PSTATE_CHANGE_WATERMARK, mask_sh) + +#define MI_DCE_MASK_SH_LIST(mask_sh)\ + MI_DCP_MASK_SH_LIST(mask_sh,),\ + MI_DMIF_PG_MASK_SH_LIST(mask_sh,),\ + MI_DMIF_PG_MASK_SH_DCE(mask_sh,),\ + MI_GFX8_TILE_MASK_SH_LIST(mask_sh,) + +#define MI_REG_FIELD_LIST(type) \ + type GRPH_ENABLE; \ + type GRPH_X_START; \ + type GRPH_Y_START; \ + type GRPH_X_END; \ + type GRPH_Y_END; \ + type GRPH_PITCH; \ + type GRPH_ROTATION_ANGLE; \ + type GRPH_RED_CROSSBAR; \ + type GRPH_BLUE_CROSSBAR; \ + type GRPH_PRESCALE_SELECT; \ + type GRPH_PRESCALE_R_SIGN; \ + type GRPH_PRESCALE_G_SIGN; \ + type GRPH_PRESCALE_B_SIGN; \ + type GRPH_DEPTH; \ + type GRPH_FORMAT; \ + type GRPH_NUM_BANKS; \ + type GRPH_BANK_WIDTH;\ + type GRPH_BANK_HEIGHT;\ + type GRPH_MACRO_TILE_ASPECT;\ + type GRPH_TILE_SPLIT;\ + type GRPH_MICRO_TILE_MODE;\ + type GRPH_PIPE_CONFIG;\ + type GRPH_ARRAY_MODE;\ + type GRPH_COLOR_EXPANSION_MODE;\ + type GRPH_SW_MODE; \ + type GRPH_NUM_SHADER_ENGINES; \ + type GRPH_NUM_PIPES; \ + type PIXEL_DURATION; \ + type URGENCY_WATERMARK_MASK; \ + type PSTATE_CHANGE_WATERMARK_MASK; \ + type NB_PSTATE_CHANGE_WATERMARK_MASK; \ + type STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK; \ + type URGENCY_LOW_WATERMARK; \ + type URGENCY_HIGH_WATERMARK; \ + type NB_PSTATE_CHANGE_ENABLE; \ + type NB_PSTATE_CHANGE_URGENT_DURING_REQUEST; \ + type NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST; \ + type NB_PSTATE_CHANGE_WATERMARK; \ + type PSTATE_CHANGE_ENABLE; \ + type PSTATE_CHANGE_URGENT_DURING_REQUEST; \ + type PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST; \ + type PSTATE_CHANGE_WATERMARK; \ + type STUTTER_ENABLE; \ + type STUTTER_IGNORE_FBC; \ + type STUTTER_EXIT_SELF_REFRESH_WATERMARK; \ + type DMIF_BUFFERS_ALLOCATED; \ + type DMIF_BUFFERS_ALLOCATION_COMPLETED; \ + type ENABLE; /* MC_HUB_RDREQ_DMIF_LIMIT */\ + +struct dce_mem_input_shift { + MI_REG_FIELD_LIST(uint8_t) +}; + +struct dce_mem_input_mask { + MI_REG_FIELD_LIST(uint32_t) +}; + +struct dce_mem_input_wa { + uint8_t single_head_rdreq_dmif_limit; +}; + +struct mem_input; +bool dce_mem_input_program_surface_config(struct mem_input *mi, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + union plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror); + +void dce_mem_input_allocate_dmif(struct mem_input *mi, + uint32_t h_total, + uint32_t v_total, + uint32_t pix_clk_khz, + uint32_t total_stream_num); + +void dce_mem_input_free_dmif(struct mem_input *mi, + uint32_t total_stream_num); + +void dce_mem_input_program_display_marks(struct mem_input *mi, + struct bw_watermarks nbp, + struct bw_watermarks stutter, + struct bw_watermarks urgent, + uint32_t total_dest_line_time_ns); + +#endif /*__DCE_MEM_INPUT_H__*/ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_scl_filters.c b/drivers/gpu/drm/amd/display/dc/dce/dce_scl_filters.c new file mode 100644 index 000000000000..3aab86781be2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_scl_filters.c @@ -0,0 +1,501 @@ +/* + * Copyright 2012-16 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. + * + * Authors: AMD + * + */ + +#include "transform.h" + +const uint16_t filter_2tap_16p[18] = { + 4096, 0, + 3840, 256, + 3584, 512, + 3328, 768, + 3072, 1024, + 2816, 1280, + 2560, 1536, + 2304, 1792, + 2048, 2048 +}; + +const uint16_t filter_3tap_16p_upscale[27] = { + 2048, 2048, 0, + 1708, 2424, 16348, + 1372, 2796, 16308, + 1056, 3148, 16272, + 768, 3464, 16244, + 512, 3728, 16236, + 296, 3928, 16252, + 124, 4052, 16296, + 0, 4096, 0 +}; + +const uint16_t filter_3tap_16p_117[27] = { + 2048, 2048, 0, + 1824, 2276, 16376, + 1600, 2496, 16380, + 1376, 2700, 16, + 1156, 2880, 52, + 948, 3032, 108, + 756, 3144, 192, + 580, 3212, 296, + 428, 3236, 428 +}; + +const uint16_t filter_3tap_16p_150[27] = { + 2048, 2048, 0, + 1872, 2184, 36, + 1692, 2308, 88, + 1516, 2420, 156, + 1340, 2516, 236, + 1168, 2592, 328, + 1004, 2648, 440, + 844, 2684, 560, + 696, 2696, 696 +}; + +const uint16_t filter_3tap_16p_183[27] = { + 2048, 2048, 0, + 1892, 2104, 92, + 1744, 2152, 196, + 1592, 2196, 300, + 1448, 2232, 412, + 1304, 2256, 528, + 1168, 2276, 648, + 1032, 2288, 772, + 900, 2292, 900 +}; + +const uint16_t filter_4tap_16p_upscale[36] = { + 0, 4096, 0, 0, + 16240, 4056, 180, 16380, + 16136, 3952, 404, 16364, + 16072, 3780, 664, 16344, + 16040, 3556, 952, 16312, + 16036, 3284, 1268, 16272, + 16052, 2980, 1604, 16224, + 16084, 2648, 1952, 16176, + 16128, 2304, 2304, 16128 +}; + +const uint16_t filter_4tap_16p_117[36] = { + 428, 3236, 428, 0, + 276, 3232, 604, 16364, + 148, 3184, 800, 16340, + 44, 3104, 1016, 16312, + 16344, 2984, 1244, 16284, + 16284, 2832, 1488, 16256, + 16244, 2648, 1732, 16236, + 16220, 2440, 1976, 16220, + 16212, 2216, 2216, 16212 +}; + +const uint16_t filter_4tap_16p_150[36] = { + 696, 2700, 696, 0, + 560, 2700, 848, 16364, + 436, 2676, 1008, 16348, + 328, 2628, 1180, 16336, + 232, 2556, 1356, 16328, + 152, 2460, 1536, 16328, + 84, 2344, 1716, 16332, + 28, 2208, 1888, 16348, + 16376, 2052, 2052, 16376 +}; + +const uint16_t filter_4tap_16p_183[36] = { + 940, 2208, 940, 0, + 832, 2200, 1052, 4, + 728, 2180, 1164, 16, + 628, 2148, 1280, 36, + 536, 2100, 1392, 60, + 448, 2044, 1504, 92, + 368, 1976, 1612, 132, + 296, 1900, 1716, 176, + 232, 1812, 1812, 232 +}; + +const uint16_t filter_2tap_64p[66] = { + 4096, 0, + 4032, 64, + 3968, 128, + 3904, 192, + 3840, 256, + 3776, 320, + 3712, 384, + 3648, 448, + 3584, 512, + 3520, 576, + 3456, 640, + 3392, 704, + 3328, 768, + 3264, 832, + 3200, 896, + 3136, 960, + 3072, 1024, + 3008, 1088, + 2944, 1152, + 2880, 1216, + 2816, 1280, + 2752, 1344, + 2688, 1408, + 2624, 1472, + 2560, 1536, + 2496, 1600, + 2432, 1664, + 2368, 1728, + 2304, 1792, + 2240, 1856, + 2176, 1920, + 2112, 1984, + 2048, 2048 }; + +const uint16_t filter_3tap_64p_upscale[99] = { + 2048, 2048, 0, + 1960, 2140, 16376, + 1876, 2236, 16364, + 1792, 2328, 16356, + 1708, 2424, 16348, + 1620, 2516, 16336, + 1540, 2612, 16328, + 1456, 2704, 16316, + 1372, 2796, 16308, + 1292, 2884, 16296, + 1212, 2976, 16288, + 1136, 3060, 16280, + 1056, 3148, 16272, + 984, 3228, 16264, + 908, 3312, 16256, + 836, 3388, 16248, + 768, 3464, 16244, + 700, 3536, 16240, + 636, 3604, 16236, + 572, 3668, 16236, + 512, 3728, 16236, + 456, 3784, 16236, + 400, 3836, 16240, + 348, 3884, 16244, + 296, 3928, 16252, + 252, 3964, 16260, + 204, 4000, 16268, + 164, 4028, 16284, + 124, 4052, 16296, + 88, 4072, 16316, + 56, 4084, 16336, + 24, 4092, 16356, + 0, 4096, 0 +}; + +const uint16_t filter_3tap_64p_117[99] = { + 2048, 2048, 0, + 1992, 2104, 16380, + 1936, 2160, 16380, + 1880, 2220, 16376, + 1824, 2276, 16376, + 1768, 2332, 16376, + 1712, 2388, 16376, + 1656, 2444, 16376, + 1600, 2496, 16380, + 1544, 2548, 0, + 1488, 2600, 4, + 1432, 2652, 8, + 1376, 2700, 16, + 1320, 2748, 20, + 1264, 2796, 32, + 1212, 2840, 40, + 1156, 2880, 52, + 1104, 2920, 64, + 1052, 2960, 80, + 1000, 2996, 92, + 948, 3032, 108, + 900, 3060, 128, + 852, 3092, 148, + 804, 3120, 168, + 756, 3144, 192, + 712, 3164, 216, + 668, 3184, 240, + 624, 3200, 268, + 580, 3212, 296, + 540, 3220, 328, + 500, 3228, 360, + 464, 3232, 392, + 428, 3236, 428 +}; + +const uint16_t filter_3tap_64p_150[99] = { + 2048, 2048, 0, + 2004, 2080, 8, + 1960, 2116, 16, + 1916, 2148, 28, + 1872, 2184, 36, + 1824, 2216, 48, + 1780, 2248, 60, + 1736, 2280, 76, + 1692, 2308, 88, + 1648, 2336, 104, + 1604, 2368, 120, + 1560, 2392, 136, + 1516, 2420, 156, + 1472, 2444, 172, + 1428, 2472, 192, + 1384, 2492, 212, + 1340, 2516, 236, + 1296, 2536, 256, + 1252, 2556, 280, + 1212, 2576, 304, + 1168, 2592, 328, + 1124, 2608, 356, + 1084, 2624, 384, + 1044, 2636, 412, + 1004, 2648, 440, + 964, 2660, 468, + 924, 2668, 500, + 884, 2676, 528, + 844, 2684, 560, + 808, 2688, 596, + 768, 2692, 628, + 732, 2696, 664, + 696, 2696, 696 +}; + +const uint16_t filter_3tap_64p_183[99] = { + 2048, 2048, 0, + 2008, 2060, 20, + 1968, 2076, 44, + 1932, 2088, 68, + 1892, 2104, 92, + 1856, 2116, 120, + 1816, 2128, 144, + 1780, 2140, 168, + 1744, 2152, 196, + 1704, 2164, 220, + 1668, 2176, 248, + 1632, 2188, 272, + 1592, 2196, 300, + 1556, 2204, 328, + 1520, 2216, 356, + 1484, 2224, 384, + 1448, 2232, 412, + 1412, 2240, 440, + 1376, 2244, 468, + 1340, 2252, 496, + 1304, 2256, 528, + 1272, 2264, 556, + 1236, 2268, 584, + 1200, 2272, 616, + 1168, 2276, 648, + 1132, 2280, 676, + 1100, 2284, 708, + 1064, 2288, 740, + 1032, 2288, 772, + 996, 2292, 800, + 964, 2292, 832, + 932, 2292, 868, + 900, 2292, 900 +}; + +const uint16_t filter_4tap_64p_upscale[132] = { + 0, 4096, 0, 0, + 16344, 4092, 40, 0, + 16308, 4084, 84, 16380, + 16272, 4072, 132, 16380, + 16240, 4056, 180, 16380, + 16212, 4036, 232, 16376, + 16184, 4012, 288, 16372, + 16160, 3984, 344, 16368, + 16136, 3952, 404, 16364, + 16116, 3916, 464, 16360, + 16100, 3872, 528, 16356, + 16084, 3828, 596, 16348, + 16072, 3780, 664, 16344, + 16060, 3728, 732, 16336, + 16052, 3676, 804, 16328, + 16044, 3616, 876, 16320, + 16040, 3556, 952, 16312, + 16036, 3492, 1028, 16300, + 16032, 3424, 1108, 16292, + 16032, 3356, 1188, 16280, + 16036, 3284, 1268, 16272, + 16036, 3212, 1352, 16260, + 16040, 3136, 1436, 16248, + 16044, 3056, 1520, 16236, + 16052, 2980, 1604, 16224, + 16060, 2896, 1688, 16212, + 16064, 2816, 1776, 16200, + 16076, 2732, 1864, 16188, + 16084, 2648, 1952, 16176, + 16092, 2564, 2040, 16164, + 16104, 2476, 2128, 16152, + 16116, 2388, 2216, 16140, + 16128, 2304, 2304, 16128 }; + +const uint16_t filter_4tap_64p_117[132] = { + 420, 3248, 420, 0, + 380, 3248, 464, 16380, + 344, 3248, 508, 16372, + 308, 3248, 552, 16368, + 272, 3240, 596, 16364, + 236, 3236, 644, 16356, + 204, 3224, 692, 16352, + 172, 3212, 744, 16344, + 144, 3196, 796, 16340, + 116, 3180, 848, 16332, + 88, 3160, 900, 16324, + 60, 3136, 956, 16320, + 36, 3112, 1012, 16312, + 16, 3084, 1068, 16304, + 16380, 3056, 1124, 16296, + 16360, 3024, 1184, 16292, + 16340, 2992, 1244, 16284, + 16324, 2956, 1304, 16276, + 16308, 2920, 1364, 16268, + 16292, 2880, 1424, 16264, + 16280, 2836, 1484, 16256, + 16268, 2792, 1548, 16252, + 16256, 2748, 1608, 16244, + 16248, 2700, 1668, 16240, + 16240, 2652, 1732, 16232, + 16232, 2604, 1792, 16228, + 16228, 2552, 1856, 16224, + 16220, 2500, 1916, 16220, + 16216, 2444, 1980, 16216, + 16216, 2388, 2040, 16216, + 16212, 2332, 2100, 16212, + 16212, 2276, 2160, 16212, + 16212, 2220, 2220, 16212 }; + +const uint16_t filter_4tap_64p_150[132] = { + 696, 2700, 696, 0, + 660, 2704, 732, 16380, + 628, 2704, 768, 16376, + 596, 2704, 804, 16372, + 564, 2700, 844, 16364, + 532, 2696, 884, 16360, + 500, 2692, 924, 16356, + 472, 2684, 964, 16352, + 440, 2676, 1004, 16352, + 412, 2668, 1044, 16348, + 384, 2656, 1088, 16344, + 360, 2644, 1128, 16340, + 332, 2632, 1172, 16336, + 308, 2616, 1216, 16336, + 284, 2600, 1260, 16332, + 260, 2580, 1304, 16332, + 236, 2560, 1348, 16328, + 216, 2540, 1392, 16328, + 196, 2516, 1436, 16328, + 176, 2492, 1480, 16324, + 156, 2468, 1524, 16324, + 136, 2440, 1568, 16328, + 120, 2412, 1612, 16328, + 104, 2384, 1656, 16328, + 88, 2352, 1700, 16332, + 72, 2324, 1744, 16332, + 60, 2288, 1788, 16336, + 48, 2256, 1828, 16340, + 36, 2220, 1872, 16344, + 24, 2184, 1912, 16352, + 12, 2148, 1952, 16356, + 4, 2112, 1996, 16364, + 16380, 2072, 2036, 16372 }; + +const uint16_t filter_4tap_64p_183[132] = { + 944, 2204, 944, 0, + 916, 2204, 972, 0, + 888, 2200, 996, 0, + 860, 2200, 1024, 4, + 832, 2196, 1052, 4, + 808, 2192, 1080, 8, + 780, 2188, 1108, 12, + 756, 2180, 1140, 12, + 728, 2176, 1168, 16, + 704, 2168, 1196, 20, + 680, 2160, 1224, 24, + 656, 2152, 1252, 28, + 632, 2144, 1280, 36, + 608, 2132, 1308, 40, + 584, 2120, 1336, 48, + 560, 2112, 1364, 52, + 536, 2096, 1392, 60, + 516, 2084, 1420, 68, + 492, 2072, 1448, 76, + 472, 2056, 1476, 84, + 452, 2040, 1504, 92, + 428, 2024, 1532, 100, + 408, 2008, 1560, 112, + 392, 1992, 1584, 120, + 372, 1972, 1612, 132, + 352, 1956, 1636, 144, + 336, 1936, 1664, 156, + 316, 1916, 1688, 168, + 300, 1896, 1712, 180, + 284, 1876, 1736, 192, + 268, 1852, 1760, 208, + 252, 1832, 1784, 220, + 236, 1808, 1808, 236 }; + +const uint16_t *get_filter_3tap_16p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_3tap_16p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_3tap_16p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_3tap_16p_150; + else + return filter_3tap_16p_183; +} + +const uint16_t *get_filter_3tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_3tap_64p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_3tap_64p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_3tap_64p_150; + else + return filter_3tap_64p_183; +} + +const uint16_t *get_filter_4tap_16p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_4tap_16p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_4tap_16p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_4tap_16p_150; + else + return filter_4tap_16p_183; +} + +const uint16_t *get_filter_4tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dal_fixed31_32_one.value) + return filter_4tap_64p_upscale; + else if (ratio.value < dal_fixed31_32_from_fraction(4, 3).value) + return filter_4tap_64p_117; + else if (ratio.value < dal_fixed31_32_from_fraction(5, 3).value) + return filter_4tap_64p_150; + else + return filter_4tap_64p_183; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c new file mode 100644 index 000000000000..842182ce93a8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c @@ -0,0 +1,1302 @@ +/* + * Copyright 2012-15 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. + * + * Authors: AMD + * + */ + +#include "dc_bios_types.h" +#include "dce_stream_encoder.h" +#include "reg_helper.h" + +enum DP_PIXEL_ENCODING { +DP_PIXEL_ENCODING_RGB444 = 0x00000000, +DP_PIXEL_ENCODING_YCBCR422 = 0x00000001, +DP_PIXEL_ENCODING_YCBCR444 = 0x00000002, +DP_PIXEL_ENCODING_RGB_WIDE_GAMUT = 0x00000003, +DP_PIXEL_ENCODING_Y_ONLY = 0x00000004, +DP_PIXEL_ENCODING_YCBCR420 = 0x00000005, +DP_PIXEL_ENCODING_RESERVED = 0x00000006, +}; + + +enum DP_COMPONENT_DEPTH { +DP_COMPONENT_DEPTH_6BPC = 0x00000000, +DP_COMPONENT_DEPTH_8BPC = 0x00000001, +DP_COMPONENT_DEPTH_10BPC = 0x00000002, +DP_COMPONENT_DEPTH_12BPC = 0x00000003, +DP_COMPONENT_DEPTH_16BPC = 0x00000004, +DP_COMPONENT_DEPTH_RESERVED = 0x00000005, +}; + + +#define REG(reg)\ + (enc110->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + enc110->se_shift->field_name, enc110->se_mask->field_name + +#define VBI_LINE_0 0 +#define DP_BLANK_MAX_RETRY 20 +#define HDMI_CLOCK_CHANNEL_RATE_MORE_340M 340000 + +#ifndef TMDS_CNTL__TMDS_PIXEL_ENCODING_MASK + #define TMDS_CNTL__TMDS_PIXEL_ENCODING_MASK 0x00000010L + #define TMDS_CNTL__TMDS_COLOR_FORMAT_MASK 0x00000300L + #define TMDS_CNTL__TMDS_PIXEL_ENCODING__SHIFT 0x00000004 + #define TMDS_CNTL__TMDS_COLOR_FORMAT__SHIFT 0x00000008 +#endif + +enum { + DP_MST_UPDATE_MAX_RETRY = 50 +}; + +#define DCE110_SE(audio)\ + container_of(audio, struct dce110_stream_encoder, base) + +#define CTX \ + enc110->base.ctx + +static void dce110_update_generic_info_packet( + struct dce110_stream_encoder *enc110, + uint32_t packet_index, + const struct encoder_info_packet *info_packet) +{ + uint32_t regval; + /* TODOFPGA Figure out a proper number for max_retries polling for lock + * use 50 for now. + */ + uint32_t max_retries = 50; + + if (REG(AFMT_VBI_PACKET_CONTROL1)) { + if (packet_index >= 8) + ASSERT(0); + + /* poll dig_update_lock is not locked -> asic internal signal + * assume otg master lock will unlock it + */ + REG_WAIT(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_LOCK_STATUS, + 1, 10, max_retries); + + /* check if HW reading GSP memory */ + REG_WAIT(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_CONFLICT, + 1, 10, max_retries); + + /* HW does is not reading GSP memory not reading too long -> + * something wrong. clear GPS memory access and notify? + * hw SW is writing to GSP memory + */ + REG_UPDATE(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_CONFLICT_CLR, 1); + } + /* choose which generic packet to use */ + { + regval = REG_READ(AFMT_VBI_PACKET_CONTROL); + REG_UPDATE(AFMT_VBI_PACKET_CONTROL, + AFMT_GENERIC_INDEX, packet_index); + } + + /* write generic packet header + * (4th byte is for GENERIC0 only) */ + { + REG_SET_4(AFMT_GENERIC_HDR, 0, + AFMT_GENERIC_HB0, info_packet->hb0, + AFMT_GENERIC_HB1, info_packet->hb1, + AFMT_GENERIC_HB2, info_packet->hb2, + AFMT_GENERIC_HB3, info_packet->hb3); + } + + /* write generic packet contents + * (we never use last 4 bytes) + * there are 8 (0-7) mmDIG0_AFMT_GENERIC0_x registers */ + { + const uint32_t *content = + (const uint32_t *) &info_packet->sb[0]; + + REG_WRITE(AFMT_GENERIC_0, *content++); + REG_WRITE(AFMT_GENERIC_1, *content++); + REG_WRITE(AFMT_GENERIC_2, *content++); + REG_WRITE(AFMT_GENERIC_3, *content++); + REG_WRITE(AFMT_GENERIC_4, *content++); + REG_WRITE(AFMT_GENERIC_5, *content++); + REG_WRITE(AFMT_GENERIC_6, *content); + REG_WRITE(AFMT_GENERIC_7, 0); + } + + if (!REG(AFMT_VBI_PACKET_CONTROL1)) { + /* force double-buffered packet update */ + REG_UPDATE_2(AFMT_VBI_PACKET_CONTROL, + AFMT_GENERIC0_UPDATE, (packet_index == 0), + AFMT_GENERIC2_UPDATE, (packet_index == 2)); + } +} + +static void dce110_update_hdmi_info_packet( + struct dce110_stream_encoder *enc110, + uint32_t packet_index, + const struct encoder_info_packet *info_packet) +{ + struct dc_context *ctx = enc110->base.ctx; + uint32_t cont, send, line; + + if (info_packet->valid) { + dce110_update_generic_info_packet( + enc110, + packet_index, + info_packet); + + /* enable transmission of packet(s) - + * packet transmission begins on the next frame */ + cont = 1; + /* send packet(s) every frame */ + send = 1; + /* select line number to send packets on */ + line = 2; + } else { + cont = 0; + send = 0; + line = 0; + } + + /* choose which generic packet control to use */ + switch (packet_index) { + case 0: + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC0_CONT, cont, + HDMI_GENERIC0_SEND, send, + HDMI_GENERIC0_LINE, line); + break; + case 1: + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC1_CONT, cont, + HDMI_GENERIC1_SEND, send, + HDMI_GENERIC1_LINE, line); + break; + case 2: + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC0_CONT, cont, + HDMI_GENERIC0_SEND, send, + HDMI_GENERIC0_LINE, line); + break; + case 3: + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC1_CONT, cont, + HDMI_GENERIC1_SEND, send, + HDMI_GENERIC1_LINE, line); + break; + default: + /* invalid HW packet index */ + dm_logger_write( + ctx->logger, LOG_WARNING, + "Invalid HW packet index: %s()\n", + __func__); + return; + } +} + +/* setup stream encoder in dp mode */ +static void dce110_stream_encoder_dp_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + enum dc_color_space output_color_space) +{ + + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + /* set pixel encoding */ + switch (crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_YCBCR422); + break; + case PIXEL_ENCODING_YCBCR444: + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_YCBCR444); + + if (crtc_timing->flags.Y_ONLY) + if (crtc_timing->display_color_depth != COLOR_DEPTH_666) + /* HW testing only, no use case yet. + * Color depth of Y-only could be + * 8, 10, 12, 16 bits */ + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_Y_ONLY); + /* Note: DP_MSA_MISC1 bit 7 is the indicator + * of Y-only mode. + * This bit is set in HW if register + * DP_PIXEL_ENCODING is programmed to 0x4 */ + break; + case PIXEL_ENCODING_YCBCR420: + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_YCBCR420); + if (enc110->se_mask->DP_VID_M_DOUBLE_VALUE_EN) + REG_UPDATE(DP_VID_TIMING, DP_VID_M_DOUBLE_VALUE_EN, 1); + + break; + default: + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_RGB444); + break; + } + + /* set color depth */ + + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_666: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + 0); + break; + case COLOR_DEPTH_888: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + DP_COMPONENT_DEPTH_8BPC); + break; + case COLOR_DEPTH_101010: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + DP_COMPONENT_DEPTH_10BPC); + + break; + case COLOR_DEPTH_121212: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + DP_COMPONENT_DEPTH_12BPC); + break; + default: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + DP_COMPONENT_DEPTH_6BPC); + break; + } + + /* set dynamic range and YCbCr range */ + if (enc110->se_mask->DP_DYN_RANGE && enc110->se_mask->DP_YCBCR_RANGE) + REG_UPDATE_2( + DP_PIXEL_FORMAT, + DP_DYN_RANGE, 0, + DP_YCBCR_RANGE, 0); + +} + +static void dce110_stream_encoder_set_stream_attribute_helper( + struct dce110_stream_encoder *enc110, + struct dc_crtc_timing *crtc_timing) +{ + if (enc110->regs->TMDS_CNTL) { + switch (crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + REG_UPDATE(TMDS_CNTL, TMDS_PIXEL_ENCODING, 1); + break; + default: + REG_UPDATE(TMDS_CNTL, TMDS_PIXEL_ENCODING, 0); + break; + } + REG_UPDATE(TMDS_CNTL, TMDS_COLOR_FORMAT, 0); + } else if (enc110->regs->DIG_FE_CNTL) { + switch (crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + REG_UPDATE(DIG_FE_CNTL, TMDS_PIXEL_ENCODING, 1); + break; + default: + REG_UPDATE(DIG_FE_CNTL, TMDS_PIXEL_ENCODING, 0); + break; + } + REG_UPDATE(DIG_FE_CNTL, TMDS_COLOR_FORMAT, 0); + } + +} + +/* setup stream encoder in hdmi mode */ +static void dce110_stream_encoder_hdmi_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + int actual_pix_clk_khz, + bool enable_audio) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct bp_encoder_control cntl = {0}; + + cntl.action = ENCODER_CONTROL_SETUP; + cntl.engine_id = enc110->base.id; + cntl.signal = SIGNAL_TYPE_HDMI_TYPE_A; + cntl.enable_dp_audio = enable_audio; + cntl.pixel_clock = actual_pix_clk_khz; + cntl.lanes_number = LANE_COUNT_FOUR; + + if (enc110->base.bp->funcs->encoder_control( + enc110->base.bp, &cntl) != BP_RESULT_OK) + return; + + dce110_stream_encoder_set_stream_attribute_helper(enc110, crtc_timing); + + /* setup HDMI engine */ + if (!enc110->se_mask->HDMI_DATA_SCRAMBLE_EN) { + REG_UPDATE_3(HDMI_CONTROL, + HDMI_PACKET_GEN_VERSION, 1, + HDMI_KEEPOUT_MODE, 1, + HDMI_DEEP_COLOR_ENABLE, 0); + } else if (enc110->regs->DIG_FE_CNTL) { + REG_UPDATE_5(HDMI_CONTROL, + HDMI_PACKET_GEN_VERSION, 1, + HDMI_KEEPOUT_MODE, 1, + HDMI_DEEP_COLOR_ENABLE, 0, + HDMI_DATA_SCRAMBLE_EN, 0, + HDMI_CLOCK_CHANNEL_RATE, 0); + } + + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_888: + REG_UPDATE(HDMI_CONTROL, HDMI_DEEP_COLOR_DEPTH, 0); + break; + case COLOR_DEPTH_101010: + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 1, + HDMI_DEEP_COLOR_ENABLE, 1); + break; + case COLOR_DEPTH_121212: + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 2, + HDMI_DEEP_COLOR_ENABLE, 1); + break; + case COLOR_DEPTH_161616: + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 3, + HDMI_DEEP_COLOR_ENABLE, 1); + break; + default: + break; + } + + if (enc110->se_mask->HDMI_DATA_SCRAMBLE_EN) { + if (actual_pix_clk_khz >= HDMI_CLOCK_CHANNEL_RATE_MORE_340M) { + /* enable HDMI data scrambler + * HDMI_CLOCK_CHANNEL_RATE_MORE_340M + * Clock channel frequency is 1/4 of character rate. + */ + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DATA_SCRAMBLE_EN, 1, + HDMI_CLOCK_CHANNEL_RATE, 1); + } else if (crtc_timing->flags.LTE_340MCSC_SCRAMBLE) { + + /* TODO: New feature for DCE11, still need to implement */ + + /* enable HDMI data scrambler + * HDMI_CLOCK_CHANNEL_FREQ_EQUAL_TO_CHAR_RATE + * Clock channel frequency is the same + * as character rate + */ + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DATA_SCRAMBLE_EN, 1, + HDMI_CLOCK_CHANNEL_RATE, 0); + } + } + + REG_UPDATE_3(HDMI_VBI_PACKET_CONTROL, + HDMI_GC_CONT, 1, + HDMI_GC_SEND, 1, + HDMI_NULL_SEND, 1); + + /* following belongs to audio */ + REG_UPDATE(HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_SEND, 1); + + REG_UPDATE(AFMT_INFOFRAME_CONTROL0, AFMT_AUDIO_INFO_UPDATE, 1); + + REG_UPDATE(HDMI_INFOFRAME_CONTROL1, HDMI_AUDIO_INFO_LINE, + VBI_LINE_0 + 2); + + REG_UPDATE(HDMI_GC, HDMI_GC_AVMUTE, 0); + +} + +/* setup stream encoder in dvi mode */ +static void dce110_stream_encoder_dvi_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + bool is_dual_link) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct bp_encoder_control cntl = {0}; + + cntl.action = ENCODER_CONTROL_SETUP; + cntl.engine_id = enc110->base.id; + cntl.signal = is_dual_link ? + SIGNAL_TYPE_DVI_DUAL_LINK : SIGNAL_TYPE_DVI_SINGLE_LINK; + cntl.enable_dp_audio = false; + cntl.pixel_clock = crtc_timing->pix_clk_khz; + cntl.lanes_number = (is_dual_link) ? LANE_COUNT_EIGHT : LANE_COUNT_FOUR; + + if (enc110->base.bp->funcs->encoder_control( + enc110->base.bp, &cntl) != BP_RESULT_OK) + return; + + ASSERT(crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB); + ASSERT(crtc_timing->display_color_depth == COLOR_DEPTH_888); + dce110_stream_encoder_set_stream_attribute_helper(enc110, crtc_timing); +} + +static void dce110_stream_encoder_set_mst_bandwidth( + struct stream_encoder *enc, + struct fixed31_32 avg_time_slots_per_mtp) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t x = dal_fixed31_32_floor( + avg_time_slots_per_mtp); + uint32_t y = dal_fixed31_32_ceil( + dal_fixed31_32_shl( + dal_fixed31_32_sub_int( + avg_time_slots_per_mtp, + x), + 26)); + + { + REG_SET_2(DP_MSE_RATE_CNTL, 0, + DP_MSE_RATE_X, x, + DP_MSE_RATE_Y, y); + } + + /* wait for update to be completed on the link */ + /* i.e. DP_MSE_RATE_UPDATE_PENDING field (read only) */ + /* is reset to 0 (not pending) */ + REG_WAIT(DP_MSE_RATE_UPDATE, DP_MSE_RATE_UPDATE_PENDING, + 0, + 10, DP_MST_UPDATE_MAX_RETRY); +} + +static void dce110_stream_encoder_update_hdmi_info_packets( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + if (enc110->se_mask->HDMI_AVI_INFO_CONT && + enc110->se_mask->HDMI_AVI_INFO_SEND) { + + if (info_frame->avi.valid) { + const uint32_t *content = + (const uint32_t *) &info_frame->avi.sb[0]; + + REG_WRITE(AFMT_AVI_INFO0, content[0]); + + REG_WRITE(AFMT_AVI_INFO1, content[1]); + + REG_WRITE(AFMT_AVI_INFO2, content[2]); + + REG_WRITE(AFMT_AVI_INFO3, content[3]); + + REG_UPDATE(AFMT_AVI_INFO3, AFMT_AVI_INFO_VERSION, + info_frame->avi.hb1); + + REG_UPDATE_2(HDMI_INFOFRAME_CONTROL0, + HDMI_AVI_INFO_SEND, 1, + HDMI_AVI_INFO_CONT, 1); + + REG_UPDATE(HDMI_INFOFRAME_CONTROL1, HDMI_AVI_INFO_LINE, + VBI_LINE_0 + 2); + + } else { + REG_UPDATE_2(HDMI_INFOFRAME_CONTROL0, + HDMI_AVI_INFO_SEND, 0, + HDMI_AVI_INFO_CONT, 0); + } + } + + if (enc110->se_mask->HDMI_AVI_INFO_CONT && + enc110->se_mask->HDMI_AVI_INFO_SEND) { + dce110_update_hdmi_info_packet(enc110, 0, &info_frame->vendor); + dce110_update_hdmi_info_packet(enc110, 1, &info_frame->gamut); + dce110_update_hdmi_info_packet(enc110, 2, &info_frame->spd); + } + +} + +static void dce110_stream_encoder_stop_hdmi_info_packets( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + /* stop generic packets 0 & 1 on HDMI */ + REG_SET_6(HDMI_GENERIC_PACKET_CONTROL0, 0, + HDMI_GENERIC1_CONT, 0, + HDMI_GENERIC1_LINE, 0, + HDMI_GENERIC1_SEND, 0, + HDMI_GENERIC0_CONT, 0, + HDMI_GENERIC0_LINE, 0, + HDMI_GENERIC0_SEND, 0); + + /* stop generic packets 2 & 3 on HDMI */ + REG_SET_6(HDMI_GENERIC_PACKET_CONTROL1, 0, + HDMI_GENERIC0_CONT, 0, + HDMI_GENERIC0_LINE, 0, + HDMI_GENERIC0_SEND, 0, + HDMI_GENERIC1_CONT, 0, + HDMI_GENERIC1_LINE, 0, + HDMI_GENERIC1_SEND, 0); + +} + +static void dce110_stream_encoder_update_dp_info_packets( + struct stream_encoder *enc, + const struct encoder_info_frame *info_frame) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t value = REG_READ(DP_SEC_CNTL); + + if (info_frame->vsc.valid) + dce110_update_generic_info_packet( + enc110, + 0, /* packetIndex */ + &info_frame->vsc); + + /* enable/disable transmission of packet(s). + * If enabled, packet transmission begins on the next frame + */ + REG_UPDATE(DP_SEC_CNTL, DP_SEC_GSP0_ENABLE, info_frame->vsc.valid); + + /* This bit is the master enable bit. + * When enabling secondary stream engine, + * this master bit must also be set. + * This register shared with audio info frame. + * Therefore we need to enable master bit + * if at least on of the fields is not 0 + */ + if (value) + REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1); +} + +static void dce110_stream_encoder_stop_dp_info_packets( + struct stream_encoder *enc) +{ + /* stop generic packets on DP */ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t value = REG_READ(DP_SEC_CNTL); + + if (enc110->se_mask->DP_SEC_AVI_ENABLE) { + REG_SET_7(DP_SEC_CNTL, 0, + DP_SEC_GSP0_ENABLE, 0, + DP_SEC_GSP1_ENABLE, 0, + DP_SEC_GSP2_ENABLE, 0, + DP_SEC_GSP3_ENABLE, 0, + DP_SEC_AVI_ENABLE, 0, + DP_SEC_MPG_ENABLE, 0, + DP_SEC_STREAM_ENABLE, 0); + } + + /* this register shared with audio info frame. + * therefore we need to keep master enabled + * if at least one of the fields is not 0 */ + + if (value) + REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1); + +} + +static void dce110_stream_encoder_dp_blank( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t retries = 0; + uint32_t max_retries = DP_BLANK_MAX_RETRY * 10; + + /* Note: For CZ, we are changing driver default to disable + * stream deferred to next VBLANK. If results are positive, we + * will make the same change to all DCE versions. There are a + * handful of panels that cannot handle disable stream at + * HBLANK and will result in a white line flash across the + * screen on stream disable. */ + + /* Specify the video stream disable point + * (2 = start of the next vertical blank) */ + REG_UPDATE(DP_VID_STREAM_CNTL, DP_VID_STREAM_DIS_DEFER, 2); + /* Larger delay to wait until VBLANK - use max retry of + * 10us*3000=30ms. This covers 16.6ms of typical 60 Hz mode + + * a little more because we may not trust delay accuracy. + */ + max_retries = DP_BLANK_MAX_RETRY * 150; + + /* disable DP stream */ + REG_UPDATE(DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, 0); + + /* the encoder stops sending the video stream + * at the start of the vertical blanking. + * Poll for DP_VID_STREAM_STATUS == 0 + */ + + REG_WAIT(DP_VID_STREAM_CNTL, DP_VID_STREAM_STATUS, + 1, + 10, max_retries); + + ASSERT(retries <= max_retries); + + /* Tell the DP encoder to ignore timing from CRTC, must be done after + * the polling. If we set DP_STEER_FIFO_RESET before DP stream blank is + * complete, stream status will be stuck in video stream enabled state, + * i.e. DP_VID_STREAM_STATUS stuck at 1. + */ + + REG_UPDATE(DP_STEER_FIFO, DP_STEER_FIFO_RESET, true); +} + +/* output video stream to link encoder */ +static void dce110_stream_encoder_dp_unblank( + struct stream_encoder *enc, + const struct encoder_unblank_param *param) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + if (param->link_settings.link_rate != LINK_RATE_UNKNOWN) { + uint32_t n_vid = 0x8000; + uint32_t m_vid; + + /* M / N = Fstream / Flink + * m_vid / n_vid = pixel rate / link rate + */ + + uint64_t m_vid_l = n_vid; + + m_vid_l *= param->crtc_timing.pixel_clock; + m_vid_l = div_u64(m_vid_l, + param->link_settings.link_rate + * LINK_RATE_REF_FREQ_IN_KHZ); + + m_vid = (uint32_t) m_vid_l; + + /* enable auto measurement */ + + REG_UPDATE(DP_VID_TIMING, DP_VID_M_N_GEN_EN, 0); + + /* auto measurement need 1 full 0x8000 symbol cycle to kick in, + * therefore program initial value for Mvid and Nvid + */ + + REG_UPDATE(DP_VID_N, DP_VID_N, n_vid); + + REG_UPDATE(DP_VID_M, DP_VID_M, m_vid); + + REG_UPDATE(DP_VID_TIMING, DP_VID_M_N_GEN_EN, 1); + } + + /* set DIG_START to 0x1 to resync FIFO */ + + REG_UPDATE(DIG_FE_CNTL, DIG_START, 1); + + /* switch DP encoder to CRTC data */ + + REG_UPDATE(DP_STEER_FIFO, DP_STEER_FIFO_RESET, 0); + + /* wait 100us for DIG/DP logic to prime + * (i.e. a few video lines) + */ + udelay(100); + + /* the hardware would start sending video at the start of the next DP + * frame (i.e. rising edge of the vblank). + * NOTE: We used to program DP_VID_STREAM_DIS_DEFER = 2 here, but this + * register has no effect on enable transition! HW always guarantees + * VID_STREAM enable at start of next frame, and this is not + * programmable + */ + + REG_UPDATE(DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, true); +} + + +#define DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT 0x8000 +#define DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC 1 + +#include "include/audio_types.h" + +/** +* speakersToChannels +* +* @brief +* translate speakers to channels +* +* FL - Front Left +* FR - Front Right +* RL - Rear Left +* RR - Rear Right +* RC - Rear Center +* FC - Front Center +* FLC - Front Left Center +* FRC - Front Right Center +* RLC - Rear Left Center +* RRC - Rear Right Center +* LFE - Low Freq Effect +* +* FC +* FLC FRC +* FL FR +* +* LFE +* () +* +* +* RL RR +* RLC RRC +* RC +* +* ch 8 7 6 5 4 3 2 1 +* 0b00000011 - - - - - - FR FL +* 0b00000111 - - - - - LFE FR FL +* 0b00001011 - - - - FC - FR FL +* 0b00001111 - - - - FC LFE FR FL +* 0b00010011 - - - RC - - FR FL +* 0b00010111 - - - RC - LFE FR FL +* 0b00011011 - - - RC FC - FR FL +* 0b00011111 - - - RC FC LFE FR FL +* 0b00110011 - - RR RL - - FR FL +* 0b00110111 - - RR RL - LFE FR FL +* 0b00111011 - - RR RL FC - FR FL +* 0b00111111 - - RR RL FC LFE FR FL +* 0b01110011 - RC RR RL - - FR FL +* 0b01110111 - RC RR RL - LFE FR FL +* 0b01111011 - RC RR RL FC - FR FL +* 0b01111111 - RC RR RL FC LFE FR FL +* 0b11110011 RRC RLC RR RL - - FR FL +* 0b11110111 RRC RLC RR RL - LFE FR FL +* 0b11111011 RRC RLC RR RL FC - FR FL +* 0b11111111 RRC RLC RR RL FC LFE FR FL +* 0b11000011 FRC FLC - - - - FR FL +* 0b11000111 FRC FLC - - - LFE FR FL +* 0b11001011 FRC FLC - - FC - FR FL +* 0b11001111 FRC FLC - - FC LFE FR FL +* 0b11010011 FRC FLC - RC - - FR FL +* 0b11010111 FRC FLC - RC - LFE FR FL +* 0b11011011 FRC FLC - RC FC - FR FL +* 0b11011111 FRC FLC - RC FC LFE FR FL +* 0b11110011 FRC FLC RR RL - - FR FL +* 0b11110111 FRC FLC RR RL - LFE FR FL +* 0b11111011 FRC FLC RR RL FC - FR FL +* 0b11111111 FRC FLC RR RL FC LFE FR FL +* +* @param +* speakers - speaker information as it comes from CEA audio block +*/ +/* translate speakers to channels */ + +union audio_cea_channels { + uint8_t all; + struct audio_cea_channels_bits { + uint32_t FL:1; + uint32_t FR:1; + uint32_t LFE:1; + uint32_t FC:1; + uint32_t RL_RC:1; + uint32_t RR:1; + uint32_t RC_RLC_FLC:1; + uint32_t RRC_FRC:1; + } channels; +}; + +struct audio_clock_info { + /* pixel clock frequency*/ + uint32_t pixel_clock_in_10khz; + /* N - 32KHz audio */ + uint32_t n_32khz; + /* CTS - 32KHz audio*/ + uint32_t cts_32khz; + uint32_t n_44khz; + uint32_t cts_44khz; + uint32_t n_48khz; + uint32_t cts_48khz; +}; + +/* 25.2MHz/1.001*/ +/* 25.2MHz/1.001*/ +/* 25.2MHz*/ +/* 27MHz */ +/* 27MHz*1.001*/ +/* 27MHz*1.001*/ +/* 54MHz*/ +/* 54MHz*1.001*/ +/* 74.25MHz/1.001*/ +/* 74.25MHz*/ +/* 148.5MHz/1.001*/ +/* 148.5MHz*/ + +static const struct audio_clock_info audio_clock_info_table[12] = { + {2517, 4576, 28125, 7007, 31250, 6864, 28125}, + {2518, 4576, 28125, 7007, 31250, 6864, 28125}, + {2520, 4096, 25200, 6272, 28000, 6144, 25200}, + {2700, 4096, 27000, 6272, 30000, 6144, 27000}, + {2702, 4096, 27027, 6272, 30030, 6144, 27027}, + {2703, 4096, 27027, 6272, 30030, 6144, 27027}, + {5400, 4096, 54000, 6272, 60000, 6144, 54000}, + {5405, 4096, 54054, 6272, 60060, 6144, 54054}, + {7417, 11648, 210937, 17836, 234375, 11648, 140625}, + {7425, 4096, 74250, 6272, 82500, 6144, 74250}, + {14835, 11648, 421875, 8918, 234375, 5824, 140625}, + {14850, 4096, 148500, 6272, 165000, 6144, 148500} +}; + +static const struct audio_clock_info audio_clock_info_table_36bpc[12] = { + {2517, 9152, 84375, 7007, 48875, 9152, 56250}, + {2518, 9152, 84375, 7007, 48875, 9152, 56250}, + {2520, 4096, 37800, 6272, 42000, 6144, 37800}, + {2700, 4096, 40500, 6272, 45000, 6144, 40500}, + {2702, 8192, 81081, 6272, 45045, 8192, 54054}, + {2703, 8192, 81081, 6272, 45045, 8192, 54054}, + {5400, 4096, 81000, 6272, 90000, 6144, 81000}, + {5405, 4096, 81081, 6272, 90090, 6144, 81081}, + {7417, 11648, 316406, 17836, 351562, 11648, 210937}, + {7425, 4096, 111375, 6272, 123750, 6144, 111375}, + {14835, 11648, 632812, 17836, 703125, 11648, 421875}, + {14850, 4096, 222750, 6272, 247500, 6144, 222750} +}; + +static const struct audio_clock_info audio_clock_info_table_48bpc[12] = { + {2517, 4576, 56250, 7007, 62500, 6864, 56250}, + {2518, 4576, 56250, 7007, 62500, 6864, 56250}, + {2520, 4096, 50400, 6272, 56000, 6144, 50400}, + {2700, 4096, 54000, 6272, 60000, 6144, 54000}, + {2702, 4096, 54054, 6267, 60060, 8192, 54054}, + {2703, 4096, 54054, 6272, 60060, 8192, 54054}, + {5400, 4096, 108000, 6272, 120000, 6144, 108000}, + {5405, 4096, 108108, 6272, 120120, 6144, 108108}, + {7417, 11648, 421875, 17836, 468750, 11648, 281250}, + {7425, 4096, 148500, 6272, 165000, 6144, 148500}, + {14835, 11648, 843750, 8918, 468750, 11648, 281250}, + {14850, 4096, 297000, 6272, 330000, 6144, 297000} +}; + +union audio_cea_channels speakers_to_channels( + struct audio_speaker_flags speaker_flags) +{ + union audio_cea_channels cea_channels = {0}; + + /* these are one to one */ + cea_channels.channels.FL = speaker_flags.FL_FR; + cea_channels.channels.FR = speaker_flags.FL_FR; + cea_channels.channels.LFE = speaker_flags.LFE; + cea_channels.channels.FC = speaker_flags.FC; + + /* if Rear Left and Right exist move RC speaker to channel 7 + * otherwise to channel 5 + */ + if (speaker_flags.RL_RR) { + cea_channels.channels.RL_RC = speaker_flags.RL_RR; + cea_channels.channels.RR = speaker_flags.RL_RR; + cea_channels.channels.RC_RLC_FLC = speaker_flags.RC; + } else { + cea_channels.channels.RL_RC = speaker_flags.RC; + } + + /* FRONT Left Right Center and REAR Left Right Center are exclusive */ + if (speaker_flags.FLC_FRC) { + cea_channels.channels.RC_RLC_FLC = speaker_flags.FLC_FRC; + cea_channels.channels.RRC_FRC = speaker_flags.FLC_FRC; + } else { + cea_channels.channels.RC_RLC_FLC = speaker_flags.RLC_RRC; + cea_channels.channels.RRC_FRC = speaker_flags.RLC_RRC; + } + + return cea_channels; +} + +uint32_t calc_max_audio_packets_per_line( + const struct audio_crtc_info *crtc_info) +{ + uint32_t max_packets_per_line; + + max_packets_per_line = + crtc_info->h_total - crtc_info->h_active; + + if (crtc_info->pixel_repetition) + max_packets_per_line *= crtc_info->pixel_repetition; + + /* for other hdmi features */ + max_packets_per_line -= 58; + /* for Control Period */ + max_packets_per_line -= 16; + /* Number of Audio Packets per Line */ + max_packets_per_line /= 32; + + return max_packets_per_line; +} + +bool get_audio_clock_info( + enum dc_color_depth color_depth, + uint32_t crtc_pixel_clock_in_khz, + uint32_t actual_pixel_clock_in_khz, + struct audio_clock_info *audio_clock_info) +{ + const struct audio_clock_info *clock_info; + uint32_t index; + uint32_t crtc_pixel_clock_in_10khz = crtc_pixel_clock_in_khz / 10; + uint32_t audio_array_size; + + if (audio_clock_info == NULL) + return false; /* should not happen */ + + switch (color_depth) { + case COLOR_DEPTH_161616: + clock_info = audio_clock_info_table_48bpc; + audio_array_size = ARRAY_SIZE( + audio_clock_info_table_48bpc); + break; + case COLOR_DEPTH_121212: + clock_info = audio_clock_info_table_36bpc; + audio_array_size = ARRAY_SIZE( + audio_clock_info_table_36bpc); + break; + default: + clock_info = audio_clock_info_table; + audio_array_size = ARRAY_SIZE( + audio_clock_info_table); + break; + } + + if (clock_info != NULL) { + /* search for exact pixel clock in table */ + for (index = 0; index < audio_array_size; index++) { + if (clock_info[index].pixel_clock_in_10khz > + crtc_pixel_clock_in_10khz) + break; /* not match */ + else if (clock_info[index].pixel_clock_in_10khz == + crtc_pixel_clock_in_10khz) { + /* match found */ + *audio_clock_info = clock_info[index]; + return true; + } + } + } + + /* not found */ + if (actual_pixel_clock_in_khz == 0) + actual_pixel_clock_in_khz = crtc_pixel_clock_in_khz; + + /* See HDMI spec the table entry under + * pixel clock of "Other". */ + audio_clock_info->pixel_clock_in_10khz = + actual_pixel_clock_in_khz / 10; + audio_clock_info->cts_32khz = actual_pixel_clock_in_khz; + audio_clock_info->cts_44khz = actual_pixel_clock_in_khz; + audio_clock_info->cts_48khz = actual_pixel_clock_in_khz; + + audio_clock_info->n_32khz = 4096; + audio_clock_info->n_44khz = 6272; + audio_clock_info->n_48khz = 6144; + + return true; +} + +static void dce110_se_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *audio_info) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + uint32_t speakers = 0; + uint32_t channels = 0; + + ASSERT(audio_info); + if (audio_info == NULL) + /* This should not happen.it does so we don't get BSOD*/ + return; + + speakers = audio_info->flags.info.ALLSPEAKERS; + channels = speakers_to_channels(audio_info->flags.speaker_flags).all; + + /* setup the audio stream source select (audio -> dig mapping) */ + REG_SET(AFMT_AUDIO_SRC_CONTROL, 0, AFMT_AUDIO_SRC_SELECT, az_inst); + + /* Channel allocation */ + REG_UPDATE(AFMT_AUDIO_PACKET_CONTROL2, AFMT_AUDIO_CHANNEL_ENABLE, channels); +} + +static void dce110_se_setup_hdmi_audio( + struct stream_encoder *enc, + const struct audio_crtc_info *crtc_info) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + struct audio_clock_info audio_clock_info = {0}; + uint32_t max_packets_per_line; + + /* For now still do calculation, although this field is ignored when + above HDMI_PACKET_GEN_VERSION set to 1 */ + max_packets_per_line = calc_max_audio_packets_per_line(crtc_info); + + /* HDMI_AUDIO_PACKET_CONTROL */ + REG_UPDATE_2(HDMI_AUDIO_PACKET_CONTROL, + HDMI_AUDIO_PACKETS_PER_LINE, max_packets_per_line, + HDMI_AUDIO_DELAY_EN, 1); + + /* AFMT_AUDIO_PACKET_CONTROL */ + REG_UPDATE(AFMT_AUDIO_PACKET_CONTROL, AFMT_60958_CS_UPDATE, 1); + + /* AFMT_AUDIO_PACKET_CONTROL2 */ + REG_UPDATE_2(AFMT_AUDIO_PACKET_CONTROL2, + AFMT_AUDIO_LAYOUT_OVRD, 0, + AFMT_60958_OSF_OVRD, 0); + + /* HDMI_ACR_PACKET_CONTROL */ + REG_UPDATE_3(HDMI_ACR_PACKET_CONTROL, + HDMI_ACR_AUTO_SEND, 1, + HDMI_ACR_SOURCE, 0, + HDMI_ACR_AUDIO_PRIORITY, 0); + + /* Program audio clock sample/regeneration parameters */ + if (get_audio_clock_info( + crtc_info->color_depth, + crtc_info->requested_pixel_clock, + crtc_info->calculated_pixel_clock, + &audio_clock_info)) { + + /* HDMI_ACR_32_0__HDMI_ACR_CTS_32_MASK */ + REG_UPDATE(HDMI_ACR_32_0, HDMI_ACR_CTS_32, audio_clock_info.cts_32khz); + + /* HDMI_ACR_32_1__HDMI_ACR_N_32_MASK */ + REG_UPDATE(HDMI_ACR_32_1, HDMI_ACR_N_32, audio_clock_info.n_32khz); + + /* HDMI_ACR_44_0__HDMI_ACR_CTS_44_MASK */ + REG_UPDATE(HDMI_ACR_44_0, HDMI_ACR_CTS_44, audio_clock_info.cts_44khz); + + /* HDMI_ACR_44_1__HDMI_ACR_N_44_MASK */ + REG_UPDATE(HDMI_ACR_44_1, HDMI_ACR_N_44, audio_clock_info.n_44khz); + + /* HDMI_ACR_48_0__HDMI_ACR_CTS_48_MASK */ + REG_UPDATE(HDMI_ACR_48_0, HDMI_ACR_CTS_48, audio_clock_info.cts_48khz); + + /* HDMI_ACR_48_1__HDMI_ACR_N_48_MASK */ + REG_UPDATE(HDMI_ACR_48_1, HDMI_ACR_N_48, audio_clock_info.n_48khz); + + /* Video driver cannot know in advance which sample rate will + be used by HD Audio driver + HDMI_ACR_PACKET_CONTROL__HDMI_ACR_N_MULTIPLE field is + programmed below in interruppt callback */ + } /* if */ + + /* AFMT_60958_0__AFMT_60958_CS_CHANNEL_NUMBER_L_MASK & + AFMT_60958_0__AFMT_60958_CS_CLOCK_ACCURACY_MASK */ + REG_UPDATE_2(AFMT_60958_0, + AFMT_60958_CS_CHANNEL_NUMBER_L, 1, + AFMT_60958_CS_CLOCK_ACCURACY, 0); + + /* AFMT_60958_1 AFMT_60958_CS_CHALNNEL_NUMBER_R */ + REG_UPDATE(AFMT_60958_1, AFMT_60958_CS_CHANNEL_NUMBER_R, 2); + + /*AFMT_60958_2 now keep this settings until + * Programming guide comes out*/ + REG_UPDATE_6(AFMT_60958_2, + AFMT_60958_CS_CHANNEL_NUMBER_2, 3, + AFMT_60958_CS_CHANNEL_NUMBER_3, 4, + AFMT_60958_CS_CHANNEL_NUMBER_4, 5, + AFMT_60958_CS_CHANNEL_NUMBER_5, 6, + AFMT_60958_CS_CHANNEL_NUMBER_6, 7, + AFMT_60958_CS_CHANNEL_NUMBER_7, 8); +} + +static void dce110_se_setup_dp_audio( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + /* --- DP Audio packet configurations --- */ + + /* ATP Configuration */ + REG_SET(DP_SEC_AUD_N, 0, + DP_SEC_AUD_N, DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT); + + /* Async/auto-calc timestamp mode */ + REG_SET(DP_SEC_TIMESTAMP, 0, DP_SEC_TIMESTAMP_MODE, + DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC); + + /* --- The following are the registers + * copied from the SetupHDMI --- */ + + /* AFMT_AUDIO_PACKET_CONTROL */ + REG_UPDATE(AFMT_AUDIO_PACKET_CONTROL, AFMT_60958_CS_UPDATE, 1); + + /* AFMT_AUDIO_PACKET_CONTROL2 */ + /* Program the ATP and AIP next */ + REG_UPDATE_2(AFMT_AUDIO_PACKET_CONTROL2, + AFMT_AUDIO_LAYOUT_OVRD, 0, + AFMT_60958_OSF_OVRD, 0); + + /* AFMT_INFOFRAME_CONTROL0 */ + REG_UPDATE(AFMT_INFOFRAME_CONTROL0, AFMT_AUDIO_INFO_UPDATE, 1); + + /* AFMT_60958_0__AFMT_60958_CS_CLOCK_ACCURACY_MASK */ + REG_UPDATE(AFMT_60958_0, AFMT_60958_CS_CLOCK_ACCURACY, 0); +} + +static void dce110_se_enable_audio_clock( + struct stream_encoder *enc, + bool enable) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + if (REG(AFMT_CNTL) == 0) + return; /* DCE8/10 does not have this register */ + + REG_UPDATE(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, !!enable); + + /* wait for AFMT clock to turn on, + * expectation: this should complete in 1-2 reads + * + * REG_WAIT(AFMT_CNTL, AFMT_AUDIO_CLOCK_ON, !!enable, 1, 10); + * + * TODO: wait for clock_on does not work well. May need HW + * program sequence. But audio seems work normally even without wait + * for clock_on status change + */ +} + +static void dce110_se_enable_dp_audio( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + /* Enable Audio packets */ + REG_UPDATE(DP_SEC_CNTL, DP_SEC_ASP_ENABLE, 1); + + /* Program the ATP and AIP next */ + REG_UPDATE_2(DP_SEC_CNTL, + DP_SEC_ATP_ENABLE, 1, + DP_SEC_AIP_ENABLE, 1); + + /* Program STREAM_ENABLE after all the other enables. */ + REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1); +} + +static void dce110_se_disable_dp_audio( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t value = REG_READ(DP_SEC_CNTL); + + /* Disable Audio packets */ + REG_UPDATE_5(DP_SEC_CNTL, + DP_SEC_ASP_ENABLE, 0, + DP_SEC_ATP_ENABLE, 0, + DP_SEC_AIP_ENABLE, 0, + DP_SEC_ACM_ENABLE, 0, + DP_SEC_STREAM_ENABLE, 0); + + /* This register shared with encoder info frame. Therefore we need to + keep master enabled if at least on of the fields is not 0 */ + if (value != 0) + REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1); + +} + +void dce110_se_audio_mute_control( + struct stream_encoder *enc, + bool mute) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + REG_UPDATE(AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND, !mute); +} + +void dce110_se_dp_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info) +{ + dce110_se_audio_setup(enc, az_inst, info); +} + +void dce110_se_dp_audio_enable( + struct stream_encoder *enc) +{ + dce110_se_enable_audio_clock(enc, true); + dce110_se_setup_dp_audio(enc); + dce110_se_enable_dp_audio(enc); +} + +void dce110_se_dp_audio_disable( + struct stream_encoder *enc) +{ + dce110_se_disable_dp_audio(enc); + dce110_se_enable_audio_clock(enc, false); +} + +void dce110_se_hdmi_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info) +{ + dce110_se_enable_audio_clock(enc, true); + dce110_se_setup_hdmi_audio(enc, audio_crtc_info); + dce110_se_audio_setup(enc, az_inst, info); +} + +void dce110_se_hdmi_audio_disable( + struct stream_encoder *enc) +{ + dce110_se_enable_audio_clock(enc, false); +} + +static const struct stream_encoder_funcs dce110_str_enc_funcs = { + .dp_set_stream_attribute = + dce110_stream_encoder_dp_set_stream_attribute, + .hdmi_set_stream_attribute = + dce110_stream_encoder_hdmi_set_stream_attribute, + .dvi_set_stream_attribute = + dce110_stream_encoder_dvi_set_stream_attribute, + .set_mst_bandwidth = + dce110_stream_encoder_set_mst_bandwidth, + .update_hdmi_info_packets = + dce110_stream_encoder_update_hdmi_info_packets, + .stop_hdmi_info_packets = + dce110_stream_encoder_stop_hdmi_info_packets, + .update_dp_info_packets = + dce110_stream_encoder_update_dp_info_packets, + .stop_dp_info_packets = + dce110_stream_encoder_stop_dp_info_packets, + .dp_blank = + dce110_stream_encoder_dp_blank, + .dp_unblank = + dce110_stream_encoder_dp_unblank, + + .audio_mute_control = dce110_se_audio_mute_control, + + .dp_audio_setup = dce110_se_dp_audio_setup, + .dp_audio_enable = dce110_se_dp_audio_enable, + .dp_audio_disable = dce110_se_dp_audio_disable, + + .hdmi_audio_setup = dce110_se_hdmi_audio_setup, + .hdmi_audio_disable = dce110_se_hdmi_audio_disable, +}; + +bool dce110_stream_encoder_construct( + struct dce110_stream_encoder *enc110, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + const struct dce110_stream_enc_registers *regs, + const struct dce_stream_encoder_shift *se_shift, + const struct dce_stream_encoder_mask *se_mask) +{ + if (!enc110) + return false; + if (!bp) + return false; + + enc110->base.funcs = &dce110_str_enc_funcs; + enc110->base.ctx = ctx; + enc110->base.id = eng_id; + enc110->base.bp = bp; + enc110->regs = regs; + enc110->se_shift = se_shift; + enc110->se_mask = se_mask; + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h new file mode 100644 index 000000000000..458a37000956 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h @@ -0,0 +1,564 @@ +/* + * Copyright 2012-15 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. + * + * Authors: AMD + * + */ + +#ifndef __DC_STREAM_ENCODER_DCE110_H__ +#define __DC_STREAM_ENCODER_DCE110_H__ + +#include "stream_encoder.h" + +#define DCE110STRENC_FROM_STRENC(stream_encoder)\ + container_of(stream_encoder, struct dce110_stream_encoder, base) + +#ifndef TMDS_CNTL__TMDS_PIXEL_ENCODING_MASK + #define TMDS_CNTL__TMDS_PIXEL_ENCODING_MASK 0x00000010L + #define TMDS_CNTL__TMDS_COLOR_FORMAT_MASK 0x00000300L + #define TMDS_CNTL__TMDS_PIXEL_ENCODING__SHIFT 0x00000004 + #define TMDS_CNTL__TMDS_COLOR_FORMAT__SHIFT 0x00000008 +#endif + + +#define SE_COMMON_REG_LIST_DCE_BASE(id) \ + SE_COMMON_REG_LIST_BASE(id),\ + SRI(AFMT_AVI_INFO0, DIG, id), \ + SRI(AFMT_AVI_INFO1, DIG, id), \ + SRI(AFMT_AVI_INFO2, DIG, id), \ + SRI(AFMT_AVI_INFO3, DIG, id) + +#define SE_COMMON_REG_LIST_BASE(id) \ + SRI(AFMT_GENERIC_0, DIG, id), \ + SRI(AFMT_GENERIC_1, DIG, id), \ + SRI(AFMT_GENERIC_2, DIG, id), \ + SRI(AFMT_GENERIC_3, DIG, id), \ + SRI(AFMT_GENERIC_4, DIG, id), \ + SRI(AFMT_GENERIC_5, DIG, id), \ + SRI(AFMT_GENERIC_6, DIG, id), \ + SRI(AFMT_GENERIC_7, DIG, id), \ + SRI(AFMT_GENERIC_HDR, DIG, id), \ + SRI(AFMT_INFOFRAME_CONTROL0, DIG, id), \ + SRI(AFMT_VBI_PACKET_CONTROL, DIG, id), \ + SRI(AFMT_AUDIO_PACKET_CONTROL, DIG, id), \ + SRI(AFMT_AUDIO_PACKET_CONTROL2, DIG, id), \ + SRI(AFMT_AUDIO_SRC_CONTROL, DIG, id), \ + SRI(AFMT_60958_0, DIG, id), \ + SRI(AFMT_60958_1, DIG, id), \ + SRI(AFMT_60958_2, DIG, id), \ + SRI(DIG_FE_CNTL, DIG, id), \ + SRI(HDMI_CONTROL, DIG, id), \ + SRI(HDMI_GC, DIG, id), \ + SRI(HDMI_GENERIC_PACKET_CONTROL0, DIG, id), \ + SRI(HDMI_GENERIC_PACKET_CONTROL1, DIG, id), \ + SRI(HDMI_INFOFRAME_CONTROL0, DIG, id), \ + SRI(HDMI_INFOFRAME_CONTROL1, DIG, id), \ + SRI(HDMI_VBI_PACKET_CONTROL, DIG, id), \ + SRI(HDMI_AUDIO_PACKET_CONTROL, DIG, id),\ + SRI(HDMI_ACR_PACKET_CONTROL, DIG, id),\ + SRI(HDMI_ACR_32_0, DIG, id),\ + SRI(HDMI_ACR_32_1, DIG, id),\ + SRI(HDMI_ACR_44_0, DIG, id),\ + SRI(HDMI_ACR_44_1, DIG, id),\ + SRI(HDMI_ACR_48_0, DIG, id),\ + SRI(HDMI_ACR_48_1, DIG, id),\ + SRI(TMDS_CNTL, DIG, id), \ + SRI(DP_MSE_RATE_CNTL, DP, id), \ + SRI(DP_MSE_RATE_UPDATE, DP, id), \ + SRI(DP_PIXEL_FORMAT, DP, id), \ + SRI(DP_SEC_CNTL, DP, id), \ + SRI(DP_STEER_FIFO, DP, id), \ + SRI(DP_VID_M, DP, id), \ + SRI(DP_VID_N, DP, id), \ + SRI(DP_VID_STREAM_CNTL, DP, id), \ + SRI(DP_VID_TIMING, DP, id), \ + SRI(DP_SEC_AUD_N, DP, id), \ + SRI(DP_SEC_TIMESTAMP, DP, id) + +#define SE_COMMON_REG_LIST(id)\ + SE_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(AFMT_CNTL, DIG, id) + +#define SE_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define SE_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh)\ + SE_SF(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_INDEX, mask_sh),\ + SE_SF(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC0_UPDATE, mask_sh),\ + SE_SF(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC2_UPDATE, mask_sh),\ + SE_SF(AFMT_GENERIC_HDR, AFMT_GENERIC_HB0, mask_sh),\ + SE_SF(AFMT_GENERIC_HDR, AFMT_GENERIC_HB1, mask_sh),\ + SE_SF(AFMT_GENERIC_HDR, AFMT_GENERIC_HB2, mask_sh),\ + SE_SF(AFMT_GENERIC_HDR, AFMT_GENERIC_HB3, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_CONT, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_SEND, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_LINE, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_CONT, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_SEND, mask_sh),\ + SE_SF(HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_LINE, mask_sh),\ + SE_SF(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, mask_sh),\ + SE_SF(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, mask_sh),\ + SE_SF(DP_PIXEL_FORMAT, DP_DYN_RANGE, mask_sh),\ + SE_SF(DP_PIXEL_FORMAT, DP_YCBCR_RANGE, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_PACKET_GEN_VERSION, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_KEEPOUT_MODE, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_DEEP_COLOR_ENABLE, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_DEEP_COLOR_DEPTH, mask_sh),\ + SE_SF(HDMI_VBI_PACKET_CONTROL, HDMI_GC_CONT, mask_sh),\ + SE_SF(HDMI_VBI_PACKET_CONTROL, HDMI_GC_SEND, mask_sh),\ + SE_SF(HDMI_VBI_PACKET_CONTROL, HDMI_NULL_SEND, mask_sh),\ + SE_SF(HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_SEND, mask_sh),\ + SE_SF(AFMT_INFOFRAME_CONTROL0, AFMT_AUDIO_INFO_UPDATE, mask_sh),\ + SE_SF(HDMI_INFOFRAME_CONTROL1, HDMI_AUDIO_INFO_LINE, mask_sh),\ + SE_SF(HDMI_GC, HDMI_GC_AVMUTE, mask_sh),\ + SE_SF(DP_MSE_RATE_CNTL, DP_MSE_RATE_X, mask_sh),\ + SE_SF(DP_MSE_RATE_CNTL, DP_MSE_RATE_Y, mask_sh),\ + SE_SF(DP_MSE_RATE_UPDATE, DP_MSE_RATE_UPDATE_PENDING, mask_sh),\ + SE_SF(AFMT_AVI_INFO3, AFMT_AVI_INFO_VERSION, mask_sh),\ + SE_SF(HDMI_INFOFRAME_CONTROL0, HDMI_AVI_INFO_SEND, mask_sh),\ + SE_SF(HDMI_INFOFRAME_CONTROL0, HDMI_AVI_INFO_CONT, mask_sh),\ + SE_SF(HDMI_INFOFRAME_CONTROL1, HDMI_AVI_INFO_LINE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_GSP0_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_GSP1_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_GSP2_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_GSP3_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_AVI_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_MPG_ENABLE, mask_sh),\ + SE_SF(DP_VID_STREAM_CNTL, DP_VID_STREAM_DIS_DEFER, mask_sh),\ + SE_SF(DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, mask_sh),\ + SE_SF(DP_VID_STREAM_CNTL, DP_VID_STREAM_STATUS, mask_sh),\ + SE_SF(DP_STEER_FIFO, DP_STEER_FIFO_RESET, mask_sh),\ + SE_SF(DP_VID_TIMING, DP_VID_M_N_GEN_EN, mask_sh),\ + SE_SF(DP_VID_N, DP_VID_N, mask_sh),\ + SE_SF(DP_VID_M, DP_VID_M, mask_sh),\ + SE_SF(DIG_FE_CNTL, DIG_START, mask_sh),\ + SE_SF(AFMT_AUDIO_SRC_CONTROL, AFMT_AUDIO_SRC_SELECT, mask_sh),\ + SE_SF(AFMT_AUDIO_PACKET_CONTROL2, AFMT_AUDIO_CHANNEL_ENABLE, mask_sh),\ + SE_SF(HDMI_AUDIO_PACKET_CONTROL, HDMI_AUDIO_PACKETS_PER_LINE, mask_sh),\ + SE_SF(HDMI_AUDIO_PACKET_CONTROL, HDMI_AUDIO_DELAY_EN, mask_sh),\ + SE_SF(AFMT_AUDIO_PACKET_CONTROL, AFMT_60958_CS_UPDATE, mask_sh),\ + SE_SF(AFMT_AUDIO_PACKET_CONTROL2, AFMT_AUDIO_LAYOUT_OVRD, mask_sh),\ + SE_SF(AFMT_AUDIO_PACKET_CONTROL2, AFMT_60958_OSF_OVRD, mask_sh),\ + SE_SF(HDMI_ACR_PACKET_CONTROL, HDMI_ACR_AUTO_SEND, mask_sh),\ + SE_SF(HDMI_ACR_PACKET_CONTROL, HDMI_ACR_SOURCE, mask_sh),\ + SE_SF(HDMI_ACR_PACKET_CONTROL, HDMI_ACR_AUDIO_PRIORITY, mask_sh),\ + SE_SF(HDMI_ACR_32_0, HDMI_ACR_CTS_32, mask_sh),\ + SE_SF(HDMI_ACR_32_1, HDMI_ACR_N_32, mask_sh),\ + SE_SF(HDMI_ACR_44_0, HDMI_ACR_CTS_44, mask_sh),\ + SE_SF(HDMI_ACR_44_1, HDMI_ACR_N_44, mask_sh),\ + SE_SF(HDMI_ACR_48_0, HDMI_ACR_CTS_48, mask_sh),\ + SE_SF(HDMI_ACR_48_1, HDMI_ACR_N_48, mask_sh),\ + SE_SF(AFMT_60958_0, AFMT_60958_CS_CHANNEL_NUMBER_L, mask_sh),\ + SE_SF(AFMT_60958_0, AFMT_60958_CS_CLOCK_ACCURACY, mask_sh),\ + SE_SF(AFMT_60958_1, AFMT_60958_CS_CHANNEL_NUMBER_R, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_2, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_3, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_4, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_5, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_6, mask_sh),\ + SE_SF(AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_7, mask_sh),\ + SE_SF(DP_SEC_AUD_N, DP_SEC_AUD_N, mask_sh),\ + SE_SF(DP_SEC_TIMESTAMP, DP_SEC_TIMESTAMP_MODE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_ASP_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_ATP_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_AIP_ENABLE, mask_sh),\ + SE_SF(DP_SEC_CNTL, DP_SEC_ACM_ENABLE, mask_sh),\ + SE_SF(AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND, mask_sh) + +#define SE_COMMON_MASK_SH_LIST_DCE_COMMON(mask_sh)\ + SE_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) + +#define SE_COMMON_MASK_SH_LIST_DCE80_100(mask_sh)\ + SE_COMMON_MASK_SH_LIST_DCE_COMMON(mask_sh),\ + SE_SF(TMDS_CNTL, TMDS_PIXEL_ENCODING, mask_sh),\ + SE_SF(TMDS_CNTL, TMDS_COLOR_FORMAT, mask_sh) + +#define SE_COMMON_MASK_SH_LIST_DCE110(mask_sh)\ + SE_COMMON_MASK_SH_LIST_DCE_COMMON(mask_sh),\ + SE_SF(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_CLOCK_CHANNEL_RATE, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_DATA_SCRAMBLE_EN, mask_sh),\ + SE_SF(DIG_FE_CNTL, TMDS_PIXEL_ENCODING, mask_sh),\ + SE_SF(DIG_FE_CNTL, TMDS_COLOR_FORMAT, mask_sh) + +#define SE_COMMON_MASK_SH_LIST_DCE112(mask_sh)\ + SE_COMMON_MASK_SH_LIST_DCE_COMMON(mask_sh),\ + SE_SF(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_CLOCK_CHANNEL_RATE, mask_sh),\ + SE_SF(HDMI_CONTROL, HDMI_DATA_SCRAMBLE_EN, mask_sh),\ + SE_SF(DIG_FE_CNTL, TMDS_PIXEL_ENCODING, mask_sh),\ + SE_SF(DIG_FE_CNTL, TMDS_COLOR_FORMAT, mask_sh),\ + SE_SF(DP_VID_TIMING, DP_VID_M_DOUBLE_VALUE_EN, mask_sh) + +struct dce_stream_encoder_shift { + uint8_t AFMT_GENERIC_INDEX; + uint8_t AFMT_GENERIC0_UPDATE; + uint8_t AFMT_GENERIC2_UPDATE; + uint8_t AFMT_GENERIC_HB0; + uint8_t AFMT_GENERIC_HB1; + uint8_t AFMT_GENERIC_HB2; + uint8_t AFMT_GENERIC_HB3; + uint8_t AFMT_GENERIC_LOCK_STATUS; + uint8_t AFMT_GENERIC_CONFLICT; + uint8_t AFMT_GENERIC_CONFLICT_CLR; + uint8_t AFMT_GENERIC0_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC1_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC2_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC3_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC4_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC5_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC6_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC7_FRAME_UPDATE_PENDING; + uint8_t AFMT_GENERIC0_FRAME_UPDATE; + uint8_t AFMT_GENERIC1_FRAME_UPDATE; + uint8_t AFMT_GENERIC2_FRAME_UPDATE; + uint8_t AFMT_GENERIC3_FRAME_UPDATE; + uint8_t AFMT_GENERIC4_FRAME_UPDATE; + uint8_t AFMT_GENERIC5_FRAME_UPDATE; + uint8_t AFMT_GENERIC6_FRAME_UPDATE; + uint8_t AFMT_GENERIC7_FRAME_UPDATE; + uint8_t HDMI_GENERIC0_CONT; + uint8_t HDMI_GENERIC0_SEND; + uint8_t HDMI_GENERIC0_LINE; + uint8_t HDMI_GENERIC1_CONT; + uint8_t HDMI_GENERIC1_SEND; + uint8_t HDMI_GENERIC1_LINE; + uint8_t DP_PIXEL_ENCODING; + uint8_t DP_COMPONENT_DEPTH; + uint8_t DP_DYN_RANGE; + uint8_t DP_YCBCR_RANGE; + uint8_t HDMI_PACKET_GEN_VERSION; + uint8_t HDMI_KEEPOUT_MODE; + uint8_t HDMI_DEEP_COLOR_ENABLE; + uint8_t HDMI_CLOCK_CHANNEL_RATE; + uint8_t HDMI_DEEP_COLOR_DEPTH; + uint8_t HDMI_GC_CONT; + uint8_t HDMI_GC_SEND; + uint8_t HDMI_NULL_SEND; + uint8_t HDMI_DATA_SCRAMBLE_EN; + uint8_t HDMI_AUDIO_INFO_SEND; + uint8_t AFMT_AUDIO_INFO_UPDATE; + uint8_t HDMI_AUDIO_INFO_LINE; + uint8_t HDMI_GC_AVMUTE; + uint8_t DP_MSE_RATE_X; + uint8_t DP_MSE_RATE_Y; + uint8_t DP_MSE_RATE_UPDATE_PENDING; + uint8_t AFMT_AVI_INFO_VERSION; + uint8_t HDMI_AVI_INFO_SEND; + uint8_t HDMI_AVI_INFO_CONT; + uint8_t HDMI_AVI_INFO_LINE; + uint8_t DP_SEC_GSP0_ENABLE; + uint8_t DP_SEC_STREAM_ENABLE; + uint8_t DP_SEC_GSP1_ENABLE; + uint8_t DP_SEC_GSP2_ENABLE; + uint8_t DP_SEC_GSP3_ENABLE; + uint8_t DP_SEC_GSP4_ENABLE; + uint8_t DP_SEC_GSP5_ENABLE; + uint8_t DP_SEC_GSP6_ENABLE; + uint8_t DP_SEC_GSP7_ENABLE; + uint8_t DP_SEC_AVI_ENABLE; + uint8_t DP_SEC_MPG_ENABLE; + uint8_t DP_VID_STREAM_DIS_DEFER; + uint8_t DP_VID_STREAM_ENABLE; + uint8_t DP_VID_STREAM_STATUS; + uint8_t DP_STEER_FIFO_RESET; + uint8_t DP_VID_M_N_GEN_EN; + uint8_t DP_VID_N; + uint8_t DP_VID_M; + uint8_t DIG_START; + uint8_t AFMT_AUDIO_SRC_SELECT; + uint8_t AFMT_AUDIO_CHANNEL_ENABLE; + uint8_t HDMI_AUDIO_PACKETS_PER_LINE; + uint8_t HDMI_AUDIO_DELAY_EN; + uint8_t AFMT_60958_CS_UPDATE; + uint8_t AFMT_AUDIO_LAYOUT_OVRD; + uint8_t AFMT_60958_OSF_OVRD; + uint8_t HDMI_ACR_AUTO_SEND; + uint8_t HDMI_ACR_SOURCE; + uint8_t HDMI_ACR_AUDIO_PRIORITY; + uint8_t HDMI_ACR_CTS_32; + uint8_t HDMI_ACR_N_32; + uint8_t HDMI_ACR_CTS_44; + uint8_t HDMI_ACR_N_44; + uint8_t HDMI_ACR_CTS_48; + uint8_t HDMI_ACR_N_48; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_L; + uint8_t AFMT_60958_CS_CLOCK_ACCURACY; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_R; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_2; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_3; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_4; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_5; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_6; + uint8_t AFMT_60958_CS_CHANNEL_NUMBER_7; + uint8_t DP_SEC_AUD_N; + uint8_t DP_SEC_TIMESTAMP_MODE; + uint8_t DP_SEC_ASP_ENABLE; + uint8_t DP_SEC_ATP_ENABLE; + uint8_t DP_SEC_AIP_ENABLE; + uint8_t DP_SEC_ACM_ENABLE; + uint8_t AFMT_AUDIO_SAMPLE_SEND; + uint8_t AFMT_AUDIO_CLOCK_EN; + uint8_t TMDS_PIXEL_ENCODING; + uint8_t TMDS_COLOR_FORMAT; + uint8_t DP_DB_DISABLE; + uint8_t DP_MSA_MISC0; + uint8_t DP_MSA_HTOTAL; + uint8_t DP_MSA_VTOTAL; + uint8_t DP_MSA_HSTART; + uint8_t DP_MSA_VSTART; + uint8_t DP_MSA_HSYNCWIDTH; + uint8_t DP_MSA_HSYNCPOLARITY; + uint8_t DP_MSA_VSYNCWIDTH; + uint8_t DP_MSA_VSYNCPOLARITY; + uint8_t DP_MSA_HWIDTH; + uint8_t DP_MSA_VHEIGHT; + uint8_t HDMI_DB_DISABLE; + uint8_t DP_VID_N_MUL; + uint8_t DP_VID_M_DOUBLE_VALUE_EN; +}; + +struct dce_stream_encoder_mask { + uint32_t AFMT_GENERIC_INDEX; + uint32_t AFMT_GENERIC0_UPDATE; + uint32_t AFMT_GENERIC2_UPDATE; + uint32_t AFMT_GENERIC_HB0; + uint32_t AFMT_GENERIC_HB1; + uint32_t AFMT_GENERIC_HB2; + uint32_t AFMT_GENERIC_HB3; + uint32_t AFMT_GENERIC_LOCK_STATUS; + uint32_t AFMT_GENERIC_CONFLICT; + uint32_t AFMT_GENERIC_CONFLICT_CLR; + uint32_t AFMT_GENERIC0_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC1_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC2_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC3_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC4_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC5_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC6_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC7_FRAME_UPDATE_PENDING; + uint32_t AFMT_GENERIC0_FRAME_UPDATE; + uint32_t AFMT_GENERIC1_FRAME_UPDATE; + uint32_t AFMT_GENERIC2_FRAME_UPDATE; + uint32_t AFMT_GENERIC3_FRAME_UPDATE; + uint32_t AFMT_GENERIC4_FRAME_UPDATE; + uint32_t AFMT_GENERIC5_FRAME_UPDATE; + uint32_t AFMT_GENERIC6_FRAME_UPDATE; + uint32_t AFMT_GENERIC7_FRAME_UPDATE; + uint32_t HDMI_GENERIC0_CONT; + uint32_t HDMI_GENERIC0_SEND; + uint32_t HDMI_GENERIC0_LINE; + uint32_t HDMI_GENERIC1_CONT; + uint32_t HDMI_GENERIC1_SEND; + uint32_t HDMI_GENERIC1_LINE; + uint32_t DP_PIXEL_ENCODING; + uint32_t DP_COMPONENT_DEPTH; + uint32_t DP_DYN_RANGE; + uint32_t DP_YCBCR_RANGE; + uint32_t HDMI_PACKET_GEN_VERSION; + uint32_t HDMI_KEEPOUT_MODE; + uint32_t HDMI_DEEP_COLOR_ENABLE; + uint32_t HDMI_CLOCK_CHANNEL_RATE; + uint32_t HDMI_DEEP_COLOR_DEPTH; + uint32_t HDMI_GC_CONT; + uint32_t HDMI_GC_SEND; + uint32_t HDMI_NULL_SEND; + uint32_t HDMI_DATA_SCRAMBLE_EN; + uint32_t HDMI_AUDIO_INFO_SEND; + uint32_t AFMT_AUDIO_INFO_UPDATE; + uint32_t HDMI_AUDIO_INFO_LINE; + uint32_t HDMI_GC_AVMUTE; + uint32_t DP_MSE_RATE_X; + uint32_t DP_MSE_RATE_Y; + uint32_t DP_MSE_RATE_UPDATE_PENDING; + uint32_t AFMT_AVI_INFO_VERSION; + uint32_t HDMI_AVI_INFO_SEND; + uint32_t HDMI_AVI_INFO_CONT; + uint32_t HDMI_AVI_INFO_LINE; + uint32_t DP_SEC_GSP0_ENABLE; + uint32_t DP_SEC_STREAM_ENABLE; + uint32_t DP_SEC_GSP1_ENABLE; + uint32_t DP_SEC_GSP2_ENABLE; + uint32_t DP_SEC_GSP3_ENABLE; + uint32_t DP_SEC_GSP4_ENABLE; + uint32_t DP_SEC_GSP5_ENABLE; + uint32_t DP_SEC_GSP6_ENABLE; + uint32_t DP_SEC_GSP7_ENABLE; + uint32_t DP_SEC_AVI_ENABLE; + uint32_t DP_SEC_MPG_ENABLE; + uint32_t DP_VID_STREAM_DIS_DEFER; + uint32_t DP_VID_STREAM_ENABLE; + uint32_t DP_VID_STREAM_STATUS; + uint32_t DP_STEER_FIFO_RESET; + uint32_t DP_VID_M_N_GEN_EN; + uint32_t DP_VID_N; + uint32_t DP_VID_M; + uint32_t DIG_START; + uint32_t AFMT_AUDIO_SRC_SELECT; + uint32_t AFMT_AUDIO_CHANNEL_ENABLE; + uint32_t HDMI_AUDIO_PACKETS_PER_LINE; + uint32_t HDMI_AUDIO_DELAY_EN; + uint32_t AFMT_60958_CS_UPDATE; + uint32_t AFMT_AUDIO_LAYOUT_OVRD; + uint32_t AFMT_60958_OSF_OVRD; + uint32_t HDMI_ACR_AUTO_SEND; + uint32_t HDMI_ACR_SOURCE; + uint32_t HDMI_ACR_AUDIO_PRIORITY; + uint32_t HDMI_ACR_CTS_32; + uint32_t HDMI_ACR_N_32; + uint32_t HDMI_ACR_CTS_44; + uint32_t HDMI_ACR_N_44; + uint32_t HDMI_ACR_CTS_48; + uint32_t HDMI_ACR_N_48; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_L; + uint32_t AFMT_60958_CS_CLOCK_ACCURACY; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_R; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_2; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_3; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_4; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_5; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_6; + uint32_t AFMT_60958_CS_CHANNEL_NUMBER_7; + uint32_t DP_SEC_AUD_N; + uint32_t DP_SEC_TIMESTAMP_MODE; + uint32_t DP_SEC_ASP_ENABLE; + uint32_t DP_SEC_ATP_ENABLE; + uint32_t DP_SEC_AIP_ENABLE; + uint32_t DP_SEC_ACM_ENABLE; + uint32_t AFMT_AUDIO_SAMPLE_SEND; + uint32_t AFMT_AUDIO_CLOCK_EN; + uint32_t TMDS_PIXEL_ENCODING; + uint32_t TMDS_COLOR_FORMAT; + uint32_t DP_DB_DISABLE; + uint32_t DP_MSA_MISC0; + uint32_t DP_MSA_HTOTAL; + uint32_t DP_MSA_VTOTAL; + uint32_t DP_MSA_HSTART; + uint32_t DP_MSA_VSTART; + uint32_t DP_MSA_HSYNCWIDTH; + uint32_t DP_MSA_HSYNCPOLARITY; + uint32_t DP_MSA_VSYNCWIDTH; + uint32_t DP_MSA_VSYNCPOLARITY; + uint32_t DP_MSA_HWIDTH; + uint32_t DP_MSA_VHEIGHT; + uint32_t HDMI_DB_DISABLE; + uint32_t DP_VID_N_MUL; + uint32_t DP_VID_M_DOUBLE_VALUE_EN; +}; + +struct dce110_stream_enc_registers { + uint32_t AFMT_CNTL; + uint32_t AFMT_AVI_INFO0; + uint32_t AFMT_AVI_INFO1; + uint32_t AFMT_AVI_INFO2; + uint32_t AFMT_AVI_INFO3; + uint32_t AFMT_GENERIC_0; + uint32_t AFMT_GENERIC_1; + uint32_t AFMT_GENERIC_2; + uint32_t AFMT_GENERIC_3; + uint32_t AFMT_GENERIC_4; + uint32_t AFMT_GENERIC_5; + uint32_t AFMT_GENERIC_6; + uint32_t AFMT_GENERIC_7; + uint32_t AFMT_GENERIC_HDR; + uint32_t AFMT_INFOFRAME_CONTROL0; + uint32_t AFMT_VBI_PACKET_CONTROL; + uint32_t AFMT_VBI_PACKET_CONTROL1; + uint32_t AFMT_AUDIO_PACKET_CONTROL; + uint32_t AFMT_AUDIO_PACKET_CONTROL2; + uint32_t AFMT_AUDIO_SRC_CONTROL; + uint32_t AFMT_60958_0; + uint32_t AFMT_60958_1; + uint32_t AFMT_60958_2; + uint32_t DIG_FE_CNTL; + uint32_t DP_MSE_RATE_CNTL; + uint32_t DP_MSE_RATE_UPDATE; + uint32_t DP_PIXEL_FORMAT; + uint32_t DP_SEC_CNTL; + uint32_t DP_STEER_FIFO; + uint32_t DP_VID_M; + uint32_t DP_VID_N; + uint32_t DP_VID_STREAM_CNTL; + uint32_t DP_VID_TIMING; + uint32_t DP_SEC_AUD_N; + uint32_t DP_SEC_TIMESTAMP; + uint32_t HDMI_CONTROL; + uint32_t HDMI_GC; + uint32_t HDMI_GENERIC_PACKET_CONTROL0; + uint32_t HDMI_GENERIC_PACKET_CONTROL1; + uint32_t HDMI_GENERIC_PACKET_CONTROL2; + uint32_t HDMI_GENERIC_PACKET_CONTROL3; + uint32_t HDMI_INFOFRAME_CONTROL0; + uint32_t HDMI_INFOFRAME_CONTROL1; + uint32_t HDMI_VBI_PACKET_CONTROL; + uint32_t HDMI_AUDIO_PACKET_CONTROL; + uint32_t HDMI_ACR_PACKET_CONTROL; + uint32_t HDMI_ACR_32_0; + uint32_t HDMI_ACR_32_1; + uint32_t HDMI_ACR_44_0; + uint32_t HDMI_ACR_44_1; + uint32_t HDMI_ACR_48_0; + uint32_t HDMI_ACR_48_1; + uint32_t TMDS_CNTL; +}; + +struct dce110_stream_encoder { + struct stream_encoder base; + const struct dce110_stream_enc_registers *regs; + const struct dce_stream_encoder_shift *se_shift; + const struct dce_stream_encoder_mask *se_mask; +}; + +bool dce110_stream_encoder_construct( + struct dce110_stream_encoder *enc110, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + const struct dce110_stream_enc_registers *regs, + const struct dce_stream_encoder_shift *se_shift, + const struct dce_stream_encoder_mask *se_mask); + + +void dce110_se_audio_mute_control( + struct stream_encoder *enc, bool mute); + +void dce110_se_dp_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info); + +void dce110_se_dp_audio_enable( + struct stream_encoder *enc); + +void dce110_se_dp_audio_disable( + struct stream_encoder *enc); + +void dce110_se_hdmi_audio_setup( + struct stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info); + +void dce110_se_hdmi_audio_disable( + struct stream_encoder *enc); + +#endif /* __DC_STREAM_ENCODER_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c new file mode 100644 index 000000000000..f47b6617f662 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -0,0 +1,1002 @@ +/* + * Copyright 2012-16 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. + * + * Authors: AMD + * + */ + +#include "dce_transform.h" +#include "reg_helper.h" +#include "opp.h" +#include "basics/conversion.h" + +#define REG(reg) \ + (xfm_dce->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + xfm_dce->xfm_shift->field_name, xfm_dce->xfm_mask->field_name + +#define CTX \ + xfm_dce->base.ctx + +#define IDENTITY_RATIO(ratio) (dal_fixed31_32_u2d19(ratio) == (1 << 19)) +#define GAMUT_MATRIX_SIZE 12 +#define SCL_PHASES 16 + +enum dcp_out_trunc_round_mode { + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_MODE_ROUND +}; + +enum dcp_out_trunc_round_depth { + DCP_OUT_TRUNC_ROUND_DEPTH_14BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_13BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_11BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_9BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_8BIT +}; + +/* defines the various methods of bit reduction available for use */ +enum dcp_bit_depth_reduction_mode { + DCP_BIT_DEPTH_REDUCTION_MODE_DITHER, + DCP_BIT_DEPTH_REDUCTION_MODE_ROUND, + DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE, + DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED, + DCP_BIT_DEPTH_REDUCTION_MODE_INVALID +}; + +enum dcp_spatial_dither_mode { + DCP_SPATIAL_DITHER_MODE_AAAA, + DCP_SPATIAL_DITHER_MODE_A_AA_A, + DCP_SPATIAL_DITHER_MODE_AABBAABB, + DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC, + DCP_SPATIAL_DITHER_MODE_INVALID +}; + +enum dcp_spatial_dither_depth { + DCP_SPATIAL_DITHER_DEPTH_30BPP, + DCP_SPATIAL_DITHER_DEPTH_24BPP +}; + +static bool setup_scaling_configuration( + struct dce_transform *xfm_dce, + const struct scaler_data *data) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + + if (data->taps.h_taps + data->taps.v_taps <= 2) { + /* Set bypass */ + REG_UPDATE_2(SCL_MODE, SCL_MODE, 0, SCL_PSCL_EN, 0); + return false; + } + + REG_SET_2(SCL_TAP_CONTROL, 0, + SCL_H_NUM_OF_TAPS, data->taps.h_taps - 1, + SCL_V_NUM_OF_TAPS, data->taps.v_taps - 1); + + if (data->format <= PIXEL_FORMAT_GRPH_END) + REG_UPDATE_2(SCL_MODE, SCL_MODE, 1, SCL_PSCL_EN, 1); + else + REG_UPDATE_2(SCL_MODE, SCL_MODE, 2, SCL_PSCL_EN, 1); + + /* 1 - Replace out of bound pixels with edge */ + REG_SET(SCL_CONTROL, 0, SCL_BOUNDARY_MODE, 1); + + return true; +} + +static void program_overscan( + struct dce_transform *xfm_dce, + const struct scaler_data *data) +{ + int overscan_right = data->h_active + - data->recout.x - data->recout.width; + int overscan_bottom = data->v_active + - data->recout.y - data->recout.height; + + if (overscan_right < 0) { + BREAK_TO_DEBUGGER(); + overscan_right = 0; + } + if (overscan_bottom < 0) { + BREAK_TO_DEBUGGER(); + overscan_bottom = 0; + } + + REG_SET_2(EXT_OVERSCAN_LEFT_RIGHT, 0, + EXT_OVERSCAN_LEFT, data->recout.x, + EXT_OVERSCAN_RIGHT, overscan_right); + REG_SET_2(EXT_OVERSCAN_TOP_BOTTOM, 0, + EXT_OVERSCAN_TOP, data->recout.y, + EXT_OVERSCAN_BOTTOM, overscan_bottom); +} + +static void program_multi_taps_filter( + struct dce_transform *xfm_dce, + int taps, + const uint16_t *coeffs, + enum ram_filter_type filter_type) +{ + int phase, pair; + int array_idx = 0; + int taps_pairs = (taps + 1) / 2; + int phases_to_program = SCL_PHASES / 2 + 1; + + uint32_t power_ctl = 0; + + if (!coeffs) + return; + + /*We need to disable power gating on coeff memory to do programming*/ + if (REG(DCFE_MEM_PWR_CTRL)) { + power_ctl = REG_READ(DCFE_MEM_PWR_CTRL); + REG_SET(DCFE_MEM_PWR_CTRL, power_ctl, SCL_COEFF_MEM_PWR_DIS, 1); + + REG_WAIT(DCFE_MEM_PWR_STATUS, SCL_COEFF_MEM_PWR_STATE, 0, 1, 10); + } + for (phase = 0; phase < phases_to_program; phase++) { + /*we always program N/2 + 1 phases, total phases N, but N/2-1 are just mirror + phase 0 is unique and phase N/2 is unique if N is even*/ + for (pair = 0; pair < taps_pairs; pair++) { + uint16_t odd_coeff = 0; + uint16_t even_coeff = coeffs[array_idx]; + + REG_SET_3(SCL_COEF_RAM_SELECT, 0, + SCL_C_RAM_FILTER_TYPE, filter_type, + SCL_C_RAM_PHASE, phase, + SCL_C_RAM_TAP_PAIR_IDX, pair); + + if (taps % 2 && pair == taps_pairs - 1) + array_idx++; + else { + odd_coeff = coeffs[array_idx + 1]; + array_idx += 2; + } + + REG_SET_4(SCL_COEF_RAM_TAP_DATA, 0, + SCL_C_RAM_EVEN_TAP_COEF_EN, 1, + SCL_C_RAM_EVEN_TAP_COEF, even_coeff, + SCL_C_RAM_ODD_TAP_COEF_EN, 1, + SCL_C_RAM_ODD_TAP_COEF, odd_coeff); + } + } + + /*We need to restore power gating on coeff memory to initial state*/ + if (REG(DCFE_MEM_PWR_CTRL)) + REG_WRITE(DCFE_MEM_PWR_CTRL, power_ctl); +} + +static void program_viewport( + struct dce_transform *xfm_dce, + const struct rect *view_port) +{ + REG_SET_2(VIEWPORT_START, 0, + VIEWPORT_X_START, view_port->x, + VIEWPORT_Y_START, view_port->y); + + REG_SET_2(VIEWPORT_SIZE, 0, + VIEWPORT_HEIGHT, view_port->height, + VIEWPORT_WIDTH, view_port->width); + + /* TODO: add stereo support */ +} + +static void calculate_inits( + struct dce_transform *xfm_dce, + const struct scaler_data *data, + struct scl_ratios_inits *inits) +{ + struct fixed31_32 h_init; + struct fixed31_32 v_init; + + inits->h_int_scale_ratio = + dal_fixed31_32_u2d19(data->ratios.horz) << 5; + inits->v_int_scale_ratio = + dal_fixed31_32_u2d19(data->ratios.vert) << 5; + + h_init = + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios.horz, + dal_fixed31_32_from_int(data->taps.h_taps + 1)), + 2); + inits->h_init.integer = dal_fixed31_32_floor(h_init); + inits->h_init.fraction = dal_fixed31_32_u0d19(h_init) << 5; + + v_init = + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios.vert, + dal_fixed31_32_from_int(data->taps.v_taps + 1)), + 2); + inits->v_init.integer = dal_fixed31_32_floor(v_init); + inits->v_init.fraction = dal_fixed31_32_u0d19(v_init) << 5; +} + +static void program_scl_ratios_inits( + struct dce_transform *xfm_dce, + struct scl_ratios_inits *inits) +{ + + REG_SET(SCL_HORZ_FILTER_SCALE_RATIO, 0, + SCL_H_SCALE_RATIO, inits->h_int_scale_ratio); + + REG_SET(SCL_VERT_FILTER_SCALE_RATIO, 0, + SCL_V_SCALE_RATIO, inits->v_int_scale_ratio); + + REG_SET_2(SCL_HORZ_FILTER_INIT, 0, + SCL_H_INIT_INT, inits->h_init.integer, + SCL_H_INIT_FRAC, inits->h_init.fraction); + + REG_SET_2(SCL_VERT_FILTER_INIT, 0, + SCL_V_INIT_INT, inits->v_init.integer, + SCL_V_INIT_FRAC, inits->v_init.fraction); + + REG_WRITE(SCL_AUTOMATIC_MODE_CONTROL, 0); +} + +static const uint16_t *get_filter_coeffs_16p(int taps, struct fixed31_32 ratio) +{ + if (taps == 4) + return get_filter_4tap_16p(ratio); + else if (taps == 3) + return get_filter_3tap_16p(ratio); + else if (taps == 2) + return filter_2tap_16p; + else if (taps == 1) + return NULL; + else { + /* should never happen, bug */ + BREAK_TO_DEBUGGER(); + return NULL; + } +} + +static void dce_transform_set_scaler( + struct transform *xfm, + const struct scaler_data *data) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + bool is_scaling_required; + bool filter_updated = false; + const uint16_t *coeffs_v, *coeffs_h; + + /*Use all three pieces of memory always*/ + REG_SET_2(LB_MEMORY_CTRL, 0, + LB_MEMORY_CONFIG, 0, + LB_MEMORY_SIZE, xfm_dce->lb_memory_size); + + /* 1. Program overscan */ + program_overscan(xfm_dce, data); + + /* 2. Program taps and configuration */ + is_scaling_required = setup_scaling_configuration(xfm_dce, data); + + if (is_scaling_required) { + /* 3. Calculate and program ratio, filter initialization */ + struct scl_ratios_inits inits = { 0 }; + + calculate_inits(xfm_dce, data, &inits); + + program_scl_ratios_inits(xfm_dce, &inits); + + coeffs_v = get_filter_coeffs_16p(data->taps.v_taps, data->ratios.vert); + coeffs_h = get_filter_coeffs_16p(data->taps.h_taps, data->ratios.horz); + + if (coeffs_v != xfm_dce->filter_v || coeffs_h != xfm_dce->filter_h) { + /* 4. Program vertical filters */ + if (xfm_dce->filter_v == NULL) + REG_SET(SCL_VERT_FILTER_CONTROL, 0, + SCL_V_2TAP_HARDCODE_COEF_EN, 0); + program_multi_taps_filter( + xfm_dce, + data->taps.v_taps, + coeffs_v, + FILTER_TYPE_RGB_Y_VERTICAL); + program_multi_taps_filter( + xfm_dce, + data->taps.v_taps, + coeffs_v, + FILTER_TYPE_ALPHA_VERTICAL); + + /* 5. Program horizontal filters */ + if (xfm_dce->filter_h == NULL) + REG_SET(SCL_HORZ_FILTER_CONTROL, 0, + SCL_H_2TAP_HARDCODE_COEF_EN, 0); + program_multi_taps_filter( + xfm_dce, + data->taps.h_taps, + coeffs_h, + FILTER_TYPE_RGB_Y_HORIZONTAL); + program_multi_taps_filter( + xfm_dce, + data->taps.h_taps, + coeffs_h, + FILTER_TYPE_ALPHA_HORIZONTAL); + + xfm_dce->filter_v = coeffs_v; + xfm_dce->filter_h = coeffs_h; + filter_updated = true; + } + } + + /* 6. Program the viewport */ + program_viewport(xfm_dce, &data->viewport); + + /* 7. Set bit to flip to new coefficient memory */ + if (filter_updated) + REG_UPDATE(SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE, 1); + + REG_UPDATE(LB_DATA_FORMAT, ALPHA_EN, data->lb_params.alpha_en); +} + +/***************************************************************************** + * set_clamp + * + * @param depth : bit depth to set the clamp to (should match denorm) + * + * @brief + * Programs clamp according to panel bit depth. + * + *******************************************************************************/ +static void set_clamp( + struct dce_transform *xfm_dce, + enum dc_color_depth depth) +{ + int clamp_max = 0; + + /* At the clamp block the data will be MSB aligned, so we set the max + * clamp accordingly. + * For example, the max value for 6 bits MSB aligned (14 bit bus) would + * be "11 1111 0000 0000" in binary, so 0x3F00. + */ + switch (depth) { + case COLOR_DEPTH_666: + /* 6bit MSB aligned on 14 bit bus '11 1111 0000 0000' */ + clamp_max = 0x3F00; + break; + case COLOR_DEPTH_888: + /* 8bit MSB aligned on 14 bit bus '11 1111 1100 0000' */ + clamp_max = 0x3FC0; + break; + case COLOR_DEPTH_101010: + /* 10bit MSB aligned on 14 bit bus '11 1111 1111 1100' */ + clamp_max = 0x3FFC; + break; + case COLOR_DEPTH_121212: + /* 12bit MSB aligned on 14 bit bus '11 1111 1111 1111' */ + clamp_max = 0x3FFF; + break; + default: + clamp_max = 0x3FC0; + BREAK_TO_DEBUGGER(); /* Invalid clamp bit depth */ + } + REG_SET_2(OUT_CLAMP_CONTROL_B_CB, 0, + OUT_CLAMP_MIN_B_CB, 0, + OUT_CLAMP_MAX_B_CB, clamp_max); + + REG_SET_2(OUT_CLAMP_CONTROL_G_Y, 0, + OUT_CLAMP_MIN_G_Y, 0, + OUT_CLAMP_MAX_G_Y, clamp_max); + + REG_SET_2(OUT_CLAMP_CONTROL_R_CR, 0, + OUT_CLAMP_MIN_R_CR, 0, + OUT_CLAMP_MAX_R_CR, clamp_max); +} + +/******************************************************************************* + * set_round + * + * @brief + * Programs Round/Truncate + * + * @param [in] mode :round or truncate + * @param [in] depth :bit depth to round/truncate to + OUT_ROUND_TRUNC_MODE 3:0 0xA Output data round or truncate mode + POSSIBLE VALUES: + 00 - truncate to u0.12 + 01 - truncate to u0.11 + 02 - truncate to u0.10 + 03 - truncate to u0.9 + 04 - truncate to u0.8 + 05 - reserved + 06 - truncate to u0.14 + 07 - truncate to u0.13 set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_R_CR, + OUT_CLAMP_MAX_R_CR); + 08 - round to u0.12 + 09 - round to u0.11 + 10 - round to u0.10 + 11 - round to u0.9 + 12 - round to u0.8 + 13 - reserved + 14 - round to u0.14 + 15 - round to u0.13 + + ******************************************************************************/ +static void set_round( + struct dce_transform *xfm_dce, + enum dcp_out_trunc_round_mode mode, + enum dcp_out_trunc_round_depth depth) +{ + int depth_bits = 0; + int mode_bit = 0; + + /* set up bit depth */ + switch (depth) { + case DCP_OUT_TRUNC_ROUND_DEPTH_14BIT: + depth_bits = 6; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_13BIT: + depth_bits = 7; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_12BIT: + depth_bits = 0; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_11BIT: + depth_bits = 1; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_10BIT: + depth_bits = 2; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_9BIT: + depth_bits = 3; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_8BIT: + depth_bits = 4; + break; + default: + depth_bits = 4; + BREAK_TO_DEBUGGER(); /* Invalid dcp_out_trunc_round_depth */ + } + + /* set up round or truncate */ + switch (mode) { + case DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE: + mode_bit = 0; + break; + case DCP_OUT_TRUNC_ROUND_MODE_ROUND: + mode_bit = 1; + break; + default: + BREAK_TO_DEBUGGER(); /* Invalid dcp_out_trunc_round_mode */ + } + + depth_bits |= mode_bit << 3; + + REG_SET(OUT_ROUND_CONTROL, 0, OUT_ROUND_TRUNC_MODE, depth_bits); +} + +/***************************************************************************** + * set_dither + * + * @brief + * Programs Dither + * + * @param [in] dither_enable : enable dither + * @param [in] dither_mode : dither mode to set + * @param [in] dither_depth : bit depth to dither to + * @param [in] frame_random_enable : enable frame random + * @param [in] rgb_random_enable : enable rgb random + * @param [in] highpass_random_enable : enable highpass random + * + ******************************************************************************/ + +static void set_dither( + struct dce_transform *xfm_dce, + bool dither_enable, + enum dcp_spatial_dither_mode dither_mode, + enum dcp_spatial_dither_depth dither_depth, + bool frame_random_enable, + bool rgb_random_enable, + bool highpass_random_enable) +{ + int dither_depth_bits = 0; + int dither_mode_bits = 0; + + switch (dither_mode) { + case DCP_SPATIAL_DITHER_MODE_AAAA: + dither_mode_bits = 0; + break; + case DCP_SPATIAL_DITHER_MODE_A_AA_A: + dither_mode_bits = 1; + break; + case DCP_SPATIAL_DITHER_MODE_AABBAABB: + dither_mode_bits = 2; + break; + case DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC: + dither_mode_bits = 3; + break; + default: + /* Invalid dcp_spatial_dither_mode */ + BREAK_TO_DEBUGGER(); + } + + switch (dither_depth) { + case DCP_SPATIAL_DITHER_DEPTH_30BPP: + dither_depth_bits = 0; + break; + case DCP_SPATIAL_DITHER_DEPTH_24BPP: + dither_depth_bits = 1; + break; + default: + /* Invalid dcp_spatial_dither_depth */ + BREAK_TO_DEBUGGER(); + } + + /* write the register */ + REG_SET_6(DCP_SPATIAL_DITHER_CNTL, 0, + DCP_SPATIAL_DITHER_EN, dither_enable, + DCP_SPATIAL_DITHER_MODE, dither_mode_bits, + DCP_SPATIAL_DITHER_DEPTH, dither_depth_bits, + DCP_FRAME_RANDOM_ENABLE, frame_random_enable, + DCP_RGB_RANDOM_ENABLE, rgb_random_enable, + DCP_HIGHPASS_RANDOM_ENABLE, highpass_random_enable); +} + +/***************************************************************************** + * dce_transform_bit_depth_reduction_program + * + * @brief + * Programs the DCP bit depth reduction registers (Clamp, Round/Truncate, + * Dither) for dce + * + * @param depth : bit depth to set the clamp to (should match denorm) + * + ******************************************************************************/ +static void program_bit_depth_reduction( + struct dce_transform *xfm_dce, + enum dc_color_depth depth, + const struct bit_depth_reduction_params *bit_depth_params) +{ + enum dcp_bit_depth_reduction_mode depth_reduction_mode; + enum dcp_spatial_dither_mode spatial_dither_mode; + bool frame_random_enable; + bool rgb_random_enable; + bool highpass_random_enable; + + ASSERT(depth < COLOR_DEPTH_121212); /* Invalid clamp bit depth */ + + if (bit_depth_params->flags.SPATIAL_DITHER_ENABLED) { + depth_reduction_mode = DCP_BIT_DEPTH_REDUCTION_MODE_DITHER; + frame_random_enable = true; + rgb_random_enable = true; + highpass_random_enable = true; + + } else { + depth_reduction_mode = DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED; + frame_random_enable = false; + rgb_random_enable = false; + highpass_random_enable = false; + } + + spatial_dither_mode = DCP_SPATIAL_DITHER_MODE_A_AA_A; + + set_clamp(xfm_dce, depth); + + switch (depth_reduction_mode) { + case DCP_BIT_DEPTH_REDUCTION_MODE_DITHER: + /* Spatial Dither: Set round/truncate to bypass (12bit), + * enable Dither (30bpp) */ + set_round(xfm_dce, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); + + set_dither(xfm_dce, true, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + case DCP_BIT_DEPTH_REDUCTION_MODE_ROUND: + /* Round: Enable round (10bit), disable Dither */ + set_round(xfm_dce, + DCP_OUT_TRUNC_ROUND_MODE_ROUND, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); + + set_dither(xfm_dce, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + case DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE: /* Truncate */ + /* Truncate: Enable truncate (10bit), disable Dither */ + set_round(xfm_dce, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); + + set_dither(xfm_dce, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + + case DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED: /* Disabled */ + /* Truncate: Set round/truncate to bypass (12bit), + * disable Dither */ + set_round(xfm_dce, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); + + set_dither(xfm_dce, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + default: + /* Invalid DCP Depth reduction mode */ + BREAK_TO_DEBUGGER(); + break; + } +} + +static int dce_transform_get_max_num_of_supported_lines( + struct dce_transform *xfm_dce, + enum lb_pixel_depth depth, + int pixel_width) +{ + int pixels_per_entries = 0; + int max_pixels_supports = 0; + + ASSERT(pixel_width); + + /* Find number of pixels that can fit into a single LB entry and + * take floor of the value since we cannot store a single pixel + * across multiple entries. */ + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + pixels_per_entries = xfm_dce->lb_bits_per_entry / 18; + break; + + case LB_PIXEL_DEPTH_24BPP: + pixels_per_entries = xfm_dce->lb_bits_per_entry / 24; + break; + + case LB_PIXEL_DEPTH_30BPP: + pixels_per_entries = xfm_dce->lb_bits_per_entry / 30; + break; + + case LB_PIXEL_DEPTH_36BPP: + pixels_per_entries = xfm_dce->lb_bits_per_entry / 36; + break; + + default: + dm_logger_write(xfm_dce->base.ctx->logger, LOG_WARNING, + "%s: Invalid LB pixel depth", + __func__); + BREAK_TO_DEBUGGER(); + break; + } + + ASSERT(pixels_per_entries); + + max_pixels_supports = + pixels_per_entries * + xfm_dce->lb_memory_size; + + return (max_pixels_supports / pixel_width); +} + +static void set_denormalization( + struct dce_transform *xfm_dce, + enum dc_color_depth depth) +{ + int denorm_mode = 0; + + switch (depth) { + case COLOR_DEPTH_666: + /* 63/64 for 6 bit output color depth */ + denorm_mode = 1; + break; + case COLOR_DEPTH_888: + /* Unity for 8 bit output color depth + * because prescale is disabled by default */ + denorm_mode = 0; + break; + case COLOR_DEPTH_101010: + /* 1023/1024 for 10 bit output color depth */ + denorm_mode = 3; + break; + case COLOR_DEPTH_121212: + /* 4095/4096 for 12 bit output color depth */ + denorm_mode = 5; + break; + case COLOR_DEPTH_141414: + case COLOR_DEPTH_161616: + default: + /* not valid used case! */ + break; + } + + REG_SET(DENORM_CONTROL, 0, DENORM_MODE, denorm_mode); +} + +static void dce_transform_set_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth depth, + const struct bit_depth_reduction_params *bit_depth_params) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + int pixel_depth, expan_mode; + enum dc_color_depth color_depth; + + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + color_depth = COLOR_DEPTH_666; + pixel_depth = 2; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_24BPP: + color_depth = COLOR_DEPTH_888; + pixel_depth = 1; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_30BPP: + color_depth = COLOR_DEPTH_101010; + pixel_depth = 0; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_36BPP: + color_depth = COLOR_DEPTH_121212; + pixel_depth = 3; + expan_mode = 0; + break; + default: + color_depth = COLOR_DEPTH_101010; + pixel_depth = 0; + expan_mode = 1; + BREAK_TO_DEBUGGER(); + break; + } + + set_denormalization(xfm_dce, color_depth); + program_bit_depth_reduction(xfm_dce, color_depth, bit_depth_params); + + REG_UPDATE_2(LB_DATA_FORMAT, + PIXEL_DEPTH, pixel_depth, + PIXEL_EXPAN_MODE, expan_mode); + + if (!(xfm_dce->lb_pixel_depth_supported & depth)) { + /*we should use unsupported capabilities + * unless it is required by w/a*/ + dm_logger_write(xfm->ctx->logger, LOG_WARNING, + "%s: Capability not supported", + __func__); + } +} + +static void program_gamut_remap( + struct dce_transform *xfm_dce, + const uint16_t *reg_val) +{ + if (reg_val) { + REG_SET_2(GAMUT_REMAP_C11_C12, 0, + GAMUT_REMAP_C11, reg_val[0], + GAMUT_REMAP_C12, reg_val[1]); + REG_SET_2(GAMUT_REMAP_C13_C14, 0, + GAMUT_REMAP_C13, reg_val[2], + GAMUT_REMAP_C14, reg_val[3]); + REG_SET_2(GAMUT_REMAP_C21_C22, 0, + GAMUT_REMAP_C21, reg_val[4], + GAMUT_REMAP_C22, reg_val[5]); + REG_SET_2(GAMUT_REMAP_C23_C24, 0, + GAMUT_REMAP_C23, reg_val[6], + GAMUT_REMAP_C24, reg_val[7]); + REG_SET_2(GAMUT_REMAP_C31_C32, 0, + GAMUT_REMAP_C31, reg_val[8], + GAMUT_REMAP_C32, reg_val[9]); + REG_SET_2(GAMUT_REMAP_C33_C34, 0, + GAMUT_REMAP_C33, reg_val[10], + GAMUT_REMAP_C34, reg_val[11]); + + REG_SET(GAMUT_REMAP_CONTROL, 0, GRPH_GAMUT_REMAP_MODE, 1); + } else + REG_SET(GAMUT_REMAP_CONTROL, 0, GRPH_GAMUT_REMAP_MODE, 0); + +} + +/** + ***************************************************************************** + * Function: dal_transform_wide_gamut_set_gamut_remap + * + * @param [in] const struct xfm_grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and apply color temperature adjustment to in Rgb color space + * + * @see + * + ***************************************************************************** + */ +static void dce_transform_set_gamut_remap( + struct transform *xfm, + const struct xfm_grph_csc_adjustment *adjust) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + if (adjust->gamut_adjust_type != GRAPHICS_GAMUT_ADJUST_TYPE_SW) + /* Bypass if type is bypass or hw */ + program_gamut_remap(xfm_dce, NULL); + else { + struct fixed31_32 arr_matrix[GAMUT_MATRIX_SIZE]; + uint16_t arr_reg_val[GAMUT_MATRIX_SIZE]; + + arr_matrix[0] = adjust->temperature_matrix[0]; + arr_matrix[1] = adjust->temperature_matrix[1]; + arr_matrix[2] = adjust->temperature_matrix[2]; + arr_matrix[3] = dal_fixed31_32_zero; + + arr_matrix[4] = adjust->temperature_matrix[3]; + arr_matrix[5] = adjust->temperature_matrix[4]; + arr_matrix[6] = adjust->temperature_matrix[5]; + arr_matrix[7] = dal_fixed31_32_zero; + + arr_matrix[8] = adjust->temperature_matrix[6]; + arr_matrix[9] = adjust->temperature_matrix[7]; + arr_matrix[10] = adjust->temperature_matrix[8]; + arr_matrix[11] = dal_fixed31_32_zero; + + convert_float_matrix( + arr_reg_val, arr_matrix, GAMUT_MATRIX_SIZE); + + program_gamut_remap(xfm_dce, arr_reg_val); + } +} + +static uint32_t decide_taps(struct fixed31_32 ratio, uint32_t in_taps, bool chroma) +{ + uint32_t taps; + + if (IDENTITY_RATIO(ratio)) { + return 1; + } else if (in_taps != 0) { + taps = in_taps; + } else { + taps = 4; + } + + if (chroma) { + taps /= 2; + if (taps < 2) + taps = 2; + } + + return taps; +} + + +bool dce_transform_get_optimal_number_of_taps( + struct transform *xfm, + struct scaler_data *scl_data, + const struct scaling_taps *in_taps) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + int pixel_width = scl_data->viewport.width; + int max_num_of_lines; + + if (xfm_dce->prescaler_on && + (scl_data->viewport.width > scl_data->recout.width)) + pixel_width = scl_data->recout.width; + + max_num_of_lines = dce_transform_get_max_num_of_supported_lines( + xfm_dce, + scl_data->lb_params.depth, + pixel_width); + + /* Fail if in_taps are impossible */ + if (in_taps->v_taps >= max_num_of_lines) + return false; + + /* + * Set taps according to this policy (in this order) + * - Use 1 for no scaling + * - Use input taps + * - Use 4 and reduce as required by line buffer size + * - Decide chroma taps if chroma is scaled + * + * Ignore input chroma taps. Decide based on non-chroma + */ + scl_data->taps.h_taps = decide_taps(scl_data->ratios.horz, in_taps->h_taps, false); + scl_data->taps.v_taps = decide_taps(scl_data->ratios.vert, in_taps->v_taps, false); + scl_data->taps.h_taps_c = decide_taps(scl_data->ratios.horz_c, in_taps->h_taps, true); + scl_data->taps.v_taps_c = decide_taps(scl_data->ratios.vert_c, in_taps->v_taps, true); + + if (!IDENTITY_RATIO(scl_data->ratios.vert)) { + /* reduce v_taps if needed but ensure we have at least two */ + if (in_taps->v_taps == 0 + && max_num_of_lines <= scl_data->taps.v_taps + && scl_data->taps.v_taps > 1) { + scl_data->taps.v_taps = max_num_of_lines - 1; + } + + if (scl_data->taps.v_taps <= 1) + return false; + } + + if (!IDENTITY_RATIO(scl_data->ratios.vert_c)) { + /* reduce chroma v_taps if needed but ensure we have at least two */ + if (max_num_of_lines <= scl_data->taps.v_taps_c && scl_data->taps.v_taps_c > 1) { + scl_data->taps.v_taps_c = max_num_of_lines - 1; + } + + if (scl_data->taps.v_taps_c <= 1) + return false; + } + + /* we've got valid taps */ + return true; +} + +static void dce_transform_reset(struct transform *xfm) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + xfm_dce->filter_h = NULL; + xfm_dce->filter_v = NULL; +} + + +static const struct transform_funcs dce_transform_funcs = { + .transform_reset = dce_transform_reset, + .transform_set_scaler = + dce_transform_set_scaler, + .transform_set_gamut_remap = + dce_transform_set_gamut_remap, + .transform_set_pixel_storage_depth = + dce_transform_set_pixel_storage_depth, + .transform_get_optimal_number_of_taps = + dce_transform_get_optimal_number_of_taps +}; + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +bool dce_transform_construct( + struct dce_transform *xfm_dce, + struct dc_context *ctx, + uint32_t inst, + const struct dce_transform_registers *regs, + const struct dce_transform_shift *xfm_shift, + const struct dce_transform_mask *xfm_mask) +{ + xfm_dce->base.ctx = ctx; + + xfm_dce->base.inst = inst; + xfm_dce->base.funcs = &dce_transform_funcs; + + xfm_dce->regs = regs; + xfm_dce->xfm_shift = xfm_shift; + xfm_dce->xfm_mask = xfm_mask; + + xfm_dce->prescaler_on = true; + xfm_dce->lb_pixel_depth_supported = + LB_PIXEL_DEPTH_18BPP | + LB_PIXEL_DEPTH_24BPP | + LB_PIXEL_DEPTH_30BPP; + + xfm_dce->lb_bits_per_entry = LB_BITS_PER_ENTRY; + xfm_dce->lb_memory_size = LB_TOTAL_NUMBER_OF_ENTRIES; /*0x6B0*/ + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.h b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.h new file mode 100644 index 000000000000..897645e2889f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.h @@ -0,0 +1,313 @@ +/* + * Copyright 2012-16 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. + * + * Authors: AMD + * + */ + +#ifndef _DCE_DCE_TRANSFORM_H_ +#define _DCE_DCE_TRANSFORM_H_ + + +#include "transform.h" + +#define TO_DCE_TRANSFORM(transform)\ + container_of(transform, struct dce_transform, base) + +#define LB_TOTAL_NUMBER_OF_ENTRIES 1712 +#define LB_BITS_PER_ENTRY 144 + +#define XFM_COMMON_REG_LIST_DCE_BASE(id) \ + SRI(LB_DATA_FORMAT, LB, id), \ + SRI(GAMUT_REMAP_CONTROL, DCP, id), \ + SRI(GAMUT_REMAP_C11_C12, DCP, id), \ + SRI(GAMUT_REMAP_C13_C14, DCP, id), \ + SRI(GAMUT_REMAP_C21_C22, DCP, id), \ + SRI(GAMUT_REMAP_C23_C24, DCP, id), \ + SRI(GAMUT_REMAP_C31_C32, DCP, id), \ + SRI(GAMUT_REMAP_C33_C34, DCP, id), \ + SRI(DENORM_CONTROL, DCP, id), \ + SRI(DCP_SPATIAL_DITHER_CNTL, DCP, id), \ + SRI(OUT_ROUND_CONTROL, DCP, id), \ + SRI(OUT_CLAMP_CONTROL_R_CR, DCP, id), \ + SRI(OUT_CLAMP_CONTROL_G_Y, DCP, id), \ + SRI(OUT_CLAMP_CONTROL_B_CB, DCP, id), \ + SRI(SCL_MODE, SCL, id), \ + SRI(SCL_TAP_CONTROL, SCL, id), \ + SRI(SCL_CONTROL, SCL, id), \ + SRI(EXT_OVERSCAN_LEFT_RIGHT, SCL, id), \ + SRI(EXT_OVERSCAN_TOP_BOTTOM, SCL, id), \ + SRI(SCL_VERT_FILTER_CONTROL, SCL, id), \ + SRI(SCL_HORZ_FILTER_CONTROL, SCL, id), \ + SRI(SCL_COEF_RAM_SELECT, SCL, id), \ + SRI(SCL_COEF_RAM_TAP_DATA, SCL, id), \ + SRI(VIEWPORT_START, SCL, id), \ + SRI(VIEWPORT_SIZE, SCL, id), \ + SRI(SCL_HORZ_FILTER_SCALE_RATIO, SCL, id), \ + SRI(SCL_VERT_FILTER_SCALE_RATIO, SCL, id), \ + SRI(SCL_HORZ_FILTER_INIT, SCL, id), \ + SRI(SCL_VERT_FILTER_INIT, SCL, id), \ + SRI(SCL_AUTOMATIC_MODE_CONTROL, SCL, id), \ + SRI(LB_MEMORY_CTRL, LB, id), \ + SRI(SCL_UPDATE, SCL, id) + +#define XFM_COMMON_REG_LIST_DCE100(id) \ + XFM_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(DCFE_MEM_PWR_CTRL, CRTC, id), \ + SRI(DCFE_MEM_PWR_STATUS, CRTC, id) + +#define XFM_COMMON_REG_LIST_DCE110(id) \ + XFM_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(DCFE_MEM_PWR_CTRL, DCFE, id), \ + SRI(DCFE_MEM_PWR_STATUS, DCFE, id) + +#define XFM_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define XFM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + XFM_SF(OUT_CLAMP_CONTROL_B_CB, OUT_CLAMP_MIN_B_CB, mask_sh), \ + XFM_SF(OUT_CLAMP_CONTROL_B_CB, OUT_CLAMP_MAX_B_CB, mask_sh), \ + XFM_SF(OUT_CLAMP_CONTROL_G_Y, OUT_CLAMP_MIN_G_Y, mask_sh), \ + XFM_SF(OUT_CLAMP_CONTROL_G_Y, OUT_CLAMP_MAX_G_Y, mask_sh), \ + XFM_SF(OUT_CLAMP_CONTROL_R_CR, OUT_CLAMP_MIN_R_CR, mask_sh), \ + XFM_SF(OUT_CLAMP_CONTROL_R_CR, OUT_CLAMP_MAX_R_CR, mask_sh), \ + XFM_SF(OUT_ROUND_CONTROL, OUT_ROUND_TRUNC_MODE, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_EN, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_MODE, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_DEPTH, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_FRAME_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_RGB_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DCP_SPATIAL_DITHER_CNTL, DCP_HIGHPASS_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DENORM_CONTROL, DENORM_MODE, mask_sh), \ + XFM_SF(LB_DATA_FORMAT, PIXEL_DEPTH, mask_sh), \ + XFM_SF(LB_DATA_FORMAT, PIXEL_EXPAN_MODE, mask_sh), \ + XFM_SF(GAMUT_REMAP_C11_C12, GAMUT_REMAP_C11, mask_sh), \ + XFM_SF(GAMUT_REMAP_C11_C12, GAMUT_REMAP_C12, mask_sh), \ + XFM_SF(GAMUT_REMAP_C13_C14, GAMUT_REMAP_C13, mask_sh), \ + XFM_SF(GAMUT_REMAP_C13_C14, GAMUT_REMAP_C14, mask_sh), \ + XFM_SF(GAMUT_REMAP_C21_C22, GAMUT_REMAP_C21, mask_sh), \ + XFM_SF(GAMUT_REMAP_C21_C22, GAMUT_REMAP_C22, mask_sh), \ + XFM_SF(GAMUT_REMAP_C23_C24, GAMUT_REMAP_C23, mask_sh), \ + XFM_SF(GAMUT_REMAP_C23_C24, GAMUT_REMAP_C24, mask_sh), \ + XFM_SF(GAMUT_REMAP_C31_C32, GAMUT_REMAP_C31, mask_sh), \ + XFM_SF(GAMUT_REMAP_C31_C32, GAMUT_REMAP_C32, mask_sh), \ + XFM_SF(GAMUT_REMAP_C33_C34, GAMUT_REMAP_C33, mask_sh), \ + XFM_SF(GAMUT_REMAP_C33_C34, GAMUT_REMAP_C34, mask_sh), \ + XFM_SF(GAMUT_REMAP_CONTROL, GRPH_GAMUT_REMAP_MODE, mask_sh), \ + XFM_SF(SCL_MODE, SCL_MODE, mask_sh), \ + XFM_SF(SCL_TAP_CONTROL, SCL_H_NUM_OF_TAPS, mask_sh), \ + XFM_SF(SCL_TAP_CONTROL, SCL_V_NUM_OF_TAPS, mask_sh), \ + XFM_SF(SCL_CONTROL, SCL_BOUNDARY_MODE, mask_sh), \ + XFM_SF(EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT, mask_sh), \ + XFM_SF(EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT, mask_sh), \ + XFM_SF(EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP, mask_sh), \ + XFM_SF(EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM, mask_sh), \ + XFM_SF(SCL_COEF_RAM_SELECT, SCL_C_RAM_FILTER_TYPE, mask_sh), \ + XFM_SF(SCL_COEF_RAM_SELECT, SCL_C_RAM_PHASE, mask_sh), \ + XFM_SF(SCL_COEF_RAM_SELECT, SCL_C_RAM_TAP_PAIR_IDX, mask_sh), \ + XFM_SF(SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_EVEN_TAP_COEF_EN, mask_sh), \ + XFM_SF(SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_EVEN_TAP_COEF, mask_sh), \ + XFM_SF(SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_ODD_TAP_COEF_EN, mask_sh), \ + XFM_SF(SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_ODD_TAP_COEF, mask_sh), \ + XFM_SF(VIEWPORT_START, VIEWPORT_X_START, mask_sh), \ + XFM_SF(VIEWPORT_START, VIEWPORT_Y_START, mask_sh), \ + XFM_SF(VIEWPORT_SIZE, VIEWPORT_HEIGHT, mask_sh), \ + XFM_SF(VIEWPORT_SIZE, VIEWPORT_WIDTH, mask_sh), \ + XFM_SF(SCL_HORZ_FILTER_SCALE_RATIO, SCL_H_SCALE_RATIO, mask_sh), \ + XFM_SF(SCL_VERT_FILTER_SCALE_RATIO, SCL_V_SCALE_RATIO, mask_sh), \ + XFM_SF(SCL_HORZ_FILTER_INIT, SCL_H_INIT_INT, mask_sh), \ + XFM_SF(SCL_HORZ_FILTER_INIT, SCL_H_INIT_FRAC, mask_sh), \ + XFM_SF(SCL_VERT_FILTER_INIT, SCL_V_INIT_INT, mask_sh), \ + XFM_SF(SCL_VERT_FILTER_INIT, SCL_V_INIT_FRAC, mask_sh), \ + XFM_SF(LB_MEMORY_CTRL, LB_MEMORY_CONFIG, mask_sh), \ + XFM_SF(LB_MEMORY_CTRL, LB_MEMORY_SIZE, mask_sh), \ + XFM_SF(SCL_VERT_FILTER_CONTROL, SCL_V_2TAP_HARDCODE_COEF_EN, mask_sh), \ + XFM_SF(SCL_HORZ_FILTER_CONTROL, SCL_H_2TAP_HARDCODE_COEF_EN, mask_sh), \ + XFM_SF(SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE, mask_sh), \ + XFM_SF(LB_DATA_FORMAT, ALPHA_EN, mask_sh) + +#define XFM_COMMON_MASK_SH_LIST_DCE110(mask_sh) \ + XFM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + XFM_SF(DCFE_MEM_PWR_CTRL, SCL_COEFF_MEM_PWR_DIS, mask_sh), \ + XFM_SF(DCFE_MEM_PWR_STATUS, SCL_COEFF_MEM_PWR_STATE, mask_sh), \ + XFM_SF(SCL_MODE, SCL_PSCL_EN, mask_sh) + +#define XFM_REG_FIELD_LIST(type) \ + type OUT_CLAMP_MIN_B_CB; \ + type OUT_CLAMP_MAX_B_CB; \ + type OUT_CLAMP_MIN_G_Y; \ + type OUT_CLAMP_MAX_G_Y; \ + type OUT_CLAMP_MIN_R_CR; \ + type OUT_CLAMP_MAX_R_CR; \ + type OUT_ROUND_TRUNC_MODE; \ + type DCP_SPATIAL_DITHER_EN; \ + type DCP_SPATIAL_DITHER_MODE; \ + type DCP_SPATIAL_DITHER_DEPTH; \ + type DCP_FRAME_RANDOM_ENABLE; \ + type DCP_RGB_RANDOM_ENABLE; \ + type DCP_HIGHPASS_RANDOM_ENABLE; \ + type DENORM_MODE; \ + type PIXEL_DEPTH; \ + type PIXEL_EXPAN_MODE; \ + type GAMUT_REMAP_C11; \ + type GAMUT_REMAP_C12; \ + type GAMUT_REMAP_C13; \ + type GAMUT_REMAP_C14; \ + type GAMUT_REMAP_C21; \ + type GAMUT_REMAP_C22; \ + type GAMUT_REMAP_C23; \ + type GAMUT_REMAP_C24; \ + type GAMUT_REMAP_C31; \ + type GAMUT_REMAP_C32; \ + type GAMUT_REMAP_C33; \ + type GAMUT_REMAP_C34; \ + type GRPH_GAMUT_REMAP_MODE; \ + type SCL_MODE; \ + type SCL_PSCL_EN; \ + type SCL_H_NUM_OF_TAPS; \ + type SCL_V_NUM_OF_TAPS; \ + type SCL_BOUNDARY_MODE; \ + type EXT_OVERSCAN_LEFT; \ + type EXT_OVERSCAN_RIGHT; \ + type EXT_OVERSCAN_TOP; \ + type EXT_OVERSCAN_BOTTOM; \ + type SCL_COEFF_MEM_PWR_DIS; \ + type SCL_COEFF_MEM_PWR_STATE; \ + type SCL_C_RAM_FILTER_TYPE; \ + type SCL_C_RAM_PHASE; \ + type SCL_C_RAM_TAP_PAIR_IDX; \ + type SCL_C_RAM_EVEN_TAP_COEF_EN; \ + type SCL_C_RAM_EVEN_TAP_COEF; \ + type SCL_C_RAM_ODD_TAP_COEF_EN; \ + type SCL_C_RAM_ODD_TAP_COEF; \ + type VIEWPORT_X_START; \ + type VIEWPORT_Y_START; \ + type VIEWPORT_HEIGHT; \ + type VIEWPORT_WIDTH; \ + type SCL_H_SCALE_RATIO; \ + type SCL_V_SCALE_RATIO; \ + type SCL_H_INIT_INT; \ + type SCL_H_INIT_FRAC; \ + type SCL_V_INIT_INT; \ + type SCL_V_INIT_FRAC; \ + type LB_MEMORY_CONFIG; \ + type LB_MEMORY_SIZE; \ + type SCL_V_2TAP_HARDCODE_COEF_EN; \ + type SCL_H_2TAP_HARDCODE_COEF_EN; \ + type SCL_COEF_UPDATE_COMPLETE; \ + type ALPHA_EN + +struct dce_transform_shift { + XFM_REG_FIELD_LIST(uint8_t); +}; + +struct dce_transform_mask { + XFM_REG_FIELD_LIST(uint32_t); +}; + +struct dce_transform_registers { + uint32_t LB_DATA_FORMAT; + uint32_t GAMUT_REMAP_CONTROL; + uint32_t GAMUT_REMAP_C11_C12; + uint32_t GAMUT_REMAP_C13_C14; + uint32_t GAMUT_REMAP_C21_C22; + uint32_t GAMUT_REMAP_C23_C24; + uint32_t GAMUT_REMAP_C31_C32; + uint32_t GAMUT_REMAP_C33_C34; + uint32_t DENORM_CONTROL; + uint32_t DCP_SPATIAL_DITHER_CNTL; + uint32_t OUT_ROUND_CONTROL; + uint32_t OUT_CLAMP_CONTROL_R_CR; + uint32_t OUT_CLAMP_CONTROL_G_Y; + uint32_t OUT_CLAMP_CONTROL_B_CB; + uint32_t SCL_MODE; + uint32_t SCL_TAP_CONTROL; + uint32_t SCL_CONTROL; + uint32_t EXT_OVERSCAN_LEFT_RIGHT; + uint32_t EXT_OVERSCAN_TOP_BOTTOM; + uint32_t SCL_VERT_FILTER_CONTROL; + uint32_t SCL_HORZ_FILTER_CONTROL; + uint32_t DCFE_MEM_PWR_CTRL; + uint32_t DCFE_MEM_PWR_STATUS; + uint32_t SCL_COEF_RAM_SELECT; + uint32_t SCL_COEF_RAM_TAP_DATA; + uint32_t VIEWPORT_START; + uint32_t VIEWPORT_SIZE; + uint32_t SCL_HORZ_FILTER_SCALE_RATIO; + uint32_t SCL_VERT_FILTER_SCALE_RATIO; + uint32_t SCL_HORZ_FILTER_INIT; + uint32_t SCL_VERT_FILTER_INIT; + uint32_t SCL_AUTOMATIC_MODE_CONTROL; + uint32_t LB_MEMORY_CTRL; + uint32_t SCL_UPDATE; +}; + +struct init_int_and_frac { + uint32_t integer; + uint32_t fraction; +}; + +struct scl_ratios_inits { + uint32_t h_int_scale_ratio; + uint32_t v_int_scale_ratio; + struct init_int_and_frac h_init; + struct init_int_and_frac v_init; +}; + +enum ram_filter_type { + FILTER_TYPE_RGB_Y_VERTICAL = 0, /* 0 - RGB/Y Vertical filter */ + FILTER_TYPE_CBCR_VERTICAL = 1, /* 1 - CbCr Vertical filter */ + FILTER_TYPE_RGB_Y_HORIZONTAL = 2, /* 1 - RGB/Y Horizontal filter */ + FILTER_TYPE_CBCR_HORIZONTAL = 3, /* 3 - CbCr Horizontal filter */ + FILTER_TYPE_ALPHA_VERTICAL = 4, /* 4 - Alpha Vertical filter. */ + FILTER_TYPE_ALPHA_HORIZONTAL = 5, /* 5 - Alpha Horizontal filter. */ +}; + +struct dce_transform { + struct transform base; + const struct dce_transform_registers *regs; + const struct dce_transform_shift *xfm_shift; + const struct dce_transform_mask *xfm_mask; + + const uint16_t *filter_v; + const uint16_t *filter_h; + const uint16_t *filter_v_c; + const uint16_t *filter_h_c; + int lb_pixel_depth_supported; + int lb_memory_size; + int lb_bits_per_entry; + bool prescaler_on; +}; + +bool dce_transform_construct(struct dce_transform *xfm110, + struct dc_context *ctx, + uint32_t inst, + const struct dce_transform_registers *regs, + const struct dce_transform_shift *xfm_shift, + const struct dce_transform_mask *xfm_mask); + +bool dce_transform_get_optimal_number_of_taps( + struct transform *xfm, + struct scaler_data *scl_data, + const struct scaling_taps *in_taps); + + +#endif /* _DCE_DCE_TRANSFORM_H_ */ |