diff options
Diffstat (limited to '')
96 files changed, 47353 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 000000000..0d7db132a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/Makefile @@ -0,0 +1,37 @@ +# +# Copyright 2017 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. +# +# +# 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 \ +dce_opp.o dce_dmcu.o dce_abm.o dce_ipp.o dce_aux.o \ +dce_i2c.o dce_i2c_hw.o dce_i2c_sw.o dmub_psr.o dmub_abm.o dce_panel_cntl.o \ +dmub_hw_lock_mgr.o dmub_outbox.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_abm.c b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c new file mode 100644 index 000000000..874b132fe --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c @@ -0,0 +1,306 @@ +/* + * 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 <linux/slab.h> + +#include "dce_abm.h" +#include "dm_services.h" +#include "reg_helper.h" +#include "fixed31_32.h" +#include "dc.h" + +#include "atom.h" + + +#define TO_DCE_ABM(abm)\ + container_of(abm, struct dce_abm, base) + +#define REG(reg) \ + (abm_dce->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + abm_dce->abm_shift->field_name, abm_dce->abm_mask->field_name + +#define DC_LOGGER \ + abm->ctx->logger +#define CTX \ + abm_dce->base.ctx + +#define MCP_ABM_LEVEL_SET 0x65 +#define MCP_ABM_PIPE_SET 0x66 +#define MCP_BL_SET 0x67 + +#define MCP_DISABLE_ABM_IMMEDIATELY 255 + +static bool dce_abm_set_pipe(struct abm *abm, uint32_t controller_id, uint32_t panel_inst) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + uint32_t rampingBoundary = 0xFFFF; + + if (abm->dmcu_is_running == false) + return true; + + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + 1, 80000); + + /* 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); + + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + 1, 80000); + + return true; +} + +static void dmcu_set_backlight_level( + struct dce_abm *abm_dce, + uint32_t backlight_pwm_u16_16, + uint32_t frame_ramp, + uint32_t controller_id, + uint32_t panel_id) +{ + unsigned int backlight_8_bit = 0; + uint32_t s2; + + if (backlight_pwm_u16_16 & 0x10000) + // Check for max backlight condition + backlight_8_bit = 0xFF; + else + // Take MSB of fractional part since backlight is not max + backlight_8_bit = (backlight_pwm_u16_16 >> 8) & 0xFF; + + dce_abm_set_pipe(&abm_dce->base, controller_id, panel_id); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, + 0, 1, 80000); + + /* setDMCUParam_BL */ + REG_UPDATE(BL1_PWM_USER_LEVEL, BL1_PWM_USER_LEVEL, backlight_pwm_u16_16); + + /* write ramp */ + if (controller_id == 0) + frame_ramp = 0; + 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; + backlight_8_bit &= (ATOM_S2_CURRENT_BL_LEVEL_MASK >> + ATOM_S2_CURRENT_BL_LEVEL_SHIFT); + s2 |= (backlight_8_bit << ATOM_S2_CURRENT_BL_LEVEL_SHIFT); + + REG_WRITE(BIOS_SCRATCH_2, s2); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, + 0, 1, 80000); +} + +static void dce_abm_init(struct abm *abm, uint32_t backlight) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + + REG_WRITE(DC_ABM1_HG_SAMPLE_RATE, 0x103); + REG_WRITE(DC_ABM1_HG_SAMPLE_RATE, 0x101); + REG_WRITE(DC_ABM1_LS_SAMPLE_RATE, 0x103); + REG_WRITE(DC_ABM1_LS_SAMPLE_RATE, 0x101); + REG_WRITE(BL1_PWM_BL_UPDATE_SAMPLE_RATE, 0x101); + + REG_SET_3(DC_ABM1_HG_MISC_CTRL, 0, + ABM1_HG_NUM_OF_BINS_SEL, 0, + ABM1_HG_VMAX_SEL, 1, + ABM1_HG_BIN_BITWIDTH_SIZE_SEL, 0); + + REG_SET_3(DC_ABM1_IPCSC_COEFF_SEL, 0, + ABM1_IPCSC_COEFF_SEL_R, 2, + ABM1_IPCSC_COEFF_SEL_G, 4, + ABM1_IPCSC_COEFF_SEL_B, 2); + + REG_UPDATE(BL1_PWM_CURRENT_ABM_LEVEL, + BL1_PWM_CURRENT_ABM_LEVEL, backlight); + + REG_UPDATE(BL1_PWM_TARGET_ABM_LEVEL, + BL1_PWM_TARGET_ABM_LEVEL, backlight); + + REG_UPDATE(BL1_PWM_USER_LEVEL, + BL1_PWM_USER_LEVEL, backlight); + + REG_UPDATE_2(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, + ABM1_LS_MIN_PIXEL_VALUE_THRES, 0, + ABM1_LS_MAX_PIXEL_VALUE_THRES, 1000); + + REG_SET_3(DC_ABM1_HGLS_REG_READ_PROGRESS, 0, + ABM1_HG_REG_READ_MISSED_FRAME_CLEAR, 1, + ABM1_LS_REG_READ_MISSED_FRAME_CLEAR, 1, + ABM1_BL_REG_READ_MISSED_FRAME_CLEAR, 1); +} + +static unsigned int dce_abm_get_current_backlight(struct abm *abm) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + unsigned int backlight = REG_READ(BL1_PWM_CURRENT_ABM_LEVEL); + + /* return backlight in hardware format which is unsigned 17 bits, with + * 1 bit integer and 16 bit fractional + */ + return backlight; +} + +static unsigned int dce_abm_get_target_backlight(struct abm *abm) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + unsigned int backlight = REG_READ(BL1_PWM_TARGET_ABM_LEVEL); + + /* return backlight in hardware format which is unsigned 17 bits, with + * 1 bit integer and 16 bit fractional + */ + return backlight; +} + +static bool dce_abm_set_level(struct abm *abm, uint32_t level) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + + if (abm->dmcu_is_running == false) + return true; + + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + 1, 80000); + + /* 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); + + return true; +} + +static bool dce_abm_immediate_disable(struct abm *abm, uint32_t panel_inst) +{ + if (abm->dmcu_is_running == false) + return true; + + dce_abm_set_pipe(abm, MCP_DISABLE_ABM_IMMEDIATELY, panel_inst); + + return true; +} + +static bool dce_abm_set_backlight_level_pwm( + struct abm *abm, + unsigned int backlight_pwm_u16_16, + unsigned int frame_ramp, + unsigned int controller_id, + unsigned int panel_inst) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(abm); + + DC_LOG_BACKLIGHT("New Backlight level: %d (0x%X)\n", + backlight_pwm_u16_16, backlight_pwm_u16_16); + + dmcu_set_backlight_level(abm_dce, + backlight_pwm_u16_16, + frame_ramp, + controller_id, + panel_inst); + + return true; +} + +static const struct abm_funcs dce_funcs = { + .abm_init = dce_abm_init, + .set_abm_level = dce_abm_set_level, + .set_pipe = dce_abm_set_pipe, + .set_backlight_level_pwm = dce_abm_set_backlight_level_pwm, + .get_current_backlight = dce_abm_get_current_backlight, + .get_target_backlight = dce_abm_get_target_backlight, + .init_abm_config = NULL, + .set_abm_immediate_disable = dce_abm_immediate_disable, +}; + +static void dce_abm_construct( + struct dce_abm *abm_dce, + struct dc_context *ctx, + const struct dce_abm_registers *regs, + const struct dce_abm_shift *abm_shift, + const struct dce_abm_mask *abm_mask) +{ + struct abm *base = &abm_dce->base; + + base->ctx = ctx; + base->funcs = &dce_funcs; + base->dmcu_is_running = false; + + abm_dce->regs = regs; + abm_dce->abm_shift = abm_shift; + abm_dce->abm_mask = abm_mask; +} + +struct abm *dce_abm_create( + struct dc_context *ctx, + const struct dce_abm_registers *regs, + const struct dce_abm_shift *abm_shift, + const struct dce_abm_mask *abm_mask) +{ + struct dce_abm *abm_dce = kzalloc(sizeof(*abm_dce), GFP_ATOMIC); + + if (abm_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_abm_construct(abm_dce, ctx, regs, abm_shift, abm_mask); + + abm_dce->base.funcs = &dce_funcs; + + return &abm_dce->base; +} + +void dce_abm_destroy(struct abm **abm) +{ + struct dce_abm *abm_dce = TO_DCE_ABM(*abm); + + kfree(abm_dce); + *abm = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.h b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.h new file mode 100644 index 000000000..e6c063257 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.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_ABM_H_ +#define _DCE_ABM_H_ + +#include "abm.h" + +#define ABM_COMMON_REG_LIST_DCE_BASE() \ + SR(MASTER_COMM_CNTL_REG), \ + SR(MASTER_COMM_CMD_REG), \ + SR(MASTER_COMM_DATA_REG1) + +#define ABM_DCE110_COMMON_REG_LIST() \ + ABM_COMMON_REG_LIST_DCE_BASE(), \ + SR(DC_ABM1_HG_SAMPLE_RATE), \ + SR(DC_ABM1_LS_SAMPLE_RATE), \ + SR(BL1_PWM_BL_UPDATE_SAMPLE_RATE), \ + SR(DC_ABM1_HG_MISC_CTRL), \ + SR(DC_ABM1_IPCSC_COEFF_SEL), \ + SR(BL1_PWM_CURRENT_ABM_LEVEL), \ + SR(BL1_PWM_TARGET_ABM_LEVEL), \ + SR(BL1_PWM_USER_LEVEL), \ + SR(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES), \ + SR(DC_ABM1_HGLS_REG_READ_PROGRESS), \ + SR(DC_ABM1_ACE_OFFSET_SLOPE_0), \ + SR(DC_ABM1_ACE_THRES_12), \ + SR(BIOS_SCRATCH_2) + +#define ABM_DCN10_REG_LIST(id)\ + ABM_COMMON_REG_LIST_DCE_BASE(), \ + SRI(DC_ABM1_HG_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_LS_SAMPLE_RATE, ABM, id), \ + SRI(BL1_PWM_BL_UPDATE_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_HG_MISC_CTRL, ABM, id), \ + SRI(DC_ABM1_IPCSC_COEFF_SEL, ABM, id), \ + SRI(BL1_PWM_CURRENT_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_TARGET_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_USER_LEVEL, ABM, id), \ + SRI(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, ABM, id), \ + SRI(DC_ABM1_HGLS_REG_READ_PROGRESS, ABM, id), \ + SRI(DC_ABM1_ACE_OFFSET_SLOPE_0, ABM, id), \ + SRI(DC_ABM1_ACE_THRES_12, ABM, id), \ + NBIO_SR(BIOS_SCRATCH_2) + +#define ABM_DCN20_REG_LIST() \ + ABM_COMMON_REG_LIST_DCE_BASE(), \ + SR(DC_ABM1_HG_SAMPLE_RATE), \ + SR(DC_ABM1_LS_SAMPLE_RATE), \ + SR(BL1_PWM_BL_UPDATE_SAMPLE_RATE), \ + SR(DC_ABM1_HG_MISC_CTRL), \ + SR(DC_ABM1_IPCSC_COEFF_SEL), \ + SR(BL1_PWM_CURRENT_ABM_LEVEL), \ + SR(BL1_PWM_TARGET_ABM_LEVEL), \ + SR(BL1_PWM_USER_LEVEL), \ + SR(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES), \ + SR(DC_ABM1_HGLS_REG_READ_PROGRESS), \ + SR(DC_ABM1_ACE_OFFSET_SLOPE_0), \ + SR(DC_ABM1_ACE_THRES_12), \ + NBIO_SR(BIOS_SCRATCH_2) + +#define ABM_DCN301_REG_LIST(id)\ + ABM_COMMON_REG_LIST_DCE_BASE(), \ + SRI(DC_ABM1_HG_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_LS_SAMPLE_RATE, ABM, id), \ + SRI(BL1_PWM_BL_UPDATE_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_HG_MISC_CTRL, ABM, id), \ + SRI(DC_ABM1_IPCSC_COEFF_SEL, ABM, id), \ + SRI(BL1_PWM_CURRENT_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_TARGET_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_USER_LEVEL, ABM, id), \ + SRI(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, ABM, id), \ + SRI(DC_ABM1_HGLS_REG_READ_PROGRESS, ABM, id), \ + NBIO_SR(BIOS_SCRATCH_2) + +#define ABM_DCN302_REG_LIST(id)\ + ABM_COMMON_REG_LIST_DCE_BASE(), \ + SRI(DC_ABM1_HG_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_LS_SAMPLE_RATE, ABM, id), \ + SRI(BL1_PWM_BL_UPDATE_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_HG_MISC_CTRL, ABM, id), \ + SRI(DC_ABM1_IPCSC_COEFF_SEL, ABM, id), \ + SRI(BL1_PWM_CURRENT_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_TARGET_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_USER_LEVEL, ABM, id), \ + SRI(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, ABM, id), \ + SRI(DC_ABM1_HGLS_REG_READ_PROGRESS, ABM, id), \ + SRI(DC_ABM1_ACE_OFFSET_SLOPE_0, ABM, id), \ + SRI(DC_ABM1_ACE_THRES_12, ABM, id), \ + NBIO_SR(BIOS_SCRATCH_2) + +#define ABM_DCN30_REG_LIST(id)\ + ABM_COMMON_REG_LIST_DCE_BASE(), \ + SRI(DC_ABM1_HG_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_LS_SAMPLE_RATE, ABM, id), \ + SRI(BL1_PWM_BL_UPDATE_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_HG_MISC_CTRL, ABM, id), \ + SRI(DC_ABM1_IPCSC_COEFF_SEL, ABM, id), \ + SRI(BL1_PWM_CURRENT_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_TARGET_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_USER_LEVEL, ABM, id), \ + SRI(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, ABM, id), \ + SRI(DC_ABM1_HGLS_REG_READ_PROGRESS, ABM, id), \ + SRI(DC_ABM1_ACE_OFFSET_SLOPE_0, ABM, id), \ + SRI(DC_ABM1_ACE_THRES_12, ABM, id), \ + NBIO_SR(BIOS_SCRATCH_2) + +#define ABM_DCN32_REG_LIST(id)\ + SRI(DC_ABM1_HG_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_LS_SAMPLE_RATE, ABM, id), \ + SRI(BL1_PWM_BL_UPDATE_SAMPLE_RATE, ABM, id), \ + SRI(DC_ABM1_HG_MISC_CTRL, ABM, id), \ + SRI(DC_ABM1_IPCSC_COEFF_SEL, ABM, id), \ + SRI(BL1_PWM_CURRENT_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_TARGET_ABM_LEVEL, ABM, id), \ + SRI(BL1_PWM_USER_LEVEL, ABM, id), \ + SRI(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, ABM, id), \ + SRI(DC_ABM1_HGLS_REG_READ_PROGRESS, ABM, id), \ + SRI(DC_ABM1_ACE_OFFSET_SLOPE_0, ABM, id), \ + SRI(DC_ABM1_ACE_THRES_12, ABM, id), \ + NBIO_SR(BIOS_SCRATCH_2) + +#define ABM_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define ABM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + ABM_SF(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, mask_sh), \ + ABM_SF(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, mask_sh), \ + ABM_SF(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE1, mask_sh), \ + ABM_SF(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE2, mask_sh) + +#define ABM_MASK_SH_LIST_DCE110(mask_sh) \ + ABM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + ABM_SF(DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_NUM_OF_BINS_SEL, mask_sh), \ + ABM_SF(DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_VMAX_SEL, mask_sh), \ + ABM_SF(DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_BIN_BITWIDTH_SIZE_SEL, mask_sh), \ + ABM_SF(DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_R, mask_sh), \ + ABM_SF(DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_G, mask_sh), \ + ABM_SF(DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_B, mask_sh), \ + ABM_SF(BL1_PWM_CURRENT_ABM_LEVEL, \ + BL1_PWM_CURRENT_ABM_LEVEL, mask_sh), \ + ABM_SF(BL1_PWM_TARGET_ABM_LEVEL, \ + BL1_PWM_TARGET_ABM_LEVEL, mask_sh), \ + ABM_SF(BL1_PWM_USER_LEVEL, \ + BL1_PWM_USER_LEVEL, mask_sh), \ + ABM_SF(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, \ + ABM1_LS_MIN_PIXEL_VALUE_THRES, mask_sh), \ + ABM_SF(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, \ + ABM1_LS_MAX_PIXEL_VALUE_THRES, mask_sh), \ + ABM_SF(DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_HG_REG_READ_MISSED_FRAME_CLEAR, mask_sh), \ + ABM_SF(DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_LS_REG_READ_MISSED_FRAME_CLEAR, mask_sh), \ + ABM_SF(DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_BL_REG_READ_MISSED_FRAME_CLEAR, mask_sh) + +#define ABM_MASK_SH_LIST_DCN10(mask_sh) \ + ABM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_NUM_OF_BINS_SEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_VMAX_SEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_BIN_BITWIDTH_SIZE_SEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_R, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_G, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_B, mask_sh), \ + ABM_SF(ABM0_BL1_PWM_CURRENT_ABM_LEVEL, \ + BL1_PWM_CURRENT_ABM_LEVEL, mask_sh), \ + ABM_SF(ABM0_BL1_PWM_TARGET_ABM_LEVEL, \ + BL1_PWM_TARGET_ABM_LEVEL, mask_sh), \ + ABM_SF(ABM0_BL1_PWM_USER_LEVEL, \ + BL1_PWM_USER_LEVEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, \ + ABM1_LS_MIN_PIXEL_VALUE_THRES, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, \ + ABM1_LS_MAX_PIXEL_VALUE_THRES, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_HG_REG_READ_MISSED_FRAME_CLEAR, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_LS_REG_READ_MISSED_FRAME_CLEAR, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_BL_REG_READ_MISSED_FRAME_CLEAR, mask_sh) + +#define ABM_MASK_SH_LIST_DCN20(mask_sh) ABM_MASK_SH_LIST_DCE110(mask_sh) + +#define ABM_MASK_SH_LIST_DCN30(mask_sh) ABM_MASK_SH_LIST_DCN10(mask_sh) + +#define ABM_MASK_SH_LIST_DCN32(mask_sh) \ + ABM_SF(ABM0_DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_NUM_OF_BINS_SEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_VMAX_SEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HG_MISC_CTRL, \ + ABM1_HG_BIN_BITWIDTH_SIZE_SEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_R, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_G, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_IPCSC_COEFF_SEL, \ + ABM1_IPCSC_COEFF_SEL_B, mask_sh), \ + ABM_SF(ABM0_BL1_PWM_CURRENT_ABM_LEVEL, \ + BL1_PWM_CURRENT_ABM_LEVEL, mask_sh), \ + ABM_SF(ABM0_BL1_PWM_TARGET_ABM_LEVEL, \ + BL1_PWM_TARGET_ABM_LEVEL, mask_sh), \ + ABM_SF(ABM0_BL1_PWM_USER_LEVEL, \ + BL1_PWM_USER_LEVEL, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, \ + ABM1_LS_MIN_PIXEL_VALUE_THRES, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, \ + ABM1_LS_MAX_PIXEL_VALUE_THRES, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_HG_REG_READ_MISSED_FRAME_CLEAR, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_LS_REG_READ_MISSED_FRAME_CLEAR, mask_sh), \ + ABM_SF(ABM0_DC_ABM1_HGLS_REG_READ_PROGRESS, \ + ABM1_BL_REG_READ_MISSED_FRAME_CLEAR, mask_sh) + +#define ABM_REG_FIELD_LIST(type) \ + type ABM1_HG_NUM_OF_BINS_SEL; \ + type ABM1_HG_VMAX_SEL; \ + type ABM1_HG_BIN_BITWIDTH_SIZE_SEL; \ + type ABM1_IPCSC_COEFF_SEL_R; \ + type ABM1_IPCSC_COEFF_SEL_G; \ + type ABM1_IPCSC_COEFF_SEL_B; \ + type BL1_PWM_CURRENT_ABM_LEVEL; \ + type BL1_PWM_TARGET_ABM_LEVEL; \ + type BL1_PWM_USER_LEVEL; \ + type ABM1_LS_MIN_PIXEL_VALUE_THRES; \ + type ABM1_LS_MAX_PIXEL_VALUE_THRES; \ + type ABM1_HG_REG_READ_MISSED_FRAME_CLEAR; \ + type ABM1_LS_REG_READ_MISSED_FRAME_CLEAR; \ + type ABM1_BL_REG_READ_MISSED_FRAME_CLEAR; \ + type MASTER_COMM_INTERRUPT; \ + type MASTER_COMM_CMD_REG_BYTE0; \ + type MASTER_COMM_CMD_REG_BYTE1; \ + type MASTER_COMM_CMD_REG_BYTE2 + +struct dce_abm_shift { + ABM_REG_FIELD_LIST(uint8_t); +}; + +struct dce_abm_mask { + ABM_REG_FIELD_LIST(uint32_t); +}; + +struct dce_abm_registers { + uint32_t DC_ABM1_HG_SAMPLE_RATE; + uint32_t DC_ABM1_LS_SAMPLE_RATE; + uint32_t BL1_PWM_BL_UPDATE_SAMPLE_RATE; + uint32_t DC_ABM1_HG_MISC_CTRL; + uint32_t DC_ABM1_IPCSC_COEFF_SEL; + uint32_t BL1_PWM_CURRENT_ABM_LEVEL; + uint32_t BL1_PWM_TARGET_ABM_LEVEL; + uint32_t BL1_PWM_USER_LEVEL; + uint32_t DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES; + uint32_t DC_ABM1_HGLS_REG_READ_PROGRESS; + uint32_t DC_ABM1_ACE_OFFSET_SLOPE_0; + uint32_t DC_ABM1_ACE_THRES_12; + uint32_t MASTER_COMM_CNTL_REG; + uint32_t MASTER_COMM_CMD_REG; + uint32_t MASTER_COMM_DATA_REG1; + uint32_t BIOS_SCRATCH_2; +}; + +struct dce_abm { + struct abm base; + const struct dce_abm_registers *regs; + const struct dce_abm_shift *abm_shift; + const struct dce_abm_mask *abm_mask; +}; + +struct abm *dce_abm_create( + struct dc_context *ctx, + const struct dce_abm_registers *regs, + const struct dce_abm_shift *abm_shift, + const struct dce_abm_mask *abm_mask); + +void dce_abm_destroy(struct abm **abm); + +#endif /* _DCE_ABM_H_ */ 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 000000000..c94a966c6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c @@ -0,0 +1,1097 @@ +/* + * 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 DC_LOGGER_INIT() + +#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); +} + +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); + + 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; + if (!crtc_info) + return; + + /* 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_100Hz <= 270000) && + (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_100Hz <= 270000) && + (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_100Hz <= 540000) && + (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 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 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); + DC_LOGGER_INIT(); + + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + CLOCK_GATING_DISABLE); + 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); + set_reg_field_value(value, 0, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + CLOCK_GATING_DISABLE); + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); + + DC_LOG_HW_AUDIO("\n\t========= AUDIO:dce_aud_az_enable: index: %u data: 0x%x\n", + audio->inst, value); +} + +void dce_aud_az_disable(struct audio *audio) +{ + uint32_t value; + DC_LOGGER_INIT(); + + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + CLOCK_GATING_DISABLE); + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); + + 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); + + set_reg_field_value(value, 0, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + CLOCK_GATING_DISABLE); + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + DC_LOG_HW_AUDIO("\n\t========= AUDIO:dce_aud_az_disable: index: %u data: 0x%x\n", + audio->inst, 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; + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + CLOCK_GATING_DISABLE); + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); + + /* 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); + + /* ACP Data - Supports AI */ + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_ACP_DATA); + + set_reg_field_value( + value, + audio_info->flags.info.SUPPORT_AI, + AZALIA_F0_CODEC_PIN_CONTROL_ACP_DATA, + SUPPORTS_AI); + + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_ACP_DATA, 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; + uint8_t channel_count = audio_mode->channel_count; + + /* adjust specific properties */ + switch (audio_format_code) { + case AUDIO_FORMAT_CODE_LINEARPCM: { + + check_audio_bandwidth( + crtc_info, + 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, + 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); + DC_LOG_HW_AUDIO("\n\tAUDIO:az_configure: index: %u data, 0x%x, displayName %s: \n", + audio->inst, value, audio_info->display_name); + + /* + *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); + 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, + CLOCK_GATING_DISABLE); + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); +} + +/* +* todo: wall clk related functionality probably belong to clock_src. +*/ + +/* search pixel clock value for Azalia HDMI Audio */ +static void get_azalia_clock_info_hdmi( + uint32_t crtc_pixel_clock_100hz, + uint32_t actual_pixel_clock_100Hz, + struct azalia_clock_info *azalia_clock_info) +{ + /* 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_100Hz; +} + +static void get_azalia_clock_info_dp( + uint32_t requested_pixel_clock_100Hz, + const struct audio_pll_info *pll_info, + struct azalia_clock_info *azalia_clock_info) +{ + /* 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; +} + +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_tmds_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_100Hz, + crtc_info->calculated_pixel_clock_100Hz, + &clock_info); + + DC_LOG_HW_AUDIO("\n%s:Input::requested_pixel_clock_100Hz = %d"\ + "calculated_pixel_clock_100Hz =%d\n"\ + "audio_dto_module = %d audio_dto_phase =%d \n\n", __func__,\ + crtc_info->requested_pixel_clock_100Hz,\ + crtc_info->calculated_pixel_clock_100Hz,\ + clock_info.audio_dto_module,\ + clock_info.audio_dto_phase); + + /* 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_100Hz, + 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); + + /* 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); + + REG_UPDATE(DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO2_USE_512FBR_DTO, 1); + + } +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_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_100Hz, + crtc_info->calculated_pixel_clock_100Hz, + &clock_info); + + DC_LOG_HW_AUDIO("\n%s:Input::requested_pixel_clock_100Hz = %d"\ + "calculated_pixel_clock_100Hz =%d\n"\ + "audio_dto_module = %d audio_dto_phase =%d \n\n", __func__,\ + crtc_info->requested_pixel_clock_100Hz,\ + crtc_info->calculated_pixel_clock_100Hz,\ + clock_info.audio_dto_module,\ + clock_info.audio_dto_phase); + + /* 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, 128*Fs from 24MHz (DCE6 does not support 512*Fs) + -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_100Hz, + 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); + + /* DCCG_AUDIO_DTO2_USE_512FBR_DTO, 1) + * Cannot select 512fs for DP + * + * DCE6 has no DCCG_AUDIO_DTO2_USE_512FBR_DTO mask + */ + + /* 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); + + /* DCE6 has no DCCG_AUDIO_DTO2_USE_512FBR_DTO mask in DCCG_AUDIO_DTO_SOURCE reg */ + + } +} +#endif + +static 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) +{ + uint32_t value; + 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 + */ + /*disable clock gating before write to endpoint register*/ + value = AZ_REG_READ(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + CLOCK_GATING_DISABLE); + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); + 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); + set_reg_field_value(value, 0, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + CLOCK_GATING_DISABLE); + AZ_REG_WRITE(AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, value); +} + +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, +}; + +#if defined(CONFIG_DRM_AMD_DC_SI) +static const struct audio_funcs dce60_funcs = { + .endpoint_valid = dce_aud_endpoint_valid, + .hw_init = dce_aud_hw_init, + .wall_dto_setup = dce60_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, +}; +#endif + +void dce_aud_destroy(struct audio **audio) +{ + struct dce_audio *aud = DCE_AUD(*audio); + + kfree(aud); + *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_audio_mask *masks + ) +{ + struct dce_audio *audio = kzalloc(sizeof(*audio), GFP_KERNEL); + + 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; +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +struct audio *dce60_audio_create( + struct dc_context *ctx, + unsigned int inst, + const struct dce_audio_registers *reg, + const struct dce_audio_shift *shifts, + const struct dce_audio_mask *masks + ) +{ + struct dce_audio *audio = kzalloc(sizeof(*audio), GFP_KERNEL); + + if (audio == NULL) { + ASSERT_CRITICAL(audio); + return NULL; + } + + audio->base.ctx = ctx; + audio->base.inst = inst; + audio->base.funcs = &dce60_funcs; + + audio->regs = reg; + audio->shifts = shifts; + audio->masks = masks; + return &audio->base; +} +#endif 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 000000000..dbd2cfed0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.h @@ -0,0 +1,180 @@ +/* + * 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_DTO_SOURCE, DCCG_AUDIO_DTO2_USE_512FBR_DTO, mask_sh),\ + SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO0_USE_512FBR_DTO, mask_sh),\ + SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO1_USE_512FBR_DTO, 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_DTO1_MODULE, DCCG_AUDIO_DTO1_MODULE, mask_sh),\ + SF(DCCG_AUDIO_DTO1_PHASE, DCCG_AUDIO_DTO1_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) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define AUD_DCE60_MASK_SH_LIST(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_DTO1_MODULE, DCCG_AUDIO_DTO1_MODULE, mask_sh),\ + SF(DCCG_AUDIO_DTO1_PHASE, DCCG_AUDIO_DTO1_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), \ + SF(AZALIA_F0_CODEC_ENDPOINT_INDEX, AZALIA_ENDPOINT_REG_INDEX, mask_sh),\ + SF(AZALIA_F0_CODEC_ENDPOINT_DATA, AZALIA_ENDPOINT_REG_DATA, mask_sh) +#endif + +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; + uint8_t DCCG_AUDIO_DTO2_USE_512FBR_DTO; + uint32_t DCCG_AUDIO_DTO0_USE_512FBR_DTO; + uint32_t DCCG_AUDIO_DTO1_USE_512FBR_DTO; + uint32_t CLOCK_GATING_DISABLE; +}; + +struct dce_audio_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; + uint32_t DCCG_AUDIO_DTO2_USE_512FBR_DTO; + uint32_t DCCG_AUDIO_DTO0_USE_512FBR_DTO; + uint32_t DCCG_AUDIO_DTO1_USE_512FBR_DTO; + uint32_t CLOCK_GATING_DISABLE; + +}; + +struct dce_audio { + struct audio base; + const struct dce_audio_registers *regs; + const struct dce_audio_shift *shifts; + const struct dce_audio_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_audio_mask *masks); + +#if defined(CONFIG_DRM_AMD_DC_SI) +struct audio *dce60_audio_create( + struct dc_context *ctx, + unsigned int inst, + const struct dce_audio_registers *reg, + const struct dce_audio_shift *shifts, + const struct dce_audio_mask *masks); +#endif + +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_aux.c b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c new file mode 100644 index 000000000..140297c8f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c @@ -0,0 +1,959 @@ +/* + * 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 "core_types.h" +#include "dce_aux.h" +#include "dce/dce_11_0_sh_mask.h" +#include "dm_event_log.h" +#include "dm_helpers.h" +#include "dmub/inc/dmub_cmd.h" + +#define CTX \ + aux110->base.ctx +#define REG(reg_name)\ + (aux110->regs->reg_name) + +#define DC_LOGGER \ + engine->ctx->logger + +#define DC_TRACE_LEVEL_MESSAGE(...) do { } while (0) +#define IS_DC_I2CAUX_LOGGING_ENABLED() (false) +#define LOG_FLAG_Error_I2cAux LOG_ERROR +#define LOG_FLAG_I2cAux_DceAux LOG_I2C_AUX + +#include "reg_helper.h" + +#undef FN +#define FN(reg_name, field_name) \ + aux110->shift->field_name, aux110->mask->field_name + +#define FROM_AUX_ENGINE(ptr) \ + container_of((ptr), struct aux_engine_dce110, base) + +#define FROM_ENGINE(ptr) \ + FROM_AUX_ENGINE(container_of((ptr), struct dce_aux, base)) + +#define FROM_AUX_ENGINE_ENGINE(ptr) \ + container_of((ptr), struct dce_aux, base) +enum { + AUX_INVALID_REPLY_RETRY_COUNTER = 1, + AUX_TIMED_OUT_RETRY_COUNTER = 2, + AUX_DEFER_RETRY_COUNTER = 6 +}; + +#define TIME_OUT_INCREMENT 1016 +#define TIME_OUT_MULTIPLIER_8 8 +#define TIME_OUT_MULTIPLIER_16 16 +#define TIME_OUT_MULTIPLIER_32 32 +#define TIME_OUT_MULTIPLIER_64 64 +#define MAX_TIMEOUT_LENGTH 127 +#define DEFAULT_AUX_ENGINE_MULT 0 +#define DEFAULT_AUX_ENGINE_LENGTH 69 + +#define DC_TRACE_LEVEL_MESSAGE(...) do { } while (0) + +static void release_engine( + struct dce_aux *engine) +{ + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); + + dal_ddc_close(engine->ddc); + + engine->ddc = NULL; + + REG_UPDATE_2(AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, 1, + AUX_SW_USE_AUX_REG_REQ, 0); +} + +#define SW_CAN_ACCESS_AUX 1 +#define DMCU_CAN_ACCESS_AUX 2 + +static bool is_engine_available( + struct dce_aux *engine) +{ + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); + + uint32_t value = REG_READ(AUX_ARB_CONTROL); + uint32_t field = get_reg_field_value( + value, + AUX_ARB_CONTROL, + AUX_REG_RW_CNTL_STATUS); + + return (field != DMCU_CAN_ACCESS_AUX); +} +static bool acquire_engine( + struct dce_aux *engine) +{ + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); + + uint32_t value = REG_READ(AUX_ARB_CONTROL); + uint32_t field = get_reg_field_value( + value, + AUX_ARB_CONTROL, + AUX_REG_RW_CNTL_STATUS); + if (field == DMCU_CAN_ACCESS_AUX) + return false; + /* enable AUX before request SW to access AUX */ + value = REG_READ(AUX_CONTROL); + field = get_reg_field_value(value, + AUX_CONTROL, + AUX_EN); + + if (field == 0) { + set_reg_field_value( + value, + 1, + AUX_CONTROL, + AUX_EN); + + if (REG(AUX_RESET_MASK)) { + /*DP_AUX block as part of the enable sequence*/ + set_reg_field_value( + value, + 1, + AUX_CONTROL, + AUX_RESET); + } + + REG_WRITE(AUX_CONTROL, value); + + if (REG(AUX_RESET_MASK)) { + /*poll HW to make sure reset it done*/ + + REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 1, + 1, 11); + + set_reg_field_value( + value, + 0, + AUX_CONTROL, + AUX_RESET); + + REG_WRITE(AUX_CONTROL, value); + + REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 0, + 1, 11); + } + } /*if (field)*/ + + /* request SW to access AUX */ + REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, 1); + + value = REG_READ(AUX_ARB_CONTROL); + field = get_reg_field_value( + value, + AUX_ARB_CONTROL, + AUX_REG_RW_CNTL_STATUS); + + return (field == SW_CAN_ACCESS_AUX); +} + +#define COMPOSE_AUX_SW_DATA_16_20(command, address) \ + ((command) | ((0xF0000 & (address)) >> 16)) + +#define COMPOSE_AUX_SW_DATA_8_15(address) \ + ((0xFF00 & (address)) >> 8) + +#define COMPOSE_AUX_SW_DATA_0_7(address) \ + (0xFF & (address)) + +static void submit_channel_request( + struct dce_aux *engine, + struct aux_request_transaction_data *request) +{ + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); + uint32_t value; + uint32_t length; + + bool is_write = + ((request->type == AUX_TRANSACTION_TYPE_DP) && + (request->action == I2CAUX_TRANSACTION_ACTION_DP_WRITE)) || + ((request->type == AUX_TRANSACTION_TYPE_I2C) && + ((request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || + (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT))); + if (REG(AUXN_IMPCAL)) { + /* clear_aux_error */ + REG_UPDATE_SEQ_2(AUXN_IMPCAL, + AUXN_CALOUT_ERROR_AK, 1, + AUXN_CALOUT_ERROR_AK, 0); + + REG_UPDATE_SEQ_2(AUXP_IMPCAL, + AUXP_CALOUT_ERROR_AK, 1, + AUXP_CALOUT_ERROR_AK, 0); + + /* force_default_calibrate */ + REG_UPDATE_SEQ_2(AUXN_IMPCAL, + AUXN_IMPCAL_ENABLE, 1, + AUXN_IMPCAL_OVERRIDE_ENABLE, 0); + + /* bug? why AUXN update EN and OVERRIDE_EN 1 by 1 while AUX P toggles OVERRIDE? */ + + REG_UPDATE_SEQ_2(AUXP_IMPCAL, + AUXP_IMPCAL_OVERRIDE_ENABLE, 1, + AUXP_IMPCAL_OVERRIDE_ENABLE, 0); + } + + REG_UPDATE(AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, 1); + + REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 0, + 10, aux110->polling_timeout_period/10); + + /* set the delay and the number of bytes to write */ + + /* The length include + * the 4 bit header and the 20 bit address + * (that is 3 byte). + * If the requested length is non zero this means + * an addition byte specifying the length is required. + */ + + length = request->length ? 4 : 3; + if (is_write) + length += request->length; + + REG_UPDATE_2(AUX_SW_CONTROL, + AUX_SW_START_DELAY, request->delay, + AUX_SW_WR_BYTES, length); + + /* program action and address and payload data (if 'is_write') */ + value = REG_UPDATE_4(AUX_SW_DATA, + AUX_SW_INDEX, 0, + AUX_SW_DATA_RW, 0, + AUX_SW_AUTOINCREMENT_DISABLE, 1, + AUX_SW_DATA, COMPOSE_AUX_SW_DATA_16_20(request->action, request->address)); + + value = REG_SET_2(AUX_SW_DATA, value, + AUX_SW_AUTOINCREMENT_DISABLE, 0, + AUX_SW_DATA, COMPOSE_AUX_SW_DATA_8_15(request->address)); + + value = REG_SET(AUX_SW_DATA, value, + AUX_SW_DATA, COMPOSE_AUX_SW_DATA_0_7(request->address)); + + if (request->length) { + value = REG_SET(AUX_SW_DATA, value, + AUX_SW_DATA, request->length - 1); + } + + if (is_write) { + /* Load the HW buffer with the Data to be sent. + * This is relevant for write operation. + * For read, the data recived data will be + * processed in process_channel_reply(). + */ + uint32_t i = 0; + + while (i < request->length) { + value = REG_SET(AUX_SW_DATA, value, + AUX_SW_DATA, request->data[i]); + + ++i; + } + } + + REG_UPDATE(AUX_SW_CONTROL, AUX_SW_GO, 1); + EVENT_LOG_AUX_REQ(engine->ddc->pin_data->en, EVENT_LOG_AUX_ORIGIN_NATIVE, + request->action, request->address, request->length, request->data); +} + +static int read_channel_reply(struct dce_aux *engine, uint32_t size, + uint8_t *buffer, uint8_t *reply_result, + uint32_t *sw_status) +{ + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); + uint32_t bytes_replied; + uint32_t reply_result_32; + + *sw_status = REG_GET(AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT, + &bytes_replied); + + /* In case HPD is LOW, exit AUX transaction */ + if ((*sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) + return -1; + + /* Need at least the status byte */ + if (!bytes_replied) + return -1; + + REG_UPDATE_SEQ_3(AUX_SW_DATA, + AUX_SW_INDEX, 0, + AUX_SW_AUTOINCREMENT_DISABLE, 1, + AUX_SW_DATA_RW, 1); + + REG_GET(AUX_SW_DATA, AUX_SW_DATA, &reply_result_32); + reply_result_32 = reply_result_32 >> 4; + if (reply_result != NULL) + *reply_result = (uint8_t)reply_result_32; + + if (reply_result_32 == 0) { /* ACK */ + uint32_t i = 0; + + /* First byte was already used to get the command status */ + --bytes_replied; + + /* Do not overflow buffer */ + if (bytes_replied > size) + return -1; + + while (i < bytes_replied) { + uint32_t aux_sw_data_val; + + REG_GET(AUX_SW_DATA, AUX_SW_DATA, &aux_sw_data_val); + buffer[i] = aux_sw_data_val; + ++i; + } + + return i; + } + + return 0; +} + +static enum aux_return_code_type get_channel_status( + struct dce_aux *engine, + uint8_t *returned_bytes) +{ + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); + + uint32_t value; + + if (returned_bytes == NULL) { + /*caller pass NULL pointer*/ + ASSERT_CRITICAL(false); + return AUX_RET_ERROR_UNKNOWN; + } + *returned_bytes = 0; + + /* poll to make sure that SW_DONE is asserted */ + REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 1, + 10, aux110->polling_timeout_period/10); + + value = REG_READ(AUX_SW_STATUS); + /* in case HPD is LOW, exit AUX transaction */ + if ((value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) + return AUX_RET_ERROR_HPD_DISCON; + + /* Note that the following bits are set in 'status.bits' + * during CTS 4.2.1.2 (FW 3.3.1): + * AUX_SW_RX_MIN_COUNT_VIOL, AUX_SW_RX_INVALID_STOP, + * AUX_SW_RX_RECV_NO_DET, AUX_SW_RX_RECV_INVALID_H. + * + * AUX_SW_RX_MIN_COUNT_VIOL is an internal, + * HW debugging bit and should be ignored. + */ + if (value & AUX_SW_STATUS__AUX_SW_DONE_MASK) { + if ((value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_STATE_MASK) || + (value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_MASK)) + return AUX_RET_ERROR_TIMEOUT; + + else if ((value & AUX_SW_STATUS__AUX_SW_RX_INVALID_STOP_MASK) || + (value & AUX_SW_STATUS__AUX_SW_RX_RECV_NO_DET_MASK) || + (value & + AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_H_MASK) || + (value & AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_L_MASK)) + return AUX_RET_ERROR_INVALID_REPLY; + + *returned_bytes = get_reg_field_value(value, + AUX_SW_STATUS, + AUX_SW_REPLY_BYTE_COUNT); + + if (*returned_bytes == 0) + return + AUX_RET_ERROR_INVALID_REPLY; + else { + *returned_bytes -= 1; + return AUX_RET_SUCCESS; + } + } else { + /*time_elapsed >= aux_engine->timeout_period + * AUX_SW_STATUS__AUX_SW_HPD_DISCON = at this point + */ + ASSERT_CRITICAL(false); + return AUX_RET_ERROR_TIMEOUT; + } +} + +static bool acquire( + struct dce_aux *engine, + struct ddc *ddc) +{ + enum gpio_result result; + + if ((engine == NULL) || !is_engine_available(engine)) + return false; + + result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, + GPIO_DDC_CONFIG_TYPE_MODE_AUX); + + if (result != GPIO_RESULT_OK) + return false; + + if (!acquire_engine(engine)) { + engine->ddc = ddc; + release_engine(engine); + return false; + } + + engine->ddc = ddc; + + return true; +} + +void dce110_engine_destroy(struct dce_aux **engine) +{ + + struct aux_engine_dce110 *engine110 = FROM_AUX_ENGINE(*engine); + + kfree(engine110); + *engine = NULL; + +} + +static uint32_t dce_aux_configure_timeout(struct ddc_service *ddc, + uint32_t timeout_in_us) +{ + uint32_t multiplier = 0; + uint32_t length = 0; + uint32_t prev_length = 0; + uint32_t prev_mult = 0; + uint32_t prev_timeout_val = 0; + struct ddc *ddc_pin = ddc->ddc_pin; + struct dce_aux *aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; + struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(aux_engine); + + /* 1-Update polling timeout period */ + aux110->polling_timeout_period = timeout_in_us * SW_AUX_TIMEOUT_PERIOD_MULTIPLIER; + + /* 2-Update aux timeout period length and multiplier */ + if (timeout_in_us == 0) { + multiplier = DEFAULT_AUX_ENGINE_MULT; + length = DEFAULT_AUX_ENGINE_LENGTH; + } else if (timeout_in_us <= TIME_OUT_INCREMENT) { + multiplier = 0; + length = timeout_in_us/TIME_OUT_MULTIPLIER_8; + if (timeout_in_us % TIME_OUT_MULTIPLIER_8 != 0) + length++; + } else if (timeout_in_us <= 2 * TIME_OUT_INCREMENT) { + multiplier = 1; + length = timeout_in_us/TIME_OUT_MULTIPLIER_16; + if (timeout_in_us % TIME_OUT_MULTIPLIER_16 != 0) + length++; + } else if (timeout_in_us <= 4 * TIME_OUT_INCREMENT) { + multiplier = 2; + length = timeout_in_us/TIME_OUT_MULTIPLIER_32; + if (timeout_in_us % TIME_OUT_MULTIPLIER_32 != 0) + length++; + } else if (timeout_in_us > 4 * TIME_OUT_INCREMENT) { + multiplier = 3; + length = timeout_in_us/TIME_OUT_MULTIPLIER_64; + if (timeout_in_us % TIME_OUT_MULTIPLIER_64 != 0) + length++; + } + + length = (length < MAX_TIMEOUT_LENGTH) ? length : MAX_TIMEOUT_LENGTH; + + REG_GET_2(AUX_DPHY_RX_CONTROL1, AUX_RX_TIMEOUT_LEN, &prev_length, AUX_RX_TIMEOUT_LEN_MUL, &prev_mult); + + switch (prev_mult) { + case 0: + prev_timeout_val = prev_length * TIME_OUT_MULTIPLIER_8; + break; + case 1: + prev_timeout_val = prev_length * TIME_OUT_MULTIPLIER_16; + break; + case 2: + prev_timeout_val = prev_length * TIME_OUT_MULTIPLIER_32; + break; + case 3: + prev_timeout_val = prev_length * TIME_OUT_MULTIPLIER_64; + break; + default: + prev_timeout_val = DEFAULT_AUX_ENGINE_LENGTH * TIME_OUT_MULTIPLIER_8; + break; + } + + REG_UPDATE_SEQ_2(AUX_DPHY_RX_CONTROL1, AUX_RX_TIMEOUT_LEN, length, AUX_RX_TIMEOUT_LEN_MUL, multiplier); + + return prev_timeout_val; +} + +static struct dce_aux_funcs aux_functions = { + .configure_timeout = NULL, + .destroy = NULL, +}; + +struct dce_aux *dce110_aux_engine_construct(struct aux_engine_dce110 *aux_engine110, + struct dc_context *ctx, + uint32_t inst, + uint32_t timeout_period, + const struct dce110_aux_registers *regs, + const struct dce110_aux_registers_mask *mask, + const struct dce110_aux_registers_shift *shift, + bool is_ext_aux_timeout_configurable) +{ + aux_engine110->base.ddc = NULL; + aux_engine110->base.ctx = ctx; + aux_engine110->base.delay = 0; + aux_engine110->base.max_defer_write_retry = 0; + aux_engine110->base.inst = inst; + aux_engine110->polling_timeout_period = timeout_period; + aux_engine110->regs = regs; + + aux_engine110->mask = mask; + aux_engine110->shift = shift; + aux_engine110->base.funcs = &aux_functions; + if (is_ext_aux_timeout_configurable) + aux_engine110->base.funcs->configure_timeout = &dce_aux_configure_timeout; + + return &aux_engine110->base; +} + +static enum i2caux_transaction_action i2caux_action_from_payload(struct aux_payload *payload) +{ + if (payload->i2c_over_aux) { + if (payload->write_status_update) { + if (payload->mot) + return I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT; + else + return I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST; + } + if (payload->write) { + if (payload->mot) + return I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT; + else + return I2CAUX_TRANSACTION_ACTION_I2C_WRITE; + } + if (payload->mot) + return I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT; + + return I2CAUX_TRANSACTION_ACTION_I2C_READ; + } + if (payload->write) + return I2CAUX_TRANSACTION_ACTION_DP_WRITE; + + return I2CAUX_TRANSACTION_ACTION_DP_READ; +} + +int dce_aux_transfer_raw(struct ddc_service *ddc, + struct aux_payload *payload, + enum aux_return_code_type *operation_result) +{ + struct ddc *ddc_pin = ddc->ddc_pin; + struct dce_aux *aux_engine; + struct aux_request_transaction_data aux_req; + uint8_t returned_bytes = 0; + int res = -1; + uint32_t status; + + memset(&aux_req, 0, sizeof(aux_req)); + + if (ddc_pin == NULL) { + *operation_result = AUX_RET_ERROR_ENGINE_ACQUIRE; + return -1; + } + + aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; + if (!acquire(aux_engine, ddc_pin)) { + *operation_result = AUX_RET_ERROR_ENGINE_ACQUIRE; + return -1; + } + + if (payload->i2c_over_aux) + aux_req.type = AUX_TRANSACTION_TYPE_I2C; + else + aux_req.type = AUX_TRANSACTION_TYPE_DP; + + aux_req.action = i2caux_action_from_payload(payload); + + aux_req.address = payload->address; + aux_req.delay = 0; + aux_req.length = payload->length; + aux_req.data = payload->data; + + submit_channel_request(aux_engine, &aux_req); + *operation_result = get_channel_status(aux_engine, &returned_bytes); + + if (*operation_result == AUX_RET_SUCCESS) { + int __maybe_unused bytes_replied = 0; + + bytes_replied = read_channel_reply(aux_engine, payload->length, + payload->data, payload->reply, + &status); + EVENT_LOG_AUX_REP(aux_engine->ddc->pin_data->en, + EVENT_LOG_AUX_ORIGIN_NATIVE, *payload->reply, + bytes_replied, payload->data); + res = returned_bytes; + } else { + res = -1; + } + + release_engine(aux_engine); + return res; +} + +int dce_aux_transfer_dmub_raw(struct ddc_service *ddc, + struct aux_payload *payload, + enum aux_return_code_type *operation_result) +{ + struct ddc *ddc_pin = ddc->ddc_pin; + + if (ddc_pin != NULL) { + struct dce_aux *aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; + /* XXX: Workaround to configure ddc channels for aux transactions */ + if (!acquire(aux_engine, ddc_pin)) { + *operation_result = AUX_RET_ERROR_ENGINE_ACQUIRE; + return -1; + } + release_engine(aux_engine); + } + + return dm_helper_dmub_aux_transfer_sync(ddc->ctx, ddc->link, payload, operation_result); +} + +#define AUX_MAX_RETRIES 7 +#define AUX_MIN_DEFER_RETRIES 7 +#define AUX_MAX_DEFER_TIMEOUT_MS 50 +#define AUX_MAX_I2C_DEFER_RETRIES 7 +#define AUX_MAX_INVALID_REPLY_RETRIES 2 +#define AUX_MAX_TIMEOUT_RETRIES 3 +#define AUX_DEFER_DELAY_FOR_DPIA 4 /*ms*/ + +static void dce_aux_log_payload(const char *payload_name, + unsigned char *payload, uint32_t length, uint32_t max_length_to_log) +{ + if (!IS_DC_I2CAUX_LOGGING_ENABLED()) + return; + + if (payload && length) { + char hex_str[128] = {0}; + char *hex_str_ptr = &hex_str[0]; + uint32_t hex_str_remaining = sizeof(hex_str); + unsigned char *payload_ptr = payload; + unsigned char *payload_max_to_log_ptr = payload_ptr + min(max_length_to_log, length); + unsigned int count; + char *padding = ""; + + while (payload_ptr < payload_max_to_log_ptr) { + count = snprintf_count(hex_str_ptr, hex_str_remaining, "%s%02X", padding, *payload_ptr); + padding = " "; + hex_str_remaining -= count; + hex_str_ptr += count; + payload_ptr++; + } + + count = snprintf_count(hex_str_ptr, hex_str_remaining, " "); + hex_str_remaining -= count; + hex_str_ptr += count; + + payload_ptr = payload; + while (payload_ptr < payload_max_to_log_ptr) { + count = snprintf_count(hex_str_ptr, hex_str_remaining, "%c", + *payload_ptr >= ' ' ? *payload_ptr : '.'); + hex_str_remaining -= count; + hex_str_ptr += count; + payload_ptr++; + } + + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_log_payload: %s: length=%u: data: %s%s", + payload_name, + length, + hex_str, + (length > max_length_to_log ? " (...)" : " ")); + } else { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_log_payload: %s: length=%u: data: <empty payload>", + payload_name, + length); + } +} + +bool dce_aux_transfer_with_retries(struct ddc_service *ddc, + struct aux_payload *payload) +{ + int i, ret = 0; + uint8_t reply; + bool payload_reply = true; + enum aux_return_code_type operation_result; + bool retry_on_defer = false; + struct ddc *ddc_pin = ddc->ddc_pin; + struct dce_aux *aux_engine = NULL; + struct aux_engine_dce110 *aux110 = NULL; + uint32_t defer_time_in_ms = 0; + + int aux_ack_retries = 0, + aux_defer_retries = 0, + aux_i2c_defer_retries = 0, + aux_timeout_retries = 0, + aux_invalid_reply_retries = 0, + aux_ack_m_retries = 0; + + if (ddc_pin) { + aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; + aux110 = FROM_AUX_ENGINE(aux_engine); + } + + if (!payload->reply) { + payload_reply = false; + payload->reply = &reply; + } + + for (i = 0; i < AUX_MAX_RETRIES; i++) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: link_index=%u: START: retry %d of %d: address=0x%04x length=%u write=%d mot=%d", + ddc && ddc->link ? ddc->link->link_index : UINT_MAX, + i + 1, + (int)AUX_MAX_RETRIES, + payload->address, + payload->length, + (unsigned int) payload->write, + (unsigned int) payload->mot); + if (payload->write) + dce_aux_log_payload(" write", payload->data, payload->length, 16); + ret = dce_aux_transfer_raw(ddc, payload, &operation_result); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: link_index=%u: END: retry %d of %d: address=0x%04x length=%u write=%d mot=%d: ret=%d operation_result=%d payload->reply=%u", + ddc && ddc->link ? ddc->link->link_index : UINT_MAX, + i + 1, + (int)AUX_MAX_RETRIES, + payload->address, + payload->length, + (unsigned int) payload->write, + (unsigned int) payload->mot, + ret, + (int)operation_result, + (unsigned int) *payload->reply); + if (!payload->write) + dce_aux_log_payload(" read", payload->data, ret > 0 ? ret : 0, 16); + + switch (operation_result) { + case AUX_RET_SUCCESS: + aux_timeout_retries = 0; + aux_invalid_reply_retries = 0; + + switch (*payload->reply) { + case AUX_TRANSACTION_REPLY_AUX_ACK: + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: AUX_RET_SUCCESS: AUX_TRANSACTION_REPLY_AUX_ACK"); + if (!payload->write && payload->length != ret) { + if (++aux_ack_retries >= AUX_MAX_RETRIES) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + LOG_FLAG_Error_I2cAux, + "dce_aux_transfer_with_retries: FAILURE: aux_ack_retries=%d >= AUX_MAX_RETRIES=%d", + aux_defer_retries, + AUX_MAX_RETRIES); + goto fail; + } else + udelay(300); + } else if (payload->write && ret > 0) { + /* sink requested more time to complete the write via AUX_ACKM */ + if (++aux_ack_m_retries >= AUX_MAX_RETRIES) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + LOG_FLAG_Error_I2cAux, + "dce_aux_transfer_with_retries: FAILURE: aux_ack_m_retries=%d >= AUX_MAX_RETRIES=%d", + aux_ack_m_retries, + AUX_MAX_RETRIES); + goto fail; + } + + /* retry reading the write status until complete + * NOTE: payload is modified here + */ + payload->write = false; + payload->write_status_update = true; + payload->length = 0; + udelay(300); + + } else + return true; + break; + + case AUX_TRANSACTION_REPLY_AUX_DEFER: + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: AUX_RET_SUCCESS: AUX_TRANSACTION_REPLY_AUX_DEFER"); + + /* polling_timeout_period is in us */ + if (aux110) + defer_time_in_ms += aux110->polling_timeout_period / 1000; + else + defer_time_in_ms += AUX_DEFER_DELAY_FOR_DPIA; + ++aux_defer_retries; + fallthrough; + case AUX_TRANSACTION_REPLY_I2C_OVER_AUX_DEFER: + if (*payload->reply == AUX_TRANSACTION_REPLY_I2C_OVER_AUX_DEFER) + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: AUX_RET_SUCCESS: AUX_TRANSACTION_REPLY_I2C_OVER_AUX_DEFER"); + + retry_on_defer = true; + + if (aux_defer_retries >= AUX_MIN_DEFER_RETRIES + && defer_time_in_ms >= AUX_MAX_DEFER_TIMEOUT_MS) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + LOG_FLAG_Error_I2cAux, + "dce_aux_transfer_with_retries: FAILURE: aux_defer_retries=%d >= AUX_MIN_DEFER_RETRIES=%d && defer_time_in_ms=%d >= AUX_MAX_DEFER_TIMEOUT_MS=%d", + aux_defer_retries, + AUX_MIN_DEFER_RETRIES, + defer_time_in_ms, + AUX_MAX_DEFER_TIMEOUT_MS); + goto fail; + } else { + if ((*payload->reply == AUX_TRANSACTION_REPLY_AUX_DEFER) || + (*payload->reply == AUX_TRANSACTION_REPLY_I2C_OVER_AUX_DEFER)) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: payload->defer_delay=%u", + payload->defer_delay); + if (payload->defer_delay > 1) { + msleep(payload->defer_delay); + defer_time_in_ms += payload->defer_delay; + } else if (payload->defer_delay <= 1) { + udelay(payload->defer_delay * 1000); + defer_time_in_ms += payload->defer_delay; + } + } + } + break; + case AUX_TRANSACTION_REPLY_I2C_OVER_AUX_NACK: + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: FAILURE: AUX_TRANSACTION_REPLY_I2C_OVER_AUX_NACK"); + goto fail; + case AUX_TRANSACTION_REPLY_I2C_DEFER: + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: AUX_RET_SUCCESS: AUX_TRANSACTION_REPLY_I2C_DEFER"); + + aux_defer_retries = 0; + if (++aux_i2c_defer_retries >= AUX_MAX_I2C_DEFER_RETRIES) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + LOG_FLAG_Error_I2cAux, + "dce_aux_transfer_with_retries: FAILURE: aux_i2c_defer_retries=%d >= AUX_MAX_I2C_DEFER_RETRIES=%d", + aux_i2c_defer_retries, + AUX_MAX_I2C_DEFER_RETRIES); + goto fail; + } + break; + + case AUX_TRANSACTION_REPLY_AUX_NACK: + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: AUX_RET_SUCCESS: AUX_TRANSACTION_REPLY_AUX_NACK"); + goto fail; + + case AUX_TRANSACTION_REPLY_HPD_DISCON: + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: AUX_RET_SUCCESS: AUX_TRANSACTION_REPLY_HPD_DISCON"); + goto fail; + + default: + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + LOG_FLAG_Error_I2cAux, + "dce_aux_transfer_with_retries: AUX_RET_SUCCESS: FAILURE: AUX_TRANSACTION_REPLY_* unknown, default case. Reply: %d", *payload->reply); + goto fail; + } + break; + + case AUX_RET_ERROR_INVALID_REPLY: + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: AUX_RET_ERROR_INVALID_REPLY"); + if (++aux_invalid_reply_retries >= AUX_MAX_INVALID_REPLY_RETRIES) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + LOG_FLAG_Error_I2cAux, + "dce_aux_transfer_with_retries: FAILURE: aux_invalid_reply_retries=%d >= AUX_MAX_INVALID_REPLY_RETRIES=%d", + aux_invalid_reply_retries, + AUX_MAX_INVALID_REPLY_RETRIES); + goto fail; + } else + udelay(400); + break; + + case AUX_RET_ERROR_TIMEOUT: + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: AUX_RET_ERROR_TIMEOUT"); + // Check whether a DEFER had occurred before the timeout. + // If so, treat timeout as a DEFER. + if (retry_on_defer) { + if (++aux_defer_retries >= AUX_MIN_DEFER_RETRIES) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + LOG_FLAG_Error_I2cAux, + "dce_aux_transfer_with_retries: FAILURE: aux_defer_retries=%d >= AUX_MIN_DEFER_RETRIES=%d", + aux_defer_retries, + AUX_MIN_DEFER_RETRIES); + goto fail; + } else if (payload->defer_delay > 0) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + LOG_FLAG_I2cAux_DceAux, + "dce_aux_transfer_with_retries: payload->defer_delay=%u", + payload->defer_delay); + msleep(payload->defer_delay); + } + } else { + if (++aux_timeout_retries >= AUX_MAX_TIMEOUT_RETRIES) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + LOG_FLAG_Error_I2cAux, + "dce_aux_transfer_with_retries: FAILURE: aux_timeout_retries=%d >= AUX_MAX_TIMEOUT_RETRIES=%d", + aux_timeout_retries, + AUX_MAX_TIMEOUT_RETRIES); + goto fail; + } else { + /* + * DP 1.4, 2.8.2: AUX Transaction Response/Reply Timeouts + * According to the DP spec there should be 3 retries total + * with a 400us wait inbetween each. Hardware already waits + * for 550us therefore no wait is required here. + */ + } + } + break; + + case AUX_RET_ERROR_HPD_DISCON: + case AUX_RET_ERROR_ENGINE_ACQUIRE: + case AUX_RET_ERROR_UNKNOWN: + default: + goto fail; + } + } + +fail: + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + LOG_FLAG_Error_I2cAux, + "%s: Failure: operation_result=%d", + __func__, + (int)operation_result); + if (!payload_reply) + payload->reply = NULL; + + return false; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.h b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.h new file mode 100644 index 000000000..e69f1899f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.h @@ -0,0 +1,321 @@ +/* + * 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_AUX_ENGINE_DCE110_H__ +#define __DAL_AUX_ENGINE_DCE110_H__ + +#include "i2caux_interface.h" +#include "inc/hw/aux_engine.h" + +enum aux_return_code_type; + +#define AUX_COMMON_REG_LIST0(id)\ + SRI(AUX_CONTROL, DP_AUX, id), \ + SRI(AUX_ARB_CONTROL, DP_AUX, id), \ + SRI(AUX_SW_DATA, DP_AUX, id), \ + SRI(AUX_SW_CONTROL, DP_AUX, id), \ + SRI(AUX_INTERRUPT_CONTROL, DP_AUX, id), \ + SRI(AUX_DPHY_RX_CONTROL1, DP_AUX, id), \ + SRI(AUX_SW_STATUS, DP_AUX, id) + +#define AUX_COMMON_REG_LIST(id)\ + SRI(AUX_CONTROL, DP_AUX, id), \ + SRI(AUX_ARB_CONTROL, DP_AUX, id), \ + SRI(AUX_SW_DATA, DP_AUX, id), \ + SRI(AUX_SW_CONTROL, DP_AUX, id), \ + SRI(AUX_INTERRUPT_CONTROL, DP_AUX, id), \ + SRI(AUX_SW_STATUS, DP_AUX, id), \ + SR(AUXN_IMPCAL), \ + SR(AUXP_IMPCAL) + +struct dce110_aux_registers { + uint32_t AUX_CONTROL; + uint32_t AUX_ARB_CONTROL; + uint32_t AUX_SW_DATA; + uint32_t AUX_SW_CONTROL; + uint32_t AUX_INTERRUPT_CONTROL; + uint32_t AUX_DPHY_RX_CONTROL1; + uint32_t AUX_SW_STATUS; + uint32_t AUXN_IMPCAL; + uint32_t AUXP_IMPCAL; + + uint32_t AUX_RESET_MASK; +}; + +#define DCE_AUX_REG_FIELD_LIST(type)\ + type AUX_EN;\ + type AUX_RESET;\ + type AUX_RESET_DONE;\ + type AUX_REG_RW_CNTL_STATUS;\ + type AUX_SW_USE_AUX_REG_REQ;\ + type AUX_SW_DONE_USING_AUX_REG;\ + type AUX_SW_AUTOINCREMENT_DISABLE;\ + type AUX_SW_DATA_RW;\ + type AUX_SW_INDEX;\ + type AUX_SW_GO;\ + type AUX_SW_DATA;\ + type AUX_SW_REPLY_BYTE_COUNT;\ + type AUX_SW_DONE;\ + type AUX_SW_DONE_ACK;\ + type AUXN_IMPCAL_ENABLE;\ + type AUXP_IMPCAL_ENABLE;\ + type AUXN_IMPCAL_OVERRIDE_ENABLE;\ + type AUXP_IMPCAL_OVERRIDE_ENABLE;\ + type AUX_RX_TIMEOUT_LEN;\ + type AUX_RX_TIMEOUT_LEN_MUL;\ + type AUXN_CALOUT_ERROR_AK;\ + type AUXP_CALOUT_ERROR_AK;\ + type AUX_SW_START_DELAY;\ + type AUX_SW_WR_BYTES + +#define DCE10_AUX_MASK_SH_LIST(mask_sh)\ + AUX_SF(AUX_CONTROL, AUX_EN, mask_sh),\ + AUX_SF(AUX_ARB_CONTROL, AUX_REG_RW_CNTL_STATUS, mask_sh),\ + AUX_SF(AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, mask_sh),\ + AUX_SF(AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, mask_sh),\ + AUX_SF(AUX_SW_CONTROL, AUX_SW_START_DELAY, mask_sh),\ + AUX_SF(AUX_SW_CONTROL, AUX_SW_WR_BYTES, mask_sh),\ + AUX_SF(AUX_SW_CONTROL, AUX_SW_GO, mask_sh),\ + AUX_SF(AUX_SW_DATA, AUX_SW_AUTOINCREMENT_DISABLE, mask_sh),\ + AUX_SF(AUX_SW_DATA, AUX_SW_DATA_RW, mask_sh),\ + AUX_SF(AUX_SW_DATA, AUX_SW_INDEX, mask_sh),\ + AUX_SF(AUX_SW_DATA, AUX_SW_DATA, mask_sh),\ + AUX_SF(AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT, mask_sh),\ + AUX_SF(AUX_SW_STATUS, AUX_SW_DONE, mask_sh),\ + AUX_SF(AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_CALOUT_ERROR_AK, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_CALOUT_ERROR_AK, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_IMPCAL_ENABLE, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_IMPCAL_ENABLE, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_IMPCAL_OVERRIDE_ENABLE, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_IMPCAL_OVERRIDE_ENABLE, mask_sh) + +#define DCE_AUX_MASK_SH_LIST(mask_sh)\ + AUX_SF(AUX_CONTROL, AUX_EN, mask_sh),\ + AUX_SF(AUX_CONTROL, AUX_RESET, mask_sh),\ + AUX_SF(AUX_CONTROL, AUX_RESET_DONE, mask_sh),\ + AUX_SF(AUX_ARB_CONTROL, AUX_REG_RW_CNTL_STATUS, mask_sh),\ + AUX_SF(AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, mask_sh),\ + AUX_SF(AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, mask_sh),\ + AUX_SF(AUX_SW_CONTROL, AUX_SW_START_DELAY, mask_sh),\ + AUX_SF(AUX_SW_CONTROL, AUX_SW_WR_BYTES, mask_sh),\ + AUX_SF(AUX_SW_CONTROL, AUX_SW_GO, mask_sh),\ + AUX_SF(AUX_SW_DATA, AUX_SW_AUTOINCREMENT_DISABLE, mask_sh),\ + AUX_SF(AUX_SW_DATA, AUX_SW_DATA_RW, mask_sh),\ + AUX_SF(AUX_SW_DATA, AUX_SW_INDEX, mask_sh),\ + AUX_SF(AUX_SW_DATA, AUX_SW_DATA, mask_sh),\ + AUX_SF(AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT, mask_sh),\ + AUX_SF(AUX_SW_STATUS, AUX_SW_DONE, mask_sh),\ + AUX_SF(AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_CALOUT_ERROR_AK, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_CALOUT_ERROR_AK, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_IMPCAL_ENABLE, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_IMPCAL_ENABLE, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_IMPCAL_OVERRIDE_ENABLE, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_IMPCAL_OVERRIDE_ENABLE, mask_sh) + +#define DCE12_AUX_MASK_SH_LIST(mask_sh)\ + AUX_SF(DP_AUX0_AUX_CONTROL, AUX_EN, mask_sh),\ + AUX_SF(DP_AUX0_AUX_CONTROL, AUX_RESET, mask_sh),\ + AUX_SF(DP_AUX0_AUX_CONTROL, AUX_RESET_DONE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_ARB_CONTROL, AUX_REG_RW_CNTL_STATUS, mask_sh),\ + AUX_SF(DP_AUX0_AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, mask_sh),\ + AUX_SF(DP_AUX0_AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_CONTROL, AUX_SW_START_DELAY, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_CONTROL, AUX_SW_WR_BYTES, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_CONTROL, AUX_SW_GO, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_AUTOINCREMENT_DISABLE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_DATA_RW, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_AUTOINCREMENT_DISABLE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_INDEX, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_DATA, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_STATUS, AUX_SW_DONE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_CALOUT_ERROR_AK, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_CALOUT_ERROR_AK, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_IMPCAL_ENABLE, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_IMPCAL_ENABLE, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_IMPCAL_OVERRIDE_ENABLE, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_IMPCAL_OVERRIDE_ENABLE, mask_sh) + +/* DCN10 MASK */ +#define DCN10_AUX_MASK_SH_LIST(mask_sh)\ + AUX_SF(DP_AUX0_AUX_CONTROL, AUX_EN, mask_sh),\ + AUX_SF(DP_AUX0_AUX_CONTROL, AUX_RESET, mask_sh),\ + AUX_SF(DP_AUX0_AUX_CONTROL, AUX_RESET_DONE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_ARB_CONTROL, AUX_REG_RW_CNTL_STATUS, mask_sh),\ + AUX_SF(DP_AUX0_AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, mask_sh),\ + AUX_SF(DP_AUX0_AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_CONTROL, AUX_SW_START_DELAY, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_CONTROL, AUX_SW_WR_BYTES, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_CONTROL, AUX_SW_GO, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_AUTOINCREMENT_DISABLE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_DATA_RW, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_AUTOINCREMENT_DISABLE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_INDEX, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_DATA, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_STATUS, AUX_SW_DONE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_CALOUT_ERROR_AK, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_CALOUT_ERROR_AK, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_IMPCAL_ENABLE, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_IMPCAL_ENABLE, mask_sh),\ + AUX_SF(AUXP_IMPCAL, AUXP_IMPCAL_OVERRIDE_ENABLE, mask_sh),\ + AUX_SF(AUXN_IMPCAL, AUXN_IMPCAL_OVERRIDE_ENABLE, mask_sh) + +/* for all other DCN */ +#define DCN_AUX_MASK_SH_LIST(mask_sh)\ + AUX_SF(DP_AUX0_AUX_CONTROL, AUX_EN, mask_sh),\ + AUX_SF(DP_AUX0_AUX_CONTROL, AUX_RESET, mask_sh),\ + AUX_SF(DP_AUX0_AUX_CONTROL, AUX_RESET_DONE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_ARB_CONTROL, AUX_REG_RW_CNTL_STATUS, mask_sh),\ + AUX_SF(DP_AUX0_AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, mask_sh),\ + AUX_SF(DP_AUX0_AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_CONTROL, AUX_SW_START_DELAY, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_CONTROL, AUX_SW_WR_BYTES, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_CONTROL, AUX_SW_GO, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_AUTOINCREMENT_DISABLE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_DATA_RW, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_AUTOINCREMENT_DISABLE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_INDEX, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_DATA, AUX_SW_DATA, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT, mask_sh),\ + AUX_SF(DP_AUX0_AUX_SW_STATUS, AUX_SW_DONE, mask_sh),\ + AUX_SF(DP_AUX0_AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, mask_sh),\ + AUX_SF(DP_AUX0_AUX_DPHY_RX_CONTROL1, AUX_RX_TIMEOUT_LEN, mask_sh),\ + AUX_SF(DP_AUX0_AUX_DPHY_RX_CONTROL1, AUX_RX_TIMEOUT_LEN_MUL, mask_sh) + +#define AUX_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +enum { /* This is the timeout as defined in DP 1.2a, + * 2.3.4 "Detailed uPacket TX AUX CH State Description". + */ + AUX_TIMEOUT_PERIOD = 400, + + /* Ideally, the SW timeout should be just above 550usec + * which is programmed in HW. + * But the SW timeout of 600usec is not reliable, + * because on some systems, delay_in_microseconds() + * returns faster than it should. + * EPR #379763: by trial-and-error on different systems, + * 700usec is the minimum reliable SW timeout for polling + * the AUX_SW_STATUS.AUX_SW_DONE bit. + * This timeout expires *only* when there is + * AUX Error or AUX Timeout conditions - not during normal operation. + * During normal operation, AUX_SW_STATUS.AUX_SW_DONE bit is set + * at most within ~240usec. That means, + * increasing this timeout will not affect normal operation, + * and we'll timeout after + * SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD = 2400usec. + * This timeout is especially important for + * converters, resume from S3, and CTS. + */ + SW_AUX_TIMEOUT_PERIOD_MULTIPLIER = 6 +}; + +struct dce_aux { + uint32_t inst; + struct ddc *ddc; + struct dc_context *ctx; + /* following values are expressed in milliseconds */ + uint32_t delay; + uint32_t max_defer_write_retry; + + bool acquire_reset; + struct dce_aux_funcs *funcs; +}; + +struct dce110_aux_registers_mask { + DCE_AUX_REG_FIELD_LIST(uint32_t); +}; + +struct dce110_aux_registers_shift { + DCE_AUX_REG_FIELD_LIST(uint8_t); +}; + + +struct aux_engine_dce110 { + struct dce_aux base; + const struct dce110_aux_registers *regs; + const struct dce110_aux_registers_mask *mask; + const struct dce110_aux_registers_shift *shift; + struct { + uint32_t aux_control; + uint32_t aux_arb_control; + uint32_t aux_sw_data; + uint32_t aux_sw_control; + uint32_t aux_interrupt_control; + uint32_t aux_dphy_rx_control1; + uint32_t aux_dphy_rx_control0; + uint32_t aux_sw_status; + } addr; + uint32_t polling_timeout_period; +}; + +struct aux_engine_dce110_init_data { + uint32_t engine_id; + uint32_t timeout_period; + struct dc_context *ctx; + const struct dce110_aux_registers *regs; +}; + +struct dce_aux *dce110_aux_engine_construct(struct aux_engine_dce110 *aux_engine110, + struct dc_context *ctx, + uint32_t inst, + uint32_t timeout_period, + const struct dce110_aux_registers *regs, + + const struct dce110_aux_registers_mask *mask, + const struct dce110_aux_registers_shift *shift, + bool is_ext_aux_timeout_configurable); + +void dce110_engine_destroy(struct dce_aux **engine); + +bool dce110_aux_engine_acquire( + struct dce_aux *aux_engine, + struct ddc *ddc); + +int dce_aux_transfer_raw(struct ddc_service *ddc, + struct aux_payload *cmd, + enum aux_return_code_type *operation_result); + +int dce_aux_transfer_dmub_raw(struct ddc_service *ddc, + struct aux_payload *payload, + enum aux_return_code_type *operation_result); +bool dce_aux_transfer_with_retries(struct ddc_service *ddc, + struct aux_payload *cmd); + +struct dce_aux_funcs { + uint32_t (*configure_timeout) + (struct ddc_service *ddc, + uint32_t timeout); + void (*destroy) + (struct aux_engine **ptr); +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clk_mgr.c new file mode 100644 index 000000000..07359eb89 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clk_mgr.c @@ -0,0 +1,967 @@ +/* + * 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 <linux/slab.h> + +#include "dce_clk_mgr.h" + +#include "reg_helper.h" +#include "dmcu.h" +#include "core_types.h" +#include "dal_asic_id.h" + +#define TO_DCE_CLK_MGR(clocks)\ + container_of(clocks, struct dce_clk_mgr, base) + +#define REG(reg) \ + (clk_mgr_dce->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + clk_mgr_dce->clk_mgr_shift->field_name, clk_mgr_dce->clk_mgr_mask->field_name + +#define CTX \ + clk_mgr_dce->base.ctx +#define DC_LOGGER \ + clk_mgr->ctx->logger + +/* Max clock values for each state indexed by "enum clocks_state": */ +static const struct state_dependent_clocks dce80_max_clks_by_state[] = { +/* ClocksStateInvalid - should not be used */ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/* ClocksStateUltraLow - not expected to be used for DCE 8.0 */ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/* ClocksStateLow */ +{ .display_clk_khz = 352000, .pixel_clk_khz = 330000}, +/* ClocksStateNominal */ +{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 }, +/* ClocksStatePerformance */ +{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 } }; + +static const struct state_dependent_clocks dce110_max_clks_by_state[] = { +/*ClocksStateInvalid - should not be used*/ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ +{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 }, +/*ClocksStateLow*/ +{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 }, +/*ClocksStateNominal*/ +{ .display_clk_khz = 467000, .pixel_clk_khz = 400000 }, +/*ClocksStatePerformance*/ +{ .display_clk_khz = 643000, .pixel_clk_khz = 400000 } }; + +static const struct state_dependent_clocks dce112_max_clks_by_state[] = { +/*ClocksStateInvalid - should not be used*/ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ +{ .display_clk_khz = 389189, .pixel_clk_khz = 346672 }, +/*ClocksStateLow*/ +{ .display_clk_khz = 459000, .pixel_clk_khz = 400000 }, +/*ClocksStateNominal*/ +{ .display_clk_khz = 667000, .pixel_clk_khz = 600000 }, +/*ClocksStatePerformance*/ +{ .display_clk_khz = 1132000, .pixel_clk_khz = 600000 } }; + +static const struct state_dependent_clocks dce120_max_clks_by_state[] = { +/*ClocksStateInvalid - should not be used*/ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ +{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, +/*ClocksStateLow*/ +{ .display_clk_khz = 460000, .pixel_clk_khz = 400000 }, +/*ClocksStateNominal*/ +{ .display_clk_khz = 670000, .pixel_clk_khz = 600000 }, +/*ClocksStatePerformance*/ +{ .display_clk_khz = 1133000, .pixel_clk_khz = 600000 } }; + +int dentist_get_divider_from_did(int did) +{ + if (did < DENTIST_BASE_DID_1) + did = DENTIST_BASE_DID_1; + if (did > DENTIST_MAX_DID) + did = DENTIST_MAX_DID; + + if (did < DENTIST_BASE_DID_2) { + return DENTIST_DIVIDER_RANGE_1_START + DENTIST_DIVIDER_RANGE_1_STEP + * (did - DENTIST_BASE_DID_1); + } else if (did < DENTIST_BASE_DID_3) { + return DENTIST_DIVIDER_RANGE_2_START + DENTIST_DIVIDER_RANGE_2_STEP + * (did - DENTIST_BASE_DID_2); + } else if (did < DENTIST_BASE_DID_4) { + return DENTIST_DIVIDER_RANGE_3_START + DENTIST_DIVIDER_RANGE_3_STEP + * (did - DENTIST_BASE_DID_3); + } else { + return DENTIST_DIVIDER_RANGE_4_START + DENTIST_DIVIDER_RANGE_4_STEP + * (did - DENTIST_BASE_DID_4); + } +} + +/* SW will adjust DP REF Clock average value for all purposes + * (DP DTO / DP Audio DTO and DP GTC) + if clock is spread for all cases: + -if SS enabled on DP Ref clock and HW de-spreading enabled with SW + calculations for DS_INCR/DS_MODULO (this is planned to be default case) + -if SS enabled on DP Ref clock and HW de-spreading enabled with HW + calculations (not planned to be used, but average clock should still + be valid) + -if SS enabled on DP Ref clock and HW de-spreading disabled + (should not be case with CIK) then SW should program all rates + generated according to average value (case as with previous ASICs) + */ +static int clk_mgr_adjust_dp_ref_freq_for_ss(struct dce_clk_mgr *clk_mgr_dce, int dp_ref_clk_khz) +{ + if (clk_mgr_dce->ss_on_dprefclk && clk_mgr_dce->dprefclk_ss_divider != 0) { + struct fixed31_32 ss_percentage = dc_fixpt_div_int( + dc_fixpt_from_fraction(clk_mgr_dce->dprefclk_ss_percentage, + clk_mgr_dce->dprefclk_ss_divider), 200); + struct fixed31_32 adj_dp_ref_clk_khz; + + ss_percentage = dc_fixpt_sub(dc_fixpt_one, ss_percentage); + adj_dp_ref_clk_khz = dc_fixpt_mul_int(ss_percentage, dp_ref_clk_khz); + dp_ref_clk_khz = dc_fixpt_floor(adj_dp_ref_clk_khz); + } + return dp_ref_clk_khz; +} + +static int dce_get_dp_ref_freq_khz(struct clk_mgr *clk_mgr) +{ + struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); + int dprefclk_wdivider; + int dprefclk_src_sel; + int dp_ref_clk_khz = 600000; + int target_div; + + /* ASSERT DP Reference Clock source is from DFS*/ + REG_GET(DPREFCLK_CNTL, DPREFCLK_SRC_SEL, &dprefclk_src_sel); + ASSERT(dprefclk_src_sel == 0); + + /* Read the mmDENTIST_DISPCLK_CNTL to get the currently + * programmed DID DENTIST_DPREFCLK_WDIVIDER*/ + REG_GET(DENTIST_DISPCLK_CNTL, DENTIST_DPREFCLK_WDIVIDER, &dprefclk_wdivider); + + /* Convert DENTIST_DPREFCLK_WDIVIDERto actual divider*/ + target_div = dentist_get_divider_from_did(dprefclk_wdivider); + + /* Calculate the current DFS clock, in kHz.*/ + dp_ref_clk_khz = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR + * clk_mgr_dce->dentist_vco_freq_khz) / target_div; + + return clk_mgr_adjust_dp_ref_freq_for_ss(clk_mgr_dce, dp_ref_clk_khz); +} + +int dce12_get_dp_ref_freq_khz(struct clk_mgr *clk_mgr) +{ + struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); + + return clk_mgr_adjust_dp_ref_freq_for_ss(clk_mgr_dce, clk_mgr_dce->dprefclk_khz); +} + +/* unit: in_khz before mode set, get pixel clock from context. ASIC register + * may not be programmed yet + */ +static uint32_t get_max_pixel_clock_for_all_paths(struct dc_state *context) +{ + uint32_t max_pix_clk = 0; + int i; + + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + /* do not check under lay */ + if (pipe_ctx->top_pipe) + continue; + + if (pipe_ctx->stream_res.pix_clk_params.requested_pix_clk_100hz / 10 > max_pix_clk) + max_pix_clk = pipe_ctx->stream_res.pix_clk_params.requested_pix_clk_100hz / 10; + + /* raise clock state for HBR3/2 if required. Confirmed with HW DCE/DPCS + * logic for HBR3 still needs Nominal (0.8V) on VDDC rail + */ + if (dc_is_dp_signal(pipe_ctx->stream->signal) && + pipe_ctx->stream_res.pix_clk_params.requested_sym_clk > max_pix_clk) + max_pix_clk = pipe_ctx->stream_res.pix_clk_params.requested_sym_clk; + } + + return max_pix_clk; +} + +static enum dm_pp_clocks_state dce_get_required_clocks_state( + struct clk_mgr *clk_mgr, + struct dc_state *context) +{ + struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); + int i; + enum dm_pp_clocks_state low_req_clk; + int max_pix_clk = get_max_pixel_clock_for_all_paths(context); + + /* Iterate from highest supported to lowest valid state, and update + * lowest RequiredState with the lowest state that satisfies + * all required clocks + */ + for (i = clk_mgr_dce->max_clks_state; i >= DM_PP_CLOCKS_STATE_ULTRA_LOW; i--) + if (context->bw_ctx.bw.dce.dispclk_khz > + clk_mgr_dce->max_clks_by_state[i].display_clk_khz + || max_pix_clk > + clk_mgr_dce->max_clks_by_state[i].pixel_clk_khz) + break; + + low_req_clk = i + 1; + if (low_req_clk > clk_mgr_dce->max_clks_state) { + /* set max clock state for high phyclock, invalid on exceeding display clock */ + if (clk_mgr_dce->max_clks_by_state[clk_mgr_dce->max_clks_state].display_clk_khz + < context->bw_ctx.bw.dce.dispclk_khz) + low_req_clk = DM_PP_CLOCKS_STATE_INVALID; + else + low_req_clk = clk_mgr_dce->max_clks_state; + } + + return low_req_clk; +} + +static int dce_set_clock( + struct clk_mgr *clk_mgr, + int requested_clk_khz) +{ + struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); + struct bp_pixel_clock_parameters pxl_clk_params = { 0 }; + struct dc_bios *bp = clk_mgr->ctx->dc_bios; + int actual_clock = requested_clk_khz; + struct dmcu *dmcu = clk_mgr_dce->base.ctx->dc->res_pool->dmcu; + + /* Make sure requested clock isn't lower than minimum threshold*/ + if (requested_clk_khz > 0) + requested_clk_khz = max(requested_clk_khz, + clk_mgr_dce->dentist_vco_freq_khz / 64); + + /* Prepare to program display clock*/ + pxl_clk_params.target_pixel_clock_100hz = requested_clk_khz * 10; + pxl_clk_params.pll_id = CLOCK_SOURCE_ID_DFS; + + if (clk_mgr_dce->dfs_bypass_active) + pxl_clk_params.flags.SET_DISPCLK_DFS_BYPASS = true; + + bp->funcs->program_display_engine_pll(bp, &pxl_clk_params); + + if (clk_mgr_dce->dfs_bypass_active) { + /* Cache the fixed display clock*/ + clk_mgr_dce->dfs_bypass_disp_clk = + pxl_clk_params.dfs_bypass_display_clock; + actual_clock = pxl_clk_params.dfs_bypass_display_clock; + } + + /* from power down, we need mark the clock state as ClocksStateNominal + * from HWReset, so when resume we will call pplib voltage regulator.*/ + if (requested_clk_khz == 0) + clk_mgr_dce->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; + + if (dmcu && dmcu->funcs->is_dmcu_initialized(dmcu)) + dmcu->funcs->set_psr_wait_loop(dmcu, actual_clock / 1000 / 7); + + return actual_clock; +} + +int dce112_set_clock(struct clk_mgr *clk_mgr, int requested_clk_khz) +{ + struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); + struct bp_set_dce_clock_parameters dce_clk_params; + struct dc_bios *bp = clk_mgr->ctx->dc_bios; + struct dc *core_dc = clk_mgr->ctx->dc; + struct dmcu *dmcu = core_dc->res_pool->dmcu; + int actual_clock = requested_clk_khz; + /* Prepare to program display clock*/ + memset(&dce_clk_params, 0, sizeof(dce_clk_params)); + + /* Make sure requested clock isn't lower than minimum threshold*/ + if (requested_clk_khz > 0) + requested_clk_khz = max(requested_clk_khz, + clk_mgr_dce->dentist_vco_freq_khz / 62); + + dce_clk_params.target_clock_frequency = requested_clk_khz; + dce_clk_params.pll_id = CLOCK_SOURCE_ID_DFS; + dce_clk_params.clock_type = DCECLOCK_TYPE_DISPLAY_CLOCK; + + bp->funcs->set_dce_clock(bp, &dce_clk_params); + actual_clock = dce_clk_params.target_clock_frequency; + + /* from power down, we need mark the clock state as ClocksStateNominal + * from HWReset, so when resume we will call pplib voltage regulator.*/ + if (requested_clk_khz == 0) + clk_mgr_dce->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; + + /*Program DP ref Clock*/ + /*VBIOS will determine DPREFCLK frequency, so we don't set it*/ + dce_clk_params.target_clock_frequency = 0; + dce_clk_params.clock_type = DCECLOCK_TYPE_DPREFCLK; + + if (!((clk_mgr->ctx->asic_id.chip_family == FAMILY_AI) && + ASICREV_IS_VEGA20_P(clk_mgr->ctx->asic_id.hw_internal_rev))) + dce_clk_params.flags.USE_GENLOCK_AS_SOURCE_FOR_DPREFCLK = + (dce_clk_params.pll_id == + CLOCK_SOURCE_COMBO_DISPLAY_PLL0); + else + dce_clk_params.flags.USE_GENLOCK_AS_SOURCE_FOR_DPREFCLK = false; + + bp->funcs->set_dce_clock(bp, &dce_clk_params); + + if (!IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment)) { + if (dmcu && dmcu->funcs->is_dmcu_initialized(dmcu)) { + if (clk_mgr_dce->dfs_bypass_disp_clk != actual_clock) + dmcu->funcs->set_psr_wait_loop(dmcu, + actual_clock / 1000 / 7); + } + } + + clk_mgr_dce->dfs_bypass_disp_clk = actual_clock; + return actual_clock; +} + +static void dce_clock_read_integrated_info(struct dce_clk_mgr *clk_mgr_dce) +{ + struct dc_debug_options *debug = &clk_mgr_dce->base.ctx->dc->debug; + struct dc_bios *bp = clk_mgr_dce->base.ctx->dc_bios; + struct integrated_info info = { { { 0 } } }; + struct dc_firmware_info fw_info = { { 0 } }; + int i; + + if (bp->integrated_info) + info = *bp->integrated_info; + + clk_mgr_dce->dentist_vco_freq_khz = info.dentist_vco_freq; + if (clk_mgr_dce->dentist_vco_freq_khz == 0) { + bp->funcs->get_firmware_info(bp, &fw_info); + clk_mgr_dce->dentist_vco_freq_khz = + fw_info.smu_gpu_pll_output_freq; + if (clk_mgr_dce->dentist_vco_freq_khz == 0) + clk_mgr_dce->dentist_vco_freq_khz = 3600000; + } + + /*update the maximum display clock for each power state*/ + for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + enum dm_pp_clocks_state clk_state = DM_PP_CLOCKS_STATE_INVALID; + + switch (i) { + case 0: + clk_state = DM_PP_CLOCKS_STATE_ULTRA_LOW; + break; + + case 1: + clk_state = DM_PP_CLOCKS_STATE_LOW; + break; + + case 2: + clk_state = DM_PP_CLOCKS_STATE_NOMINAL; + break; + + case 3: + clk_state = DM_PP_CLOCKS_STATE_PERFORMANCE; + break; + + default: + clk_state = DM_PP_CLOCKS_STATE_INVALID; + break; + } + + /*Do not allow bad VBIOS/SBIOS to override with invalid values, + * check for > 100MHz*/ + if (info.disp_clk_voltage[i].max_supported_clk >= 100000) + clk_mgr_dce->max_clks_by_state[clk_state].display_clk_khz = + info.disp_clk_voltage[i].max_supported_clk; + } + + if (!debug->disable_dfs_bypass && bp->integrated_info) + if (bp->integrated_info->gpu_cap_info & DFS_BYPASS_ENABLE) + clk_mgr_dce->dfs_bypass_enabled = true; +} + +void dce_clock_read_ss_info(struct dce_clk_mgr *clk_mgr_dce) +{ + struct dc_bios *bp = clk_mgr_dce->base.ctx->dc_bios; + int ss_info_num = bp->funcs->get_ss_entry_number( + bp, AS_SIGNAL_TYPE_GPU_PLL); + + if (ss_info_num) { + struct spread_spectrum_info info = { { 0 } }; + enum bp_result result = bp->funcs->get_spread_spectrum_info( + bp, AS_SIGNAL_TYPE_GPU_PLL, 0, &info); + + /* Based on VBIOS, VBIOS will keep entry for GPU PLL SS + * even if SS not enabled and in that case + * SSInfo.spreadSpectrumPercentage !=0 would be sign + * that SS is enabled + */ + if (result == BP_RESULT_OK && + info.spread_spectrum_percentage != 0) { + clk_mgr_dce->ss_on_dprefclk = true; + clk_mgr_dce->dprefclk_ss_divider = info.spread_percentage_divider; + + if (info.type.CENTER_MODE == 0) { + /* TODO: Currently for DP Reference clock we + * need only SS percentage for + * downspread */ + clk_mgr_dce->dprefclk_ss_percentage = + info.spread_spectrum_percentage; + } + + return; + } + + result = bp->funcs->get_spread_spectrum_info( + bp, AS_SIGNAL_TYPE_DISPLAY_PORT, 0, &info); + + /* Based on VBIOS, VBIOS will keep entry for DPREFCLK SS + * even if SS not enabled and in that case + * SSInfo.spreadSpectrumPercentage !=0 would be sign + * that SS is enabled + */ + if (result == BP_RESULT_OK && + info.spread_spectrum_percentage != 0) { + clk_mgr_dce->ss_on_dprefclk = true; + clk_mgr_dce->dprefclk_ss_divider = info.spread_percentage_divider; + + if (info.type.CENTER_MODE == 0) { + /* Currently for DP Reference clock we + * need only SS percentage for + * downspread */ + clk_mgr_dce->dprefclk_ss_percentage = + info.spread_spectrum_percentage; + } + if (clk_mgr_dce->base.ctx->dc->debug.ignore_dpref_ss) + clk_mgr_dce->dprefclk_ss_percentage = 0; + } + } +} + +/** + * dce121_clock_patch_xgmi_ss_info() - Save XGMI spread spectrum info + * @clk_mgr: clock manager base structure + * + * Reads from VBIOS the XGMI spread spectrum info and saves it within + * the dce clock manager. This operation will overwrite the existing dprefclk + * SS values if the vBIOS query succeeds. Otherwise, it does nothing. It also + * sets the ->xgmi_enabled flag. + */ +void dce121_clock_patch_xgmi_ss_info(struct clk_mgr *clk_mgr) +{ + struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); + enum bp_result result; + struct spread_spectrum_info info = { { 0 } }; + struct dc_bios *bp = clk_mgr_dce->base.ctx->dc_bios; + + clk_mgr_dce->xgmi_enabled = false; + + result = bp->funcs->get_spread_spectrum_info(bp, AS_SIGNAL_TYPE_XGMI, + 0, &info); + if (result == BP_RESULT_OK && info.spread_spectrum_percentage != 0) { + clk_mgr_dce->xgmi_enabled = true; + clk_mgr_dce->ss_on_dprefclk = true; + clk_mgr_dce->dprefclk_ss_divider = + info.spread_percentage_divider; + + if (info.type.CENTER_MODE == 0) { + /* Currently for DP Reference clock we + * need only SS percentage for + * downspread */ + clk_mgr_dce->dprefclk_ss_percentage = + info.spread_spectrum_percentage; + } + } +} + +void dce110_fill_display_configs( + const struct dc_state *context, + struct dm_pp_display_configuration *pp_display_cfg) +{ + int j; + int num_cfgs = 0; + + for (j = 0; j < context->stream_count; j++) { + int k; + + const struct dc_stream_state *stream = context->streams[j]; + struct dm_pp_single_disp_config *cfg = + &pp_display_cfg->disp_configs[num_cfgs]; + const struct pipe_ctx *pipe_ctx = NULL; + + for (k = 0; k < MAX_PIPES; k++) + if (stream == context->res_ctx.pipe_ctx[k].stream) { + pipe_ctx = &context->res_ctx.pipe_ctx[k]; + break; + } + + ASSERT(pipe_ctx != NULL); + + /* only notify active stream */ + if (stream->dpms_off) + continue; + + num_cfgs++; + cfg->signal = pipe_ctx->stream->signal; + cfg->pipe_idx = pipe_ctx->stream_res.tg->inst; + cfg->src_height = stream->src.height; + cfg->src_width = stream->src.width; + cfg->ddi_channel_mapping = + stream->link->ddi_channel_mapping.raw; + cfg->transmitter = + stream->link->link_enc->transmitter; + cfg->link_settings.lane_count = + stream->link->cur_link_settings.lane_count; + cfg->link_settings.link_rate = + stream->link->cur_link_settings.link_rate; + cfg->link_settings.link_spread = + stream->link->cur_link_settings.link_spread; + cfg->sym_clock = stream->phy_pix_clk; + /* Round v_refresh*/ + cfg->v_refresh = stream->timing.pix_clk_100hz * 100; + cfg->v_refresh /= stream->timing.h_total; + cfg->v_refresh = (cfg->v_refresh + stream->timing.v_total / 2) + / stream->timing.v_total; + } + + pp_display_cfg->display_count = num_cfgs; +} + +static uint32_t dce110_get_min_vblank_time_us(const struct dc_state *context) +{ + uint8_t j; + uint32_t min_vertical_blank_time = -1; + + for (j = 0; j < context->stream_count; j++) { + struct dc_stream_state *stream = context->streams[j]; + uint32_t vertical_blank_in_pixels = 0; + uint32_t vertical_blank_time = 0; + + vertical_blank_in_pixels = stream->timing.h_total * + (stream->timing.v_total + - stream->timing.v_addressable); + + vertical_blank_time = vertical_blank_in_pixels + * 10000 / stream->timing.pix_clk_100hz; + + if (min_vertical_blank_time > vertical_blank_time) + min_vertical_blank_time = vertical_blank_time; + } + + return min_vertical_blank_time; +} + +static int determine_sclk_from_bounding_box( + const struct dc *dc, + int required_sclk) +{ + int i; + + /* + * Some asics do not give us sclk levels, so we just report the actual + * required sclk + */ + if (dc->sclk_lvls.num_levels == 0) + return required_sclk; + + for (i = 0; i < dc->sclk_lvls.num_levels; i++) { + if (dc->sclk_lvls.clocks_in_khz[i] >= required_sclk) + return dc->sclk_lvls.clocks_in_khz[i]; + } + /* + * even maximum level could not satisfy requirement, this + * is unexpected at this stage, should have been caught at + * validation time + */ + ASSERT(0); + return dc->sclk_lvls.clocks_in_khz[dc->sclk_lvls.num_levels - 1]; +} + +static void dce_pplib_apply_display_requirements( + struct dc *dc, + struct dc_state *context) +{ + struct dm_pp_display_configuration *pp_display_cfg = &context->pp_display_cfg; + + pp_display_cfg->avail_mclk_switch_time_us = dce110_get_min_vblank_time_us(context); + + dce110_fill_display_configs(context, pp_display_cfg); + + if (memcmp(&dc->current_state->pp_display_cfg, pp_display_cfg, sizeof(*pp_display_cfg)) != 0) + dm_pp_apply_display_requirements(dc->ctx, pp_display_cfg); +} + +static void dce11_pplib_apply_display_requirements( + struct dc *dc, + struct dc_state *context) +{ + struct dm_pp_display_configuration *pp_display_cfg = &context->pp_display_cfg; + + pp_display_cfg->all_displays_in_sync = + context->bw_ctx.bw.dce.all_displays_in_sync; + pp_display_cfg->nb_pstate_switch_disable = + context->bw_ctx.bw.dce.nbp_state_change_enable == false; + pp_display_cfg->cpu_cc6_disable = + context->bw_ctx.bw.dce.cpuc_state_change_enable == false; + pp_display_cfg->cpu_pstate_disable = + context->bw_ctx.bw.dce.cpup_state_change_enable == false; + pp_display_cfg->cpu_pstate_separation_time = + context->bw_ctx.bw.dce.blackout_recovery_time_us; + + pp_display_cfg->min_memory_clock_khz = context->bw_ctx.bw.dce.yclk_khz + / MEMORY_TYPE_MULTIPLIER_CZ; + + pp_display_cfg->min_engine_clock_khz = determine_sclk_from_bounding_box( + dc, + context->bw_ctx.bw.dce.sclk_khz); + + /* + * As workaround for >4x4K lightup set dcfclock to min_engine_clock value. + * This is not required for less than 5 displays, + * thus don't request decfclk in dc to avoid impact + * on power saving. + * + */ + pp_display_cfg->min_dcfclock_khz = (context->stream_count > 4)? + pp_display_cfg->min_engine_clock_khz : 0; + + pp_display_cfg->min_engine_clock_deep_sleep_khz + = context->bw_ctx.bw.dce.sclk_deep_sleep_khz; + + pp_display_cfg->avail_mclk_switch_time_us = + dce110_get_min_vblank_time_us(context); + /* TODO: dce11.2*/ + pp_display_cfg->avail_mclk_switch_time_in_disp_active_us = 0; + + pp_display_cfg->disp_clk_khz = dc->res_pool->clk_mgr->clks.dispclk_khz; + + dce110_fill_display_configs(context, pp_display_cfg); + + /* TODO: is this still applicable?*/ + if (pp_display_cfg->display_count == 1) { + const struct dc_crtc_timing *timing = + &context->streams[0]->timing; + + pp_display_cfg->crtc_index = + pp_display_cfg->disp_configs[0].pipe_idx; + pp_display_cfg->line_time_in_us = timing->h_total * 10000 / timing->pix_clk_100hz; + } + + if (memcmp(&dc->current_state->pp_display_cfg, pp_display_cfg, sizeof(*pp_display_cfg)) != 0) + dm_pp_apply_display_requirements(dc->ctx, pp_display_cfg); +} + +static void dce_update_clocks(struct clk_mgr *clk_mgr, + struct dc_state *context, + bool safe_to_lower) +{ + struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); + struct dm_pp_power_level_change_request level_change_req; + int patched_disp_clk = context->bw_ctx.bw.dce.dispclk_khz; + + /*TODO: W/A for dal3 linux, investigate why this works */ + if (!clk_mgr_dce->dfs_bypass_active) + patched_disp_clk = patched_disp_clk * 115 / 100; + + level_change_req.power_level = dce_get_required_clocks_state(clk_mgr, context); + /* get max clock state from PPLIB */ + if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower) + || level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) { + if (dm_pp_apply_power_level_change_request(clk_mgr->ctx, &level_change_req)) + clk_mgr_dce->cur_min_clks_state = level_change_req.power_level; + } + + if (should_set_clock(safe_to_lower, patched_disp_clk, clk_mgr->clks.dispclk_khz)) { + patched_disp_clk = dce_set_clock(clk_mgr, patched_disp_clk); + clk_mgr->clks.dispclk_khz = patched_disp_clk; + } + dce_pplib_apply_display_requirements(clk_mgr->ctx->dc, context); +} + +static void dce11_update_clocks(struct clk_mgr *clk_mgr, + struct dc_state *context, + bool safe_to_lower) +{ + struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); + struct dm_pp_power_level_change_request level_change_req; + int patched_disp_clk = context->bw_ctx.bw.dce.dispclk_khz; + + /*TODO: W/A for dal3 linux, investigate why this works */ + if (!clk_mgr_dce->dfs_bypass_active) + patched_disp_clk = patched_disp_clk * 115 / 100; + + level_change_req.power_level = dce_get_required_clocks_state(clk_mgr, context); + /* get max clock state from PPLIB */ + if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower) + || level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) { + if (dm_pp_apply_power_level_change_request(clk_mgr->ctx, &level_change_req)) + clk_mgr_dce->cur_min_clks_state = level_change_req.power_level; + } + + if (should_set_clock(safe_to_lower, patched_disp_clk, clk_mgr->clks.dispclk_khz)) { + context->bw_ctx.bw.dce.dispclk_khz = dce_set_clock(clk_mgr, patched_disp_clk); + clk_mgr->clks.dispclk_khz = patched_disp_clk; + } + dce11_pplib_apply_display_requirements(clk_mgr->ctx->dc, context); +} + +static void dce112_update_clocks(struct clk_mgr *clk_mgr, + struct dc_state *context, + bool safe_to_lower) +{ + struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); + struct dm_pp_power_level_change_request level_change_req; + int patched_disp_clk = context->bw_ctx.bw.dce.dispclk_khz; + + /*TODO: W/A for dal3 linux, investigate why this works */ + if (!clk_mgr_dce->dfs_bypass_active) + patched_disp_clk = patched_disp_clk * 115 / 100; + + level_change_req.power_level = dce_get_required_clocks_state(clk_mgr, context); + /* get max clock state from PPLIB */ + if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower) + || level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) { + if (dm_pp_apply_power_level_change_request(clk_mgr->ctx, &level_change_req)) + clk_mgr_dce->cur_min_clks_state = level_change_req.power_level; + } + + if (should_set_clock(safe_to_lower, patched_disp_clk, clk_mgr->clks.dispclk_khz)) { + patched_disp_clk = dce112_set_clock(clk_mgr, patched_disp_clk); + clk_mgr->clks.dispclk_khz = patched_disp_clk; + } + dce11_pplib_apply_display_requirements(clk_mgr->ctx->dc, context); +} + +static void dce12_update_clocks(struct clk_mgr *clk_mgr, + struct dc_state *context, + bool safe_to_lower) +{ + struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); + struct dm_pp_clock_for_voltage_req clock_voltage_req = {0}; + int max_pix_clk = get_max_pixel_clock_for_all_paths(context); + int patched_disp_clk = context->bw_ctx.bw.dce.dispclk_khz; + + /*TODO: W/A for dal3 linux, investigate why this works */ + if (!clk_mgr_dce->dfs_bypass_active) + patched_disp_clk = patched_disp_clk * 115 / 100; + + if (should_set_clock(safe_to_lower, patched_disp_clk, clk_mgr->clks.dispclk_khz)) { + clock_voltage_req.clk_type = DM_PP_CLOCK_TYPE_DISPLAY_CLK; + /* + * When xGMI is enabled, the display clk needs to be adjusted + * with the WAFL link's SS percentage. + */ + if (clk_mgr_dce->xgmi_enabled) + patched_disp_clk = clk_mgr_adjust_dp_ref_freq_for_ss( + clk_mgr_dce, patched_disp_clk); + clock_voltage_req.clocks_in_khz = patched_disp_clk; + clk_mgr->clks.dispclk_khz = dce112_set_clock(clk_mgr, patched_disp_clk); + + dm_pp_apply_clock_for_voltage_request(clk_mgr->ctx, &clock_voltage_req); + } + + if (should_set_clock(safe_to_lower, max_pix_clk, clk_mgr->clks.phyclk_khz)) { + clock_voltage_req.clk_type = DM_PP_CLOCK_TYPE_DISPLAYPHYCLK; + clock_voltage_req.clocks_in_khz = max_pix_clk; + clk_mgr->clks.phyclk_khz = max_pix_clk; + + dm_pp_apply_clock_for_voltage_request(clk_mgr->ctx, &clock_voltage_req); + } + dce11_pplib_apply_display_requirements(clk_mgr->ctx->dc, context); +} + +static const struct clk_mgr_funcs dce120_funcs = { + .get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz, + .update_clocks = dce12_update_clocks +}; + +static const struct clk_mgr_funcs dce112_funcs = { + .get_dp_ref_clk_frequency = dce_get_dp_ref_freq_khz, + .update_clocks = dce112_update_clocks +}; + +static const struct clk_mgr_funcs dce110_funcs = { + .get_dp_ref_clk_frequency = dce_get_dp_ref_freq_khz, + .update_clocks = dce11_update_clocks, +}; + +static const struct clk_mgr_funcs dce_funcs = { + .get_dp_ref_clk_frequency = dce_get_dp_ref_freq_khz, + .update_clocks = dce_update_clocks +}; + +static void dce_clk_mgr_construct( + struct dce_clk_mgr *clk_mgr_dce, + struct dc_context *ctx, + const struct clk_mgr_registers *regs, + const struct clk_mgr_shift *clk_shift, + const struct clk_mgr_mask *clk_mask) +{ + struct clk_mgr *base = &clk_mgr_dce->base; + struct dm_pp_static_clock_info static_clk_info = {0}; + + base->ctx = ctx; + base->funcs = &dce_funcs; + + clk_mgr_dce->regs = regs; + clk_mgr_dce->clk_mgr_shift = clk_shift; + clk_mgr_dce->clk_mgr_mask = clk_mask; + + clk_mgr_dce->dfs_bypass_disp_clk = 0; + + clk_mgr_dce->dprefclk_ss_percentage = 0; + clk_mgr_dce->dprefclk_ss_divider = 1000; + clk_mgr_dce->ss_on_dprefclk = false; + + + if (dm_pp_get_static_clocks(ctx, &static_clk_info)) + clk_mgr_dce->max_clks_state = static_clk_info.max_clocks_state; + else + clk_mgr_dce->max_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; + clk_mgr_dce->cur_min_clks_state = DM_PP_CLOCKS_STATE_INVALID; + + dce_clock_read_integrated_info(clk_mgr_dce); + dce_clock_read_ss_info(clk_mgr_dce); +} + +struct clk_mgr *dce_clk_mgr_create( + struct dc_context *ctx, + const struct clk_mgr_registers *regs, + const struct clk_mgr_shift *clk_shift, + const struct clk_mgr_mask *clk_mask) +{ + struct dce_clk_mgr *clk_mgr_dce = kzalloc(sizeof(*clk_mgr_dce), GFP_KERNEL); + + if (clk_mgr_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + memcpy(clk_mgr_dce->max_clks_by_state, + dce80_max_clks_by_state, + sizeof(dce80_max_clks_by_state)); + + dce_clk_mgr_construct( + clk_mgr_dce, ctx, regs, clk_shift, clk_mask); + + return &clk_mgr_dce->base; +} + +struct clk_mgr *dce110_clk_mgr_create( + struct dc_context *ctx, + const struct clk_mgr_registers *regs, + const struct clk_mgr_shift *clk_shift, + const struct clk_mgr_mask *clk_mask) +{ + struct dce_clk_mgr *clk_mgr_dce = kzalloc(sizeof(*clk_mgr_dce), GFP_KERNEL); + + if (clk_mgr_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + memcpy(clk_mgr_dce->max_clks_by_state, + dce110_max_clks_by_state, + sizeof(dce110_max_clks_by_state)); + + dce_clk_mgr_construct( + clk_mgr_dce, ctx, regs, clk_shift, clk_mask); + + clk_mgr_dce->base.funcs = &dce110_funcs; + + return &clk_mgr_dce->base; +} + +struct clk_mgr *dce112_clk_mgr_create( + struct dc_context *ctx, + const struct clk_mgr_registers *regs, + const struct clk_mgr_shift *clk_shift, + const struct clk_mgr_mask *clk_mask) +{ + struct dce_clk_mgr *clk_mgr_dce = kzalloc(sizeof(*clk_mgr_dce), GFP_KERNEL); + + if (clk_mgr_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + memcpy(clk_mgr_dce->max_clks_by_state, + dce112_max_clks_by_state, + sizeof(dce112_max_clks_by_state)); + + dce_clk_mgr_construct( + clk_mgr_dce, ctx, regs, clk_shift, clk_mask); + + clk_mgr_dce->base.funcs = &dce112_funcs; + + return &clk_mgr_dce->base; +} + +struct clk_mgr *dce120_clk_mgr_create(struct dc_context *ctx) +{ + struct dce_clk_mgr *clk_mgr_dce = kzalloc(sizeof(*clk_mgr_dce), GFP_KERNEL); + + if (clk_mgr_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + memcpy(clk_mgr_dce->max_clks_by_state, + dce120_max_clks_by_state, + sizeof(dce120_max_clks_by_state)); + + dce_clk_mgr_construct( + clk_mgr_dce, ctx, NULL, NULL, NULL); + + clk_mgr_dce->dprefclk_khz = 600000; + clk_mgr_dce->base.funcs = &dce120_funcs; + + return &clk_mgr_dce->base; +} + +struct clk_mgr *dce121_clk_mgr_create(struct dc_context *ctx) +{ + struct dce_clk_mgr *clk_mgr_dce = kzalloc(sizeof(*clk_mgr_dce), + GFP_KERNEL); + + if (clk_mgr_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + memcpy(clk_mgr_dce->max_clks_by_state, dce120_max_clks_by_state, + sizeof(dce120_max_clks_by_state)); + + dce_clk_mgr_construct(clk_mgr_dce, ctx, NULL, NULL, NULL); + + clk_mgr_dce->dprefclk_khz = 625000; + clk_mgr_dce->base.funcs = &dce120_funcs; + + return &clk_mgr_dce->base; +} + +void dce_clk_mgr_destroy(struct clk_mgr **clk_mgr) +{ + struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(*clk_mgr); + + kfree(clk_mgr_dce); + *clk_mgr = NULL; +} 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 000000000..165392380 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -0,0 +1,1770 @@ +/* + * 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 "clk_mgr.h" + +#include "reg_helper.h" + +#define REG(reg)\ + (clk_src->regs->reg) + +#define CTX \ + clk_src->base.ctx + +#define DC_LOGGER_INIT() + +#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 + +#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0])) + +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_LVDS: + ss_parm = clk_src->lvds_ss_params; + entrys_num = clk_src->lvds_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; +} + +/** + * calculate_fb_and_fractional_fb_divider - Calculates feedback and fractional + * feedback dividers values + * + * @calc_pll_cs: Pointer to clock source information + * @target_pix_clk_100hz: Desired frequency in 100 Hz + * @ref_divider: Reference divider (already known) + * @post_divider: 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 + * + * return: + * 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_100hz, + 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_100hz * 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 * 10ull); + +/*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 += 5ULL * + 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 - Calculates Feedback and + * Fractional Feedback divider values + * for passed Reference and Post divider, + * checking for tolerance. + * @calc_pll_cs: Pointer to clock source information + * @pll_settings: Pointer to PLL settings + * @ref_divider: Reference divider (already known) + * @post_divider: Post Divider (already known) + * @tolerance: Tolerance for Calculated Pixel Clock to be within + * + * return: + * It fills the PLLSettings structure with PLL Dividers values + * if calculated values are within required tolerance + * It returns - true if error is within tolerance + * - false if error 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_100hz; + uint32_t abs_err; + uint64_t actual_calc_clk_100hz; + + calculate_fb_and_fractional_fb_divider( + calc_pll_cs, + pll_settings->adjusted_pix_clk_100hz, + ref_divider, + post_divider, + &feedback_divider, + &fract_feedback_divider); + + /*Actual calculated value*/ + actual_calc_clk_100hz = (uint64_t)feedback_divider * + calc_pll_cs->fract_fb_divider_factor + + fract_feedback_divider; + actual_calc_clk_100hz *= calc_pll_cs->ref_freq_khz * 10; + actual_calc_clk_100hz = + div_u64(actual_calc_clk_100hz, + ref_divider * post_divider * + calc_pll_cs->fract_fb_divider_factor); + + actual_calculated_clock_100hz = (uint32_t)(actual_calc_clk_100hz); + + abs_err = (actual_calculated_clock_100hz > + pll_settings->adjusted_pix_clk_100hz) + ? actual_calculated_clock_100hz - + pll_settings->adjusted_pix_clk_100hz + : pll_settings->adjusted_pix_clk_100hz - + actual_calculated_clock_100hz; + + 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_100hz = + actual_calculated_clock_100hz; + pll_settings->vco_freq = + div_u64((u64)actual_calculated_clock_100hz * post_divider, 10); + 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_100hz * err_tolerance) / + 100000; + 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_100hz == 0) { + DC_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_100hz < + calc_pll_cs->min_vco_khz * 10) { + min_post_divider = calc_pll_cs->min_vco_khz * 10 / + pll_settings->adjusted_pix_clk_100hz; + if ((min_post_divider * + pll_settings->adjusted_pix_clk_100hz) < + calc_pll_cs->min_vco_khz * 10) + min_post_divider++; + } + + max_post_divider = calc_pll_cs->max_pix_clock_pll_post_divider; + if (max_post_divider * pll_settings->adjusted_pix_clk_100hz + > calc_pll_cs->max_vco_khz * 10) + max_post_divider = calc_pll_cs->max_vco_khz * 10 / + pll_settings->adjusted_pix_clk_100hz; + } + +/* 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) { + DC_LOG_ERROR( + "%s Post divider range is invalid", __func__); + return MAX_PLL_CALC_ERROR; + } + + if (min_ref_divider > max_ref_divider) { + DC_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_100hz = 0; + uint32_t requested_clk_100hz = 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_100hz = pix_clk_params->requested_pix_clk_100hz; + if (pix_clk_params->pixel_encoding != PIXEL_ENCODING_YCBCR422) { + switch (pix_clk_params->color_depth) { + case COLOR_DEPTH_101010: + requested_clk_100hz = (requested_clk_100hz * 5) >> 2; + break; /* x1.25*/ + case COLOR_DEPTH_121212: + requested_clk_100hz = (requested_clk_100hz * 6) >> 2; + break; /* x1.5*/ + case COLOR_DEPTH_161616: + requested_clk_100hz = requested_clk_100hz * 2; + break; /* x2.0*/ + default: + break; + } + } + actual_pix_clk_100hz = requested_clk_100hz; + } + break; + + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + requested_clk_100hz = pix_clk_params->requested_sym_clk * 10; + actual_pix_clk_100hz = pix_clk_params->requested_pix_clk_100hz; + break; + + default: + requested_clk_100hz = pix_clk_params->requested_pix_clk_100hz; + actual_pix_clk_100hz = pix_clk_params->requested_pix_clk_100hz; + break; + } + + bp_adjust_pixel_clock_params.pixel_clock = requested_clk_100hz / 10; + 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_100hz = actual_pix_clk_100hz; + pll_settings->adjusted_pix_clk_100hz = + bp_adjust_pixel_clock_params.adjusted_pixel_clock * 10; + 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 field = 0; + uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; + DC_LOGGER_INIT(); + /* 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 */ + 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_100hz / 10); + + 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. */ + DC_LOG_ERROR( + "%s: Failed to adjust pixel clock!!", __func__); + pll_settings->actual_pix_clk_100hz = + pix_clk_params->requested_pix_clk_100hz; + pll_settings->adjusted_pix_clk_100hz = + pix_clk_params->requested_pix_clk_100hz; + + if (dc_is_dp_signal(pix_clk_params->signal_type)) + pll_settings->adjusted_pix_clk_100hz = 1000000; + } + + /* 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 actual_pixel_clock_100hz; + + actual_pixel_clock_100hz = pix_clk_params->requested_pix_clk_100hz; + /* Calculate Dividers */ + if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) { + switch (pix_clk_params->color_depth) { + case COLOR_DEPTH_101010: + actual_pixel_clock_100hz = (actual_pixel_clock_100hz * 5) >> 2; + actual_pixel_clock_100hz -= actual_pixel_clock_100hz % 10; + break; + case COLOR_DEPTH_121212: + actual_pixel_clock_100hz = (actual_pixel_clock_100hz * 6) >> 2; + actual_pixel_clock_100hz -= actual_pixel_clock_100hz % 10; + break; + case COLOR_DEPTH_161616: + actual_pixel_clock_100hz = actual_pixel_clock_100hz * 2; + break; + default: + break; + } + } + pll_settings->actual_pix_clk_100hz = actual_pixel_clock_100hz; + pll_settings->adjusted_pix_clk_100hz = actual_pixel_clock_100hz; + pll_settings->calculated_pix_clk_100hz = pix_clk_params->requested_pix_clk_100hz; +} + +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; + DC_LOGGER_INIT(); + + if (pix_clk_params == NULL || pll_settings == NULL + || pix_clk_params->requested_pix_clk_100hz == 0) { + DC_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_100hz = clk_src->ext_clk_khz * 10; + pll_settings->calculated_pix_clk_100hz = clk_src->ext_clk_khz * 10; + pll_settings->actual_pix_clk_100hz = + pix_clk_params->requested_pix_clk_100hz; + return 0; + } + + pll_calc_error = dce110_get_pix_clk_dividers_helper(clk_src, + pll_settings, pix_clk_params); + + return pll_calc_error; +} + +static uint32_t dce112_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); + DC_LOGGER_INIT(); + + if (pix_clk_params == NULL || pll_settings == NULL + || pix_clk_params->requested_pix_clk_100hz == 0) { + DC_LOG_ERROR( + "%s: Invalid parameters!!\n", __func__); + return -1; + } + + 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_100hz = clk_src->ext_clk_khz * 10; + pll_settings->calculated_pix_clk_100hz = clk_src->ext_clk_khz * 10; + pll_settings->actual_pix_clk_100hz = + pix_clk_params->requested_pix_clk_100hz; + return -1; + } + + dce112_get_pix_clk_dividers_helper(clk_src, + pll_settings, pix_clk_params); + + return 0; +} + +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 fixed31_32 fb_div; + struct fixed31_32 ss_amount; + struct fixed31_32 ss_nslip_amount; + struct fixed31_32 ss_ds_frac_amount; + struct fixed31_32 ss_step_size; + struct fixed31_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 = dc_fixpt_from_fraction( + pll_settings->fract_feedback_divider, 1000000); + fb_div = dc_fixpt_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 = dc_fixpt_mul( + fb_div, dc_fixpt_from_fraction(ss_data->percentage, + 100 * ss_data->percentage_divider)); + ds_data->feedback_amount = dc_fixpt_floor(ss_amount); + + ss_nslip_amount = dc_fixpt_sub(ss_amount, + dc_fixpt_from_int(ds_data->feedback_amount)); + ss_nslip_amount = dc_fixpt_mul_int(ss_nslip_amount, 10); + ds_data->nfrac_amount = dc_fixpt_floor(ss_nslip_amount); + + ss_ds_frac_amount = dc_fixpt_sub(ss_nslip_amount, + dc_fixpt_from_int(ds_data->nfrac_amount)); + ss_ds_frac_amount = dc_fixpt_mul_int(ss_ds_frac_amount, 65536); + ds_data->ds_frac_amount = dc_fixpt_floor(ss_ds_frac_amount); + + /* compute SS_STEP_SIZE_DSFRAC */ + modulation_time = dc_fixpt_from_fraction( + pll_settings->reference_freq * 1000, + pll_settings->reference_divider * ss_data->modulation_freq_hz); + + if (ss_data->flags.CENTER_SPREAD) + modulation_time = dc_fixpt_div_int(modulation_time, 4); + else + modulation_time = dc_fixpt_div_int(modulation_time, 2); + + ss_step_size = dc_fixpt_div(ss_amount, modulation_time); + /* SS_STEP_SIZE_DSFRAC_DEC = Int(SS_STEP_SIZE * 2 ^ 16 * 10)*/ + ss_step_size = dc_fixpt_mul_int(ss_step_size, 65536 * 10); + ds_data->ds_frac_size = dc_fixpt_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_100hz / 10); + +/* 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) +{ + 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 deep_color_cntl = 0; + uint32_t double_rate_enable = 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) { + double_rate_enable = enable_ycbcr420 ? 1 : 0; + + switch (colordepth) { + case COLOR_DEPTH_888: + deep_color_cntl = 0; + break; + case COLOR_DEPTH_101010: + deep_color_cntl = 1; + break; + case COLOR_DEPTH_121212: + deep_color_cntl = 2; + break; + case COLOR_DEPTH_161616: + deep_color_cntl = 3; + break; + default: + break; + } + } + + if (clk_src->cs_mask->PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE) + REG_UPDATE_2(PIXCLK_RESYNC_CNTL, + PHYPLLA_DCCG_DEEP_COLOR_CNTL, deep_color_cntl, + PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE, double_rate_enable); + else + REG_UPDATE(PIXCLK_RESYNC_CNTL, + PHYPLLA_DCCG_DEEP_COLOR_CNTL, deep_color_cntl); + +} + +static bool dce110_program_pix_clk( + struct clock_source *clock_source, + struct pixel_clk_params *pix_clk_params, + enum dp_link_encoding encoding, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); + 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 (clock_source->id != CLOCK_SOURCE_ID_EXTERNAL && + !dc_is_dp_signal(pix_clk_params->signal_type) && + clock_source->ctx->dce_version <= DCE_VERSION_11_0) + disable_spread_spectrum(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 = clock_source->id; + bp_pc_params.target_pixel_clock_100hz = pll_settings->actual_pix_clk_100hz; + bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id; + bp_pc_params.signal_type = pix_clk_params->signal_type; + + 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; + + switch (pix_clk_params->color_depth) { + case COLOR_DEPTH_101010: + bp_pc_params.color_depth = TRANSMITTER_COLOR_DEPTH_30; + break; + case COLOR_DEPTH_121212: + bp_pc_params.color_depth = TRANSMITTER_COLOR_DEPTH_36; + break; + case COLOR_DEPTH_161616: + bp_pc_params.color_depth = TRANSMITTER_COLOR_DEPTH_48; + break; + default: + break; + } + + if (clk_src->bios->funcs->set_pixel_clock( + 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 (clock_source->id != CLOCK_SOURCE_ID_EXTERNAL + && !dc_is_dp_signal(pix_clk_params->signal_type)) { + + if (pix_clk_params->flags.ENABLE_SS) + if (!enable_spread_spectrum(clk_src, + pix_clk_params->signal_type, + pll_settings)) + return false; + + /* Resync deep color DTO */ + dce110_program_pixel_clk_resync(clk_src, + pix_clk_params->signal_type, + pix_clk_params->color_depth); + } + + return true; +} + +static bool dce112_program_pix_clk( + struct clock_source *clock_source, + struct pixel_clk_params *pix_clk_params, + enum dp_link_encoding encoding, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); + struct bp_pixel_clock_parameters bp_pc_params = {0}; + + if (IS_FPGA_MAXIMUS_DC(clock_source->ctx->dce_environment)) { + unsigned int inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; + unsigned dp_dto_ref_100hz = 7000000; + unsigned clock_100hz = pll_settings->actual_pix_clk_100hz; + + /* Set DTO values: phase = target clock, modulo = reference clock */ + REG_WRITE(PHASE[inst], clock_100hz); + REG_WRITE(MODULO[inst], dp_dto_ref_100hz); + + /* Enable DTO */ + REG_UPDATE(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, 1); + return true; + } + /* First disable SS + * ATOMBIOS will enable by default SS on PLL for DP, + * do not disable it here + */ + if (clock_source->id != CLOCK_SOURCE_ID_EXTERNAL && + !dc_is_dp_signal(pix_clk_params->signal_type) && + clock_source->ctx->dce_version <= DCE_VERSION_11_0) + disable_spread_spectrum(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 = clock_source->id; + bp_pc_params.target_pixel_clock_100hz = pll_settings->actual_pix_clk_100hz; + bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id; + bp_pc_params.signal_type = pix_clk_params->signal_type; + + if (clock_source->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.flags.SUPPORT_YUV_420 = 1; + } + } + if (clk_src->bios->funcs->set_pixel_clock( + clk_src->bios, &bp_pc_params) != BP_RESULT_OK) + return false; + /* Resync deep color DTO */ + if (clock_source->id != CLOCK_SOURCE_ID_DP_DTO) + dce112_program_pixel_clk_resync(clk_src, + pix_clk_params->signal_type, + pix_clk_params->color_depth, + pix_clk_params->flags.SUPPORT_YCBCR420); + + return true; +} + +static bool dcn31_program_pix_clk( + struct clock_source *clock_source, + struct pixel_clk_params *pix_clk_params, + enum dp_link_encoding encoding, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); + unsigned int inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; + unsigned int dp_dto_ref_khz = clock_source->ctx->dc->clk_mgr->dprefclk_khz; + const struct pixel_rate_range_table_entry *e = + look_up_in_video_optimized_rate_tlb(pix_clk_params->requested_pix_clk_100hz / 10); + struct bp_pixel_clock_parameters bp_pc_params = {0}; + enum transmitter_color_depth bp_pc_colour_depth = TRANSMITTER_COLOR_DEPTH_24; + // For these signal types Driver to program DP_DTO without calling VBIOS Command table + if (dc_is_dp_signal(pix_clk_params->signal_type) || dc_is_virtual_signal(pix_clk_params->signal_type)) { + if (e) { + /* Set DTO values: phase = target clock, modulo = reference clock*/ + REG_WRITE(PHASE[inst], e->target_pixel_rate_khz * e->mult_factor); + REG_WRITE(MODULO[inst], dp_dto_ref_khz * e->div_factor); + } else { + /* Set DTO values: phase = target clock, modulo = reference clock*/ + REG_WRITE(PHASE[inst], pll_settings->actual_pix_clk_100hz * 100); + REG_WRITE(MODULO[inst], dp_dto_ref_khz * 1000); + } +#if defined(CONFIG_DRM_AMD_DC_DCN) + /* Enable DTO */ + if (clk_src->cs_mask->PIPE0_DTO_SRC_SEL) + if (encoding == DP_128b_132b_ENCODING) + REG_UPDATE_2(PIXEL_RATE_CNTL[inst], + DP_DTO0_ENABLE, 1, + PIPE0_DTO_SRC_SEL, 2); + else + REG_UPDATE_2(PIXEL_RATE_CNTL[inst], + DP_DTO0_ENABLE, 1, + PIPE0_DTO_SRC_SEL, 1); + else + REG_UPDATE(PIXEL_RATE_CNTL[inst], + DP_DTO0_ENABLE, 1); +#else + REG_UPDATE(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, 1); +#endif + } else { + if (IS_FPGA_MAXIMUS_DC(clock_source->ctx->dce_environment)) { + unsigned int inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; + unsigned dp_dto_ref_100hz = 7000000; + unsigned clock_100hz = pll_settings->actual_pix_clk_100hz; + + /* Set DTO values: phase = target clock, modulo = reference clock */ + REG_WRITE(PHASE[inst], clock_100hz); + REG_WRITE(MODULO[inst], dp_dto_ref_100hz); + + /* Enable DTO */ + #if defined(CONFIG_DRM_AMD_DC_DCN) + if (clk_src->cs_mask->PIPE0_DTO_SRC_SEL) + REG_UPDATE_2(PIXEL_RATE_CNTL[inst], + DP_DTO0_ENABLE, 1, + PIPE0_DTO_SRC_SEL, 1); + else + REG_UPDATE(PIXEL_RATE_CNTL[inst], + DP_DTO0_ENABLE, 1); + #else + REG_UPDATE(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, 1); + #endif + return true; + } + +#if defined(CONFIG_DRM_AMD_DC_DCN) + if (clk_src->cs_mask->PIPE0_DTO_SRC_SEL) + REG_UPDATE(PIXEL_RATE_CNTL[inst], + PIPE0_DTO_SRC_SEL, 0); +#endif + + /*ATOMBIOS expects pixel rate adjusted by deep color ratio)*/ + bp_pc_params.controller_id = pix_clk_params->controller_id; + bp_pc_params.pll_id = clock_source->id; + bp_pc_params.target_pixel_clock_100hz = pll_settings->actual_pix_clk_100hz; + bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id; + bp_pc_params.signal_type = pix_clk_params->signal_type; + + // Make sure we send the correct color depth to DMUB for HDMI + if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) { + switch (pix_clk_params->color_depth) { + case COLOR_DEPTH_888: + bp_pc_colour_depth = TRANSMITTER_COLOR_DEPTH_24; + break; + case COLOR_DEPTH_101010: + bp_pc_colour_depth = TRANSMITTER_COLOR_DEPTH_30; + break; + case COLOR_DEPTH_121212: + bp_pc_colour_depth = TRANSMITTER_COLOR_DEPTH_36; + break; + case COLOR_DEPTH_161616: + bp_pc_colour_depth = TRANSMITTER_COLOR_DEPTH_48; + break; + default: + bp_pc_colour_depth = TRANSMITTER_COLOR_DEPTH_24; + break; + } + bp_pc_params.color_depth = bp_pc_colour_depth; + } + + if (clock_source->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.flags.SUPPORT_YUV_420 = 1; + } + } + if (clk_src->bios->funcs->set_pixel_clock( + clk_src->bios, &bp_pc_params) != BP_RESULT_OK) + return false; + /* Resync deep color DTO */ + if (clock_source->id != CLOCK_SOURCE_ID_DP_DTO) + dce112_program_pixel_clk_resync(clk_src, + pix_clk_params->signal_type, + pix_clk_params->color_depth, + pix_clk_params->flags.SUPPORT_YCBCR420); + } + + 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; +} + +static bool get_pixel_clk_frequency_100hz( + const struct clock_source *clock_source, + unsigned int inst, + unsigned int *pixel_clk_khz) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); + unsigned int clock_hz = 0; + unsigned int modulo_hz = 0; + + if (clock_source->id == CLOCK_SOURCE_ID_DP_DTO) { + clock_hz = REG_READ(PHASE[inst]); + + if (clock_source->ctx->dc->hwss.enable_vblanks_synchronization && + clock_source->ctx->dc->config.vblank_alignment_max_frame_time_diff > 0) { + /* NOTE: In case VBLANK syncronization is enabled, MODULO may + * not be programmed equal to DPREFCLK + */ + modulo_hz = REG_READ(MODULO[inst]); + if (modulo_hz) + *pixel_clk_khz = div_u64((uint64_t)clock_hz* + clock_source->ctx->dc->clk_mgr->dprefclk_khz*10, + modulo_hz); + else + *pixel_clk_khz = 0; + } else { + /* NOTE: There is agreement with VBIOS here that MODULO is + * programmed equal to DPREFCLK, in which case PHASE will be + * equivalent to pixel clock. + */ + *pixel_clk_khz = clock_hz / 100; + } + return true; + } + + return false; +} + +/* this table is use to find *1.001 and /1.001 pixel rates from non-precise pixel rate */ +const struct pixel_rate_range_table_entry video_optimized_pixel_rates[] = { + // /1.001 rates + {25170, 25180, 25200, 1000, 1001}, //25.2MHz -> 25.17 + {59340, 59350, 59400, 1000, 1001}, //59.4Mhz -> 59.340 + {74170, 74180, 74250, 1000, 1001}, //74.25Mhz -> 74.1758 + {125870, 125880, 126000, 1000, 1001}, //126Mhz -> 125.87 + {148350, 148360, 148500, 1000, 1001}, //148.5Mhz -> 148.3516 + {167830, 167840, 168000, 1000, 1001}, //168Mhz -> 167.83 + {222520, 222530, 222750, 1000, 1001}, //222.75Mhz -> 222.527 + {257140, 257150, 257400, 1000, 1001}, //257.4Mhz -> 257.1429 + {296700, 296710, 297000, 1000, 1001}, //297Mhz -> 296.7033 + {342850, 342860, 343200, 1000, 1001}, //343.2Mhz -> 342.857 + {395600, 395610, 396000, 1000, 1001}, //396Mhz -> 395.6 + {409090, 409100, 409500, 1000, 1001}, //409.5Mhz -> 409.091 + {445050, 445060, 445500, 1000, 1001}, //445.5Mhz -> 445.055 + {467530, 467540, 468000, 1000, 1001}, //468Mhz -> 467.5325 + {519230, 519240, 519750, 1000, 1001}, //519.75Mhz -> 519.231 + {525970, 525980, 526500, 1000, 1001}, //526.5Mhz -> 525.974 + {545450, 545460, 546000, 1000, 1001}, //546Mhz -> 545.455 + {593400, 593410, 594000, 1000, 1001}, //594Mhz -> 593.4066 + {623370, 623380, 624000, 1000, 1001}, //624Mhz -> 623.377 + {692300, 692310, 693000, 1000, 1001}, //693Mhz -> 692.308 + {701290, 701300, 702000, 1000, 1001}, //702Mhz -> 701.2987 + {791200, 791210, 792000, 1000, 1001}, //792Mhz -> 791.209 + {890100, 890110, 891000, 1000, 1001}, //891Mhz -> 890.1099 + {1186810, 1186820, 1188000, 1000, 1001},//1188Mhz -> 1186.8131 + + // *1.001 rates + {27020, 27030, 27000, 1001, 1000}, //27Mhz + {54050, 54060, 54000, 1001, 1000}, //54Mhz + {108100, 108110, 108000, 1001, 1000},//108Mhz +}; + +const struct pixel_rate_range_table_entry *look_up_in_video_optimized_rate_tlb( + unsigned int pixel_rate_khz) +{ + int i; + + for (i = 0; i < NUM_ELEMENTS(video_optimized_pixel_rates); i++) { + const struct pixel_rate_range_table_entry *e = &video_optimized_pixel_rates[i]; + + if (e->range_min_khz <= pixel_rate_khz && pixel_rate_khz <= e->range_max_khz) { + return e; + } + } + + return NULL; +} + +static bool dcn20_program_pix_clk( + struct clock_source *clock_source, + struct pixel_clk_params *pix_clk_params, + enum dp_link_encoding encoding, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); + unsigned int inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; + + dce112_program_pix_clk(clock_source, pix_clk_params, encoding, pll_settings); + + if (clock_source->ctx->dc->hwss.enable_vblanks_synchronization && + clock_source->ctx->dc->config.vblank_alignment_max_frame_time_diff > 0) { + /* NOTE: In case VBLANK syncronization is enabled, + * we need to set modulo to default DPREFCLK first + * dce112_program_pix_clk does not set default DPREFCLK + */ + REG_WRITE(MODULO[inst], + clock_source->ctx->dc->clk_mgr->dprefclk_khz*1000); + } + return true; +} + +static bool dcn20_override_dp_pix_clk( + struct clock_source *clock_source, + unsigned int inst, + unsigned int pixel_clk, + unsigned int ref_clk) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); + + REG_UPDATE(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, 0); + REG_WRITE(PHASE[inst], pixel_clk); + REG_WRITE(MODULO[inst], ref_clk); + REG_UPDATE(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, 1); + return true; +} + +static const struct clock_source_funcs dcn20_clk_src_funcs = { + .cs_power_down = dce110_clock_source_power_down, + .program_pix_clk = dcn20_program_pix_clk, + .get_pix_clk_dividers = dce112_get_pix_clk_dividers, + .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz, + .override_dp_pix_clk = dcn20_override_dp_pix_clk +}; + +static bool dcn3_program_pix_clk( + struct clock_source *clock_source, + struct pixel_clk_params *pix_clk_params, + enum dp_link_encoding encoding, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); + unsigned int inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; + unsigned int dp_dto_ref_khz = clock_source->ctx->dc->clk_mgr->dprefclk_khz; + const struct pixel_rate_range_table_entry *e = + look_up_in_video_optimized_rate_tlb(pix_clk_params->requested_pix_clk_100hz / 10); + + // For these signal types Driver to program DP_DTO without calling VBIOS Command table + if (dc_is_dp_signal(pix_clk_params->signal_type)) { + if (e) { + /* Set DTO values: phase = target clock, modulo = reference clock*/ + REG_WRITE(PHASE[inst], e->target_pixel_rate_khz * e->mult_factor); + REG_WRITE(MODULO[inst], dp_dto_ref_khz * e->div_factor); + } else { + /* Set DTO values: phase = target clock, modulo = reference clock*/ + REG_WRITE(PHASE[inst], pll_settings->actual_pix_clk_100hz * 100); + REG_WRITE(MODULO[inst], dp_dto_ref_khz * 1000); + } + REG_UPDATE(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, 1); + } else + // For other signal types(HDMI_TYPE_A, DVI) Driver still to call VBIOS Command table + dce112_program_pix_clk(clock_source, pix_clk_params, encoding, pll_settings); + + return true; +} + +static uint32_t dcn3_get_pix_clk_dividers( + struct clock_source *cs, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + unsigned long long actual_pix_clk_100Hz = pix_clk_params ? pix_clk_params->requested_pix_clk_100hz : 0; + + DC_LOGGER_INIT(); + + if (pix_clk_params == NULL || pll_settings == NULL + || pix_clk_params->requested_pix_clk_100hz == 0) { + DC_LOG_ERROR( + "%s: Invalid parameters!!\n", __func__); + return -1; + } + + memset(pll_settings, 0, sizeof(*pll_settings)); + /* Adjust for HDMI Type A deep color */ + if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) { + switch (pix_clk_params->color_depth) { + case COLOR_DEPTH_101010: + actual_pix_clk_100Hz = (actual_pix_clk_100Hz * 5) >> 2; + break; + case COLOR_DEPTH_121212: + actual_pix_clk_100Hz = (actual_pix_clk_100Hz * 6) >> 2; + break; + case COLOR_DEPTH_161616: + actual_pix_clk_100Hz = actual_pix_clk_100Hz * 2; + break; + default: + break; + } + } + pll_settings->actual_pix_clk_100hz = (unsigned int) actual_pix_clk_100Hz; + pll_settings->adjusted_pix_clk_100hz = (unsigned int) actual_pix_clk_100Hz; + pll_settings->calculated_pix_clk_100hz = (unsigned int) actual_pix_clk_100Hz; + + return 0; +} + +static const struct clock_source_funcs dcn3_clk_src_funcs = { + .cs_power_down = dce110_clock_source_power_down, + .program_pix_clk = dcn3_program_pix_clk, + .get_pix_clk_dividers = dcn3_get_pix_clk_dividers, + .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz +}; + +static const struct clock_source_funcs dcn31_clk_src_funcs = { + .cs_power_down = dce110_clock_source_power_down, + .program_pix_clk = dcn31_program_pix_clk, + .get_pix_clk_dividers = dcn3_get_pix_clk_dividers, + .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz +}; + +/*****************************************/ +/* Constructor */ +/*****************************************/ + +static const struct clock_source_funcs dce112_clk_src_funcs = { + .cs_power_down = dce110_clock_source_power_down, + .program_pix_clk = dce112_program_pix_clk, + .get_pix_clk_dividers = dce112_get_pix_clk_dividers, + .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz +}; +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, + .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz +}; + + +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; + DC_LOGGER_INIT(); + if (ss_entries_num == NULL) { + DC_LOG_SYNC( + "Invalid entry !!!\n"); + return; + } + if (spread_spectrum_data == NULL) { + DC_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 = kcalloc(*ss_entries_num, + sizeof(struct spread_spectrum_info), + GFP_KERNEL); + ss_info_cur = ss_info; + if (ss_info == NULL) + return; + + ss_data = kcalloc(*ss_entries_num, + sizeof(struct spread_spectrum_data), + GFP_KERNEL); + 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) { + DC_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 */ + DC_LOG_SYNC( + "Invalid SS percentage "); + DC_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; + kfree(ss_info); + return; + +out_free_data: + kfree(ss_data); + *ss_entries_num = 0; +out_free_info: + kfree(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); + get_ss_info_from_atombios( + clk_src, + AS_SIGNAL_TYPE_LVDS, + &clk_src->lvds_ss_params, + &clk_src->lvds_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 dc_firmware_info *fw_info; + if (calc_pll_cs == NULL || + init_data == NULL || + init_data->bp == NULL) + return false; + + if (!init_data->bp->fw_info_valid) + return false; + + fw_info = &init_data->bp->fw_info; + 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) { + DC_LOG_ERROR( + "The dec point num or precision is incorrect!"); + return false; + } + if (init_data->num_fract_fb_divider_decimal_point_precision == 0) { + DC_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 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->fw_info_valid) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } + + clk_src->ext_clk_khz = clk_src->bios->fw_info.external_clock_source_frequency_for_dp; + + /* 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 = clk_src->bios->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; + } + + + calc_pll_cs_init_data_hdmi. + min_override_input_pxl_clk_pll_freq_khz = clk_src->ref_freq_khz/2; + calc_pll_cs_init_data_hdmi. + max_override_input_pxl_clk_pll_freq_khz = clk_src->ref_freq_khz; + + + if (!calc_pll_max_vco_construct( + &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } + + return true; + +unexpected_failure: + return false; +} + +bool dce112_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) +{ + clk_src->base.ctx = ctx; + clk_src->bios = bios; + clk_src->base.id = id; + clk_src->base.funcs = &dce112_clk_src_funcs; + + clk_src->regs = regs; + clk_src->cs_shift = cs_shift; + clk_src->cs_mask = cs_mask; + + if (!clk_src->bios->fw_info_valid) { + ASSERT_CRITICAL(false); + return false; + } + + clk_src->ext_clk_khz = clk_src->bios->fw_info.external_clock_source_frequency_for_dp; + + return true; +} + +bool dcn20_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) +{ + bool ret = dce112_clk_src_construct(clk_src, ctx, bios, id, regs, cs_shift, cs_mask); + + clk_src->base.funcs = &dcn20_clk_src_funcs; + + return ret; +} + +bool dcn3_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) +{ + bool ret = dce112_clk_src_construct(clk_src, ctx, bios, id, regs, cs_shift, cs_mask); + + clk_src->base.funcs = &dcn3_clk_src_funcs; + + return ret; +} + +bool dcn31_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) +{ + bool ret = dce112_clk_src_construct(clk_src, ctx, bios, id, regs, cs_shift, cs_mask); + + clk_src->base.funcs = &dcn31_clk_src_funcs; + + return ret; +} + +bool dcn301_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) +{ + bool ret = dce112_clk_src_construct(clk_src, ctx, bios, id, regs, cs_shift, cs_mask); + + clk_src->base.funcs = &dcn3_clk_src_funcs; + + return ret; +} 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 000000000..aaf33c79b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h @@ -0,0 +1,329 @@ +/* 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_COMMON_REG_LIST_DCN2_0(index, pllid) \ + SRI(PIXCLK_RESYNC_CNTL, PHYPLL, pllid),\ + SRII(PHASE, DP_DTO, 0),\ + SRII(PHASE, DP_DTO, 1),\ + SRII(PHASE, DP_DTO, 2),\ + SRII(PHASE, DP_DTO, 3),\ + SRII(PHASE, DP_DTO, 4),\ + SRII(PHASE, DP_DTO, 5),\ + SRII(MODULO, DP_DTO, 0),\ + SRII(MODULO, DP_DTO, 1),\ + SRII(MODULO, DP_DTO, 2),\ + SRII(MODULO, DP_DTO, 3),\ + SRII(MODULO, DP_DTO, 4),\ + SRII(MODULO, DP_DTO, 5),\ + SRII(PIXEL_RATE_CNTL, OTG, 0),\ + SRII(PIXEL_RATE_CNTL, OTG, 1),\ + SRII(PIXEL_RATE_CNTL, OTG, 2),\ + SRII(PIXEL_RATE_CNTL, OTG, 3),\ + SRII(PIXEL_RATE_CNTL, OTG, 4),\ + SRII(PIXEL_RATE_CNTL, OTG, 5) + +#define CS_COMMON_REG_LIST_DCN201(index, pllid) \ + SRI(PIXCLK_RESYNC_CNTL, PHYPLL, pllid),\ + SRII(PHASE, DP_DTO, 0),\ + SRII(PHASE, DP_DTO, 1),\ + SRII(MODULO, DP_DTO, 0),\ + SRII(MODULO, DP_DTO, 1),\ + SRII(PIXEL_RATE_CNTL, OTG, 0),\ + SRII(PIXEL_RATE_CNTL, OTG, 1) + +#define CS_COMMON_REG_LIST_DCN2_1(index, pllid) \ + SRI(PIXCLK_RESYNC_CNTL, PHYPLL, pllid),\ + SRII(PHASE, DP_DTO, 0),\ + SRII(PHASE, DP_DTO, 1),\ + SRII(PHASE, DP_DTO, 2),\ + SRII(PHASE, DP_DTO, 3),\ + SRII(MODULO, DP_DTO, 0),\ + SRII(MODULO, DP_DTO, 1),\ + SRII(MODULO, DP_DTO, 2),\ + SRII(MODULO, DP_DTO, 3),\ + SRII(PIXEL_RATE_CNTL, OTG, 0),\ + SRII(PIXEL_RATE_CNTL, OTG, 1),\ + SRII(PIXEL_RATE_CNTL, OTG, 2),\ + SRII(PIXEL_RATE_CNTL, OTG, 3) + +#define CS_COMMON_REG_LIST_DCN3_0(index, pllid) \ + SRI(PIXCLK_RESYNC_CNTL, PHYPLL, pllid),\ + SRII(PHASE, DP_DTO, 0),\ + SRII(PHASE, DP_DTO, 1),\ + SRII(PHASE, DP_DTO, 2),\ + SRII(PHASE, DP_DTO, 3),\ + SRII(MODULO, DP_DTO, 0),\ + SRII(MODULO, DP_DTO, 1),\ + SRII(MODULO, DP_DTO, 2),\ + SRII(MODULO, DP_DTO, 3),\ + SRII(PIXEL_RATE_CNTL, OTG, 0),\ + SRII(PIXEL_RATE_CNTL, OTG, 1),\ + SRII(PIXEL_RATE_CNTL, OTG, 2),\ + SRII(PIXEL_RATE_CNTL, OTG, 3) + +#define CS_COMMON_REG_LIST_DCN3_01(index, pllid) \ + SRI(PIXCLK_RESYNC_CNTL, PHYPLL, pllid),\ + SRII(PHASE, DP_DTO, 0),\ + SRII(PHASE, DP_DTO, 1),\ + SRII(PHASE, DP_DTO, 2),\ + SRII(PHASE, DP_DTO, 3),\ + SRII(MODULO, DP_DTO, 0),\ + SRII(MODULO, DP_DTO, 1),\ + SRII(MODULO, DP_DTO, 2),\ + SRII(MODULO, DP_DTO, 3),\ + SRII(PIXEL_RATE_CNTL, OTG, 0),\ + SRII(PIXEL_RATE_CNTL, OTG, 1),\ + SRII(PIXEL_RATE_CNTL, OTG, 2),\ + SRII(PIXEL_RATE_CNTL, OTG, 3) + +#define CS_COMMON_REG_LIST_DCN3_02(index, pllid) \ + SRI(PIXCLK_RESYNC_CNTL, PHYPLL, pllid),\ + SRII(PHASE, DP_DTO, 0),\ + SRII(PHASE, DP_DTO, 1),\ + SRII(PHASE, DP_DTO, 2),\ + SRII(PHASE, DP_DTO, 3),\ + SRII(PHASE, DP_DTO, 4),\ + SRII(MODULO, DP_DTO, 0),\ + SRII(MODULO, DP_DTO, 1),\ + SRII(MODULO, DP_DTO, 2),\ + SRII(MODULO, DP_DTO, 3),\ + SRII(MODULO, DP_DTO, 4),\ + SRII(PIXEL_RATE_CNTL, OTG, 0),\ + SRII(PIXEL_RATE_CNTL, OTG, 1),\ + SRII(PIXEL_RATE_CNTL, OTG, 2),\ + SRII(PIXEL_RATE_CNTL, OTG, 3),\ + SRII(PIXEL_RATE_CNTL, OTG, 4) + +#define CS_COMMON_REG_LIST_DCN3_03(index, pllid) \ + SRI(PIXCLK_RESYNC_CNTL, PHYPLL, pllid),\ + SRII(PHASE, DP_DTO, 0),\ + SRII(PHASE, DP_DTO, 1),\ + SRII(MODULO, DP_DTO, 0),\ + SRII(MODULO, DP_DTO, 1),\ + SRII(PIXEL_RATE_CNTL, OTG, 0),\ + SRII(PIXEL_RATE_CNTL, OTG, 1) + +#define CS_COMMON_MASK_SH_LIST_DCN2_0(mask_sh)\ + CS_SF(DP_DTO0_PHASE, DP_DTO0_PHASE, mask_sh),\ + CS_SF(DP_DTO0_MODULO, DP_DTO0_MODULO, mask_sh),\ + CS_SF(PHYPLLA_PIXCLK_RESYNC_CNTL, PHYPLLA_DCCG_DEEP_COLOR_CNTL, mask_sh),\ + CS_SF(OTG0_PIXEL_RATE_CNTL, DP_DTO0_ENABLE, mask_sh) + +#define CS_COMMON_MASK_SH_LIST_DCN3_1_4(mask_sh)\ + CS_COMMON_MASK_SH_LIST_DCN2_0(mask_sh),\ + CS_SF(OTG0_PIXEL_RATE_CNTL, PIPE0_DTO_SRC_SEL, mask_sh), + +#define CS_COMMON_MASK_SH_LIST_DCN3_2(mask_sh)\ + CS_COMMON_MASK_SH_LIST_DCN2_0(mask_sh),\ + CS_SF(OTG0_PIXEL_RATE_CNTL, PIPE0_DTO_SRC_SEL, mask_sh) + +#define CS_COMMON_REG_LIST_DCN1_0(index, pllid) \ + SRI(PIXCLK_RESYNC_CNTL, PHYPLL, pllid),\ + SRII(PHASE, DP_DTO, 0),\ + SRII(PHASE, DP_DTO, 1),\ + SRII(PHASE, DP_DTO, 2),\ + SRII(PHASE, DP_DTO, 3),\ + SRII(MODULO, DP_DTO, 0),\ + SRII(MODULO, DP_DTO, 1),\ + SRII(MODULO, DP_DTO, 2),\ + SRII(MODULO, DP_DTO, 3),\ + SRII(PIXEL_RATE_CNTL, OTG, 0), \ + SRII(PIXEL_RATE_CNTL, OTG, 1), \ + SRII(PIXEL_RATE_CNTL, OTG, 2), \ + SRII(PIXEL_RATE_CNTL, OTG, 3) + +#define CS_COMMON_MASK_SH_LIST_DCN1_0(mask_sh)\ + CS_SF(DP_DTO0_PHASE, DP_DTO0_PHASE, mask_sh),\ + CS_SF(DP_DTO0_MODULO, DP_DTO0_MODULO, mask_sh),\ + CS_SF(PHYPLLA_PIXCLK_RESYNC_CNTL, PHYPLLA_DCCG_DEEP_COLOR_CNTL, mask_sh),\ + CS_SF(OTG0_PIXEL_RATE_CNTL, DP_DTO0_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; \ + type DP_DTO0_PHASE; \ + type DP_DTO0_MODULO; \ + type DP_DTO0_ENABLE; + +#if defined(CONFIG_DRM_AMD_DC_DCN) +#define CS_REG_FIELD_LIST_DCN32(type) \ + type PIPE0_DTO_SRC_SEL; +#endif + +struct dce110_clk_src_shift { + CS_REG_FIELD_LIST(uint8_t) +#if defined(CONFIG_DRM_AMD_DC_DCN) + CS_REG_FIELD_LIST_DCN32(uint8_t) +#endif +}; + +struct dce110_clk_src_mask{ + CS_REG_FIELD_LIST(uint32_t) +#if defined(CONFIG_DRM_AMD_DC_DCN) + CS_REG_FIELD_LIST_DCN32(uint32_t) +#endif +}; + +struct dce110_clk_src_regs { + uint32_t RESYNC_CNTL; + uint32_t PIXCLK_RESYNC_CNTL; + uint32_t PLL_CNTL; + + /* below are for DTO. + * todo: should probably use different struct to not waste space + */ + uint32_t PHASE[MAX_PIPES]; + uint32_t MODULO[MAX_PIPES]; + uint32_t PIXEL_RATE_CNTL[MAX_PIPES]; +}; + +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; + struct spread_spectrum_data *lvds_ss_params; + uint32_t lvds_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); + +bool dce112_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); + +bool dcn20_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); + +bool dcn3_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); + +bool dcn301_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); + +bool dcn31_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); + +/* this table is use to find *1.001 and /1.001 pixel rates from non-precise pixel rate */ +struct pixel_rate_range_table_entry { + unsigned int range_min_khz; + unsigned int range_max_khz; + unsigned int target_pixel_rate_khz; + unsigned short mult_factor; + unsigned short div_factor; +}; + +extern const struct pixel_rate_range_table_entry video_optimized_pixel_rates[]; +const struct pixel_rate_range_table_entry *look_up_in_video_optimized_rate_tlb( + unsigned int pixel_rate_khz); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c new file mode 100644 index 000000000..d3cc5ec46 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c @@ -0,0 +1,1193 @@ +/* + * 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 "core_types.h" +#include "link_encoder.h" +#include "dce_dmcu.h" +#include "dm_services.h" +#include "reg_helper.h" +#include "fixed31_32.h" +#include "dc.h" + +#define TO_DCE_DMCU(dmcu)\ + container_of(dmcu, struct dce_dmcu, base) + +#define REG(reg) \ + (dmcu_dce->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + dmcu_dce->dmcu_shift->field_name, dmcu_dce->dmcu_mask->field_name + +#define CTX \ + dmcu_dce->base.ctx + +/* PSR related commands */ +#define PSR_ENABLE 0x20 +#define PSR_EXIT 0x21 +#define PSR_SET 0x23 +#define PSR_SET_WAITLOOP 0x31 +#define MCP_INIT_DMCU 0x88 +#define MCP_INIT_IRAM 0x89 +#define MCP_SYNC_PHY_LOCK 0x90 +#define MCP_SYNC_PHY_UNLOCK 0x91 +#define MCP_BL_SET_PWM_FRAC 0x6A /* Enable or disable Fractional PWM */ +#define CRC_WIN_NOTIFY 0x92 +#define CRC_STOP_UPDATE 0x93 +#define MCP_SEND_EDID_CEA 0xA0 +#define EDID_CEA_CMD_ACK 1 +#define EDID_CEA_CMD_NACK 2 +#define MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK 0x00000001L + +// PSP FW version +#define mmMP0_SMN_C2PMSG_58 0x1607A + +//Register access policy version +#define mmMP0_SMN_C2PMSG_91 0x1609B + +static const uint32_t abm_gain_stepsize = 0x0060; + +static bool dce_dmcu_init(struct dmcu *dmcu) +{ + // Do nothing + return true; +} + +static bool dce_dmcu_load_iram(struct dmcu *dmcu, + unsigned int start_offset, + const char *src, + unsigned int bytes) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int count = 0; + + /* Enable write access to IRAM */ + REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, + IRAM_HOST_ACCESS_EN, 1, + IRAM_WR_ADDR_AUTO_INC, 1); + + REG_WAIT(DCI_MEM_PWR_STATUS, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); + + REG_WRITE(DMCU_IRAM_WR_CTRL, start_offset); + + for (count = 0; count < bytes; count++) + REG_WRITE(DMCU_IRAM_WR_DATA, src[count]); + + /* Disable write access to IRAM to allow dynamic sleep state */ + REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, + IRAM_HOST_ACCESS_EN, 0, + IRAM_WR_ADDR_AUTO_INC, 0); + + return true; +} + +static void dce_get_dmcu_psr_state(struct dmcu *dmcu, enum dc_psr_state *state) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + uint32_t psr_state_offset = 0xf0; + + /* Enable write access to IRAM */ + REG_UPDATE(DMCU_RAM_ACCESS_CTRL, IRAM_HOST_ACCESS_EN, 1); + + REG_WAIT(DCI_MEM_PWR_STATUS, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); + + /* Write address to IRAM_RD_ADDR in DMCU_IRAM_RD_CTRL */ + REG_WRITE(DMCU_IRAM_RD_CTRL, psr_state_offset); + + /* Read data from IRAM_RD_DATA in DMCU_IRAM_RD_DATA*/ + *state = (enum dc_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); +} + +static void dce_dmcu_set_psr_enable(struct dmcu *dmcu, bool enable, bool wait) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + + unsigned int retryCount; + enum dc_psr_state state = PSR_STATE0; + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + dmcu_wait_reg_ready_interval, + dmcu_max_retry_on_wait_reg_ready); + + /* 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); + if (wait == true) { + for (retryCount = 0; retryCount <= 100; retryCount++) { + dce_get_dmcu_psr_state(dmcu, &state); + if (enable) { + if (state != PSR_STATE0) + break; + } else { + if (state == PSR_STATE0) + break; + } + udelay(10); + } + } +} + +static bool dce_dmcu_setup_psr(struct dmcu *dmcu, + struct dc_link *link, + struct psr_context *psr_context) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + + union dce_dmcu_psr_config_data_reg1 masterCmdData1; + union dce_dmcu_psr_config_data_reg2 masterCmdData2; + union dce_dmcu_psr_config_data_reg3 masterCmdData3; + + link->link_enc->funcs->psr_program_dp_dphy_fast_training(link->link_enc, + psr_context->psrExitLinkTrainingRequired); + + /* 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: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 1); + break; + case 2: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN2_INT_TO_UC_EN, 1); + break; + case 3: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN3_INT_TO_UC_EN, 1); + break; + case 4: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN4_INT_TO_UC_EN, 1); + break; + case 5: + /* CZ/NL only has 4 CRTC!! + * really valid. + * There is no interrupt enable mask for these instances. + */ + break; + case 6: + /* 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: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 1); + break; + } + + link->link_enc->funcs->psr_program_secondary_packet(link->link_enc, + psr_context->sdpTransmitLineNumDeadline); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + dmcu_wait_reg_ready_interval, + dmcu_max_retry_on_wait_reg_ready); + + /* 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(dmcu->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(dmcu->ctx, REG(MASTER_COMM_DATA_REG2), + masterCmdData2.u32All); + + masterCmdData3.u32All = 0; + masterCmdData3.bits.psr_level = psr_context->psr_level.u32all; + dm_write_reg(dmcu->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); + + return true; +} + +static bool dce_is_dmcu_initialized(struct dmcu *dmcu) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int dmcu_uc_reset; + + /* microcontroller is not running */ + REG_GET(DMCU_STATUS, UC_IN_RESET, &dmcu_uc_reset); + + /* DMCU is not running */ + if (dmcu_uc_reset) + return false; + + return true; +} + +static void dce_psr_wait_loop( + struct dmcu *dmcu, + unsigned int wait_loop_number) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + union dce_dmcu_psr_config_data_wait_loop_reg1 masterCmdData1; + + if (dmcu->cached_wait_loop_number == wait_loop_number) + return; + + /* DMCU is not running */ + if (!dce_is_dmcu_initialized(dmcu)) + return; + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); + + masterCmdData1.u32 = 0; + masterCmdData1.bits.wait_loop = wait_loop_number; + dmcu->cached_wait_loop_number = wait_loop_number; + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG1), masterCmdData1.u32); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, PSR_SET_WAITLOOP); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); +} + +static void dce_get_psr_wait_loop( + struct dmcu *dmcu, unsigned int *psr_wait_loop_number) +{ + *psr_wait_loop_number = dmcu->cached_wait_loop_number; + return; +} + +static void dcn10_get_dmcu_version(struct dmcu *dmcu) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + uint32_t dmcu_version_offset = 0xf1; + + /* Enable write access to IRAM */ + REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, + IRAM_HOST_ACCESS_EN, 1, + IRAM_RD_ADDR_AUTO_INC, 1); + + REG_WAIT(DMU_MEM_PWR_CNTL, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); + + /* Write address to IRAM_RD_ADDR and read from DATA register */ + REG_WRITE(DMCU_IRAM_RD_CTRL, dmcu_version_offset); + dmcu->dmcu_version.interface_version = REG_READ(DMCU_IRAM_RD_DATA); + dmcu->dmcu_version.abm_version = REG_READ(DMCU_IRAM_RD_DATA); + dmcu->dmcu_version.psr_version = REG_READ(DMCU_IRAM_RD_DATA); + dmcu->dmcu_version.build_version = ((REG_READ(DMCU_IRAM_RD_DATA) << 8) | + REG_READ(DMCU_IRAM_RD_DATA)); + + /* Disable write access to IRAM to allow dynamic sleep state */ + REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, + IRAM_HOST_ACCESS_EN, 0, + IRAM_RD_ADDR_AUTO_INC, 0); +} + +static void dcn10_dmcu_enable_fractional_pwm(struct dmcu *dmcu, + uint32_t fractional_pwm) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + /* Wait until microcontroller is ready to process interrupt */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); + + /* Set PWM fractional enable/disable */ + REG_WRITE(MASTER_COMM_DATA_REG1, fractional_pwm); + + /* Set command to enable or disable fractional PWM microcontroller */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, + MCP_BL_SET_PWM_FRAC); + + /* Notify microcontroller of new command */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + /* Ensure command has been executed before continuing */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); +} + +static bool dcn10_dmcu_init(struct dmcu *dmcu) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + const struct dc_config *config = &dmcu->ctx->dc->config; + bool status = false; + struct dc_context *ctx = dmcu->ctx; + unsigned int i; + // 5 4 3 2 1 0 + // F E D C B A - bit 0 is A, bit 5 is F + unsigned int tx_interrupt_mask = 0; + + PERF_TRACE(); + /* Definition of DC_DMCU_SCRATCH + * 0 : firmare not loaded + * 1 : PSP load DMCU FW but not initialized + * 2 : Firmware already initialized + */ + dmcu->dmcu_state = REG_READ(DC_DMCU_SCRATCH); + + for (i = 0; i < ctx->dc->link_count; i++) { + if (ctx->dc->links[i]->link_enc->features.flags.bits.DP_IS_USB_C) { + if (ctx->dc->links[i]->link_enc->transmitter >= TRANSMITTER_UNIPHY_A && + ctx->dc->links[i]->link_enc->transmitter <= TRANSMITTER_UNIPHY_F) { + tx_interrupt_mask |= 1 << ctx->dc->links[i]->link_enc->transmitter; + } + } + } + + switch (dmcu->dmcu_state) { + case DMCU_UNLOADED: + status = false; + break; + case DMCU_LOADED_UNINITIALIZED: + /* Wait until microcontroller is ready to process interrupt */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); + + /* Set initialized ramping boundary value */ + REG_WRITE(MASTER_COMM_DATA_REG1, 0xFFFF); + + /* Set backlight ramping stepsize */ + REG_WRITE(MASTER_COMM_DATA_REG2, abm_gain_stepsize); + + REG_WRITE(MASTER_COMM_DATA_REG3, tx_interrupt_mask); + + /* Set command to initialize microcontroller */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, + MCP_INIT_DMCU); + + /* Notify microcontroller of new command */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + /* Ensure command has been executed before continuing */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); + + // Check state is initialized + dmcu->dmcu_state = REG_READ(DC_DMCU_SCRATCH); + + // If microcontroller is not in running state, fail + if (dmcu->dmcu_state == DMCU_RUNNING) { + /* Retrieve and cache the DMCU firmware version. */ + dcn10_get_dmcu_version(dmcu); + + /* Initialize DMCU to use fractional PWM or not */ + dcn10_dmcu_enable_fractional_pwm(dmcu, + (config->disable_fractional_pwm == false) ? 1 : 0); + status = true; + } else { + status = false; + } + + break; + case DMCU_RUNNING: + status = true; + break; + default: + status = false; + break; + } + + PERF_TRACE(); + return status; +} + +static bool dcn21_dmcu_init(struct dmcu *dmcu) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + uint32_t dmcub_psp_version = REG_READ(DMCUB_SCRATCH15); + + if (dmcu->auto_load_dmcu && dmcub_psp_version == 0) { + return false; + } + + return dcn10_dmcu_init(dmcu); +} + +static bool dcn10_dmcu_load_iram(struct dmcu *dmcu, + unsigned int start_offset, + const char *src, + unsigned int bytes) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int count = 0; + + /* If microcontroller is not running, do nothing */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return false; + + /* Enable write access to IRAM */ + REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, + IRAM_HOST_ACCESS_EN, 1, + IRAM_WR_ADDR_AUTO_INC, 1); + + REG_WAIT(DMU_MEM_PWR_CNTL, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); + + REG_WRITE(DMCU_IRAM_WR_CTRL, start_offset); + + for (count = 0; count < bytes; count++) + REG_WRITE(DMCU_IRAM_WR_DATA, src[count]); + + /* Disable write access to IRAM to allow dynamic sleep state */ + REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, + IRAM_HOST_ACCESS_EN, 0, + IRAM_WR_ADDR_AUTO_INC, 0); + + /* Wait until microcontroller is ready to process interrupt */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); + + /* Set command to signal IRAM is loaded and to initialize IRAM */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, + MCP_INIT_IRAM); + + /* Notify microcontroller of new command */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + /* Ensure command has been executed before continuing */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); + + return true; +} + +static void dcn10_get_dmcu_psr_state(struct dmcu *dmcu, enum dc_psr_state *state) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + uint32_t psr_state_offset = 0xf0; + + /* If microcontroller is not running, do nothing */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return; + + /* Enable write access to IRAM */ + REG_UPDATE(DMCU_RAM_ACCESS_CTRL, IRAM_HOST_ACCESS_EN, 1); + + REG_WAIT(DMU_MEM_PWR_CNTL, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); + + /* Write address to IRAM_RD_ADDR in DMCU_IRAM_RD_CTRL */ + REG_WRITE(DMCU_IRAM_RD_CTRL, psr_state_offset); + + /* Read data from IRAM_RD_DATA in DMCU_IRAM_RD_DATA*/ + *state = (enum dc_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); +} + +static void dcn10_dmcu_set_psr_enable(struct dmcu *dmcu, bool enable, bool wait) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + + unsigned int retryCount; + enum dc_psr_state state = PSR_STATE0; + + /* If microcontroller is not running, do nothing */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return; + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + dmcu_wait_reg_ready_interval, + dmcu_max_retry_on_wait_reg_ready); + + /* 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); + + /* Below loops 1000 x 500us = 500 ms. + * Exit PSR may need to wait 1-2 frames to power up. Timeout after at + * least a few frames. Should never hit the max retry assert below. + */ + if (wait == true) { + for (retryCount = 0; retryCount <= 1000; retryCount++) { + dcn10_get_dmcu_psr_state(dmcu, &state); + if (enable) { + if (state != PSR_STATE0) + break; + } else { + if (state == PSR_STATE0) + break; + } + udelay(500); + } + + /* assert if max retry hit */ + if (retryCount >= 1000) + ASSERT(0); + } +} + +static bool dcn10_dmcu_setup_psr(struct dmcu *dmcu, + struct dc_link *link, + struct psr_context *psr_context) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + + union dce_dmcu_psr_config_data_reg1 masterCmdData1; + union dce_dmcu_psr_config_data_reg2 masterCmdData2; + union dce_dmcu_psr_config_data_reg3 masterCmdData3; + + /* If microcontroller is not running, do nothing */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return false; + + link->link_enc->funcs->psr_program_dp_dphy_fast_training(link->link_enc, + psr_context->psrExitLinkTrainingRequired); + + /* 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: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 1); + break; + case 2: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN2_INT_TO_UC_EN, 1); + break; + case 3: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN3_INT_TO_UC_EN, 1); + break; + case 4: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN4_INT_TO_UC_EN, 1); + break; + case 5: + /* CZ/NL only has 4 CRTC!! + * really valid. + * There is no interrupt enable mask for these instances. + */ + break; + case 6: + /* 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: + REG_UPDATE(DMCU_INTERRUPT_TO_UC_EN_MASK, + STATIC_SCREEN1_INT_TO_UC_EN, 1); + break; + } + + link->link_enc->funcs->psr_program_secondary_packet(link->link_enc, + psr_context->sdpTransmitLineNumDeadline); + + if (psr_context->allow_smu_optimizations) + REG_UPDATE(SMU_INTERRUPT_CONTROL, DC_SMU_INT_ENABLE, 1); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + dmcu_wait_reg_ready_interval, + dmcu_max_retry_on_wait_reg_ready); + + /* 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; + masterCmdData1.bits.allow_smu_optimizations = psr_context->allow_smu_optimizations; + dm_write_reg(dmcu->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(dmcu->ctx, REG(MASTER_COMM_DATA_REG2), + masterCmdData2.u32All); + + masterCmdData3.u32All = 0; + masterCmdData3.bits.psr_level = psr_context->psr_level.u32all; + dm_write_reg(dmcu->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); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); + + return true; +} + +static void dcn10_psr_wait_loop( + struct dmcu *dmcu, + unsigned int wait_loop_number) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + union dce_dmcu_psr_config_data_wait_loop_reg1 masterCmdData1; + + /* If microcontroller is not running, do nothing */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return; + + if (wait_loop_number != 0) { + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); + + masterCmdData1.u32 = 0; + masterCmdData1.bits.wait_loop = wait_loop_number; + dmcu->cached_wait_loop_number = wait_loop_number; + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG1), masterCmdData1.u32); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, PSR_SET_WAITLOOP); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + } +} + +static void dcn10_get_psr_wait_loop( + struct dmcu *dmcu, unsigned int *psr_wait_loop_number) +{ + *psr_wait_loop_number = dmcu->cached_wait_loop_number; + return; +} + +static bool dcn10_is_dmcu_initialized(struct dmcu *dmcu) +{ + /* microcontroller is not running */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return false; + return true; +} + + + +static bool dcn20_lock_phy(struct dmcu *dmcu) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + /* If microcontroller is not running, do nothing */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return false; + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, MCP_SYNC_PHY_LOCK); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); + + return true; +} + +static bool dcn20_unlock_phy(struct dmcu *dmcu) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + /* If microcontroller is not running, do nothing */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return false; + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, MCP_SYNC_PHY_UNLOCK); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); + + return true; +} + +static bool dcn10_send_edid_cea(struct dmcu *dmcu, + int offset, + int total_length, + uint8_t *data, + int length) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + uint32_t header, data1, data2; + + /* If microcontroller is not running, do nothing */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return false; + + if (length > 8 || length <= 0) + return false; + + header = ((uint32_t)offset & 0xFFFF) << 16 | (total_length & 0xFFFF); + data1 = (((uint32_t)data[0]) << 24) | (((uint32_t)data[1]) << 16) | + (((uint32_t)data[2]) << 8) | ((uint32_t)data[3]); + data2 = (((uint32_t)data[4]) << 24) | (((uint32_t)data[5]) << 16) | + (((uint32_t)data[6]) << 8) | ((uint32_t)data[7]); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, MCP_SEND_EDID_CEA); + + REG_WRITE(MASTER_COMM_DATA_REG1, header); + REG_WRITE(MASTER_COMM_DATA_REG2, data1); + REG_WRITE(MASTER_COMM_DATA_REG3, data2); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); + + return true; +} + +static bool dcn10_get_scp_results(struct dmcu *dmcu, + uint32_t *cmd, + uint32_t *data1, + uint32_t *data2, + uint32_t *data3) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + + /* If microcontroller is not running, do nothing */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return false; + + *cmd = REG_READ(SLAVE_COMM_CMD_REG); + *data1 = REG_READ(SLAVE_COMM_DATA_REG1); + *data2 = REG_READ(SLAVE_COMM_DATA_REG2); + *data3 = REG_READ(SLAVE_COMM_DATA_REG3); + + /* clear SCP interrupt */ + REG_UPDATE(SLAVE_COMM_CNTL_REG, SLAVE_COMM_INTERRUPT, 0); + + return true; +} + +static bool dcn10_recv_amd_vsdb(struct dmcu *dmcu, + int *version, + int *min_frame_rate, + int *max_frame_rate) +{ + uint32_t data[4]; + int cmd, ack, len; + + if (!dcn10_get_scp_results(dmcu, &data[0], &data[1], &data[2], &data[3])) + return false; + + cmd = data[0] & 0x3FF; + len = (data[0] >> 10) & 0x3F; + ack = data[1]; + + if (cmd != MCP_SEND_EDID_CEA || ack != EDID_CEA_CMD_ACK || len != 12) + return false; + + if ((data[2] & 0xFF)) { + *version = (data[2] >> 8) & 0xFF; + *min_frame_rate = (data[3] >> 16) & 0xFFFF; + *max_frame_rate = data[3] & 0xFFFF; + return true; + } + + return false; +} + +static bool dcn10_recv_edid_cea_ack(struct dmcu *dmcu, int *offset) +{ + uint32_t data[4]; + int cmd, ack; + + if (!dcn10_get_scp_results(dmcu, + &data[0], &data[1], &data[2], &data[3])) + return false; + + cmd = data[0] & 0x3FF; + ack = data[1]; + + if (cmd != MCP_SEND_EDID_CEA) + return false; + + if (ack == EDID_CEA_CMD_ACK) + return true; + + *offset = data[2]; /* nack */ + return false; +} + + +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) +static void dcn10_forward_crc_window(struct dmcu *dmcu, + struct rect *rect, + struct otg_phy_mux *mux_mapping) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + unsigned int crc_start = 0, crc_end = 0, otg_phy_mux = 0; + int x_start, y_start, x_end, y_end; + + /* If microcontroller is not running, do nothing */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return; + + if (!rect) + return; + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + dmcu_wait_reg_ready_interval, + dmcu_max_retry_on_wait_reg_ready); + + x_start = rect->x; + y_start = rect->y; + x_end = x_start + rect->width; + y_end = y_start + rect->height; + + /* build up nitification data */ + crc_start = (((unsigned int) x_start) << 16) | y_start; + crc_end = (((unsigned int) x_end) << 16) | y_end; + otg_phy_mux = + (((unsigned int) mux_mapping->otg_output_num) << 16) | mux_mapping->phy_output_num; + + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG1), + crc_start); + + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG2), + crc_end); + + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG3), + otg_phy_mux); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, + CRC_WIN_NOTIFY); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); +} + +static void dcn10_stop_crc_win_update(struct dmcu *dmcu, + struct otg_phy_mux *mux_mapping) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int dmcu_max_retry_on_wait_reg_ready = 801; + unsigned int dmcu_wait_reg_ready_interval = 100; + unsigned int otg_phy_mux = 0; + + /* If microcontroller is not running, do nothing */ + if (dmcu->dmcu_state != DMCU_RUNNING) + return; + + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + dmcu_wait_reg_ready_interval, + dmcu_max_retry_on_wait_reg_ready); + + /* build up nitification data */ + otg_phy_mux = + (((unsigned int) mux_mapping->otg_output_num) << 16) | mux_mapping->phy_output_num; + + dm_write_reg(dmcu->ctx, REG(MASTER_COMM_DATA_REG1), + otg_phy_mux); + + /* setDMCUParam_Cmd */ + REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, + CRC_STOP_UPDATE); + + /* notifyDMCUMsg */ + REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); +} +#endif + +static const struct dmcu_funcs dce_funcs = { + .dmcu_init = dce_dmcu_init, + .load_iram = dce_dmcu_load_iram, + .set_psr_enable = dce_dmcu_set_psr_enable, + .setup_psr = dce_dmcu_setup_psr, + .get_psr_state = dce_get_dmcu_psr_state, + .set_psr_wait_loop = dce_psr_wait_loop, + .get_psr_wait_loop = dce_get_psr_wait_loop, + .is_dmcu_initialized = dce_is_dmcu_initialized +}; + +static const struct dmcu_funcs dcn10_funcs = { + .dmcu_init = dcn10_dmcu_init, + .load_iram = dcn10_dmcu_load_iram, + .set_psr_enable = dcn10_dmcu_set_psr_enable, + .setup_psr = dcn10_dmcu_setup_psr, + .get_psr_state = dcn10_get_dmcu_psr_state, + .set_psr_wait_loop = dcn10_psr_wait_loop, + .get_psr_wait_loop = dcn10_get_psr_wait_loop, + .send_edid_cea = dcn10_send_edid_cea, + .recv_amd_vsdb = dcn10_recv_amd_vsdb, + .recv_edid_cea_ack = dcn10_recv_edid_cea_ack, +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + .forward_crc_window = dcn10_forward_crc_window, + .stop_crc_win_update = dcn10_stop_crc_win_update, +#endif + .is_dmcu_initialized = dcn10_is_dmcu_initialized +}; + +static const struct dmcu_funcs dcn20_funcs = { + .dmcu_init = dcn10_dmcu_init, + .load_iram = dcn10_dmcu_load_iram, + .set_psr_enable = dcn10_dmcu_set_psr_enable, + .setup_psr = dcn10_dmcu_setup_psr, + .get_psr_state = dcn10_get_dmcu_psr_state, + .set_psr_wait_loop = dcn10_psr_wait_loop, + .get_psr_wait_loop = dcn10_get_psr_wait_loop, + .is_dmcu_initialized = dcn10_is_dmcu_initialized, + .lock_phy = dcn20_lock_phy, + .unlock_phy = dcn20_unlock_phy +}; + +static const struct dmcu_funcs dcn21_funcs = { + .dmcu_init = dcn21_dmcu_init, + .load_iram = dcn10_dmcu_load_iram, + .set_psr_enable = dcn10_dmcu_set_psr_enable, + .setup_psr = dcn10_dmcu_setup_psr, + .get_psr_state = dcn10_get_dmcu_psr_state, + .set_psr_wait_loop = dcn10_psr_wait_loop, + .get_psr_wait_loop = dcn10_get_psr_wait_loop, + .is_dmcu_initialized = dcn10_is_dmcu_initialized, + .lock_phy = dcn20_lock_phy, + .unlock_phy = dcn20_unlock_phy +}; + +static void dce_dmcu_construct( + struct dce_dmcu *dmcu_dce, + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask) +{ + struct dmcu *base = &dmcu_dce->base; + + base->ctx = ctx; + base->funcs = &dce_funcs; + base->cached_wait_loop_number = 0; + + dmcu_dce->regs = regs; + dmcu_dce->dmcu_shift = dmcu_shift; + dmcu_dce->dmcu_mask = dmcu_mask; +} + +static void dcn21_dmcu_construct( + struct dce_dmcu *dmcu_dce, + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask) +{ + uint32_t psp_version = 0; + + dce_dmcu_construct(dmcu_dce, ctx, regs, dmcu_shift, dmcu_mask); + + if (!IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) { + psp_version = dm_read_reg(ctx, mmMP0_SMN_C2PMSG_58); + dmcu_dce->base.auto_load_dmcu = ((psp_version & 0x00FF00FF) > 0x00110029); + dmcu_dce->base.psp_version = psp_version; + } +} + +struct dmcu *dce_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask) +{ + struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_KERNEL); + + if (dmcu_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_dmcu_construct( + dmcu_dce, ctx, regs, dmcu_shift, dmcu_mask); + + dmcu_dce->base.funcs = &dce_funcs; + + return &dmcu_dce->base; +} + +struct dmcu *dcn10_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask) +{ + struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_ATOMIC); + + if (dmcu_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_dmcu_construct( + dmcu_dce, ctx, regs, dmcu_shift, dmcu_mask); + + dmcu_dce->base.funcs = &dcn10_funcs; + + return &dmcu_dce->base; +} + +struct dmcu *dcn20_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask) +{ + struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_ATOMIC); + + if (dmcu_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_dmcu_construct( + dmcu_dce, ctx, regs, dmcu_shift, dmcu_mask); + + dmcu_dce->base.funcs = &dcn20_funcs; + + return &dmcu_dce->base; +} + +struct dmcu *dcn21_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask) +{ + struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_ATOMIC); + + if (dmcu_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dcn21_dmcu_construct( + dmcu_dce, ctx, regs, dmcu_shift, dmcu_mask); + + dmcu_dce->base.funcs = &dcn21_funcs; + + return &dmcu_dce->base; +} + +void dce_dmcu_destroy(struct dmcu **dmcu) +{ + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(*dmcu); + + kfree(dmcu_dce); + *dmcu = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h new file mode 100644 index 000000000..ff726b35e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h @@ -0,0 +1,331 @@ +/* + * 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_DMCU_H_ +#define _DCE_DMCU_H_ + +#include "dmcu.h" + +#define DMCU_COMMON_REG_LIST_DCE_BASE() \ + SR(DMCU_CTRL), \ + SR(DMCU_STATUS), \ + SR(DMCU_RAM_ACCESS_CTRL), \ + SR(DMCU_IRAM_WR_CTRL), \ + SR(DMCU_IRAM_WR_DATA), \ + 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(SLAVE_COMM_DATA_REG1), \ + SR(SLAVE_COMM_DATA_REG2), \ + SR(SLAVE_COMM_DATA_REG3), \ + SR(SLAVE_COMM_CMD_REG), \ + SR(DMCU_IRAM_RD_CTRL), \ + SR(DMCU_IRAM_RD_DATA), \ + SR(DMCU_INTERRUPT_TO_UC_EN_MASK), \ + SR(SMU_INTERRUPT_CONTROL), \ + SR(DC_DMCU_SCRATCH) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define DMCU_DCE60_REG_LIST() \ + SR(DMCU_CTRL), \ + SR(DMCU_STATUS), \ + SR(DMCU_RAM_ACCESS_CTRL), \ + SR(DMCU_IRAM_WR_CTRL), \ + SR(DMCU_IRAM_WR_DATA), \ + 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_IRAM_RD_CTRL), \ + SR(DMCU_IRAM_RD_DATA), \ + SR(DMCU_INTERRUPT_TO_UC_EN_MASK), \ + SR(DC_DMCU_SCRATCH) +#endif + +#define DMCU_DCE80_REG_LIST() \ + SR(DMCU_CTRL), \ + SR(DMCU_STATUS), \ + SR(DMCU_RAM_ACCESS_CTRL), \ + SR(DMCU_IRAM_WR_CTRL), \ + SR(DMCU_IRAM_WR_DATA), \ + 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_IRAM_RD_CTRL), \ + SR(DMCU_IRAM_RD_DATA), \ + SR(DMCU_INTERRUPT_TO_UC_EN_MASK), \ + SR(SMU_INTERRUPT_CONTROL), \ + SR(DC_DMCU_SCRATCH) + +#define DMCU_DCE110_COMMON_REG_LIST() \ + DMCU_COMMON_REG_LIST_DCE_BASE(), \ + SR(DCI_MEM_PWR_STATUS) + +#define DMCU_DCN10_REG_LIST()\ + DMCU_COMMON_REG_LIST_DCE_BASE(), \ + SR(DMU_MEM_PWR_CNTL) + +#define DMCU_DCN20_REG_LIST()\ + DMCU_DCN10_REG_LIST(), \ + SR(DMCUB_SCRATCH15) + +#define DMCU_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define DMCU_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + DMCU_SF(DMCU_CTRL, \ + DMCU_ENABLE, mask_sh), \ + DMCU_SF(DMCU_STATUS, \ + UC_IN_STOP_MODE, mask_sh), \ + DMCU_SF(DMCU_STATUS, \ + UC_IN_RESET, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_HOST_ACCESS_EN, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_WR_ADDR_AUTO_INC, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_RD_ADDR_AUTO_INC, mask_sh), \ + DMCU_SF(MASTER_COMM_CMD_REG, \ + MASTER_COMM_CMD_REG_BYTE0, mask_sh), \ + DMCU_SF(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, mask_sh), \ + DMCU_SF(SLAVE_COMM_CNTL_REG, SLAVE_COMM_INTERRUPT, mask_sh), \ + DMCU_SF(DMCU_INTERRUPT_TO_UC_EN_MASK, \ + STATIC_SCREEN1_INT_TO_UC_EN, mask_sh), \ + DMCU_SF(DMCU_INTERRUPT_TO_UC_EN_MASK, \ + STATIC_SCREEN2_INT_TO_UC_EN, mask_sh), \ + DMCU_SF(DMCU_INTERRUPT_TO_UC_EN_MASK, \ + STATIC_SCREEN3_INT_TO_UC_EN, mask_sh), \ + DMCU_SF(DMCU_INTERRUPT_TO_UC_EN_MASK, \ + STATIC_SCREEN4_INT_TO_UC_EN, mask_sh), \ + DMCU_SF(SMU_INTERRUPT_CONTROL, DC_SMU_INT_ENABLE, mask_sh) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define DMCU_MASK_SH_LIST_DCE60(mask_sh) \ + DMCU_SF(DMCU_CTRL, \ + DMCU_ENABLE, mask_sh), \ + DMCU_SF(DMCU_STATUS, \ + UC_IN_STOP_MODE, mask_sh), \ + DMCU_SF(DMCU_STATUS, \ + UC_IN_RESET, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_HOST_ACCESS_EN, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_WR_ADDR_AUTO_INC, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_RD_ADDR_AUTO_INC, mask_sh), \ + DMCU_SF(MASTER_COMM_CMD_REG, \ + MASTER_COMM_CMD_REG_BYTE0, mask_sh), \ + DMCU_SF(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, mask_sh) +#endif + +#define DMCU_MASK_SH_LIST_DCE80(mask_sh) \ + DMCU_SF(DMCU_CTRL, \ + DMCU_ENABLE, mask_sh), \ + DMCU_SF(DMCU_STATUS, \ + UC_IN_STOP_MODE, mask_sh), \ + DMCU_SF(DMCU_STATUS, \ + UC_IN_RESET, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_HOST_ACCESS_EN, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_WR_ADDR_AUTO_INC, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_RD_ADDR_AUTO_INC, mask_sh), \ + DMCU_SF(MASTER_COMM_CMD_REG, \ + MASTER_COMM_CMD_REG_BYTE0, mask_sh), \ + DMCU_SF(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, mask_sh), \ + DMCU_SF(SMU_INTERRUPT_CONTROL, DC_SMU_INT_ENABLE, mask_sh) + +#define DMCU_MASK_SH_LIST_DCE110(mask_sh) \ + DMCU_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + DMCU_SF(DCI_MEM_PWR_STATUS, \ + DMCU_IRAM_MEM_PWR_STATE, mask_sh) + +#define DMCU_MASK_SH_LIST_DCN10(mask_sh) \ + DMCU_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + DMCU_SF(DMU_MEM_PWR_CNTL, \ + DMCU_IRAM_MEM_PWR_STATE, mask_sh) + +#define DMCU_REG_FIELD_LIST(type) \ + type DMCU_IRAM_MEM_PWR_STATE; \ + type IRAM_HOST_ACCESS_EN; \ + type IRAM_WR_ADDR_AUTO_INC; \ + type IRAM_RD_ADDR_AUTO_INC; \ + type DMCU_ENABLE; \ + type UC_IN_STOP_MODE; \ + type UC_IN_RESET; \ + type MASTER_COMM_CMD_REG_BYTE0; \ + type MASTER_COMM_INTERRUPT; \ + type SLAVE_COMM_INTERRUPT; \ + type DPHY_RX_FAST_TRAINING_CAPABLE; \ + type DPHY_LOAD_BS_COUNT; \ + type STATIC_SCREEN1_INT_TO_UC_EN; \ + type STATIC_SCREEN2_INT_TO_UC_EN; \ + type STATIC_SCREEN3_INT_TO_UC_EN; \ + type STATIC_SCREEN4_INT_TO_UC_EN; \ + type DP_SEC_GSP0_LINE_NUM; \ + type DP_SEC_GSP0_PRIORITY; \ + type DC_SMU_INT_ENABLE + +struct dce_dmcu_shift { + DMCU_REG_FIELD_LIST(uint8_t); +}; + +struct dce_dmcu_mask { + DMCU_REG_FIELD_LIST(uint32_t); +}; + +struct dce_dmcu_registers { + uint32_t DMCU_CTRL; + uint32_t DMCU_STATUS; + uint32_t DMCU_RAM_ACCESS_CTRL; + uint32_t DCI_MEM_PWR_STATUS; + uint32_t DMU_MEM_PWR_CNTL; + uint32_t DMCU_IRAM_WR_CTRL; + uint32_t DMCU_IRAM_WR_DATA; + + 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 SLAVE_COMM_DATA_REG1; + uint32_t SLAVE_COMM_DATA_REG2; + uint32_t SLAVE_COMM_DATA_REG3; + uint32_t SLAVE_COMM_CMD_REG; + uint32_t SLAVE_COMM_CNTL_REG; + 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; + uint32_t DC_DMCU_SCRATCH; + uint32_t DMCUB_SCRATCH15; +}; + +struct dce_dmcu { + struct dmcu base; + const struct dce_dmcu_registers *regs; + const struct dce_dmcu_shift *dmcu_shift; + const struct dce_dmcu_mask *dmcu_mask; +}; + +/******************************************************************* + * 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 dce_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 allow_smu_optimizations:1; /*[31:31]*/ + } bits; + unsigned int u32All; +}; + +/******************************************************************* + * MASTER_COMM_DATA_REG2 + *******************************************************************/ +union dce_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 dce_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; +}; + +union dce_dmcu_psr_config_data_wait_loop_reg1 { + struct { + unsigned int wait_loop:16; /* [15:0] */ + unsigned int reserved:16; /* [31:16] */ + } bits; + unsigned int u32; +}; + +struct dmcu *dce_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask); + +struct dmcu *dcn10_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask); + +struct dmcu *dcn20_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask); + +struct dmcu *dcn21_dmcu_create( + struct dc_context *ctx, + const struct dce_dmcu_registers *regs, + const struct dce_dmcu_shift *dmcu_shift, + const struct dce_dmcu_mask *dmcu_mask); + +void dce_dmcu_destroy(struct dmcu **dmcu); + +#endif /* _DCE_ABM_H_ */ 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 000000000..4202fadb2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.c @@ -0,0 +1,219 @@ +/* + * 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_private.h" +#include "core_types.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 dc *dc, + struct pipe_ctx *pipe, + bool lock) +{ + uint32_t lock_val = lock ? 1 : 0; + uint32_t dcp_grph, scl, blnd, update_lock_mode, val; + struct dce_hwseq *hws = dc->hwseq; + + /* Not lock pipe when blank */ + if (lock && pipe->stream_res.tg->funcs->is_blanked && + pipe->stream_res.tg->funcs->is_blanked(pipe->stream_res.tg)) + return; + + val = REG_GET_4(BLND_V_UPDATE_LOCK[pipe->stream_res.tg->inst], + BLND_DCP_GRPH_V_UPDATE_LOCK, &dcp_grph, + BLND_SCL_V_UPDATE_LOCK, &scl, + BLND_BLND_V_UPDATE_LOCK, &blnd, + BLND_V_UPDATE_LOCK_MODE, &update_lock_mode); + + dcp_grph = lock_val; + scl = lock_val; + blnd = lock_val; + update_lock_mode = lock_val; + + REG_SET_2(BLND_V_UPDATE_LOCK[pipe->stream_res.tg->inst], val, + BLND_DCP_GRPH_V_UPDATE_LOCK, dcp_grph, + BLND_SCL_V_UPDATE_LOCK, scl); + + if (hws->masks->BLND_BLND_V_UPDATE_LOCK != 0) + REG_SET_2(BLND_V_UPDATE_LOCK[pipe->stream_res.tg->inst], val, + BLND_BLND_V_UPDATE_LOCK, blnd, + BLND_V_UPDATE_LOCK_MODE, update_lock_mode); + + if (hws->wa.blnd_crtc_trigger) { + if (!lock) { + uint32_t value = REG_READ(CRTC_H_BLANK_START_END[pipe->stream_res.tg->inst]); + REG_WRITE(CRTC_H_BLANK_START_END[pipe->stream_res.tg->inst], value); + } + } +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +void dce60_pipe_control_lock(struct dc *dc, + struct pipe_ctx *pipe, + bool lock) +{ + /* DCE6 has no BLND_V_UPDATE_LOCK register */ +} +#endif + +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(BLND_CONTROL[blnd_inst], + BLND_MODE, blnd_mode); + + if (hws->masks->BLND_ALPHA_MODE != 0) { + REG_UPDATE_3(BLND_CONTROL[blnd_inst], + BLND_FEEDTHROUGH_EN, feedthrough, + BLND_ALPHA_MODE, alpha_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 || clk_src->dp_clk_src) { + 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. clk_src id: %d, TG_inst: %d", + clk_src->id, tg_inst); + } +} + +/* Only use LUT for 8 bit formats */ +bool dce_use_lut(enum surface_pixel_format format) +{ + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + return true; + default: + return false; + } +} 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 000000000..a3fee929c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h @@ -0,0 +1,1210 @@ +/* + * 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 "dc_types.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 HSWEQ_DCN_PIXEL_RATE_REG_LIST(blk, inst) \ + SRII(PIXEL_RATE_CNTL, blk, inst), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, inst) + +#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_PIXEL_RATE_REG_LIST_201(blk) \ + SRII(PIXEL_RATE_CNTL, blk, 0), \ + SRII(PIXEL_RATE_CNTL, blk, 1) + +#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_PIXEL_RATE_REG_LIST_3(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_3(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_PHYPLL_REG_LIST_201(blk) \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 0), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 1) + +#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) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define HWSEQ_DCE6_REG_LIST() \ + HWSEQ_DCEF_REG_LIST_DCE8(), \ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC) +#endif + +#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_DCE120_REG_LIST() \ + HWSEQ_DCE10_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC), \ + HWSEQ_PHYPLL_REG_LIST(CRTC), \ + SR(DCHUB_FB_LOCATION),\ + SR(DCHUB_AGP_BASE),\ + SR(DCHUB_AGP_BOT),\ + SR(DCHUB_AGP_TOP) + +#define HWSEQ_VG20_REG_LIST() \ + HWSEQ_DCE120_REG_LIST(),\ + MMHUB_SR(MC_VM_XGMI_LFB_CNTL) + +#define HWSEQ_DCE112_REG_LIST() \ + HWSEQ_DCE10_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST(CRTC), \ + HWSEQ_PHYPLL_REG_LIST(CRTC) + +#define HWSEQ_DCN_REG_LIST()\ + SR(REFCLK_CNTL), \ + SR(DCHUBBUB_GLOBAL_TIMER_CNTL), \ + SR(DIO_MEM_PWR_CTRL), \ + SR(DCCG_GATE_DISABLE_CNTL), \ + SR(DCCG_GATE_DISABLE_CNTL2), \ + SR(DCFCLK_CNTL),\ + SR(DCFCLK_CNTL), \ + SR(DC_MEM_GLOBAL_PWR_REQ_CNTL) + + +#define MMHUB_DCN_REG_LIST()\ + /* todo: get these from GVM instead of reading registers ourselves */\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32),\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32),\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_START_ADDR_HI32),\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_START_ADDR_LO32),\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_END_ADDR_HI32),\ + MMHUB_SR(VM_CONTEXT0_PAGE_TABLE_END_ADDR_LO32),\ + MMHUB_SR(VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_HI32),\ + MMHUB_SR(VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_LO32),\ + MMHUB_SR(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB),\ + MMHUB_SR(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB),\ + MMHUB_SR(MC_VM_SYSTEM_APERTURE_LOW_ADDR),\ + MMHUB_SR(MC_VM_SYSTEM_APERTURE_HIGH_ADDR) + + +#define HWSEQ_DCN1_REG_LIST()\ + HWSEQ_DCN_REG_LIST(), \ + MMHUB_DCN_REG_LIST(), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 0), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 1), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 2), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 3), \ + SR(DCHUBBUB_SDPIF_FB_BASE),\ + SR(DCHUBBUB_SDPIF_FB_OFFSET),\ + SR(DCHUBBUB_SDPIF_AGP_BASE),\ + SR(DCHUBBUB_SDPIF_AGP_BOT),\ + SR(DCHUBBUB_SDPIF_AGP_TOP),\ + SR(DOMAIN0_PG_CONFIG), \ + SR(DOMAIN1_PG_CONFIG), \ + SR(DOMAIN2_PG_CONFIG), \ + SR(DOMAIN3_PG_CONFIG), \ + SR(DOMAIN4_PG_CONFIG), \ + SR(DOMAIN5_PG_CONFIG), \ + SR(DOMAIN6_PG_CONFIG), \ + SR(DOMAIN7_PG_CONFIG), \ + SR(DOMAIN0_PG_STATUS), \ + SR(DOMAIN1_PG_STATUS), \ + SR(DOMAIN2_PG_STATUS), \ + SR(DOMAIN3_PG_STATUS), \ + SR(DOMAIN4_PG_STATUS), \ + SR(DOMAIN5_PG_STATUS), \ + SR(DOMAIN6_PG_STATUS), \ + SR(DOMAIN7_PG_STATUS), \ + SR(D1VGA_CONTROL), \ + SR(D2VGA_CONTROL), \ + SR(D3VGA_CONTROL), \ + SR(D4VGA_CONTROL), \ + SR(VGA_TEST_CONTROL), \ + SR(DC_IP_REQUEST_CNTL) + +#define HWSEQ_DCN2_REG_LIST()\ + HWSEQ_DCN_REG_LIST(), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 0), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 1), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 2), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 3), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 4), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 5), \ + SR(MICROSECOND_TIME_BASE_DIV), \ + SR(MILLISECOND_TIME_BASE_DIV), \ + SR(DISPCLK_FREQ_CHANGE_CNTL), \ + SR(RBBMIF_TIMEOUT_DIS), \ + SR(RBBMIF_TIMEOUT_DIS_2), \ + SR(DCHUBBUB_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_VAL_B_A), \ + SR(DPP_TOP0_DPP_CRC_VAL_R_G), \ + SR(MPC_CRC_CTRL), \ + SR(MPC_CRC_RESULT_GB), \ + SR(MPC_CRC_RESULT_C), \ + SR(MPC_CRC_RESULT_AR), \ + SR(DOMAIN0_PG_CONFIG), \ + SR(DOMAIN1_PG_CONFIG), \ + SR(DOMAIN2_PG_CONFIG), \ + SR(DOMAIN3_PG_CONFIG), \ + SR(DOMAIN4_PG_CONFIG), \ + SR(DOMAIN5_PG_CONFIG), \ + SR(DOMAIN6_PG_CONFIG), \ + SR(DOMAIN7_PG_CONFIG), \ + SR(DOMAIN8_PG_CONFIG), \ + SR(DOMAIN9_PG_CONFIG), \ +/* SR(DOMAIN10_PG_CONFIG), Navi1x HUBP5 not powergate-able*/\ +/* SR(DOMAIN11_PG_CONFIG), Navi1x DPP5 is not powergate-able */\ + SR(DOMAIN16_PG_CONFIG), \ + SR(DOMAIN17_PG_CONFIG), \ + SR(DOMAIN18_PG_CONFIG), \ + SR(DOMAIN19_PG_CONFIG), \ + SR(DOMAIN20_PG_CONFIG), \ + SR(DOMAIN21_PG_CONFIG), \ + SR(DOMAIN0_PG_STATUS), \ + SR(DOMAIN1_PG_STATUS), \ + SR(DOMAIN2_PG_STATUS), \ + SR(DOMAIN3_PG_STATUS), \ + SR(DOMAIN4_PG_STATUS), \ + SR(DOMAIN5_PG_STATUS), \ + SR(DOMAIN6_PG_STATUS), \ + SR(DOMAIN7_PG_STATUS), \ + SR(DOMAIN8_PG_STATUS), \ + SR(DOMAIN9_PG_STATUS), \ + SR(DOMAIN10_PG_STATUS), \ + SR(DOMAIN11_PG_STATUS), \ + SR(DOMAIN16_PG_STATUS), \ + SR(DOMAIN17_PG_STATUS), \ + SR(DOMAIN18_PG_STATUS), \ + SR(DOMAIN19_PG_STATUS), \ + SR(DOMAIN20_PG_STATUS), \ + SR(DOMAIN21_PG_STATUS), \ + SR(D1VGA_CONTROL), \ + SR(D2VGA_CONTROL), \ + SR(D3VGA_CONTROL), \ + SR(D4VGA_CONTROL), \ + SR(D5VGA_CONTROL), \ + SR(D6VGA_CONTROL), \ + SR(DC_IP_REQUEST_CNTL) + +#define HWSEQ_DCN21_REG_LIST()\ + HWSEQ_DCN_REG_LIST(), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 0), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 1), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 2), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 3), \ + MMHUB_DCN_REG_LIST(), \ + SR(MICROSECOND_TIME_BASE_DIV), \ + SR(MILLISECOND_TIME_BASE_DIV), \ + SR(DISPCLK_FREQ_CHANGE_CNTL), \ + SR(RBBMIF_TIMEOUT_DIS), \ + SR(RBBMIF_TIMEOUT_DIS_2), \ + SR(DCHUBBUB_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_VAL_B_A), \ + SR(DPP_TOP0_DPP_CRC_VAL_R_G), \ + SR(MPC_CRC_CTRL), \ + SR(MPC_CRC_RESULT_GB), \ + SR(MPC_CRC_RESULT_C), \ + SR(MPC_CRC_RESULT_AR), \ + SR(DOMAIN0_PG_CONFIG), \ + SR(DOMAIN1_PG_CONFIG), \ + SR(DOMAIN2_PG_CONFIG), \ + SR(DOMAIN3_PG_CONFIG), \ + SR(DOMAIN4_PG_CONFIG), \ + SR(DOMAIN5_PG_CONFIG), \ + SR(DOMAIN6_PG_CONFIG), \ + SR(DOMAIN7_PG_CONFIG), \ + SR(DOMAIN16_PG_CONFIG), \ + SR(DOMAIN17_PG_CONFIG), \ + SR(DOMAIN18_PG_CONFIG), \ + SR(DOMAIN0_PG_STATUS), \ + SR(DOMAIN1_PG_STATUS), \ + SR(DOMAIN2_PG_STATUS), \ + SR(DOMAIN3_PG_STATUS), \ + SR(DOMAIN4_PG_STATUS), \ + SR(DOMAIN5_PG_STATUS), \ + SR(DOMAIN6_PG_STATUS), \ + SR(DOMAIN7_PG_STATUS), \ + SR(DOMAIN16_PG_STATUS), \ + SR(DOMAIN17_PG_STATUS), \ + SR(DOMAIN18_PG_STATUS), \ + SR(D1VGA_CONTROL), \ + SR(D2VGA_CONTROL), \ + SR(D3VGA_CONTROL), \ + SR(D4VGA_CONTROL), \ + SR(D5VGA_CONTROL), \ + SR(D6VGA_CONTROL), \ + SR(DC_IP_REQUEST_CNTL) + +#define HWSEQ_DCN201_REG_LIST()\ + HWSEQ_DCN_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST_201(OTG), \ + HWSEQ_PHYPLL_REG_LIST_201(OTG), \ + SR(MICROSECOND_TIME_BASE_DIV), \ + SR(MILLISECOND_TIME_BASE_DIV), \ + SR(DISPCLK_FREQ_CHANGE_CNTL), \ + SR(RBBMIF_TIMEOUT_DIS), \ + SR(RBBMIF_TIMEOUT_DIS_2), \ + SR(DCHUBBUB_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_VAL_B_A), \ + SR(DPP_TOP0_DPP_CRC_VAL_R_G), \ + SR(MPC_CRC_CTRL), \ + SR(MPC_CRC_RESULT_GB), \ + SR(MPC_CRC_RESULT_C), \ + SR(MPC_CRC_RESULT_AR), \ + SR(AZALIA_AUDIO_DTO), \ + SR(AZALIA_CONTROLLER_CLOCK_GATING), \ + MMHUB_SR(MC_VM_FB_LOCATION_BASE), \ + MMHUB_SR(MC_VM_FB_LOCATION_TOP), \ + MMHUB_SR(MC_VM_FB_OFFSET) + +#define HWSEQ_DCN30_REG_LIST()\ + HWSEQ_DCN2_REG_LIST(),\ + HWSEQ_DCN_REG_LIST(), \ + HWSEQ_PIXEL_RATE_REG_LIST_3(OTG), \ + HWSEQ_PHYPLL_REG_LIST_3(OTG), \ + SR(MICROSECOND_TIME_BASE_DIV), \ + SR(MILLISECOND_TIME_BASE_DIV), \ + SR(DISPCLK_FREQ_CHANGE_CNTL), \ + SR(RBBMIF_TIMEOUT_DIS), \ + SR(RBBMIF_TIMEOUT_DIS_2), \ + SR(DCHUBBUB_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_VAL_B_A), \ + SR(DPP_TOP0_DPP_CRC_VAL_R_G), \ + SR(MPC_CRC_CTRL), \ + SR(MPC_CRC_RESULT_GB), \ + SR(MPC_CRC_RESULT_C), \ + SR(MPC_CRC_RESULT_AR), \ + SR(AZALIA_AUDIO_DTO), \ + SR(AZALIA_CONTROLLER_CLOCK_GATING) + +#define HWSEQ_DCN301_REG_LIST()\ + SR(REFCLK_CNTL), \ + SR(DCHUBBUB_GLOBAL_TIMER_CNTL), \ + SR(DIO_MEM_PWR_CTRL), \ + SR(DCCG_GATE_DISABLE_CNTL), \ + SR(DCCG_GATE_DISABLE_CNTL2), \ + SR(DCFCLK_CNTL),\ + SR(DCFCLK_CNTL), \ + SR(DC_MEM_GLOBAL_PWR_REQ_CNTL), \ + SRII(PIXEL_RATE_CNTL, OTG, 0), \ + SRII(PIXEL_RATE_CNTL, OTG, 1),\ + SRII(PIXEL_RATE_CNTL, OTG, 2),\ + SRII(PIXEL_RATE_CNTL, OTG, 3),\ + SRII(PHYPLL_PIXEL_RATE_CNTL, OTG, 0),\ + SRII(PHYPLL_PIXEL_RATE_CNTL, OTG, 1),\ + SRII(PHYPLL_PIXEL_RATE_CNTL, OTG, 2),\ + SRII(PHYPLL_PIXEL_RATE_CNTL, OTG, 3),\ + SR(MICROSECOND_TIME_BASE_DIV), \ + SR(MILLISECOND_TIME_BASE_DIV), \ + SR(DISPCLK_FREQ_CHANGE_CNTL), \ + SR(RBBMIF_TIMEOUT_DIS), \ + SR(RBBMIF_TIMEOUT_DIS_2), \ + SR(DCHUBBUB_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_VAL_B_A), \ + SR(DPP_TOP0_DPP_CRC_VAL_R_G), \ + SR(MPC_CRC_CTRL), \ + SR(MPC_CRC_RESULT_GB), \ + SR(MPC_CRC_RESULT_C), \ + SR(MPC_CRC_RESULT_AR), \ + SR(DOMAIN0_PG_CONFIG), \ + SR(DOMAIN1_PG_CONFIG), \ + SR(DOMAIN2_PG_CONFIG), \ + SR(DOMAIN3_PG_CONFIG), \ + SR(DOMAIN4_PG_CONFIG), \ + SR(DOMAIN5_PG_CONFIG), \ + SR(DOMAIN6_PG_CONFIG), \ + SR(DOMAIN7_PG_CONFIG), \ + SR(DOMAIN16_PG_CONFIG), \ + SR(DOMAIN17_PG_CONFIG), \ + SR(DOMAIN18_PG_CONFIG), \ + SR(DOMAIN0_PG_STATUS), \ + SR(DOMAIN1_PG_STATUS), \ + SR(DOMAIN2_PG_STATUS), \ + SR(DOMAIN3_PG_STATUS), \ + SR(DOMAIN4_PG_STATUS), \ + SR(DOMAIN5_PG_STATUS), \ + SR(DOMAIN6_PG_STATUS), \ + SR(DOMAIN7_PG_STATUS), \ + SR(DOMAIN16_PG_STATUS), \ + SR(DOMAIN17_PG_STATUS), \ + SR(DOMAIN18_PG_STATUS), \ + SR(D1VGA_CONTROL), \ + SR(D2VGA_CONTROL), \ + SR(D3VGA_CONTROL), \ + SR(D4VGA_CONTROL), \ + SR(D5VGA_CONTROL), \ + SR(D6VGA_CONTROL), \ + SR(DC_IP_REQUEST_CNTL), \ + SR(AZALIA_AUDIO_DTO), \ + SR(AZALIA_CONTROLLER_CLOCK_GATING) + +#define HWSEQ_DCN302_REG_LIST()\ + HWSEQ_DCN_REG_LIST(), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 0), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 1), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 2), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 3), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 4), \ + SR(MICROSECOND_TIME_BASE_DIV), \ + SR(MILLISECOND_TIME_BASE_DIV), \ + SR(DISPCLK_FREQ_CHANGE_CNTL), \ + SR(RBBMIF_TIMEOUT_DIS), \ + SR(RBBMIF_TIMEOUT_DIS_2), \ + SR(DCHUBBUB_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_VAL_B_A), \ + SR(DPP_TOP0_DPP_CRC_VAL_R_G), \ + SR(MPC_CRC_CTRL), \ + SR(MPC_CRC_RESULT_GB), \ + SR(MPC_CRC_RESULT_C), \ + SR(MPC_CRC_RESULT_AR), \ + SR(DOMAIN0_PG_CONFIG), \ + SR(DOMAIN1_PG_CONFIG), \ + SR(DOMAIN2_PG_CONFIG), \ + SR(DOMAIN3_PG_CONFIG), \ + SR(DOMAIN4_PG_CONFIG), \ + SR(DOMAIN5_PG_CONFIG), \ + SR(DOMAIN6_PG_CONFIG), \ + SR(DOMAIN7_PG_CONFIG), \ + SR(DOMAIN8_PG_CONFIG), \ + SR(DOMAIN9_PG_CONFIG), \ + SR(DOMAIN16_PG_CONFIG), \ + SR(DOMAIN17_PG_CONFIG), \ + SR(DOMAIN18_PG_CONFIG), \ + SR(DOMAIN19_PG_CONFIG), \ + SR(DOMAIN20_PG_CONFIG), \ + SR(DOMAIN0_PG_STATUS), \ + SR(DOMAIN1_PG_STATUS), \ + SR(DOMAIN2_PG_STATUS), \ + SR(DOMAIN3_PG_STATUS), \ + SR(DOMAIN4_PG_STATUS), \ + SR(DOMAIN5_PG_STATUS), \ + SR(DOMAIN6_PG_STATUS), \ + SR(DOMAIN7_PG_STATUS), \ + SR(DOMAIN8_PG_STATUS), \ + SR(DOMAIN9_PG_STATUS), \ + SR(DOMAIN16_PG_STATUS), \ + SR(DOMAIN17_PG_STATUS), \ + SR(DOMAIN18_PG_STATUS), \ + SR(DOMAIN19_PG_STATUS), \ + SR(DOMAIN20_PG_STATUS), \ + SR(D1VGA_CONTROL), \ + SR(D2VGA_CONTROL), \ + SR(D3VGA_CONTROL), \ + SR(D4VGA_CONTROL), \ + SR(D5VGA_CONTROL), \ + SR(D6VGA_CONTROL), \ + SR(DC_IP_REQUEST_CNTL), \ + SR(AZALIA_AUDIO_DTO), \ + SR(AZALIA_CONTROLLER_CLOCK_GATING) + +#define HWSEQ_DCN303_REG_LIST() \ + HWSEQ_DCN_REG_LIST(), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 0), \ + HSWEQ_DCN_PIXEL_RATE_REG_LIST(OTG, 1), \ + SR(MICROSECOND_TIME_BASE_DIV), \ + SR(MILLISECOND_TIME_BASE_DIV), \ + SR(DISPCLK_FREQ_CHANGE_CNTL), \ + SR(RBBMIF_TIMEOUT_DIS), \ + SR(RBBMIF_TIMEOUT_DIS_2), \ + SR(DCHUBBUB_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_CTRL), \ + SR(DPP_TOP0_DPP_CRC_VAL_B_A), \ + SR(DPP_TOP0_DPP_CRC_VAL_R_G), \ + SR(MPC_CRC_CTRL), \ + SR(MPC_CRC_RESULT_GB), \ + SR(MPC_CRC_RESULT_C), \ + SR(MPC_CRC_RESULT_AR), \ + SR(D1VGA_CONTROL), \ + SR(D2VGA_CONTROL), \ + SR(D3VGA_CONTROL), \ + SR(D4VGA_CONTROL), \ + SR(D5VGA_CONTROL), \ + SR(D6VGA_CONTROL), \ + HWSEQ_PIXEL_RATE_REG_LIST_303(OTG), \ + HWSEQ_PHYPLL_REG_LIST_303(OTG), \ + SR(AZALIA_AUDIO_DTO), \ + SR(AZALIA_CONTROLLER_CLOCK_GATING), \ + SR(HPO_TOP_CLOCK_CONTROL) + +#define HWSEQ_PIXEL_RATE_REG_LIST_302(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) + +#define HWSEQ_PHYPLL_REG_LIST_302(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) + +#define HWSEQ_PIXEL_RATE_REG_LIST_303(blk) \ + SRII(PIXEL_RATE_CNTL, blk, 0), \ + SRII(PIXEL_RATE_CNTL, blk, 1) + +#define HWSEQ_PHYPLL_REG_LIST_303(blk) \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 0), \ + SRII(PHYPLL_PIXEL_RATE_CNTL, blk, 1) + +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]; + /*DCHUB*/ + uint32_t DCHUB_FB_LOCATION; + uint32_t DCHUB_AGP_BASE; + uint32_t DCHUB_AGP_BOT; + uint32_t DCHUB_AGP_TOP; + + uint32_t REFCLK_CNTL; + + uint32_t DCHUBBUB_GLOBAL_TIMER_CNTL; + uint32_t DCHUBBUB_SDPIF_FB_BASE; + uint32_t DCHUBBUB_SDPIF_FB_OFFSET; + uint32_t DCHUBBUB_SDPIF_AGP_BASE; + uint32_t DCHUBBUB_SDPIF_AGP_BOT; + uint32_t DCHUBBUB_SDPIF_AGP_TOP; + uint32_t DC_IP_REQUEST_CNTL; + uint32_t DOMAIN0_PG_CONFIG; + uint32_t DOMAIN1_PG_CONFIG; + uint32_t DOMAIN2_PG_CONFIG; + uint32_t DOMAIN3_PG_CONFIG; + uint32_t DOMAIN4_PG_CONFIG; + uint32_t DOMAIN5_PG_CONFIG; + uint32_t DOMAIN6_PG_CONFIG; + uint32_t DOMAIN7_PG_CONFIG; + uint32_t DOMAIN8_PG_CONFIG; + uint32_t DOMAIN9_PG_CONFIG; + uint32_t DOMAIN10_PG_CONFIG; + uint32_t DOMAIN11_PG_CONFIG; + uint32_t DOMAIN16_PG_CONFIG; + uint32_t DOMAIN17_PG_CONFIG; + uint32_t DOMAIN18_PG_CONFIG; + uint32_t DOMAIN19_PG_CONFIG; + uint32_t DOMAIN20_PG_CONFIG; + uint32_t DOMAIN21_PG_CONFIG; + uint32_t DOMAIN0_PG_STATUS; + uint32_t DOMAIN1_PG_STATUS; + uint32_t DOMAIN2_PG_STATUS; + uint32_t DOMAIN3_PG_STATUS; + uint32_t DOMAIN4_PG_STATUS; + uint32_t DOMAIN5_PG_STATUS; + uint32_t DOMAIN6_PG_STATUS; + uint32_t DOMAIN7_PG_STATUS; + uint32_t DOMAIN8_PG_STATUS; + uint32_t DOMAIN9_PG_STATUS; + uint32_t DOMAIN10_PG_STATUS; + uint32_t DOMAIN11_PG_STATUS; + uint32_t DOMAIN16_PG_STATUS; + uint32_t DOMAIN17_PG_STATUS; + uint32_t DOMAIN18_PG_STATUS; + uint32_t DOMAIN19_PG_STATUS; + uint32_t DOMAIN20_PG_STATUS; + uint32_t DOMAIN21_PG_STATUS; + uint32_t DIO_MEM_PWR_CTRL; + uint32_t DCCG_GATE_DISABLE_CNTL; + uint32_t DCCG_GATE_DISABLE_CNTL2; + uint32_t DCFCLK_CNTL; + uint32_t MICROSECOND_TIME_BASE_DIV; + uint32_t MILLISECOND_TIME_BASE_DIV; + uint32_t DISPCLK_FREQ_CHANGE_CNTL; + uint32_t RBBMIF_TIMEOUT_DIS; + uint32_t RBBMIF_TIMEOUT_DIS_2; + uint32_t DCHUBBUB_CRC_CTRL; + uint32_t DPP_TOP0_DPP_CRC_CTRL; + uint32_t DPP_TOP0_DPP_CRC_VAL_R_G; + uint32_t DPP_TOP0_DPP_CRC_VAL_B_A; + uint32_t MPC_CRC_CTRL; + uint32_t MPC_CRC_RESULT_GB; + uint32_t MPC_CRC_RESULT_C; + uint32_t MPC_CRC_RESULT_AR; + uint32_t D1VGA_CONTROL; + uint32_t D2VGA_CONTROL; + uint32_t D3VGA_CONTROL; + uint32_t D4VGA_CONTROL; + uint32_t D5VGA_CONTROL; + uint32_t D6VGA_CONTROL; + uint32_t VGA_TEST_CONTROL; + /* MMHUB registers. read only. temporary hack */ + uint32_t VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32; + uint32_t VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32; + uint32_t VM_CONTEXT0_PAGE_TABLE_START_ADDR_HI32; + uint32_t VM_CONTEXT0_PAGE_TABLE_START_ADDR_LO32; + uint32_t VM_CONTEXT0_PAGE_TABLE_END_ADDR_HI32; + uint32_t VM_CONTEXT0_PAGE_TABLE_END_ADDR_LO32; + uint32_t VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_HI32; + uint32_t VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_LO32; + uint32_t MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB; + uint32_t MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB; + uint32_t MC_VM_SYSTEM_APERTURE_LOW_ADDR; + uint32_t MC_VM_SYSTEM_APERTURE_HIGH_ADDR; + uint32_t MC_VM_XGMI_LFB_CNTL; + uint32_t AZALIA_AUDIO_DTO; + uint32_t AZALIA_CONTROLLER_CLOCK_GATING; + uint32_t HPO_TOP_CLOCK_CONTROL; + uint32_t ODM_MEM_PWR_CTRL3; + uint32_t DMU_MEM_PWR_CNTL; + uint32_t MMHUBBUB_MEM_PWR_CNTL; + uint32_t DCHUBBUB_ARB_HOSTVM_CNTL; + uint32_t MC_VM_FB_LOCATION_BASE; + uint32_t MC_VM_FB_LOCATION_TOP; + uint32_t MC_VM_FB_OFFSET; + uint32_t HPO_TOP_HW_CONTROL; +}; + /* 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) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define HWSEQ_DCE6_MASK_SH_LIST(mask_sh)\ + .DCFE_CLOCK_ENABLE = CRTC_DCFE_CLOCK_CONTROL__CRTC_DCFE_CLOCK_ENABLE ## mask_sh, \ + HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, CRTC0_) +#endif + +#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_GFX9_DCHUB_MASK_SH_LIST(mask_sh)\ + SF(DCHUB_FB_LOCATION, FB_TOP, mask_sh),\ + SF(DCHUB_FB_LOCATION, FB_BASE, mask_sh),\ + SF(DCHUB_AGP_BASE, AGP_BASE, mask_sh),\ + SF(DCHUB_AGP_BOT, AGP_BOT, mask_sh),\ + SF(DCHUB_AGP_TOP, AGP_TOP, mask_sh) + +#define HWSEQ_DCE12_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCEF_MASK_SH_LIST(mask_sh, DCFE0_DCFE_),\ + HWSEQ_BLND_MASK_SH_LIST(mask_sh, BLND0_BLND_),\ + HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, CRTC0_),\ + HWSEQ_PHYPLL_MASK_SH_LIST(mask_sh, CRTC0_),\ + HWSEQ_GFX9_DCHUB_MASK_SH_LIST(mask_sh) + +#define HWSEQ_VG20_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCE12_MASK_SH_LIST(mask_sh),\ + HWS_SF(, MC_VM_XGMI_LFB_CNTL, PF_LFB_REGION, mask_sh),\ + HWS_SF(, MC_VM_XGMI_LFB_CNTL, PF_MAX_REGION, mask_sh) + +#define HWSEQ_DCN_MASK_SH_LIST(mask_sh)\ + HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, OTG0_),\ + HWS_SF1(OTG0_, PHYPLL_PIXEL_RATE_CNTL, PHYPLL_PIXEL_RATE_SOURCE, mask_sh), \ + HWS_SF(, DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, mask_sh), \ + HWS_SF(, DCFCLK_CNTL, DCFCLK_GATE_DIS, mask_sh), \ + HWS_SF(, DC_MEM_GLOBAL_PWR_REQ_CNTL, DC_MEM_GLOBAL_PWR_REQ_DIS, mask_sh) + +#define HWSEQ_DCN1_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCN_MASK_SH_LIST(mask_sh), \ + HWS_SF1(OTG0_, PHYPLL_PIXEL_RATE_CNTL, PIXEL_RATE_PLL_SOURCE, mask_sh), \ + HWS_SF(, DCHUBBUB_SDPIF_FB_BASE, SDPIF_FB_BASE, mask_sh), \ + HWS_SF(, DCHUBBUB_SDPIF_FB_OFFSET, SDPIF_FB_OFFSET, mask_sh), \ + HWS_SF(, DCHUBBUB_SDPIF_AGP_BASE, SDPIF_AGP_BASE, mask_sh), \ + HWS_SF(, DCHUBBUB_SDPIF_AGP_BOT, SDPIF_AGP_BOT, mask_sh), \ + HWS_SF(, DCHUBBUB_SDPIF_AGP_TOP, SDPIF_AGP_TOP, mask_sh), \ + /* todo: get these from GVM instead of reading registers ourselves */\ + HWS_SF(, VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32, PAGE_DIRECTORY_ENTRY_HI32, mask_sh),\ + HWS_SF(, VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32, PAGE_DIRECTORY_ENTRY_LO32, mask_sh),\ + HWS_SF(, VM_CONTEXT0_PAGE_TABLE_START_ADDR_HI32, LOGICAL_PAGE_NUMBER_HI4, mask_sh),\ + HWS_SF(, VM_CONTEXT0_PAGE_TABLE_START_ADDR_LO32, LOGICAL_PAGE_NUMBER_LO32, mask_sh),\ + HWS_SF(, VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_HI32, PHYSICAL_PAGE_ADDR_HI4, mask_sh),\ + HWS_SF(, VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_LO32, PHYSICAL_PAGE_ADDR_LO32, mask_sh),\ + HWS_SF(, MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, PHYSICAL_PAGE_NUMBER_MSB, mask_sh),\ + HWS_SF(, MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, PHYSICAL_PAGE_NUMBER_LSB, mask_sh),\ + HWS_SF(, MC_VM_SYSTEM_APERTURE_LOW_ADDR, LOGICAL_ADDR, mask_sh),\ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN0_PG_STATUS, DOMAIN0_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN1_PG_STATUS, DOMAIN1_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN2_PG_STATUS, DOMAIN2_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN3_PG_STATUS, DOMAIN3_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN4_PG_STATUS, DOMAIN4_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN5_PG_STATUS, DOMAIN5_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN6_PG_STATUS, DOMAIN6_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN7_PG_STATUS, DOMAIN7_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh), \ + HWS_SF(, D1VGA_CONTROL, D1VGA_MODE_ENABLE, mask_sh),\ + HWS_SF(, D2VGA_CONTROL, D2VGA_MODE_ENABLE, mask_sh),\ + HWS_SF(, D3VGA_CONTROL, D3VGA_MODE_ENABLE, mask_sh),\ + HWS_SF(, D4VGA_CONTROL, D4VGA_MODE_ENABLE, mask_sh),\ + HWS_SF(, VGA_TEST_CONTROL, VGA_TEST_ENABLE, mask_sh),\ + HWS_SF(, VGA_TEST_CONTROL, VGA_TEST_RENDER_START, mask_sh) + +#define HWSEQ_DCN2_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCN_MASK_SH_LIST(mask_sh), \ + HWS_SF(, DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, mask_sh), \ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN8_PG_CONFIG, DOMAIN8_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN8_PG_CONFIG, DOMAIN8_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN9_PG_CONFIG, DOMAIN9_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN9_PG_CONFIG, DOMAIN9_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN10_PG_CONFIG, DOMAIN10_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN10_PG_CONFIG, DOMAIN10_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN11_PG_CONFIG, DOMAIN11_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN11_PG_CONFIG, DOMAIN11_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN16_PG_CONFIG, DOMAIN16_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN16_PG_CONFIG, DOMAIN16_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN17_PG_CONFIG, DOMAIN17_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN17_PG_CONFIG, DOMAIN17_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN18_PG_CONFIG, DOMAIN18_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN18_PG_CONFIG, DOMAIN18_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN19_PG_CONFIG, DOMAIN19_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN19_PG_CONFIG, DOMAIN19_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN20_PG_CONFIG, DOMAIN20_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN20_PG_CONFIG, DOMAIN20_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN21_PG_CONFIG, DOMAIN21_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN21_PG_CONFIG, DOMAIN21_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN0_PG_STATUS, DOMAIN0_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN1_PG_STATUS, DOMAIN1_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN2_PG_STATUS, DOMAIN2_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN3_PG_STATUS, DOMAIN3_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN4_PG_STATUS, DOMAIN4_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN5_PG_STATUS, DOMAIN5_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN6_PG_STATUS, DOMAIN6_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN7_PG_STATUS, DOMAIN7_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN8_PG_STATUS, DOMAIN8_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN9_PG_STATUS, DOMAIN9_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN10_PG_STATUS, DOMAIN10_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN11_PG_STATUS, DOMAIN11_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN16_PG_STATUS, DOMAIN16_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN17_PG_STATUS, DOMAIN17_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN18_PG_STATUS, DOMAIN18_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN19_PG_STATUS, DOMAIN19_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN20_PG_STATUS, DOMAIN20_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN21_PG_STATUS, DOMAIN21_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh) + +#define HWSEQ_DCN21_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCN_MASK_SH_LIST(mask_sh), \ + HWS_SF(, DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, mask_sh), \ + HWS_SF(, MMVM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32, PAGE_DIRECTORY_ENTRY_HI32, mask_sh),\ + HWS_SF(, MMVM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32, PAGE_DIRECTORY_ENTRY_LO32, mask_sh),\ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN16_PG_CONFIG, DOMAIN16_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN16_PG_CONFIG, DOMAIN16_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN17_PG_CONFIG, DOMAIN17_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN17_PG_CONFIG, DOMAIN17_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN18_PG_CONFIG, DOMAIN18_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN18_PG_CONFIG, DOMAIN18_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN0_PG_STATUS, DOMAIN0_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN1_PG_STATUS, DOMAIN1_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN2_PG_STATUS, DOMAIN2_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN3_PG_STATUS, DOMAIN3_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN4_PG_STATUS, DOMAIN4_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN5_PG_STATUS, DOMAIN5_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN6_PG_STATUS, DOMAIN6_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN7_PG_STATUS, DOMAIN7_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN16_PG_STATUS, DOMAIN16_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN17_PG_STATUS, DOMAIN17_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN18_PG_STATUS, DOMAIN18_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh) + +#define HWSEQ_DCN201_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCN_MASK_SH_LIST(mask_sh), \ + HWS_SF(, DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, mask_sh), \ + HWS_SF(, AZALIA_AUDIO_DTO, AZALIA_AUDIO_DTO_MODULE, mask_sh) + +#define HWSEQ_DCN30_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCN2_MASK_SH_LIST(mask_sh), \ + HWS_SF(, AZALIA_AUDIO_DTO, AZALIA_AUDIO_DTO_MODULE, mask_sh), \ + HWS_SF(, ODM_MEM_PWR_CTRL3, ODM_MEM_UNASSIGNED_PWR_MODE, mask_sh), \ + HWS_SF(, ODM_MEM_PWR_CTRL3, ODM_MEM_VBLANK_PWR_MODE, mask_sh), \ + HWS_SF(, DMU_MEM_PWR_CNTL, DMCU_ERAM_MEM_PWR_FORCE, mask_sh), \ + HWS_SF(, MMHUBBUB_MEM_PWR_CNTL, VGA_MEM_PWR_FORCE, mask_sh) + +#define HWSEQ_DCN301_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCN_MASK_SH_LIST(mask_sh), \ + HWS_SF(, DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, mask_sh), \ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN16_PG_CONFIG, DOMAIN16_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN16_PG_CONFIG, DOMAIN16_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN17_PG_CONFIG, DOMAIN17_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN17_PG_CONFIG, DOMAIN17_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN18_PG_CONFIG, DOMAIN18_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN18_PG_CONFIG, DOMAIN18_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN0_PG_STATUS, DOMAIN0_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN1_PG_STATUS, DOMAIN1_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN2_PG_STATUS, DOMAIN2_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN3_PG_STATUS, DOMAIN3_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN4_PG_STATUS, DOMAIN4_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN5_PG_STATUS, DOMAIN5_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN6_PG_STATUS, DOMAIN6_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN7_PG_STATUS, DOMAIN7_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN16_PG_STATUS, DOMAIN16_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN17_PG_STATUS, DOMAIN17_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN18_PG_STATUS, DOMAIN18_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh), \ + HWS_SF(, PANEL_PWRSEQ0_CNTL, PANEL_BLON, mask_sh),\ + HWS_SF(, PANEL_PWRSEQ0_CNTL, PANEL_DIGON, mask_sh),\ + HWS_SF(, PANEL_PWRSEQ0_CNTL, PANEL_DIGON_OVRD, mask_sh),\ + HWS_SF(, PANEL_PWRSEQ0_STATE, PANEL_PWRSEQ_TARGET_STATE_R, mask_sh),\ + HWS_SF(, AZALIA_AUDIO_DTO, AZALIA_AUDIO_DTO_MODULE, mask_sh) + +#define HWSEQ_DCN302_MASK_SH_LIST(mask_sh)\ + HWSEQ_DCN_MASK_SH_LIST(mask_sh), \ + HWS_SF(, DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, mask_sh), \ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN0_PG_CONFIG, DOMAIN0_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN1_PG_CONFIG, DOMAIN1_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN2_PG_CONFIG, DOMAIN2_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN3_PG_CONFIG, DOMAIN3_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN4_PG_CONFIG, DOMAIN4_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN5_PG_CONFIG, DOMAIN5_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN6_PG_CONFIG, DOMAIN6_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN7_PG_CONFIG, DOMAIN7_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN8_PG_CONFIG, DOMAIN8_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN8_PG_CONFIG, DOMAIN8_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN9_PG_CONFIG, DOMAIN9_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN9_PG_CONFIG, DOMAIN9_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN16_PG_CONFIG, DOMAIN16_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN16_PG_CONFIG, DOMAIN16_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN17_PG_CONFIG, DOMAIN17_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN17_PG_CONFIG, DOMAIN17_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN18_PG_CONFIG, DOMAIN18_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN18_PG_CONFIG, DOMAIN18_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN19_PG_CONFIG, DOMAIN19_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN19_PG_CONFIG, DOMAIN19_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN20_PG_CONFIG, DOMAIN20_POWER_FORCEON, mask_sh), \ + HWS_SF(, DOMAIN20_PG_CONFIG, DOMAIN20_POWER_GATE, mask_sh), \ + HWS_SF(, DOMAIN0_PG_STATUS, DOMAIN0_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN1_PG_STATUS, DOMAIN1_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN2_PG_STATUS, DOMAIN2_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN3_PG_STATUS, DOMAIN3_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN4_PG_STATUS, DOMAIN4_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN5_PG_STATUS, DOMAIN5_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN6_PG_STATUS, DOMAIN6_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN7_PG_STATUS, DOMAIN7_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN8_PG_STATUS, DOMAIN8_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN9_PG_STATUS, DOMAIN9_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN16_PG_STATUS, DOMAIN16_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN17_PG_STATUS, DOMAIN17_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN18_PG_STATUS, DOMAIN18_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN19_PG_STATUS, DOMAIN19_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DOMAIN20_PG_STATUS, DOMAIN20_PGFSM_PWR_STATUS, mask_sh), \ + HWS_SF(, DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh), \ + HWS_SF(, AZALIA_AUDIO_DTO, AZALIA_AUDIO_DTO_MODULE, mask_sh) + +#define HWSEQ_DCN303_MASK_SH_LIST(mask_sh) \ + HWSEQ_DCN_MASK_SH_LIST(mask_sh), \ + HWS_SF(, DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, mask_sh), \ + HWS_SF(, AZALIA_AUDIO_DTO, AZALIA_AUDIO_DTO_MODULE, mask_sh), \ + HWS_SF(, HPO_TOP_CLOCK_CONTROL, HPO_HDMISTREAMCLK_GATE_DIS, mask_sh) + +#define HWSEQ_REG_FIELD_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; \ + /* todo: get these from GVM instead of reading registers ourselves */\ + type PAGE_DIRECTORY_ENTRY_HI32;\ + type PAGE_DIRECTORY_ENTRY_LO32;\ + type LOGICAL_PAGE_NUMBER_HI4;\ + type LOGICAL_PAGE_NUMBER_LO32;\ + type PHYSICAL_PAGE_ADDR_HI4;\ + type PHYSICAL_PAGE_ADDR_LO32;\ + type PHYSICAL_PAGE_NUMBER_MSB;\ + type PHYSICAL_PAGE_NUMBER_LSB;\ + type LOGICAL_ADDR; \ + type PF_LFB_REGION;\ + type PF_MAX_REGION;\ + type ENABLE_L1_TLB;\ + type SYSTEM_ACCESS_MODE; + +#define HWSEQ_DCN_REG_FIELD_LIST(type) \ + type HUBP_VTG_SEL; \ + type HUBP_CLOCK_ENABLE; \ + type DPP_CLOCK_ENABLE; \ + type SDPIF_FB_BASE;\ + type SDPIF_FB_OFFSET;\ + type SDPIF_AGP_BASE;\ + type SDPIF_AGP_BOT;\ + type SDPIF_AGP_TOP;\ + type FB_TOP;\ + type FB_BASE;\ + type FB_OFFSET;\ + type AGP_BASE;\ + type AGP_BOT;\ + type AGP_TOP;\ + type DCHUBBUB_GLOBAL_TIMER_ENABLE; \ + type OPP_PIPE_CLOCK_EN;\ + type IP_REQUEST_EN; \ + type DOMAIN0_POWER_FORCEON; \ + type DOMAIN0_POWER_GATE; \ + type DOMAIN1_POWER_FORCEON; \ + type DOMAIN1_POWER_GATE; \ + type DOMAIN2_POWER_FORCEON; \ + type DOMAIN2_POWER_GATE; \ + type DOMAIN3_POWER_FORCEON; \ + type DOMAIN3_POWER_GATE; \ + type DOMAIN4_POWER_FORCEON; \ + type DOMAIN4_POWER_GATE; \ + type DOMAIN5_POWER_FORCEON; \ + type DOMAIN5_POWER_GATE; \ + type DOMAIN6_POWER_FORCEON; \ + type DOMAIN6_POWER_GATE; \ + type DOMAIN7_POWER_FORCEON; \ + type DOMAIN7_POWER_GATE; \ + type DOMAIN8_POWER_FORCEON; \ + type DOMAIN8_POWER_GATE; \ + type DOMAIN9_POWER_FORCEON; \ + type DOMAIN9_POWER_GATE; \ + type DOMAIN10_POWER_FORCEON; \ + type DOMAIN10_POWER_GATE; \ + type DOMAIN11_POWER_FORCEON; \ + type DOMAIN11_POWER_GATE; \ + type DOMAIN16_POWER_FORCEON; \ + type DOMAIN16_POWER_GATE; \ + type DOMAIN17_POWER_FORCEON; \ + type DOMAIN17_POWER_GATE; \ + type DOMAIN18_POWER_FORCEON; \ + type DOMAIN18_POWER_GATE; \ + type DOMAIN19_POWER_FORCEON; \ + type DOMAIN19_POWER_GATE; \ + type DOMAIN20_POWER_FORCEON; \ + type DOMAIN20_POWER_GATE; \ + type DOMAIN21_POWER_FORCEON; \ + type DOMAIN21_POWER_GATE; \ + type DOMAIN0_PGFSM_PWR_STATUS; \ + type DOMAIN1_PGFSM_PWR_STATUS; \ + type DOMAIN2_PGFSM_PWR_STATUS; \ + type DOMAIN3_PGFSM_PWR_STATUS; \ + type DOMAIN4_PGFSM_PWR_STATUS; \ + type DOMAIN5_PGFSM_PWR_STATUS; \ + type DOMAIN6_PGFSM_PWR_STATUS; \ + type DOMAIN7_PGFSM_PWR_STATUS; \ + type DOMAIN8_PGFSM_PWR_STATUS; \ + type DOMAIN9_PGFSM_PWR_STATUS; \ + type DOMAIN10_PGFSM_PWR_STATUS; \ + type DOMAIN11_PGFSM_PWR_STATUS; \ + type DOMAIN16_PGFSM_PWR_STATUS; \ + type DOMAIN17_PGFSM_PWR_STATUS; \ + type DOMAIN18_PGFSM_PWR_STATUS; \ + type DOMAIN19_PGFSM_PWR_STATUS; \ + type DOMAIN20_PGFSM_PWR_STATUS; \ + type DOMAIN21_PGFSM_PWR_STATUS; \ + type DCFCLK_GATE_DIS; \ + type DCHUBBUB_GLOBAL_TIMER_REFDIV; \ + type VGA_TEST_ENABLE; \ + type VGA_TEST_RENDER_START; \ + type D1VGA_MODE_ENABLE; \ + type D2VGA_MODE_ENABLE; \ + type D3VGA_MODE_ENABLE; \ + type D4VGA_MODE_ENABLE; \ + type AZALIA_AUDIO_DTO_MODULE; \ + type ODM_MEM_UNASSIGNED_PWR_MODE; \ + type ODM_MEM_VBLANK_PWR_MODE; \ + type DMCU_ERAM_MEM_PWR_FORCE; \ + type VGA_MEM_PWR_FORCE; + +#define HWSEQ_DCN3_REG_FIELD_LIST(type) \ + type HPO_HDMISTREAMCLK_GATE_DIS; + +#define HWSEQ_DCN301_REG_FIELD_LIST(type) \ + type PANEL_BLON;\ + type PANEL_DIGON;\ + type PANEL_DIGON_OVRD;\ + type PANEL_PWRSEQ_TARGET_STATE_R; + +#define HWSEQ_DCN31_REG_FIELD_LIST(type) \ + type DOMAIN_POWER_FORCEON;\ + type DOMAIN_POWER_GATE;\ + type DOMAIN_PGFSM_PWR_STATUS;\ + type HPO_HDMISTREAMCLK_G_GATE_DIS;\ + type DISABLE_HOSTVM_FORCE_ALLOW_PSTATE;\ + type I2C_LIGHT_SLEEP_FORCE;\ + type HPO_IO_EN; + +struct dce_hwseq_shift { + HWSEQ_REG_FIELD_LIST(uint8_t) + HWSEQ_DCN_REG_FIELD_LIST(uint8_t) + HWSEQ_DCN3_REG_FIELD_LIST(uint8_t) + HWSEQ_DCN301_REG_FIELD_LIST(uint8_t) + HWSEQ_DCN31_REG_FIELD_LIST(uint8_t) +}; + +struct dce_hwseq_mask { + HWSEQ_REG_FIELD_LIST(uint32_t) + HWSEQ_DCN_REG_FIELD_LIST(uint32_t) + HWSEQ_DCN3_REG_FIELD_LIST(uint32_t) + HWSEQ_DCN301_REG_FIELD_LIST(uint32_t) + HWSEQ_DCN31_REG_FIELD_LIST(uint32_t) +}; + + +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' */ +}; + +struct dce_hwseq; +struct pipe_ctx; +struct clock_source; + +void dce_enable_fe_clock(struct dce_hwseq *hwss, + unsigned int inst, bool enable); + +void dce_pipe_control_lock(struct dc *dc, + struct pipe_ctx *pipe, + bool lock); + +void dce_set_blender_mode(struct dce_hwseq *hws, + unsigned int blnd_inst, enum blnd_mode mode); + +#if defined(CONFIG_DRM_AMD_DC_SI) +void dce60_pipe_control_lock(struct dc *dc, + struct pipe_ctx *pipe, + bool lock); +#endif + +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); + +bool dce_use_lut(enum surface_pixel_format format); +#endif /*__DCE_HWSEQ_H__*/ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.c b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.c new file mode 100644 index 000000000..f5cd2392f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.c @@ -0,0 +1,83 @@ +/* + * Copyright 2018 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_i2c.h" +#include "reg_helper.h" + +bool dce_i2c_oem_device_present( + struct resource_pool *pool, + struct ddc_service *ddc, + size_t slave_address +) +{ + struct dc *dc = ddc->ctx->dc; + struct dc_bios *dcb = dc->ctx->dc_bios; + struct graphics_object_id id = {0}; + struct graphics_object_i2c_info i2c_info; + + if (!dc->ctx->dc_bios->fw_info.oem_i2c_present) + return false; + + id.id = dc->ctx->dc_bios->fw_info.oem_i2c_obj_id; + id.enum_id = 0; + id.type = OBJECT_TYPE_GENERIC; + if (dcb->funcs->get_i2c_info(dcb, id, &i2c_info) != BP_RESULT_OK) + return false; + + if (i2c_info.i2c_slave_address != slave_address) + return false; + + return true; +} + +bool dce_i2c_submit_command( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd) +{ + struct dce_i2c_hw *dce_i2c_hw; + struct dce_i2c_sw dce_i2c_sw = {0}; + + if (!ddc) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!cmd) { + BREAK_TO_DEBUGGER(); + return false; + } + + dce_i2c_hw = acquire_i2c_hw_engine(pool, ddc); + + if (dce_i2c_hw) + return dce_i2c_submit_command_hw(pool, ddc, cmd, dce_i2c_hw); + + dce_i2c_sw.ctx = ddc->ctx; + if (dce_i2c_engine_acquire_sw(&dce_i2c_sw, ddc)) { + return dce_i2c_submit_command_sw(pool, ddc, cmd, &dce_i2c_sw); + } + + return false; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.h b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.h new file mode 100644 index 000000000..535fd58de --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.h @@ -0,0 +1,44 @@ +/* + * Copyright 2018 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_I2C_H__ +#define __DCE_I2C_H__ + +#include "inc/core_types.h" +#include "dce_i2c_hw.h" +#include "dce_i2c_sw.h" + +bool dce_i2c_oem_device_present( + struct resource_pool *pool, + struct ddc_service *ddc, + size_t slave_address +); + +bool dce_i2c_submit_command( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c new file mode 100644 index 000000000..6d1b01c26 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c @@ -0,0 +1,709 @@ +/* + * Copyright 2018 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 <linux/delay.h> + +#include "resource.h" +#include "dce_i2c.h" +#include "dce_i2c_hw.h" +#include "reg_helper.h" +#include "include/gpio_service_interface.h" + +#define CTX \ + dce_i2c_hw->ctx +#define REG(reg)\ + dce_i2c_hw->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + dce_i2c_hw->shifts->field_name, dce_i2c_hw->masks->field_name + +static void execute_transaction( + struct dce_i2c_hw *dce_i2c_hw) +{ + REG_UPDATE_N(SETUP, 5, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_EN), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_CLK_DRIVE_EN), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_SEL), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_TRANSACTION_DELAY), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_BYTE_DELAY), 0); + + + REG_UPDATE_5(DC_I2C_CONTROL, + DC_I2C_SOFT_RESET, 0, + DC_I2C_SW_STATUS_RESET, 0, + DC_I2C_SEND_RESET, 0, + DC_I2C_GO, 0, + DC_I2C_TRANSACTION_COUNT, dce_i2c_hw->transaction_count - 1); + + /* start I2C transfer */ + REG_UPDATE(DC_I2C_CONTROL, DC_I2C_GO, 1); + + /* all transactions were executed and HW buffer became empty + * (even though it actually happens when status becomes DONE) + */ + dce_i2c_hw->transaction_count = 0; + dce_i2c_hw->buffer_used_bytes = 0; +} + +static enum i2c_channel_operation_result get_channel_status( + struct dce_i2c_hw *dce_i2c_hw, + uint8_t *returned_bytes) +{ + uint32_t i2c_sw_status = 0; + uint32_t value = + REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); + if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW) + return I2C_CHANNEL_OPERATION_ENGINE_BUSY; + else if (value & dce_i2c_hw->masks->DC_I2C_SW_STOPPED_ON_NACK) + return I2C_CHANNEL_OPERATION_NO_RESPONSE; + else if (value & dce_i2c_hw->masks->DC_I2C_SW_TIMEOUT) + return I2C_CHANNEL_OPERATION_TIMEOUT; + else if (value & dce_i2c_hw->masks->DC_I2C_SW_ABORTED) + return I2C_CHANNEL_OPERATION_FAILED; + else if (value & dce_i2c_hw->masks->DC_I2C_SW_DONE) + return I2C_CHANNEL_OPERATION_SUCCEEDED; + + /* + * this is the case when HW used for communication, I2C_SW_STATUS + * could be zero + */ + return I2C_CHANNEL_OPERATION_SUCCEEDED; +} + +static uint32_t get_hw_buffer_available_size( + const struct dce_i2c_hw *dce_i2c_hw) +{ + return dce_i2c_hw->buffer_size - + dce_i2c_hw->buffer_used_bytes; +} + +static void process_channel_reply( + struct dce_i2c_hw *dce_i2c_hw, + struct i2c_payload *reply) +{ + uint32_t length = reply->length; + uint8_t *buffer = reply->data; + + REG_SET_3(DC_I2C_DATA, 0, + DC_I2C_INDEX, dce_i2c_hw->buffer_used_write, + DC_I2C_DATA_RW, 1, + DC_I2C_INDEX_WRITE, 1); + + while (length) { + /* after reading the status, + * if the I2C operation executed successfully + * (i.e. DC_I2C_STATUS_DONE = 1) then the I2C controller + * should read data bytes from I2C circular data buffer + */ + + uint32_t i2c_data; + + REG_GET(DC_I2C_DATA, DC_I2C_DATA, &i2c_data); + *buffer++ = i2c_data; + + --length; + } +} + +static bool is_engine_available(struct dce_i2c_hw *dce_i2c_hw) +{ + unsigned int arbitrate; + unsigned int i2c_hw_status; + + REG_GET(HW_STATUS, DC_I2C_DDC1_HW_STATUS, &i2c_hw_status); + if (i2c_hw_status == DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_HW) + return false; + + REG_GET(DC_I2C_ARBITRATION, DC_I2C_REG_RW_CNTL_STATUS, &arbitrate); + if (arbitrate == DC_I2C_REG_RW_CNTL_STATUS_DMCU_ONLY) + return false; + + return true; +} + +static bool is_hw_busy(struct dce_i2c_hw *dce_i2c_hw) +{ + uint32_t i2c_sw_status = 0; + + REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); + if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_IDLE) + return false; + + if (is_engine_available(dce_i2c_hw)) + return false; + + return true; +} + +static bool process_transaction( + struct dce_i2c_hw *dce_i2c_hw, + struct i2c_request_transaction_data *request) +{ + uint32_t length = request->length; + uint8_t *buffer = request->data; + + bool last_transaction = false; + uint32_t value = 0; + + if (is_hw_busy(dce_i2c_hw)) { + request->status = I2C_CHANNEL_OPERATION_ENGINE_BUSY; + return false; + } + + last_transaction = ((dce_i2c_hw->transaction_count == 3) || + (request->action == DCE_I2C_TRANSACTION_ACTION_I2C_WRITE) || + (request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ)); + + + switch (dce_i2c_hw->transaction_count) { + case 0: + REG_UPDATE_5(DC_I2C_TRANSACTION0, + DC_I2C_STOP_ON_NACK0, 1, + DC_I2C_START0, 1, + DC_I2C_RW0, 0 != (request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ), + DC_I2C_COUNT0, length, + DC_I2C_STOP0, last_transaction ? 1 : 0); + break; + case 1: + REG_UPDATE_5(DC_I2C_TRANSACTION1, + DC_I2C_STOP_ON_NACK0, 1, + DC_I2C_START0, 1, + DC_I2C_RW0, 0 != (request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ), + DC_I2C_COUNT0, length, + DC_I2C_STOP0, last_transaction ? 1 : 0); + break; + case 2: + REG_UPDATE_5(DC_I2C_TRANSACTION2, + DC_I2C_STOP_ON_NACK0, 1, + DC_I2C_START0, 1, + DC_I2C_RW0, 0 != (request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ), + DC_I2C_COUNT0, length, + DC_I2C_STOP0, last_transaction ? 1 : 0); + break; + case 3: + REG_UPDATE_5(DC_I2C_TRANSACTION3, + DC_I2C_STOP_ON_NACK0, 1, + DC_I2C_START0, 1, + DC_I2C_RW0, 0 != (request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ), + DC_I2C_COUNT0, length, + DC_I2C_STOP0, last_transaction ? 1 : 0); + break; + default: + /* TODO Warning ? */ + break; + } + + /* Write the I2C address and I2C data + * into the hardware circular buffer, one byte per entry. + * As an example, the 7-bit I2C slave address for CRT monitor + * for reading DDC/EDID information is 0b1010001. + * For an I2C send operation, the LSB must be programmed to 0; + * for I2C receive operation, the LSB must be programmed to 1. + */ + if (dce_i2c_hw->transaction_count == 0) { + value = REG_SET_4(DC_I2C_DATA, 0, + DC_I2C_DATA_RW, false, + DC_I2C_DATA, request->address, + DC_I2C_INDEX, 0, + DC_I2C_INDEX_WRITE, 1); + dce_i2c_hw->buffer_used_write = 0; + } else + value = REG_SET_2(DC_I2C_DATA, 0, + DC_I2C_DATA_RW, false, + DC_I2C_DATA, request->address); + + dce_i2c_hw->buffer_used_write++; + + if (!(request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ)) { + while (length) { + REG_SET_2(DC_I2C_DATA, value, + DC_I2C_INDEX_WRITE, 0, + DC_I2C_DATA, *buffer++); + dce_i2c_hw->buffer_used_write++; + --length; + } + } + + ++dce_i2c_hw->transaction_count; + dce_i2c_hw->buffer_used_bytes += length + 1; + + return last_transaction; +} + +static inline void reset_hw_engine(struct dce_i2c_hw *dce_i2c_hw) +{ + REG_UPDATE_2(DC_I2C_CONTROL, + DC_I2C_SW_STATUS_RESET, 1, + DC_I2C_SW_STATUS_RESET, 1); +} + +static void set_speed( + struct dce_i2c_hw *dce_i2c_hw, + uint32_t speed) +{ + uint32_t xtal_ref_div = 0, ref_base_div = 0; + uint32_t prescale = 0; + uint32_t i2c_ref_clock = 0; + + if (speed == 0) + return; + + REG_GET_2(MICROSECOND_TIME_BASE_DIV, MICROSECOND_TIME_BASE_DIV, &ref_base_div, + XTAL_REF_DIV, &xtal_ref_div); + + if (xtal_ref_div == 0) + xtal_ref_div = 2; + + if (ref_base_div == 0) + i2c_ref_clock = (dce_i2c_hw->reference_frequency * 2); + else + i2c_ref_clock = ref_base_div * 1000; + + prescale = (i2c_ref_clock / xtal_ref_div) / speed; + + if (dce_i2c_hw->masks->DC_I2C_DDC1_START_STOP_TIMING_CNTL) + REG_UPDATE_N(SPEED, 3, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE), prescale, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD), 2, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_START_STOP_TIMING_CNTL), speed > 50 ? 2:1); + else + REG_UPDATE_N(SPEED, 2, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE), prescale, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD), 2); +} + +static bool setup_engine( + struct dce_i2c_hw *dce_i2c_hw) +{ + uint32_t i2c_setup_limit = I2C_SETUP_TIME_LIMIT_DCE; + uint32_t reset_length = 0; + + if (dce_i2c_hw->ctx->dc->debug.enable_mem_low_power.bits.i2c) { + if (dce_i2c_hw->regs->DIO_MEM_PWR_CTRL) { + REG_UPDATE(DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, 0); + REG_WAIT(DIO_MEM_PWR_STATUS, I2C_MEM_PWR_STATE, 0, 0, 5); + } + } + + /* we have checked I2c not used by DMCU, set SW use I2C REQ to 1 to indicate SW using it*/ + REG_UPDATE(DC_I2C_ARBITRATION, DC_I2C_SW_USE_I2C_REG_REQ, 1); + + /* we have checked I2c not used by DMCU, set SW use I2C REQ to 1 to indicate SW using it*/ + REG_UPDATE(DC_I2C_ARBITRATION, DC_I2C_SW_USE_I2C_REG_REQ, 1); + + /*set SW requested I2c speed to default, if API calls in it will be override later*/ + set_speed(dce_i2c_hw, dce_i2c_hw->ctx->dc->caps.i2c_speed_in_khz); + + if (dce_i2c_hw->setup_limit != 0) + i2c_setup_limit = dce_i2c_hw->setup_limit; + + /* Program pin select */ + REG_UPDATE_6(DC_I2C_CONTROL, + DC_I2C_GO, 0, + DC_I2C_SOFT_RESET, 0, + DC_I2C_SEND_RESET, 0, + DC_I2C_SW_STATUS_RESET, 1, + DC_I2C_TRANSACTION_COUNT, 0, + DC_I2C_DDC_SELECT, dce_i2c_hw->engine_id); + + /* Program time limit */ + if (dce_i2c_hw->send_reset_length == 0) { + /*pre-dcn*/ + REG_UPDATE_N(SETUP, 2, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_TIME_LIMIT), i2c_setup_limit, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE), 1); + } else { + reset_length = dce_i2c_hw->send_reset_length; + REG_UPDATE_N(SETUP, 3, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_TIME_LIMIT), i2c_setup_limit, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_SEND_RESET_LENGTH), reset_length, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE), 1); + } + /* Program HW priority + * set to High - interrupt software I2C at any time + * Enable restart of SW I2C that was interrupted by HW + * disable queuing of software while I2C is in use by HW + */ + REG_UPDATE(DC_I2C_ARBITRATION, + DC_I2C_NO_QUEUED_SW_GO, 0); + + return true; +} + +static void release_engine( + struct dce_i2c_hw *dce_i2c_hw) +{ + bool safe_to_reset; + + + /* Reset HW engine */ + { + uint32_t i2c_sw_status = 0; + + REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); + /* if used by SW, safe to reset */ + safe_to_reset = (i2c_sw_status == 1); + } + + if (safe_to_reset) + REG_UPDATE_2(DC_I2C_CONTROL, + DC_I2C_SOFT_RESET, 1, + DC_I2C_SW_STATUS_RESET, 1); + else + REG_UPDATE(DC_I2C_CONTROL, DC_I2C_SW_STATUS_RESET, 1); + /* HW I2c engine - clock gating feature */ + if (!dce_i2c_hw->engine_keep_power_up_count) + REG_UPDATE_N(SETUP, 1, FN(SETUP, DC_I2C_DDC1_ENABLE), 0); + + /*for HW HDCP Ri polling failure w/a test*/ + set_speed(dce_i2c_hw, dce_i2c_hw->ctx->dc->caps.i2c_speed_in_khz_hdcp); + /* Release I2C after reset, so HW or DMCU could use it */ + REG_UPDATE_2(DC_I2C_ARBITRATION, DC_I2C_SW_DONE_USING_I2C_REG, 1, + DC_I2C_SW_USE_I2C_REG_REQ, 0); + + if (dce_i2c_hw->ctx->dc->debug.enable_mem_low_power.bits.i2c) { + if (dce_i2c_hw->regs->DIO_MEM_PWR_CTRL) + REG_UPDATE(DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, 1); + } +} + +struct dce_i2c_hw *acquire_i2c_hw_engine( + struct resource_pool *pool, + struct ddc *ddc) +{ + uint32_t counter = 0; + enum gpio_result result; + struct dce_i2c_hw *dce_i2c_hw = NULL; + + if (!ddc) + return NULL; + + if (ddc->hw_info.hw_supported) { + enum gpio_ddc_line line = dal_ddc_get_line(ddc); + + if (line < pool->res_cap->num_ddc) + dce_i2c_hw = pool->hw_i2cs[line]; + } + + if (!dce_i2c_hw) + return NULL; + + if (pool->i2c_hw_buffer_in_use || !is_engine_available(dce_i2c_hw)) + return NULL; + + do { + result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, + GPIO_DDC_CONFIG_TYPE_MODE_I2C); + + if (result == GPIO_RESULT_OK) + break; + + /* i2c_engine is busy by VBios, lets wait and retry */ + + udelay(10); + + ++counter; + } while (counter < 2); + + if (result != GPIO_RESULT_OK) + return NULL; + + dce_i2c_hw->ddc = ddc; + + if (!setup_engine(dce_i2c_hw)) { + release_engine(dce_i2c_hw); + return NULL; + } + + pool->i2c_hw_buffer_in_use = true; + return dce_i2c_hw; +} + +static enum i2c_channel_operation_result dce_i2c_hw_engine_wait_on_operation_result( + struct dce_i2c_hw *dce_i2c_hw, + uint32_t timeout, + enum i2c_channel_operation_result expected_result) +{ + enum i2c_channel_operation_result result; + uint32_t i = 0; + + if (!timeout) + return I2C_CHANNEL_OPERATION_SUCCEEDED; + + do { + + result = get_channel_status( + dce_i2c_hw, NULL); + + if (result != expected_result) + break; + + udelay(1); + + ++i; + } while (i < timeout); + return result; +} + +static void submit_channel_request_hw( + struct dce_i2c_hw *dce_i2c_hw, + struct i2c_request_transaction_data *request) +{ + request->status = I2C_CHANNEL_OPERATION_SUCCEEDED; + + if (!process_transaction(dce_i2c_hw, request)) + return; + + if (is_hw_busy(dce_i2c_hw)) { + request->status = I2C_CHANNEL_OPERATION_ENGINE_BUSY; + return; + } + reset_hw_engine(dce_i2c_hw); + + execute_transaction(dce_i2c_hw); + + +} + +static uint32_t get_transaction_timeout_hw( + const struct dce_i2c_hw *dce_i2c_hw, + uint32_t length, + uint32_t speed) +{ + uint32_t period_timeout; + uint32_t num_of_clock_stretches; + + if (!speed) + return 0; + + period_timeout = (1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed; + + num_of_clock_stretches = 1 + (length << 3) + 1; + num_of_clock_stretches += + (dce_i2c_hw->buffer_used_bytes << 3) + + (dce_i2c_hw->transaction_count << 1); + + return period_timeout * num_of_clock_stretches; +} + +static bool dce_i2c_hw_engine_submit_payload( + struct dce_i2c_hw *dce_i2c_hw, + struct i2c_payload *payload, + bool middle_of_transaction, + uint32_t speed) +{ + + struct i2c_request_transaction_data request; + + uint32_t transaction_timeout; + + enum i2c_channel_operation_result operation_result; + + bool result = false; + + /* We need following: + * transaction length will not exceed + * the number of free bytes in HW buffer (minus one for address) + */ + + if (payload->length >= + get_hw_buffer_available_size(dce_i2c_hw)) { + return false; + } + + if (!payload->write) + request.action = middle_of_transaction ? + DCE_I2C_TRANSACTION_ACTION_I2C_READ_MOT : + DCE_I2C_TRANSACTION_ACTION_I2C_READ; + else + request.action = middle_of_transaction ? + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE_MOT : + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE; + + + request.address = (uint8_t) ((payload->address << 1) | !payload->write); + request.length = payload->length; + request.data = payload->data; + + /* obtain timeout value before submitting request */ + + transaction_timeout = get_transaction_timeout_hw( + dce_i2c_hw, payload->length + 1, speed); + + submit_channel_request_hw( + dce_i2c_hw, &request); + + if ((request.status == I2C_CHANNEL_OPERATION_FAILED) || + (request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY)) + return false; + + /* wait until transaction proceed */ + + operation_result = dce_i2c_hw_engine_wait_on_operation_result( + dce_i2c_hw, + transaction_timeout, + I2C_CHANNEL_OPERATION_ENGINE_BUSY); + + /* update transaction status */ + + if (operation_result == I2C_CHANNEL_OPERATION_SUCCEEDED) + result = true; + + if (result && (!payload->write)) + process_channel_reply(dce_i2c_hw, payload); + + return result; +} + +bool dce_i2c_submit_command_hw( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd, + struct dce_i2c_hw *dce_i2c_hw) +{ + uint8_t index_of_payload = 0; + bool result; + + set_speed(dce_i2c_hw, cmd->speed); + + result = true; + + while (index_of_payload < cmd->number_of_payloads) { + bool mot = (index_of_payload != cmd->number_of_payloads - 1); + + struct i2c_payload *payload = cmd->payloads + index_of_payload; + + if (!dce_i2c_hw_engine_submit_payload( + dce_i2c_hw, payload, mot, cmd->speed)) { + result = false; + break; + } + + ++index_of_payload; + } + + pool->i2c_hw_buffer_in_use = false; + + release_engine(dce_i2c_hw); + dal_ddc_close(dce_i2c_hw->ddc); + + dce_i2c_hw->ddc = NULL; + + return result; +} + +void dce_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks) +{ + dce_i2c_hw->ctx = ctx; + dce_i2c_hw->engine_id = engine_id; + dce_i2c_hw->reference_frequency = (ctx->dc_bios->fw_info.pll_info.crystal_frequency) >> 1; + dce_i2c_hw->regs = regs; + dce_i2c_hw->shifts = shifts; + dce_i2c_hw->masks = masks; + dce_i2c_hw->buffer_used_bytes = 0; + dce_i2c_hw->transaction_count = 0; + dce_i2c_hw->engine_keep_power_up_count = 1; + dce_i2c_hw->default_speed = DEFAULT_I2C_HW_SPEED; + dce_i2c_hw->send_reset_length = 0; + dce_i2c_hw->setup_limit = I2C_SETUP_TIME_LIMIT_DCE; + dce_i2c_hw->buffer_size = I2C_HW_BUFFER_SIZE_DCE; +} + +void dce100_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks) +{ + dce_i2c_hw_construct(dce_i2c_hw, + ctx, + engine_id, + regs, + shifts, + masks); + dce_i2c_hw->buffer_size = I2C_HW_BUFFER_SIZE_DCE100; +} + +void dce112_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks) +{ + dce100_i2c_hw_construct(dce_i2c_hw, + ctx, + engine_id, + regs, + shifts, + masks); + dce_i2c_hw->default_speed = DEFAULT_I2C_HW_SPEED_100KHZ; +} + +void dcn1_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks) +{ + dce112_i2c_hw_construct(dce_i2c_hw, + ctx, + engine_id, + regs, + shifts, + masks); + dce_i2c_hw->setup_limit = I2C_SETUP_TIME_LIMIT_DCN; +} + +void dcn2_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks) +{ + dcn1_i2c_hw_construct(dce_i2c_hw, + ctx, + engine_id, + regs, + shifts, + masks); + dce_i2c_hw->send_reset_length = I2C_SEND_RESET_LENGTH_9; + if (ctx->dc->debug.scl_reset_length10) + dce_i2c_hw->send_reset_length = I2C_SEND_RESET_LENGTH_10; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.h b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.h new file mode 100644 index 000000000..3f45ecd18 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.h @@ -0,0 +1,345 @@ +/* + * Copyright 2018 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_I2C_HW_H__ +#define __DCE_I2C_HW_H__ + +enum dc_i2c_status { + DC_I2C_STATUS__DC_I2C_STATUS_IDLE, + DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW, + DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_HW, + DC_I2C_REG_RW_CNTL_STATUS_DMCU_ONLY = 2, +}; + +enum dc_i2c_arbitration { + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL, + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_HIGH +}; + +enum i2c_channel_operation_result { + I2C_CHANNEL_OPERATION_SUCCEEDED, + I2C_CHANNEL_OPERATION_FAILED, + I2C_CHANNEL_OPERATION_NOT_GRANTED, + I2C_CHANNEL_OPERATION_IS_BUSY, + I2C_CHANNEL_OPERATION_NO_HANDLE_PROVIDED, + I2C_CHANNEL_OPERATION_CHANNEL_IN_USE, + I2C_CHANNEL_OPERATION_CHANNEL_CLIENT_MAX_ALLOWED, + I2C_CHANNEL_OPERATION_ENGINE_BUSY, + I2C_CHANNEL_OPERATION_TIMEOUT, + I2C_CHANNEL_OPERATION_NO_RESPONSE, + I2C_CHANNEL_OPERATION_HW_REQUEST_I2C_BUS, + I2C_CHANNEL_OPERATION_WRONG_PARAMETER, + I2C_CHANNEL_OPERATION_OUT_NB_OF_RETRIES, + I2C_CHANNEL_OPERATION_NOT_STARTED +}; + + +enum dce_i2c_transaction_action { + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE = 0x00, + DCE_I2C_TRANSACTION_ACTION_I2C_READ = 0x10, + DCE_I2C_TRANSACTION_ACTION_I2C_STATUS_REQUEST = 0x20, + + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE_MOT = 0x40, + DCE_I2C_TRANSACTION_ACTION_I2C_READ_MOT = 0x50, + DCE_I2C_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT = 0x60, + + DCE_I2C_TRANSACTION_ACTION_DP_WRITE = 0x80, + DCE_I2C_TRANSACTION_ACTION_DP_READ = 0x90 +}; + +enum { + I2C_SETUP_TIME_LIMIT_DCE = 255, + I2C_SETUP_TIME_LIMIT_DCN = 3, + I2C_HW_BUFFER_SIZE_DCE100 = 538, + I2C_HW_BUFFER_SIZE_DCE = 144, + I2C_SEND_RESET_LENGTH_9 = 9, + I2C_SEND_RESET_LENGTH_10 = 10, + DEFAULT_I2C_HW_SPEED = 50, + DEFAULT_I2C_HW_SPEED_100KHZ = 100, + TRANSACTION_TIMEOUT_IN_I2C_CLOCKS = 32, +}; + +#define I2C_HW_ENGINE_COMMON_REG_LIST(id)\ + SRI(SETUP, DC_I2C_DDC, id),\ + SRI(SPEED, DC_I2C_DDC, id),\ + SRI(HW_STATUS, DC_I2C_DDC, id),\ + SR(DC_I2C_ARBITRATION),\ + SR(DC_I2C_CONTROL),\ + SR(DC_I2C_SW_STATUS),\ + SR(DC_I2C_TRANSACTION0),\ + SR(DC_I2C_TRANSACTION1),\ + SR(DC_I2C_TRANSACTION2),\ + SR(DC_I2C_TRANSACTION3),\ + SR(DC_I2C_DATA),\ + SR(MICROSECOND_TIME_BASE_DIV) + +#define I2C_HW_ENGINE_COMMON_REG_LIST_DCN30(id)\ + I2C_HW_ENGINE_COMMON_REG_LIST(id),\ + SR(DIO_MEM_PWR_CTRL),\ + SR(DIO_MEM_PWR_STATUS) + +#define I2C_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh)\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_TIME_LIMIT, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_EN, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_CLK_DRIVE_EN, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_SEL, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_TRANSACTION_DELAY, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_BYTE_DELAY, mask_sh),\ + I2C_SF(DC_I2C_DDC1_HW_STATUS, DC_I2C_DDC1_HW_STATUS, mask_sh),\ + I2C_SF(DC_I2C_ARBITRATION, DC_I2C_SW_USE_I2C_REG_REQ, mask_sh),\ + I2C_SF(DC_I2C_ARBITRATION, DC_I2C_SW_DONE_USING_I2C_REG, mask_sh),\ + I2C_SF(DC_I2C_ARBITRATION, DC_I2C_NO_QUEUED_SW_GO, mask_sh),\ + I2C_SF(DC_I2C_ARBITRATION, DC_I2C_SW_PRIORITY, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_SOFT_RESET, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_SW_STATUS_RESET, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_GO, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_SEND_RESET, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_TRANSACTION_COUNT, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_DDC_SELECT, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_STOPPED_ON_NACK, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_TIMEOUT, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_ABORTED, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_DONE, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_STOP_ON_NACK0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_START0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_RW0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_STOP0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_COUNT0, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_DATA_RW, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_DATA, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_INDEX, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_INDEX_WRITE, mask_sh),\ + I2C_SF(MICROSECOND_TIME_BASE_DIV, XTAL_REF_DIV, mask_sh),\ + I2C_SF(MICROSECOND_TIME_BASE_DIV, MICROSECOND_TIME_BASE_DIV, mask_sh),\ + I2C_SF(DC_I2C_ARBITRATION, DC_I2C_REG_RW_CNTL_STATUS, mask_sh) + +#define I2C_COMMON_MASK_SH_LIST_DCE110(mask_sh)\ + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh),\ + I2C_SF(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_START_STOP_TIMING_CNTL, mask_sh) + +struct dce_i2c_shift { + uint8_t DC_I2C_DDC1_ENABLE; + uint8_t DC_I2C_DDC1_TIME_LIMIT; + uint8_t DC_I2C_DDC1_DATA_DRIVE_EN; + uint8_t DC_I2C_DDC1_CLK_DRIVE_EN; + uint8_t DC_I2C_DDC1_DATA_DRIVE_SEL; + uint8_t DC_I2C_DDC1_INTRA_TRANSACTION_DELAY; + uint8_t DC_I2C_DDC1_INTRA_BYTE_DELAY; + uint8_t DC_I2C_DDC1_HW_STATUS; + uint8_t DC_I2C_SW_DONE_USING_I2C_REG; + uint8_t DC_I2C_SW_USE_I2C_REG_REQ; + uint8_t DC_I2C_NO_QUEUED_SW_GO; + uint8_t DC_I2C_SW_PRIORITY; + uint8_t DC_I2C_SOFT_RESET; + uint8_t DC_I2C_SW_STATUS_RESET; + uint8_t DC_I2C_GO; + uint8_t DC_I2C_SEND_RESET; + uint8_t DC_I2C_TRANSACTION_COUNT; + uint8_t DC_I2C_DDC_SELECT; + uint8_t DC_I2C_DDC1_PRESCALE; + uint8_t DC_I2C_DDC1_THRESHOLD; + uint8_t DC_I2C_DDC1_START_STOP_TIMING_CNTL; + uint8_t DC_I2C_SW_STOPPED_ON_NACK; + uint8_t DC_I2C_SW_TIMEOUT; + uint8_t DC_I2C_SW_ABORTED; + uint8_t DC_I2C_SW_DONE; + uint8_t DC_I2C_SW_STATUS; + uint8_t DC_I2C_STOP_ON_NACK0; + uint8_t DC_I2C_START0; + uint8_t DC_I2C_RW0; + uint8_t DC_I2C_STOP0; + uint8_t DC_I2C_COUNT0; + uint8_t DC_I2C_DATA_RW; + uint8_t DC_I2C_DATA; + uint8_t DC_I2C_INDEX; + uint8_t DC_I2C_INDEX_WRITE; + uint8_t XTAL_REF_DIV; + uint8_t MICROSECOND_TIME_BASE_DIV; + uint8_t DC_I2C_DDC1_SEND_RESET_LENGTH; + uint8_t DC_I2C_REG_RW_CNTL_STATUS; + uint8_t I2C_LIGHT_SLEEP_FORCE; + uint8_t I2C_MEM_PWR_STATE; +}; + +struct dce_i2c_mask { + uint32_t DC_I2C_DDC1_ENABLE; + uint32_t DC_I2C_DDC1_TIME_LIMIT; + uint32_t DC_I2C_DDC1_DATA_DRIVE_EN; + uint32_t DC_I2C_DDC1_CLK_DRIVE_EN; + uint32_t DC_I2C_DDC1_DATA_DRIVE_SEL; + uint32_t DC_I2C_DDC1_INTRA_TRANSACTION_DELAY; + uint32_t DC_I2C_DDC1_INTRA_BYTE_DELAY; + uint32_t DC_I2C_DDC1_HW_STATUS; + uint32_t DC_I2C_SW_DONE_USING_I2C_REG; + uint32_t DC_I2C_SW_USE_I2C_REG_REQ; + uint32_t DC_I2C_NO_QUEUED_SW_GO; + uint32_t DC_I2C_SW_PRIORITY; + uint32_t DC_I2C_SOFT_RESET; + uint32_t DC_I2C_SW_STATUS_RESET; + uint32_t DC_I2C_GO; + uint32_t DC_I2C_SEND_RESET; + uint32_t DC_I2C_TRANSACTION_COUNT; + uint32_t DC_I2C_DDC_SELECT; + uint32_t DC_I2C_DDC1_PRESCALE; + uint32_t DC_I2C_DDC1_THRESHOLD; + uint32_t DC_I2C_DDC1_START_STOP_TIMING_CNTL; + uint32_t DC_I2C_SW_STOPPED_ON_NACK; + uint32_t DC_I2C_SW_TIMEOUT; + uint32_t DC_I2C_SW_ABORTED; + uint32_t DC_I2C_SW_DONE; + uint32_t DC_I2C_SW_STATUS; + uint32_t DC_I2C_STOP_ON_NACK0; + uint32_t DC_I2C_START0; + uint32_t DC_I2C_RW0; + uint32_t DC_I2C_STOP0; + uint32_t DC_I2C_COUNT0; + uint32_t DC_I2C_DATA_RW; + uint32_t DC_I2C_DATA; + uint32_t DC_I2C_INDEX; + uint32_t DC_I2C_INDEX_WRITE; + uint32_t XTAL_REF_DIV; + uint32_t MICROSECOND_TIME_BASE_DIV; + uint32_t DC_I2C_DDC1_SEND_RESET_LENGTH; + uint32_t DC_I2C_REG_RW_CNTL_STATUS; + uint32_t I2C_LIGHT_SLEEP_FORCE; + uint32_t I2C_MEM_PWR_STATE; +}; + +#define I2C_COMMON_MASK_SH_LIST_DCN2(mask_sh)\ + I2C_COMMON_MASK_SH_LIST_DCE110(mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_SEND_RESET_LENGTH, mask_sh) + +#define I2C_COMMON_MASK_SH_LIST_DCN30(mask_sh)\ + I2C_COMMON_MASK_SH_LIST_DCN2(mask_sh),\ + I2C_SF(DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh),\ + I2C_SF(DIO_MEM_PWR_STATUS, I2C_MEM_PWR_STATE, mask_sh) + +struct dce_i2c_registers { + uint32_t SETUP; + uint32_t SPEED; + uint32_t HW_STATUS; + uint32_t DC_I2C_ARBITRATION; + uint32_t DC_I2C_CONTROL; + uint32_t DC_I2C_SW_STATUS; + uint32_t DC_I2C_TRANSACTION0; + uint32_t DC_I2C_TRANSACTION1; + uint32_t DC_I2C_TRANSACTION2; + uint32_t DC_I2C_TRANSACTION3; + uint32_t DC_I2C_DATA; + uint32_t MICROSECOND_TIME_BASE_DIV; + uint32_t DIO_MEM_PWR_CTRL; + uint32_t DIO_MEM_PWR_STATUS; +}; + +enum dce_i2c_transaction_address_space { + DCE_I2C_TRANSACTION_ADDRESS_SPACE_I2C = 1, + DCE_I2C_TRANSACTION_ADDRESS_SPACE_DPCD +}; + +struct i2c_request_transaction_data { + enum dce_i2c_transaction_action action; + enum i2c_channel_operation_result status; + uint8_t address; + uint32_t length; + uint8_t *data; +}; + +struct dce_i2c_hw { + struct ddc *ddc; + uint32_t engine_keep_power_up_count; + uint32_t transaction_count; + uint32_t buffer_used_bytes; + uint32_t buffer_used_write; + uint32_t reference_frequency; + uint32_t default_speed; + uint32_t engine_id; + uint32_t setup_limit; + uint32_t send_reset_length; + uint32_t buffer_size; + struct dc_context *ctx; + + const struct dce_i2c_registers *regs; + const struct dce_i2c_shift *shifts; + const struct dce_i2c_mask *masks; +}; + +void dce_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks); + +void dce100_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks); + +void dce112_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks); + +void dcn1_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks); + +void dcn2_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks); + +bool dce_i2c_submit_command_hw( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd, + struct dce_i2c_hw *dce_i2c_hw); + +struct dce_i2c_hw *acquire_i2c_hw_engine( + struct resource_pool *pool, + struct ddc *ddc); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c new file mode 100644 index 000000000..f1aeb6d19 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c @@ -0,0 +1,500 @@ +/* + * Copyright 2018 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_i2c.h" +#include "dce_i2c_sw.h" +#include "include/gpio_service_interface.h" +#define SCL false +#define SDA true + +void dce_i2c_sw_construct( + struct dce_i2c_sw *dce_i2c_sw, + struct dc_context *ctx) +{ + dce_i2c_sw->ctx = ctx; +} + +static inline bool read_bit_from_ddc( + struct ddc *ddc, + bool data_nor_clock) +{ + uint32_t value = 0; + + if (data_nor_clock) + dal_gpio_get_value(ddc->pin_data, &value); + else + dal_gpio_get_value(ddc->pin_clock, &value); + + return (value != 0); +} + +static inline void write_bit_to_ddc( + struct ddc *ddc, + bool data_nor_clock, + bool bit) +{ + uint32_t value = bit ? 1 : 0; + + if (data_nor_clock) + dal_gpio_set_value(ddc->pin_data, value); + else + dal_gpio_set_value(ddc->pin_clock, value); +} + +static void release_engine_dce_sw( + struct resource_pool *pool, + struct dce_i2c_sw *dce_i2c_sw) +{ + dal_ddc_close(dce_i2c_sw->ddc); + dce_i2c_sw->ddc = NULL; +} + +static bool wait_for_scl_high_sw( + struct dc_context *ctx, + struct ddc *ddc, + uint16_t clock_delay_div_4) +{ + uint32_t scl_retry = 0; + uint32_t scl_retry_max = I2C_SW_TIMEOUT_DELAY / clock_delay_div_4; + + udelay(clock_delay_div_4); + + do { + if (read_bit_from_ddc(ddc, SCL)) + return true; + + udelay(clock_delay_div_4); + + ++scl_retry; + } while (scl_retry <= scl_retry_max); + + return false; +} +static bool write_byte_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t byte) +{ + int32_t shift = 7; + bool ack; + + /* bits are transmitted serially, starting from MSB */ + + do { + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, (byte >> shift) & 1); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + return false; + + write_bit_to_ddc(ddc_handle, SCL, false); + + --shift; + } while (shift >= 0); + + /* The display sends ACK by preventing the SDA from going high + * after the SCL pulse we use to send our last data bit. + * If the SDA goes high after that bit, it's a NACK + */ + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, true); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + return false; + + /* read ACK bit */ + + ack = !read_bit_from_ddc(ddc_handle, SDA); + + udelay(clock_delay_div_4 << 1); + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4 << 1); + + return ack; +} + +static bool read_byte_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t *byte, + bool more) +{ + int32_t shift = 7; + + uint8_t data = 0; + + /* The data bits are read from MSB to LSB; + * bit is read while SCL is high + */ + + do { + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + return false; + + if (read_bit_from_ddc(ddc_handle, SDA)) + data |= (1 << shift); + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4 << 1); + + --shift; + } while (shift >= 0); + + /* read only whole byte */ + + *byte = data; + + udelay(clock_delay_div_4); + + /* send the acknowledge bit: + * SDA low means ACK, SDA high means NACK + */ + + write_bit_to_ddc(ddc_handle, SDA, !more); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + return false; + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, true); + + udelay(clock_delay_div_4); + + return true; +} +static bool stop_sync_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4) +{ + uint32_t retry = 0; + + /* The I2C communications stop signal is: + * the SDA going high from low, while the SCL is high. + */ + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + return false; + + write_bit_to_ddc(ddc_handle, SDA, true); + + do { + udelay(clock_delay_div_4); + + if (read_bit_from_ddc(ddc_handle, SDA)) + return true; + + ++retry; + } while (retry <= 2); + + return false; +} +static bool i2c_write_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t address, + uint32_t length, + const uint8_t *data) +{ + uint32_t i = 0; + + if (!write_byte_sw(ctx, ddc_handle, clock_delay_div_4, address)) + return false; + + while (i < length) { + if (!write_byte_sw(ctx, ddc_handle, clock_delay_div_4, data[i])) + return false; + ++i; + } + + return true; +} + +static bool i2c_read_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t address, + uint32_t length, + uint8_t *data) +{ + uint32_t i = 0; + + if (!write_byte_sw(ctx, ddc_handle, clock_delay_div_4, address)) + return false; + + while (i < length) { + if (!read_byte_sw(ctx, ddc_handle, clock_delay_div_4, data + i, + i < length - 1)) + return false; + ++i; + } + + return true; +} + + + +static bool start_sync_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4) +{ + uint32_t retry = 0; + + /* The I2C communications start signal is: + * the SDA going low from high, while the SCL is high. + */ + + write_bit_to_ddc(ddc_handle, SCL, true); + + udelay(clock_delay_div_4); + + do { + write_bit_to_ddc(ddc_handle, SDA, true); + + if (!read_bit_from_ddc(ddc_handle, SDA)) { + ++retry; + continue; + } + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + break; + + write_bit_to_ddc(ddc_handle, SDA, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4); + + return true; + } while (retry <= I2C_SW_RETRIES); + + return false; +} + +static void dce_i2c_sw_engine_set_speed( + struct dce_i2c_sw *engine, + uint32_t speed) +{ + ASSERT(speed); + + engine->speed = speed ? speed : DCE_I2C_DEFAULT_I2C_SW_SPEED; + + engine->clock_delay = 1000 / engine->speed; + + if (engine->clock_delay < 12) + engine->clock_delay = 12; +} + +static bool dce_i2c_sw_engine_acquire_engine( + struct dce_i2c_sw *engine, + struct ddc *ddc) +{ + enum gpio_result result; + + result = dal_ddc_open(ddc, GPIO_MODE_FAST_OUTPUT, + GPIO_DDC_CONFIG_TYPE_MODE_I2C); + + if (result != GPIO_RESULT_OK) + return false; + + engine->ddc = ddc; + + return true; +} +bool dce_i2c_engine_acquire_sw( + struct dce_i2c_sw *dce_i2c_sw, + struct ddc *ddc_handle) +{ + uint32_t counter = 0; + bool result; + + do { + + result = dce_i2c_sw_engine_acquire_engine( + dce_i2c_sw, ddc_handle); + + if (result) + break; + + /* i2c_engine is busy by VBios, lets wait and retry */ + + udelay(10); + + ++counter; + } while (counter < 2); + + return result; +} + + + + +static void dce_i2c_sw_engine_submit_channel_request( + struct dce_i2c_sw *engine, + struct i2c_request_transaction_data *req) +{ + struct ddc *ddc = engine->ddc; + uint16_t clock_delay_div_4 = engine->clock_delay >> 2; + + /* send sync (start / repeated start) */ + + bool result = start_sync_sw(engine->ctx, ddc, clock_delay_div_4); + + /* process payload */ + + if (result) { + switch (req->action) { + case DCE_I2C_TRANSACTION_ACTION_I2C_WRITE: + case DCE_I2C_TRANSACTION_ACTION_I2C_WRITE_MOT: + result = i2c_write_sw(engine->ctx, ddc, clock_delay_div_4, + req->address, req->length, req->data); + break; + case DCE_I2C_TRANSACTION_ACTION_I2C_READ: + case DCE_I2C_TRANSACTION_ACTION_I2C_READ_MOT: + result = i2c_read_sw(engine->ctx, ddc, clock_delay_div_4, + req->address, req->length, req->data); + break; + default: + result = false; + break; + } + } + + /* send stop if not 'mot' or operation failed */ + + if (!result || + (req->action == DCE_I2C_TRANSACTION_ACTION_I2C_WRITE) || + (req->action == DCE_I2C_TRANSACTION_ACTION_I2C_READ)) + if (!stop_sync_sw(engine->ctx, ddc, clock_delay_div_4)) + result = false; + + req->status = result ? + I2C_CHANNEL_OPERATION_SUCCEEDED : + I2C_CHANNEL_OPERATION_FAILED; +} + +static bool dce_i2c_sw_engine_submit_payload( + struct dce_i2c_sw *engine, + struct i2c_payload *payload, + bool middle_of_transaction) +{ + struct i2c_request_transaction_data request; + + if (!payload->write) + request.action = middle_of_transaction ? + DCE_I2C_TRANSACTION_ACTION_I2C_READ_MOT : + DCE_I2C_TRANSACTION_ACTION_I2C_READ; + else + request.action = middle_of_transaction ? + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE_MOT : + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE; + + request.address = (uint8_t) ((payload->address << 1) | !payload->write); + request.length = payload->length; + request.data = payload->data; + + dce_i2c_sw_engine_submit_channel_request(engine, &request); + + if ((request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY) || + (request.status == I2C_CHANNEL_OPERATION_FAILED)) + return false; + + return true; +} +bool dce_i2c_submit_command_sw( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd, + struct dce_i2c_sw *dce_i2c_sw) +{ + uint8_t index_of_payload = 0; + bool result; + + dce_i2c_sw_engine_set_speed(dce_i2c_sw, cmd->speed); + + result = true; + + while (index_of_payload < cmd->number_of_payloads) { + bool mot = (index_of_payload != cmd->number_of_payloads - 1); + + struct i2c_payload *payload = cmd->payloads + index_of_payload; + + if (!dce_i2c_sw_engine_submit_payload( + dce_i2c_sw, payload, mot)) { + result = false; + break; + } + + ++index_of_payload; + } + + release_engine_dce_sw(pool, dce_i2c_sw); + + return result; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.h b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.h new file mode 100644 index 000000000..019fc47bb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.h @@ -0,0 +1,57 @@ +/* + * Copyright 2018 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_I2C_SW_H__ +#define __DCE_I2C_SW_H__ + +enum { + DCE_I2C_DEFAULT_I2C_SW_SPEED = 50, + I2C_SW_RETRIES = 10, + I2C_SW_TIMEOUT_DELAY = 3000, +}; + +struct dce_i2c_sw { + struct ddc *ddc; + struct dc_context *ctx; + uint32_t clock_delay; + uint32_t speed; +}; + +void dce_i2c_sw_construct( + struct dce_i2c_sw *dce_i2c_sw, + struct dc_context *ctx); + +bool dce_i2c_submit_command_sw( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd, + struct dce_i2c_sw *dce_i2c_sw); + +bool dce_i2c_engine_acquire_sw( + struct dce_i2c_sw *dce_i2c_sw, + struct ddc *ddc_handle); + +#endif + diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.c b/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.c new file mode 100644 index 000000000..34bff9aef --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.c @@ -0,0 +1,311 @@ +/* + * Copyright 2017 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_ipp.h" +#include "reg_helper.h" +#include "dm_services.h" + +#define REG(reg) \ + (ipp_dce->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + ipp_dce->ipp_shift->field_name, ipp_dce->ipp_mask->field_name + +#define CTX \ + ipp_dce->base.ctx + + +static void dce_ipp_cursor_set_position( + struct input_pixel_processor *ipp, + const struct dc_cursor_position *position, + const struct dc_cursor_mi_param *param) +{ + struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); + + /* lock cursor registers */ + REG_UPDATE(CUR_UPDATE, CURSOR_UPDATE_LOCK, true); + + /* Flag passed in structure differentiates cursor enable/disable. */ + /* Update if it differs from cached state. */ + REG_UPDATE(CUR_CONTROL, CURSOR_EN, position->enable); + + REG_SET_2(CUR_POSITION, 0, + CURSOR_X_POSITION, position->x, + CURSOR_Y_POSITION, position->y); + + REG_SET_2(CUR_HOT_SPOT, 0, + CURSOR_HOT_SPOT_X, position->x_hotspot, + CURSOR_HOT_SPOT_Y, position->y_hotspot); + + /* unlock cursor registers */ + REG_UPDATE(CUR_UPDATE, CURSOR_UPDATE_LOCK, false); +} + +static void dce_ipp_cursor_set_attributes( + struct input_pixel_processor *ipp, + const struct dc_cursor_attributes *attributes) +{ + struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); + int mode; + + /* Lock cursor registers */ + REG_UPDATE(CUR_UPDATE, CURSOR_UPDATE_LOCK, true); + + /* Program cursor control */ + switch (attributes->color_format) { + case CURSOR_MODE_MONO: + mode = 0; + break; + case CURSOR_MODE_COLOR_1BIT_AND: + mode = 1; + break; + case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA: + mode = 2; + break; + case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA: + mode = 3; + break; + default: + BREAK_TO_DEBUGGER(); /* unsupported */ + mode = 0; + } + + REG_UPDATE_3(CUR_CONTROL, + CURSOR_MODE, mode, + CURSOR_2X_MAGNIFY, attributes->attribute_flags.bits.ENABLE_MAGNIFICATION, + CUR_INV_TRANS_CLAMP, attributes->attribute_flags.bits.INVERSE_TRANSPARENT_CLAMPING); + + if (attributes->color_format == CURSOR_MODE_MONO) { + REG_SET_3(CUR_COLOR1, 0, + CUR_COLOR1_BLUE, 0, + CUR_COLOR1_GREEN, 0, + CUR_COLOR1_RED, 0); + + REG_SET_3(CUR_COLOR2, 0, + CUR_COLOR2_BLUE, 0xff, + CUR_COLOR2_GREEN, 0xff, + CUR_COLOR2_RED, 0xff); + } + + /* + * Program cursor size -- NOTE: HW spec specifies that HW register + * stores size as (height - 1, width - 1) + */ + REG_SET_2(CUR_SIZE, 0, + CURSOR_WIDTH, attributes->width-1, + CURSOR_HEIGHT, attributes->height-1); + + /* Program cursor surface address */ + /* SURFACE_ADDRESS_HIGH: Higher order bits (39:32) of hardware cursor + * surface base address in byte. It is 4K byte aligned. + * The correct way to program cursor surface address is to first write + * to CUR_SURFACE_ADDRESS_HIGH, and then write to CUR_SURFACE_ADDRESS + */ + REG_SET(CUR_SURFACE_ADDRESS_HIGH, 0, + CURSOR_SURFACE_ADDRESS_HIGH, attributes->address.high_part); + + REG_SET(CUR_SURFACE_ADDRESS, 0, + CURSOR_SURFACE_ADDRESS, attributes->address.low_part); + + /* Unlock Cursor registers. */ + REG_UPDATE(CUR_UPDATE, CURSOR_UPDATE_LOCK, false); +} + + +static void dce_ipp_program_prescale(struct input_pixel_processor *ipp, + struct ipp_prescale_params *params) +{ + struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); + + /* set to bypass mode first before change */ + REG_UPDATE(PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_BYPASS, 1); + + REG_SET_2(PRESCALE_VALUES_GRPH_R, 0, + GRPH_PRESCALE_SCALE_R, params->scale, + GRPH_PRESCALE_BIAS_R, params->bias); + + REG_SET_2(PRESCALE_VALUES_GRPH_G, 0, + GRPH_PRESCALE_SCALE_G, params->scale, + GRPH_PRESCALE_BIAS_G, params->bias); + + REG_SET_2(PRESCALE_VALUES_GRPH_B, 0, + GRPH_PRESCALE_SCALE_B, params->scale, + GRPH_PRESCALE_BIAS_B, params->bias); + + if (params->mode != IPP_PRESCALE_MODE_BYPASS) { + REG_UPDATE(PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_BYPASS, 0); + + /* If prescale is in use, then legacy lut should be bypassed */ + REG_UPDATE(INPUT_GAMMA_CONTROL, + GRPH_INPUT_GAMMA_MODE, 1); + } +} + +static void dce_ipp_program_input_lut( + struct input_pixel_processor *ipp, + const struct dc_gamma *gamma) +{ + int i; + struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); + + /* power on LUT memory */ + if (REG(DCFE_MEM_PWR_CTRL)) + REG_SET(DCFE_MEM_PWR_CTRL, 0, DCP_LUT_MEM_PWR_DIS, 1); + + /* enable all */ + REG_SET(DC_LUT_WRITE_EN_MASK, 0, DC_LUT_WRITE_EN_MASK, 0x7); + + /* 256 entry mode */ + REG_UPDATE(DC_LUT_RW_MODE, DC_LUT_RW_MODE, 0); + + /* LUT-256, unsigned, integer, new u0.12 format */ + REG_SET_3(DC_LUT_CONTROL, 0, + DC_LUT_DATA_R_FORMAT, 3, + DC_LUT_DATA_G_FORMAT, 3, + DC_LUT_DATA_B_FORMAT, 3); + + /* start from index 0 */ + REG_SET(DC_LUT_RW_INDEX, 0, + DC_LUT_RW_INDEX, 0); + + for (i = 0; i < gamma->num_entries; i++) { + REG_SET(DC_LUT_SEQ_COLOR, 0, DC_LUT_SEQ_COLOR, + dc_fixpt_round( + gamma->entries.red[i])); + REG_SET(DC_LUT_SEQ_COLOR, 0, DC_LUT_SEQ_COLOR, + dc_fixpt_round( + gamma->entries.green[i])); + REG_SET(DC_LUT_SEQ_COLOR, 0, DC_LUT_SEQ_COLOR, + dc_fixpt_round( + gamma->entries.blue[i])); + } + + /* power off LUT memory */ + if (REG(DCFE_MEM_PWR_CTRL)) + REG_SET(DCFE_MEM_PWR_CTRL, 0, DCP_LUT_MEM_PWR_DIS, 0); + + /* bypass prescale, enable legacy LUT */ + REG_UPDATE(PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_BYPASS, 1); + REG_UPDATE(INPUT_GAMMA_CONTROL, GRPH_INPUT_GAMMA_MODE, 0); +} + +static void dce_ipp_set_degamma( + struct input_pixel_processor *ipp, + enum ipp_degamma_mode mode) +{ + struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); + uint32_t degamma_type = (mode == IPP_DEGAMMA_MODE_HW_sRGB) ? 1 : 0; + + ASSERT(mode == IPP_DEGAMMA_MODE_BYPASS || mode == IPP_DEGAMMA_MODE_HW_sRGB); + + REG_SET_3(DEGAMMA_CONTROL, 0, + GRPH_DEGAMMA_MODE, degamma_type, + CURSOR_DEGAMMA_MODE, degamma_type, + CURSOR2_DEGAMMA_MODE, degamma_type); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_ipp_set_degamma( + struct input_pixel_processor *ipp, + enum ipp_degamma_mode mode) +{ + struct dce_ipp *ipp_dce = TO_DCE_IPP(ipp); + uint32_t degamma_type = (mode == IPP_DEGAMMA_MODE_HW_sRGB) ? 1 : 0; + + ASSERT(mode == IPP_DEGAMMA_MODE_BYPASS || mode == IPP_DEGAMMA_MODE_HW_sRGB); + /* DCE6 does not have CURSOR2_DEGAMMA_MODE bit in DEGAMMA_CONTROL reg */ + REG_SET_2(DEGAMMA_CONTROL, 0, + GRPH_DEGAMMA_MODE, degamma_type, + CURSOR_DEGAMMA_MODE, degamma_type); +} +#endif + +static const struct ipp_funcs dce_ipp_funcs = { + .ipp_cursor_set_attributes = dce_ipp_cursor_set_attributes, + .ipp_cursor_set_position = dce_ipp_cursor_set_position, + .ipp_program_prescale = dce_ipp_program_prescale, + .ipp_program_input_lut = dce_ipp_program_input_lut, + .ipp_set_degamma = dce_ipp_set_degamma +}; + +#if defined(CONFIG_DRM_AMD_DC_SI) +static const struct ipp_funcs dce60_ipp_funcs = { + .ipp_cursor_set_attributes = dce_ipp_cursor_set_attributes, + .ipp_cursor_set_position = dce_ipp_cursor_set_position, + .ipp_program_prescale = dce_ipp_program_prescale, + .ipp_program_input_lut = dce_ipp_program_input_lut, + .ipp_set_degamma = dce60_ipp_set_degamma +}; +#endif + + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +void dce_ipp_construct( + struct dce_ipp *ipp_dce, + struct dc_context *ctx, + int inst, + const struct dce_ipp_registers *regs, + const struct dce_ipp_shift *ipp_shift, + const struct dce_ipp_mask *ipp_mask) +{ + ipp_dce->base.ctx = ctx; + ipp_dce->base.inst = inst; + ipp_dce->base.funcs = &dce_ipp_funcs; + + ipp_dce->regs = regs; + ipp_dce->ipp_shift = ipp_shift; + ipp_dce->ipp_mask = ipp_mask; +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +void dce60_ipp_construct( + struct dce_ipp *ipp_dce, + struct dc_context *ctx, + int inst, + const struct dce_ipp_registers *regs, + const struct dce_ipp_shift *ipp_shift, + const struct dce_ipp_mask *ipp_mask) +{ + ipp_dce->base.ctx = ctx; + ipp_dce->base.inst = inst; + ipp_dce->base.funcs = &dce60_ipp_funcs; + + ipp_dce->regs = regs; + ipp_dce->ipp_shift = ipp_shift; + ipp_dce->ipp_mask = ipp_mask; +} +#endif + +void dce_ipp_destroy(struct input_pixel_processor **ipp) +{ + kfree(TO_DCE_IPP(*ipp)); + *ipp = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.h b/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.h new file mode 100644 index 000000000..0028d4bdd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_ipp.h @@ -0,0 +1,287 @@ +/* + * Copyright 2017 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_IPP_H_ +#define _DCE_IPP_H_ + +#include "ipp.h" + +#define TO_DCE_IPP(ipp)\ + container_of(ipp, struct dce_ipp, base) + +#define IPP_COMMON_REG_LIST_DCE_BASE(id) \ + SRI(CUR_UPDATE, DCP, id), \ + SRI(CUR_CONTROL, DCP, id), \ + SRI(CUR_POSITION, DCP, id), \ + SRI(CUR_HOT_SPOT, DCP, id), \ + SRI(CUR_COLOR1, DCP, id), \ + SRI(CUR_COLOR2, DCP, id), \ + SRI(CUR_SIZE, DCP, id), \ + SRI(CUR_SURFACE_ADDRESS_HIGH, DCP, id), \ + SRI(CUR_SURFACE_ADDRESS, DCP, id), \ + SRI(PRESCALE_GRPH_CONTROL, DCP, id), \ + SRI(PRESCALE_VALUES_GRPH_R, DCP, id), \ + SRI(PRESCALE_VALUES_GRPH_G, DCP, id), \ + SRI(PRESCALE_VALUES_GRPH_B, DCP, id), \ + SRI(INPUT_GAMMA_CONTROL, DCP, id), \ + SRI(DC_LUT_WRITE_EN_MASK, DCP, id), \ + SRI(DC_LUT_RW_MODE, DCP, id), \ + SRI(DC_LUT_CONTROL, DCP, id), \ + SRI(DC_LUT_RW_INDEX, DCP, id), \ + SRI(DC_LUT_SEQ_COLOR, DCP, id), \ + SRI(DEGAMMA_CONTROL, DCP, id) + +#define IPP_DCE100_REG_LIST_DCE_BASE(id) \ + IPP_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(DCFE_MEM_PWR_CTRL, CRTC, id) + +#define IPP_DCE110_REG_LIST_DCE_BASE(id) \ + IPP_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(DCFE_MEM_PWR_CTRL, DCFE, id) + +#define IPP_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define IPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + IPP_SF(CUR_UPDATE, CURSOR_UPDATE_LOCK, mask_sh), \ + IPP_SF(CUR_CONTROL, CURSOR_EN, mask_sh), \ + IPP_SF(CUR_CONTROL, CURSOR_MODE, mask_sh), \ + IPP_SF(CUR_CONTROL, CURSOR_2X_MAGNIFY, mask_sh), \ + IPP_SF(CUR_CONTROL, CUR_INV_TRANS_CLAMP, mask_sh), \ + IPP_SF(CUR_POSITION, CURSOR_X_POSITION, mask_sh), \ + IPP_SF(CUR_POSITION, CURSOR_Y_POSITION, mask_sh), \ + IPP_SF(CUR_HOT_SPOT, CURSOR_HOT_SPOT_X, mask_sh), \ + IPP_SF(CUR_HOT_SPOT, CURSOR_HOT_SPOT_Y, mask_sh), \ + IPP_SF(CUR_COLOR1, CUR_COLOR1_BLUE, mask_sh), \ + IPP_SF(CUR_COLOR1, CUR_COLOR1_GREEN, mask_sh), \ + IPP_SF(CUR_COLOR1, CUR_COLOR1_RED, mask_sh), \ + IPP_SF(CUR_COLOR2, CUR_COLOR2_BLUE, mask_sh), \ + IPP_SF(CUR_COLOR2, CUR_COLOR2_GREEN, mask_sh), \ + IPP_SF(CUR_COLOR2, CUR_COLOR2_RED, mask_sh), \ + IPP_SF(CUR_SIZE, CURSOR_WIDTH, mask_sh), \ + IPP_SF(CUR_SIZE, CURSOR_HEIGHT, mask_sh), \ + IPP_SF(CUR_SURFACE_ADDRESS_HIGH, CURSOR_SURFACE_ADDRESS_HIGH, mask_sh), \ + IPP_SF(CUR_SURFACE_ADDRESS, CURSOR_SURFACE_ADDRESS, mask_sh), \ + IPP_SF(PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_BYPASS, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_R, GRPH_PRESCALE_SCALE_R, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_R, GRPH_PRESCALE_BIAS_R, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_G, GRPH_PRESCALE_SCALE_G, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_G, GRPH_PRESCALE_BIAS_G, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_B, GRPH_PRESCALE_SCALE_B, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_B, GRPH_PRESCALE_BIAS_B, mask_sh), \ + IPP_SF(INPUT_GAMMA_CONTROL, GRPH_INPUT_GAMMA_MODE, mask_sh), \ + IPP_SF(DC_LUT_WRITE_EN_MASK, DC_LUT_WRITE_EN_MASK, mask_sh), \ + IPP_SF(DC_LUT_RW_MODE, DC_LUT_RW_MODE, mask_sh), \ + IPP_SF(DC_LUT_CONTROL, DC_LUT_DATA_R_FORMAT, mask_sh), \ + IPP_SF(DC_LUT_CONTROL, DC_LUT_DATA_G_FORMAT, mask_sh), \ + IPP_SF(DC_LUT_CONTROL, DC_LUT_DATA_B_FORMAT, mask_sh), \ + IPP_SF(DC_LUT_RW_INDEX, DC_LUT_RW_INDEX, mask_sh), \ + IPP_SF(DC_LUT_SEQ_COLOR, DC_LUT_SEQ_COLOR, mask_sh), \ + IPP_SF(DEGAMMA_CONTROL, GRPH_DEGAMMA_MODE, mask_sh), \ + IPP_SF(DEGAMMA_CONTROL, CURSOR_DEGAMMA_MODE, mask_sh), \ + IPP_SF(DEGAMMA_CONTROL, CURSOR2_DEGAMMA_MODE, mask_sh) + +#define IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + IPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + IPP_SF(DCFE_MEM_PWR_CTRL, DCP_LUT_MEM_PWR_DIS, mask_sh) + +#define IPP_DCE120_MASK_SH_LIST_SOC_BASE(mask_sh) \ + IPP_SF(DCP0_CUR_UPDATE, CURSOR_UPDATE_LOCK, mask_sh), \ + IPP_SF(DCP0_CUR_CONTROL, CURSOR_EN, mask_sh), \ + IPP_SF(DCP0_CUR_CONTROL, CURSOR_MODE, mask_sh), \ + IPP_SF(DCP0_CUR_CONTROL, CURSOR_2X_MAGNIFY, mask_sh), \ + IPP_SF(DCP0_CUR_CONTROL, CUR_INV_TRANS_CLAMP, mask_sh), \ + IPP_SF(DCP0_CUR_POSITION, CURSOR_X_POSITION, mask_sh), \ + IPP_SF(DCP0_CUR_POSITION, CURSOR_Y_POSITION, mask_sh), \ + IPP_SF(DCP0_CUR_HOT_SPOT, CURSOR_HOT_SPOT_X, mask_sh), \ + IPP_SF(DCP0_CUR_HOT_SPOT, CURSOR_HOT_SPOT_Y, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR1, CUR_COLOR1_BLUE, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR1, CUR_COLOR1_GREEN, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR1, CUR_COLOR1_RED, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR2, CUR_COLOR2_BLUE, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR2, CUR_COLOR2_GREEN, mask_sh), \ + IPP_SF(DCP0_CUR_COLOR2, CUR_COLOR2_RED, mask_sh), \ + IPP_SF(DCP0_CUR_SIZE, CURSOR_WIDTH, mask_sh), \ + IPP_SF(DCP0_CUR_SIZE, CURSOR_HEIGHT, mask_sh), \ + IPP_SF(DCP0_CUR_SURFACE_ADDRESS_HIGH, CURSOR_SURFACE_ADDRESS_HIGH, mask_sh), \ + IPP_SF(DCP0_CUR_SURFACE_ADDRESS, CURSOR_SURFACE_ADDRESS, mask_sh), \ + IPP_SF(DCP0_PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_BYPASS, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_R, GRPH_PRESCALE_SCALE_R, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_R, GRPH_PRESCALE_BIAS_R, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_G, GRPH_PRESCALE_SCALE_G, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_G, GRPH_PRESCALE_BIAS_G, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_B, GRPH_PRESCALE_SCALE_B, mask_sh), \ + IPP_SF(DCP0_PRESCALE_VALUES_GRPH_B, GRPH_PRESCALE_BIAS_B, mask_sh), \ + IPP_SF(DCP0_INPUT_GAMMA_CONTROL, GRPH_INPUT_GAMMA_MODE, mask_sh), \ + IPP_SF(DCFE0_DCFE_MEM_PWR_CTRL, DCP_LUT_MEM_PWR_DIS, mask_sh), \ + IPP_SF(DCP0_DC_LUT_WRITE_EN_MASK, DC_LUT_WRITE_EN_MASK, mask_sh), \ + IPP_SF(DCP0_DC_LUT_RW_MODE, DC_LUT_RW_MODE, mask_sh), \ + IPP_SF(DCP0_DC_LUT_CONTROL, DC_LUT_DATA_R_FORMAT, mask_sh), \ + IPP_SF(DCP0_DC_LUT_CONTROL, DC_LUT_DATA_G_FORMAT, mask_sh), \ + IPP_SF(DCP0_DC_LUT_CONTROL, DC_LUT_DATA_B_FORMAT, mask_sh), \ + IPP_SF(DCP0_DC_LUT_RW_INDEX, DC_LUT_RW_INDEX, mask_sh), \ + IPP_SF(DCP0_DC_LUT_SEQ_COLOR, DC_LUT_SEQ_COLOR, mask_sh), \ + IPP_SF(DCP0_DEGAMMA_CONTROL, GRPH_DEGAMMA_MODE, mask_sh), \ + IPP_SF(DCP0_DEGAMMA_CONTROL, CURSOR_DEGAMMA_MODE, mask_sh), \ + IPP_SF(DCP0_DEGAMMA_CONTROL, CURSOR2_DEGAMMA_MODE, mask_sh) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define IPP_DCE60_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + IPP_SF(CUR_UPDATE, CURSOR_UPDATE_LOCK, mask_sh), \ + IPP_SF(CUR_CONTROL, CURSOR_EN, mask_sh), \ + IPP_SF(CUR_CONTROL, CURSOR_MODE, mask_sh), \ + IPP_SF(CUR_CONTROL, CURSOR_2X_MAGNIFY, mask_sh), \ + IPP_SF(CUR_CONTROL, CUR_INV_TRANS_CLAMP, mask_sh), \ + IPP_SF(CUR_POSITION, CURSOR_X_POSITION, mask_sh), \ + IPP_SF(CUR_POSITION, CURSOR_Y_POSITION, mask_sh), \ + IPP_SF(CUR_HOT_SPOT, CURSOR_HOT_SPOT_X, mask_sh), \ + IPP_SF(CUR_HOT_SPOT, CURSOR_HOT_SPOT_Y, mask_sh), \ + IPP_SF(CUR_COLOR1, CUR_COLOR1_BLUE, mask_sh), \ + IPP_SF(CUR_COLOR1, CUR_COLOR1_GREEN, mask_sh), \ + IPP_SF(CUR_COLOR1, CUR_COLOR1_RED, mask_sh), \ + IPP_SF(CUR_COLOR2, CUR_COLOR2_BLUE, mask_sh), \ + IPP_SF(CUR_COLOR2, CUR_COLOR2_GREEN, mask_sh), \ + IPP_SF(CUR_COLOR2, CUR_COLOR2_RED, mask_sh), \ + IPP_SF(CUR_SIZE, CURSOR_WIDTH, mask_sh), \ + IPP_SF(CUR_SIZE, CURSOR_HEIGHT, mask_sh), \ + IPP_SF(CUR_SURFACE_ADDRESS_HIGH, CURSOR_SURFACE_ADDRESS_HIGH, mask_sh), \ + IPP_SF(CUR_SURFACE_ADDRESS, CURSOR_SURFACE_ADDRESS, mask_sh), \ + IPP_SF(PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_BYPASS, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_R, GRPH_PRESCALE_SCALE_R, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_R, GRPH_PRESCALE_BIAS_R, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_G, GRPH_PRESCALE_SCALE_G, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_G, GRPH_PRESCALE_BIAS_G, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_B, GRPH_PRESCALE_SCALE_B, mask_sh), \ + IPP_SF(PRESCALE_VALUES_GRPH_B, GRPH_PRESCALE_BIAS_B, mask_sh), \ + IPP_SF(INPUT_GAMMA_CONTROL, GRPH_INPUT_GAMMA_MODE, mask_sh), \ + IPP_SF(DC_LUT_WRITE_EN_MASK, DC_LUT_WRITE_EN_MASK, mask_sh), \ + IPP_SF(DC_LUT_RW_MODE, DC_LUT_RW_MODE, mask_sh), \ + IPP_SF(DC_LUT_CONTROL, DC_LUT_DATA_R_FORMAT, mask_sh), \ + IPP_SF(DC_LUT_CONTROL, DC_LUT_DATA_G_FORMAT, mask_sh), \ + IPP_SF(DC_LUT_CONTROL, DC_LUT_DATA_B_FORMAT, mask_sh), \ + IPP_SF(DC_LUT_RW_INDEX, DC_LUT_RW_INDEX, mask_sh), \ + IPP_SF(DC_LUT_SEQ_COLOR, DC_LUT_SEQ_COLOR, mask_sh), \ + IPP_SF(DEGAMMA_CONTROL, GRPH_DEGAMMA_MODE, mask_sh), \ + IPP_SF(DEGAMMA_CONTROL, CURSOR_DEGAMMA_MODE, mask_sh) +#endif + +#define IPP_REG_FIELD_LIST(type) \ + type CURSOR_UPDATE_LOCK; \ + type CURSOR_EN; \ + type CURSOR_X_POSITION; \ + type CURSOR_Y_POSITION; \ + type CURSOR_HOT_SPOT_X; \ + type CURSOR_HOT_SPOT_Y; \ + type CURSOR_MODE; \ + type CURSOR_2X_MAGNIFY; \ + type CUR_INV_TRANS_CLAMP; \ + type CUR_COLOR1_BLUE; \ + type CUR_COLOR1_GREEN; \ + type CUR_COLOR1_RED; \ + type CUR_COLOR2_BLUE; \ + type CUR_COLOR2_GREEN; \ + type CUR_COLOR2_RED; \ + type CURSOR_WIDTH; \ + type CURSOR_HEIGHT; \ + type CURSOR_SURFACE_ADDRESS_HIGH; \ + type CURSOR_SURFACE_ADDRESS; \ + type GRPH_PRESCALE_BYPASS; \ + type GRPH_PRESCALE_SCALE_R; \ + type GRPH_PRESCALE_BIAS_R; \ + type GRPH_PRESCALE_SCALE_G; \ + type GRPH_PRESCALE_BIAS_G; \ + type GRPH_PRESCALE_SCALE_B; \ + type GRPH_PRESCALE_BIAS_B; \ + type GRPH_INPUT_GAMMA_MODE; \ + type DCP_LUT_MEM_PWR_DIS; \ + type DC_LUT_WRITE_EN_MASK; \ + type DC_LUT_RW_MODE; \ + type DC_LUT_DATA_R_FORMAT; \ + type DC_LUT_DATA_G_FORMAT; \ + type DC_LUT_DATA_B_FORMAT; \ + type DC_LUT_RW_INDEX; \ + type DC_LUT_SEQ_COLOR; \ + type GRPH_DEGAMMA_MODE; \ + type CURSOR_DEGAMMA_MODE; \ + type CURSOR2_DEGAMMA_MODE + +struct dce_ipp_shift { + IPP_REG_FIELD_LIST(uint8_t); +}; + +struct dce_ipp_mask { + IPP_REG_FIELD_LIST(uint32_t); +}; + +struct dce_ipp_registers { + uint32_t CUR_UPDATE; + uint32_t CUR_CONTROL; + uint32_t CUR_POSITION; + uint32_t CUR_HOT_SPOT; + uint32_t CUR_COLOR1; + uint32_t CUR_COLOR2; + uint32_t CUR_SIZE; + uint32_t CUR_SURFACE_ADDRESS_HIGH; + uint32_t CUR_SURFACE_ADDRESS; + uint32_t PRESCALE_GRPH_CONTROL; + uint32_t PRESCALE_VALUES_GRPH_R; + uint32_t PRESCALE_VALUES_GRPH_G; + uint32_t PRESCALE_VALUES_GRPH_B; + uint32_t INPUT_GAMMA_CONTROL; + uint32_t DCFE_MEM_PWR_CTRL; + uint32_t DC_LUT_WRITE_EN_MASK; + uint32_t DC_LUT_RW_MODE; + uint32_t DC_LUT_CONTROL; + uint32_t DC_LUT_RW_INDEX; + uint32_t DC_LUT_SEQ_COLOR; + uint32_t DEGAMMA_CONTROL; +}; + +struct dce_ipp { + struct input_pixel_processor base; + const struct dce_ipp_registers *regs; + const struct dce_ipp_shift *ipp_shift; + const struct dce_ipp_mask *ipp_mask; +}; + +void dce_ipp_construct(struct dce_ipp *ipp_dce, + struct dc_context *ctx, + int inst, + const struct dce_ipp_registers *regs, + const struct dce_ipp_shift *ipp_shift, + const struct dce_ipp_mask *ipp_mask); + +#if defined(CONFIG_DRM_AMD_DC_SI) +void dce60_ipp_construct(struct dce_ipp *ipp_dce, + struct dc_context *ctx, + int inst, + const struct dce_ipp_registers *regs, + const struct dce_ipp_shift *ipp_shift, + const struct dce_ipp_mask *ipp_mask); +#endif + +void dce_ipp_destroy(struct input_pixel_processor **ipp); + +#endif /* _DCE_IPP_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 000000000..09260c23c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c @@ -0,0 +1,1816 @@ +/* + * 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 DMU_MEM_PWR_CNTL__DMCU_IRAM_MEM_PWR_STATE__SHIFT +#define DMU_MEM_PWR_CNTL__DMCU_IRAM_MEM_PWR_STATE__SHIFT 0xa +#endif + +#ifndef DMU_MEM_PWR_CNTL__DMCU_IRAM_MEM_PWR_STATE_MASK +#define DMU_MEM_PWR_CNTL__DMCU_IRAM_MEM_PWR_STATE_MASK 0x00000400L +#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 DC_LOGGER \ + enc110->base.ctx->logger + +#define REG(reg)\ + (enc110->link_regs->reg) + +#define AUX_REG(reg)\ + (enc110->aux_regs->reg) + +#define HPD_REG(reg)\ + (enc110->hpd_regs->reg) + +#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 +#define DCE110_DIG_FE_SOURCE_SELECT_DIGG 0x40 + +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, + .enable_lvds_output = dce110_link_encoder_enable_lvds_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, + .psr_program_dp_dphy_fast_training = + dce110_psr_program_dp_dphy_fast_training, + .psr_program_secondary_packet = dce110_psr_program_secondary_packet, + .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, + .is_dig_enabled = dce110_is_dig_enabled, + .destroy = dce110_link_encoder_destroy, + .get_max_link_cap = dce110_link_encoder_get_max_link_cap, + .get_dig_frontend = dce110_get_dig_frontend, +}; + +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) +{ + 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 */ + 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); + +} + +unsigned int dce110_get_dig_frontend(struct link_encoder *enc) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + u32 value; + enum engine_id result; + + REG_GET(DIG_BE_CNTL, DIG_FE_SOURCE_SELECT, &value); + + switch (value) { + case DCE110_DIG_FE_SOURCE_SELECT_DIGA: + result = ENGINE_ID_DIGA; + break; + case DCE110_DIG_FE_SOURCE_SELECT_DIGB: + result = ENGINE_ID_DIGB; + break; + case DCE110_DIG_FE_SOURCE_SELECT_DIGC: + result = ENGINE_ID_DIGC; + break; + case DCE110_DIG_FE_SOURCE_SELECT_DIGD: + result = ENGINE_ID_DIGD; + break; + case DCE110_DIG_FE_SOURCE_SELECT_DIGE: + result = ENGINE_ID_DIGE; + break; + case DCE110_DIG_FE_SOURCE_SELECT_DIGF: + result = ENGINE_ID_DIGF; + break; + case DCE110_DIG_FE_SOURCE_SELECT_DIGG: + result = ENGINE_ID_DIGG; + break; + default: + // invalid source select DIG + result = ENGINE_ID_UNKNOWN; + } + + return result; +} + +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 */ + disable_prbs_mode(enc110); +} + +static void setup_panel_mode( + struct dce110_link_encoder *enc110, + enum dp_panel_mode panel_mode) +{ + uint32_t value; + struct dc_context *ctx = enc110->base.ctx; + + /* if psp set panel mode, dal should be program it */ + if (ctx->dc->caps.psp_setup_panel_mode) + return; + + 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); +} + +static void set_dp_phy_pattern_symbol_error( + struct dce110_link_encoder *enc110) +{ + /* Disable PHY Bypass mode to setup the test pattern */ + enable_phy_bypass_mode(enc110, false); + + /* program correct panel mode*/ + setup_panel_mode(enc110, DP_PANEL_MODE_DEFAULT); + + /* 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_cp2520_2( + struct dce110_link_encoder *enc110, + unsigned int cp2520_pattern) +{ + + /* 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); + + /* ensure normal panel mode. */ + setup_panel_mode(enc110, DP_PANEL_MODE_DEFAULT); + + /* no vbid after BS (SR) + * DP_LINK_FRAMING_CNTL changed history Sandra Liu + * 11000260 / 11000104 / 110000FC */ + REG_UPDATE_3(DP_LINK_FRAMING_CNTL, + DP_IDLE_BS_INTERVAL, 0xFC, + DP_VBID_DISABLE, 1, + DP_VID_ENHANCED_FRAME_MODE, 1); + + /* swap every BS with SR */ + REG_UPDATE(DP_DPHY_SCRAM_CNTL, DPHY_SCRAMBLER_BS_COUNT, 0); + + /* select cp2520 patterns */ + if (REG(DP_DPHY_HBR2_PATTERN_CONTROL)) + REG_UPDATE(DP_DPHY_HBR2_PATTERN_CONTROL, + DP_DPHY_HBR2_PATTERN_CONTROL, cp2520_pattern); + else + /* pre-DCE11 can only generate CP2520 pattern 2 */ + ASSERT(cp2520_pattern == 2); + + /* set link training complete */ + set_link_training_complete(enc110, true); + + /* disable 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); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_set_dp_phy_pattern_hbr2_compliance_cp2520_2( + struct dce110_link_encoder *enc110, + unsigned int cp2520_pattern) +{ + + /* 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); + + /* ensure normal panel mode. */ + setup_panel_mode(enc110, DP_PANEL_MODE_DEFAULT); + + /* no vbid after BS (SR) + * DP_LINK_FRAMING_CNTL changed history Sandra Liu + * 11000260 / 11000104 / 110000FC */ + REG_UPDATE_3(DP_LINK_FRAMING_CNTL, + DP_IDLE_BS_INTERVAL, 0xFC, + DP_VBID_DISABLE, 1, + DP_VID_ENHANCED_FRAME_MODE, 1); + + /* DCE6 has no DP_DPHY_SCRAM_CNTL register, skip swap BS with SR */ + + /* select cp2520 patterns */ + if (REG(DP_DPHY_HBR2_PATTERN_CONTROL)) + REG_UPDATE(DP_DPHY_HBR2_PATTERN_CONTROL, + DP_DPHY_HBR2_PATTERN_CONTROL, cp2520_pattern); + else + /* pre-DCE11 can only generate CP2520 pattern 2 */ + ASSERT(cp2520_pattern == 2); + + /* set link training complete */ + set_link_training_complete(enc110, true); + + /* disable 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); +} +#endif + +static void set_dp_phy_pattern_passthrough_mode( + struct dce110_link_encoder *enc110, + enum dp_panel_mode panel_mode) +{ + /* program correct panel mode */ + setup_panel_mode(enc110, panel_mode); + + /* restore LINK_FRAMING_CNTL and DPHY_SCRAMBLER_BS_COUNT + * in case we were doing HBR2 compliance pattern before + */ + REG_UPDATE_3(DP_LINK_FRAMING_CNTL, + DP_IDLE_BS_INTERVAL, 0x2000, + DP_VBID_DISABLE, 0, + DP_VID_ENHANCED_FRAME_MODE, 1); + + 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 */ + disable_prbs_mode(enc110); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_set_dp_phy_pattern_passthrough_mode( + struct dce110_link_encoder *enc110, + enum dp_panel_mode panel_mode) +{ + /* program correct panel mode */ + setup_panel_mode(enc110, panel_mode); + + /* restore LINK_FRAMING_CNTL + * in case we were doing HBR2 compliance pattern before + */ + REG_UPDATE_3(DP_LINK_FRAMING_CNTL, + DP_IDLE_BS_INTERVAL, 0x2000, + DP_VBID_DISABLE, 0, + DP_VID_ENHANCED_FRAME_MODE, 1); + + /* DCE6 has no DP_DPHY_SCRAM_CNTL register, skip DPHY_SCRAMBLER_BS_COUNT restore */ + + /* 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 */ + disable_prbs_mode(enc110); +} +#endif + +/* 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; + case ENGINE_ID_DIGG: + return DCE110_DIG_FE_SOURCE_SELECT_DIGG; + 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); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_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); + + /* DCE6 has no DP_DPHY_SCRAM_CNTL register, skip setup scrambler */ +} +#endif + +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); + +} + +void dce110_psr_program_dp_dphy_fast_training(struct link_encoder *enc, + bool exit_link_training_required) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + + if (exit_link_training_required) + 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); + } +} + +void dce110_psr_program_secondary_packet(struct link_encoder *enc, + unsigned int sdp_transmit_line_num_deadline) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + + REG_UPDATE_2(DP_SEC_CNTL1, + DP_SEC_GSP0_LINE_NUM, sdp_transmit_line_num_deadline, + DP_SEC_GSP0_PRIORITY, 1); +} + +bool dce110_is_dig_enabled(struct link_encoder *enc) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + 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 */ + setup_panel_mode(enc110, DP_PANEL_MODE_DEFAULT); +} + +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 (signal == SIGNAL_TYPE_DVI_DUAL_LINK) + max_pixel_clock *= 2; + + /* 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_hdmi_pixel_clock; + + /* DVI only support RGB pixel encoding */ + if (crtc_timing->pixel_encoding != PIXEL_ENCODING_RGB) + return false; + + /*connect DVI via adpater's HDMI connector*/ + if ((connector_signal == SIGNAL_TYPE_DVI_SINGLE_LINK || + connector_signal == SIGNAL_TYPE_HDMI_TYPE_A) && + signal != SIGNAL_TYPE_HDMI_TYPE_A && + crtc_timing->pix_clk_100hz > (TMDS_MAX_PIXEL_CLOCK * 10)) + return false; + if (crtc_timing->pix_clk_100hz < (TMDS_MIN_PIXEL_CLOCK * 10)) + return false; + + if (crtc_timing->pix_clk_100hz > (max_pixel_clock * 10)) + 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 < crtc_timing->display_color_depth) + return false; + + if (crtc_timing->display_color_depth < COLOR_DEPTH_888) + 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)) + return false; + + /* DCE11 HW does not support 420 */ + if (!enc110->base.features.hdmi_ycbcr420_supported && + crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) + return false; + + if ((!enc110->base.features.flags.bits.HDMI_6GB_EN || + enc110->base.ctx->dc->debug.hdmi20_disable) && + adjusted_pix_clk_khz >= 300000) + return false; + if (enc110->base.ctx->dc->debug.hdmi20_disable && + crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) + return false; + return true; +} + +bool dce110_link_encoder_validate_dp_output( + const struct dce110_link_encoder *enc110, + const struct dc_crtc_timing *crtc_timing) +{ + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) + return false; + + return true; +} + +void dce110_link_encoder_construct( + struct dce110_link_encoder *enc110, + const struct encoder_init_data *init_data, + const struct encoder_feature_support *enc_features, + 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) +{ + struct bp_encoder_cap_info bp_cap_info = {0}; + const struct dc_vbios_funcs *bp_funcs = init_data->ctx->dc_bios->funcs; + enum bp_result result = BP_RESULT_OK; + + 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.preferred_engine = ENGINE_ID_UNKNOWN; + + enc110->base.features = *enc_features; + + enc110->base.transmitter = init_data->transmitter; + + /* 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; + case TRANSMITTER_UNIPHY_G: + enc110->base.preferred_engine = ENGINE_ID_DIGG; + break; + default: + ASSERT_CRITICAL(false); + enc110->base.preferred_engine = ENGINE_ID_UNKNOWN; + } + + /* default to one to mirror Windows behavior */ + enc110->base.features.flags.bits.HDMI_6GB_EN = 1; + + result = bp_funcs->get_encoder_cap_info(enc110->base.ctx->dc_bios, + enc110->base.id, &bp_cap_info); + + /* Override features with DCE-specific values */ + if (BP_RESULT_OK == result) { + enc110->base.features.flags.bits.IS_HBR2_CAPABLE = + bp_cap_info.DP_HBR2_EN; + enc110->base.features.flags.bits.IS_HBR3_CAPABLE = + bp_cap_info.DP_HBR3_EN; + enc110->base.features.flags.bits.HDMI_6GB_EN = bp_cap_info.HDMI_6GB_EN; + } else { + DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", + __func__, + result); + } + if (enc110->base.ctx->dc->debug.hdmi20_disable) { + enc110->base.features.flags.bits.HDMI_6GB_EN = 0; + } +} + +bool dce110_link_encoder_validate_output_with_stream( + struct link_encoder *enc, + const struct dc_stream_state *stream) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + bool is_valid; + + switch (stream->signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + is_valid = dce110_link_encoder_validate_dvi_output( + enc110, + stream->link->connector_signal, + stream->signal, + &stream->timing); + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + is_valid = dce110_link_encoder_validate_hdmi_output( + enc110, + &stream->timing, + stream->phy_pix_clk); + break; + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + is_valid = dce110_link_encoder_validate_dp_output( + enc110, &stream->timing); + break; + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_LVDS: + is_valid = + (stream->timing. + pixel_encoding == PIXEL_ENCODING_RGB) ? true : false; + break; + case SIGNAL_TYPE_VIRTUAL: + is_valid = true; + break; + default: + is_valid = false; + 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 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; + + if (enc110->base.connector.id == CONNECTOR_ID_EDP) + cntl.signal = SIGNAL_TYPE_EDP; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + DC_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); + + } + 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) +{ + kfree(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, + enum signal_type signal, + uint32_t pixel_clock) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + cntl.connector_obj_id = enc110->base.connector; + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = enc->preferred_engine; + cntl.transmitter = enc110->base.transmitter; + cntl.pll_id = clock_source; + cntl.signal = signal; + if (cntl.signal == SIGNAL_TYPE_DVI_DUAL_LINK) + cntl.lanes_number = 8; + else + 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) { + DC_LOG_ERROR("%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} + +/* TODO: still need depth or just pass in adjusted pixel clock? */ +void dce110_link_encoder_enable_lvds_output( + struct link_encoder *enc, + enum clock_source_id clock_source, + uint32_t pixel_clock) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + cntl.connector_obj_id = enc110->base.connector; + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = enc->preferred_engine; + cntl.transmitter = enc110->base.transmitter; + cntl.pll_id = clock_source; + cntl.signal = SIGNAL_TYPE_LVDS; + cntl.lanes_number = 4; + + cntl.hpd_sel = enc110->base.hpd_source; + + cntl.pixel_clock = pixel_clock; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + DC_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 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.connector_obj_id = enc110->base.connector; + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = enc->preferred_engine; + 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) { + DC_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 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) { + DC_LOG_ERROR("%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +/* enables DP PHY output */ +static void dce60_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 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. + */ + dce60_configure_encoder(enc110, link_settings); + cntl.connector_obj_id = enc110->base.connector; + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = enc->preferred_engine; + 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) { + DC_LOG_ERROR("%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} + +/* enables DP PHY output in MST mode */ +static void dce60_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 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. + */ + dce60_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) { + DC_LOG_ERROR("%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} +#endif + +/* + * @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 bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + if (!dce110_is_dig_enabled(enc)) { + /* 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) { + DC_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); +} + +void dce110_link_encoder_dp_set_lane_settings( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]) +{ + 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->lane_count; + cntl.hpd_sel = enc110->base.hpd_source; + cntl.pixel_clock = link_settings->link_rate * + LINK_RATE_REF_FREQ_IN_KHZ; + + for (lane = 0; lane < link_settings->lane_count; lane++) { + /* translate lane settings */ + + training_lane_set.bits.VOLTAGE_SWING_SET = + lane_settings[lane].VOLTAGE_SWING; + training_lane_set.bits.PRE_EMPHASIS_SET = + lane_settings[lane].PRE_EMPHASIS; + + /* post cursor 2 setting only applies to HBR2 link rate */ + if (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 = + 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_TRAINING_PATTERN4: + dce110_link_encoder_set_dp_phy_pattern_training_pattern(enc, 3); + 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_CP2520_1: + set_dp_phy_pattern_hbr2_compliance_cp2520_2(enc110, 1); + break; + case DP_TEST_PATTERN_CP2520_2: + set_dp_phy_pattern_hbr2_compliance_cp2520_2(enc110, 2); + break; + case DP_TEST_PATTERN_CP2520_3: + set_dp_phy_pattern_hbr2_compliance_cp2520_2(enc110, 3); + 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; + } +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +/* set DP PHY test and training patterns */ +static void dce60_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_TRAINING_PATTERN4: + dce110_link_encoder_set_dp_phy_pattern_training_pattern(enc, 3); + 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_CP2520_1: + dce60_set_dp_phy_pattern_hbr2_compliance_cp2520_2(enc110, 1); + break; + case DP_TEST_PATTERN_CP2520_2: + dce60_set_dp_phy_pattern_hbr2_compliance_cp2520_2(enc110, 2); + break; + case DP_TEST_PATTERN_CP2520_3: + dce60_set_dp_phy_pattern_hbr2_compliance_cp2520_2(enc110, 3); + break; + case DP_TEST_PATTERN_VIDEO_MODE: { + dce60_set_dp_phy_pattern_passthrough_mode( + enc110, param->dp_panel_mode); + break; + } + + default: + /* invalid phy pattern */ + ASSERT_CRITICAL(false); + break; + } +} +#endif + +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 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); + + 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_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); +} + +void dce110_link_encoder_get_max_link_cap(struct link_encoder *enc, + struct dc_link_settings *link_settings) +{ + /* Set Default link settings */ + struct dc_link_settings max_link_cap = {LANE_COUNT_FOUR, LINK_RATE_HIGH, + LINK_SPREAD_05_DOWNSPREAD_30KHZ, false, 0}; + + /* Higher link settings based on feature supported */ + if (enc->features.flags.bits.IS_HBR2_CAPABLE) + max_link_cap.link_rate = LINK_RATE_HIGH2; + + if (enc->features.flags.bits.IS_HBR3_CAPABLE) + max_link_cap.link_rate = LINK_RATE_HIGH3; + + *link_settings = max_link_cap; +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static const struct link_encoder_funcs dce60_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 = dce60_link_encoder_enable_dp_output, + .enable_dp_mst_output = dce60_link_encoder_enable_dp_mst_output, + .enable_lvds_output = dce110_link_encoder_enable_lvds_output, + .disable_output = dce110_link_encoder_disable_output, + .dp_set_lane_settings = dce110_link_encoder_dp_set_lane_settings, + .dp_set_phy_pattern = dce60_link_encoder_dp_set_phy_pattern, + .update_mst_stream_allocation_table = + dce110_link_encoder_update_mst_stream_allocation_table, + .psr_program_dp_dphy_fast_training = + dce110_psr_program_dp_dphy_fast_training, + .psr_program_secondary_packet = dce110_psr_program_secondary_packet, + .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, + .is_dig_enabled = dce110_is_dig_enabled, + .destroy = dce110_link_encoder_destroy, + .get_max_link_cap = dce110_link_encoder_get_max_link_cap, + .get_dig_frontend = dce110_get_dig_frontend +}; + +void dce60_link_encoder_construct( + struct dce110_link_encoder *enc110, + const struct encoder_init_data *init_data, + const struct encoder_feature_support *enc_features, + 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) +{ + struct bp_encoder_cap_info bp_cap_info = {0}; + const struct dc_vbios_funcs *bp_funcs = init_data->ctx->dc_bios->funcs; + enum bp_result result = BP_RESULT_OK; + + enc110->base.funcs = &dce60_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.preferred_engine = ENGINE_ID_UNKNOWN; + + enc110->base.features = *enc_features; + + enc110->base.transmitter = init_data->transmitter; + + /* 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; + case TRANSMITTER_UNIPHY_G: + enc110->base.preferred_engine = ENGINE_ID_DIGG; + break; + default: + ASSERT_CRITICAL(false); + enc110->base.preferred_engine = ENGINE_ID_UNKNOWN; + } + + /* default to one to mirror Windows behavior */ + enc110->base.features.flags.bits.HDMI_6GB_EN = 1; + + result = bp_funcs->get_encoder_cap_info(enc110->base.ctx->dc_bios, + enc110->base.id, &bp_cap_info); + + /* Override features with DCE-specific values */ + if (BP_RESULT_OK == result) { + enc110->base.features.flags.bits.IS_HBR2_CAPABLE = + bp_cap_info.DP_HBR2_EN; + enc110->base.features.flags.bits.IS_HBR3_CAPABLE = + bp_cap_info.DP_HBR3_EN; + enc110->base.features.flags.bits.HDMI_6GB_EN = bp_cap_info.HDMI_6GB_EN; + } else { + DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", + __func__, + result); + } + if (enc110->base.ctx->dc->debug.hdmi20_disable) { + enc110->base.features.flags.bits.HDMI_6GB_EN = 0; + } +} +#endif 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 000000000..261c70e01 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h @@ -0,0 +1,320 @@ +/* + * 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) + +/* Not found regs in dce120 spec + * BIOS_SCRATCH_2 + * DP_DPHY_INTERNAL_CTRL + */ + +#define AUX_REG_LIST(id)\ + SRI(AUX_CONTROL, DP_AUX, id), \ + SRI(AUX_DPHY_RX_CONTROL0, DP_AUX, id), \ + SRI(AUX_DPHY_RX_CONTROL1, DP_AUX, id) + +#define HPD_REG_LIST(id)\ + SRI(DC_HPD_CONTROL, HPD, id) + +#define LE_COMMON_REG_LIST_BASE(id) \ + SR(DMCU_RAM_ACCESS_CTRL), \ + SR(DMCU_IRAM_RD_CTRL), \ + SR(DMCU_IRAM_RD_DATA), \ + SR(DMCU_INTERRUPT_TO_UC_EN_MASK), \ + 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(DCI_MEM_PWR_STATUS) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define LE_DCE60_REG_LIST(id)\ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ + SR(DMCU_RAM_ACCESS_CTRL), \ + SR(DMCU_IRAM_RD_CTRL), \ + SR(DMCU_IRAM_RD_DATA), \ + SR(DMCU_INTERRUPT_TO_UC_EN_MASK), \ + 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_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) +#endif + +#define LE_DCE80_REG_LIST(id)\ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \ + LE_COMMON_REG_LIST_BASE(id) + +#define LE_DCE100_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(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), \ + SRI(DP_DPHY_HBR2_PATTERN_CONTROL, DP, id), \ + SR(DCI_MEM_PWR_STATUS) + +#define LE_DCE120_REG_LIST(id)\ + LE_COMMON_REG_LIST_BASE(id), \ + SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \ + SRI(DP_DPHY_HBR2_PATTERN_CONTROL, DP, id), \ + SR(DCI_MEM_PWR_STATUS) + +#define LE_DCN10_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), \ + SRI(DP_DPHY_HBR2_PATTERN_CONTROL, DP, id) + +struct dce110_link_enc_aux_registers { + uint32_t AUX_CONTROL; + uint32_t AUX_DPHY_RX_CONTROL0; + uint32_t AUX_DPHY_RX_CONTROL1; +}; + +struct dce110_link_enc_hpd_registers { + uint32_t DC_HPD_CONTROL; +}; + +struct dce110_link_enc_registers { + /* DMCU registers */ + 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 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; + + /* 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_DPHY_HBR2_PATTERN_CONTROL; + 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; +}; + + +void dce110_link_encoder_construct( + struct dce110_link_encoder *enc110, + const struct encoder_init_data *init_data, + const struct encoder_feature_support *enc_features, + 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); + +#if defined(CONFIG_DRM_AMD_DC_SI) +void dce60_link_encoder_construct( + struct dce110_link_encoder *enc110, + const struct encoder_init_data *init_data, + const struct encoder_feature_support *enc_features, + 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); +#endif + +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, + const struct dc_stream_state *stream); + +/****************** 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, + enum signal_type signal, + 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); + +/* enables LVDS PHY output */ +void dce110_link_encoder_enable_lvds_output( + struct link_encoder *enc, + enum clock_source_id clock_source, + uint32_t pixel_clock); + +/* disable PHY output */ +void dce110_link_encoder_disable_output( + struct link_encoder *enc, + enum signal_type signal); + +/* set DP lane settings */ +void dce110_link_encoder_dp_set_lane_settings( + struct link_encoder *enc, + const struct dc_link_settings *link_settings, + const struct dc_lane_settings lane_settings[LANE_COUNT_DP_MAX]); + +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_connect_dig_be_to_fe( + struct link_encoder *enc, + enum engine_id engine, + bool connect); + +unsigned int dce110_get_dig_frontend(struct link_encoder *enc); + +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); + +void dce110_psr_program_dp_dphy_fast_training(struct link_encoder *enc, + bool exit_link_training_required); + +void dce110_psr_program_secondary_packet(struct link_encoder *enc, + unsigned int sdp_transmit_line_num_deadline); + +bool dce110_is_dig_enabled(struct link_encoder *enc); + +void dce110_link_encoder_get_max_link_cap(struct link_encoder *enc, + struct dc_link_settings *link_settings); + +#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 000000000..4cdd4dacb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.c @@ -0,0 +1,984 @@ +/* + * 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_mem_input.h" +#include "reg_helper.h" +#include "basics/conversion.h" + +#define CTX \ + dce_mi->base.ctx +#define REG(reg)\ + dce_mi->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + dce_mi->shifts->field_name, dce_mi->masks->field_name + +struct pte_setting { + unsigned int bpp; + unsigned int page_width; + unsigned int page_height; + unsigned char min_pte_before_flip_horiz_scan; + unsigned char min_pte_before_flip_vert_scan; + unsigned char pte_req_per_chunk; + unsigned char param_6; + unsigned char param_7; + unsigned char param_8; +}; + +enum mi_bits_per_pixel { + mi_bpp_8 = 0, + mi_bpp_16, + mi_bpp_32, + mi_bpp_64, + mi_bpp_count, +}; + +enum mi_tiling_format { + mi_tiling_linear = 0, + mi_tiling_1D, + mi_tiling_2D, + mi_tiling_count, +}; + +static const struct pte_setting pte_settings[mi_tiling_count][mi_bpp_count] = { + [mi_tiling_linear] = { + { 8, 4096, 1, 8, 0, 1, 0, 0, 0}, + { 16, 2048, 1, 8, 0, 1, 0, 0, 0}, + { 32, 1024, 1, 8, 0, 1, 0, 0, 0}, + { 64, 512, 1, 8, 0, 1, 0, 0, 0}, /* new for 64bpp from HW */ + }, + [mi_tiling_1D] = { + { 8, 512, 8, 1, 0, 1, 0, 0, 0}, /* 0 for invalid */ + { 16, 256, 8, 2, 0, 1, 0, 0, 0}, + { 32, 128, 8, 4, 0, 1, 0, 0, 0}, + { 64, 64, 8, 4, 0, 1, 0, 0, 0}, /* fake */ + }, + [mi_tiling_2D] = { + { 8, 64, 64, 8, 8, 1, 4, 0, 0}, + { 16, 64, 32, 8, 16, 1, 8, 0, 0}, + { 32, 32, 32, 16, 16, 1, 8, 0, 0}, + { 64, 8, 32, 16, 16, 1, 8, 0, 0}, /* fake */ + }, +}; + +static enum mi_bits_per_pixel get_mi_bpp( + enum surface_pixel_format format) +{ + if (format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616) + return mi_bpp_64; + else if (format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB8888) + return mi_bpp_32; + else if (format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB1555) + return mi_bpp_16; + else + return mi_bpp_8; +} + +static enum mi_tiling_format get_mi_tiling( + union dc_tiling_info *tiling_info) +{ + switch (tiling_info->gfx8.array_mode) { + case DC_ARRAY_1D_TILED_THIN1: + case DC_ARRAY_1D_TILED_THICK: + case DC_ARRAY_PRT_TILED_THIN1: + return mi_tiling_1D; + case DC_ARRAY_2D_TILED_THIN1: + case DC_ARRAY_2D_TILED_THICK: + case DC_ARRAY_2D_TILED_X_THICK: + case DC_ARRAY_PRT_2D_TILED_THIN1: + case DC_ARRAY_PRT_2D_TILED_THICK: + return mi_tiling_2D; + case DC_ARRAY_LINEAR_GENERAL: + case DC_ARRAY_LINEAR_ALLIGNED: + return mi_tiling_linear; + default: + return mi_tiling_2D; + } +} + +static bool is_vert_scan(enum dc_rotation_angle rotation) +{ + switch (rotation) { + case ROTATION_ANGLE_90: + case ROTATION_ANGLE_270: + return true; + default: + return false; + } +} + +static void dce_mi_program_pte_vm( + struct mem_input *mi, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + enum dc_rotation_angle rotation) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + enum mi_bits_per_pixel mi_bpp = get_mi_bpp(format); + enum mi_tiling_format mi_tiling = get_mi_tiling(tiling_info); + const struct pte_setting *pte = &pte_settings[mi_tiling][mi_bpp]; + + unsigned int page_width = log_2(pte->page_width); + unsigned int page_height = log_2(pte->page_height); + unsigned int min_pte_before_flip = is_vert_scan(rotation) ? + pte->min_pte_before_flip_vert_scan : + pte->min_pte_before_flip_horiz_scan; + + REG_UPDATE(GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, + GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, 0x7f); + + REG_UPDATE_3(DVMM_PTE_CONTROL, + DVMM_PAGE_WIDTH, page_width, + DVMM_PAGE_HEIGHT, page_height, + DVMM_MIN_PTE_BEFORE_FLIP, min_pte_before_flip); + + REG_UPDATE_2(DVMM_PTE_ARB_CONTROL, + DVMM_PTE_REQ_PER_CHUNK, pte->pte_req_per_chunk, + DVMM_MAX_PTE_REQ_OUTSTANDING, 0x7f); +} + +static void program_urgency_watermark( + struct dce_mem_input *dce_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); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_program_urgency_watermark( + struct dce_mem_input *dce_mi, + uint32_t wm_select, + uint32_t urgency_low_wm, + uint32_t urgency_high_wm) +{ + REG_UPDATE(DPG_PIPE_ARBITRATION_CONTROL3, + 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); +} +#endif + +static void dce120_program_urgency_watermark( + struct dce_mem_input *dce_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); + + REG_SET_2(DPG_PIPE_URGENT_LEVEL_CONTROL, 0, + URGENT_LEVEL_LOW_WATERMARK, urgency_low_wm, + URGENT_LEVEL_HIGH_WATERMARK, urgency_high_wm); + +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_program_nbp_watermark( + struct dce_mem_input *dce_mi, + uint32_t wm_select, + uint32_t nbp_wm) +{ + REG_UPDATE(DPG_PIPE_NB_PSTATE_CHANGE_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); +} +#endif + +static void program_nbp_watermark( + struct dce_mem_input *dce_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); + } + + if (REG(DPG_PIPE_LOW_POWER_CONTROL)) { + REG_UPDATE(DPG_WATERMARK_MASK_CONTROL, + PSTATE_CHANGE_WATERMARK_MASK, wm_select); + + REG_UPDATE_3(DPG_PIPE_LOW_POWER_CONTROL, + PSTATE_CHANGE_ENABLE, 1, + PSTATE_CHANGE_URGENT_DURING_REQUEST, 1, + PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST, 1); + + REG_UPDATE(DPG_PIPE_LOW_POWER_CONTROL, + PSTATE_CHANGE_WATERMARK, nbp_wm); + } +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_program_stutter_watermark( + struct dce_mem_input *dce_mi, + uint32_t wm_select, + uint32_t stutter_mark) +{ + REG_UPDATE(DPG_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK, wm_select); + + REG_UPDATE(DPG_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK, stutter_mark); +} +#endif + +static void dce120_program_stutter_watermark( + struct dce_mem_input *dce_mi, + uint32_t wm_select, + uint32_t stutter_mark, + uint32_t stutter_entry) +{ + REG_UPDATE(DPG_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK, wm_select); + + if (REG(DPG_PIPE_STUTTER_CONTROL2)) + REG_UPDATE_2(DPG_PIPE_STUTTER_CONTROL2, + STUTTER_EXIT_SELF_REFRESH_WATERMARK, stutter_mark, + STUTTER_ENTER_SELF_REFRESH_WATERMARK, stutter_entry); + else + REG_UPDATE_2(DPG_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK, stutter_mark, + STUTTER_ENTER_SELF_REFRESH_WATERMARK, stutter_entry); +} + +static void program_stutter_watermark( + struct dce_mem_input *dce_mi, + uint32_t wm_select, + uint32_t stutter_mark) +{ + REG_UPDATE(DPG_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK, wm_select); + + if (REG(DPG_PIPE_STUTTER_CONTROL2)) + REG_UPDATE(DPG_PIPE_STUTTER_CONTROL2, + STUTTER_EXIT_SELF_REFRESH_WATERMARK, stutter_mark); + else + REG_UPDATE(DPG_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK, stutter_mark); +} + +static void dce_mi_program_display_marks( + struct mem_input *mi, + struct dce_watermarks nbp, + struct dce_watermarks stutter_exit, + struct dce_watermarks stutter_enter, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + uint32_t stutter_en = mi->ctx->dc->debug.disable_stutter ? 0 : 1; + + program_urgency_watermark(dce_mi, 2, /* set a */ + urgent.a_mark, total_dest_line_time_ns); + program_urgency_watermark(dce_mi, 1, /* 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(dce_mi, 2, nbp.a_mark); /* set a */ + program_nbp_watermark(dce_mi, 1, nbp.d_mark); /* set d */ + + program_stutter_watermark(dce_mi, 2, stutter_exit.a_mark); /* set a */ + program_stutter_watermark(dce_mi, 1, stutter_exit.d_mark); /* set d */ +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_mi_program_display_marks( + struct mem_input *mi, + struct dce_watermarks nbp, + struct dce_watermarks stutter_exit, + struct dce_watermarks stutter_enter, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + uint32_t stutter_en = mi->ctx->dc->debug.disable_stutter ? 0 : 1; + + dce60_program_urgency_watermark(dce_mi, 2, /* set a */ + urgent.a_mark, total_dest_line_time_ns); + dce60_program_urgency_watermark(dce_mi, 1, /* 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); + dce60_program_nbp_watermark(dce_mi, 2, nbp.a_mark); /* set a */ + dce60_program_nbp_watermark(dce_mi, 1, nbp.d_mark); /* set d */ + + dce60_program_stutter_watermark(dce_mi, 2, stutter_exit.a_mark); /* set a */ + dce60_program_stutter_watermark(dce_mi, 1, stutter_exit.d_mark); /* set d */ +} +#endif + +static void dce112_mi_program_display_marks(struct mem_input *mi, + struct dce_watermarks nbp, + struct dce_watermarks stutter_exit, + struct dce_watermarks stutter_entry, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + uint32_t stutter_en = mi->ctx->dc->debug.disable_stutter ? 0 : 1; + + program_urgency_watermark(dce_mi, 0, /* set a */ + urgent.a_mark, total_dest_line_time_ns); + program_urgency_watermark(dce_mi, 1, /* set b */ + urgent.b_mark, total_dest_line_time_ns); + program_urgency_watermark(dce_mi, 2, /* set c */ + urgent.c_mark, total_dest_line_time_ns); + program_urgency_watermark(dce_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(dce_mi, 0, nbp.a_mark); /* set a */ + program_nbp_watermark(dce_mi, 1, nbp.b_mark); /* set b */ + program_nbp_watermark(dce_mi, 2, nbp.c_mark); /* set c */ + program_nbp_watermark(dce_mi, 3, nbp.d_mark); /* set d */ + + program_stutter_watermark(dce_mi, 0, stutter_exit.a_mark); /* set a */ + program_stutter_watermark(dce_mi, 1, stutter_exit.b_mark); /* set b */ + program_stutter_watermark(dce_mi, 2, stutter_exit.c_mark); /* set c */ + program_stutter_watermark(dce_mi, 3, stutter_exit.d_mark); /* set d */ +} + +static void dce120_mi_program_display_marks(struct mem_input *mi, + struct dce_watermarks nbp, + struct dce_watermarks stutter_exit, + struct dce_watermarks stutter_entry, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + uint32_t stutter_en = mi->ctx->dc->debug.disable_stutter ? 0 : 1; + + dce120_program_urgency_watermark(dce_mi, 0, /* set a */ + urgent.a_mark, total_dest_line_time_ns); + dce120_program_urgency_watermark(dce_mi, 1, /* set b */ + urgent.b_mark, total_dest_line_time_ns); + dce120_program_urgency_watermark(dce_mi, 2, /* set c */ + urgent.c_mark, total_dest_line_time_ns); + dce120_program_urgency_watermark(dce_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(dce_mi, 0, nbp.a_mark); /* set a */ + program_nbp_watermark(dce_mi, 1, nbp.b_mark); /* set b */ + program_nbp_watermark(dce_mi, 2, nbp.c_mark); /* set c */ + program_nbp_watermark(dce_mi, 3, nbp.d_mark); /* set d */ + + dce120_program_stutter_watermark(dce_mi, 0, stutter_exit.a_mark, stutter_entry.a_mark); /* set a */ + dce120_program_stutter_watermark(dce_mi, 1, stutter_exit.b_mark, stutter_entry.b_mark); /* set b */ + dce120_program_stutter_watermark(dce_mi, 2, stutter_exit.c_mark, stutter_entry.c_mark); /* set c */ + dce120_program_stutter_watermark(dce_mi, 3, stutter_exit.d_mark, stutter_entry.d_mark); /* set d */ +} + +static void program_tiling( + struct dce_mem_input *dce_mi, const union dc_tiling_info *info) +{ + if (dce_mi->masks->GRPH_SW_MODE) { /* GFX9 */ + REG_UPDATE_6(GRPH_CONTROL, + GRPH_SW_MODE, info->gfx9.swizzle, + GRPH_NUM_BANKS, log_2(info->gfx9.num_banks), + GRPH_NUM_SHADER_ENGINES, log_2(info->gfx9.num_shader_engines), + GRPH_NUM_PIPES, log_2(info->gfx9.num_pipes), + GRPH_COLOR_EXPANSION_MODE, 1, + GRPH_SE_ENABLE, info->gfx9.shaderEnable); + /* TODO: DCP0_GRPH_CONTROL__GRPH_SE_ENABLE where to get info + GRPH_SE_ENABLE, 1, + GRPH_Z, 0); + */ + } + + if (dce_mi->masks->GRPH_MICRO_TILE_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); + */ + } + + if (dce_mi->masks->GRPH_ARRAY_MODE) { /* GFX6 but reuses gfx8 struct */ + REG_UPDATE_8(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, + /* DCE6 has no GRPH_MICRO_TILE_MODE mask */ + 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 dce_mem_input *dce_mi, + enum dc_rotation_angle rotation, + const struct plane_size *plane_size) +{ + const struct rect *in_rect = &plane_size->surface_size; + struct rect hw_rect = plane_size->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->surface_pitch); + + REG_SET(HW_ROTATION, 0, + GRPH_ROTATION_ANGLE, rotation_angles[rotation]); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_program_size( + struct dce_mem_input *dce_mi, + enum dc_rotation_angle rotation, /* not used in DCE6 */ + const struct plane_size *plane_size) +{ + struct rect hw_rect = plane_size->surface_size; + /* DCE6 has no HW rotation, skip rotation_angles declaration */ + + /* DCE6 has no HW rotation, skip ROTATION_ANGLE_* processing */ + + 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->surface_pitch); + + /* DCE6 has no HW_ROTATION register, skip setting rotation_angles */ +} +#endif + +static void program_grph_pixel_format( + struct dce_mem_input *dce_mi, + enum surface_pixel_format format) +{ + uint32_t red_xbar = 0, blue_xbar = 0; /* no swap */ + uint32_t grph_depth = 0, grph_format = 0; + uint32_t sign = 0, floating = 0; + + if (format == SURFACE_PIXEL_FORMAT_GRPH_ABGR8888 || + /*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_ABGR16161616 || + 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_ABGR8888: + 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; + fallthrough; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: /* shouldn't this get float too? */ + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616: + 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); +} + +static void dce_mi_program_surface_config( + struct mem_input *mi, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + struct plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + REG_UPDATE(GRPH_ENABLE, GRPH_ENABLE, 1); + + program_tiling(dce_mi, tiling_info); + program_size_and_rotation(dce_mi, rotation, plane_size); + + if (format >= SURFACE_PIXEL_FORMAT_GRPH_BEGIN && + format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + program_grph_pixel_format(dce_mi, format); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_mi_program_surface_config( + struct mem_input *mi, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + struct plane_size *plane_size, + enum dc_rotation_angle rotation, /* not used in DCE6 */ + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + REG_UPDATE(GRPH_ENABLE, GRPH_ENABLE, 1); + + program_tiling(dce_mi, tiling_info); + dce60_program_size(dce_mi, rotation, plane_size); + + if (format >= SURFACE_PIXEL_FORMAT_GRPH_BEGIN && + format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + program_grph_pixel_format(dce_mi, format); +} +#endif + +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; +} + +static void dce_mi_allocate_dmif( + struct mem_input *mi, + uint32_t h_total, + uint32_t v_total, + uint32_t pix_clk_khz, + uint32_t total_stream_num) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + 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 (dce_mi->wa.single_head_rdreq_dmif_limit) { + uint32_t enable = (total_stream_num > 1) ? 0 : + dce_mi->wa.single_head_rdreq_dmif_limit; + + REG_UPDATE(MC_HUB_RDREQ_DMIF_LIMIT, + ENABLE, enable); + } +} + +static void dce_mi_free_dmif( + struct mem_input *mi, + uint32_t total_stream_num) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mi); + 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, 3500); + + if (dce_mi->wa.single_head_rdreq_dmif_limit) { + uint32_t enable = (total_stream_num > 1) ? 0 : + dce_mi->wa.single_head_rdreq_dmif_limit; + + REG_UPDATE(MC_HUB_RDREQ_DMIF_LIMIT, + ENABLE, enable); + } +} + + +static void program_sec_addr( + struct dce_mem_input *dce_mi, + PHYSICAL_ADDRESS_LOC address) +{ + /*high register MUST be programmed first*/ + REG_SET(GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0, + GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, + address.high_part); + + REG_SET_2(GRPH_SECONDARY_SURFACE_ADDRESS, 0, + GRPH_SECONDARY_SURFACE_ADDRESS, address.low_part >> 8, + GRPH_SECONDARY_DFQ_ENABLE, 0); +} + +static void program_pri_addr( + struct dce_mem_input *dce_mi, + PHYSICAL_ADDRESS_LOC address) +{ + /*high register MUST be programmed first*/ + REG_SET(GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 0, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, + address.high_part); + + REG_SET(GRPH_PRIMARY_SURFACE_ADDRESS, 0, + GRPH_PRIMARY_SURFACE_ADDRESS, + address.low_part >> 8); +} + + +static bool dce_mi_is_flip_pending(struct mem_input *mem_input) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mem_input); + uint32_t update_pending; + + REG_GET(GRPH_UPDATE, GRPH_SURFACE_UPDATE_PENDING, &update_pending); + if (update_pending) + return true; + + mem_input->current_address = mem_input->request_address; + return false; +} + +static bool dce_mi_program_surface_flip_and_addr( + struct mem_input *mem_input, + const struct dc_plane_address *address, + bool flip_immediate) +{ + struct dce_mem_input *dce_mi = TO_DCE_MEM_INPUT(mem_input); + + REG_UPDATE(GRPH_UPDATE, GRPH_UPDATE_LOCK, 1); + + REG_UPDATE( + GRPH_FLIP_CONTROL, + GRPH_SURFACE_UPDATE_H_RETRACE_EN, flip_immediate ? 1 : 0); + + switch (address->type) { + case PLN_ADDR_TYPE_GRAPHICS: + if (address->grph.addr.quad_part == 0) + break; + program_pri_addr(dce_mi, address->grph.addr); + break; + case PLN_ADDR_TYPE_GRPH_STEREO: + if (address->grph_stereo.left_addr.quad_part == 0 || + address->grph_stereo.right_addr.quad_part == 0) + break; + program_pri_addr(dce_mi, address->grph_stereo.left_addr); + program_sec_addr(dce_mi, address->grph_stereo.right_addr); + break; + default: + /* not supported */ + BREAK_TO_DEBUGGER(); + break; + } + + mem_input->request_address = *address; + + if (flip_immediate) + mem_input->current_address = *address; + + REG_UPDATE(GRPH_UPDATE, GRPH_UPDATE_LOCK, 0); + + return true; +} + +static const struct mem_input_funcs dce_mi_funcs = { + .mem_input_program_display_marks = dce_mi_program_display_marks, + .allocate_mem_input = dce_mi_allocate_dmif, + .free_mem_input = dce_mi_free_dmif, + .mem_input_program_surface_flip_and_addr = + dce_mi_program_surface_flip_and_addr, + .mem_input_program_pte_vm = dce_mi_program_pte_vm, + .mem_input_program_surface_config = + dce_mi_program_surface_config, + .mem_input_is_flip_pending = dce_mi_is_flip_pending +}; + +#if defined(CONFIG_DRM_AMD_DC_SI) +static const struct mem_input_funcs dce60_mi_funcs = { + .mem_input_program_display_marks = dce60_mi_program_display_marks, + .allocate_mem_input = dce_mi_allocate_dmif, + .free_mem_input = dce_mi_free_dmif, + .mem_input_program_surface_flip_and_addr = + dce_mi_program_surface_flip_and_addr, + .mem_input_program_pte_vm = dce_mi_program_pte_vm, + .mem_input_program_surface_config = + dce60_mi_program_surface_config, + .mem_input_is_flip_pending = dce_mi_is_flip_pending +}; +#endif + +static const struct mem_input_funcs dce112_mi_funcs = { + .mem_input_program_display_marks = dce112_mi_program_display_marks, + .allocate_mem_input = dce_mi_allocate_dmif, + .free_mem_input = dce_mi_free_dmif, + .mem_input_program_surface_flip_and_addr = + dce_mi_program_surface_flip_and_addr, + .mem_input_program_pte_vm = dce_mi_program_pte_vm, + .mem_input_program_surface_config = + dce_mi_program_surface_config, + .mem_input_is_flip_pending = dce_mi_is_flip_pending +}; + +static const struct mem_input_funcs dce120_mi_funcs = { + .mem_input_program_display_marks = dce120_mi_program_display_marks, + .allocate_mem_input = dce_mi_allocate_dmif, + .free_mem_input = dce_mi_free_dmif, + .mem_input_program_surface_flip_and_addr = + dce_mi_program_surface_flip_and_addr, + .mem_input_program_pte_vm = dce_mi_program_pte_vm, + .mem_input_program_surface_config = + dce_mi_program_surface_config, + .mem_input_is_flip_pending = dce_mi_is_flip_pending +}; + +void dce_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask) +{ + dce_mi->base.ctx = ctx; + + dce_mi->base.inst = inst; + dce_mi->base.funcs = &dce_mi_funcs; + + dce_mi->regs = regs; + dce_mi->shifts = mi_shift; + dce_mi->masks = mi_mask; +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +void dce60_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask) +{ + dce_mem_input_construct(dce_mi, ctx, inst, regs, mi_shift, mi_mask); + dce_mi->base.funcs = &dce60_mi_funcs; +} +#endif + +void dce112_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask) +{ + dce_mem_input_construct(dce_mi, ctx, inst, regs, mi_shift, mi_mask); + dce_mi->base.funcs = &dce112_mi_funcs; +} + +void dce120_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask) +{ + dce_mem_input_construct(dce_mi, ctx, inst, regs, mi_shift, mi_mask); + dce_mi->base.funcs = &dce120_mi_funcs; +} 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 000000000..f98400efd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_mem_input.h @@ -0,0 +1,466 @@ +/* + * 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__ + +#include "dc_hw_types.h" +#include "mem_input.h" + +#define TO_DCE_MEM_INPUT(mem_input)\ + container_of(mem_input, struct dce_mem_input, base) + +#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(GRPH_UPDATE, DCP, id),\ + SRI(GRPH_FLIP_CONTROL, DCP, id),\ + SRI(GRPH_PRIMARY_SURFACE_ADDRESS, DCP, id),\ + SRI(GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, DCP, id),\ + SRI(GRPH_SECONDARY_SURFACE_ADDRESS, DCP, id),\ + SRI(GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 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_DCE_PTE_REG_LIST(id)\ + SRI(DVMM_PTE_CONTROL, DCP, id),\ + SRI(DVMM_PTE_ARB_CONTROL, DCP, id) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define MI_DCE6_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(GRPH_SWAP_CNTL, DCP, id),\ + SRI(PRESCALE_GRPH_CONTROL, DCP, id),\ + SRI(GRPH_UPDATE, DCP, id),\ + SRI(GRPH_FLIP_CONTROL, DCP, id),\ + SRI(GRPH_PRIMARY_SURFACE_ADDRESS, DCP, id),\ + SRI(GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, DCP, id),\ + SRI(GRPH_SECONDARY_SURFACE_ADDRESS, DCP, id),\ + SRI(GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, DCP, id),\ + SRI(DPG_PIPE_ARBITRATION_CONTROL1, DMIF_PG, id),\ + SRI(DPG_PIPE_ARBITRATION_CONTROL3, DMIF_PG, id),\ + SRI(DPG_PIPE_NB_PSTATE_CHANGE_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) +#endif + +#define MI_DCE8_REG_LIST(id)\ + MI_DCE_BASE_REG_LIST(id),\ + SRI(DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, DMIF_PG, id) + +#define MI_DCE11_2_REG_LIST(id)\ + MI_DCE8_REG_LIST(id),\ + SRI(GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, DCP, id) + +#define MI_DCE11_REG_LIST(id)\ + MI_DCE11_2_REG_LIST(id),\ + MI_DCE_PTE_REG_LIST(id) + +#define MI_DCE12_REG_LIST(id)\ + MI_DCE_BASE_REG_LIST(id),\ + MI_DCE_PTE_REG_LIST(id),\ + SRI(GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, DCP, id),\ + SRI(DPG_PIPE_STUTTER_CONTROL2, DMIF_PG, id),\ + SRI(DPG_PIPE_LOW_POWER_CONTROL, DMIF_PG, id),\ + SR(DCHUB_FB_LOCATION),\ + SR(DCHUB_AGP_BASE),\ + SR(DCHUB_AGP_BOT),\ + SR(DCHUB_AGP_TOP) + +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; + uint32_t GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT; + uint32_t DVMM_PTE_CONTROL; + uint32_t DVMM_PTE_ARB_CONTROL; + uint32_t GRPH_UPDATE; + uint32_t GRPH_FLIP_CONTROL; + uint32_t GRPH_PRIMARY_SURFACE_ADDRESS; + uint32_t GRPH_PRIMARY_SURFACE_ADDRESS_HIGH; + uint32_t GRPH_SECONDARY_SURFACE_ADDRESS; + uint32_t GRPH_SECONDARY_SURFACE_ADDRESS_HIGH; + /* DMIF_PG */ + uint32_t DPG_PIPE_ARBITRATION_CONTROL1; +#if defined(CONFIG_DRM_AMD_DC_SI) + uint32_t DPG_PIPE_ARBITRATION_CONTROL3; +#endif + uint32_t DPG_WATERMARK_MASK_CONTROL; + uint32_t DPG_PIPE_URGENCY_CONTROL; + uint32_t DPG_PIPE_URGENT_LEVEL_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; + /*DCHUB*/ + uint32_t DCHUB_FB_LOCATION; + uint32_t DCHUB_AGP_BASE; + uint32_t DCHUB_AGP_BOT; + uint32_t DCHUB_AGP_TOP; +}; + +/* Set_Filed_for_Block */ +#define SFB(blk_name, reg_name, field_name, post_fix)\ + .field_name = blk_name ## reg_name ## __ ## field_name ## post_fix + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define MI_GFX6_TILE_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, GRPH_CONTROL, GRPH_NUM_BANKS, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_BANK_WIDTH, mask_sh),\ + 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_PIPE_CONFIG, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_ARRAY_MODE, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE, mask_sh) +#endif + +#define MI_GFX8_TILE_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, GRPH_CONTROL, GRPH_NUM_BANKS, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_BANK_WIDTH, mask_sh),\ + 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_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),\ + SFB(blk, GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, mask_sh),\ + SFB(blk, GRPH_SECONDARY_SURFACE_ADDRESS, GRPH_SECONDARY_SURFACE_ADDRESS, mask_sh),\ + SFB(blk, GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, mask_sh),\ + SFB(blk, GRPH_PRIMARY_SURFACE_ADDRESS, GRPH_PRIMARY_SURFACE_ADDRESS, mask_sh),\ + SFB(blk, GRPH_UPDATE, GRPH_SURFACE_UPDATE_PENDING, mask_sh),\ + SFB(blk, GRPH_UPDATE, GRPH_UPDATE_LOCK, mask_sh),\ + SFB(blk, GRPH_FLIP_CONTROL, GRPH_SURFACE_UPDATE_H_RETRACE_EN, mask_sh) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define MI_DCP_MASK_SH_LIST_DCE6(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_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, 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),\ + SFB(blk, GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, mask_sh),\ + SFB(blk, GRPH_SECONDARY_SURFACE_ADDRESS, GRPH_SECONDARY_SURFACE_ADDRESS, mask_sh),\ + SFB(blk, GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, mask_sh),\ + SFB(blk, GRPH_PRIMARY_SURFACE_ADDRESS, GRPH_PRIMARY_SURFACE_ADDRESS, mask_sh),\ + SFB(blk, GRPH_UPDATE, GRPH_SURFACE_UPDATE_PENDING, mask_sh),\ + SFB(blk, GRPH_UPDATE, GRPH_UPDATE_LOCK, mask_sh),\ + SFB(blk, GRPH_FLIP_CONTROL, GRPH_SURFACE_UPDATE_H_RETRACE_EN, mask_sh) +#endif + +#define MI_DCP_DCE11_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, mask_sh) + +#define MI_DCP_PTE_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, DVMM_PTE_CONTROL, DVMM_PAGE_WIDTH, mask_sh),\ + SFB(blk, DVMM_PTE_CONTROL, DVMM_PAGE_HEIGHT, mask_sh),\ + SFB(blk, DVMM_PTE_CONTROL, DVMM_MIN_PTE_BEFORE_FLIP, mask_sh),\ + SFB(blk, DVMM_PTE_ARB_CONTROL, DVMM_PTE_REQ_PER_CHUNK, mask_sh),\ + SFB(blk, DVMM_PTE_ARB_CONTROL, DVMM_MAX_PTE_REQ_OUTSTANDING, mask_sh) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define MI_DMIF_PG_MASK_SH_LIST_DCE6(mask_sh, blk)\ + SFB(blk, DPG_PIPE_ARBITRATION_CONTROL1, PIXEL_DURATION, 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_DCE6(mask_sh, blk)\ + SFB(blk, DPG_PIPE_ARBITRATION_CONTROL3, URGENCY_WATERMARK_MASK, mask_sh),\ + SFB(blk, DPG_PIPE_STUTTER_CONTROL, STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK, mask_sh),\ + SFB(blk, DPG_PIPE_STUTTER_CONTROL, STUTTER_EXIT_SELF_REFRESH_WATERMARK, mask_sh),\ + SFB(blk, DPG_PIPE_NB_PSTATE_CHANGE_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_DCE6_MASK_SH_LIST(mask_sh)\ + MI_DCP_MASK_SH_LIST_DCE6(mask_sh, ),\ + MI_DMIF_PG_MASK_SH_LIST_DCE6(mask_sh, ),\ + MI_DMIF_PG_MASK_SH_DCE6(mask_sh, ),\ + MI_GFX6_TILE_MASK_SH_LIST(mask_sh, ) +#endif + +#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_DCE8_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_DCE11_2_MASK_SH_LIST(mask_sh)\ + MI_DCE8_MASK_SH_LIST(mask_sh),\ + MI_DCP_DCE11_MASK_SH_LIST(mask_sh, ) + +#define MI_DCE11_MASK_SH_LIST(mask_sh)\ + MI_DCE11_2_MASK_SH_LIST(mask_sh),\ + MI_DCP_PTE_MASK_SH_LIST(mask_sh, ) + +#define MI_GFX9_TILE_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, GRPH_CONTROL, GRPH_NUM_BANKS, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_SW_MODE, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_SE_ENABLE, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_NUM_SHADER_ENGINES, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_NUM_PIPES, mask_sh),\ + SFB(blk, GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE, mask_sh) + +#define MI_DCE12_DMIF_PG_MASK_SH_LIST(mask_sh, blk)\ + SFB(blk, DPG_PIPE_STUTTER_CONTROL2, STUTTER_EXIT_SELF_REFRESH_WATERMARK, mask_sh),\ + SFB(blk, DPG_PIPE_STUTTER_CONTROL2, STUTTER_ENTER_SELF_REFRESH_WATERMARK, mask_sh),\ + SFB(blk, DPG_PIPE_URGENT_LEVEL_CONTROL, URGENT_LEVEL_LOW_WATERMARK, mask_sh),\ + SFB(blk, DPG_PIPE_URGENT_LEVEL_CONTROL, URGENT_LEVEL_HIGH_WATERMARK, 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_WATERMARK_MASK_CONTROL, PSTATE_CHANGE_WATERMARK_MASK, mask_sh),\ + SFB(blk, DPG_PIPE_LOW_POWER_CONTROL, PSTATE_CHANGE_ENABLE, mask_sh),\ + SFB(blk, DPG_PIPE_LOW_POWER_CONTROL, PSTATE_CHANGE_URGENT_DURING_REQUEST, mask_sh),\ + SFB(blk, DPG_PIPE_LOW_POWER_CONTROL, PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST, mask_sh),\ + SFB(blk, DPG_PIPE_LOW_POWER_CONTROL, PSTATE_CHANGE_WATERMARK, mask_sh) + +#define MI_GFX9_DCHUB_MASK_SH_LIST(mask_sh)\ + SF(DCHUB_FB_LOCATION, FB_TOP, mask_sh),\ + SF(DCHUB_FB_LOCATION, FB_BASE, mask_sh),\ + SF(DCHUB_AGP_BASE, AGP_BASE, mask_sh),\ + SF(DCHUB_AGP_BOT, AGP_BOT, mask_sh),\ + SF(DCHUB_AGP_TOP, AGP_TOP, mask_sh) + +#define MI_DCE12_MASK_SH_LIST(mask_sh)\ + MI_DCP_MASK_SH_LIST(mask_sh, DCP0_),\ + SF(DCP0_GRPH_SECONDARY_SURFACE_ADDRESS, GRPH_SECONDARY_DFQ_ENABLE, mask_sh),\ + MI_DCP_DCE11_MASK_SH_LIST(mask_sh, DCP0_),\ + MI_DCP_PTE_MASK_SH_LIST(mask_sh, DCP0_),\ + MI_DMIF_PG_MASK_SH_LIST(mask_sh, DMIF_PG0_),\ + MI_DCE12_DMIF_PG_MASK_SH_LIST(mask_sh, DMIF_PG0_),\ + MI_GFX9_TILE_MASK_SH_LIST(mask_sh, DCP0_),\ + MI_GFX9_DCHUB_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_PIPE_OUTSTANDING_REQUEST_LIMIT; \ + type DVMM_PAGE_WIDTH; \ + type DVMM_PAGE_HEIGHT; \ + type DVMM_MIN_PTE_BEFORE_FLIP; \ + type DVMM_PTE_REQ_PER_CHUNK; \ + type DVMM_MAX_PTE_REQ_OUTSTANDING; \ + 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_SE_ENABLE; \ + type GRPH_NUM_SHADER_ENGINES; \ + type GRPH_NUM_PIPES; \ + type GRPH_SECONDARY_SURFACE_ADDRESS_HIGH; \ + type GRPH_SECONDARY_SURFACE_ADDRESS; \ + type GRPH_SECONDARY_DFQ_ENABLE; \ + type GRPH_PRIMARY_SURFACE_ADDRESS_HIGH; \ + type GRPH_PRIMARY_SURFACE_ADDRESS; \ + type GRPH_SURFACE_UPDATE_PENDING; \ + type GRPH_SURFACE_UPDATE_H_RETRACE_EN; \ + type GRPH_UPDATE_LOCK; \ + 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 URGENT_LEVEL_LOW_WATERMARK;\ + type URGENT_LEVEL_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 STUTTER_ENTER_SELF_REFRESH_WATERMARK; \ + type DMIF_BUFFERS_ALLOCATED; \ + type DMIF_BUFFERS_ALLOCATION_COMPLETED; \ + type ENABLE; /* MC_HUB_RDREQ_DMIF_LIMIT */\ + type FB_BASE; \ + type FB_TOP; \ + type AGP_BASE; \ + type AGP_TOP; \ + type AGP_BOT; \ + +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 dce_mem_input { + struct mem_input base; + + const struct dce_mem_input_registers *regs; + const struct dce_mem_input_shift *shifts; + const struct dce_mem_input_mask *masks; + + struct dce_mem_input_wa wa; +}; + +void dce_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask); + +#if defined(CONFIG_DRM_AMD_DC_SI) +void dce60_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask); +#endif + +void dce112_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask); + +void dce120_mem_input_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx, + int inst, + const struct dce_mem_input_registers *regs, + const struct dce_mem_input_shift *mi_shift, + const struct dce_mem_input_mask *mi_mask); + +#endif /*__DCE_MEM_INPUT_H__*/ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_opp.c b/drivers/gpu/drm/amd/display/dc/dce/dce_opp.c new file mode 100644 index 000000000..63ae4bc2a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_opp.c @@ -0,0 +1,769 @@ +/* + * 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 "basics/conversion.h" + +#include "dce_opp.h" + +#include "reg_helper.h" + +#define REG(reg)\ + (opp110->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + opp110->opp_shift->field_name, opp110->opp_mask->field_name + +#define CTX \ + opp110->base.ctx + +enum { + MAX_PWL_ENTRY = 128, + MAX_REGIONS_NUMBER = 16 +}; + +enum { + MAX_LUT_ENTRY = 256, + MAX_NUMBER_OF_ENTRIES = 256 +}; + + +enum { + OUTPUT_CSC_MATRIX_SIZE = 12 +}; + + + + + + + + + + + + + + + + + + + + + + +/* + ***************************************************************************** + * Function: regamma_config_regions_and_segments + * + * build regamma curve by using predefined hw points + * uses interface parameters ,like EDID coeff. + * + * @param : parameters interface parameters + * @return void + * + * @note + * + * @see + * + ***************************************************************************** + */ + + + +/* + * set_truncation + * 1) set truncation depth: 0 for 18 bpp or 1 for 24 bpp + * 2) enable truncation + * 3) HW remove 12bit FMT support for DCE11 power saving reason. + */ +static void set_truncation( + struct dce110_opp *opp110, + const struct bit_depth_reduction_params *params) +{ + /*Disable truncation*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 0, + FMT_TRUNCATE_DEPTH, 0, + FMT_TRUNCATE_MODE, 0); + + + if (params->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + /* 8bpc trunc on YCbCr422*/ + if (params->flags.TRUNCATE_DEPTH == 1) + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 1, + FMT_TRUNCATE_DEPTH, 1, + FMT_TRUNCATE_MODE, 0); + else if (params->flags.TRUNCATE_DEPTH == 2) + /* 10bpc trunc on YCbCr422*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 1, + FMT_TRUNCATE_DEPTH, 2, + FMT_TRUNCATE_MODE, 0); + return; + } + /* on other format-to do */ + if (params->flags.TRUNCATE_ENABLED == 0) + return; + /*Set truncation depth and Enable truncation*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 1, + FMT_TRUNCATE_DEPTH, + params->flags.TRUNCATE_DEPTH, + FMT_TRUNCATE_MODE, + params->flags.TRUNCATE_MODE); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +/* + * dce60_set_truncation + * 1) set truncation depth: 0 for 18 bpp or 1 for 24 bpp + * 2) enable truncation + * 3) HW remove 12bit FMT support for DCE11 power saving reason. + */ +static void dce60_set_truncation( + struct dce110_opp *opp110, + const struct bit_depth_reduction_params *params) +{ + /* DCE6 has no FMT_TRUNCATE_MODE bit in FMT_BIT_DEPTH_CONTROL reg */ + + /*Disable truncation*/ + REG_UPDATE_2(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 0, + FMT_TRUNCATE_DEPTH, 0); + + if (params->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + /* 8bpc trunc on YCbCr422*/ + if (params->flags.TRUNCATE_DEPTH == 1) + REG_UPDATE_2(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 1, + FMT_TRUNCATE_DEPTH, 1); + else if (params->flags.TRUNCATE_DEPTH == 2) + /* 10bpc trunc on YCbCr422*/ + REG_UPDATE_2(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 1, + FMT_TRUNCATE_DEPTH, 2); + return; + } + /* on other format-to do */ + if (params->flags.TRUNCATE_ENABLED == 0) + return; + /*Set truncation depth and Enable truncation*/ + REG_UPDATE_2(FMT_BIT_DEPTH_CONTROL, + FMT_TRUNCATE_EN, 1, + FMT_TRUNCATE_DEPTH, + params->flags.TRUNCATE_DEPTH); +} +#endif + +/* + * set_spatial_dither + * 1) set spatial dithering mode: pattern of seed + * 2) set spatial dithering depth: 0 for 18bpp or 1 for 24bpp + * 3) set random seed + * 4) set random mode + * lfsr is reset every frame or not reset + * RGB dithering method + * 0: RGB data are all dithered with x^28+x^3+1 + * 1: R data is dithered with x^28+x^3+1 + * G data is dithered with x^28+X^9+1 + * B data is dithered with x^28+x^13+1 + * enable high pass filter or not + * 5) enable spatical dithering + */ +static void set_spatial_dither( + struct dce110_opp *opp110, + const struct bit_depth_reduction_params *params) +{ + /*Disable spatial (random) dithering*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_EN, 0, + FMT_SPATIAL_DITHER_DEPTH, 0, + FMT_SPATIAL_DITHER_MODE, 0); + + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_HIGHPASS_RANDOM_ENABLE, 0, + FMT_FRAME_RANDOM_ENABLE, 0, + FMT_RGB_RANDOM_ENABLE, 0); + + REG_UPDATE(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN, 0); + + if (params->flags.SPATIAL_DITHER_ENABLED == 0) + return; + + /* only use FRAME_COUNTER_MAX if frameRandom == 1*/ + + if (opp110->opp_mask->FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX && + opp110->opp_mask->FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP) { + if (params->flags.FRAME_RANDOM == 1) { + if (params->flags.SPATIAL_DITHER_DEPTH == 0 || + params->flags.SPATIAL_DITHER_DEPTH == 1) { + REG_UPDATE_2(FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 15, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 2); + } else if (params->flags.SPATIAL_DITHER_DEPTH == 2) { + REG_UPDATE_2(FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 3, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 1); + } else + return; + } else { + REG_UPDATE_2(FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 0, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 0); + } + } + /* Set seed for random values for + * spatial dithering for R,G,B channels + */ + REG_UPDATE(FMT_DITHER_RAND_R_SEED, + FMT_RAND_R_SEED, params->r_seed_value); + + REG_UPDATE(FMT_DITHER_RAND_G_SEED, + FMT_RAND_G_SEED, params->g_seed_value); + + REG_UPDATE(FMT_DITHER_RAND_B_SEED, + FMT_RAND_B_SEED, params->b_seed_value); + + /* FMT_OFFSET_R_Cr 31:16 0x0 Setting the zero + * offset for the R/Cr channel, lower 4LSB + * is forced to zeros. Typically set to 0 + * RGB and 0x80000 YCbCr. + */ + /* FMT_OFFSET_G_Y 31:16 0x0 Setting the zero + * offset for the G/Y channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB + * and 0x80000 YCbCr. + */ + /* FMT_OFFSET_B_Cb 31:16 0x0 Setting the zero + * offset for the B/Cb channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB and + * 0x80000 YCbCr. + */ + + /* Disable High pass filter + * Reset only at startup + * Set RGB data dithered with x^28+x^3+1 + */ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_HIGHPASS_RANDOM_ENABLE, params->flags.HIGHPASS_RANDOM, + FMT_FRAME_RANDOM_ENABLE, params->flags.FRAME_RANDOM, + FMT_RGB_RANDOM_ENABLE, params->flags.RGB_RANDOM); + + /* Set spatial dithering bit depth + * Set spatial dithering mode + * (default is Seed patterrn AAAA...) + * Enable spatial dithering + */ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_DEPTH, params->flags.SPATIAL_DITHER_DEPTH, + FMT_SPATIAL_DITHER_MODE, params->flags.SPATIAL_DITHER_MODE, + FMT_SPATIAL_DITHER_EN, 1); +} + +/* + * SetTemporalDither (Frame Modulation) + * 1) set temporal dither depth + * 2) select pattern: from hard-coded pattern or programmable pattern + * 3) select optimized strips for BGR or RGB LCD sub-pixel + * 4) set s matrix + * 5) set t matrix + * 6) set grey level for 0.25, 0.5, 0.75 + * 7) enable temporal dithering + */ + +static void set_temporal_dither( + struct dce110_opp *opp110, + const struct bit_depth_reduction_params *params) +{ + /*Disable temporal (frame modulation) dithering first*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN, 0, + FMT_TEMPORAL_DITHER_RESET, 0, + FMT_TEMPORAL_DITHER_OFFSET, 0); + + REG_UPDATE_2(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_DEPTH, 0, + FMT_TEMPORAL_LEVEL, 0); + + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_25FRC_SEL, 0, + FMT_50FRC_SEL, 0, + FMT_75FRC_SEL, 0); + + /* no 10bpc dither on DCE11*/ + if (params->flags.FRAME_MODULATION_ENABLED == 0 || + params->flags.FRAME_MODULATION_DEPTH == 2) + return; + + /* Set temporal dithering depth*/ + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_DEPTH, params->flags.FRAME_MODULATION_DEPTH, + FMT_TEMPORAL_DITHER_RESET, 0, + FMT_TEMPORAL_DITHER_OFFSET, 0); + + /*Select legacy pattern based on FRC and Temporal level*/ + if (REG(FMT_TEMPORAL_DITHER_PATTERN_CONTROL)) { + REG_WRITE(FMT_TEMPORAL_DITHER_PATTERN_CONTROL, 0); + /*Set s matrix*/ + REG_WRITE(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX, 0); + /*Set t matrix*/ + REG_WRITE(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX, 0); + } + + /*Select patterns for 0.25, 0.5 and 0.75 grey level*/ + REG_UPDATE(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_LEVEL, params->flags.TEMPORAL_LEVEL); + + REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, + FMT_25FRC_SEL, params->flags.FRC25, + FMT_50FRC_SEL, params->flags.FRC50, + FMT_75FRC_SEL, params->flags.FRC75); + + /*Enable bit reduction by temporal (frame modulation) dithering*/ + REG_UPDATE(FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN, 1); +} + +/* + * Set Clamping + * 1) Set clamping format based on bpc - 0 for 6bpc (No clamping) + * 1 for 8 bpc + * 2 for 10 bpc + * 3 for 12 bpc + * 7 for programable + * 2) Enable clamp if Limited range requested + */ +void dce110_opp_set_clamping( + struct dce110_opp *opp110, + const struct clamping_and_pixel_encoding_params *params) +{ + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 0, + FMT_CLAMP_COLOR_FORMAT, 0); + + switch (params->clamping_level) { + case CLAMPING_FULL_RANGE: + break; + case CLAMPING_LIMITED_RANGE_8BPC: + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 1); + break; + case CLAMPING_LIMITED_RANGE_10BPC: + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 2); + break; + case CLAMPING_LIMITED_RANGE_12BPC: + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 3); + break; + case CLAMPING_LIMITED_RANGE_PROGRAMMABLE: + /*Set clamp control*/ + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 7); + + /*set the defaults*/ + REG_SET_2(FMT_CLAMP_COMPONENT_R, 0, + FMT_CLAMP_LOWER_R, 0x10, + FMT_CLAMP_UPPER_R, 0xFEF); + + REG_SET_2(FMT_CLAMP_COMPONENT_G, 0, + FMT_CLAMP_LOWER_G, 0x10, + FMT_CLAMP_UPPER_G, 0xFEF); + + REG_SET_2(FMT_CLAMP_COMPONENT_B, 0, + FMT_CLAMP_LOWER_B, 0x10, + FMT_CLAMP_UPPER_B, 0xFEF); + break; + default: + break; + } +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +/* + * Set Clamping for DCE6 parts + * 1) Set clamping format based on bpc - 0 for 6bpc (No clamping) + * 1 for 8 bpc + * 2 for 10 bpc + * 3 for 12 bpc + * 7 for programable + * 2) Enable clamp if Limited range requested + */ +static void dce60_opp_set_clamping( + struct dce110_opp *opp110, + const struct clamping_and_pixel_encoding_params *params) +{ + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 0, + FMT_CLAMP_COLOR_FORMAT, 0); + + switch (params->clamping_level) { + case CLAMPING_FULL_RANGE: + break; + case CLAMPING_LIMITED_RANGE_8BPC: + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 1); + break; + case CLAMPING_LIMITED_RANGE_10BPC: + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 2); + break; + case CLAMPING_LIMITED_RANGE_12BPC: + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 3); + break; + case CLAMPING_LIMITED_RANGE_PROGRAMMABLE: + /*Set clamp control*/ + REG_SET_2(FMT_CLAMP_CNTL, 0, + FMT_CLAMP_DATA_EN, 1, + FMT_CLAMP_COLOR_FORMAT, 7); + + /* DCE6 does have FMT_CLAMP_COMPONENT_{R,G,B} registers */ + + break; + default: + break; + } +} +#endif + +/* + * set_pixel_encoding + * + * Set Pixel Encoding + * 0: RGB 4:4:4 or YCbCr 4:4:4 or YOnly + * 1: YCbCr 4:2:2 + */ +static void set_pixel_encoding( + struct dce110_opp *opp110, + const struct clamping_and_pixel_encoding_params *params) +{ + if (opp110->opp_mask->FMT_CBCR_BIT_REDUCTION_BYPASS) + REG_UPDATE_3(FMT_CONTROL, + FMT_PIXEL_ENCODING, 0, + FMT_SUBSAMPLING_MODE, 0, + FMT_CBCR_BIT_REDUCTION_BYPASS, 0); + else + REG_UPDATE_2(FMT_CONTROL, + FMT_PIXEL_ENCODING, 0, + FMT_SUBSAMPLING_MODE, 0); + + if (params->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE_2(FMT_CONTROL, + FMT_PIXEL_ENCODING, 1, + FMT_SUBSAMPLING_ORDER, 0); + } + if (params->pixel_encoding == PIXEL_ENCODING_YCBCR420) { + REG_UPDATE_3(FMT_CONTROL, + FMT_PIXEL_ENCODING, 2, + FMT_SUBSAMPLING_MODE, 2, + FMT_CBCR_BIT_REDUCTION_BYPASS, 1); + } + +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +/* + * dce60_set_pixel_encoding + * DCE6 has no FMT_SUBSAMPLING_{MODE,ORDER} bits in FMT_CONTROL reg + * Set Pixel Encoding + * 0: RGB 4:4:4 or YCbCr 4:4:4 or YOnly + * 1: YCbCr 4:2:2 + */ +static void dce60_set_pixel_encoding( + struct dce110_opp *opp110, + const struct clamping_and_pixel_encoding_params *params) +{ + if (opp110->opp_mask->FMT_CBCR_BIT_REDUCTION_BYPASS) + REG_UPDATE_2(FMT_CONTROL, + FMT_PIXEL_ENCODING, 0, + FMT_CBCR_BIT_REDUCTION_BYPASS, 0); + else + REG_UPDATE(FMT_CONTROL, + FMT_PIXEL_ENCODING, 0); + + if (params->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE(FMT_CONTROL, + FMT_PIXEL_ENCODING, 1); + } + if (params->pixel_encoding == PIXEL_ENCODING_YCBCR420) { + REG_UPDATE_2(FMT_CONTROL, + FMT_PIXEL_ENCODING, 2, + FMT_CBCR_BIT_REDUCTION_BYPASS, 1); + } + +} +#endif + +void dce110_opp_program_bit_depth_reduction( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + set_truncation(opp110, params); + set_spatial_dither(opp110, params); + set_temporal_dither(opp110, params); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_opp_program_bit_depth_reduction( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + dce60_set_truncation(opp110, params); + set_spatial_dither(opp110, params); + set_temporal_dither(opp110, params); +} +#endif + +void dce110_opp_program_clamping_and_pixel_encoding( + struct output_pixel_processor *opp, + const struct clamping_and_pixel_encoding_params *params) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + dce110_opp_set_clamping(opp110, params); + set_pixel_encoding(opp110, params); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_opp_program_clamping_and_pixel_encoding( + struct output_pixel_processor *opp, + const struct clamping_and_pixel_encoding_params *params) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + dce60_opp_set_clamping(opp110, params); + dce60_set_pixel_encoding(opp110, params); +} +#endif + + +static void program_formatter_420_memory(struct output_pixel_processor *opp) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + uint32_t fmt_mem_cntl_value; + + /* Program source select*/ + /* Use HW default source select for FMT_MEMORYx_CONTROL */ + /* Use that value for FMT_SRC_SELECT as well*/ + REG_GET(CONTROL, + FMT420_MEM0_SOURCE_SEL, &fmt_mem_cntl_value); + + REG_UPDATE(FMT_CONTROL, + FMT_SRC_SELECT, fmt_mem_cntl_value); + + /* Turn on the memory */ + REG_UPDATE(CONTROL, + FMT420_MEM0_PWR_FORCE, 0); +} + +void dce110_opp_set_dyn_expansion( + struct output_pixel_processor *opp, + enum dc_color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 0, + FMT_DYNAMIC_EXP_MODE, 0); + + /*00 - 10-bit -> 12-bit dynamic expansion*/ + /*01 - 8-bit -> 12-bit dynamic expansion*/ + if (signal == SIGNAL_TYPE_HDMI_TYPE_A || + signal == SIGNAL_TYPE_DISPLAY_PORT || + signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + switch (color_dpth) { + case COLOR_DEPTH_888: + REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 1, + FMT_DYNAMIC_EXP_MODE, 1); + break; + case COLOR_DEPTH_101010: + REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 1, + FMT_DYNAMIC_EXP_MODE, 0); + break; + case COLOR_DEPTH_121212: + REG_UPDATE_2( + FMT_DYNAMIC_EXP_CNTL, + FMT_DYNAMIC_EXP_EN, 1,/*otherwise last two bits are zero*/ + FMT_DYNAMIC_EXP_MODE, 0); + break; + default: + break; + } + } +} + +static void program_formatter_reset_dig_resync_fifo(struct output_pixel_processor *opp) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + /* clear previous phase lock status*/ + REG_UPDATE(FMT_CONTROL, + FMT_420_PIXEL_PHASE_LOCKED_CLEAR, 1); + + /* poll until FMT_420_PIXEL_PHASE_LOCKED become 1*/ + REG_WAIT(FMT_CONTROL, FMT_420_PIXEL_PHASE_LOCKED, 1, 10, 10); + +} + +void dce110_opp_program_fmt( + struct output_pixel_processor *opp, + struct bit_depth_reduction_params *fmt_bit_depth, + struct clamping_and_pixel_encoding_params *clamping) +{ + /* dithering is affected by <CrtcSourceSelect>, hence should be + * programmed afterwards */ + + if (clamping->pixel_encoding == PIXEL_ENCODING_YCBCR420) + program_formatter_420_memory(opp); + + dce110_opp_program_bit_depth_reduction( + opp, + fmt_bit_depth); + + dce110_opp_program_clamping_and_pixel_encoding( + opp, + clamping); + + if (clamping->pixel_encoding == PIXEL_ENCODING_YCBCR420) + program_formatter_reset_dig_resync_fifo(opp); + + return; +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_opp_program_fmt( + struct output_pixel_processor *opp, + struct bit_depth_reduction_params *fmt_bit_depth, + struct clamping_and_pixel_encoding_params *clamping) +{ + /* dithering is affected by <CrtcSourceSelect>, hence should be + * programmed afterwards */ + + if (clamping->pixel_encoding == PIXEL_ENCODING_YCBCR420) + program_formatter_420_memory(opp); + + dce60_opp_program_bit_depth_reduction( + opp, + fmt_bit_depth); + + dce60_opp_program_clamping_and_pixel_encoding( + opp, + clamping); + + if (clamping->pixel_encoding == PIXEL_ENCODING_YCBCR420) + program_formatter_reset_dig_resync_fifo(opp); + + return; +} +#endif + + + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +static const struct opp_funcs funcs = { + .opp_set_dyn_expansion = dce110_opp_set_dyn_expansion, + .opp_destroy = dce110_opp_destroy, + .opp_program_fmt = dce110_opp_program_fmt, + .opp_program_bit_depth_reduction = dce110_opp_program_bit_depth_reduction +}; + +#if defined(CONFIG_DRM_AMD_DC_SI) +static const struct opp_funcs dce60_opp_funcs = { + .opp_set_dyn_expansion = dce110_opp_set_dyn_expansion, + .opp_destroy = dce110_opp_destroy, + .opp_program_fmt = dce60_opp_program_fmt, + .opp_program_bit_depth_reduction = dce60_opp_program_bit_depth_reduction +}; +#endif + +void dce110_opp_construct(struct dce110_opp *opp110, + struct dc_context *ctx, + uint32_t inst, + const struct dce_opp_registers *regs, + const struct dce_opp_shift *opp_shift, + const struct dce_opp_mask *opp_mask) +{ + opp110->base.funcs = &funcs; + + opp110->base.ctx = ctx; + + opp110->base.inst = inst; + + opp110->regs = regs; + opp110->opp_shift = opp_shift; + opp110->opp_mask = opp_mask; +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +void dce60_opp_construct(struct dce110_opp *opp110, + struct dc_context *ctx, + uint32_t inst, + const struct dce_opp_registers *regs, + const struct dce_opp_shift *opp_shift, + const struct dce_opp_mask *opp_mask) +{ + opp110->base.funcs = &dce60_opp_funcs; + + opp110->base.ctx = ctx; + + opp110->base.inst = inst; + + opp110->regs = regs; + opp110->opp_shift = opp_shift; + opp110->opp_mask = opp_mask; +} +#endif + +void dce110_opp_destroy(struct output_pixel_processor **opp) +{ + if (*opp) + kfree(FROM_DCE11_OPP(*opp)); + *opp = NULL; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_opp.h b/drivers/gpu/drm/amd/display/dc/dce/dce_opp.h new file mode 100644 index 000000000..bf1ffc362 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_opp.h @@ -0,0 +1,357 @@ +/* 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_OPP_DCE_H__ +#define __DC_OPP_DCE_H__ + +#include "dc_types.h" +#include "opp.h" +#include "core_types.h" + +#define FROM_DCE11_OPP(opp)\ + container_of(opp, struct dce110_opp, base) + +enum dce110_opp_reg_type { + DCE110_OPP_REG_DCP = 0, + DCE110_OPP_REG_DCFE, + DCE110_OPP_REG_FMT, + + DCE110_OPP_REG_MAX +}; + +#define OPP_COMMON_REG_LIST_BASE(id) \ + SRI(FMT_DYNAMIC_EXP_CNTL, FMT, id), \ + SRI(FMT_BIT_DEPTH_CONTROL, FMT, id), \ + SRI(FMT_CONTROL, FMT, id), \ + SRI(FMT_DITHER_RAND_R_SEED, FMT, id), \ + SRI(FMT_DITHER_RAND_G_SEED, FMT, id), \ + SRI(FMT_DITHER_RAND_B_SEED, FMT, id), \ + SRI(FMT_CLAMP_CNTL, FMT, id), \ + SRI(FMT_CLAMP_COMPONENT_R, FMT, id), \ + SRI(FMT_CLAMP_COMPONENT_G, FMT, id), \ + SRI(FMT_CLAMP_COMPONENT_B, FMT, id) + +#define OPP_DCE_80_REG_LIST(id) \ + OPP_COMMON_REG_LIST_BASE(id), \ + SRI(FMT_TEMPORAL_DITHER_PATTERN_CONTROL, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX, FMT, id) + +#define OPP_DCE_100_REG_LIST(id) \ + OPP_COMMON_REG_LIST_BASE(id), \ + SRI(FMT_TEMPORAL_DITHER_PATTERN_CONTROL, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX, FMT, id) + +#define OPP_DCE_110_REG_LIST(id) \ + OPP_COMMON_REG_LIST_BASE(id), \ + SRI(FMT_TEMPORAL_DITHER_PATTERN_CONTROL, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX, FMT, id) + +#define OPP_DCE_112_REG_LIST(id) \ + OPP_COMMON_REG_LIST_BASE(id), \ + SRI(FMT_TEMPORAL_DITHER_PATTERN_CONTROL, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX, FMT, id), \ + SRI(FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX, FMT, id), \ + SRI(CONTROL, FMT_MEMORY, id) + +#define OPP_DCE_120_REG_LIST(id) \ + OPP_COMMON_REG_LIST_BASE(id), \ + SRI(CONTROL, FMT_MEMORY, id) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define OPP_DCE_60_REG_LIST(id) \ + SRI(FMT_DYNAMIC_EXP_CNTL, FMT, id), \ + SRI(FMT_BIT_DEPTH_CONTROL, FMT, id), \ + SRI(FMT_CONTROL, FMT, id), \ + SRI(FMT_DITHER_RAND_R_SEED, FMT, id), \ + SRI(FMT_DITHER_RAND_G_SEED, FMT, id), \ + SRI(FMT_DITHER_RAND_B_SEED, FMT, id), \ + SRI(FMT_CLAMP_CNTL, FMT, id) +#endif + +#define OPP_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define OPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh)\ + OPP_SF(FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN, mask_sh),\ + OPP_SF(FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT_DITHER_RAND_R_SEED, FMT_RAND_R_SEED, mask_sh),\ + OPP_SF(FMT_DITHER_RAND_G_SEED, FMT_RAND_G_SEED, mask_sh),\ + OPP_SF(FMT_DITHER_RAND_B_SEED, FMT_RAND_B_SEED, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_RESET, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_OFFSET, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_DEPTH, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_LEVEL, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_25FRC_SEL, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_50FRC_SEL, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_75FRC_SEL, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SRC_SELECT, mask_sh),\ + OPP_SF(FMT_CLAMP_CNTL, FMT_CLAMP_DATA_EN, mask_sh),\ + OPP_SF(FMT_CLAMP_CNTL, FMT_CLAMP_COLOR_FORMAT, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_R, FMT_CLAMP_LOWER_R, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_R, FMT_CLAMP_UPPER_R, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_G, FMT_CLAMP_LOWER_G, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_G, FMT_CLAMP_UPPER_G, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_B, FMT_CLAMP_LOWER_B, mask_sh),\ + OPP_SF(FMT_CLAMP_COMPONENT_B, FMT_CLAMP_UPPER_B, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_PIXEL_ENCODING, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SUBSAMPLING_MODE, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SUBSAMPLING_ORDER, mask_sh) + +#define OPP_COMMON_MASK_SH_LIST_DCE_110(mask_sh)\ + OPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, mask_sh) + +#define OPP_COMMON_MASK_SH_LIST_DCE_100(mask_sh)\ + OPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, mask_sh) + +#define OPP_COMMON_MASK_SH_LIST_DCE_112(mask_sh)\ + OPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh),\ + OPP_SF(FMT_MEMORY0_CONTROL, FMT420_MEM0_SOURCE_SEL, mask_sh),\ + OPP_SF(FMT_MEMORY0_CONTROL, FMT420_MEM0_PWR_FORCE, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_420_PIXEL_PHASE_LOCKED_CLEAR, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_420_PIXEL_PHASE_LOCKED, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_CBCR_BIT_REDUCTION_BYPASS, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, mask_sh) + +#define OPP_COMMON_MASK_SH_LIST_DCE_80(mask_sh)\ + OPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) + +#define OPP_COMMON_MASK_SH_LIST_DCE_120(mask_sh)\ + OPP_SF(FMT0_FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN, mask_sh),\ + OPP_SF(FMT0_FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_RESET, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_OFFSET, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_DEPTH, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_LEVEL, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_25FRC_SEL, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_50FRC_SEL, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_75FRC_SEL, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, mask_sh),\ + OPP_SF(FMT0_FMT_DITHER_RAND_R_SEED, FMT_RAND_R_SEED, mask_sh),\ + OPP_SF(FMT0_FMT_DITHER_RAND_G_SEED, FMT_RAND_G_SEED, mask_sh),\ + OPP_SF(FMT0_FMT_DITHER_RAND_B_SEED, FMT_RAND_B_SEED, mask_sh),\ + OPP_SF(FMT_MEMORY0_CONTROL, FMT420_MEM0_SOURCE_SEL, mask_sh),\ + OPP_SF(FMT_MEMORY0_CONTROL, FMT420_MEM0_PWR_FORCE, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_SRC_SELECT, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_420_PIXEL_PHASE_LOCKED_CLEAR, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_420_PIXEL_PHASE_LOCKED, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_CNTL, FMT_CLAMP_DATA_EN, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_CNTL, FMT_CLAMP_COLOR_FORMAT, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_R, FMT_CLAMP_LOWER_R, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_R, FMT_CLAMP_UPPER_R, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_G, FMT_CLAMP_LOWER_G, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_G, FMT_CLAMP_UPPER_G, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_B, FMT_CLAMP_LOWER_B, mask_sh),\ + OPP_SF(FMT0_FMT_CLAMP_COMPONENT_B, FMT_CLAMP_UPPER_B, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_PIXEL_ENCODING, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_SUBSAMPLING_MODE, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_SUBSAMPLING_ORDER, mask_sh),\ + OPP_SF(FMT0_FMT_CONTROL, FMT_CBCR_BIT_REDUCTION_BYPASS, mask_sh) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define OPP_COMMON_MASK_SH_LIST_DCE_60(mask_sh)\ + OPP_SF(FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN, mask_sh),\ + OPP_SF(FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN, mask_sh),\ + OPP_SF(FMT_DITHER_RAND_R_SEED, FMT_RAND_R_SEED, mask_sh),\ + OPP_SF(FMT_DITHER_RAND_G_SEED, FMT_RAND_G_SEED, mask_sh),\ + OPP_SF(FMT_DITHER_RAND_B_SEED, FMT_RAND_B_SEED, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_RESET, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_OFFSET, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_DEPTH, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_LEVEL, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_25FRC_SEL, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_50FRC_SEL, mask_sh),\ + OPP_SF(FMT_BIT_DEPTH_CONTROL, FMT_75FRC_SEL, mask_sh),\ + OPP_SF(FMT_CLAMP_CNTL, FMT_CLAMP_DATA_EN, mask_sh),\ + OPP_SF(FMT_CLAMP_CNTL, FMT_CLAMP_COLOR_FORMAT, mask_sh),\ + OPP_SF(FMT_CONTROL, FMT_PIXEL_ENCODING, mask_sh) +#endif + +#define OPP_REG_FIELD_LIST(type) \ + type FMT_DYNAMIC_EXP_EN; \ + type FMT_DYNAMIC_EXP_MODE; \ + type FMT_TRUNCATE_EN; \ + type FMT_TRUNCATE_DEPTH; \ + type FMT_TRUNCATE_MODE; \ + type FMT_SPATIAL_DITHER_EN; \ + type FMT_SPATIAL_DITHER_DEPTH; \ + type FMT_SPATIAL_DITHER_MODE; \ + type FMT_TEMPORAL_DITHER_EN; \ + type FMT_TEMPORAL_DITHER_RESET; \ + type FMT_TEMPORAL_DITHER_OFFSET; \ + type FMT_TEMPORAL_DITHER_DEPTH; \ + type FMT_TEMPORAL_LEVEL; \ + type FMT_25FRC_SEL; \ + type FMT_50FRC_SEL; \ + type FMT_75FRC_SEL; \ + type FMT_HIGHPASS_RANDOM_ENABLE; \ + type FMT_FRAME_RANDOM_ENABLE; \ + type FMT_RGB_RANDOM_ENABLE; \ + type FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX; \ + type FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP; \ + type FMT_STEREOSYNC_OVERRIDE; \ + type FMT_RAND_R_SEED; \ + type FMT_RAND_G_SEED; \ + type FMT_RAND_B_SEED; \ + type FMT420_MEM0_SOURCE_SEL; \ + type FMT420_MEM0_PWR_FORCE; \ + type FMT_SRC_SELECT; \ + type FMT_420_PIXEL_PHASE_LOCKED_CLEAR; \ + type FMT_420_PIXEL_PHASE_LOCKED; \ + type FMT_CLAMP_DATA_EN; \ + type FMT_CLAMP_COLOR_FORMAT; \ + type FMT_CLAMP_LOWER_R; \ + type FMT_CLAMP_UPPER_R; \ + type FMT_CLAMP_LOWER_G; \ + type FMT_CLAMP_UPPER_G; \ + type FMT_CLAMP_LOWER_B; \ + type FMT_CLAMP_UPPER_B; \ + type FMT_PIXEL_ENCODING; \ + type FMT_SUBSAMPLING_ORDER; \ + type FMT_SUBSAMPLING_MODE; \ + type FMT_CBCR_BIT_REDUCTION_BYPASS;\ + +struct dce_opp_shift { + OPP_REG_FIELD_LIST(uint8_t) +}; + +struct dce_opp_mask { + OPP_REG_FIELD_LIST(uint32_t) +}; + +struct dce_opp_registers { + uint32_t FMT_DYNAMIC_EXP_CNTL; + uint32_t FMT_BIT_DEPTH_CONTROL; + uint32_t FMT_CONTROL; + uint32_t FMT_DITHER_RAND_R_SEED; + uint32_t FMT_DITHER_RAND_G_SEED; + uint32_t FMT_DITHER_RAND_B_SEED; + uint32_t FMT_TEMPORAL_DITHER_PATTERN_CONTROL; + uint32_t FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX; + uint32_t FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX; + uint32_t CONTROL; + uint32_t FMT_CLAMP_CNTL; + uint32_t FMT_CLAMP_COMPONENT_R; + uint32_t FMT_CLAMP_COMPONENT_G; + uint32_t FMT_CLAMP_COMPONENT_B; +}; + +/* OPP RELATED */ +#define TO_DCE110_OPP(opp)\ + container_of(opp, struct dce110_opp, base) + +struct dce110_opp { + struct output_pixel_processor base; + const struct dce_opp_registers *regs; + const struct dce_opp_shift *opp_shift; + const struct dce_opp_mask *opp_mask; +}; + +void dce110_opp_construct(struct dce110_opp *opp110, + struct dc_context *ctx, + uint32_t inst, + const struct dce_opp_registers *regs, + const struct dce_opp_shift *opp_shift, + const struct dce_opp_mask *opp_mask); + +#if defined(CONFIG_DRM_AMD_DC_SI) +void dce60_opp_construct(struct dce110_opp *opp110, + struct dc_context *ctx, + uint32_t inst, + const struct dce_opp_registers *regs, + const struct dce_opp_shift *opp_shift, + const struct dce_opp_mask *opp_mask); +#endif + +void dce110_opp_destroy(struct output_pixel_processor **opp); + + + +/* FORMATTER RELATED */ +void dce110_opp_program_bit_depth_reduction( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params); + +void dce110_opp_program_clamping_and_pixel_encoding( + struct output_pixel_processor *opp, + const struct clamping_and_pixel_encoding_params *params); + +void dce110_opp_set_dyn_expansion( + struct output_pixel_processor *opp, + enum dc_color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal); + +void dce110_opp_program_fmt( + struct output_pixel_processor *opp, + struct bit_depth_reduction_params *fmt_bit_depth, + struct clamping_and_pixel_encoding_params *clamping); + +void dce110_opp_set_clamping( + struct dce110_opp *opp110, + const struct clamping_and_pixel_encoding_params *params); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_panel_cntl.c b/drivers/gpu/drm/amd/display/dc/dce/dce_panel_cntl.c new file mode 100644 index 000000000..e8570060d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_panel_cntl.c @@ -0,0 +1,293 @@ +/* + * 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 "dc_dmub_srv.h" +#include "panel_cntl.h" +#include "dce_panel_cntl.h" +#include "atom.h" + +#define TO_DCE_PANEL_CNTL(panel_cntl)\ + container_of(panel_cntl, struct dce_panel_cntl, base) + +#define CTX \ + dce_panel_cntl->base.ctx + +#define DC_LOGGER \ + dce_panel_cntl->base.ctx->logger + +#define REG(reg)\ + dce_panel_cntl->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + dce_panel_cntl->shift->field_name, dce_panel_cntl->mask->field_name + +static unsigned int dce_get_16_bit_backlight_from_pwm(struct panel_cntl *panel_cntl) +{ + uint64_t current_backlight; + uint32_t bl_period, bl_int_count; + uint32_t bl_pwm, fractional_duty_cycle_en; + uint32_t bl_period_mask, bl_pwm_mask; + struct dce_panel_cntl *dce_panel_cntl = TO_DCE_PANEL_CNTL(panel_cntl); + + REG_READ(BL_PWM_PERIOD_CNTL); + REG_GET(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD, &bl_period); + REG_GET(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD_BITCNT, &bl_int_count); + + REG_READ(BL_PWM_CNTL); + REG_GET(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, (uint32_t *)(&bl_pwm)); + REG_GET(BL_PWM_CNTL, BL_PWM_FRACTIONAL_EN, &fractional_duty_cycle_en); + + if (bl_int_count == 0) + bl_int_count = 16; + + bl_period_mask = (1 << bl_int_count) - 1; + bl_period &= bl_period_mask; + + bl_pwm_mask = bl_period_mask << (16 - bl_int_count); + + if (fractional_duty_cycle_en == 0) + bl_pwm &= bl_pwm_mask; + else + bl_pwm &= 0xFFFF; + + current_backlight = (uint64_t)bl_pwm << (1 + bl_int_count); + + if (bl_period == 0) + bl_period = 0xFFFF; + + current_backlight = div_u64(current_backlight, bl_period); + current_backlight = (current_backlight + 1) >> 1; + + return (uint32_t)(current_backlight); +} + +static uint32_t dce_panel_cntl_hw_init(struct panel_cntl *panel_cntl) +{ + struct dce_panel_cntl *dce_panel_cntl = TO_DCE_PANEL_CNTL(panel_cntl); + uint32_t value; + uint32_t current_backlight; + + /* 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 (panel_cntl->stored_backlight_registers.BL_PWM_CNTL != 0) { + REG_WRITE(BL_PWM_CNTL, + panel_cntl->stored_backlight_registers.BL_PWM_CNTL); + REG_WRITE(BL_PWM_CNTL2, + panel_cntl->stored_backlight_registers.BL_PWM_CNTL2); + REG_WRITE(BL_PWM_PERIOD_CNTL, + panel_cntl->stored_backlight_registers.BL_PWM_PERIOD_CNTL); + REG_UPDATE(PWRSEQ_REF_DIV, + BL_PWM_REF_DIV, + panel_cntl->stored_backlight_registers.LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV); + } else if ((value != 0) && (value != 1)) { + panel_cntl->stored_backlight_registers.BL_PWM_CNTL = + REG_READ(BL_PWM_CNTL); + panel_cntl->stored_backlight_registers.BL_PWM_CNTL2 = + REG_READ(BL_PWM_CNTL2); + panel_cntl->stored_backlight_registers.BL_PWM_PERIOD_CNTL = + REG_READ(BL_PWM_PERIOD_CNTL); + + REG_GET(PWRSEQ_REF_DIV, BL_PWM_REF_DIV, + &panel_cntl->stored_backlight_registers.LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV); + } else { + /* TODO: Note: This should not really happen since VBIOS + * should have initialized PWM registers on boot. + */ + REG_WRITE(BL_PWM_CNTL, 0x8000FA00); + REG_WRITE(BL_PWM_PERIOD_CNTL, 0x000C0FA0); + } + + // Have driver take backlight control + // TakeBacklightControl(true) + value = REG_READ(BIOS_SCRATCH_2); + value |= ATOM_S2_VRI_BRIGHT_ENABLE; + REG_WRITE(BIOS_SCRATCH_2, value); + + // Enable the backlight output + REG_UPDATE(BL_PWM_CNTL, BL_PWM_EN, 1); + + // Unlock group 2 backlight registers + REG_UPDATE(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_REG_LOCK, 0); + + current_backlight = dce_get_16_bit_backlight_from_pwm(panel_cntl); + + return current_backlight; +} + +static bool dce_is_panel_backlight_on(struct panel_cntl *panel_cntl) +{ + struct dce_panel_cntl *dce_panel_cntl = TO_DCE_PANEL_CNTL(panel_cntl); + uint32_t blon, blon_ovrd, pwrseq_target_state; + + REG_GET_2(PWRSEQ_CNTL, LVTMA_BLON, &blon, LVTMA_BLON_OVRD, &blon_ovrd); + REG_GET(PWRSEQ_CNTL, LVTMA_PWRSEQ_TARGET_STATE, &pwrseq_target_state); + + if (blon_ovrd) + return blon; + else + return pwrseq_target_state; +} + +static bool dce_is_panel_powered_on(struct panel_cntl *panel_cntl) +{ + struct dce_panel_cntl *dce_panel_cntl = TO_DCE_PANEL_CNTL(panel_cntl); + uint32_t pwr_seq_state, dig_on, dig_on_ovrd; + + REG_GET(PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, &pwr_seq_state); + + REG_GET_2(PWRSEQ_CNTL, LVTMA_DIGON, &dig_on, LVTMA_DIGON_OVRD, &dig_on_ovrd); + + return (pwr_seq_state == 1) || (dig_on == 1 && dig_on_ovrd == 1); +} + +static void dce_store_backlight_level(struct panel_cntl *panel_cntl) +{ + struct dce_panel_cntl *dce_panel_cntl = TO_DCE_PANEL_CNTL(panel_cntl); + + panel_cntl->stored_backlight_registers.BL_PWM_CNTL = + REG_READ(BL_PWM_CNTL); + panel_cntl->stored_backlight_registers.BL_PWM_CNTL2 = + REG_READ(BL_PWM_CNTL2); + panel_cntl->stored_backlight_registers.BL_PWM_PERIOD_CNTL = + REG_READ(BL_PWM_PERIOD_CNTL); + + REG_GET(PWRSEQ_REF_DIV, BL_PWM_REF_DIV, + &panel_cntl->stored_backlight_registers.LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV); +} + +static void dce_driver_set_backlight(struct panel_cntl *panel_cntl, + uint32_t backlight_pwm_u16_16) +{ + uint32_t backlight_16bit; + uint32_t masked_pwm_period; + uint8_t bit_count; + uint64_t active_duty_cycle; + uint32_t pwm_period_bitcnt; + struct dce_panel_cntl *dce_panel_cntl = TO_DCE_PANEL_CNTL(panel_cntl); + + /* + * 1. Find 16 bit backlight active duty cycle, where 0 <= backlight + * active duty cycle <= backlight period + */ + + /* 1.1 Apply bitmask for backlight period value based on value of BITCNT + */ + REG_GET_2(BL_PWM_PERIOD_CNTL, + BL_PWM_PERIOD_BITCNT, &pwm_period_bitcnt, + BL_PWM_PERIOD, &masked_pwm_period); + + 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 = masked_pwm_period & ((1 << bit_count) - 1); + + /* 1.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_pwm_u16_16 * masked_pwm_period; + + /* 1.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; + + /* + * 2. Program register with updated value + */ + + /* 2.1 Lock group 2 backlight registers */ + + REG_UPDATE_2(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_IGNORE_MASTER_LOCK_EN, 1, + BL_PWM_GRP1_REG_LOCK, 1); + + // 2.2 Write new active duty cycle + REG_UPDATE(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, backlight_16bit); + + /* 2.3 Unlock group 2 backlight registers */ + REG_UPDATE(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_REG_LOCK, 0); + + /* 3 Wait for pending bit to be cleared */ + REG_WAIT(BL_PWM_GRP1_REG_LOCK, + BL_PWM_GRP1_REG_UPDATE_PENDING, 0, + 1, 10000); +} + +static void dce_panel_cntl_destroy(struct panel_cntl **panel_cntl) +{ + struct dce_panel_cntl *dce_panel_cntl = TO_DCE_PANEL_CNTL(*panel_cntl); + + kfree(dce_panel_cntl); + *panel_cntl = NULL; +} + +static const struct panel_cntl_funcs dce_link_panel_cntl_funcs = { + .destroy = dce_panel_cntl_destroy, + .hw_init = dce_panel_cntl_hw_init, + .is_panel_backlight_on = dce_is_panel_backlight_on, + .is_panel_powered_on = dce_is_panel_powered_on, + .store_backlight_level = dce_store_backlight_level, + .driver_set_backlight = dce_driver_set_backlight, + .get_current_backlight = dce_get_16_bit_backlight_from_pwm, +}; + +void dce_panel_cntl_construct( + struct dce_panel_cntl *dce_panel_cntl, + const struct panel_cntl_init_data *init_data, + const struct dce_panel_cntl_registers *regs, + const struct dce_panel_cntl_shift *shift, + const struct dce_panel_cntl_mask *mask) +{ + struct panel_cntl *base = &dce_panel_cntl->base; + + base->stored_backlight_registers.BL_PWM_CNTL = 0; + base->stored_backlight_registers.BL_PWM_CNTL2 = 0; + base->stored_backlight_registers.BL_PWM_PERIOD_CNTL = 0; + base->stored_backlight_registers.LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV = 0; + + dce_panel_cntl->regs = regs; + dce_panel_cntl->shift = shift; + dce_panel_cntl->mask = mask; + + dce_panel_cntl->base.funcs = &dce_link_panel_cntl_funcs; + dce_panel_cntl->base.ctx = init_data->ctx; + dce_panel_cntl->base.inst = init_data->inst; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_panel_cntl.h b/drivers/gpu/drm/amd/display/dc/dce/dce_panel_cntl.h new file mode 100644 index 000000000..6bd119608 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_panel_cntl.h @@ -0,0 +1,129 @@ +/* + * 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_PANEL_CNTL__DCE_H__ +#define __DC_PANEL_CNTL__DCE_H__ + +#include "panel_cntl.h" + +/* set register offset with instance */ +#define DCE_PANEL_CNTL_SR(reg_name, block)\ + .reg_name = mm ## block ## _ ## reg_name + +#define DCE_PANEL_CNTL_REG_LIST()\ + DCE_PANEL_CNTL_SR(PWRSEQ_CNTL, LVTMA), \ + DCE_PANEL_CNTL_SR(PWRSEQ_STATE, LVTMA), \ + DCE_PANEL_CNTL_SR(PWRSEQ_REF_DIV, LVTMA), \ + SR(BL_PWM_CNTL), \ + SR(BL_PWM_CNTL2), \ + SR(BL_PWM_PERIOD_CNTL), \ + SR(BL_PWM_GRP1_REG_LOCK), \ + SR(BIOS_SCRATCH_2) + +#define DCN_PANEL_CNTL_SR(reg_name, block)\ + .reg_name = BASE(mm ## block ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## _ ## reg_name + +#define DCN_PANEL_CNTL_REG_LIST()\ + DCN_PANEL_CNTL_SR(PWRSEQ_CNTL, LVTMA), \ + DCN_PANEL_CNTL_SR(PWRSEQ_STATE, LVTMA), \ + DCN_PANEL_CNTL_SR(PWRSEQ_REF_DIV, LVTMA), \ + SR(BL_PWM_CNTL), \ + SR(BL_PWM_CNTL2), \ + SR(BL_PWM_PERIOD_CNTL), \ + SR(BL_PWM_GRP1_REG_LOCK), \ + NBIO_SR(BIOS_SCRATCH_2) + +#define DCE_PANEL_CNTL_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define DCE_PANEL_CNTL_MASK_SH_LIST(mask_sh) \ + DCE_PANEL_CNTL_SF(LVTMA_PWRSEQ_CNTL, LVTMA_BLON, mask_sh),\ + DCE_PANEL_CNTL_SF(LVTMA_PWRSEQ_CNTL, LVTMA_BLON_OVRD, mask_sh),\ + DCE_PANEL_CNTL_SF(LVTMA_PWRSEQ_CNTL, LVTMA_DIGON, mask_sh),\ + DCE_PANEL_CNTL_SF(LVTMA_PWRSEQ_CNTL, LVTMA_DIGON_OVRD, mask_sh),\ + DCE_PANEL_CNTL_SF(LVTMA_PWRSEQ_CNTL, LVTMA_PWRSEQ_TARGET_STATE, mask_sh), \ + DCE_PANEL_CNTL_SF(LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, mask_sh), \ + DCE_PANEL_CNTL_SF(LVTMA_PWRSEQ_REF_DIV, BL_PWM_REF_DIV, mask_sh), \ + DCE_PANEL_CNTL_SF(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD, mask_sh), \ + DCE_PANEL_CNTL_SF(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD_BITCNT, mask_sh), \ + DCE_PANEL_CNTL_SF(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, mask_sh), \ + DCE_PANEL_CNTL_SF(BL_PWM_CNTL, BL_PWM_FRACTIONAL_EN, mask_sh), \ + DCE_PANEL_CNTL_SF(BL_PWM_CNTL, BL_PWM_EN, mask_sh), \ + DCE_PANEL_CNTL_SF(BL_PWM_GRP1_REG_LOCK, BL_PWM_GRP1_IGNORE_MASTER_LOCK_EN, mask_sh), \ + DCE_PANEL_CNTL_SF(BL_PWM_GRP1_REG_LOCK, BL_PWM_GRP1_REG_LOCK, mask_sh), \ + DCE_PANEL_CNTL_SF(BL_PWM_GRP1_REG_LOCK, BL_PWM_GRP1_REG_UPDATE_PENDING, mask_sh) + +#define DCE_PANEL_CNTL_REG_FIELD_LIST(type) \ + type LVTMA_BLON;\ + type LVTMA_BLON_OVRD;\ + type LVTMA_DIGON;\ + type LVTMA_DIGON_OVRD;\ + type LVTMA_PWRSEQ_TARGET_STATE; \ + type LVTMA_PWRSEQ_TARGET_STATE_R; \ + type BL_PWM_REF_DIV; \ + type BL_PWM_EN; \ + type BL_ACTIVE_INT_FRAC_CNT; \ + type BL_PWM_FRACTIONAL_EN; \ + type BL_PWM_PERIOD; \ + type BL_PWM_PERIOD_BITCNT; \ + type BL_PWM_GRP1_IGNORE_MASTER_LOCK_EN; \ + type BL_PWM_GRP1_REG_LOCK; \ + type BL_PWM_GRP1_REG_UPDATE_PENDING + +struct dce_panel_cntl_shift { + DCE_PANEL_CNTL_REG_FIELD_LIST(uint8_t); +}; + +struct dce_panel_cntl_mask { + DCE_PANEL_CNTL_REG_FIELD_LIST(uint32_t); +}; + +struct dce_panel_cntl_registers { + uint32_t PWRSEQ_CNTL; + uint32_t PWRSEQ_STATE; + uint32_t BL_PWM_CNTL; + uint32_t BL_PWM_CNTL2; + uint32_t BL_PWM_PERIOD_CNTL; + uint32_t BL_PWM_GRP1_REG_LOCK; + uint32_t PWRSEQ_REF_DIV; + uint32_t BIOS_SCRATCH_2; +}; + +struct dce_panel_cntl { + struct panel_cntl base; + const struct dce_panel_cntl_registers *regs; + const struct dce_panel_cntl_shift *shift; + const struct dce_panel_cntl_mask *mask; +}; + +void dce_panel_cntl_construct( + struct dce_panel_cntl *panel_cntl, + const struct panel_cntl_init_data *init_data, + const struct dce_panel_cntl_registers *regs, + const struct dce_panel_cntl_shift *shift, + const struct dce_panel_cntl_mask *mask); + +#endif /* __DC_PANEL_CNTL__DCE_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 000000000..7311f3123 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_scl_filters.c @@ -0,0 +1,1445 @@ +/* + * 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" + +//========================================= +// <num_taps> = 2 +// <num_phases> = 16 +// <scale_ratio> = 0.833333 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = s1.10 +// <CoefOut> = s1.12 +//========================================= +static const uint16_t filter_2tap_16p[18] = { + 0x1000, 0x0000, + 0x0FF0, 0x0010, + 0x0FB0, 0x0050, + 0x0F34, 0x00CC, + 0x0E68, 0x0198, + 0x0D44, 0x02BC, + 0x0BC4, 0x043C, + 0x09FC, 0x0604, + 0x0800, 0x0800 +}; + +//========================================= +// <num_taps> = 3 +// <num_phases> = 16 +// <scale_ratio> = 0.83333 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_3tap_16p_upscale[27] = { + 0x0804, 0x07FC, 0x0000, + 0x06AC, 0x0978, 0x3FDC, + 0x055C, 0x0AF0, 0x3FB4, + 0x0420, 0x0C50, 0x3F90, + 0x0300, 0x0D88, 0x3F78, + 0x0200, 0x0E90, 0x3F70, + 0x0128, 0x0F5C, 0x3F7C, + 0x007C, 0x0FD8, 0x3FAC, + 0x0000, 0x1000, 0x0000 +}; + +//========================================= +// <num_taps> = 3 +// <num_phases> = 16 +// <scale_ratio> = 1.16666 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_3tap_16p_116[27] = { + 0x0804, 0x07FC, 0x0000, + 0x0700, 0x0914, 0x3FEC, + 0x0604, 0x0A1C, 0x3FE0, + 0x050C, 0x0B14, 0x3FE0, + 0x041C, 0x0BF4, 0x3FF0, + 0x0340, 0x0CB0, 0x0010, + 0x0274, 0x0D3C, 0x0050, + 0x01C0, 0x0D94, 0x00AC, + 0x0128, 0x0DB4, 0x0124 +}; + +//========================================= +// <num_taps> = 3 +// <num_phases> = 16 +// <scale_ratio> = 1.49999 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_3tap_16p_149[27] = { + 0x0804, 0x07FC, 0x0000, + 0x0730, 0x08CC, 0x0004, + 0x0660, 0x098C, 0x0014, + 0x0590, 0x0A3C, 0x0034, + 0x04C4, 0x0AD4, 0x0068, + 0x0400, 0x0B54, 0x00AC, + 0x0348, 0x0BB0, 0x0108, + 0x029C, 0x0BEC, 0x0178, + 0x0200, 0x0C00, 0x0200 +}; + +//========================================= +// <num_taps> = 3 +// <num_phases> = 16 +// <scale_ratio> = 1.83332 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_3tap_16p_183[27] = { + 0x0804, 0x07FC, 0x0000, + 0x0754, 0x0880, 0x002C, + 0x06A8, 0x08F0, 0x0068, + 0x05FC, 0x0954, 0x00B0, + 0x0550, 0x09AC, 0x0104, + 0x04A8, 0x09F0, 0x0168, + 0x0408, 0x0A20, 0x01D8, + 0x036C, 0x0A40, 0x0254, + 0x02DC, 0x0A48, 0x02DC +}; + +//========================================= +// <num_taps> = 4 +// <num_phases> = 16 +// <scale_ratio> = 0.83333 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_4tap_16p_upscale[36] = { + 0x0000, 0x1000, 0x0000, 0x0000, + 0x3F74, 0x0FDC, 0x00B4, 0x3FFC, + 0x3F0C, 0x0F70, 0x0194, 0x3FF0, + 0x3ECC, 0x0EC4, 0x0298, 0x3FD8, + 0x3EAC, 0x0DE4, 0x03B8, 0x3FB8, + 0x3EA4, 0x0CD8, 0x04F4, 0x3F90, + 0x3EB8, 0x0BA0, 0x0644, 0x3F64, + 0x3ED8, 0x0A54, 0x07A0, 0x3F34, + 0x3F00, 0x08FC, 0x0900, 0x3F04 +}; + +//========================================= +// <num_taps> = 4 +// <num_phases> = 16 +// <scale_ratio> = 1.16666 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_4tap_16p_116[36] = { + 0x01A8, 0x0CB4, 0x01A4, 0x0000, + 0x0110, 0x0CB0, 0x0254, 0x3FEC, + 0x0090, 0x0C80, 0x031C, 0x3FD4, + 0x0024, 0x0C2C, 0x03F4, 0x3FBC, + 0x3FD8, 0x0BAC, 0x04DC, 0x3FA0, + 0x3F9C, 0x0B14, 0x05CC, 0x3F84, + 0x3F70, 0x0A60, 0x06C4, 0x3F6C, + 0x3F5C, 0x098C, 0x07BC, 0x3F5C, + 0x3F54, 0x08AC, 0x08AC, 0x3F54 +}; + +//========================================= +// <num_taps> = 4 +// <num_phases> = 16 +// <scale_ratio> = 1.49999 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_4tap_16p_149[36] = { + 0x02B8, 0x0A90, 0x02B8, 0x0000, + 0x0230, 0x0A90, 0x0350, 0x3FF0, + 0x01B8, 0x0A78, 0x03F0, 0x3FE0, + 0x0148, 0x0A48, 0x049C, 0x3FD4, + 0x00E8, 0x0A00, 0x054C, 0x3FCC, + 0x0098, 0x09A0, 0x0600, 0x3FC8, + 0x0054, 0x0928, 0x06B4, 0x3FD0, + 0x001C, 0x08A4, 0x0760, 0x3FE0, + 0x3FFC, 0x0804, 0x0804, 0x3FFC +}; + +//========================================= +// <num_taps> = 4 +// <num_phases> = 16 +// <scale_ratio> = 1.83332 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_4tap_16p_183[36] = { + 0x03B0, 0x08A0, 0x03B0, 0x0000, + 0x0348, 0x0898, 0x041C, 0x0004, + 0x02DC, 0x0884, 0x0490, 0x0010, + 0x0278, 0x0864, 0x0500, 0x0024, + 0x021C, 0x0838, 0x0570, 0x003C, + 0x01C8, 0x07FC, 0x05E0, 0x005C, + 0x0178, 0x07B8, 0x064C, 0x0084, + 0x0130, 0x076C, 0x06B0, 0x00B4, + 0x00F0, 0x0714, 0x0710, 0x00EC +}; + +//========================================= +// <num_taps> = 2 +// <num_phases> = 64 +// <scale_ratio> = 0.833333 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = s1.10 +// <CoefOut> = s1.12 +//========================================= +static const uint16_t filter_2tap_64p[66] = { + 0x1000, 0x0000, + 0x1000, 0x0000, + 0x0FFC, 0x0004, + 0x0FF8, 0x0008, + 0x0FF0, 0x0010, + 0x0FE4, 0x001C, + 0x0FD8, 0x0028, + 0x0FC4, 0x003C, + 0x0FB0, 0x0050, + 0x0F98, 0x0068, + 0x0F7C, 0x0084, + 0x0F58, 0x00A8, + 0x0F34, 0x00CC, + 0x0F08, 0x00F8, + 0x0ED8, 0x0128, + 0x0EA4, 0x015C, + 0x0E68, 0x0198, + 0x0E28, 0x01D8, + 0x0DE4, 0x021C, + 0x0D98, 0x0268, + 0x0D44, 0x02BC, + 0x0CEC, 0x0314, + 0x0C90, 0x0370, + 0x0C2C, 0x03D4, + 0x0BC4, 0x043C, + 0x0B58, 0x04A8, + 0x0AE8, 0x0518, + 0x0A74, 0x058C, + 0x09FC, 0x0604, + 0x0980, 0x0680, + 0x0900, 0x0700, + 0x0880, 0x0780, + 0x0800, 0x0800 +}; + +//========================================= +// <num_taps> = 3 +// <num_phases> = 64 +// <scale_ratio> = 0.83333 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_3tap_64p_upscale[99] = { + 0x0804, 0x07FC, 0x0000, + 0x07A8, 0x0860, 0x3FF8, + 0x0754, 0x08BC, 0x3FF0, + 0x0700, 0x0918, 0x3FE8, + 0x06AC, 0x0978, 0x3FDC, + 0x0654, 0x09D8, 0x3FD4, + 0x0604, 0x0A34, 0x3FC8, + 0x05B0, 0x0A90, 0x3FC0, + 0x055C, 0x0AF0, 0x3FB4, + 0x050C, 0x0B48, 0x3FAC, + 0x04BC, 0x0BA0, 0x3FA4, + 0x0470, 0x0BF4, 0x3F9C, + 0x0420, 0x0C50, 0x3F90, + 0x03D8, 0x0C9C, 0x3F8C, + 0x038C, 0x0CF0, 0x3F84, + 0x0344, 0x0D40, 0x3F7C, + 0x0300, 0x0D88, 0x3F78, + 0x02BC, 0x0DD0, 0x3F74, + 0x027C, 0x0E14, 0x3F70, + 0x023C, 0x0E54, 0x3F70, + 0x0200, 0x0E90, 0x3F70, + 0x01C8, 0x0EC8, 0x3F70, + 0x0190, 0x0EFC, 0x3F74, + 0x015C, 0x0F2C, 0x3F78, + 0x0128, 0x0F5C, 0x3F7C, + 0x00FC, 0x0F7C, 0x3F88, + 0x00CC, 0x0FA4, 0x3F90, + 0x00A4, 0x0FC0, 0x3F9C, + 0x007C, 0x0FD8, 0x3FAC, + 0x0058, 0x0FE8, 0x3FC0, + 0x0038, 0x0FF4, 0x3FD4, + 0x0018, 0x1000, 0x3FE8, + 0x0000, 0x1000, 0x0000 +}; + +//========================================= +// <num_taps> = 3 +// <num_phases> = 64 +// <scale_ratio> = 1.16666 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_3tap_64p_116[99] = { + 0x0804, 0x07FC, 0x0000, + 0x07C0, 0x0844, 0x3FFC, + 0x0780, 0x0888, 0x3FF8, + 0x0740, 0x08D0, 0x3FF0, + 0x0700, 0x0914, 0x3FEC, + 0x06C0, 0x0958, 0x3FE8, + 0x0684, 0x0998, 0x3FE4, + 0x0644, 0x09DC, 0x3FE0, + 0x0604, 0x0A1C, 0x3FE0, + 0x05C4, 0x0A5C, 0x3FE0, + 0x0588, 0x0A9C, 0x3FDC, + 0x0548, 0x0ADC, 0x3FDC, + 0x050C, 0x0B14, 0x3FE0, + 0x04CC, 0x0B54, 0x3FE0, + 0x0490, 0x0B8C, 0x3FE4, + 0x0458, 0x0BC0, 0x3FE8, + 0x041C, 0x0BF4, 0x3FF0, + 0x03E0, 0x0C28, 0x3FF8, + 0x03A8, 0x0C58, 0x0000, + 0x0374, 0x0C88, 0x0004, + 0x0340, 0x0CB0, 0x0010, + 0x0308, 0x0CD8, 0x0020, + 0x02D8, 0x0CFC, 0x002C, + 0x02A0, 0x0D20, 0x0040, + 0x0274, 0x0D3C, 0x0050, + 0x0244, 0x0D58, 0x0064, + 0x0214, 0x0D70, 0x007C, + 0x01E8, 0x0D84, 0x0094, + 0x01C0, 0x0D94, 0x00AC, + 0x0198, 0x0DA0, 0x00C8, + 0x0170, 0x0DAC, 0x00E4, + 0x014C, 0x0DB0, 0x0104, + 0x0128, 0x0DB4, 0x0124 +}; + +//========================================= +// <num_taps> = 3 +// <num_phases> = 64 +// <scale_ratio> = 1.49999 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_3tap_64p_149[99] = { + 0x0804, 0x07FC, 0x0000, + 0x07CC, 0x0834, 0x0000, + 0x0798, 0x0868, 0x0000, + 0x0764, 0x089C, 0x0000, + 0x0730, 0x08CC, 0x0004, + 0x0700, 0x08FC, 0x0004, + 0x06CC, 0x092C, 0x0008, + 0x0698, 0x095C, 0x000C, + 0x0660, 0x098C, 0x0014, + 0x062C, 0x09B8, 0x001C, + 0x05FC, 0x09E4, 0x0020, + 0x05C4, 0x0A10, 0x002C, + 0x0590, 0x0A3C, 0x0034, + 0x055C, 0x0A64, 0x0040, + 0x0528, 0x0A8C, 0x004C, + 0x04F8, 0x0AB0, 0x0058, + 0x04C4, 0x0AD4, 0x0068, + 0x0490, 0x0AF8, 0x0078, + 0x0460, 0x0B18, 0x0088, + 0x0430, 0x0B38, 0x0098, + 0x0400, 0x0B54, 0x00AC, + 0x03D0, 0x0B6C, 0x00C4, + 0x03A0, 0x0B88, 0x00D8, + 0x0374, 0x0B9C, 0x00F0, + 0x0348, 0x0BB0, 0x0108, + 0x0318, 0x0BC4, 0x0124, + 0x02EC, 0x0BD4, 0x0140, + 0x02C4, 0x0BE0, 0x015C, + 0x029C, 0x0BEC, 0x0178, + 0x0274, 0x0BF4, 0x0198, + 0x024C, 0x0BFC, 0x01B8, + 0x0228, 0x0BFC, 0x01DC, + 0x0200, 0x0C00, 0x0200 +}; + +//========================================= +// <num_taps> = 3 +// <num_phases> = 64 +// <scale_ratio> = 1.83332 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_3tap_64p_183[99] = { + 0x0804, 0x07FC, 0x0000, + 0x07D4, 0x0824, 0x0008, + 0x07AC, 0x0840, 0x0014, + 0x0780, 0x0860, 0x0020, + 0x0754, 0x0880, 0x002C, + 0x0728, 0x089C, 0x003C, + 0x0700, 0x08B8, 0x0048, + 0x06D4, 0x08D4, 0x0058, + 0x06A8, 0x08F0, 0x0068, + 0x067C, 0x090C, 0x0078, + 0x0650, 0x0924, 0x008C, + 0x0628, 0x093C, 0x009C, + 0x05FC, 0x0954, 0x00B0, + 0x05D0, 0x096C, 0x00C4, + 0x05A8, 0x0980, 0x00D8, + 0x0578, 0x0998, 0x00F0, + 0x0550, 0x09AC, 0x0104, + 0x0528, 0x09BC, 0x011C, + 0x04FC, 0x09D0, 0x0134, + 0x04D4, 0x09E0, 0x014C, + 0x04A8, 0x09F0, 0x0168, + 0x0480, 0x09FC, 0x0184, + 0x045C, 0x0A08, 0x019C, + 0x0434, 0x0A14, 0x01B8, + 0x0408, 0x0A20, 0x01D8, + 0x03E0, 0x0A2C, 0x01F4, + 0x03B8, 0x0A34, 0x0214, + 0x0394, 0x0A38, 0x0234, + 0x036C, 0x0A40, 0x0254, + 0x0348, 0x0A44, 0x0274, + 0x0324, 0x0A48, 0x0294, + 0x0300, 0x0A48, 0x02B8, + 0x02DC, 0x0A48, 0x02DC +}; + +//========================================= +// <num_taps> = 4 +// <num_phases> = 64 +// <scale_ratio> = 0.83333 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_4tap_64p_upscale[132] = { + 0x0000, 0x1000, 0x0000, 0x0000, + 0x3FDC, 0x0FFC, 0x0028, 0x0000, + 0x3FB4, 0x0FF8, 0x0054, 0x0000, + 0x3F94, 0x0FE8, 0x0084, 0x0000, + 0x3F74, 0x0FDC, 0x00B4, 0x3FFC, + 0x3F58, 0x0FC4, 0x00E8, 0x3FFC, + 0x3F3C, 0x0FAC, 0x0120, 0x3FF8, + 0x3F24, 0x0F90, 0x0158, 0x3FF4, + 0x3F0C, 0x0F70, 0x0194, 0x3FF0, + 0x3EF8, 0x0F4C, 0x01D0, 0x3FEC, + 0x3EE8, 0x0F20, 0x0210, 0x3FE8, + 0x3ED8, 0x0EF4, 0x0254, 0x3FE0, + 0x3ECC, 0x0EC4, 0x0298, 0x3FD8, + 0x3EC0, 0x0E90, 0x02DC, 0x3FD4, + 0x3EB8, 0x0E58, 0x0324, 0x3FCC, + 0x3EB0, 0x0E20, 0x036C, 0x3FC4, + 0x3EAC, 0x0DE4, 0x03B8, 0x3FB8, + 0x3EA8, 0x0DA4, 0x0404, 0x3FB0, + 0x3EA4, 0x0D60, 0x0454, 0x3FA8, + 0x3EA4, 0x0D1C, 0x04A4, 0x3F9C, + 0x3EA4, 0x0CD8, 0x04F4, 0x3F90, + 0x3EA8, 0x0C88, 0x0548, 0x3F88, + 0x3EAC, 0x0C3C, 0x059C, 0x3F7C, + 0x3EB0, 0x0BF0, 0x05F0, 0x3F70, + 0x3EB8, 0x0BA0, 0x0644, 0x3F64, + 0x3EBC, 0x0B54, 0x0698, 0x3F58, + 0x3EC4, 0x0B00, 0x06F0, 0x3F4C, + 0x3ECC, 0x0AAC, 0x0748, 0x3F40, + 0x3ED8, 0x0A54, 0x07A0, 0x3F34, + 0x3EE0, 0x0A04, 0x07F8, 0x3F24, + 0x3EEC, 0x09AC, 0x0850, 0x3F18, + 0x3EF8, 0x0954, 0x08A8, 0x3F0C, + 0x3F00, 0x08FC, 0x0900, 0x3F04 +}; + +//========================================= +// <num_taps> = 4 +// <num_phases> = 64 +// <scale_ratio> = 1.16666 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_4tap_64p_116[132] = { + 0x01A8, 0x0CB4, 0x01A4, 0x0000, + 0x017C, 0x0CB8, 0x01D0, 0x3FFC, + 0x0158, 0x0CB8, 0x01F8, 0x3FF8, + 0x0130, 0x0CB4, 0x0228, 0x3FF4, + 0x0110, 0x0CB0, 0x0254, 0x3FEC, + 0x00EC, 0x0CA8, 0x0284, 0x3FE8, + 0x00CC, 0x0C9C, 0x02B4, 0x3FE4, + 0x00AC, 0x0C90, 0x02E8, 0x3FDC, + 0x0090, 0x0C80, 0x031C, 0x3FD4, + 0x0070, 0x0C70, 0x0350, 0x3FD0, + 0x0058, 0x0C5C, 0x0384, 0x3FC8, + 0x003C, 0x0C48, 0x03BC, 0x3FC0, + 0x0024, 0x0C2C, 0x03F4, 0x3FBC, + 0x0010, 0x0C10, 0x042C, 0x3FB4, + 0x3FFC, 0x0BF4, 0x0464, 0x3FAC, + 0x3FE8, 0x0BD4, 0x04A0, 0x3FA4, + 0x3FD8, 0x0BAC, 0x04DC, 0x3FA0, + 0x3FC4, 0x0B8C, 0x0518, 0x3F98, + 0x3FB4, 0x0B68, 0x0554, 0x3F90, + 0x3FA8, 0x0B40, 0x0590, 0x3F88, + 0x3F9C, 0x0B14, 0x05CC, 0x3F84, + 0x3F90, 0x0AEC, 0x0608, 0x3F7C, + 0x3F84, 0x0ABC, 0x0648, 0x3F78, + 0x3F7C, 0x0A90, 0x0684, 0x3F70, + 0x3F70, 0x0A60, 0x06C4, 0x3F6C, + 0x3F6C, 0x0A2C, 0x0700, 0x3F68, + 0x3F64, 0x09F8, 0x0740, 0x3F64, + 0x3F60, 0x09C4, 0x077C, 0x3F60, + 0x3F5C, 0x098C, 0x07BC, 0x3F5C, + 0x3F58, 0x0958, 0x07F8, 0x3F58, + 0x3F58, 0x091C, 0x0834, 0x3F58, + 0x3F54, 0x08E4, 0x0870, 0x3F58, + 0x3F54, 0x08AC, 0x08AC, 0x3F54 +}; + +//========================================= +// <num_taps> = 4 +// <num_phases> = 64 +// <scale_ratio> = 1.49999 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_4tap_64p_149[132] = { + 0x02B8, 0x0A90, 0x02B8, 0x0000, + 0x0294, 0x0A94, 0x02DC, 0x3FFC, + 0x0274, 0x0A94, 0x0300, 0x3FF8, + 0x0250, 0x0A94, 0x0328, 0x3FF4, + 0x0230, 0x0A90, 0x0350, 0x3FF0, + 0x0214, 0x0A8C, 0x0374, 0x3FEC, + 0x01F0, 0x0A88, 0x03A0, 0x3FE8, + 0x01D4, 0x0A80, 0x03C8, 0x3FE4, + 0x01B8, 0x0A78, 0x03F0, 0x3FE0, + 0x0198, 0x0A70, 0x041C, 0x3FDC, + 0x0180, 0x0A64, 0x0444, 0x3FD8, + 0x0164, 0x0A54, 0x0470, 0x3FD8, + 0x0148, 0x0A48, 0x049C, 0x3FD4, + 0x0130, 0x0A38, 0x04C8, 0x3FD0, + 0x0118, 0x0A24, 0x04F4, 0x3FD0, + 0x0100, 0x0A14, 0x0520, 0x3FCC, + 0x00E8, 0x0A00, 0x054C, 0x3FCC, + 0x00D4, 0x09E8, 0x057C, 0x3FC8, + 0x00C0, 0x09D0, 0x05A8, 0x3FC8, + 0x00AC, 0x09B8, 0x05D4, 0x3FC8, + 0x0098, 0x09A0, 0x0600, 0x3FC8, + 0x0084, 0x0984, 0x0630, 0x3FC8, + 0x0074, 0x0964, 0x065C, 0x3FCC, + 0x0064, 0x0948, 0x0688, 0x3FCC, + 0x0054, 0x0928, 0x06B4, 0x3FD0, + 0x0044, 0x0908, 0x06E0, 0x3FD4, + 0x0038, 0x08E8, 0x070C, 0x3FD4, + 0x002C, 0x08C4, 0x0738, 0x3FD8, + 0x001C, 0x08A4, 0x0760, 0x3FE0, + 0x0014, 0x087C, 0x078C, 0x3FE4, + 0x0008, 0x0858, 0x07B4, 0x3FEC, + 0x0000, 0x0830, 0x07DC, 0x3FF4, + 0x3FFC, 0x0804, 0x0804, 0x3FFC +}; + +//========================================= +// <num_taps> = 4 +// <num_phases> = 64 +// <scale_ratio> = 1.83332 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_4tap_64p_183[132] = { + 0x03B0, 0x08A0, 0x03B0, 0x0000, + 0x0394, 0x08A0, 0x03CC, 0x0000, + 0x037C, 0x089C, 0x03E8, 0x0000, + 0x0360, 0x089C, 0x0400, 0x0004, + 0x0348, 0x0898, 0x041C, 0x0004, + 0x032C, 0x0894, 0x0438, 0x0008, + 0x0310, 0x0890, 0x0454, 0x000C, + 0x02F8, 0x0888, 0x0474, 0x000C, + 0x02DC, 0x0884, 0x0490, 0x0010, + 0x02C4, 0x087C, 0x04AC, 0x0014, + 0x02AC, 0x0874, 0x04C8, 0x0018, + 0x0290, 0x086C, 0x04E4, 0x0020, + 0x0278, 0x0864, 0x0500, 0x0024, + 0x0264, 0x0858, 0x051C, 0x0028, + 0x024C, 0x084C, 0x0538, 0x0030, + 0x0234, 0x0844, 0x0554, 0x0034, + 0x021C, 0x0838, 0x0570, 0x003C, + 0x0208, 0x0828, 0x058C, 0x0044, + 0x01F0, 0x081C, 0x05A8, 0x004C, + 0x01DC, 0x080C, 0x05C4, 0x0054, + 0x01C8, 0x07FC, 0x05E0, 0x005C, + 0x01B4, 0x07EC, 0x05FC, 0x0064, + 0x019C, 0x07DC, 0x0618, 0x0070, + 0x018C, 0x07CC, 0x0630, 0x0078, + 0x0178, 0x07B8, 0x064C, 0x0084, + 0x0164, 0x07A8, 0x0664, 0x0090, + 0x0150, 0x0794, 0x0680, 0x009C, + 0x0140, 0x0780, 0x0698, 0x00A8, + 0x0130, 0x076C, 0x06B0, 0x00B4, + 0x0120, 0x0758, 0x06C8, 0x00C0, + 0x0110, 0x0740, 0x06E0, 0x00D0, + 0x0100, 0x072C, 0x06F8, 0x00DC, + 0x00F0, 0x0714, 0x0710, 0x00EC +}; + +//========================================= +// <num_taps> = 5 +// <num_phases> = 64 +// <scale_ratio> = 0.83333 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_5tap_64p_upscale[165] = { + 0x3E40, 0x09C0, 0x09C0, 0x3E40, 0x0000, + 0x3E50, 0x0964, 0x0A18, 0x3E34, 0x0000, + 0x3E5C, 0x0908, 0x0A6C, 0x3E2C, 0x0004, + 0x3E6C, 0x08AC, 0x0AC0, 0x3E20, 0x0008, + 0x3E78, 0x0850, 0x0B14, 0x3E18, 0x000C, + 0x3E88, 0x07F4, 0x0B60, 0x3E14, 0x0010, + 0x3E98, 0x0798, 0x0BB0, 0x3E0C, 0x0014, + 0x3EA8, 0x073C, 0x0C00, 0x3E08, 0x0014, + 0x3EB8, 0x06E4, 0x0C48, 0x3E04, 0x0018, + 0x3ECC, 0x0684, 0x0C90, 0x3E04, 0x001C, + 0x3EDC, 0x062C, 0x0CD4, 0x3E04, 0x0020, + 0x3EEC, 0x05D4, 0x0D1C, 0x3E04, 0x0020, + 0x3EFC, 0x057C, 0x0D5C, 0x3E08, 0x0024, + 0x3F0C, 0x0524, 0x0D98, 0x3E10, 0x0028, + 0x3F20, 0x04CC, 0x0DD8, 0x3E14, 0x0028, + 0x3F30, 0x0478, 0x0E14, 0x3E1C, 0x0028, + 0x3F40, 0x0424, 0x0E48, 0x3E28, 0x002C, + 0x3F50, 0x03D4, 0x0E7C, 0x3E34, 0x002C, + 0x3F60, 0x0384, 0x0EAC, 0x3E44, 0x002C, + 0x3F6C, 0x0338, 0x0EDC, 0x3E54, 0x002C, + 0x3F7C, 0x02E8, 0x0F08, 0x3E68, 0x002C, + 0x3F8C, 0x02A0, 0x0F2C, 0x3E7C, 0x002C, + 0x3F98, 0x0258, 0x0F50, 0x3E94, 0x002C, + 0x3FA4, 0x0210, 0x0F74, 0x3EB0, 0x0028, + 0x3FB0, 0x01CC, 0x0F90, 0x3ECC, 0x0028, + 0x3FC0, 0x018C, 0x0FA8, 0x3EE8, 0x0024, + 0x3FC8, 0x014C, 0x0FC0, 0x3F0C, 0x0020, + 0x3FD4, 0x0110, 0x0FD4, 0x3F2C, 0x001C, + 0x3FE0, 0x00D4, 0x0FE0, 0x3F54, 0x0018, + 0x3FE8, 0x009C, 0x0FF0, 0x3F7C, 0x0010, + 0x3FF0, 0x0064, 0x0FFC, 0x3FA4, 0x000C, + 0x3FFC, 0x0030, 0x0FFC, 0x3FD4, 0x0004, + 0x0000, 0x0000, 0x1000, 0x0000, 0x0000 +}; + +//========================================= +// <num_taps> = 5 +// <num_phases> = 64 +// <scale_ratio> = 1.16666 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_5tap_64p_116[165] = { + 0x3EDC, 0x0924, 0x0924, 0x3EDC, 0x0000, + 0x3ED8, 0x08EC, 0x095C, 0x3EE0, 0x0000, + 0x3ED4, 0x08B0, 0x0994, 0x3EE8, 0x0000, + 0x3ED0, 0x0878, 0x09C8, 0x3EF0, 0x0000, + 0x3ED0, 0x083C, 0x09FC, 0x3EF8, 0x0000, + 0x3ED0, 0x0800, 0x0A2C, 0x3F04, 0x0000, + 0x3ED0, 0x07C4, 0x0A5C, 0x3F10, 0x0000, + 0x3ED0, 0x0788, 0x0A8C, 0x3F1C, 0x0000, + 0x3ED0, 0x074C, 0x0AC0, 0x3F28, 0x3FFC, + 0x3ED4, 0x0710, 0x0AE8, 0x3F38, 0x3FFC, + 0x3ED8, 0x06D0, 0x0B18, 0x3F48, 0x3FF8, + 0x3EDC, 0x0694, 0x0B3C, 0x3F5C, 0x3FF8, + 0x3EE0, 0x0658, 0x0B68, 0x3F6C, 0x3FF4, + 0x3EE4, 0x061C, 0x0B90, 0x3F80, 0x3FF0, + 0x3EEC, 0x05DC, 0x0BB4, 0x3F98, 0x3FEC, + 0x3EF0, 0x05A0, 0x0BD8, 0x3FB0, 0x3FE8, + 0x3EF8, 0x0564, 0x0BF8, 0x3FC8, 0x3FE4, + 0x3EFC, 0x0528, 0x0C1C, 0x3FE0, 0x3FE0, + 0x3F04, 0x04EC, 0x0C38, 0x3FFC, 0x3FDC, + 0x3F0C, 0x04B4, 0x0C54, 0x0014, 0x3FD8, + 0x3F14, 0x047C, 0x0C70, 0x0030, 0x3FD0, + 0x3F1C, 0x0440, 0x0C88, 0x0050, 0x3FCC, + 0x3F24, 0x0408, 0x0CA0, 0x0070, 0x3FC4, + 0x3F2C, 0x03D0, 0x0CB0, 0x0094, 0x3FC0, + 0x3F34, 0x0398, 0x0CC4, 0x00B8, 0x3FB8, + 0x3F3C, 0x0364, 0x0CD4, 0x00DC, 0x3FB0, + 0x3F48, 0x032C, 0x0CE0, 0x0100, 0x3FAC, + 0x3F50, 0x02F8, 0x0CEC, 0x0128, 0x3FA4, + 0x3F58, 0x02C4, 0x0CF8, 0x0150, 0x3F9C, + 0x3F60, 0x0290, 0x0D00, 0x017C, 0x3F94, + 0x3F68, 0x0260, 0x0D04, 0x01A8, 0x3F8C, + 0x3F74, 0x0230, 0x0D04, 0x01D4, 0x3F84, + 0x3F7C, 0x0200, 0x0D08, 0x0200, 0x3F7C +}; + +//========================================= +// <num_taps> = 5 +// <num_phases> = 64 +// <scale_ratio> = 1.49999 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_5tap_64p_149[165] = { + 0x3FF4, 0x080C, 0x080C, 0x3FF4, 0x0000, + 0x3FE8, 0x07E8, 0x0830, 0x0000, 0x0000, + 0x3FDC, 0x07C8, 0x0850, 0x0010, 0x3FFC, + 0x3FD0, 0x07A4, 0x0878, 0x001C, 0x3FF8, + 0x3FC4, 0x0780, 0x0898, 0x0030, 0x3FF4, + 0x3FB8, 0x075C, 0x08B8, 0x0040, 0x3FF4, + 0x3FB0, 0x0738, 0x08D8, 0x0050, 0x3FF0, + 0x3FA8, 0x0710, 0x08F8, 0x0064, 0x3FEC, + 0x3FA0, 0x06EC, 0x0914, 0x0078, 0x3FE8, + 0x3F98, 0x06C4, 0x0934, 0x008C, 0x3FE4, + 0x3F90, 0x06A0, 0x094C, 0x00A4, 0x3FE0, + 0x3F8C, 0x0678, 0x0968, 0x00B8, 0x3FDC, + 0x3F84, 0x0650, 0x0984, 0x00D0, 0x3FD8, + 0x3F80, 0x0628, 0x099C, 0x00E8, 0x3FD4, + 0x3F7C, 0x0600, 0x09B8, 0x0100, 0x3FCC, + 0x3F78, 0x05D8, 0x09D0, 0x0118, 0x3FC8, + 0x3F74, 0x05B0, 0x09E4, 0x0134, 0x3FC4, + 0x3F70, 0x0588, 0x09F8, 0x0150, 0x3FC0, + 0x3F70, 0x0560, 0x0A08, 0x016C, 0x3FBC, + 0x3F6C, 0x0538, 0x0A20, 0x0188, 0x3FB4, + 0x3F6C, 0x0510, 0x0A30, 0x01A4, 0x3FB0, + 0x3F6C, 0x04E8, 0x0A3C, 0x01C4, 0x3FAC, + 0x3F6C, 0x04C0, 0x0A48, 0x01E4, 0x3FA8, + 0x3F6C, 0x0498, 0x0A58, 0x0200, 0x3FA4, + 0x3F6C, 0x0470, 0x0A60, 0x0224, 0x3FA0, + 0x3F6C, 0x0448, 0x0A70, 0x0244, 0x3F98, + 0x3F70, 0x0420, 0x0A78, 0x0264, 0x3F94, + 0x3F70, 0x03F8, 0x0A80, 0x0288, 0x3F90, + 0x3F74, 0x03D4, 0x0A84, 0x02A8, 0x3F8C, + 0x3F74, 0x03AC, 0x0A8C, 0x02CC, 0x3F88, + 0x3F78, 0x0384, 0x0A90, 0x02F0, 0x3F84, + 0x3F7C, 0x0360, 0x0A90, 0x0314, 0x3F80, + 0x3F7C, 0x033C, 0x0A90, 0x033C, 0x3F7C +}; + +//========================================= +// <num_taps> = 5 +// <num_phases> = 64 +// <scale_ratio> = 1.83332 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_5tap_64p_183[165] = { + 0x0168, 0x069C, 0x0698, 0x0164, 0x0000, + 0x0154, 0x068C, 0x06AC, 0x0174, 0x0000, + 0x0144, 0x0674, 0x06C0, 0x0188, 0x0000, + 0x0138, 0x0664, 0x06D0, 0x0198, 0x3FFC, + 0x0128, 0x0654, 0x06E0, 0x01A8, 0x3FFC, + 0x0118, 0x0640, 0x06F0, 0x01BC, 0x3FFC, + 0x010C, 0x0630, 0x0700, 0x01CC, 0x3FF8, + 0x00FC, 0x061C, 0x0710, 0x01E0, 0x3FF8, + 0x00F0, 0x060C, 0x071C, 0x01F0, 0x3FF8, + 0x00E4, 0x05F4, 0x072C, 0x0204, 0x3FF8, + 0x00D8, 0x05E4, 0x0738, 0x0218, 0x3FF4, + 0x00CC, 0x05D0, 0x0744, 0x022C, 0x3FF4, + 0x00C0, 0x05B8, 0x0754, 0x0240, 0x3FF4, + 0x00B4, 0x05A4, 0x0760, 0x0254, 0x3FF4, + 0x00A8, 0x0590, 0x076C, 0x0268, 0x3FF4, + 0x009C, 0x057C, 0x0778, 0x027C, 0x3FF4, + 0x0094, 0x0564, 0x0780, 0x0294, 0x3FF4, + 0x0088, 0x0550, 0x0788, 0x02A8, 0x3FF8, + 0x0080, 0x0538, 0x0794, 0x02BC, 0x3FF8, + 0x0074, 0x0524, 0x079C, 0x02D4, 0x3FF8, + 0x006C, 0x0510, 0x07A4, 0x02E8, 0x3FF8, + 0x0064, 0x04F4, 0x07AC, 0x0300, 0x3FFC, + 0x005C, 0x04E4, 0x07B0, 0x0314, 0x3FFC, + 0x0054, 0x04C8, 0x07B8, 0x032C, 0x0000, + 0x004C, 0x04B4, 0x07C0, 0x0340, 0x0000, + 0x0044, 0x04A0, 0x07C4, 0x0358, 0x0000, + 0x003C, 0x0488, 0x07C8, 0x0370, 0x0004, + 0x0038, 0x0470, 0x07CC, 0x0384, 0x0008, + 0x0030, 0x045C, 0x07D0, 0x039C, 0x0008, + 0x002C, 0x0444, 0x07D0, 0x03B4, 0x000C, + 0x0024, 0x042C, 0x07D4, 0x03CC, 0x0010, + 0x0020, 0x0414, 0x07D4, 0x03E0, 0x0018, + 0x001C, 0x03FC, 0x07D4, 0x03F8, 0x001C +}; + +//========================================= +// <num_taps> = 6 +// <num_phases> = 64 +// <scale_ratio> = 0.83333 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_6tap_64p_upscale[198] = { + 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, + 0x000C, 0x3FD0, 0x0FFC, 0x0034, 0x3FF4, 0x0000, + 0x0018, 0x3F9C, 0x0FF8, 0x006C, 0x3FE8, 0x0000, + 0x0024, 0x3F6C, 0x0FF0, 0x00A8, 0x3FD8, 0x0000, + 0x002C, 0x3F44, 0x0FE4, 0x00E4, 0x3FC8, 0x0000, + 0x0038, 0x3F18, 0x0FD4, 0x0124, 0x3FB8, 0x0000, + 0x0040, 0x3EF0, 0x0FC0, 0x0164, 0x3FA8, 0x0004, + 0x0048, 0x3EC8, 0x0FAC, 0x01A8, 0x3F98, 0x0004, + 0x0050, 0x3EA8, 0x0F94, 0x01EC, 0x3F84, 0x0004, + 0x0058, 0x3E84, 0x0F74, 0x0234, 0x3F74, 0x0008, + 0x0060, 0x3E68, 0x0F54, 0x027C, 0x3F60, 0x0008, + 0x0064, 0x3E4C, 0x0F30, 0x02C8, 0x3F4C, 0x000C, + 0x006C, 0x3E30, 0x0F04, 0x0314, 0x3F3C, 0x0010, + 0x0070, 0x3E18, 0x0EDC, 0x0360, 0x3F28, 0x0014, + 0x0074, 0x3E04, 0x0EB0, 0x03B0, 0x3F14, 0x0014, + 0x0078, 0x3DF0, 0x0E80, 0x0400, 0x3F00, 0x0018, + 0x0078, 0x3DE0, 0x0E4C, 0x0454, 0x3EEC, 0x001C, + 0x007C, 0x3DD0, 0x0E14, 0x04A8, 0x3ED8, 0x0020, + 0x007C, 0x3DC4, 0x0DDC, 0x04FC, 0x3EC4, 0x0024, + 0x007C, 0x3DBC, 0x0DA0, 0x0550, 0x3EB0, 0x0028, + 0x0080, 0x3DB4, 0x0D5C, 0x05A8, 0x3E9C, 0x002C, + 0x0080, 0x3DAC, 0x0D1C, 0x0600, 0x3E88, 0x0030, + 0x007C, 0x3DA8, 0x0CDC, 0x0658, 0x3E74, 0x0034, + 0x007C, 0x3DA4, 0x0C94, 0x06B0, 0x3E64, 0x0038, + 0x007C, 0x3DA4, 0x0C48, 0x0708, 0x3E50, 0x0040, + 0x0078, 0x3DA4, 0x0C00, 0x0760, 0x3E40, 0x0044, + 0x0078, 0x3DA8, 0x0BB4, 0x07B8, 0x3E2C, 0x0048, + 0x0074, 0x3DAC, 0x0B68, 0x0810, 0x3E1C, 0x004C, + 0x0070, 0x3DB4, 0x0B18, 0x0868, 0x3E0C, 0x0050, + 0x006C, 0x3DBC, 0x0AC4, 0x08C4, 0x3DFC, 0x0054, + 0x0068, 0x3DC4, 0x0A74, 0x0918, 0x3DF0, 0x0058, + 0x0068, 0x3DCC, 0x0A20, 0x0970, 0x3DE0, 0x005C, + 0x0064, 0x3DD4, 0x09C8, 0x09C8, 0x3DD4, 0x0064 +}; + +//========================================= +// <num_taps> = 6 +// <num_phases> = 64 +// <scale_ratio> = 1.16666 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_6tap_64p_116[198] = { + 0x3F0C, 0x0240, 0x0D68, 0x0240, 0x3F0C, 0x0000, + 0x3F18, 0x0210, 0x0D64, 0x0274, 0x3F00, 0x0000, + 0x3F24, 0x01E0, 0x0D58, 0x02A8, 0x3EF8, 0x0004, + 0x3F2C, 0x01B0, 0x0D58, 0x02DC, 0x3EEC, 0x0004, + 0x3F38, 0x0180, 0x0D50, 0x0310, 0x3EE0, 0x0008, + 0x3F44, 0x0154, 0x0D40, 0x0348, 0x3ED8, 0x0008, + 0x3F50, 0x0128, 0x0D34, 0x037C, 0x3ECC, 0x000C, + 0x3F5C, 0x00FC, 0x0D20, 0x03B4, 0x3EC4, 0x0010, + 0x3F64, 0x00D4, 0x0D14, 0x03EC, 0x3EB8, 0x0010, + 0x3F70, 0x00AC, 0x0CFC, 0x0424, 0x3EB0, 0x0014, + 0x3F78, 0x0084, 0x0CE8, 0x0460, 0x3EA8, 0x0014, + 0x3F84, 0x0060, 0x0CCC, 0x0498, 0x3EA0, 0x0018, + 0x3F90, 0x003C, 0x0CB4, 0x04D0, 0x3E98, 0x0018, + 0x3F98, 0x0018, 0x0C9C, 0x050C, 0x3E90, 0x0018, + 0x3FA0, 0x3FFC, 0x0C78, 0x0548, 0x3E88, 0x001C, + 0x3FAC, 0x3FDC, 0x0C54, 0x0584, 0x3E84, 0x001C, + 0x3FB4, 0x3FBC, 0x0C3C, 0x05BC, 0x3E7C, 0x001C, + 0x3FBC, 0x3FA0, 0x0C14, 0x05F8, 0x3E78, 0x0020, + 0x3FC4, 0x3F84, 0x0BF0, 0x0634, 0x3E74, 0x0020, + 0x3FCC, 0x3F68, 0x0BCC, 0x0670, 0x3E70, 0x0020, + 0x3FD4, 0x3F50, 0x0BA4, 0x06AC, 0x3E6C, 0x0020, + 0x3FDC, 0x3F38, 0x0B78, 0x06E8, 0x3E6C, 0x0020, + 0x3FE0, 0x3F24, 0x0B50, 0x0724, 0x3E68, 0x0020, + 0x3FE8, 0x3F0C, 0x0B24, 0x0760, 0x3E68, 0x0020, + 0x3FF0, 0x3EFC, 0x0AF4, 0x0798, 0x3E68, 0x0020, + 0x3FF4, 0x3EE8, 0x0AC8, 0x07D4, 0x3E68, 0x0020, + 0x3FFC, 0x3ED8, 0x0A94, 0x0810, 0x3E6C, 0x001C, + 0x0000, 0x3EC8, 0x0A64, 0x0848, 0x3E70, 0x001C, + 0x0000, 0x3EB8, 0x0A38, 0x0880, 0x3E74, 0x001C, + 0x0004, 0x3EAC, 0x0A04, 0x08BC, 0x3E78, 0x0018, + 0x0008, 0x3EA4, 0x09D0, 0x08F4, 0x3E7C, 0x0014, + 0x000C, 0x3E98, 0x0998, 0x092C, 0x3E84, 0x0014, + 0x0010, 0x3E90, 0x0964, 0x0960, 0x3E8C, 0x0010 +}; + +//========================================= +// <num_taps> = 6 +// <num_phases> = 64 +// <scale_ratio> = 1.49999 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_6tap_64p_149[198] = { + 0x3F14, 0x0394, 0x0AB0, 0x0394, 0x3F14, 0x0000, + 0x3F18, 0x036C, 0x0AB0, 0x03B8, 0x3F14, 0x0000, + 0x3F18, 0x0348, 0x0AAC, 0x03E0, 0x3F14, 0x0000, + 0x3F1C, 0x0320, 0x0AAC, 0x0408, 0x3F10, 0x0000, + 0x3F20, 0x02FC, 0x0AA8, 0x042C, 0x3F10, 0x0000, + 0x3F24, 0x02D8, 0x0AA0, 0x0454, 0x3F10, 0x0000, + 0x3F28, 0x02B4, 0x0A98, 0x047C, 0x3F10, 0x0000, + 0x3F28, 0x0290, 0x0A90, 0x04A4, 0x3F14, 0x0000, + 0x3F30, 0x026C, 0x0A84, 0x04CC, 0x3F14, 0x0000, + 0x3F34, 0x024C, 0x0A7C, 0x04F4, 0x3F14, 0x3FFC, + 0x3F38, 0x0228, 0x0A70, 0x051C, 0x3F18, 0x3FFC, + 0x3F3C, 0x0208, 0x0A64, 0x0544, 0x3F1C, 0x3FF8, + 0x3F40, 0x01E8, 0x0A54, 0x056C, 0x3F20, 0x3FF8, + 0x3F44, 0x01C8, 0x0A48, 0x0594, 0x3F24, 0x3FF4, + 0x3F4C, 0x01A8, 0x0A34, 0x05BC, 0x3F28, 0x3FF4, + 0x3F50, 0x0188, 0x0A28, 0x05E4, 0x3F2C, 0x3FF0, + 0x3F54, 0x016C, 0x0A10, 0x060C, 0x3F34, 0x3FF0, + 0x3F5C, 0x014C, 0x09FC, 0x0634, 0x3F3C, 0x3FEC, + 0x3F60, 0x0130, 0x09EC, 0x065C, 0x3F40, 0x3FE8, + 0x3F68, 0x0114, 0x09D0, 0x0684, 0x3F48, 0x3FE8, + 0x3F6C, 0x00F8, 0x09B8, 0x06AC, 0x3F54, 0x3FE4, + 0x3F74, 0x00E0, 0x09A0, 0x06D0, 0x3F5C, 0x3FE0, + 0x3F78, 0x00C4, 0x098C, 0x06F8, 0x3F64, 0x3FDC, + 0x3F7C, 0x00AC, 0x0970, 0x0720, 0x3F70, 0x3FD8, + 0x3F84, 0x0094, 0x0954, 0x0744, 0x3F7C, 0x3FD4, + 0x3F88, 0x007C, 0x093C, 0x0768, 0x3F88, 0x3FD0, + 0x3F90, 0x0064, 0x091C, 0x0790, 0x3F94, 0x3FCC, + 0x3F94, 0x0050, 0x08FC, 0x07B4, 0x3FA4, 0x3FC8, + 0x3F98, 0x003C, 0x08E0, 0x07D8, 0x3FB0, 0x3FC4, + 0x3FA0, 0x0024, 0x08C0, 0x07FC, 0x3FC0, 0x3FC0, + 0x3FA4, 0x0014, 0x08A4, 0x081C, 0x3FD0, 0x3FB8, + 0x3FAC, 0x0000, 0x0880, 0x0840, 0x3FE0, 0x3FB4, + 0x3FB0, 0x3FF0, 0x0860, 0x0860, 0x3FF0, 0x3FB0 +}; + +//========================================= +// <num_taps> = 6 +// <num_phases> = 64 +// <scale_ratio> = 1.83332 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_6tap_64p_183[198] = { + 0x002C, 0x0420, 0x076C, 0x041C, 0x002C, 0x0000, + 0x0028, 0x040C, 0x0768, 0x0430, 0x0034, 0x0000, + 0x0020, 0x03F8, 0x0768, 0x0448, 0x003C, 0x3FFC, + 0x0018, 0x03E4, 0x0768, 0x045C, 0x0044, 0x3FFC, + 0x0014, 0x03D0, 0x0768, 0x0470, 0x004C, 0x3FF8, + 0x000C, 0x03BC, 0x0764, 0x0484, 0x0058, 0x3FF8, + 0x0008, 0x03A4, 0x0764, 0x049C, 0x0060, 0x3FF4, + 0x0004, 0x0390, 0x0760, 0x04B0, 0x0068, 0x3FF4, + 0x0000, 0x037C, 0x0760, 0x04C4, 0x0070, 0x3FF0, + 0x3FFC, 0x0364, 0x075C, 0x04D8, 0x007C, 0x3FF0, + 0x3FF8, 0x0350, 0x0758, 0x04F0, 0x0084, 0x3FEC, + 0x3FF4, 0x033C, 0x0750, 0x0504, 0x0090, 0x3FEC, + 0x3FF0, 0x0328, 0x074C, 0x0518, 0x009C, 0x3FE8, + 0x3FEC, 0x0314, 0x0744, 0x052C, 0x00A8, 0x3FE8, + 0x3FE8, 0x0304, 0x0740, 0x0540, 0x00B0, 0x3FE4, + 0x3FE4, 0x02EC, 0x073C, 0x0554, 0x00BC, 0x3FE4, + 0x3FE0, 0x02DC, 0x0734, 0x0568, 0x00C8, 0x3FE0, + 0x3FE0, 0x02C4, 0x072C, 0x057C, 0x00D4, 0x3FE0, + 0x3FDC, 0x02B4, 0x0724, 0x058C, 0x00E4, 0x3FDC, + 0x3FDC, 0x02A0, 0x0718, 0x05A0, 0x00F0, 0x3FDC, + 0x3FD8, 0x028C, 0x0714, 0x05B4, 0x00FC, 0x3FD8, + 0x3FD8, 0x0278, 0x0704, 0x05C8, 0x010C, 0x3FD8, + 0x3FD4, 0x0264, 0x0700, 0x05D8, 0x0118, 0x3FD8, + 0x3FD4, 0x0254, 0x06F0, 0x05EC, 0x0128, 0x3FD4, + 0x3FD0, 0x0244, 0x06E8, 0x05FC, 0x0134, 0x3FD4, + 0x3FD0, 0x0230, 0x06DC, 0x060C, 0x0144, 0x3FD4, + 0x3FD0, 0x021C, 0x06D0, 0x0620, 0x0154, 0x3FD0, + 0x3FD0, 0x0208, 0x06C4, 0x0630, 0x0164, 0x3FD0, + 0x3FD0, 0x01F8, 0x06B8, 0x0640, 0x0170, 0x3FD0, + 0x3FCC, 0x01E8, 0x06AC, 0x0650, 0x0180, 0x3FD0, + 0x3FCC, 0x01D8, 0x069C, 0x0660, 0x0190, 0x3FD0, + 0x3FCC, 0x01C4, 0x068C, 0x0670, 0x01A4, 0x3FD0, + 0x3FCC, 0x01B8, 0x0680, 0x067C, 0x01B4, 0x3FCC +}; + +//========================================= +// <num_taps> = 7 +// <num_phases> = 64 +// <scale_ratio> = 0.83333 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_7tap_64p_upscale[231] = { + 0x00B0, 0x3D98, 0x09BC, 0x09B8, 0x3D94, 0x00B0, 0x0000, + 0x00AC, 0x3DA0, 0x0968, 0x0A10, 0x3D88, 0x00B4, 0x0000, + 0x00A8, 0x3DAC, 0x0914, 0x0A60, 0x3D80, 0x00B8, 0x0000, + 0x00A4, 0x3DB8, 0x08C0, 0x0AB4, 0x3D78, 0x00BC, 0x3FFC, + 0x00A0, 0x3DC8, 0x0868, 0x0B00, 0x3D74, 0x00C0, 0x3FFC, + 0x0098, 0x3DD8, 0x0818, 0x0B54, 0x3D6C, 0x00C0, 0x3FF8, + 0x0094, 0x3DE8, 0x07C0, 0x0B9C, 0x3D6C, 0x00C4, 0x3FF8, + 0x008C, 0x3DFC, 0x0768, 0x0BEC, 0x3D68, 0x00C4, 0x3FF8, + 0x0088, 0x3E0C, 0x0714, 0x0C38, 0x3D68, 0x00C4, 0x3FF4, + 0x0080, 0x3E20, 0x06BC, 0x0C80, 0x3D6C, 0x00C4, 0x3FF4, + 0x0078, 0x3E34, 0x0668, 0x0CC4, 0x3D70, 0x00C4, 0x3FF4, + 0x0074, 0x3E48, 0x0610, 0x0D08, 0x3D78, 0x00C4, 0x3FF0, + 0x006C, 0x3E5C, 0x05BC, 0x0D48, 0x3D80, 0x00C4, 0x3FF0, + 0x0068, 0x3E74, 0x0568, 0x0D84, 0x3D88, 0x00C0, 0x3FF0, + 0x0060, 0x3E88, 0x0514, 0x0DC8, 0x3D94, 0x00BC, 0x3FEC, + 0x0058, 0x3E9C, 0x04C0, 0x0E04, 0x3DA4, 0x00B8, 0x3FEC, + 0x0054, 0x3EB4, 0x046C, 0x0E38, 0x3DB4, 0x00B4, 0x3FEC, + 0x004C, 0x3ECC, 0x0418, 0x0E6C, 0x3DC8, 0x00B0, 0x3FEC, + 0x0044, 0x3EE0, 0x03C8, 0x0EA4, 0x3DDC, 0x00A8, 0x3FEC, + 0x0040, 0x3EF8, 0x0378, 0x0ED0, 0x3DF4, 0x00A0, 0x3FEC, + 0x0038, 0x3F0C, 0x032C, 0x0EFC, 0x3E10, 0x0098, 0x3FEC, + 0x0034, 0x3F24, 0x02DC, 0x0F24, 0x3E2C, 0x0090, 0x3FEC, + 0x002C, 0x3F38, 0x0294, 0x0F4C, 0x3E48, 0x0088, 0x3FEC, + 0x0028, 0x3F50, 0x0248, 0x0F68, 0x3E6C, 0x007C, 0x3FF0, + 0x0020, 0x3F64, 0x0200, 0x0F88, 0x3E90, 0x0074, 0x3FF0, + 0x001C, 0x3F7C, 0x01B8, 0x0FA4, 0x3EB4, 0x0068, 0x3FF0, + 0x0018, 0x3F90, 0x0174, 0x0FBC, 0x3EDC, 0x0058, 0x3FF4, + 0x0014, 0x3FA4, 0x0130, 0x0FD0, 0x3F08, 0x004C, 0x3FF4, + 0x000C, 0x3FB8, 0x00F0, 0x0FE4, 0x3F34, 0x003C, 0x3FF8, + 0x0008, 0x3FCC, 0x00B0, 0x0FF0, 0x3F64, 0x0030, 0x3FF8, + 0x0004, 0x3FDC, 0x0070, 0x0FFC, 0x3F98, 0x0020, 0x3FFC, + 0x0000, 0x3FF0, 0x0038, 0x0FFC, 0x3FCC, 0x0010, 0x0000, + 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000 +}; + +//========================================= +// <num_taps> = 7 +// <num_phases> = 64 +// <scale_ratio> = 1.16666 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_7tap_64p_116[231] = { + 0x0020, 0x3E58, 0x0988, 0x0988, 0x3E58, 0x0020, 0x0000, + 0x0024, 0x3E4C, 0x0954, 0x09C0, 0x3E64, 0x0018, 0x0000, + 0x002C, 0x3E44, 0x091C, 0x09F4, 0x3E70, 0x0010, 0x0000, + 0x0030, 0x3E3C, 0x08E8, 0x0A24, 0x3E80, 0x0008, 0x0000, + 0x0034, 0x3E34, 0x08AC, 0x0A5C, 0x3E90, 0x0000, 0x0000, + 0x003C, 0x3E30, 0x0870, 0x0A84, 0x3EA0, 0x3FFC, 0x0004, + 0x0040, 0x3E28, 0x0838, 0x0AB4, 0x3EB4, 0x3FF4, 0x0004, + 0x0044, 0x3E24, 0x07FC, 0x0AE4, 0x3EC8, 0x3FEC, 0x0004, + 0x0048, 0x3E24, 0x07C4, 0x0B08, 0x3EDC, 0x3FE4, 0x0008, + 0x0048, 0x3E20, 0x0788, 0x0B3C, 0x3EF4, 0x3FD8, 0x0008, + 0x004C, 0x3E20, 0x074C, 0x0B60, 0x3F0C, 0x3FD0, 0x000C, + 0x0050, 0x3E20, 0x0710, 0x0B8C, 0x3F24, 0x3FC4, 0x000C, + 0x0050, 0x3E20, 0x06D4, 0x0BB0, 0x3F40, 0x3FBC, 0x0010, + 0x0054, 0x3E24, 0x0698, 0x0BD4, 0x3F5C, 0x3FB0, 0x0010, + 0x0054, 0x3E24, 0x065C, 0x0BFC, 0x3F78, 0x3FA4, 0x0014, + 0x0054, 0x3E28, 0x0624, 0x0C1C, 0x3F98, 0x3F98, 0x0014, + 0x0058, 0x3E2C, 0x05E4, 0x0C3C, 0x3FB8, 0x3F8C, 0x0018, + 0x0058, 0x3E34, 0x05A8, 0x0C58, 0x3FD8, 0x3F80, 0x001C, + 0x0058, 0x3E38, 0x0570, 0x0C78, 0x3FF8, 0x3F74, 0x001C, + 0x0058, 0x3E40, 0x0534, 0x0C94, 0x0018, 0x3F68, 0x0020, + 0x0058, 0x3E48, 0x04F4, 0x0CAC, 0x0040, 0x3F5C, 0x0024, + 0x0058, 0x3E50, 0x04BC, 0x0CC4, 0x0064, 0x3F50, 0x0024, + 0x0054, 0x3E58, 0x0484, 0x0CD8, 0x008C, 0x3F44, 0x0028, + 0x0054, 0x3E60, 0x0448, 0x0CEC, 0x00B4, 0x3F38, 0x002C, + 0x0054, 0x3E68, 0x0410, 0x0CFC, 0x00E0, 0x3F28, 0x0030, + 0x0054, 0x3E74, 0x03D4, 0x0D0C, 0x010C, 0x3F1C, 0x0030, + 0x0050, 0x3E7C, 0x03A0, 0x0D18, 0x0138, 0x3F10, 0x0034, + 0x0050, 0x3E88, 0x0364, 0x0D24, 0x0164, 0x3F04, 0x0038, + 0x004C, 0x3E94, 0x0330, 0x0D30, 0x0194, 0x3EF4, 0x0038, + 0x004C, 0x3EA0, 0x02F8, 0x0D34, 0x01C4, 0x3EE8, 0x003C, + 0x0048, 0x3EAC, 0x02C0, 0x0D3C, 0x01F4, 0x3EDC, 0x0040, + 0x0048, 0x3EB8, 0x0290, 0x0D3C, 0x0224, 0x3ED0, 0x0040, + 0x0044, 0x3EC4, 0x0258, 0x0D40, 0x0258, 0x3EC4, 0x0044 +}; + +//========================================= +// <num_taps> = 7 +// <num_phases> = 64 +// <scale_ratio> = 1.49999 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_7tap_64p_149[231] = { + 0x3F68, 0x3FEC, 0x08A8, 0x08AC, 0x3FF0, 0x3F68, 0x0000, + 0x3F70, 0x3FDC, 0x0888, 0x08CC, 0x0000, 0x3F60, 0x0000, + 0x3F74, 0x3FC8, 0x0868, 0x08F0, 0x0014, 0x3F58, 0x0000, + 0x3F7C, 0x3FB4, 0x0844, 0x0908, 0x002C, 0x3F54, 0x0004, + 0x3F84, 0x3FA4, 0x0820, 0x0924, 0x0044, 0x3F4C, 0x0004, + 0x3F88, 0x3F90, 0x0800, 0x0944, 0x005C, 0x3F44, 0x0004, + 0x3F90, 0x3F80, 0x07D8, 0x095C, 0x0074, 0x3F40, 0x0008, + 0x3F98, 0x3F70, 0x07B0, 0x097C, 0x008C, 0x3F38, 0x0008, + 0x3F9C, 0x3F60, 0x0790, 0x0994, 0x00A8, 0x3F30, 0x0008, + 0x3FA4, 0x3F54, 0x0764, 0x09B0, 0x00C4, 0x3F28, 0x0008, + 0x3FA8, 0x3F48, 0x0740, 0x09C4, 0x00DC, 0x3F24, 0x000C, + 0x3FB0, 0x3F38, 0x0718, 0x09DC, 0x00FC, 0x3F1C, 0x000C, + 0x3FB4, 0x3F2C, 0x06F0, 0x09F4, 0x0118, 0x3F18, 0x000C, + 0x3FBC, 0x3F24, 0x06C8, 0x0A08, 0x0134, 0x3F10, 0x000C, + 0x3FC0, 0x3F18, 0x06A0, 0x0A1C, 0x0154, 0x3F08, 0x0010, + 0x3FC8, 0x3F10, 0x0678, 0x0A2C, 0x0170, 0x3F04, 0x0010, + 0x3FCC, 0x3F04, 0x0650, 0x0A40, 0x0190, 0x3F00, 0x0010, + 0x3FD0, 0x3EFC, 0x0628, 0x0A54, 0x01B0, 0x3EF8, 0x0010, + 0x3FD4, 0x3EF4, 0x0600, 0x0A64, 0x01D0, 0x3EF4, 0x0010, + 0x3FDC, 0x3EEC, 0x05D8, 0x0A6C, 0x01F4, 0x3EF0, 0x0010, + 0x3FE0, 0x3EE8, 0x05B0, 0x0A7C, 0x0214, 0x3EE8, 0x0010, + 0x3FE4, 0x3EE0, 0x0588, 0x0A88, 0x0238, 0x3EE4, 0x0010, + 0x3FE8, 0x3EDC, 0x055C, 0x0A98, 0x0258, 0x3EE0, 0x0010, + 0x3FEC, 0x3ED8, 0x0534, 0x0AA0, 0x027C, 0x3EDC, 0x0010, + 0x3FF0, 0x3ED4, 0x050C, 0x0AAC, 0x02A0, 0x3ED8, 0x000C, + 0x3FF4, 0x3ED0, 0x04E4, 0x0AB4, 0x02C4, 0x3ED4, 0x000C, + 0x3FF4, 0x3ECC, 0x04C0, 0x0ABC, 0x02E8, 0x3ED0, 0x000C, + 0x3FF8, 0x3ECC, 0x0494, 0x0AC0, 0x030C, 0x3ED0, 0x000C, + 0x3FFC, 0x3EC8, 0x046C, 0x0AC8, 0x0334, 0x3ECC, 0x0008, + 0x0000, 0x3EC8, 0x0444, 0x0AC8, 0x0358, 0x3ECC, 0x0008, + 0x0000, 0x3EC8, 0x041C, 0x0ACC, 0x0380, 0x3EC8, 0x0008, + 0x0000, 0x3EC8, 0x03F4, 0x0AD0, 0x03A8, 0x3EC8, 0x0004, + 0x0004, 0x3EC8, 0x03CC, 0x0AD0, 0x03CC, 0x3EC8, 0x0004 +}; + +//========================================= +// <num_taps> = 7 +// <num_phases> = 64 +// <scale_ratio> = 1.83332 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_7tap_64p_183[231] = { + 0x3FA4, 0x01E8, 0x0674, 0x0674, 0x01E8, 0x3FA4, 0x0000, + 0x3FA4, 0x01D4, 0x0668, 0x0684, 0x01F8, 0x3FA4, 0x0000, + 0x3FA4, 0x01C4, 0x0658, 0x0690, 0x0208, 0x3FA8, 0x0000, + 0x3FA0, 0x01B4, 0x064C, 0x06A0, 0x021C, 0x3FA8, 0x3FFC, + 0x3FA0, 0x01A4, 0x063C, 0x06AC, 0x022C, 0x3FAC, 0x3FFC, + 0x3FA0, 0x0194, 0x0630, 0x06B4, 0x0240, 0x3FAC, 0x3FFC, + 0x3FA0, 0x0184, 0x0620, 0x06C4, 0x0250, 0x3FB0, 0x3FF8, + 0x3FA0, 0x0174, 0x0614, 0x06CC, 0x0264, 0x3FB0, 0x3FF8, + 0x3FA0, 0x0164, 0x0604, 0x06D8, 0x0278, 0x3FB4, 0x3FF4, + 0x3FA0, 0x0154, 0x05F4, 0x06E4, 0x0288, 0x3FB8, 0x3FF4, + 0x3FA0, 0x0148, 0x05E4, 0x06EC, 0x029C, 0x3FBC, 0x3FF0, + 0x3FA0, 0x0138, 0x05D4, 0x06F4, 0x02B0, 0x3FC0, 0x3FF0, + 0x3FA0, 0x0128, 0x05C4, 0x0704, 0x02C4, 0x3FC0, 0x3FEC, + 0x3FA0, 0x011C, 0x05B4, 0x0708, 0x02D8, 0x3FC4, 0x3FEC, + 0x3FA4, 0x010C, 0x05A4, 0x0714, 0x02E8, 0x3FC8, 0x3FE8, + 0x3FA4, 0x0100, 0x0590, 0x0718, 0x02FC, 0x3FD0, 0x3FE8, + 0x3FA4, 0x00F0, 0x0580, 0x0724, 0x0310, 0x3FD4, 0x3FE4, + 0x3FA4, 0x00E4, 0x056C, 0x072C, 0x0324, 0x3FD8, 0x3FE4, + 0x3FA8, 0x00D8, 0x055C, 0x0730, 0x0338, 0x3FDC, 0x3FE0, + 0x3FA8, 0x00CC, 0x0548, 0x0738, 0x034C, 0x3FE4, 0x3FDC, + 0x3FA8, 0x00BC, 0x0538, 0x0740, 0x0360, 0x3FE8, 0x3FDC, + 0x3FAC, 0x00B0, 0x0528, 0x0744, 0x0374, 0x3FEC, 0x3FD8, + 0x3FAC, 0x00A4, 0x0514, 0x0748, 0x0388, 0x3FF4, 0x3FD8, + 0x3FB0, 0x0098, 0x0500, 0x074C, 0x039C, 0x3FFC, 0x3FD4, + 0x3FB0, 0x0090, 0x04EC, 0x0750, 0x03B0, 0x0000, 0x3FD4, + 0x3FB0, 0x0084, 0x04DC, 0x0758, 0x03C4, 0x0004, 0x3FD0, + 0x3FB4, 0x0078, 0x04CC, 0x0758, 0x03D8, 0x000C, 0x3FCC, + 0x3FB4, 0x006C, 0x04B8, 0x075C, 0x03EC, 0x0014, 0x3FCC, + 0x3FB8, 0x0064, 0x04A0, 0x0760, 0x0400, 0x001C, 0x3FC8, + 0x3FB8, 0x0058, 0x0490, 0x0760, 0x0414, 0x0024, 0x3FC8, + 0x3FBC, 0x0050, 0x047C, 0x0760, 0x0428, 0x002C, 0x3FC4, + 0x3FBC, 0x0048, 0x0464, 0x0764, 0x043C, 0x0034, 0x3FC4, + 0x3FC0, 0x003C, 0x0454, 0x0764, 0x0450, 0x003C, 0x3FC0 +}; + +//========================================= +// <num_taps> = 8 +// <num_phases> = 64 +// <scale_ratio> = 0.83333 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_8tap_64p_upscale[264] = { + 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x3FFC, 0x0014, 0x3FC8, 0x1000, 0x0038, 0x3FEC, 0x0004, 0x0000, + 0x3FF4, 0x0024, 0x3F94, 0x0FFC, 0x0074, 0x3FD8, 0x000C, 0x0000, + 0x3FF0, 0x0038, 0x3F60, 0x0FEC, 0x00B4, 0x3FC4, 0x0014, 0x0000, + 0x3FEC, 0x004C, 0x3F2C, 0x0FE4, 0x00F4, 0x3FAC, 0x0018, 0x0000, + 0x3FE4, 0x005C, 0x3F00, 0x0FD4, 0x0138, 0x3F94, 0x0020, 0x0000, + 0x3FE0, 0x006C, 0x3ED0, 0x0FC4, 0x017C, 0x3F7C, 0x0028, 0x0000, + 0x3FDC, 0x007C, 0x3EA8, 0x0FA4, 0x01C4, 0x3F68, 0x0030, 0x0000, + 0x3FD8, 0x0088, 0x3E80, 0x0F90, 0x020C, 0x3F50, 0x0038, 0x3FFC, + 0x3FD4, 0x0098, 0x3E58, 0x0F70, 0x0258, 0x3F38, 0x0040, 0x3FFC, + 0x3FD0, 0x00A4, 0x3E34, 0x0F54, 0x02A0, 0x3F1C, 0x004C, 0x3FFC, + 0x3FD0, 0x00B0, 0x3E14, 0x0F28, 0x02F0, 0x3F04, 0x0054, 0x3FFC, + 0x3FCC, 0x00BC, 0x3DF4, 0x0F08, 0x033C, 0x3EEC, 0x005C, 0x3FF8, + 0x3FC8, 0x00C8, 0x3DD8, 0x0EDC, 0x038C, 0x3ED4, 0x0064, 0x3FF8, + 0x3FC8, 0x00D0, 0x3DC0, 0x0EAC, 0x03E0, 0x3EBC, 0x006C, 0x3FF4, + 0x3FC4, 0x00D8, 0x3DA8, 0x0E7C, 0x0430, 0x3EA4, 0x0078, 0x3FF4, + 0x3FC4, 0x00E0, 0x3D94, 0x0E48, 0x0484, 0x3E8C, 0x0080, 0x3FF0, + 0x3FC4, 0x00E8, 0x3D80, 0x0E10, 0x04D8, 0x3E74, 0x0088, 0x3FF0, + 0x3FC4, 0x00F0, 0x3D70, 0x0DD8, 0x052C, 0x3E5C, 0x0090, 0x3FEC, + 0x3FC0, 0x00F4, 0x3D60, 0x0DA0, 0x0584, 0x3E44, 0x0098, 0x3FEC, + 0x3FC0, 0x00F8, 0x3D54, 0x0D68, 0x05D8, 0x3E2C, 0x00A0, 0x3FE8, + 0x3FC0, 0x00FC, 0x3D48, 0x0D20, 0x0630, 0x3E18, 0x00AC, 0x3FE8, + 0x3FC0, 0x0100, 0x3D40, 0x0CE0, 0x0688, 0x3E00, 0x00B4, 0x3FE4, + 0x3FC4, 0x0100, 0x3D3C, 0x0C98, 0x06DC, 0x3DEC, 0x00BC, 0x3FE4, + 0x3FC4, 0x0100, 0x3D38, 0x0C58, 0x0734, 0x3DD8, 0x00C0, 0x3FE0, + 0x3FC4, 0x0104, 0x3D38, 0x0C0C, 0x078C, 0x3DC4, 0x00C8, 0x3FDC, + 0x3FC4, 0x0100, 0x3D38, 0x0BC4, 0x07E4, 0x3DB0, 0x00D0, 0x3FDC, + 0x3FC4, 0x0100, 0x3D38, 0x0B78, 0x083C, 0x3DA0, 0x00D8, 0x3FD8, + 0x3FC8, 0x0100, 0x3D3C, 0x0B28, 0x0890, 0x3D90, 0x00DC, 0x3FD8, + 0x3FC8, 0x00FC, 0x3D40, 0x0ADC, 0x08E8, 0x3D80, 0x00E4, 0x3FD4, + 0x3FCC, 0x00FC, 0x3D48, 0x0A84, 0x093C, 0x3D74, 0x00E8, 0x3FD4, + 0x3FCC, 0x00F8, 0x3D50, 0x0A38, 0x0990, 0x3D64, 0x00F0, 0x3FD0, + 0x3FD0, 0x00F4, 0x3D58, 0x09E0, 0x09E4, 0x3D5C, 0x00F4, 0x3FD0 +}; + +//========================================= +// <num_taps> = 8 +// <num_phases> = 64 +// <scale_ratio> = 1.16666 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_8tap_64p_116[264] = { + 0x0080, 0x3E90, 0x0268, 0x0D14, 0x0264, 0x3E90, 0x0080, 0x0000, + 0x007C, 0x3E9C, 0x0238, 0x0D14, 0x0298, 0x3E84, 0x0080, 0x0000, + 0x0078, 0x3EAC, 0x0200, 0x0D10, 0x02D0, 0x3E78, 0x0084, 0x0000, + 0x0078, 0x3EB8, 0x01D0, 0x0D0C, 0x0304, 0x3E6C, 0x0084, 0x0000, + 0x0074, 0x3EC8, 0x01A0, 0x0D00, 0x033C, 0x3E60, 0x0088, 0x0000, + 0x0070, 0x3ED4, 0x0170, 0x0D00, 0x0374, 0x3E54, 0x0088, 0x3FFC, + 0x006C, 0x3EE4, 0x0140, 0x0CF8, 0x03AC, 0x3E48, 0x0088, 0x3FFC, + 0x006C, 0x3EF0, 0x0114, 0x0CE8, 0x03E4, 0x3E3C, 0x008C, 0x3FFC, + 0x0068, 0x3F00, 0x00E8, 0x0CD8, 0x041C, 0x3E34, 0x008C, 0x3FFC, + 0x0064, 0x3F10, 0x00BC, 0x0CCC, 0x0454, 0x3E28, 0x008C, 0x3FFC, + 0x0060, 0x3F1C, 0x0090, 0x0CBC, 0x0490, 0x3E20, 0x008C, 0x3FFC, + 0x005C, 0x3F2C, 0x0068, 0x0CA4, 0x04CC, 0x3E18, 0x008C, 0x3FFC, + 0x0058, 0x3F38, 0x0040, 0x0C94, 0x0504, 0x3E10, 0x008C, 0x3FFC, + 0x0054, 0x3F48, 0x001C, 0x0C7C, 0x0540, 0x3E08, 0x0088, 0x3FFC, + 0x0050, 0x3F54, 0x3FF8, 0x0C60, 0x057C, 0x3E04, 0x0088, 0x3FFC, + 0x004C, 0x3F64, 0x3FD4, 0x0C44, 0x05B8, 0x3DFC, 0x0088, 0x3FFC, + 0x0048, 0x3F70, 0x3FB4, 0x0C28, 0x05F4, 0x3DF8, 0x0084, 0x3FFC, + 0x0044, 0x3F80, 0x3F90, 0x0C0C, 0x0630, 0x3DF4, 0x0080, 0x3FFC, + 0x0040, 0x3F8C, 0x3F70, 0x0BE8, 0x066C, 0x3DF4, 0x0080, 0x3FFC, + 0x003C, 0x3F9C, 0x3F50, 0x0BC8, 0x06A8, 0x3DF0, 0x007C, 0x3FFC, + 0x0038, 0x3FA8, 0x3F34, 0x0BA0, 0x06E4, 0x3DF0, 0x0078, 0x0000, + 0x0034, 0x3FB4, 0x3F18, 0x0B80, 0x071C, 0x3DF0, 0x0074, 0x0000, + 0x0030, 0x3FC0, 0x3EFC, 0x0B5C, 0x0758, 0x3DF0, 0x0070, 0x0000, + 0x002C, 0x3FCC, 0x3EE4, 0x0B34, 0x0794, 0x3DF4, 0x0068, 0x0000, + 0x002C, 0x3FDC, 0x3ECC, 0x0B08, 0x07CC, 0x3DF4, 0x0064, 0x0000, + 0x0028, 0x3FE4, 0x3EB4, 0x0AE0, 0x0808, 0x3DF8, 0x0060, 0x0000, + 0x0024, 0x3FF0, 0x3EA0, 0x0AB0, 0x0840, 0x3E00, 0x0058, 0x0004, + 0x0020, 0x3FFC, 0x3E90, 0x0A84, 0x0878, 0x3E04, 0x0050, 0x0004, + 0x001C, 0x0004, 0x3E7C, 0x0A54, 0x08B0, 0x3E0C, 0x004C, 0x0008, + 0x0018, 0x000C, 0x3E68, 0x0A28, 0x08E8, 0x3E18, 0x0044, 0x0008, + 0x0018, 0x0018, 0x3E54, 0x09F4, 0x0920, 0x3E20, 0x003C, 0x000C, + 0x0014, 0x0020, 0x3E48, 0x09C0, 0x0954, 0x3E2C, 0x0034, 0x0010, + 0x0010, 0x002C, 0x3E3C, 0x098C, 0x0988, 0x3E38, 0x002C, 0x0010 +}; + +//========================================= +// <num_taps> = 8 +// <num_phases> = 64 +// <scale_ratio> = 1.49999 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_8tap_64p_149[264] = { + 0x0008, 0x3E8C, 0x03F8, 0x0AE8, 0x03F8, 0x3E8C, 0x0008, 0x0000, + 0x000C, 0x3E8C, 0x03D0, 0x0AE8, 0x0420, 0x3E90, 0x0000, 0x0000, + 0x000C, 0x3E8C, 0x03AC, 0x0AE8, 0x0444, 0x3E90, 0x0000, 0x0000, + 0x0010, 0x3E90, 0x0384, 0x0AE0, 0x046C, 0x3E94, 0x3FFC, 0x0000, + 0x0014, 0x3E90, 0x035C, 0x0ADC, 0x0494, 0x3E94, 0x3FF8, 0x0004, + 0x0018, 0x3E90, 0x0334, 0x0AD8, 0x04BC, 0x3E98, 0x3FF4, 0x0004, + 0x001C, 0x3E94, 0x0310, 0x0AD0, 0x04E4, 0x3E9C, 0x3FEC, 0x0004, + 0x0020, 0x3E98, 0x02E8, 0x0AC4, 0x050C, 0x3EA0, 0x3FE8, 0x0008, + 0x0020, 0x3E98, 0x02C4, 0x0AC0, 0x0534, 0x3EA4, 0x3FE4, 0x0008, + 0x0024, 0x3E9C, 0x02A0, 0x0AB4, 0x055C, 0x3EAC, 0x3FDC, 0x0008, + 0x0024, 0x3EA0, 0x027C, 0x0AA8, 0x0584, 0x3EB0, 0x3FD8, 0x000C, + 0x0028, 0x3EA4, 0x0258, 0x0A9C, 0x05AC, 0x3EB8, 0x3FD0, 0x000C, + 0x0028, 0x3EA8, 0x0234, 0x0A90, 0x05D4, 0x3EC0, 0x3FC8, 0x0010, + 0x002C, 0x3EAC, 0x0210, 0x0A80, 0x05FC, 0x3EC8, 0x3FC4, 0x0010, + 0x002C, 0x3EB4, 0x01F0, 0x0A70, 0x0624, 0x3ED0, 0x3FBC, 0x0010, + 0x002C, 0x3EB8, 0x01CC, 0x0A60, 0x064C, 0x3EDC, 0x3FB4, 0x0014, + 0x0030, 0x3EBC, 0x01A8, 0x0A50, 0x0674, 0x3EE4, 0x3FB0, 0x0014, + 0x0030, 0x3EC4, 0x0188, 0x0A38, 0x069C, 0x3EF0, 0x3FA8, 0x0018, + 0x0030, 0x3ECC, 0x0168, 0x0A28, 0x06C0, 0x3EFC, 0x3FA0, 0x0018, + 0x0030, 0x3ED0, 0x0148, 0x0A14, 0x06E8, 0x3F08, 0x3F98, 0x001C, + 0x0030, 0x3ED8, 0x012C, 0x0A00, 0x070C, 0x3F14, 0x3F90, 0x001C, + 0x0034, 0x3EE0, 0x0108, 0x09E4, 0x0734, 0x3F24, 0x3F8C, 0x001C, + 0x0034, 0x3EE4, 0x00EC, 0x09CC, 0x0758, 0x3F34, 0x3F84, 0x0020, + 0x0034, 0x3EEC, 0x00D0, 0x09B8, 0x077C, 0x3F40, 0x3F7C, 0x0020, + 0x0034, 0x3EF4, 0x00B4, 0x0998, 0x07A4, 0x3F50, 0x3F74, 0x0024, + 0x0030, 0x3EFC, 0x0098, 0x0980, 0x07C8, 0x3F64, 0x3F6C, 0x0024, + 0x0030, 0x3F04, 0x0080, 0x0968, 0x07E8, 0x3F74, 0x3F64, 0x0024, + 0x0030, 0x3F0C, 0x0060, 0x094C, 0x080C, 0x3F88, 0x3F5C, 0x0028, + 0x0030, 0x3F14, 0x0048, 0x0930, 0x0830, 0x3F98, 0x3F54, 0x0028, + 0x0030, 0x3F1C, 0x0030, 0x0914, 0x0850, 0x3FAC, 0x3F4C, 0x0028, + 0x0030, 0x3F24, 0x0018, 0x08F0, 0x0874, 0x3FC0, 0x3F44, 0x002C, + 0x002C, 0x3F2C, 0x0000, 0x08D4, 0x0894, 0x3FD8, 0x3F3C, 0x002C, + 0x002C, 0x3F34, 0x3FEC, 0x08B4, 0x08B4, 0x3FEC, 0x3F34, 0x002C +}; + +//========================================= +// <num_taps> = 8 +// <num_phases> = 64 +// <scale_ratio> = 1.83332 (input/output) +// <sharpness> = 0 +// <CoefType> = ModifiedLanczos +// <CoefQuant> = 1.10 +// <CoefOut> = 1.12 +//========================================= +static const uint16_t filter_8tap_64p_183[264] = { + 0x3F88, 0x0048, 0x047C, 0x0768, 0x047C, 0x0048, 0x3F88, 0x0000, + 0x3F88, 0x003C, 0x0468, 0x076C, 0x0490, 0x0054, 0x3F84, 0x0000, + 0x3F8C, 0x0034, 0x0454, 0x0768, 0x04A4, 0x005C, 0x3F84, 0x0000, + 0x3F8C, 0x0028, 0x0444, 0x076C, 0x04B4, 0x0068, 0x3F80, 0x0000, + 0x3F90, 0x0020, 0x042C, 0x0768, 0x04C8, 0x0074, 0x3F80, 0x0000, + 0x3F90, 0x0018, 0x041C, 0x0764, 0x04DC, 0x0080, 0x3F7C, 0x0000, + 0x3F94, 0x0010, 0x0408, 0x075C, 0x04F0, 0x008C, 0x3F7C, 0x0000, + 0x3F94, 0x0004, 0x03F8, 0x0760, 0x0500, 0x0098, 0x3F7C, 0x3FFC, + 0x3F98, 0x0000, 0x03E0, 0x075C, 0x0514, 0x00A4, 0x3F78, 0x3FFC, + 0x3F9C, 0x3FF8, 0x03CC, 0x0754, 0x0528, 0x00B0, 0x3F78, 0x3FFC, + 0x3F9C, 0x3FF0, 0x03B8, 0x0754, 0x0538, 0x00BC, 0x3F78, 0x3FFC, + 0x3FA0, 0x3FE8, 0x03A4, 0x0750, 0x054C, 0x00CC, 0x3F74, 0x3FF8, + 0x3FA4, 0x3FE0, 0x0390, 0x074C, 0x055C, 0x00D8, 0x3F74, 0x3FF8, + 0x3FA4, 0x3FDC, 0x037C, 0x0744, 0x0570, 0x00E4, 0x3F74, 0x3FF8, + 0x3FA8, 0x3FD4, 0x0368, 0x0740, 0x0580, 0x00F4, 0x3F74, 0x3FF4, + 0x3FA8, 0x3FCC, 0x0354, 0x073C, 0x0590, 0x0104, 0x3F74, 0x3FF4, + 0x3FAC, 0x3FC8, 0x0340, 0x0730, 0x05A4, 0x0110, 0x3F74, 0x3FF4, + 0x3FB0, 0x3FC0, 0x0330, 0x0728, 0x05B4, 0x0120, 0x3F74, 0x3FF0, + 0x3FB0, 0x3FBC, 0x031C, 0x0724, 0x05C4, 0x0130, 0x3F70, 0x3FF0, + 0x3FB4, 0x3FB4, 0x0308, 0x0720, 0x05D4, 0x013C, 0x3F70, 0x3FF0, + 0x3FB8, 0x3FB0, 0x02F4, 0x0714, 0x05E4, 0x014C, 0x3F74, 0x3FEC, + 0x3FB8, 0x3FAC, 0x02E0, 0x0708, 0x05F8, 0x015C, 0x3F74, 0x3FEC, + 0x3FBC, 0x3FA8, 0x02CC, 0x0704, 0x0604, 0x016C, 0x3F74, 0x3FE8, + 0x3FC0, 0x3FA0, 0x02BC, 0x06F8, 0x0614, 0x017C, 0x3F74, 0x3FE8, + 0x3FC0, 0x3F9C, 0x02A8, 0x06F4, 0x0624, 0x018C, 0x3F74, 0x3FE4, + 0x3FC4, 0x3F98, 0x0294, 0x06E8, 0x0634, 0x019C, 0x3F74, 0x3FE4, + 0x3FC8, 0x3F94, 0x0284, 0x06D8, 0x0644, 0x01AC, 0x3F78, 0x3FE0, + 0x3FC8, 0x3F90, 0x0270, 0x06D4, 0x0650, 0x01BC, 0x3F78, 0x3FE0, + 0x3FCC, 0x3F8C, 0x025C, 0x06C8, 0x0660, 0x01D0, 0x3F78, 0x3FDC, + 0x3FCC, 0x3F8C, 0x024C, 0x06B8, 0x066C, 0x01E0, 0x3F7C, 0x3FDC, + 0x3FD0, 0x3F88, 0x0238, 0x06B0, 0x067C, 0x01F0, 0x3F7C, 0x3FD8, + 0x3FD4, 0x3F84, 0x0228, 0x069C, 0x0688, 0x0204, 0x3F80, 0x3FD8, + 0x3FD4, 0x3F84, 0x0214, 0x0694, 0x0694, 0x0214, 0x3F84, 0x3FD4 +}; + +const uint16_t *get_filter_3tap_16p(struct fixed31_32 ratio) +{ + if (ratio.value < dc_fixpt_one.value) + return filter_3tap_16p_upscale; + else if (ratio.value < dc_fixpt_from_fraction(4, 3).value) + return filter_3tap_16p_116; + else if (ratio.value < dc_fixpt_from_fraction(5, 3).value) + return filter_3tap_16p_149; + else + return filter_3tap_16p_183; +} + +const uint16_t *get_filter_3tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dc_fixpt_one.value) + return filter_3tap_64p_upscale; + else if (ratio.value < dc_fixpt_from_fraction(4, 3).value) + return filter_3tap_64p_116; + else if (ratio.value < dc_fixpt_from_fraction(5, 3).value) + return filter_3tap_64p_149; + else + return filter_3tap_64p_183; +} + +const uint16_t *get_filter_4tap_16p(struct fixed31_32 ratio) +{ + if (ratio.value < dc_fixpt_one.value) + return filter_4tap_16p_upscale; + else if (ratio.value < dc_fixpt_from_fraction(4, 3).value) + return filter_4tap_16p_116; + else if (ratio.value < dc_fixpt_from_fraction(5, 3).value) + return filter_4tap_16p_149; + else + return filter_4tap_16p_183; +} + +const uint16_t *get_filter_4tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dc_fixpt_one.value) + return filter_4tap_64p_upscale; + else if (ratio.value < dc_fixpt_from_fraction(4, 3).value) + return filter_4tap_64p_116; + else if (ratio.value < dc_fixpt_from_fraction(5, 3).value) + return filter_4tap_64p_149; + else + return filter_4tap_64p_183; +} + +const uint16_t *get_filter_5tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dc_fixpt_one.value) + return filter_5tap_64p_upscale; + else if (ratio.value < dc_fixpt_from_fraction(4, 3).value) + return filter_5tap_64p_116; + else if (ratio.value < dc_fixpt_from_fraction(5, 3).value) + return filter_5tap_64p_149; + else + return filter_5tap_64p_183; +} + +const uint16_t *get_filter_6tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dc_fixpt_one.value) + return filter_6tap_64p_upscale; + else if (ratio.value < dc_fixpt_from_fraction(4, 3).value) + return filter_6tap_64p_116; + else if (ratio.value < dc_fixpt_from_fraction(5, 3).value) + return filter_6tap_64p_149; + else + return filter_6tap_64p_183; +} + +const uint16_t *get_filter_7tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dc_fixpt_one.value) + return filter_7tap_64p_upscale; + else if (ratio.value < dc_fixpt_from_fraction(4, 3).value) + return filter_7tap_64p_116; + else if (ratio.value < dc_fixpt_from_fraction(5, 3).value) + return filter_7tap_64p_149; + else + return filter_7tap_64p_183; +} + +const uint16_t *get_filter_8tap_64p(struct fixed31_32 ratio) +{ + if (ratio.value < dc_fixpt_one.value) + return filter_8tap_64p_upscale; + else if (ratio.value < dc_fixpt_from_fraction(4, 3).value) + return filter_8tap_64p_116; + else if (ratio.value < dc_fixpt_from_fraction(5, 3).value) + return filter_8tap_64p_149; + else + return filter_8tap_64p_183; +} + +const uint16_t *get_filter_2tap_16p(void) +{ + return filter_2tap_16p; +} + +const uint16_t *get_filter_2tap_64p(void) +{ + return filter_2tap_64p; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_scl_filters_old.c b/drivers/gpu/drm/amd/display/dc/dce/dce_scl_filters_old.c new file mode 100644 index 000000000..bb0e1b80e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_scl_filters_old.c @@ -0,0 +1,25 @@ +/* + * 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 + * + */ + 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 000000000..f81082532 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c @@ -0,0 +1,1569 @@ +/* + * 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" +#include "hw_shared.h" + +#define DC_LOGGER \ + enc110->base.ctx->logger + +#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 dc_info_packet *info_packet) +{ + /* TODOFPGA Figure out a proper number for max_retries polling for lock + * use 50 for now. + */ + uint32_t max_retries = 50; + + /*we need turn on clock before programming AFMT block*/ + if (REG(AFMT_CNTL)) + REG_UPDATE(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, 1); + + 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, + 0, 10, max_retries);*/ + + /* check if HW reading GSP memory */ + REG_WAIT(AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_CONFLICT, + 0, 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 */ + { + 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, *content); + } + + 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)); + } + + if (REG(AFMT_VBI_PACKET_CONTROL1)) { + switch (packet_index) { + case 0: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC0_FRAME_UPDATE, 1); + break; + case 1: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC1_FRAME_UPDATE, 1); + break; + case 2: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC2_FRAME_UPDATE, 1); + break; + case 3: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC3_FRAME_UPDATE, 1); + break; + case 4: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC4_FRAME_UPDATE, 1); + break; + case 5: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC5_FRAME_UPDATE, 1); + break; + case 6: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC6_FRAME_UPDATE, 1); + break; + case 7: + REG_UPDATE(AFMT_VBI_PACKET_CONTROL1, + AFMT_GENERIC7_FRAME_UPDATE, 1); + break; + default: + break; + } + } +} + +static void dce110_update_hdmi_info_packet( + struct dce110_stream_encoder *enc110, + uint32_t packet_index, + const struct dc_info_packet *info_packet) +{ + 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; + case 4: + if (REG(HDMI_GENERIC_PACKET_CONTROL2)) + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL2, + HDMI_GENERIC0_CONT, cont, + HDMI_GENERIC0_SEND, send, + HDMI_GENERIC0_LINE, line); + break; + case 5: + if (REG(HDMI_GENERIC_PACKET_CONTROL2)) + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL2, + HDMI_GENERIC1_CONT, cont, + HDMI_GENERIC1_SEND, send, + HDMI_GENERIC1_LINE, line); + break; + case 6: + if (REG(HDMI_GENERIC_PACKET_CONTROL3)) + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL3, + HDMI_GENERIC0_CONT, cont, + HDMI_GENERIC0_SEND, send, + HDMI_GENERIC0_LINE, line); + break; + case 7: + if (REG(HDMI_GENERIC_PACKET_CONTROL3)) + REG_UPDATE_3(HDMI_GENERIC_PACKET_CONTROL3, + HDMI_GENERIC1_CONT, cont, + HDMI_GENERIC1_SEND, send, + HDMI_GENERIC1_LINE, line); + break; + default: + /* invalid HW packet index */ + DC_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, + bool use_vsc_sdp_for_colorimetry, + uint32_t enable_sdp_splitting) +{ + uint32_t h_active_start; + uint32_t v_active_start; + uint32_t misc0 = 0; + uint32_t misc1 = 0; + uint32_t h_blank; + uint32_t h_back_porch; + uint8_t synchronous_clock = 0; /* asynchronous mode */ + uint8_t colorimetry_bpc; + uint8_t dynamic_range_rgb = 0; /*full range*/ + uint8_t dynamic_range_ycbcr = 1; /*bt709*/ + + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct dc_crtc_timing hw_crtc_timing = *crtc_timing; + if (hw_crtc_timing.flags.INTERLACE) { + /*the input timing is in VESA spec format with Interlace flag =1*/ + hw_crtc_timing.v_total /= 2; + hw_crtc_timing.v_border_top /= 2; + hw_crtc_timing.v_addressable /= 2; + hw_crtc_timing.v_border_bottom /= 2; + hw_crtc_timing.v_front_porch /= 2; + hw_crtc_timing.v_sync_width /= 2; + } + /* set pixel encoding */ + switch (hw_crtc_timing.pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_TYPE_YCBCR422); + break; + case PIXEL_ENCODING_YCBCR444: + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_TYPE_YCBCR444); + + if (hw_crtc_timing.flags.Y_ONLY) + if (hw_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_TYPE_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_TYPE_YCBCR420); + if (enc110->se_mask->DP_VID_M_DOUBLE_VALUE_EN) + REG_UPDATE(DP_VID_TIMING, DP_VID_M_DOUBLE_VALUE_EN, 1); + + if (enc110->se_mask->DP_VID_N_MUL) + REG_UPDATE(DP_VID_TIMING, DP_VID_N_MUL, 1); + break; + default: + REG_UPDATE(DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, + DP_PIXEL_ENCODING_TYPE_RGB444); + break; + } + + if (REG(DP_MSA_MISC)) + misc1 = REG_READ(DP_MSA_MISC); + + /* set color depth */ + + switch (hw_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_PIXEL_DEPTH_8BPC); + break; + case COLOR_DEPTH_101010: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + DP_COMPONENT_PIXEL_DEPTH_10BPC); + + break; + case COLOR_DEPTH_121212: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + DP_COMPONENT_PIXEL_DEPTH_12BPC); + break; + default: + REG_UPDATE(DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, + DP_COMPONENT_PIXEL_DEPTH_6BPC); + break; + } + + /* set dynamic range and YCbCr range */ + + + switch (hw_crtc_timing.display_color_depth) { + case COLOR_DEPTH_666: + colorimetry_bpc = 0; + break; + case COLOR_DEPTH_888: + colorimetry_bpc = 1; + break; + case COLOR_DEPTH_101010: + colorimetry_bpc = 2; + break; + case COLOR_DEPTH_121212: + colorimetry_bpc = 3; + break; + default: + colorimetry_bpc = 0; + break; + } + + misc0 = misc0 | synchronous_clock; + misc0 = colorimetry_bpc << 5; + + if (REG(DP_MSA_TIMING_PARAM1)) { + switch (output_color_space) { + case COLOR_SPACE_SRGB: + misc0 = misc0 | 0x0; + misc1 = misc1 & ~0x80; /* bit7 = 0*/ + dynamic_range_rgb = 0; /*full range*/ + break; + case COLOR_SPACE_SRGB_LIMITED: + misc0 = misc0 | 0x8; /* bit3=1 */ + misc1 = misc1 & ~0x80; /* bit7 = 0*/ + dynamic_range_rgb = 1; /*limited range*/ + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR601_LIMITED: + misc0 = misc0 | 0x8; /* bit3=1, bit4=0 */ + misc1 = misc1 & ~0x80; /* bit7 = 0*/ + dynamic_range_ycbcr = 0; /*bt601*/ + if (hw_crtc_timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) + misc0 = misc0 | 0x2; /* bit2=0, bit1=1 */ + else if (hw_crtc_timing.pixel_encoding == PIXEL_ENCODING_YCBCR444) + misc0 = misc0 | 0x4; /* bit2=1, bit1=0 */ + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR709_LIMITED: + case COLOR_SPACE_YCBCR709_BLACK: + misc0 = misc0 | 0x18; /* bit3=1, bit4=1 */ + misc1 = misc1 & ~0x80; /* bit7 = 0*/ + dynamic_range_ycbcr = 1; /*bt709*/ + if (hw_crtc_timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) + misc0 = misc0 | 0x2; /* bit2=0, bit1=1 */ + else if (hw_crtc_timing.pixel_encoding == PIXEL_ENCODING_YCBCR444) + misc0 = misc0 | 0x4; /* bit2=1, bit1=0 */ + break; + case COLOR_SPACE_2020_RGB_LIMITEDRANGE: + dynamic_range_rgb = 1; /*limited range*/ + break; + case COLOR_SPACE_2020_RGB_FULLRANGE: + case COLOR_SPACE_2020_YCBCR: + case COLOR_SPACE_XR_RGB: + case COLOR_SPACE_MSREF_SCRGB: + case COLOR_SPACE_ADOBERGB: + case COLOR_SPACE_DCIP3: + case COLOR_SPACE_XV_YCC_709: + case COLOR_SPACE_XV_YCC_601: + case COLOR_SPACE_DISPLAYNATIVE: + case COLOR_SPACE_DOLBYVISION: + case COLOR_SPACE_APPCTRL: + case COLOR_SPACE_CUSTOMPOINTS: + case COLOR_SPACE_UNKNOWN: + /* do nothing */ + break; + } + if (enc110->se_mask->DP_DYN_RANGE && enc110->se_mask->DP_YCBCR_RANGE) + REG_UPDATE_2( + DP_PIXEL_FORMAT, + DP_DYN_RANGE, dynamic_range_rgb, + DP_YCBCR_RANGE, dynamic_range_ycbcr); + + if (REG(DP_MSA_COLORIMETRY)) + REG_SET(DP_MSA_COLORIMETRY, 0, DP_MSA_MISC0, misc0); + + if (REG(DP_MSA_MISC)) + REG_WRITE(DP_MSA_MISC, misc1); /* MSA_MISC1 */ + + /* dcn new register + * dc_crtc_timing is vesa dmt struct. data from edid + */ + if (REG(DP_MSA_TIMING_PARAM1)) + REG_SET_2(DP_MSA_TIMING_PARAM1, 0, + DP_MSA_HTOTAL, hw_crtc_timing.h_total, + DP_MSA_VTOTAL, hw_crtc_timing.v_total); + + /* calcuate from vesa timing parameters + * h_active_start related to leading edge of sync + */ + + h_blank = hw_crtc_timing.h_total - hw_crtc_timing.h_border_left - + hw_crtc_timing.h_addressable - hw_crtc_timing.h_border_right; + + h_back_porch = h_blank - hw_crtc_timing.h_front_porch - + hw_crtc_timing.h_sync_width; + + /* start at begining of left border */ + h_active_start = hw_crtc_timing.h_sync_width + h_back_porch; + + + v_active_start = hw_crtc_timing.v_total - hw_crtc_timing.v_border_top - + hw_crtc_timing.v_addressable - hw_crtc_timing.v_border_bottom - + hw_crtc_timing.v_front_porch; + + + /* start at begining of left border */ + if (REG(DP_MSA_TIMING_PARAM2)) + REG_SET_2(DP_MSA_TIMING_PARAM2, 0, + DP_MSA_HSTART, h_active_start, + DP_MSA_VSTART, v_active_start); + + if (REG(DP_MSA_TIMING_PARAM3)) + REG_SET_4(DP_MSA_TIMING_PARAM3, 0, + DP_MSA_HSYNCWIDTH, + hw_crtc_timing.h_sync_width, + DP_MSA_HSYNCPOLARITY, + !hw_crtc_timing.flags.HSYNC_POSITIVE_POLARITY, + DP_MSA_VSYNCWIDTH, + hw_crtc_timing.v_sync_width, + DP_MSA_VSYNCPOLARITY, + !hw_crtc_timing.flags.VSYNC_POSITIVE_POLARITY); + + /* HWDITH include border or overscan */ + if (REG(DP_MSA_TIMING_PARAM4)) + REG_SET_2(DP_MSA_TIMING_PARAM4, 0, + DP_MSA_HWIDTH, hw_crtc_timing.h_border_left + + hw_crtc_timing.h_addressable + hw_crtc_timing.h_border_right, + DP_MSA_VHEIGHT, hw_crtc_timing.v_border_top + + hw_crtc_timing.v_addressable + hw_crtc_timing.v_border_bottom); + } +} + +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; + cntl.color_depth = crtc_timing->display_color_depth; + + 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: + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 1, + HDMI_DEEP_COLOR_ENABLE, 0); + } else { + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 1, + HDMI_DEEP_COLOR_ENABLE, 1); + } + break; + case COLOR_DEPTH_121212: + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE_2(HDMI_CONTROL, + HDMI_DEEP_COLOR_DEPTH, 2, + HDMI_DEEP_COLOR_ENABLE, 0); + } else { + 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); + + REG_UPDATE(HDMI_VBI_PACKET_CONTROL, HDMI_ACP_SEND, 0); + + /* 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_100hz / 10; + 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); +} + +/* setup stream encoder in LVDS mode */ +static void dce110_stream_encoder_lvds_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing) +{ + 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_LVDS; + cntl.enable_dp_audio = false; + cntl.pixel_clock = crtc_timing->pix_clk_100hz / 10; + cntl.lanes_number = 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); +} + +static void dce110_stream_encoder_set_throttled_vcp_size( + struct stream_encoder *enc, + struct fixed31_32 avg_time_slots_per_mtp) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t x = dc_fixpt_floor( + avg_time_slots_per_mtp); + uint32_t y = dc_fixpt_ceil( + dc_fixpt_shl( + dc_fixpt_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]; + /*we need turn on clock before programming AFMT block*/ + if (REG(AFMT_CNTL)) + REG_UPDATE(AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, 1); + + 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); + dce110_update_hdmi_info_packet(enc110, 3, &info_frame->hdrsmd); + } + + if (enc110->se_mask->HDMI_DB_DISABLE) { + /* for bring up, disable dp double TODO */ + if (REG(HDMI_DB_CONTROL)) + REG_UPDATE(HDMI_DB_CONTROL, HDMI_DB_DISABLE, 1); + + dce110_update_hdmi_info_packet(enc110, 0, &info_frame->avi); + dce110_update_hdmi_info_packet(enc110, 1, &info_frame->vendor); + dce110_update_hdmi_info_packet(enc110, 2, &info_frame->gamut); + dce110_update_hdmi_info_packet(enc110, 3, &info_frame->spd); + dce110_update_hdmi_info_packet(enc110, 4, &info_frame->hdrsmd); + } +} + +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); + + /* stop generic packets 2 & 3 on HDMI */ + if (REG(HDMI_GENERIC_PACKET_CONTROL2)) + REG_SET_6(HDMI_GENERIC_PACKET_CONTROL2, 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); + + if (REG(HDMI_GENERIC_PACKET_CONTROL3)) + REG_SET_6(HDMI_GENERIC_PACKET_CONTROL3, 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 = 0; + + if (info_frame->vsc.valid) + dce110_update_generic_info_packet( + enc110, + 0, /* packetIndex */ + &info_frame->vsc); + + if (info_frame->spd.valid) + dce110_update_generic_info_packet( + enc110, + 2, /* packetIndex */ + &info_frame->spd); + + if (info_frame->hdrsmd.valid) + dce110_update_generic_info_packet( + enc110, + 3, /* packetIndex */ + &info_frame->hdrsmd); + + /* 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); + REG_UPDATE(DP_SEC_CNTL, DP_SEC_GSP2_ENABLE, info_frame->spd.valid); + REG_UPDATE(DP_SEC_CNTL, DP_SEC_GSP3_ENABLE, info_frame->hdrsmd.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 + */ + value = REG_READ(DP_SEC_CNTL); + 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 = 0; + + 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 */ + value = REG_READ(DP_SEC_CNTL); + if (value) + REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1); + +} + +static void dce110_stream_encoder_dp_blank( + struct dc_link *link, + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + uint32_t reg1 = 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. */ + REG_GET(DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, ®1); + if ((reg1 & 0x1) == 0) + /*stream not enabled*/ + return; + /* 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, + 0, + 10, 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 dc_link *link, + 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->timing.pix_clk_100hz / 10; + 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); +} + +static void dce110_stream_encoder_set_avmute( + struct stream_encoder *enc, + bool enable) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + unsigned int value = enable ? 1 : 0; + + REG_UPDATE(HDMI_GC, HDMI_GC_AVMUTE, value); +} + + +static void dce110_reset_hdmi_stream_attribute( + struct stream_encoder *enc) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + if (enc110->se_mask->HDMI_DATA_SCRAMBLE_EN) + 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); + else + REG_UPDATE_3(HDMI_CONTROL, + HDMI_PACKET_GEN_VERSION, 1, + HDMI_KEEPOUT_MODE, 1, + HDMI_DEEP_COLOR_ENABLE, 0); +} + +#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" + + +/* 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[16] = { + {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}, + {29670, 5824, 421875, 4459, 234375, 5824, 281250}, + {29700, 3072, 222750, 4704, 247500, 5120, 247500}, + {59340, 5824, 843750, 8918, 937500, 5824, 562500}, + {59400, 3072, 445500, 9408, 990000, 6144, 594000} +}; + +static const struct audio_clock_info audio_clock_info_table_36bpc[14] = { + {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}, + {29670, 5824, 632812, 8918, 703125, 5824, 421875}, + {29700, 4096, 445500, 4704, 371250, 5120, 371250} +}; + +static const struct audio_clock_info audio_clock_info_table_48bpc[14] = { + {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}, + {29670, 5824, 843750, 4459, 468750, 5824, 562500}, + {29700, 3072, 445500, 4704, 495000, 5120, 495000} + + +}; + +static 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; +} + +static 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; +} + +static void get_audio_clock_info( + enum dc_color_depth color_depth, + uint32_t crtc_pixel_clock_100Hz, + uint32_t actual_pixel_clock_100Hz, + 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_100Hz / 100; + uint32_t audio_array_size; + + 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; + } + } + } + + /* not found */ + if (actual_pixel_clock_100Hz == 0) + actual_pixel_clock_100Hz = crtc_pixel_clock_100Hz; + + /* See HDMI spec the table entry under + * pixel clock of "Other". */ + audio_clock_info->pixel_clock_in_10khz = + actual_pixel_clock_100Hz / 100; + audio_clock_info->cts_32khz = actual_pixel_clock_100Hz / 10; + audio_clock_info->cts_44khz = actual_pixel_clock_100Hz / 10; + audio_clock_info->cts_48khz = actual_pixel_clock_100Hz / 10; + + audio_clock_info->n_32khz = 4096; + audio_clock_info->n_44khz = 6272; + audio_clock_info->n_48khz = 6144; +} + +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 channels = 0; + + ASSERT(audio_info); + if (audio_info == NULL) + /* This should not happen.it does so we don't get BSOD*/ + return; + + 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 */ + get_audio_clock_info(crtc_info->color_depth, + crtc_info->requested_pixel_clock_100Hz, + crtc_info->calculated_pixel_clock_100Hz, + &audio_clock_info); + DC_LOG_HW_AUDIO( + "\n%s:Input::requested_pixel_clock_100Hz = %d" \ + "calculated_pixel_clock_100Hz = %d \n", __func__, \ + crtc_info->requested_pixel_clock_100Hz, \ + crtc_info->calculated_pixel_clock_100Hz); + + /* 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 */ + + /* 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 = 0; + + /* 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 */ + value = REG_READ(DP_SEC_CNTL); + 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 void setup_stereo_sync( + struct stream_encoder *enc, + int tg_inst, bool enable) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + REG_UPDATE(DIG_FE_CNTL, DIG_STEREOSYNC_SELECT, tg_inst); + REG_UPDATE(DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, !enable); +} + +static void dig_connect_to_otg( + struct stream_encoder *enc, + int tg_inst) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + REG_UPDATE(DIG_FE_CNTL, DIG_SOURCE_SELECT, tg_inst); +} + +static unsigned int dig_source_otg( + struct stream_encoder *enc) +{ + uint32_t tg_inst = 0; + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + + REG_GET(DIG_FE_CNTL, DIG_SOURCE_SELECT, &tg_inst); + + return tg_inst; +} + +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, + .lvds_set_stream_attribute = + dce110_stream_encoder_lvds_set_stream_attribute, + .set_throttled_vcp_size = + dce110_stream_encoder_set_throttled_vcp_size, + .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, + .setup_stereo_sync = setup_stereo_sync, + .set_avmute = dce110_stream_encoder_set_avmute, + .dig_connect_to_otg = dig_connect_to_otg, + .hdmi_reset_stream_attribute = dce110_reset_hdmi_stream_attribute, + .dig_source_otg = dig_source_otg, +}; + +void 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) +{ + 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; +} 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 000000000..cc5020a8e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h @@ -0,0 +1,735 @@ +/* + * 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_DCN_REG_LIST(id)\ + SE_COMMON_REG_LIST_BASE(id),\ + SRI(AFMT_CNTL, DIG, id),\ + SRI(AFMT_VBI_PACKET_CONTROL1, DIG, id),\ + SRI(HDMI_GENERIC_PACKET_CONTROL2, DIG, id), \ + SRI(HDMI_GENERIC_PACKET_CONTROL3, DIG, id), \ + SRI(DP_DB_CNTL, DP, id), \ + SRI(DP_MSA_MISC, DP, id), \ + SRI(DP_MSA_COLORIMETRY, DP, id), \ + SRI(DP_MSA_TIMING_PARAM1, DP, id), \ + SRI(DP_MSA_TIMING_PARAM2, DP, id), \ + SRI(DP_MSA_TIMING_PARAM3, DP, id), \ + SRI(DP_MSA_TIMING_PARAM4, DP, id), \ + SRI(HDMI_DB_CONTROL, 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(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_VBI_PACKET_CONTROL, HDMI_ACP_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(DIG_FE_CNTL, DIG_STEREOSYNC_SELECT, mask_sh),\ + SE_SF(DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, 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),\ + SE_SF(DIG_FE_CNTL, DIG_SOURCE_SELECT, mask_sh) + +#define SE_COMMON_MASK_SH_LIST_SOC(mask_sh)\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_INDEX, mask_sh),\ + SE_SF(DIG0_AFMT_GENERIC_HDR, AFMT_GENERIC_HB0, mask_sh),\ + SE_SF(DIG0_AFMT_GENERIC_HDR, AFMT_GENERIC_HB1, mask_sh),\ + SE_SF(DIG0_AFMT_GENERIC_HDR, AFMT_GENERIC_HB2, mask_sh),\ + SE_SF(DIG0_AFMT_GENERIC_HDR, AFMT_GENERIC_HB3, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_LINE, mask_sh),\ + SE_SF(DP0_DP_PIXEL_FORMAT, DP_PIXEL_ENCODING, mask_sh),\ + SE_SF(DP0_DP_PIXEL_FORMAT, DP_COMPONENT_DEPTH, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_PACKET_GEN_VERSION, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_KEEPOUT_MODE, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_DEEP_COLOR_ENABLE, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_DEEP_COLOR_DEPTH, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_DATA_SCRAMBLE_EN, mask_sh),\ + SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_GC_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_GC_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_NULL_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_ACP_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_SEND, mask_sh),\ + SE_SF(DIG0_AFMT_INFOFRAME_CONTROL0, AFMT_AUDIO_INFO_UPDATE, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL1, HDMI_AUDIO_INFO_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_GC, HDMI_GC_AVMUTE, mask_sh),\ + SE_SF(DP0_DP_MSE_RATE_CNTL, DP_MSE_RATE_X, mask_sh),\ + SE_SF(DP0_DP_MSE_RATE_CNTL, DP_MSE_RATE_Y, mask_sh),\ + SE_SF(DP0_DP_MSE_RATE_UPDATE, DP_MSE_RATE_UPDATE_PENDING, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP0_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP1_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP2_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP3_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_MPG_ENABLE, mask_sh),\ + SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_DIS_DEFER, mask_sh),\ + SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, mask_sh),\ + SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_STATUS, mask_sh),\ + SE_SF(DP0_DP_STEER_FIFO, DP_STEER_FIFO_RESET, mask_sh),\ + SE_SF(DP0_DP_VID_TIMING, DP_VID_M_N_GEN_EN, mask_sh),\ + SE_SF(DP0_DP_VID_N, DP_VID_N, mask_sh),\ + SE_SF(DP0_DP_VID_M, DP_VID_M, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, DIG_START, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_SRC_CONTROL, AFMT_AUDIO_SRC_SELECT, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_PACKET_CONTROL2, AFMT_AUDIO_CHANNEL_ENABLE, mask_sh),\ + SE_SF(DIG0_HDMI_AUDIO_PACKET_CONTROL, HDMI_AUDIO_PACKETS_PER_LINE, mask_sh),\ + SE_SF(DIG0_HDMI_AUDIO_PACKET_CONTROL, HDMI_AUDIO_DELAY_EN, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_PACKET_CONTROL, AFMT_60958_CS_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_PACKET_CONTROL2, AFMT_AUDIO_LAYOUT_OVRD, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_PACKET_CONTROL2, AFMT_60958_OSF_OVRD, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_PACKET_CONTROL, HDMI_ACR_AUTO_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_PACKET_CONTROL, HDMI_ACR_SOURCE, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_PACKET_CONTROL, HDMI_ACR_AUDIO_PRIORITY, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_32_0, HDMI_ACR_CTS_32, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_32_1, HDMI_ACR_N_32, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_44_0, HDMI_ACR_CTS_44, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_44_1, HDMI_ACR_N_44, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_48_0, HDMI_ACR_CTS_48, mask_sh),\ + SE_SF(DIG0_HDMI_ACR_48_1, HDMI_ACR_N_48, mask_sh),\ + SE_SF(DIG0_AFMT_60958_0, AFMT_60958_CS_CHANNEL_NUMBER_L, mask_sh),\ + SE_SF(DIG0_AFMT_60958_0, AFMT_60958_CS_CLOCK_ACCURACY, mask_sh),\ + SE_SF(DIG0_AFMT_60958_1, AFMT_60958_CS_CHANNEL_NUMBER_R, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_2, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_3, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_4, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_5, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_6, mask_sh),\ + SE_SF(DIG0_AFMT_60958_2, AFMT_60958_CS_CHANNEL_NUMBER_7, mask_sh),\ + SE_SF(DP0_DP_SEC_AUD_N, DP_SEC_AUD_N, mask_sh),\ + SE_SF(DP0_DP_SEC_TIMESTAMP, DP_SEC_TIMESTAMP_MODE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ASP_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ATP_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_AIP_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ACM_ENABLE, mask_sh),\ + SE_SF(DIG0_AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND, mask_sh),\ + SE_SF(DIG0_AFMT_CNTL, AFMT_AUDIO_CLOCK_EN, mask_sh),\ + SE_SF(DIG0_HDMI_CONTROL, HDMI_CLOCK_CHANNEL_RATE, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, TMDS_PIXEL_ENCODING, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, TMDS_COLOR_FORMAT, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, DIG_STEREOSYNC_SELECT, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, mask_sh),\ + SE_SF(DIG0_DIG_FE_CNTL, DIG_SOURCE_SELECT, 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),\ + SE_SF(DIG_FE_CNTL, DIG_STEREOSYNC_SELECT, mask_sh),\ + SE_SF(DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, 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) + +#define SE_COMMON_MASK_SH_LIST_DCE120(mask_sh)\ + SE_COMMON_MASK_SH_LIST_SOC(mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC0_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC2_UPDATE, mask_sh),\ + SE_SF(DP0_DP_PIXEL_FORMAT, DP_DYN_RANGE, mask_sh),\ + SE_SF(DP0_DP_PIXEL_FORMAT, DP_YCBCR_RANGE, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL0, HDMI_AVI_INFO_SEND, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL0, HDMI_AVI_INFO_CONT, mask_sh),\ + SE_SF(DIG0_HDMI_INFOFRAME_CONTROL1, HDMI_AVI_INFO_LINE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_AVI_ENABLE, mask_sh),\ + SE_SF(DIG0_AFMT_AVI_INFO3, AFMT_AVI_INFO_VERSION, mask_sh),\ + SE_SF(DP0_DP_VID_TIMING, DP_VID_M_DOUBLE_VALUE_EN, mask_sh) + +#define SE_COMMON_MASK_SH_LIST_DCN10(mask_sh)\ + SE_COMMON_MASK_SH_LIST_SOC(mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_LOCK_STATUS, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_CONFLICT, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL, AFMT_GENERIC_CONFLICT_CLR, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC0_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC1_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC2_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC3_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC4_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC5_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC6_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC7_FRAME_UPDATE_PENDING, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC0_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC1_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC2_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC3_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC4_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC5_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC6_FRAME_UPDATE, mask_sh),\ + SE_SF(DIG0_AFMT_VBI_PACKET_CONTROL1, AFMT_GENERIC7_FRAME_UPDATE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP4_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP5_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP6_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP7_ENABLE, mask_sh),\ + SE_SF(DP0_DP_DB_CNTL, DP_DB_DISABLE, mask_sh),\ + SE_SF(DP0_DP_MSA_COLORIMETRY, DP_MSA_MISC0, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM1, DP_MSA_HTOTAL, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM1, DP_MSA_VTOTAL, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM2, DP_MSA_HSTART, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM2, DP_MSA_VSTART, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_HSYNCWIDTH, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_HSYNCPOLARITY, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_VSYNCWIDTH, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_VSYNCPOLARITY, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM4, DP_MSA_HWIDTH, mask_sh),\ + SE_SF(DP0_DP_MSA_TIMING_PARAM4, DP_MSA_VHEIGHT, mask_sh),\ + SE_SF(DIG0_HDMI_DB_CONTROL, HDMI_DB_DISABLE, mask_sh),\ + SE_SF(DP0_DP_VID_TIMING, DP_VID_N_MUL, 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_ACP_SEND; + 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 DIG_STEREOSYNC_SELECT; + uint8_t DIG_STEREOSYNC_GATE_EN; + 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; + uint8_t DIG_SOURCE_SELECT; +}; + +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_ACP_SEND; + 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 DIG_STEREOSYNC_SELECT; + uint32_t DIG_STEREOSYNC_GATE_EN; + 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; + uint32_t DIG_SOURCE_SELECT; +}; + +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; + uint32_t DP_DB_CNTL; + uint32_t DP_MSA_MISC; + uint32_t DP_MSA_COLORIMETRY; + uint32_t DP_MSA_TIMING_PARAM1; + uint32_t DP_MSA_TIMING_PARAM2; + uint32_t DP_MSA_TIMING_PARAM3; + uint32_t DP_MSA_TIMING_PARAM4; + uint32_t HDMI_DB_CONTROL; +}; + +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; +}; + +void 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 000000000..d9fd4ec60 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -0,0 +1,1685 @@ +/* + * 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" +#include "dc.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 DC_LOGGER \ + xfm_dce->base.ctx->logger + +#define IDENTITY_RATIO(ratio) (dc_fixpt_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 +}; + +enum csc_color_mode { + /* 00 - BITS2:0 Bypass */ + CSC_COLOR_MODE_GRAPHICS_BYPASS, + /* 01 - hard coded coefficient TV RGB */ + CSC_COLOR_MODE_GRAPHICS_PREDEFINED, + /* 04 - programmable OUTPUT CSC coefficient */ + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC, +}; + +enum grph_color_adjust_option { + GRPH_COLOR_MATRIX_HW_DEFAULT = 1, + GRPH_COLOR_MATRIX_SW +}; + +static const struct out_csc_color_matrix global_color_matrix[] = { +{ COLOR_SPACE_SRGB, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_SRGB_LIMITED, + { 0x1B60, 0, 0, 0x200, 0, 0x1B60, 0, 0x200, 0, 0, 0x1B60, 0x200} }, +{ COLOR_SPACE_YCBCR601, + { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x82F, 0x1012, 0x31F, 0x200, 0xFB47, + 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x5D2, 0x1394, 0x1FA, + 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +/* TODO: correct values below */ +{ COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, + 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } +}; + +static bool setup_scaling_configuration( + struct dce_transform *xfm_dce, + const struct scaler_data *data) +{ + REG_SET(SCL_BYPASS_CONTROL, 0, SCL_BYPASS_MODE, 0); + + if (data->taps.h_taps + data->taps.v_taps <= 2) { + /* Set bypass */ + if (xfm_dce->xfm_mask->SCL_PSCL_EN != 0) + REG_UPDATE_2(SCL_MODE, SCL_MODE, 0, SCL_PSCL_EN, 0); + else + REG_UPDATE(SCL_MODE, SCL_MODE, 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(SCL_MODE, SCL_MODE, 1); + else + REG_UPDATE(SCL_MODE, SCL_MODE, 2); + + if (xfm_dce->xfm_mask->SCL_PSCL_EN != 0) + REG_UPDATE(SCL_MODE, SCL_PSCL_EN, 1); + + /* 1 - Replace out of bound pixels with edge */ + REG_SET(SCL_CONTROL, 0, SCL_BOUNDARY_MODE, 1); + + return true; +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static bool dce60_setup_scaling_configuration( + struct dce_transform *xfm_dce, + const struct scaler_data *data) +{ + REG_SET(SCL_BYPASS_CONTROL, 0, SCL_BYPASS_MODE, 0); + + if (data->taps.h_taps + data->taps.v_taps <= 2) { + /* Set bypass */ + + /* DCE6 has no SCL_MODE register, skip scale mode programming */ + + 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); + + /* DCE6 has no SCL_MODE register, skip scale mode programming */ + + /* DCE6 has no SCL_BOUNDARY_MODE bit, skip replace out of bound pixels */ + + return true; +} +#endif + +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 (xfm_dce->base.ctx->dc->debug.visual_confirm != VISUAL_CONFIRM_DISABLE) { + overscan_bottom += 2; + overscan_right += 2; + } + + 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 = + dc_fixpt_u2d19(data->ratios.horz) << 5; + inits->v_int_scale_ratio = + dc_fixpt_u2d19(data->ratios.vert) << 5; + + h_init = + dc_fixpt_div_int( + dc_fixpt_add( + data->ratios.horz, + dc_fixpt_from_int(data->taps.h_taps + 1)), + 2); + inits->h_init.integer = dc_fixpt_floor(h_init); + inits->h_init.fraction = dc_fixpt_u0d19(h_init) << 5; + + v_init = + dc_fixpt_div_int( + dc_fixpt_add( + data->ratios.vert, + dc_fixpt_from_int(data->taps.v_taps + 1)), + 2); + inits->v_init.integer = dc_fixpt_floor(v_init); + inits->v_init.fraction = dc_fixpt_u0d19(v_init) << 5; +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_calculate_inits( + struct dce_transform *xfm_dce, + const struct scaler_data *data, + struct sclh_ratios_inits *inits) +{ + struct fixed31_32 v_init; + + inits->h_int_scale_ratio = + dc_fixpt_u2d19(data->ratios.horz) << 5; + inits->v_int_scale_ratio = + dc_fixpt_u2d19(data->ratios.vert) << 5; + + /* DCE6 h_init_luma setting inspired by DCE110 */ + inits->h_init_luma.integer = 1; + + /* DCE6 h_init_chroma setting inspired by DCE110 */ + inits->h_init_chroma.integer = 1; + + v_init = + dc_fixpt_div_int( + dc_fixpt_add( + data->ratios.vert, + dc_fixpt_from_int(data->taps.v_taps + 1)), + 2); + inits->v_init.integer = dc_fixpt_floor(v_init); + inits->v_init.fraction = dc_fixpt_u0d19(v_init) << 5; +} +#endif + +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); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_program_scl_ratios_inits( + struct dce_transform *xfm_dce, + struct sclh_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); + + /* DCE6 has SCL_HORZ_FILTER_INIT_RGB_LUMA register */ + REG_SET_2(SCL_HORZ_FILTER_INIT_RGB_LUMA, 0, + SCL_H_INIT_INT_RGB_Y, inits->h_init_luma.integer, + SCL_H_INIT_FRAC_RGB_Y, inits->h_init_luma.fraction); + + /* DCE6 has SCL_HORZ_FILTER_INIT_CHROMA register */ + REG_SET_2(SCL_HORZ_FILTER_INIT_CHROMA, 0, + SCL_H_INIT_INT_CBCR, inits->h_init_chroma.integer, + SCL_H_INIT_FRAC_CBCR, inits->h_init_chroma.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); +} +#endif + +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 get_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); + + /* Clear SCL_F_SHARP_CONTROL value to 0 */ + REG_WRITE(SCL_F_SHARP_CONTROL, 0); + + /* 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); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_transform_set_scaler( + struct transform *xfm, + const struct scaler_data *data) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + bool is_scaling_required; + const uint16_t *coeffs_v, *coeffs_h; + + /*Use whole line buffer memory always*/ + REG_SET(DC_LB_MEMORY_SPLIT, 0, + DC_LB_MEMORY_CONFIG, 0); + + REG_SET(DC_LB_MEM_SIZE, 0, + DC_LB_MEM_SIZE, xfm_dce->lb_memory_size); + + /* Clear SCL_F_SHARP_CONTROL value to 0 */ + REG_WRITE(SCL_F_SHARP_CONTROL, 0); + + /* 1. Program overscan */ + program_overscan(xfm_dce, data); + + /* 2. Program taps and configuration */ + is_scaling_required = dce60_setup_scaling_configuration(xfm_dce, data); + + if (is_scaling_required) { + /* 3. Calculate and program ratio, DCE6 filter initialization */ + struct sclh_ratios_inits inits = { 0 }; + + /* DCE6 has specific calculate_inits() function */ + dce60_calculate_inits(xfm_dce, data, &inits); + + /* DCE6 has specific program_scl_ratios_inits() function */ + dce60_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; + } + } + + /* 6. Program the viewport */ + program_viewport(xfm_dce, &data->viewport); + + /* DCE6 has no SCL_COEF_UPDATE_COMPLETE bit to flip to new coefficient memory */ + + /* DCE6 DATA_FORMAT register does not support ALPHA_EN */ +} +#endif + +/***************************************************************************** + * 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 0000' */ + clamp_max = 0x3FF0; + break; + case COLOR_DEPTH_121212: + /* 12bit MSB aligned on 14 bit bus '11 1111 1111 1100' */ + clamp_max = 0x3FFC; + 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_out_trunc_round_depth trunc_round_depth; + enum dcp_out_trunc_round_mode trunc_mode; + bool spatial_dither_enable; + + ASSERT(depth <= COLOR_DEPTH_121212); /* Invalid clamp bit depth */ + + spatial_dither_enable = bit_depth_params->flags.SPATIAL_DITHER_ENABLED; + /* Default to 12 bit truncation without rounding */ + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_12BIT; + trunc_mode = DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE; + + if (bit_depth_params->flags.TRUNCATE_ENABLED) { + /* Don't enable dithering if truncation is enabled */ + spatial_dither_enable = false; + trunc_mode = bit_depth_params->flags.TRUNCATE_MODE ? + DCP_OUT_TRUNC_ROUND_MODE_ROUND : + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE; + + if (bit_depth_params->flags.TRUNCATE_DEPTH == 0 || + bit_depth_params->flags.TRUNCATE_DEPTH == 1) + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_8BIT; + else if (bit_depth_params->flags.TRUNCATE_DEPTH == 2) + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_10BIT; + else { + /* + * Invalid truncate/round depth. Setting here to 12bit + * to prevent use-before-initialize errors. + */ + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_12BIT; + BREAK_TO_DEBUGGER(); + } + } + + set_clamp(xfm_dce, depth); + set_round(xfm_dce, trunc_mode, trunc_round_depth); + set_dither(xfm_dce, + spatial_dither_enable, + DCP_SPATIAL_DITHER_MODE_A_AA_A, + DCP_SPATIAL_DITHER_DEPTH_30BPP, + bit_depth_params->flags.FRAME_RANDOM, + bit_depth_params->flags.RGB_RANDOM, + bit_depth_params->flags.HIGHPASS_RANDOM); +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +/***************************************************************************** + * dce60_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 dce60_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_out_trunc_round_depth trunc_round_depth; + enum dcp_out_trunc_round_mode trunc_mode; + bool spatial_dither_enable; + + ASSERT(depth <= COLOR_DEPTH_121212); /* Invalid clamp bit depth */ + + spatial_dither_enable = bit_depth_params->flags.SPATIAL_DITHER_ENABLED; + /* Default to 12 bit truncation without rounding */ + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_12BIT; + trunc_mode = DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE; + + if (bit_depth_params->flags.TRUNCATE_ENABLED) { + /* Don't enable dithering if truncation is enabled */ + spatial_dither_enable = false; + trunc_mode = bit_depth_params->flags.TRUNCATE_MODE ? + DCP_OUT_TRUNC_ROUND_MODE_ROUND : + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE; + + if (bit_depth_params->flags.TRUNCATE_DEPTH == 0 || + bit_depth_params->flags.TRUNCATE_DEPTH == 1) + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_8BIT; + else if (bit_depth_params->flags.TRUNCATE_DEPTH == 2) + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_10BIT; + else { + /* + * Invalid truncate/round depth. Setting here to 12bit + * to prevent use-before-initialize errors. + */ + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_12BIT; + BREAK_TO_DEBUGGER(); + } + } + + /* DCE6 has no OUT_CLAMP_CONTROL_* registers - set_clamp() is skipped */ + set_round(xfm_dce, trunc_mode, trunc_round_depth); + set_dither(xfm_dce, + spatial_dither_enable, + DCP_SPATIAL_DITHER_MODE_A_AA_A, + DCP_SPATIAL_DITHER_DEPTH_30BPP, + bit_depth_params->flags.FRAME_RANDOM, + bit_depth_params->flags.RGB_RANDOM, + bit_depth_params->flags.HIGHPASS_RANDOM); +} +#endif + +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: + DC_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*/ + DC_LOG_WARNING("%s: Capability not supported", + __func__); + } +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +static void dce60_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); + enum dc_color_depth color_depth; + + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + color_depth = COLOR_DEPTH_666; + break; + case LB_PIXEL_DEPTH_24BPP: + color_depth = COLOR_DEPTH_888; + break; + case LB_PIXEL_DEPTH_30BPP: + color_depth = COLOR_DEPTH_101010; + break; + case LB_PIXEL_DEPTH_36BPP: + color_depth = COLOR_DEPTH_121212; + break; + default: + color_depth = COLOR_DEPTH_101010; + BREAK_TO_DEBUGGER(); + break; + } + + set_denormalization(xfm_dce, color_depth); + dce60_program_bit_depth_reduction(xfm_dce, color_depth, bit_depth_params); + + /* DATA_FORMAT in DCE6 does not have PIXEL_DEPTH and PIXEL_EXPAN_MODE masks */ + + if (!(xfm_dce->lb_pixel_depth_supported & depth)) { + /*we should use unsupported capabilities + * unless it is required by w/a*/ + DC_LOG_WARNING("%s: Capability not supported", + __func__); + } +} +#endif + +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); + int i = 0; + + 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]; + + for (i = 0; i < GAMUT_MATRIX_SIZE; i++) + arr_matrix[i] = adjust->temperature_matrix[i]; + + 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 void program_color_matrix( + struct dce_transform *xfm_dce, + const struct out_csc_color_matrix *tbl_entry, + enum grph_color_adjust_option options) +{ + { + REG_SET_2(OUTPUT_CSC_C11_C12, 0, + OUTPUT_CSC_C11, tbl_entry->regval[0], + OUTPUT_CSC_C12, tbl_entry->regval[1]); + } + { + REG_SET_2(OUTPUT_CSC_C13_C14, 0, + OUTPUT_CSC_C11, tbl_entry->regval[2], + OUTPUT_CSC_C12, tbl_entry->regval[3]); + } + { + REG_SET_2(OUTPUT_CSC_C21_C22, 0, + OUTPUT_CSC_C11, tbl_entry->regval[4], + OUTPUT_CSC_C12, tbl_entry->regval[5]); + } + { + REG_SET_2(OUTPUT_CSC_C23_C24, 0, + OUTPUT_CSC_C11, tbl_entry->regval[6], + OUTPUT_CSC_C12, tbl_entry->regval[7]); + } + { + REG_SET_2(OUTPUT_CSC_C31_C32, 0, + OUTPUT_CSC_C11, tbl_entry->regval[8], + OUTPUT_CSC_C12, tbl_entry->regval[9]); + } + { + REG_SET_2(OUTPUT_CSC_C33_C34, 0, + OUTPUT_CSC_C11, tbl_entry->regval[10], + OUTPUT_CSC_C12, tbl_entry->regval[11]); + } +} + +static bool configure_graphics_mode( + struct dce_transform *xfm_dce, + enum csc_color_mode config, + enum graphics_csc_adjust_type csc_adjust_type, + enum dc_color_space color_space) +{ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 0); + + if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_SW) { + if (config == CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC) { + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 4); + } else { + + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 0); + break; + case COLOR_SPACE_SRGB_LIMITED: + /* TV RGB */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 1); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 2); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 3); + break; + default: + return false; + } + } + } else if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_HW) { + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 0); + break; + case COLOR_SPACE_SRGB_LIMITED: + /* TV RGB */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 1); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 2); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 3); + break; + default: + return false; + } + + } else + /* by pass */ + REG_SET(OUTPUT_CSC_CONTROL, 0, + OUTPUT_CSC_GRPH_MODE, 0); + + return true; +} + +void dce110_opp_set_csc_adjustment( + struct transform *xfm, + const struct out_csc_color_matrix *tbl_entry) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + + program_color_matrix( + xfm_dce, tbl_entry, GRPH_COLOR_MATRIX_SW); + + /* We did everything ,now program DxOUTPUT_CSC_CONTROL */ + configure_graphics_mode(xfm_dce, config, GRAPHICS_CSC_ADJUST_TYPE_SW, + tbl_entry->color_space); +} + +void dce110_opp_set_csc_default( + struct transform *xfm, + const struct default_adjustment *default_adjust) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_PREDEFINED; + + if (default_adjust->force_hw_default == false) { + const struct out_csc_color_matrix *elm; + /* currently parameter not in use */ + enum grph_color_adjust_option option = + GRPH_COLOR_MATRIX_HW_DEFAULT; + uint32_t i; + /* + * HW default false we program locally defined matrix + * HW default true we use predefined hw matrix and we + * do not need to program matrix + * OEM wants the HW default via runtime parameter. + */ + option = GRPH_COLOR_MATRIX_SW; + + for (i = 0; i < ARRAY_SIZE(global_color_matrix); ++i) { + elm = &global_color_matrix[i]; + if (elm->color_space != default_adjust->out_color_space) + continue; + /* program the matrix with default values from this + * file */ + program_color_matrix(xfm_dce, elm, option); + config = CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + break; + } + } + + /* configure the what we programmed : + * 1. Default values from this file + * 2. Use hardware default from ROM_A and we do not need to program + * matrix */ + + configure_graphics_mode(xfm_dce, config, + default_adjust->csc_adjust_type, + default_adjust->out_color_space); +} + +static void program_pwl(struct dce_transform *xfm_dce, + const struct pwl_params *params) +{ + int retval; + uint8_t max_tries = 10; + uint8_t counter = 0; + uint32_t i = 0; + const struct pwl_result_data *rgb = params->rgb_resulted; + + /* Power on LUT memory */ + if (REG(DCFE_MEM_PWR_CTRL)) + REG_UPDATE(DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS, 1); + else + REG_UPDATE(DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_LIGHT_SLEEP_DIS, 1); + + while (counter < max_tries) { + if (REG(DCFE_MEM_PWR_STATUS)) { + REG_GET(DCFE_MEM_PWR_STATUS, + DCP_REGAMMA_MEM_PWR_STATE, + &retval); + + if (retval == 0) + break; + ++counter; + } else { + REG_GET(DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_MEM_PWR_STATE, + &retval); + + if (retval == 0) + break; + ++counter; + } + } + + if (counter == max_tries) { + DC_LOG_WARNING("%s: regamma lut was not powered on " + "in a timely manner," + " programming still proceeds\n", + __func__); + } + + REG_UPDATE(REGAMMA_LUT_WRITE_EN_MASK, + REGAMMA_LUT_WRITE_EN_MASK, 7); + + REG_WRITE(REGAMMA_LUT_INDEX, 0); + + /* Program REGAMMA_LUT_DATA */ + while (i != params->hw_points_num) { + + REG_WRITE(REGAMMA_LUT_DATA, rgb->red_reg); + REG_WRITE(REGAMMA_LUT_DATA, rgb->green_reg); + REG_WRITE(REGAMMA_LUT_DATA, rgb->blue_reg); + REG_WRITE(REGAMMA_LUT_DATA, rgb->delta_red_reg); + REG_WRITE(REGAMMA_LUT_DATA, rgb->delta_green_reg); + REG_WRITE(REGAMMA_LUT_DATA, rgb->delta_blue_reg); + + ++rgb; + ++i; + } + + /* we are done with DCP LUT memory; re-enable low power mode */ + if (REG(DCFE_MEM_PWR_CTRL)) + REG_UPDATE(DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS, 0); + else + REG_UPDATE(DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_LIGHT_SLEEP_DIS, 0); +} + +static void regamma_config_regions_and_segments(struct dce_transform *xfm_dce, + const struct pwl_params *params) +{ + const struct gamma_curve *curve; + + REG_SET_2(REGAMMA_CNTLA_START_CNTL, 0, + REGAMMA_CNTLA_EXP_REGION_START, params->arr_points[0].custom_float_x, + REGAMMA_CNTLA_EXP_REGION_START_SEGMENT, 0); + + REG_SET(REGAMMA_CNTLA_SLOPE_CNTL, 0, + REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE, params->arr_points[0].custom_float_slope); + + REG_SET(REGAMMA_CNTLA_END_CNTL1, 0, + REGAMMA_CNTLA_EXP_REGION_END, params->arr_points[1].custom_float_x); + + REG_SET_2(REGAMMA_CNTLA_END_CNTL2, 0, + REGAMMA_CNTLA_EXP_REGION_END_BASE, params->arr_points[1].custom_float_y, + REGAMMA_CNTLA_EXP_REGION_END_SLOPE, params->arr_points[1].custom_float_slope); + + curve = params->arr_curve_points; + + REG_SET_4(REGAMMA_CNTLA_REGION_0_1, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + curve += 2; + + REG_SET_4(REGAMMA_CNTLA_REGION_2_3, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + curve += 2; + + REG_SET_4(REGAMMA_CNTLA_REGION_4_5, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + curve += 2; + + REG_SET_4(REGAMMA_CNTLA_REGION_6_7, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + curve += 2; + + REG_SET_4(REGAMMA_CNTLA_REGION_8_9, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + curve += 2; + + REG_SET_4(REGAMMA_CNTLA_REGION_10_11, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + curve += 2; + + REG_SET_4(REGAMMA_CNTLA_REGION_12_13, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); + curve += 2; + + REG_SET_4(REGAMMA_CNTLA_REGION_14_15, 0, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, curve[0].offset, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, curve[0].segments_num, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, curve[1].offset, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, curve[1].segments_num); +} + + + +void dce110_opp_program_regamma_pwl(struct transform *xfm, + const struct pwl_params *params) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + /* Setup regions */ + regamma_config_regions_and_segments(xfm_dce, params); + + /* Program PWL */ + program_pwl(xfm_dce, params); +} + +void dce110_opp_power_on_regamma_lut(struct transform *xfm, + bool power_on) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + if (REG(DCFE_MEM_PWR_CTRL)) + REG_UPDATE_2(DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS, power_on, + DCP_LUT_MEM_PWR_DIS, power_on); + else + REG_UPDATE_2(DCFE_MEM_LIGHT_SLEEP_CNTL, + REGAMMA_LUT_LIGHT_SLEEP_DIS, power_on, + DCP_LUT_LIGHT_SLEEP_DIS, power_on); + +} + +void dce110_opp_set_regamma_mode(struct transform *xfm, + enum opp_regamma mode) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + REG_SET(REGAMMA_CONTROL, 0, + GRPH_REGAMMA_MODE, mode); +} + +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, + .opp_set_csc_adjustment = dce110_opp_set_csc_adjustment, + .opp_set_csc_default = dce110_opp_set_csc_default, + .opp_power_on_regamma_lut = dce110_opp_power_on_regamma_lut, + .opp_program_regamma_pwl = dce110_opp_program_regamma_pwl, + .opp_set_regamma_mode = dce110_opp_set_regamma_mode, + .transform_set_pixel_storage_depth = dce_transform_set_pixel_storage_depth, + .transform_get_optimal_number_of_taps = dce_transform_get_optimal_number_of_taps +}; + +#if defined(CONFIG_DRM_AMD_DC_SI) +static const struct transform_funcs dce60_transform_funcs = { + .transform_reset = dce_transform_reset, + .transform_set_scaler = dce60_transform_set_scaler, + .transform_set_gamut_remap = dce_transform_set_gamut_remap, + .opp_set_csc_adjustment = dce110_opp_set_csc_adjustment, + .opp_set_csc_default = dce110_opp_set_csc_default, + .opp_power_on_regamma_lut = dce110_opp_power_on_regamma_lut, + .opp_program_regamma_pwl = dce110_opp_program_regamma_pwl, + .opp_set_regamma_mode = dce110_opp_set_regamma_mode, + .transform_set_pixel_storage_depth = dce60_transform_set_pixel_storage_depth, + .transform_get_optimal_number_of_taps = dce_transform_get_optimal_number_of_taps +}; +#endif + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +void 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 | + LB_PIXEL_DEPTH_36BPP; + + xfm_dce->lb_bits_per_entry = LB_BITS_PER_ENTRY; + xfm_dce->lb_memory_size = LB_TOTAL_NUMBER_OF_ENTRIES; /*0x6B0*/ +} + +#if defined(CONFIG_DRM_AMD_DC_SI) +void dce60_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 = &dce60_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 | + LB_PIXEL_DEPTH_36BPP; + + xfm_dce->lb_bits_per_entry = LB_BITS_PER_ENTRY; + xfm_dce->lb_memory_size = LB_TOTAL_NUMBER_OF_ENTRIES; /*0x6B0*/ +} +#endif 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 000000000..cbce194ec --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.h @@ -0,0 +1,695 @@ +/* + * 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(OUTPUT_CSC_C11_C12, DCP, id), \ + SRI(OUTPUT_CSC_C13_C14, DCP, id), \ + SRI(OUTPUT_CSC_C21_C22, DCP, id), \ + SRI(OUTPUT_CSC_C23_C24, DCP, id), \ + SRI(OUTPUT_CSC_C31_C32, DCP, id), \ + SRI(OUTPUT_CSC_C33_C34, DCP, id), \ + SRI(OUTPUT_CSC_CONTROL, DCP, id), \ + SRI(REGAMMA_CNTLA_START_CNTL, DCP, id), \ + SRI(REGAMMA_CNTLA_SLOPE_CNTL, DCP, id), \ + SRI(REGAMMA_CNTLA_END_CNTL1, DCP, id), \ + SRI(REGAMMA_CNTLA_END_CNTL2, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_0_1, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_2_3, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_4_5, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_6_7, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_8_9, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_10_11, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_12_13, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_14_15, DCP, id), \ + SRI(REGAMMA_LUT_WRITE_EN_MASK, DCP, id), \ + SRI(REGAMMA_LUT_INDEX, DCP, id), \ + SRI(REGAMMA_LUT_DATA, DCP, id), \ + SRI(REGAMMA_CONTROL, 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(SCL_BYPASS_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), \ + SRI(SCL_F_SHARP_CONTROL, SCL, id) + +#define XFM_COMMON_REG_LIST_DCE80(id) \ + XFM_COMMON_REG_LIST_DCE_BASE(id), \ + SRI(DCFE_MEM_LIGHT_SLEEP_CNTL, CRTC, 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) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define XFM_COMMON_REG_LIST_DCE60_BASE(id) \ + SRI(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(OUTPUT_CSC_C11_C12, DCP, id), \ + SRI(OUTPUT_CSC_C13_C14, DCP, id), \ + SRI(OUTPUT_CSC_C21_C22, DCP, id), \ + SRI(OUTPUT_CSC_C23_C24, DCP, id), \ + SRI(OUTPUT_CSC_C31_C32, DCP, id), \ + SRI(OUTPUT_CSC_C33_C34, DCP, id), \ + SRI(OUTPUT_CSC_CONTROL, DCP, id), \ + SRI(REGAMMA_CNTLA_START_CNTL, DCP, id), \ + SRI(REGAMMA_CNTLA_SLOPE_CNTL, DCP, id), \ + SRI(REGAMMA_CNTLA_END_CNTL1, DCP, id), \ + SRI(REGAMMA_CNTLA_END_CNTL2, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_0_1, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_2_3, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_4_5, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_6_7, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_8_9, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_10_11, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_12_13, DCP, id), \ + SRI(REGAMMA_CNTLA_REGION_14_15, DCP, id), \ + SRI(REGAMMA_LUT_WRITE_EN_MASK, DCP, id), \ + SRI(REGAMMA_LUT_INDEX, DCP, id), \ + SRI(REGAMMA_LUT_DATA, DCP, id), \ + SRI(REGAMMA_CONTROL, DCP, id), \ + SRI(DENORM_CONTROL, DCP, id), \ + SRI(DCP_SPATIAL_DITHER_CNTL, DCP, id), \ + SRI(OUT_ROUND_CONTROL, DCP, id), \ + SRI(SCL_TAP_CONTROL, SCL, id), \ + SRI(SCL_CONTROL, SCL, id), \ + SRI(SCL_BYPASS_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_VERT_FILTER_INIT, SCL, id), \ + SRI(SCL_AUTOMATIC_MODE_CONTROL, SCL, id), \ + SRI(DC_LB_MEMORY_SPLIT, LB, id), \ + SRI(DC_LB_MEM_SIZE, LB, id), \ + SRI(DCFE_MEM_LIGHT_SLEEP_CNTL, CRTC, id), \ + SRI(SCL_UPDATE, SCL, id), \ + SRI(SCL_F_SHARP_CONTROL, SCL, id) + +#define XFM_COMMON_REG_LIST_DCE60(id) \ + XFM_COMMON_REG_LIST_DCE60_BASE(id), \ + SRI(DCFE_MEM_LIGHT_SLEEP_CNTL, CRTC, id) +#endif + +#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(OUTPUT_CSC_C11_C12, OUTPUT_CSC_C11, mask_sh),\ + XFM_SF(OUTPUT_CSC_C11_C12, OUTPUT_CSC_C12, mask_sh),\ + XFM_SF(OUTPUT_CSC_CONTROL, OUTPUT_CSC_GRPH_MODE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_START_CNTL, REGAMMA_CNTLA_EXP_REGION_START, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_START_CNTL, REGAMMA_CNTLA_EXP_REGION_START_SEGMENT, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_SLOPE_CNTL, REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_END_CNTL1, REGAMMA_CNTLA_EXP_REGION_END, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_END_CNTL2, REGAMMA_CNTLA_EXP_REGION_END_BASE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_END_CNTL2, REGAMMA_CNTLA_EXP_REGION_END_SLOPE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + XFM_SF(REGAMMA_LUT_WRITE_EN_MASK, REGAMMA_LUT_WRITE_EN_MASK, mask_sh),\ + XFM_SF(REGAMMA_CONTROL, GRPH_REGAMMA_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(SCL_BYPASS_CONTROL, SCL_BYPASS_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_DCE80(mask_sh) \ + XFM_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh), \ + OPP_SF(DCFE_MEM_LIGHT_SLEEP_CNTL, REGAMMA_LUT_LIGHT_SLEEP_DIS, mask_sh),\ + OPP_SF(DCFE_MEM_LIGHT_SLEEP_CNTL, DCP_LUT_LIGHT_SLEEP_DIS, mask_sh),\ + OPP_SF(DCFE_MEM_LIGHT_SLEEP_CNTL, REGAMMA_LUT_MEM_PWR_STATE, 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(DCFE_MEM_PWR_CTRL, DCP_REGAMMA_MEM_PWR_DIS, mask_sh),\ + XFM_SF(DCFE_MEM_PWR_CTRL, DCP_LUT_MEM_PWR_DIS, mask_sh),\ + XFM_SF(DCFE_MEM_PWR_STATUS, DCP_REGAMMA_MEM_PWR_STATE, mask_sh),\ + XFM_SF(SCL_MODE, SCL_PSCL_EN, mask_sh) + +#if defined(CONFIG_DRM_AMD_DC_SI) +#define XFM_COMMON_MASK_SH_LIST_DCE60(mask_sh) \ + XFM_COMMON_MASK_SH_LIST_DCE60_COMMON_BASE(mask_sh), \ + OPP_SF(DCFE_MEM_LIGHT_SLEEP_CNTL, REGAMMA_LUT_LIGHT_SLEEP_DIS, mask_sh),\ + OPP_SF(DCFE_MEM_LIGHT_SLEEP_CNTL, DCP_LUT_LIGHT_SLEEP_DIS, mask_sh),\ + OPP_SF(DCFE_MEM_LIGHT_SLEEP_CNTL, REGAMMA_LUT_MEM_PWR_STATE, mask_sh) + +#define XFM_COMMON_MASK_SH_LIST_DCE60_COMMON_BASE(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(DATA_FORMAT, INTERLEAVE_EN, 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(OUTPUT_CSC_C11_C12, OUTPUT_CSC_C11, mask_sh),\ + XFM_SF(OUTPUT_CSC_C11_C12, OUTPUT_CSC_C12, mask_sh),\ + XFM_SF(OUTPUT_CSC_CONTROL, OUTPUT_CSC_GRPH_MODE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_START_CNTL, REGAMMA_CNTLA_EXP_REGION_START, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_START_CNTL, REGAMMA_CNTLA_EXP_REGION_START_SEGMENT, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_SLOPE_CNTL, REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_END_CNTL1, REGAMMA_CNTLA_EXP_REGION_END, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_END_CNTL2, REGAMMA_CNTLA_EXP_REGION_END_BASE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_END_CNTL2, REGAMMA_CNTLA_EXP_REGION_END_SLOPE, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, mask_sh),\ + XFM_SF(REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + XFM_SF(REGAMMA_LUT_WRITE_EN_MASK, REGAMMA_LUT_WRITE_EN_MASK, mask_sh),\ + XFM_SF(REGAMMA_CONTROL, GRPH_REGAMMA_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_BYPASS_CONTROL, SCL_BYPASS_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_RGB_LUMA, SCL_H_INIT_INT_RGB_Y, mask_sh), \ + XFM_SF(SCL_HORZ_FILTER_INIT_RGB_LUMA, SCL_H_INIT_FRAC_RGB_Y, mask_sh), \ + XFM_SF(SCL_HORZ_FILTER_INIT_CHROMA, SCL_H_INIT_INT_CBCR, mask_sh), \ + XFM_SF(SCL_HORZ_FILTER_INIT_CHROMA, SCL_H_INIT_FRAC_CBCR, 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(SCL_HORZ_FILTER_CONTROL, SCL_H_FILTER_PICK_NEAREST, mask_sh), \ + XFM_SF(SCL_VERT_FILTER_CONTROL, SCL_V_FILTER_PICK_NEAREST, mask_sh), \ + XFM_SF(DC_LB_MEMORY_SPLIT, DC_LB_MEMORY_CONFIG, mask_sh), \ + XFM_SF(DC_LB_MEM_SIZE, DC_LB_MEM_SIZE, mask_sh) +#endif + +#define XFM_COMMON_MASK_SH_LIST_SOC_BASE(mask_sh) \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_B_CB, OUT_CLAMP_MIN_B_CB, mask_sh), \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_B_CB, OUT_CLAMP_MAX_B_CB, mask_sh), \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_G_Y, OUT_CLAMP_MIN_G_Y, mask_sh), \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_G_Y, OUT_CLAMP_MAX_G_Y, mask_sh), \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_R_CR, OUT_CLAMP_MIN_R_CR, mask_sh), \ + XFM_SF(DCP0_OUT_CLAMP_CONTROL_R_CR, OUT_CLAMP_MAX_R_CR, mask_sh), \ + XFM_SF(DCP0_OUT_ROUND_CONTROL, OUT_ROUND_TRUNC_MODE, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_EN, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_MODE, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_SPATIAL_DITHER_DEPTH, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_FRAME_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_RGB_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DCP0_DCP_SPATIAL_DITHER_CNTL, DCP_HIGHPASS_RANDOM_ENABLE, mask_sh), \ + XFM_SF(DCP0_DENORM_CONTROL, DENORM_MODE, mask_sh), \ + XFM_SF(LB0_LB_DATA_FORMAT, PIXEL_DEPTH, mask_sh), \ + XFM_SF(LB0_LB_DATA_FORMAT, PIXEL_EXPAN_MODE, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C11_C12, GAMUT_REMAP_C11, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C11_C12, GAMUT_REMAP_C12, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C13_C14, GAMUT_REMAP_C13, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C13_C14, GAMUT_REMAP_C14, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C21_C22, GAMUT_REMAP_C21, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C21_C22, GAMUT_REMAP_C22, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C23_C24, GAMUT_REMAP_C23, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C23_C24, GAMUT_REMAP_C24, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C31_C32, GAMUT_REMAP_C31, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C31_C32, GAMUT_REMAP_C32, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C33_C34, GAMUT_REMAP_C33, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_C33_C34, GAMUT_REMAP_C34, mask_sh), \ + XFM_SF(DCP0_GAMUT_REMAP_CONTROL, GRPH_GAMUT_REMAP_MODE, mask_sh), \ + XFM_SF(DCP0_OUTPUT_CSC_C11_C12, OUTPUT_CSC_C11, mask_sh),\ + XFM_SF(DCP0_OUTPUT_CSC_C11_C12, OUTPUT_CSC_C12, mask_sh),\ + XFM_SF(DCP0_OUTPUT_CSC_CONTROL, OUTPUT_CSC_GRPH_MODE, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_START_CNTL, REGAMMA_CNTLA_EXP_REGION_START, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_START_CNTL, REGAMMA_CNTLA_EXP_REGION_START_SEGMENT, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_SLOPE_CNTL, REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_END_CNTL1, REGAMMA_CNTLA_EXP_REGION_END, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_END_CNTL2, REGAMMA_CNTLA_EXP_REGION_END_BASE, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_END_CNTL2, REGAMMA_CNTLA_EXP_REGION_END_SLOPE, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CNTLA_REGION_0_1, REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS, mask_sh),\ + XFM_SF(DCP0_REGAMMA_CONTROL, GRPH_REGAMMA_MODE, mask_sh),\ + XFM_SF(DCP0_REGAMMA_LUT_WRITE_EN_MASK, REGAMMA_LUT_WRITE_EN_MASK, mask_sh),\ + XFM_SF(SCL0_SCL_MODE, SCL_MODE, mask_sh), \ + XFM_SF(SCL0_SCL_TAP_CONTROL, SCL_H_NUM_OF_TAPS, mask_sh), \ + XFM_SF(SCL0_SCL_TAP_CONTROL, SCL_V_NUM_OF_TAPS, mask_sh), \ + XFM_SF(SCL0_SCL_CONTROL, SCL_BOUNDARY_MODE, mask_sh), \ + XFM_SF(SCL0_SCL_BYPASS_CONTROL, SCL_BYPASS_MODE, mask_sh), \ + XFM_SF(SCL0_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT, mask_sh), \ + XFM_SF(SCL0_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT, mask_sh), \ + XFM_SF(SCL0_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP, mask_sh), \ + XFM_SF(SCL0_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_SELECT, SCL_C_RAM_FILTER_TYPE, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_SELECT, SCL_C_RAM_PHASE, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_SELECT, SCL_C_RAM_TAP_PAIR_IDX, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_EVEN_TAP_COEF_EN, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_EVEN_TAP_COEF, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_ODD_TAP_COEF_EN, mask_sh), \ + XFM_SF(SCL0_SCL_COEF_RAM_TAP_DATA, SCL_C_RAM_ODD_TAP_COEF, mask_sh), \ + XFM_SF(SCL0_VIEWPORT_START, VIEWPORT_X_START, mask_sh), \ + XFM_SF(SCL0_VIEWPORT_START, VIEWPORT_Y_START, mask_sh), \ + XFM_SF(SCL0_VIEWPORT_SIZE, VIEWPORT_HEIGHT, mask_sh), \ + XFM_SF(SCL0_VIEWPORT_SIZE, VIEWPORT_WIDTH, mask_sh), \ + XFM_SF(SCL0_SCL_HORZ_FILTER_SCALE_RATIO, SCL_H_SCALE_RATIO, mask_sh), \ + XFM_SF(SCL0_SCL_VERT_FILTER_SCALE_RATIO, SCL_V_SCALE_RATIO, mask_sh), \ + XFM_SF(SCL0_SCL_HORZ_FILTER_INIT, SCL_H_INIT_INT, mask_sh), \ + XFM_SF(SCL0_SCL_HORZ_FILTER_INIT, SCL_H_INIT_FRAC, mask_sh), \ + XFM_SF(SCL0_SCL_VERT_FILTER_INIT, SCL_V_INIT_INT, mask_sh), \ + XFM_SF(SCL0_SCL_VERT_FILTER_INIT, SCL_V_INIT_FRAC, mask_sh), \ + XFM_SF(LB0_LB_MEMORY_CTRL, LB_MEMORY_CONFIG, mask_sh), \ + XFM_SF(LB0_LB_MEMORY_CTRL, LB_MEMORY_SIZE, mask_sh), \ + XFM_SF(SCL0_SCL_VERT_FILTER_CONTROL, SCL_V_2TAP_HARDCODE_COEF_EN, mask_sh), \ + XFM_SF(SCL0_SCL_HORZ_FILTER_CONTROL, SCL_H_2TAP_HARDCODE_COEF_EN, mask_sh), \ + XFM_SF(SCL0_SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE, mask_sh), \ + XFM_SF(LB0_LB_DATA_FORMAT, ALPHA_EN, mask_sh), \ + XFM_SF(DCFE0_DCFE_MEM_PWR_CTRL, SCL_COEFF_MEM_PWR_DIS, mask_sh), \ + XFM_SF(DCFE0_DCFE_MEM_PWR_CTRL, DCP_REGAMMA_MEM_PWR_DIS, mask_sh),\ + XFM_SF(DCFE0_DCFE_MEM_PWR_CTRL, DCP_LUT_MEM_PWR_DIS, mask_sh),\ + XFM_SF(DCFE0_DCFE_MEM_PWR_STATUS, SCL_COEFF_MEM_PWR_STATE, mask_sh), \ + XFM_SF(SCL0_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 INTERLEAVE_EN; \ + 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 OUTPUT_CSC_C11; \ + type OUTPUT_CSC_C12; \ + type OUTPUT_CSC_GRPH_MODE; \ + type DCP_REGAMMA_MEM_PWR_DIS; \ + type DCP_LUT_MEM_PWR_DIS; \ + type REGAMMA_LUT_LIGHT_SLEEP_DIS; \ + type DCP_LUT_LIGHT_SLEEP_DIS; \ + type REGAMMA_CNTLA_EXP_REGION_START; \ + type REGAMMA_CNTLA_EXP_REGION_START_SEGMENT; \ + type REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE; \ + type REGAMMA_CNTLA_EXP_REGION_END; \ + type REGAMMA_CNTLA_EXP_REGION_END_BASE; \ + type REGAMMA_CNTLA_EXP_REGION_END_SLOPE; \ + type REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET; \ + type REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS; \ + type REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET; \ + type REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS; \ + type DCP_REGAMMA_MEM_PWR_STATE; \ + type REGAMMA_LUT_MEM_PWR_STATE; \ + type REGAMMA_LUT_WRITE_EN_MASK; \ + type GRPH_REGAMMA_MODE; \ + type SCL_MODE; \ + type SCL_BYPASS_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_H_INIT_INT_RGB_Y; \ + type SCL_H_INIT_FRAC_RGB_Y; \ + type SCL_H_INIT_INT_CBCR; \ + type SCL_H_INIT_FRAC_CBCR; \ + type SCL_V_INIT_INT; \ + type SCL_V_INIT_FRAC; \ + type DC_LB_MEMORY_CONFIG; \ + type DC_LB_MEM_SIZE; \ + type LB_MEMORY_CONFIG; \ + type LB_MEMORY_SIZE; \ + type SCL_V_2TAP_HARDCODE_COEF_EN; \ + type SCL_H_2TAP_HARDCODE_COEF_EN; \ + type SCL_V_FILTER_PICK_NEAREST; \ + type SCL_H_FILTER_PICK_NEAREST; \ + 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 { +#if defined(CONFIG_DRM_AMD_DC_SI) + uint32_t DATA_FORMAT; +#endif + 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 OUTPUT_CSC_C11_C12; + uint32_t OUTPUT_CSC_C13_C14; + uint32_t OUTPUT_CSC_C21_C22; + uint32_t OUTPUT_CSC_C23_C24; + uint32_t OUTPUT_CSC_C31_C32; + uint32_t OUTPUT_CSC_C33_C34; + uint32_t OUTPUT_CSC_CONTROL; + uint32_t DCFE_MEM_LIGHT_SLEEP_CNTL; + uint32_t REGAMMA_CNTLA_START_CNTL; + uint32_t REGAMMA_CNTLA_SLOPE_CNTL; + uint32_t REGAMMA_CNTLA_END_CNTL1; + uint32_t REGAMMA_CNTLA_END_CNTL2; + uint32_t REGAMMA_CNTLA_REGION_0_1; + uint32_t REGAMMA_CNTLA_REGION_2_3; + uint32_t REGAMMA_CNTLA_REGION_4_5; + uint32_t REGAMMA_CNTLA_REGION_6_7; + uint32_t REGAMMA_CNTLA_REGION_8_9; + uint32_t REGAMMA_CNTLA_REGION_10_11; + uint32_t REGAMMA_CNTLA_REGION_12_13; + uint32_t REGAMMA_CNTLA_REGION_14_15; + uint32_t REGAMMA_LUT_WRITE_EN_MASK; + uint32_t REGAMMA_LUT_INDEX; + uint32_t REGAMMA_LUT_DATA; + uint32_t REGAMMA_CONTROL; + 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 SCL_BYPASS_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; +#if defined(CONFIG_DRM_AMD_DC_SI) + uint32_t SCL_HORZ_FILTER_INIT_RGB_LUMA; + uint32_t SCL_HORZ_FILTER_INIT_CHROMA; +#endif + uint32_t SCL_VERT_FILTER_INIT; + uint32_t SCL_AUTOMATIC_MODE_CONTROL; +#if defined(CONFIG_DRM_AMD_DC_SI) + uint32_t DC_LB_MEMORY_SPLIT; + uint32_t DC_LB_MEM_SIZE; +#endif + uint32_t LB_MEMORY_CTRL; + uint32_t SCL_UPDATE; + uint32_t SCL_F_SHARP_CONTROL; +}; + +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; +}; + +#if defined(CONFIG_DRM_AMD_DC_SI) +struct sclh_ratios_inits { + uint32_t h_int_scale_ratio; + uint32_t v_int_scale_ratio; + struct init_int_and_frac h_init_luma; + struct init_int_and_frac h_init_chroma; + struct init_int_and_frac v_init; +}; +#endif + +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; +}; + +void 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); + +#if defined(CONFIG_DRM_AMD_DC_SI) +void dce60_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); +#endif + +bool dce_transform_get_optimal_number_of_taps( + struct transform *xfm, + struct scaler_data *scl_data, + const struct scaling_taps *in_taps); + +void dce110_opp_set_csc_adjustment( + struct transform *xfm, + const struct out_csc_color_matrix *tbl_entry); + +void dce110_opp_set_csc_default( + struct transform *xfm, + const struct default_adjustment *default_adjust); + +/* REGAMMA RELATED */ +void dce110_opp_power_on_regamma_lut( + struct transform *xfm, + bool power_on); + +void dce110_opp_program_regamma_pwl( + struct transform *xfm, + const struct pwl_params *params); + +void dce110_opp_set_regamma_mode(struct transform *xfm, + enum opp_regamma mode); + +#endif /* _DCE_DCE_TRANSFORM_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c new file mode 100644 index 000000000..fb0dec4ed --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.c @@ -0,0 +1,277 @@ +/* + * Copyright 2019 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 "dmub_abm.h" +#include "dce_abm.h" +#include "dc.h" +#include "dc_dmub_srv.h" +#include "dmub/dmub_srv.h" +#include "core_types.h" +#include "dm_services.h" +#include "reg_helper.h" +#include "fixed31_32.h" + +#include "atom.h" + +#define TO_DMUB_ABM(abm)\ + container_of(abm, struct dce_abm, base) + +#define REG(reg) \ + (dce_abm->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + dce_abm->abm_shift->field_name, dce_abm->abm_mask->field_name + +#define CTX \ + dce_abm->base.ctx + +#define DISABLE_ABM_IMMEDIATELY 255 + + + +static void dmub_abm_enable_fractional_pwm(struct dc_context *dc) +{ + union dmub_rb_cmd cmd; + uint32_t fractional_pwm = (dc->dc->config.disable_fractional_pwm == false) ? 1 : 0; + uint32_t edp_id_count = dc->dc_edp_id_count; + int i; + uint8_t panel_mask = 0; + + for (i = 0; i < edp_id_count; i++) + panel_mask |= 0x01 << i; + + memset(&cmd, 0, sizeof(cmd)); + cmd.abm_set_pwm_frac.header.type = DMUB_CMD__ABM; + cmd.abm_set_pwm_frac.header.sub_type = DMUB_CMD__ABM_SET_PWM_FRAC; + cmd.abm_set_pwm_frac.abm_set_pwm_frac_data.fractional_pwm = fractional_pwm; + cmd.abm_set_pwm_frac.abm_set_pwm_frac_data.version = DMUB_CMD_ABM_CONTROL_VERSION_1; + cmd.abm_set_pwm_frac.abm_set_pwm_frac_data.panel_mask = panel_mask; + cmd.abm_set_pwm_frac.header.payload_bytes = sizeof(struct dmub_cmd_abm_set_pwm_frac_data); + + dc_dmub_srv_cmd_queue(dc->dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dc->dmub_srv); + dc_dmub_srv_wait_idle(dc->dmub_srv); +} + +static void dmub_abm_init(struct abm *abm, uint32_t backlight) +{ + struct dce_abm *dce_abm = TO_DMUB_ABM(abm); + + REG_WRITE(DC_ABM1_HG_SAMPLE_RATE, 0x3); + REG_WRITE(DC_ABM1_HG_SAMPLE_RATE, 0x1); + REG_WRITE(DC_ABM1_LS_SAMPLE_RATE, 0x3); + REG_WRITE(DC_ABM1_LS_SAMPLE_RATE, 0x1); + REG_WRITE(BL1_PWM_BL_UPDATE_SAMPLE_RATE, 0x1); + + REG_SET_3(DC_ABM1_HG_MISC_CTRL, 0, + ABM1_HG_NUM_OF_BINS_SEL, 0, + ABM1_HG_VMAX_SEL, 1, + ABM1_HG_BIN_BITWIDTH_SIZE_SEL, 0); + + REG_SET_3(DC_ABM1_IPCSC_COEFF_SEL, 0, + ABM1_IPCSC_COEFF_SEL_R, 2, + ABM1_IPCSC_COEFF_SEL_G, 4, + ABM1_IPCSC_COEFF_SEL_B, 2); + + REG_UPDATE(BL1_PWM_CURRENT_ABM_LEVEL, + BL1_PWM_CURRENT_ABM_LEVEL, backlight); + + REG_UPDATE(BL1_PWM_TARGET_ABM_LEVEL, + BL1_PWM_TARGET_ABM_LEVEL, backlight); + + REG_UPDATE(BL1_PWM_USER_LEVEL, + BL1_PWM_USER_LEVEL, backlight); + + REG_UPDATE_2(DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES, + ABM1_LS_MIN_PIXEL_VALUE_THRES, 0, + ABM1_LS_MAX_PIXEL_VALUE_THRES, 1000); + + REG_SET_3(DC_ABM1_HGLS_REG_READ_PROGRESS, 0, + ABM1_HG_REG_READ_MISSED_FRAME_CLEAR, 1, + ABM1_LS_REG_READ_MISSED_FRAME_CLEAR, 1, + ABM1_BL_REG_READ_MISSED_FRAME_CLEAR, 1); + + dmub_abm_enable_fractional_pwm(abm->ctx); +} + +static unsigned int dmub_abm_get_current_backlight(struct abm *abm) +{ + struct dce_abm *dce_abm = TO_DMUB_ABM(abm); + unsigned int backlight = REG_READ(BL1_PWM_CURRENT_ABM_LEVEL); + + /* return backlight in hardware format which is unsigned 17 bits, with + * 1 bit integer and 16 bit fractional + */ + return backlight; +} + +static unsigned int dmub_abm_get_target_backlight(struct abm *abm) +{ + struct dce_abm *dce_abm = TO_DMUB_ABM(abm); + unsigned int backlight = REG_READ(BL1_PWM_TARGET_ABM_LEVEL); + + /* return backlight in hardware format which is unsigned 17 bits, with + * 1 bit integer and 16 bit fractional + */ + return backlight; +} + +static bool dmub_abm_set_level(struct abm *abm, uint32_t level) +{ + union dmub_rb_cmd cmd; + struct dc_context *dc = abm->ctx; + struct dc_link *edp_links[MAX_NUM_EDP]; + int i; + int edp_num; + uint8_t panel_mask = 0; + + get_edp_links(dc->dc, edp_links, &edp_num); + + for (i = 0; i < edp_num; i++) { + if (edp_links[i]->link_status.link_active) + panel_mask |= (0x01 << i); + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.abm_set_level.header.type = DMUB_CMD__ABM; + cmd.abm_set_level.header.sub_type = DMUB_CMD__ABM_SET_LEVEL; + cmd.abm_set_level.abm_set_level_data.level = level; + cmd.abm_set_level.abm_set_level_data.version = DMUB_CMD_ABM_CONTROL_VERSION_1; + cmd.abm_set_level.abm_set_level_data.panel_mask = panel_mask; + cmd.abm_set_level.header.payload_bytes = sizeof(struct dmub_cmd_abm_set_level_data); + + dc_dmub_srv_cmd_queue(dc->dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dc->dmub_srv); + dc_dmub_srv_wait_idle(dc->dmub_srv); + + return true; +} + +static bool dmub_abm_init_config(struct abm *abm, + const char *src, + unsigned int bytes, + unsigned int inst) +{ + union dmub_rb_cmd cmd; + struct dc_context *dc = abm->ctx; + uint8_t panel_mask = 0x01 << inst; + + // TODO: Optimize by only reading back final 4 bytes + dmub_flush_buffer_mem(&dc->dmub_srv->dmub->scratch_mem_fb); + + // Copy iramtable into cw7 + memcpy(dc->dmub_srv->dmub->scratch_mem_fb.cpu_addr, (void *)src, bytes); + + memset(&cmd, 0, sizeof(cmd)); + // Fw will copy from cw7 to fw_state + cmd.abm_init_config.header.type = DMUB_CMD__ABM; + cmd.abm_init_config.header.sub_type = DMUB_CMD__ABM_INIT_CONFIG; + cmd.abm_init_config.abm_init_config_data.src.quad_part = dc->dmub_srv->dmub->scratch_mem_fb.gpu_addr; + cmd.abm_init_config.abm_init_config_data.bytes = bytes; + cmd.abm_init_config.abm_init_config_data.version = DMUB_CMD_ABM_CONTROL_VERSION_1; + cmd.abm_init_config.abm_init_config_data.panel_mask = panel_mask; + + cmd.abm_init_config.header.payload_bytes = sizeof(struct dmub_cmd_abm_init_config_data); + + dc_dmub_srv_cmd_queue(dc->dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dc->dmub_srv); + dc_dmub_srv_wait_idle(dc->dmub_srv); + + return true; +} + +static bool dmub_abm_set_pause(struct abm *abm, bool pause, unsigned int panel_inst, unsigned int stream_inst) +{ + union dmub_rb_cmd cmd; + struct dc_context *dc = abm->ctx; + uint8_t panel_mask = 0x01 << panel_inst; + + memset(&cmd, 0, sizeof(cmd)); + cmd.abm_pause.header.type = DMUB_CMD__ABM; + cmd.abm_pause.header.sub_type = DMUB_CMD__ABM_PAUSE; + cmd.abm_pause.abm_pause_data.enable = pause; + cmd.abm_pause.abm_pause_data.panel_mask = panel_mask; + cmd.abm_set_level.header.payload_bytes = sizeof(struct dmub_cmd_abm_pause_data); + + dc_dmub_srv_cmd_queue(dc->dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dc->dmub_srv); + dc_dmub_srv_wait_idle(dc->dmub_srv); + + return true; +} + +static const struct abm_funcs abm_funcs = { + .abm_init = dmub_abm_init, + .set_abm_level = dmub_abm_set_level, + .get_current_backlight = dmub_abm_get_current_backlight, + .get_target_backlight = dmub_abm_get_target_backlight, + .init_abm_config = dmub_abm_init_config, + .set_abm_pause = dmub_abm_set_pause, +}; + +static void dmub_abm_construct( + struct dce_abm *abm_dce, + struct dc_context *ctx, + const struct dce_abm_registers *regs, + const struct dce_abm_shift *abm_shift, + const struct dce_abm_mask *abm_mask) +{ + struct abm *base = &abm_dce->base; + + base->ctx = ctx; + base->funcs = &abm_funcs; + base->dmcu_is_running = false; + + abm_dce->regs = regs; + abm_dce->abm_shift = abm_shift; + abm_dce->abm_mask = abm_mask; +} + +struct abm *dmub_abm_create( + struct dc_context *ctx, + const struct dce_abm_registers *regs, + const struct dce_abm_shift *abm_shift, + const struct dce_abm_mask *abm_mask) +{ + struct dce_abm *abm_dce = kzalloc(sizeof(*abm_dce), GFP_KERNEL); + + if (abm_dce == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dmub_abm_construct(abm_dce, ctx, regs, abm_shift, abm_mask); + + return &abm_dce->base; +} + +void dmub_abm_destroy(struct abm **abm) +{ + struct dce_abm *abm_dce = TO_DMUB_ABM(*abm); + + kfree(abm_dce); + *abm = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.h b/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.h new file mode 100644 index 000000000..3a5d5ac7a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_abm.h @@ -0,0 +1,40 @@ +/* + * Copyright 2019 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 __DMUB_ABM_H__ +#define __DMUB_ABM_H__ + +#include "abm.h" +#include "dce_abm.h" + +struct abm *dmub_abm_create( + struct dc_context *ctx, + const struct dce_abm_registers *regs, + const struct dce_abm_shift *abm_shift, + const struct dce_abm_mask *abm_mask); + +void dmub_abm_destroy(struct abm **abm); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c new file mode 100644 index 000000000..3f32e9c3f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c @@ -0,0 +1,71 @@ +/* + * Copyright 2019 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 "dmub_hw_lock_mgr.h" +#include "dc_dmub_srv.h" +#include "dc_types.h" +#include "core_types.h" + +void dmub_hw_lock_mgr_cmd(struct dc_dmub_srv *dmub_srv, + bool lock, + union dmub_hw_lock_flags *hw_locks, + struct dmub_hw_lock_inst_flags *inst_flags) +{ + union dmub_rb_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.lock_hw.header.type = DMUB_CMD__HW_LOCK; + cmd.lock_hw.header.sub_type = 0; + cmd.lock_hw.header.payload_bytes = sizeof(struct dmub_cmd_lock_hw_data); + cmd.lock_hw.lock_hw_data.client = HW_LOCK_CLIENT_DRIVER; + cmd.lock_hw.lock_hw_data.lock = lock; + cmd.lock_hw.lock_hw_data.hw_locks.u8All = hw_locks->u8All; + memcpy(&cmd.lock_hw.lock_hw_data.inst_flags, inst_flags, sizeof(struct dmub_hw_lock_inst_flags)); + + if (!lock) + cmd.lock_hw.lock_hw_data.should_release = 1; + + dc_dmub_srv_cmd_queue(dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dmub_srv); + dc_dmub_srv_wait_idle(dmub_srv); +} + +void dmub_hw_lock_mgr_inbox0_cmd(struct dc_dmub_srv *dmub_srv, + union dmub_inbox0_cmd_lock_hw hw_lock_cmd) +{ + union dmub_inbox0_data_register data = { 0 }; + + data.inbox0_cmd_lock_hw = hw_lock_cmd; + dc_dmub_srv_clear_inbox0_ack(dmub_srv); + dc_dmub_srv_send_inbox0_cmd(dmub_srv, data); + dc_dmub_srv_wait_for_inbox0_ack(dmub_srv); +} + +bool should_use_dmub_lock(struct dc_link *link) +{ + if (link->psr_settings.psr_version == DC_PSR_VERSION_SU_1) + return true; + return false; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.h b/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.h new file mode 100644 index 000000000..5a72b168f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.h @@ -0,0 +1,42 @@ +/* + * 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 _DMUB_HW_LOCK_MGR_H_ +#define _DMUB_HW_LOCK_MGR_H_ + +#include "dc_dmub_srv.h" +#include "core_types.h" + +void dmub_hw_lock_mgr_cmd(struct dc_dmub_srv *dmub_srv, + bool lock, + union dmub_hw_lock_flags *hw_locks, + struct dmub_hw_lock_inst_flags *inst_flags); + +void dmub_hw_lock_mgr_inbox0_cmd(struct dc_dmub_srv *dmub_srv, + union dmub_inbox0_cmd_lock_hw hw_lock_cmd); + +bool should_use_dmub_lock(struct dc_link *link); + +#endif /*_DMUB_HW_LOCK_MGR_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_outbox.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_outbox.c new file mode 100644 index 000000000..fff1d07d8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_outbox.c @@ -0,0 +1,54 @@ +/* + * Copyright 2020 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.h" +#include "dc_dmub_srv.h" +#include "dmub_outbox.h" +#include "dmub/inc/dmub_cmd.h" + +/* + * Function: dmub_enable_outbox_notification + * + * @brief + * Sends inbox cmd to dmub for enabling outbox notifications to x86. + * + * @param + * [in] dmub_srv: dmub_srv structure + */ +void dmub_enable_outbox_notification(struct dc_dmub_srv *dmub_srv) +{ + union dmub_rb_cmd cmd; + + memset(&cmd, 0x0, sizeof(cmd)); + cmd.outbox1_enable.header.type = DMUB_CMD__OUTBOX1_ENABLE; + cmd.outbox1_enable.header.sub_type = 0; + cmd.outbox1_enable.header.payload_bytes = + sizeof(cmd.outbox1_enable) - + sizeof(cmd.outbox1_enable.header); + cmd.outbox1_enable.enable = true; + + dc_dmub_srv_cmd_queue(dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dmub_srv); + dc_dmub_srv_wait_idle(dmub_srv); +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_outbox.h b/drivers/gpu/drm/amd/display/dc/dce/dmub_outbox.h new file mode 100644 index 000000000..58ceabb9d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_outbox.h @@ -0,0 +1,33 @@ +/* + * Copyright 2020 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 _DMUB_OUTBOX_H_ +#define _DMUB_OUTBOX_H_ + +struct dc_dmub_srv; + +void dmub_enable_outbox_notification(struct dc_dmub_srv *dmub_srv); + +#endif /* _DMUB_OUTBOX_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c new file mode 100644 index 000000000..2d3201b77 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c @@ -0,0 +1,507 @@ +/* + * Copyright 2019 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 "dmub_psr.h" +#include "dc.h" +#include "dc_dmub_srv.h" +#include "dmub/dmub_srv.h" +#include "core_types.h" + +#define DC_TRACE_LEVEL_MESSAGE(...) do {} while (0) /* do nothing */ + +#define MAX_PIPES 6 + +/* + * Convert dmcub psr state to dmcu psr state. + */ +static enum dc_psr_state convert_psr_state(uint32_t raw_state) +{ + enum dc_psr_state state = PSR_STATE0; + + if (raw_state == 0) + state = PSR_STATE0; + else if (raw_state == 0x10) + state = PSR_STATE1; + else if (raw_state == 0x11) + state = PSR_STATE1a; + else if (raw_state == 0x20) + state = PSR_STATE2; + else if (raw_state == 0x21) + state = PSR_STATE2a; + else if (raw_state == 0x22) + state = PSR_STATE2b; + else if (raw_state == 0x30) + state = PSR_STATE3; + else if (raw_state == 0x31) + state = PSR_STATE3Init; + else if (raw_state == 0x40) + state = PSR_STATE4; + else if (raw_state == 0x41) + state = PSR_STATE4a; + else if (raw_state == 0x42) + state = PSR_STATE4b; + else if (raw_state == 0x43) + state = PSR_STATE4c; + else if (raw_state == 0x44) + state = PSR_STATE4d; + else if (raw_state == 0x50) + state = PSR_STATE5; + else if (raw_state == 0x51) + state = PSR_STATE5a; + else if (raw_state == 0x52) + state = PSR_STATE5b; + else if (raw_state == 0x53) + state = PSR_STATE5c; + else if (raw_state == 0x4A) + state = PSR_STATE4_FULL_FRAME; + else if (raw_state == 0x4B) + state = PSR_STATE4a_FULL_FRAME; + else if (raw_state == 0x4C) + state = PSR_STATE4b_FULL_FRAME; + else if (raw_state == 0x4D) + state = PSR_STATE4c_FULL_FRAME; + else if (raw_state == 0x4E) + state = PSR_STATE4_FULL_FRAME_POWERUP; + else if (raw_state == 0x60) + state = PSR_STATE_HWLOCK_MGR; + else if (raw_state == 0x61) + state = PSR_STATE_POLLVUPDATE; + else + state = PSR_STATE_INVALID; + + return state; +} + +/* + * Get PSR state from firmware. + */ +static void dmub_psr_get_state(struct dmub_psr *dmub, enum dc_psr_state *state, uint8_t panel_inst) +{ + struct dmub_srv *srv = dmub->ctx->dmub_srv->dmub; + uint32_t raw_state = 0; + uint32_t retry_count = 0; + enum dmub_status status; + + do { + // Send gpint command and wait for ack + status = dmub_srv_send_gpint_command(srv, DMUB_GPINT__GET_PSR_STATE, panel_inst, 30); + + if (status == DMUB_STATUS_OK) { + // GPINT was executed, get response + dmub_srv_get_gpint_response(srv, &raw_state); + *state = convert_psr_state(raw_state); + } else + // Return invalid state when GPINT times out + *state = PSR_STATE_INVALID; + + } while (++retry_count <= 1000 && *state == PSR_STATE_INVALID); + + // Assert if max retry hit + if (retry_count >= 1000 && *state == PSR_STATE_INVALID) { + ASSERT(0); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "Unable to get PSR state from FW."); + } else + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE, + WPP_BIT_FLAG_Firmware_PsrState, + "Got PSR state from FW. PSR state: %d, Retry count: %d", + *state, retry_count); +} + +/* + * Set PSR version. + */ +static bool dmub_psr_set_version(struct dmub_psr *dmub, struct dc_stream_state *stream, uint8_t panel_inst) +{ + union dmub_rb_cmd cmd; + struct dc_context *dc = dmub->ctx; + + if (stream->link->psr_settings.psr_version == DC_PSR_VERSION_UNSUPPORTED) + return false; + + memset(&cmd, 0, sizeof(cmd)); + cmd.psr_set_version.header.type = DMUB_CMD__PSR; + cmd.psr_set_version.header.sub_type = DMUB_CMD__PSR_SET_VERSION; + switch (stream->link->psr_settings.psr_version) { + case DC_PSR_VERSION_1: + cmd.psr_set_version.psr_set_version_data.version = PSR_VERSION_1; + break; + case DC_PSR_VERSION_SU_1: + cmd.psr_set_version.psr_set_version_data.version = PSR_VERSION_SU_1; + break; + case DC_PSR_VERSION_UNSUPPORTED: + default: + cmd.psr_set_version.psr_set_version_data.version = PSR_VERSION_UNSUPPORTED; + break; + } + + if (cmd.psr_set_version.psr_set_version_data.version == PSR_VERSION_UNSUPPORTED) + return false; + + cmd.psr_set_version.psr_set_version_data.cmd_version = DMUB_CMD_PSR_CONTROL_VERSION_1; + cmd.psr_set_version.psr_set_version_data.panel_inst = panel_inst; + cmd.psr_set_version.header.payload_bytes = sizeof(struct dmub_cmd_psr_set_version_data); + + dc_dmub_srv_cmd_queue(dc->dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dc->dmub_srv); + dc_dmub_srv_wait_idle(dc->dmub_srv); + + return true; +} + +/* + * Enable/Disable PSR. + */ +static void dmub_psr_enable(struct dmub_psr *dmub, bool enable, bool wait, uint8_t panel_inst) +{ + union dmub_rb_cmd cmd; + struct dc_context *dc = dmub->ctx; + uint32_t retry_count; + enum dc_psr_state state = PSR_STATE0; + + memset(&cmd, 0, sizeof(cmd)); + cmd.psr_enable.header.type = DMUB_CMD__PSR; + + cmd.psr_enable.data.cmd_version = DMUB_CMD_PSR_CONTROL_VERSION_1; + cmd.psr_enable.data.panel_inst = panel_inst; + + if (enable) + cmd.psr_enable.header.sub_type = DMUB_CMD__PSR_ENABLE; + else + cmd.psr_enable.header.sub_type = DMUB_CMD__PSR_DISABLE; + + cmd.psr_enable.header.payload_bytes = 0; // Send header only + + dc_dmub_srv_cmd_queue(dc->dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dc->dmub_srv); + dc_dmub_srv_wait_idle(dc->dmub_srv); + + /* Below loops 1000 x 500us = 500 ms. + * Exit PSR may need to wait 1-2 frames to power up. Timeout after at + * least a few frames. Should never hit the max retry assert below. + */ + if (wait) { + for (retry_count = 0; retry_count <= 1000; retry_count++) { + dmub_psr_get_state(dmub, &state, panel_inst); + + if (enable) { + if (state != PSR_STATE0) + break; + } else { + if (state == PSR_STATE0) + break; + } + + udelay(500); + } + + /* assert if max retry hit */ + if (retry_count >= 1000) + ASSERT(0); + } +} + +/* + * Set PSR level. + */ +static void dmub_psr_set_level(struct dmub_psr *dmub, uint16_t psr_level, uint8_t panel_inst) +{ + union dmub_rb_cmd cmd; + enum dc_psr_state state = PSR_STATE0; + struct dc_context *dc = dmub->ctx; + + dmub_psr_get_state(dmub, &state, panel_inst); + + if (state == PSR_STATE0) + return; + + memset(&cmd, 0, sizeof(cmd)); + cmd.psr_set_level.header.type = DMUB_CMD__PSR; + cmd.psr_set_level.header.sub_type = DMUB_CMD__PSR_SET_LEVEL; + cmd.psr_set_level.header.payload_bytes = sizeof(struct dmub_cmd_psr_set_level_data); + cmd.psr_set_level.psr_set_level_data.psr_level = psr_level; + cmd.psr_set_level.psr_set_level_data.cmd_version = DMUB_CMD_PSR_CONTROL_VERSION_1; + cmd.psr_set_level.psr_set_level_data.panel_inst = panel_inst; + dc_dmub_srv_cmd_queue(dc->dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dc->dmub_srv); + dc_dmub_srv_wait_idle(dc->dmub_srv); +} + +/** + * Set PSR vtotal requirement for FreeSync PSR. + */ +static void dmub_psr_set_sink_vtotal_in_psr_active(struct dmub_psr *dmub, + uint16_t psr_vtotal_idle, uint16_t psr_vtotal_su) +{ + union dmub_rb_cmd cmd; + struct dc_context *dc = dmub->ctx; + + memset(&cmd, 0, sizeof(cmd)); + cmd.psr_set_vtotal.header.type = DMUB_CMD__PSR; + cmd.psr_set_vtotal.header.sub_type = DMUB_CMD__SET_SINK_VTOTAL_IN_PSR_ACTIVE; + cmd.psr_set_vtotal.header.payload_bytes = sizeof(struct dmub_cmd_psr_set_vtotal_data); + cmd.psr_set_vtotal.psr_set_vtotal_data.psr_vtotal_idle = psr_vtotal_idle; + cmd.psr_set_vtotal.psr_set_vtotal_data.psr_vtotal_su = psr_vtotal_su; + + dc_dmub_srv_cmd_queue(dc->dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dc->dmub_srv); + dc_dmub_srv_wait_idle(dc->dmub_srv); +} + +/* + * Set PSR power optimization flags. + */ +static void dmub_psr_set_power_opt(struct dmub_psr *dmub, unsigned int power_opt, uint8_t panel_inst) +{ + union dmub_rb_cmd cmd; + struct dc_context *dc = dmub->ctx; + + memset(&cmd, 0, sizeof(cmd)); + cmd.psr_set_power_opt.header.type = DMUB_CMD__PSR; + cmd.psr_set_power_opt.header.sub_type = DMUB_CMD__SET_PSR_POWER_OPT; + cmd.psr_set_power_opt.header.payload_bytes = sizeof(struct dmub_cmd_psr_set_power_opt_data); + cmd.psr_set_power_opt.psr_set_power_opt_data.cmd_version = DMUB_CMD_PSR_CONTROL_VERSION_1; + cmd.psr_set_power_opt.psr_set_power_opt_data.power_opt = power_opt; + cmd.psr_set_power_opt.psr_set_power_opt_data.panel_inst = panel_inst; + + dc_dmub_srv_cmd_queue(dc->dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dc->dmub_srv); + dc_dmub_srv_wait_idle(dc->dmub_srv); +} + +/* + * Setup PSR by programming phy registers and sending psr hw context values to firmware. + */ +static bool dmub_psr_copy_settings(struct dmub_psr *dmub, + struct dc_link *link, + struct psr_context *psr_context, + uint8_t panel_inst) +{ + union dmub_rb_cmd cmd; + struct dc_context *dc = dmub->ctx; + struct dmub_cmd_psr_copy_settings_data *copy_settings_data + = &cmd.psr_copy_settings.psr_copy_settings_data; + struct pipe_ctx *pipe_ctx = NULL; + struct resource_context *res_ctx = &link->ctx->dc->current_state->res_ctx; + int i = 0; + + for (i = 0; i < MAX_PIPES; i++) { + if (res_ctx->pipe_ctx[i].stream && + res_ctx->pipe_ctx[i].stream->link == link && + res_ctx->pipe_ctx[i].stream->link->connector_signal == SIGNAL_TYPE_EDP) { + pipe_ctx = &res_ctx->pipe_ctx[i]; + //TODO: refactor for multi edp support + break; + } + } + + if (!pipe_ctx) + return false; + + // First, set the psr version + if (!dmub_psr_set_version(dmub, pipe_ctx->stream, panel_inst)) + return false; + + // Program DP DPHY fast training registers + link->link_enc->funcs->psr_program_dp_dphy_fast_training(link->link_enc, + psr_context->psrExitLinkTrainingRequired); + + // Program DP_SEC_CNTL1 register to set transmission GPS0 line num and priority to high + link->link_enc->funcs->psr_program_secondary_packet(link->link_enc, + psr_context->sdpTransmitLineNumDeadline); + + memset(&cmd, 0, sizeof(cmd)); + cmd.psr_copy_settings.header.type = DMUB_CMD__PSR; + cmd.psr_copy_settings.header.sub_type = DMUB_CMD__PSR_COPY_SETTINGS; + cmd.psr_copy_settings.header.payload_bytes = sizeof(struct dmub_cmd_psr_copy_settings_data); + + // Hw insts + copy_settings_data->dpphy_inst = psr_context->transmitterId; + copy_settings_data->aux_inst = psr_context->channel; + copy_settings_data->digfe_inst = psr_context->engineId; + copy_settings_data->digbe_inst = psr_context->transmitterId; + + copy_settings_data->mpcc_inst = pipe_ctx->plane_res.mpcc_inst; + + if (pipe_ctx->plane_res.dpp) + copy_settings_data->dpp_inst = pipe_ctx->plane_res.dpp->inst; + else + copy_settings_data->dpp_inst = 0; + if (pipe_ctx->stream_res.opp) + copy_settings_data->opp_inst = pipe_ctx->stream_res.opp->inst; + else + copy_settings_data->opp_inst = 0; + if (pipe_ctx->stream_res.tg) + copy_settings_data->otg_inst = pipe_ctx->stream_res.tg->inst; + else + copy_settings_data->otg_inst = 0; + + // Misc + copy_settings_data->use_phy_fsm = link->ctx->dc->debug.psr_power_use_phy_fsm; + copy_settings_data->psr_level = psr_context->psr_level.u32all; + copy_settings_data->smu_optimizations_en = psr_context->allow_smu_optimizations; + copy_settings_data->multi_disp_optimizations_en = psr_context->allow_multi_disp_optimizations; + copy_settings_data->frame_delay = psr_context->frame_delay; + copy_settings_data->frame_cap_ind = psr_context->psrFrameCaptureIndicationReq; + copy_settings_data->init_sdp_deadline = psr_context->sdpTransmitLineNumDeadline; + copy_settings_data->debug.u32All = 0; + copy_settings_data->debug.bitfields.visual_confirm = dc->dc->debug.visual_confirm == VISUAL_CONFIRM_PSR; + copy_settings_data->debug.bitfields.use_hw_lock_mgr = 1; + copy_settings_data->debug.bitfields.force_full_frame_update = 0; + + if (psr_context->su_granularity_required == 0) + copy_settings_data->su_y_granularity = 0; + else + copy_settings_data->su_y_granularity = psr_context->su_y_granularity; + + copy_settings_data->line_capture_indication = 0; + copy_settings_data->line_time_in_us = psr_context->line_time_in_us; + copy_settings_data->rate_control_caps = psr_context->rate_control_caps; + copy_settings_data->fec_enable_status = (link->fec_state == dc_link_fec_enabled); + copy_settings_data->fec_enable_delay_in100us = link->dc->debug.fec_enable_delay_in100us; + copy_settings_data->cmd_version = DMUB_CMD_PSR_CONTROL_VERSION_1; + copy_settings_data->panel_inst = panel_inst; + copy_settings_data->dsc_enable_status = (pipe_ctx->stream->timing.flags.DSC == 1); + + /** + * WA for PSRSU+DSC on specific TCON, if DSC is enabled, force PSRSU as ffu mode(full frame update) + * Note that PSRSU+DSC is still under development. + */ + if (copy_settings_data->dsc_enable_status && + link->dpcd_caps.sink_dev_id == DP_DEVICE_ID_38EC11 && + !memcmp(link->dpcd_caps.sink_dev_id_str, DP_SINK_DEVICE_STR_ID_1, + sizeof(DP_SINK_DEVICE_STR_ID_1))) + link->psr_settings.force_ffu_mode = 1; + else + link->psr_settings.force_ffu_mode = 0; + copy_settings_data->force_ffu_mode = link->psr_settings.force_ffu_mode; + + if (((link->dpcd_caps.fec_cap.bits.FEC_CAPABLE && + !link->dc->debug.disable_fec) && + (link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT && + !link->panel_config.dsc.disable_dsc_edp && + link->dc->caps.edp_dsc_support)) && + link->dpcd_caps.sink_dev_id == DP_DEVICE_ID_38EC11 && + (!memcmp(link->dpcd_caps.sink_dev_id_str, DP_SINK_DEVICE_STR_ID_1, + sizeof(DP_SINK_DEVICE_STR_ID_1)) || + !memcmp(link->dpcd_caps.sink_dev_id_str, DP_SINK_DEVICE_STR_ID_2, + sizeof(DP_SINK_DEVICE_STR_ID_2)))) + copy_settings_data->debug.bitfields.force_wakeup_by_tps3 = 1; + else + copy_settings_data->debug.bitfields.force_wakeup_by_tps3 = 0; + + //WA for PSR1 on specific TCON, require frame delay for frame re-lock + copy_settings_data->relock_delay_frame_cnt = 0; + if (link->dpcd_caps.sink_dev_id == DP_BRANCH_DEVICE_ID_001CF8) + copy_settings_data->relock_delay_frame_cnt = 2; + + dc_dmub_srv_cmd_queue(dc->dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dc->dmub_srv); + dc_dmub_srv_wait_idle(dc->dmub_srv); + + return true; +} + +/* + * Send command to PSR to force static ENTER and ignore all state changes until exit + */ +static void dmub_psr_force_static(struct dmub_psr *dmub, uint8_t panel_inst) +{ + union dmub_rb_cmd cmd; + struct dc_context *dc = dmub->ctx; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.psr_force_static.psr_force_static_data.panel_inst = panel_inst; + cmd.psr_force_static.psr_force_static_data.cmd_version = DMUB_CMD_PSR_CONTROL_VERSION_1; + cmd.psr_force_static.header.type = DMUB_CMD__PSR; + cmd.psr_force_static.header.sub_type = DMUB_CMD__PSR_FORCE_STATIC; + cmd.psr_enable.header.payload_bytes = 0; + + dc_dmub_srv_cmd_queue(dc->dmub_srv, &cmd); + dc_dmub_srv_cmd_execute(dc->dmub_srv); + dc_dmub_srv_wait_idle(dc->dmub_srv); +} + +/* + * Get PSR residency from firmware. + */ +static void dmub_psr_get_residency(struct dmub_psr *dmub, uint32_t *residency, uint8_t panel_inst) +{ + struct dmub_srv *srv = dmub->ctx->dmub_srv->dmub; + uint16_t param = (uint16_t)(panel_inst << 8); + + /* Send gpint command and wait for ack */ + dmub_srv_send_gpint_command(srv, DMUB_GPINT__PSR_RESIDENCY, param, 30); + + dmub_srv_get_gpint_response(srv, residency); +} + +static const struct dmub_psr_funcs psr_funcs = { + .psr_copy_settings = dmub_psr_copy_settings, + .psr_enable = dmub_psr_enable, + .psr_get_state = dmub_psr_get_state, + .psr_set_level = dmub_psr_set_level, + .psr_force_static = dmub_psr_force_static, + .psr_get_residency = dmub_psr_get_residency, + .psr_set_sink_vtotal_in_psr_active = dmub_psr_set_sink_vtotal_in_psr_active, + .psr_set_power_opt = dmub_psr_set_power_opt, +}; + +/* + * Construct PSR object. + */ +static void dmub_psr_construct(struct dmub_psr *psr, struct dc_context *ctx) +{ + psr->ctx = ctx; + psr->funcs = &psr_funcs; +} + +/* + * Allocate and initialize PSR object. + */ +struct dmub_psr *dmub_psr_create(struct dc_context *ctx) +{ + struct dmub_psr *psr = kzalloc(sizeof(struct dmub_psr), GFP_KERNEL); + + if (psr == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dmub_psr_construct(psr, ctx); + + return psr; +} + +/* + * Deallocate PSR object. + */ +void dmub_psr_destroy(struct dmub_psr **dmub) +{ + kfree(*dmub); + *dmub = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.h b/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.h new file mode 100644 index 000000000..74005b9d3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.h @@ -0,0 +1,58 @@ +/* + * 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 _DMUB_PSR_H_ +#define _DMUB_PSR_H_ + +#include "os_types.h" +#include "dc_link.h" + +struct dmub_psr { + struct dc_context *ctx; + const struct dmub_psr_funcs *funcs; +}; + +struct dmub_psr_funcs { + bool (*psr_copy_settings)(struct dmub_psr *dmub, struct dc_link *link, + struct psr_context *psr_context, uint8_t panel_inst); + void (*psr_enable)(struct dmub_psr *dmub, bool enable, bool wait, + uint8_t panel_inst); + void (*psr_get_state)(struct dmub_psr *dmub, enum dc_psr_state *dc_psr_state, + uint8_t panel_inst); + void (*psr_set_level)(struct dmub_psr *dmub, uint16_t psr_level, + uint8_t panel_inst); + void (*psr_force_static)(struct dmub_psr *dmub, uint8_t panel_inst); + void (*psr_get_residency)(struct dmub_psr *dmub, uint32_t *residency, + uint8_t panel_inst); + void (*psr_set_sink_vtotal_in_psr_active)(struct dmub_psr *dmub, + uint16_t psr_vtotal_idle, uint16_t psr_vtotal_su); + void (*psr_set_power_opt)(struct dmub_psr *dmub, unsigned int power_opt, uint8_t panel_inst); +}; + +struct dmub_psr *dmub_psr_create(struct dc_context *ctx); +void dmub_psr_destroy(struct dmub_psr **dmub); + + +#endif /* _DMUB_PSR_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce100/Makefile b/drivers/gpu/drm/amd/display/dc/dce100/Makefile new file mode 100644 index 000000000..ff20c47f5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce100/Makefile @@ -0,0 +1,46 @@ +# +# Copyright 2017 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. +# +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +CFLAGS_$(AMDDALPATH)/dc/dce100/dce100_resource.o = $(call cc-disable-warning, override-init) + +DCE100 = dce100_resource.o dce100_hw_sequencer.o + +AMD_DAL_DCE100 = $(addprefix $(AMDDALPATH)/dc/dce100/,$(DCE100)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE100) + + +############################################################################### +# DCE 10x +############################################################################### +ifdef 0#CONFIG_DRM_AMD_DC_DCE11_0 +TG_DCE100 = dce100_resource.o + +AMD_DAL_TG_DCE100 = $(addprefix \ + $(AMDDALPATH)/dc/dce100/,$(TG_DCE100)) + +AMD_DISPLAY_FILES += $(AMD_DAL_TG_DCE100) +endif + diff --git a/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.c new file mode 100644 index 000000000..753cb8edd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.c @@ -0,0 +1,142 @@ +/* + * Copyright 2015 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.h" +#include "core_types.h" +#include "clk_mgr.h" +#include "dce100_hw_sequencer.h" +#include "resource.h" + +#include "dce110/dce110_hw_sequencer.h" + +/* include DCE10 register header files */ +#include "dce/dce_10_0_d.h" +#include "dce/dce_10_0_sh_mask.h" + +struct dce100_hw_seq_reg_offsets { + uint32_t blnd; + uint32_t crtc; +}; + +static const struct dce100_hw_seq_reg_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC3_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC4_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC5_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +} +}; + +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + +/******************************************************************************* + * Private definitions + ******************************************************************************/ +/***************************PIPE_CONTROL***********************************/ + +bool dce100_enable_display_power_gating( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + struct dc_context *ctx = dc->ctx; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (!(power_gating == PIPE_GATING_CONTROL_INIT && controller_id != 0)){ + + bp_result = dcb->funcs->enable_disp_power_gating( + dcb, controller_id + 1, cntl); + + /* Revert MASTER_UPDATE_MODE to 0 because bios sets it 2 + * by default when command table is called + */ + dm_write_reg(ctx, + HW_REG_CRTC(mmMASTER_UPDATE_MODE, controller_id), + 0); + } + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +} + +void dce100_prepare_bandwidth( + struct dc *dc, + struct dc_state *context) +{ + dce110_set_safe_displaymarks(&context->res_ctx, dc->res_pool); + + dc->clk_mgr->funcs->update_clocks( + dc->clk_mgr, + context, + false); +} + +void dce100_optimize_bandwidth( + struct dc *dc, + struct dc_state *context) +{ + dce110_set_safe_displaymarks(&context->res_ctx, dc->res_pool); + + dc->clk_mgr->funcs->update_clocks( + dc->clk_mgr, + context, + true); +} + +/**************************************************************************/ + +void dce100_hw_sequencer_construct(struct dc *dc) +{ + dce110_hw_sequencer_construct(dc); + + dc->hwseq->funcs.enable_display_power_gating = dce100_enable_display_power_gating; + dc->hwss.prepare_bandwidth = dce100_prepare_bandwidth; + dc->hwss.optimize_bandwidth = dce100_optimize_bandwidth; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.h new file mode 100644 index 000000000..34518da20 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce100/dce100_hw_sequencer.h @@ -0,0 +1,50 @@ +/* +* 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_HWSS_DCE100_H__ +#define __DC_HWSS_DCE100_H__ + +#include "core_types.h" +#include "hw_sequencer_private.h" + +struct dc; +struct dc_state; + +void dce100_hw_sequencer_construct(struct dc *dc); + +void dce100_prepare_bandwidth( + struct dc *dc, + struct dc_state *context); + +void dce100_optimize_bandwidth( + struct dc *dc, + struct dc_state *context); + +bool dce100_enable_display_power_gating(struct dc *dc, uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating); + +#endif /* __DC_HWSS_DCE100_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c new file mode 100644 index 000000000..54805802c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c @@ -0,0 +1,1174 @@ +/* + * 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 "link_encoder.h" +#include "stream_encoder.h" + +#include "resource.h" +#include "include/irq_service_interface.h" +#include "../virtual/virtual_stream_encoder.h" +#include "dce110/dce110_resource.h" +#include "dce110/dce110_timing_generator.h" +#include "irq/dce110/irq_service_dce110.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_mem_input.h" +#include "dce/dce_ipp.h" +#include "dce/dce_transform.h" +#include "dce/dce_opp.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_audio.h" +#include "dce/dce_hwseq.h" +#include "dce100/dce100_hw_sequencer.h" +#include "dce/dce_panel_cntl.h" + +#include "reg_helper.h" + +#include "dce/dce_10_0_d.h" +#include "dce/dce_10_0_sh_mask.h" + +#include "dce/dce_dmcu.h" +#include "dce/dce_aux.h" +#include "dce/dce_abm.h" +#include "dce/dce_i2c.h" + +#include "dce100_resource.h" + +#ifndef mmMC_HUB_RDREQ_DMIF_LIMIT +#include "gmc/gmc_8_2_d.h" +#include "gmc/gmc_8_2_sh_mask.h" +#endif + +#ifndef mmDP_DPHY_INTERNAL_CTRL + #define mmDP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x4ba7 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x4ca7 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x4da7 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x4ea7 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4fa7 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x54a7 + #define mmDP7_DP_DPHY_INTERNAL_CTRL 0x56a7 + #define mmDP8_DP_DPHY_INTERNAL_CTRL 0x57a7 +#endif + +#ifndef mmBIOS_SCRATCH_2 + #define mmBIOS_SCRATCH_2 0x05CB + #define mmBIOS_SCRATCH_3 0x05CC + #define mmBIOS_SCRATCH_6 0x05CF +#endif + +#ifndef mmDP_DPHY_BS_SR_SWAP_CNTL + #define mmDP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP0_DP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP1_DP_DPHY_BS_SR_SWAP_CNTL 0x4BDC + #define mmDP2_DP_DPHY_BS_SR_SWAP_CNTL 0x4CDC + #define mmDP3_DP_DPHY_BS_SR_SWAP_CNTL 0x4DDC + #define mmDP4_DP_DPHY_BS_SR_SWAP_CNTL 0x4EDC + #define mmDP5_DP_DPHY_BS_SR_SWAP_CNTL 0x4FDC + #define mmDP6_DP_DPHY_BS_SR_SWAP_CNTL 0x54DC +#endif + +#ifndef mmDP_DPHY_FAST_TRAINING + #define mmDP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP0_DP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP1_DP_DPHY_FAST_TRAINING 0x4BBC + #define mmDP2_DP_DPHY_FAST_TRAINING 0x4CBC + #define mmDP3_DP_DPHY_FAST_TRAINING 0x4DBC + #define mmDP4_DP_DPHY_FAST_TRAINING 0x4EBC + #define mmDP5_DP_DPHY_FAST_TRAINING 0x4FBC + #define mmDP6_DP_DPHY_FAST_TRAINING 0x54BC +#endif + +static const struct dce110_timing_generator_offsets dce100_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + } +}; + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + +#define ipp_regs(id)\ +[id] = {\ + IPP_DCE100_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2), + ipp_regs(3), + ipp_regs(4), + ipp_regs(5) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE100(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2), + transform_regs(3), + transform_regs(4), + transform_regs(5) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +#define link_regs(id)\ +[id] = {\ + LE_DCE100_REG_LIST(id)\ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST_DCE_BASE(id),\ + .AFMT_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2), + stream_enc_regs(3), + stream_enc_regs(4), + stream_enc_regs(5), + stream_enc_regs(6) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE80_100(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE80_100(_MASK) +}; + +static const struct dce_panel_cntl_registers panel_cntl_regs[] = { + { DCE_PANEL_CNTL_REG_LIST() } +}; + +static const struct dce_panel_cntl_shift panel_cntl_shift = { + DCE_PANEL_CNTL_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_panel_cntl_mask panel_cntl_mask = { + DCE_PANEL_CNTL_MASK_SH_LIST(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_100_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_100(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_100(_MASK) +}; +#define aux_engine_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = 0 \ +} + +static const struct dce110_aux_registers aux_engine_regs[] = { + aux_engine_regs(0), + aux_engine_regs(1), + aux_engine_regs(2), + aux_engine_regs(3), + aux_engine_regs(4), + aux_engine_regs(5) +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5), + audio_regs(6), +}; + +static const struct dce_audio_shift audio_shift = { + AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_audio_mask audio_mask = { + AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +#define clk_src_regs(id)\ +[id] = {\ + CS_COMMON_REG_LIST_DCE_100_110(id),\ +} + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0), + clk_src_regs(1), + clk_src_regs(2) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static const struct dce_dmcu_registers dmcu_regs = { + DMCU_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_dmcu_shift dmcu_shift = { + DMCU_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_dmcu_mask dmcu_mask = { + DMCU_MASK_SH_LIST_DCE110(_MASK) +}; + +static const struct dce_abm_registers abm_regs = { + ABM_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCE110(_MASK) +}; + +#define DCFE_MEM_PWR_CTRL_REG_BASE 0x1b03 + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3, + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 +}; + +static const struct resource_caps res_cap = { + .num_timing_generator = 6, + .num_audio = 6, + .num_stream_encoder = 6, + .num_pll = 3, + .num_ddc = 6, +}; + +static const struct dc_plane_cap plane_cap = { + .type = DC_PLANE_TYPE_DCE_RGB, + + .pixel_format_support = { + .argb8888 = true, + .nv12 = false, + .fp16 = true + }, + + .max_upscale_factor = { + .argb8888 = 16000, + .nv12 = 1, + .fp16 = 1 + }, + + .max_downscale_factor = { + .argb8888 = 250, + .nv12 = 1, + .fp16 = 1 + } +}; + +#define CTX ctx +#define REG(reg) mm ## reg + +#ifndef mmCC_DC_HDMI_STRAPS +#define mmCC_DC_HDMI_STRAPS 0x1918 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE_MASK 0x40 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE__SHIFT 0x6 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x700 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 +#endif + +static int map_transmitter_id_to_phy_instance( + enum transmitter transmitter) +{ + switch (transmitter) { + case TRANSMITTER_UNIPHY_A: + return 0; + case TRANSMITTER_UNIPHY_B: + return 1; + case TRANSMITTER_UNIPHY_C: + return 2; + case TRANSMITTER_UNIPHY_D: + return 3; + case TRANSMITTER_UNIPHY_E: + return 4; + case TRANSMITTER_UNIPHY_F: + return 5; + case TRANSMITTER_UNIPHY_G: + return 6; + default: + ASSERT(0); + return 0; + } +} + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + REG_GET_2(CC_DC_HDMI_STRAPS, + HDMI_DISABLE, &straps->hdmi_disable, + AUDIO_STREAM_NUMBER, &straps->audio_stream_number); + + REG_GET(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO, &straps->dc_pinstraps_audio); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static struct timing_generator *dce100_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce110_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static struct stream_encoder *dce100_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_reg = { + HWSEQ_DCE10_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE10_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE10_MASK_SH_LIST(_MASK) +}; + +static struct dce_hwseq *dce100_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce100_stream_encoder_create, + .create_hwseq = dce100_hwseq_create, +}; + +#define mi_inst_regs(id) { \ + MI_DCE8_REG_LIST(id), \ + .MC_HUB_RDREQ_DMIF_LIMIT = mmMC_HUB_RDREQ_DMIF_LIMIT \ +} +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), + mi_inst_regs(3), + mi_inst_regs(4), + mi_inst_regs(5), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE8_MASK_SH_LIST(__SHIFT), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE__SHIFT +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE8_MASK_SH_LIST(_MASK), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE_MASK +}; + +static const struct dce110_aux_registers_shift aux_shift = { + DCE10_AUX_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce110_aux_registers_mask aux_mask = { + DCE10_AUX_MASK_SH_LIST(_MASK) +}; + +static struct mem_input *dce100_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + dce_mi->wa.single_head_rdreq_dmif_limit = 2; + return &dce_mi->base; +} + +static void dce100_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce100_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + return &transform->base; +} + +static struct input_pixel_processor *dce100_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 300000, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true +}; + +static struct link_encoder *dce100_link_encoder_create( + struct dc_context *ctx, + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + int link_regs_id; + + if (!enc110) + return NULL; + + link_regs_id = + map_transmitter_id_to_phy_instance(enc_init_data->transmitter); + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[link_regs_id], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + return &enc110->base; +} + +static struct panel_cntl *dce100_panel_cntl_create(const struct panel_cntl_init_data *init_data) +{ + struct dce_panel_cntl *panel_cntl = + kzalloc(sizeof(struct dce_panel_cntl), GFP_KERNEL); + + if (!panel_cntl) + return NULL; + + dce_panel_cntl_construct(panel_cntl, + init_data, + &panel_cntl_regs[init_data->inst], + &panel_cntl_shift, + &panel_cntl_mask); + + return &panel_cntl->base; +} + +static struct output_pixel_processor *dce100_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} + +static struct dce_aux *dce100_aux_engine_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct aux_engine_dce110 *aux_engine = + kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); + + if (!aux_engine) + return NULL; + + dce110_aux_engine_construct(aux_engine, ctx, inst, + SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, + &aux_engine_regs[inst], + &aux_mask, + &aux_shift, + ctx->dc->caps.extended_aux_timeout_support); + + return &aux_engine->base; +} +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static struct dce_i2c_hw *dce100_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + + dce100_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} +static struct clock_source *dce100_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + kfree(clk_src); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static void dce100_clock_source_destroy(struct clock_source **clk_src) +{ + kfree(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + +static void dce100_resource_destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce100_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + if (pool->base.engines[i] != NULL) + dce110_engine_destroy(&pool->base.engines[i]); + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) + dce100_clock_source_destroy(&pool->base.clock_sources[i]); + } + + if (pool->base.dp_clock_source != NULL) + dce100_clock_source_destroy(&pool->base.dp_clock_source); + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i] != NULL) + dce_aud_destroy(&pool->base.audios[i]); + } + + if (pool->base.abm != NULL) + dce_abm_destroy(&pool->base.abm); + + if (pool->base.dmcu != NULL) + dce_dmcu_destroy(&pool->base.dmcu); + + if (pool->base.irqs != NULL) + dal_irq_service_destroy(&pool->base.irqs); +} + +static enum dc_status build_mapped_resource( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + struct pipe_ctx *pipe_ctx = resource_get_head_pipe_for_stream(&context->res_ctx, stream); + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; + + dce110_resource_build_pipe_hw_param(pipe_ctx); + + resource_build_info_frame(pipe_ctx); + + return DC_OK; +} + +static bool dce100_validate_bandwidth( + struct dc *dc, + struct dc_state *context, + bool fast_validate) +{ + int i; + bool at_least_one_pipe = false; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (context->res_ctx.pipe_ctx[i].stream) + at_least_one_pipe = true; + } + + if (at_least_one_pipe) { + /* TODO implement when needed but for now hardcode max value*/ + context->bw_ctx.bw.dce.dispclk_khz = 681000; + context->bw_ctx.bw.dce.yclk_khz = 250000 * MEMORY_TYPE_MULTIPLIER_CZ; + } else { + context->bw_ctx.bw.dce.dispclk_khz = 0; + context->bw_ctx.bw.dce.yclk_khz = 0; + } + + return true; +} + +static bool dce100_validate_surface_sets( + struct dc_state *context) +{ + int i; + + for (i = 0; i < context->stream_count; i++) { + if (context->stream_status[i].plane_count == 0) + continue; + + if (context->stream_status[i].plane_count > 1) + return false; + + if (context->stream_status[i].plane_states[0]->format + >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + } + + return true; +} + +static enum dc_status dce100_validate_global( + struct dc *dc, + struct dc_state *context) +{ + if (!dce100_validate_surface_sets(context)) + return DC_FAIL_SURFACE_VALIDATE; + + return DC_OK; +} + +enum dc_status dce100_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + result = resource_map_pool_resources(dc, new_ctx, dc_stream); + + if (result == DC_OK) + result = resource_map_clock_resources(dc, new_ctx, dc_stream); + + if (result == DC_OK) + result = build_mapped_resource(dc, new_ctx, dc_stream); + + return result; +} + +static void dce100_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + dce100_resource_destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + +enum dc_status dce100_validate_plane(const struct dc_plane_state *plane_state, struct dc_caps *caps) +{ + + if (plane_state->format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return DC_OK; + + return DC_FAIL_SURFACE_VALIDATE; +} + +struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct dc_stream_state *stream) +{ + int i; + int j = -1; + struct dc_link *link = stream->link; + + for (i = 0; i < pool->stream_enc_count; i++) { + if (!res_ctx->is_stream_enc_acquired[i] && + pool->stream_enc[i]) { + /* Store first available for MST second display + * in daisy chain use case + */ + j = i; + if (pool->stream_enc[i]->id == + link->link_enc->preferred_engine) + return pool->stream_enc[i]; + } + } + + /* + * below can happen in cases when stream encoder is acquired: + * 1) for second MST display in chain, so preferred engine already + * acquired; + * 2) for another link, which preferred engine already acquired by any + * MST configuration. + * + * If signal is of DP type and preferred engine not found, return last available + * + * TODO - This is just a patch up and a generic solution is + * required for non DP connectors. + */ + + if (j >= 0 && link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) + return pool->stream_enc[j]; + + return NULL; +} + +static const struct resource_funcs dce100_res_pool_funcs = { + .destroy = dce100_destroy_resource_pool, + .link_enc_create = dce100_link_encoder_create, + .panel_cntl_create = dce100_panel_cntl_create, + .validate_bandwidth = dce100_validate_bandwidth, + .validate_plane = dce100_validate_plane, + .add_stream_to_ctx = dce100_add_stream_to_ctx, + .validate_global = dce100_validate_global, + .find_first_free_match_stream_enc_for_link = dce100_find_first_free_match_stream_enc_for_link +}; + +static bool dce100_resource_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_bios *bp; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap; + pool->base.funcs = &dce100_res_pool_funcs; + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + + bp = ctx->dc_bios; + + if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[2] = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 3; + + } else { + pool->base.dp_clock_source = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[1] = + dce100_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 2; + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce110_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = res_cap.num_timing_generator; + pool->base.timing_generator_count = pool->base.res_cap->num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.max_cursor_size = 128; + dc->caps.min_horizontal_blanking_period = 80; + dc->caps.dual_link_dvi = true; + dc->caps.disable_dp_clk_share = true; + dc->caps.extended_aux_timeout_support = false; + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = + dce100_timing_generator_create( + ctx, + i, + &dce100_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce100_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce100_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce100_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce100_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce100_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce100_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = NULL; + } + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < dc->caps.max_planes; ++i) + dc->caps.planes[i] = plane_cap; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce100_hw_sequencer_construct(dc); + return true; + +res_create_fail: + dce100_resource_destruct(pool); + + return false; +} + +struct resource_pool *dce100_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce100_resource_construct(num_virtual_links, dc, pool)) + return &pool->base; + + kfree(pool); + BREAK_TO_DEBUGGER(); + return NULL; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.h b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.h new file mode 100644 index 000000000..fecab7c56 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.h @@ -0,0 +1,54 @@ +/* + * Copyright 2017 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. + * + * + */ +/* + * dce100_resource.h + * + * Created on: 2016-01-20 + * Author: qyang + */ + +#ifndef DCE100_RESOURCE_H_ +#define DCE100_RESOURCE_H_ + +struct dc; +struct resource_pool; +struct dc_validation_set; + +struct resource_pool *dce100_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +enum dc_status dce100_validate_plane(const struct dc_plane_state *plane_state, struct dc_caps *caps); + +enum dc_status dce100_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream); + +struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct dc_stream_state *stream); + +#endif /* DCE100_RESOURCE_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/Makefile b/drivers/gpu/drm/amd/display/dc/dce110/Makefile new file mode 100644 index 000000000..84ab48df0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/Makefile @@ -0,0 +1,35 @@ +# +# Copyright 2017 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. +# +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +CFLAGS_$(AMDDALPATH)/dc/dce110/dce110_resource.o = $(call cc-disable-warning, override-init) + +DCE110 = dce110_timing_generator.o \ +dce110_compressor.o dce110_hw_sequencer.o dce110_resource.o \ +dce110_opp_regamma_v.o dce110_opp_csc_v.o dce110_timing_generator_v.o \ +dce110_mem_input_v.o dce110_opp_v.o dce110_transform_v.o + +AMD_DAL_DCE110 = $(addprefix $(AMDDALPATH)/dc/dce110/,$(DCE110)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE110) diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c new file mode 100644 index 000000000..d241ee13b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c @@ -0,0 +1,471 @@ +/* + * 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 "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "gmc/gmc_8_2_sh_mask.h" +#include "gmc/gmc_8_2_d.h" + +#include "include/logger_interface.h" + +#include "dce110_compressor.h" + +#define DC_LOGGER \ + cp110->base.ctx->logger +#define DCP_REG(reg)\ + (reg + cp110->offsets.dcp_offset) +#define DMIF_REG(reg)\ + (reg + cp110->offsets.dmif_offset) + +static const struct dce110_compressor_reg_offsets reg_offsets[] = { +{ + .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG0_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG1_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG2_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +} +}; + +static uint32_t align_to_chunks_number_per_line(uint32_t pixels) +{ + return 256 * ((pixels + 255) / 256); +} + +static void reset_lb_on_vblank(struct compressor *compressor, uint32_t crtc_inst) +{ + uint32_t value; + uint32_t frame_count; + uint32_t status_pos; + uint32_t retry = 0; + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + + cp110->offsets = reg_offsets[crtc_inst]; + + status_pos = dm_read_reg(compressor->ctx, DCP_REG(mmCRTC_STATUS_POSITION)); + + + /* Only if CRTC is enabled and counter is moving we wait for one frame. */ + if (status_pos != dm_read_reg(compressor->ctx, DCP_REG(mmCRTC_STATUS_POSITION))) { + /* Resetting LB on VBlank */ + value = dm_read_reg(compressor->ctx, DCP_REG(mmLB_SYNC_RESET_SEL)); + set_reg_field_value(value, 3, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL); + set_reg_field_value(value, 1, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL2); + dm_write_reg(compressor->ctx, DCP_REG(mmLB_SYNC_RESET_SEL), value); + + frame_count = dm_read_reg(compressor->ctx, DCP_REG(mmCRTC_STATUS_FRAME_COUNT)); + + + for (retry = 10000; retry > 0; retry--) { + if (frame_count != dm_read_reg(compressor->ctx, DCP_REG(mmCRTC_STATUS_FRAME_COUNT))) + break; + udelay(10); + } + if (!retry) + dm_error("Frame count did not increase for 100ms.\n"); + + /* Resetting LB on VBlank */ + value = dm_read_reg(compressor->ctx, DCP_REG(mmLB_SYNC_RESET_SEL)); + set_reg_field_value(value, 2, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL); + set_reg_field_value(value, 0, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL2); + dm_write_reg(compressor->ctx, DCP_REG(mmLB_SYNC_RESET_SEL), value); + } +} + +static void wait_for_fbc_state_changed( + struct dce110_compressor *cp110, + bool enabled) +{ + uint32_t counter = 0; + uint32_t addr = mmFBC_STATUS; + uint32_t value; + + while (counter < 1000) { + value = dm_read_reg(cp110->base.ctx, addr); + if (get_reg_field_value( + value, + FBC_STATUS, + FBC_ENABLE_STATUS) == enabled) + break; + udelay(100); + counter++; + } + + if (counter == 1000) { + DC_LOG_WARNING("%s: wait counter exceeded, changes to HW not applied", + __func__); + } else { + DC_LOG_SYNC("FBC status changed to %d", enabled); + } + + +} + +void dce110_compressor_power_up_fbc(struct compressor *compressor) +{ + uint32_t value; + uint32_t addr; + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value(value, 1, FBC_CNTL, FBC_EN); + set_reg_field_value(value, 2, FBC_CNTL, FBC_COHERENCY_MODE); + if (compressor->options.bits.CLK_GATING_DISABLED == 1) { + /* HW needs to do power measurement comparison. */ + set_reg_field_value( + value, + 0, + FBC_CNTL, + FBC_COMP_CLK_GATE_EN); + } + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_MODE; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_RLE_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_DPCM4_RGB_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_IND_EN); + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_CNTL, FBC_DEPTH_RGB08_EN); + dm_write_reg(compressor->ctx, addr, value); + /*FBC_MIN_COMPRESSION 0 ==> 2:1 */ + /* 1 ==> 4:1 */ + /* 2 ==> 8:1 */ + /* 0xF ==> 1:1 */ + set_reg_field_value(value, 0xF, FBC_COMP_CNTL, FBC_MIN_COMPRESSION); + dm_write_reg(compressor->ctx, addr, value); + compressor->min_compress_ratio = FBC_COMPRESS_RATIO_1TO1; + + value = 0; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT0, value); + + value = 0xFFFFFF; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT1, value); +} + +void dce110_compressor_enable_fbc( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + (!dce110_compressor_is_fbc_enabled_in_hw(compressor, NULL))) { + + uint32_t addr; + uint32_t value, misc_value; + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + /* params->inst is valid HW CRTC instance start from 0 */ + set_reg_field_value( + value, + params->inst, + FBC_CNTL, FBC_SRC_SEL); + dm_write_reg(compressor->ctx, addr, value); + + /* Keep track of enum controller_id FBC is attached to */ + compressor->is_enabled = true; + /* attached_inst is SW CRTC instance start from 1 + * 0 = CONTROLLER_ID_UNDEFINED means not attached crtc + */ + compressor->attached_inst = params->inst + CONTROLLER_ID_D0; + + /* Toggle it as there is bug in HW */ + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + + /* FBC usage with scatter & gather for dce110 */ + misc_value = dm_read_reg(compressor->ctx, mmFBC_MISC); + + set_reg_field_value(misc_value, 1, + FBC_MISC, FBC_INVALIDATE_ON_ERROR); + set_reg_field_value(misc_value, 1, + FBC_MISC, FBC_DECOMPRESS_ERROR_CLEAR); + set_reg_field_value(misc_value, 0x14, + FBC_MISC, FBC_SLOW_REQ_INTERVAL); + + dm_write_reg(compressor->ctx, mmFBC_MISC, misc_value); + + /* Enable FBC */ + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + + wait_for_fbc_state_changed(cp110, true); + } +} + +void dce110_compressor_disable_fbc(struct compressor *compressor) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + uint32_t crtc_inst = 0; + + if (compressor->options.bits.FBC_SUPPORT) { + if (dce110_compressor_is_fbc_enabled_in_hw(compressor, &crtc_inst)) { + uint32_t reg_data; + /* Turn off compression */ + reg_data = dm_read_reg(compressor->ctx, mmFBC_CNTL); + set_reg_field_value(reg_data, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, mmFBC_CNTL, reg_data); + + /* Reset enum controller_id to undefined */ + compressor->attached_inst = 0; + compressor->is_enabled = false; + + wait_for_fbc_state_changed(cp110, false); + } + + /* Sync line buffer which fbc was attached to dce100/110 only */ + if (crtc_inst > CONTROLLER_ID_UNDEFINED && crtc_inst < CONTROLLER_ID_D3) + reset_lb_on_vblank(compressor, + crtc_inst - CONTROLLER_ID_D0); + } +} + +bool dce110_compressor_is_fbc_enabled_in_hw( + struct compressor *compressor, + uint32_t *inst) +{ + /* Check the hardware register */ + uint32_t value; + + value = dm_read_reg(compressor->ctx, mmFBC_STATUS); + if (get_reg_field_value(value, FBC_STATUS, FBC_ENABLE_STATUS)) { + if (inst != NULL) + *inst = compressor->attached_inst; + return true; + } + + value = dm_read_reg(compressor->ctx, mmFBC_MISC); + if (get_reg_field_value(value, FBC_MISC, FBC_STOP_ON_HFLIP_EVENT)) { + value = dm_read_reg(compressor->ctx, mmFBC_CNTL); + + if (get_reg_field_value(value, FBC_CNTL, FBC_GRPH_COMP_EN)) { + if (inst != NULL) + *inst = + compressor->attached_inst; + return true; + } + } + return false; +} + + +void dce110_compressor_program_compressed_surface_address_and_pitch( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + uint32_t value = 0; + uint32_t fbc_pitch = 0; + uint32_t compressed_surf_address_low_part = + compressor->compr_surface_address.addr.low_part; + + cp110->offsets = reg_offsets[params->inst]; + + /* Clear content first. */ + dm_write_reg( + compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + 0); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), 0); + + /* Write address, HIGH has to be first. */ + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + compressor->compr_surface_address.addr.high_part); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), + compressed_surf_address_low_part); + + fbc_pitch = align_to_chunks_number_per_line(params->source_view_width); + + if (compressor->min_compress_ratio == FBC_COMPRESS_RATIO_1TO1) + fbc_pitch = fbc_pitch / 8; + else + DC_LOG_WARNING("%s: Unexpected DCE11 compression ratio", + __func__); + + /* Clear content first. */ + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), 0); + + /* Write FBC Pitch. */ + set_reg_field_value( + value, + fbc_pitch, + GRPH_COMPRESS_PITCH, + GRPH_COMPRESS_PITCH); + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), value); + +} + +void dce110_compressor_set_fbc_invalidation_triggers( + struct compressor *compressor, + uint32_t fbc_trigger) +{ + /* Disable region hit event, FBC_MEMORY_REGION_MASK = 0 (bits 16-19) + * for DCE 11 regions cannot be used - does not work with S/G + */ + uint32_t addr = mmFBC_CLIENT_REGION_MASK; + uint32_t value = dm_read_reg(compressor->ctx, addr); + + set_reg_field_value( + value, + 0, + FBC_CLIENT_REGION_MASK, + FBC_MEMORY_REGION_MASK); + dm_write_reg(compressor->ctx, addr, value); + + /* Setup events when to clear all CSM entries (effectively marking + * current compressed data invalid) + * For DCE 11 CSM metadata 11111 means - "Not Compressed" + * Used as the initial value of the metadata sent to the compressor + * after invalidation, to indicate that the compressor should attempt + * to compress all chunks on the current pass. Also used when the chunk + * is not successfully written to memory. + * When this CSM value is detected, FBC reads from the uncompressed + * buffer. Set events according to passed in value, these events are + * valid for DCE11: + * - bit 0 - display register updated + * - bit 28 - memory write from any client except from MCIF + * - bit 29 - CG static screen signal is inactive + * In addition, DCE11.1 also needs to set new DCE11.1 specific events + * that are used to trigger invalidation on certain register changes, + * for example enabling of Alpha Compression may trigger invalidation of + * FBC once bit is set. These events are as follows: + * - Bit 2 - FBC_GRPH_COMP_EN register updated + * - Bit 3 - FBC_SRC_SEL register updated + * - Bit 4 - FBC_MIN_COMPRESSION register updated + * - Bit 5 - FBC_ALPHA_COMP_EN register updated + * - Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated + * - Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated + */ + addr = mmFBC_IDLE_FORCE_CLEAR_MASK; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + fbc_trigger, + FBC_IDLE_FORCE_CLEAR_MASK, + FBC_IDLE_FORCE_CLEAR_MASK); + dm_write_reg(compressor->ctx, addr, value); +} + +struct compressor *dce110_compressor_create(struct dc_context *ctx) +{ + struct dce110_compressor *cp110 = + kzalloc(sizeof(struct dce110_compressor), GFP_KERNEL); + + if (!cp110) + return NULL; + + dce110_compressor_construct(cp110, ctx); + return &cp110->base; +} + +void dce110_compressor_destroy(struct compressor **compressor) +{ + kfree(TO_DCE110_COMPRESSOR(*compressor)); + *compressor = NULL; +} + +void get_max_support_fbc_buffersize(unsigned int *max_x, unsigned int *max_y) +{ + *max_x = FBC_MAX_X; + *max_y = FBC_MAX_Y; + + /* if (m_smallLocalFrameBufferMemory == 1) + * { + * *max_x = FBC_MAX_X_SG; + * *max_y = FBC_MAX_Y_SG; + * } + */ +} + +static const struct compressor_funcs dce110_compressor_funcs = { + .power_up_fbc = dce110_compressor_power_up_fbc, + .enable_fbc = dce110_compressor_enable_fbc, + .disable_fbc = dce110_compressor_disable_fbc, + .set_fbc_invalidation_triggers = dce110_compressor_set_fbc_invalidation_triggers, + .surface_address_and_pitch = dce110_compressor_program_compressed_surface_address_and_pitch, + .is_fbc_enabled_in_hw = dce110_compressor_is_fbc_enabled_in_hw +}; + + +void dce110_compressor_construct(struct dce110_compressor *compressor, + struct dc_context *ctx) +{ + + compressor->base.options.raw = 0; + compressor->base.options.bits.FBC_SUPPORT = true; + + /* for dce 11 always use one dram channel for lpt */ + compressor->base.lpt_channels_num = 1; + compressor->base.options.bits.DUMMY_BACKEND = false; + + /* + * check if this system has more than 1 dram channel; if only 1 then lpt + * should not be supported + */ + + + compressor->base.options.bits.CLK_GATING_DISABLED = false; + + compressor->base.ctx = ctx; + compressor->base.embedded_panel_h_size = 0; + compressor->base.embedded_panel_v_size = 0; + compressor->base.memory_bus_width = ctx->asic_id.vram_width; + compressor->base.allocated_size = 0; + compressor->base.preferred_requested_size = 0; + compressor->base.min_compress_ratio = FBC_COMPRESS_RATIO_INVALID; + compressor->base.banks_num = 0; + compressor->base.raw_size = 0; + compressor->base.channel_interleave_size = 0; + compressor->base.dram_channels_num = 0; + compressor->base.lpt_channels_num = 0; + compressor->base.attached_inst = CONTROLLER_ID_UNDEFINED; + compressor->base.is_enabled = false; + compressor->base.funcs = &dce110_compressor_funcs; + +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.h new file mode 100644 index 000000000..26c7335a1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.h @@ -0,0 +1,81 @@ +/* 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_COMPRESSOR_DCE110_H__ +#define __DC_COMPRESSOR_DCE110_H__ + +#include "../inc/compressor.h" + +#define TO_DCE110_COMPRESSOR(compressor)\ + container_of(compressor, struct dce110_compressor, base) + +struct dce110_compressor_reg_offsets { + uint32_t dcp_offset; + uint32_t dmif_offset; +}; + +struct dce110_compressor { + struct compressor base; + struct dce110_compressor_reg_offsets offsets; +}; + +struct compressor *dce110_compressor_create(struct dc_context *ctx); + +void dce110_compressor_construct(struct dce110_compressor *cp110, + struct dc_context *ctx); + +void dce110_compressor_destroy(struct compressor **cp); + +/* FBC RELATED */ +void dce110_compressor_power_up_fbc(struct compressor *cp); + +void dce110_compressor_enable_fbc(struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +void dce110_compressor_disable_fbc(struct compressor *cp); + +void dce110_compressor_set_fbc_invalidation_triggers(struct compressor *cp, + uint32_t fbc_trigger); + +void dce110_compressor_program_compressed_surface_address_and_pitch( + struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce110_compressor_is_fbc_enabled_in_hw(struct compressor *cp, + uint32_t *fbc_mapped_crtc_id); + +/* LPT RELATED */ +void dce110_compressor_enable_lpt(struct compressor *cp); + +void dce110_compressor_disable_lpt(struct compressor *cp); + +void dce110_compressor_program_lpt_control(struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce110_compressor_is_lpt_enabled_in_hw(struct compressor *cp); + +void get_max_support_fbc_buffersize(unsigned int *max_x, unsigned int *max_y); + +#endif + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c new file mode 100644 index 000000000..913a1fe6b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c @@ -0,0 +1,3153 @@ +/* + * Copyright 2015 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.h" +#include "dc_bios_types.h" +#include "core_types.h" +#include "core_status.h" +#include "resource.h" +#include "dm_helpers.h" +#include "dce110_timing_generator.h" +#include "dce/dce_hwseq.h" +#include "gpio_service_interface.h" + +#include "dce110_compressor.h" + +#include "bios/bios_parser_helper.h" +#include "timing_generator.h" +#include "mem_input.h" +#include "opp.h" +#include "ipp.h" +#include "transform.h" +#include "stream_encoder.h" +#include "link_encoder.h" +#include "link_enc_cfg.h" +#include "link_hwss.h" +#include "dc_link_dp.h" +#include "dccg.h" +#include "clock_source.h" +#include "clk_mgr.h" +#include "abm.h" +#include "audio.h" +#include "reg_helper.h" +#include "panel_cntl.h" +#include "inc/link_dpcd.h" +#include "dpcd_defs.h" +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "custom_float.h" + +#include "atomfirmware.h" + +#include "dcn10/dcn10_hw_sequencer.h" + +#include "link/link_dp_trace.h" +#include "dce110_hw_sequencer.h" + +#define GAMMA_HW_POINTS_NUM 256 + +/* + * 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 +#define OLED_POST_T7_DELAY 100 +#define OLED_PRE_T11_DELAY 150 + +#define CTX \ + hws->ctx + +#define DC_LOGGER_INIT() + +#define REG(reg)\ + hws->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + hws->shifts->field_name, hws->masks->field_name + +struct dce110_hw_seq_reg_offsets { + uint32_t crtc; +}; + +static const struct dce110_hw_seq_reg_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTCV_GSL_CONTROL - mmCRTC_GSL_CONTROL), +} +}; + +#define HW_REG_BLND(reg, id)\ + (reg + reg_offsets[id].blnd) + +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + +#define MAX_WATERMARK 0xFFFF +#define SAFE_NBP_MARK 0x7FFF + +/******************************************************************************* + * Private definitions + ******************************************************************************/ +/***************************PIPE_CONTROL***********************************/ +static void dce110_init_pte(struct dc_context *ctx) +{ + uint32_t addr; + uint32_t value = 0; + uint32_t chunk_int = 0; + uint32_t chunk_mul = 0; + + addr = mmUNP_DVMM_PTE_CONTROL; + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + DVMM_PTE_CONTROL, + DVMM_USE_SINGLE_PTE); + + set_reg_field_value( + value, + 1, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE0); + + set_reg_field_value( + value, + 1, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE1); + + dm_write_reg(ctx, addr, value); + + addr = mmDVMM_PTE_REQ; + value = dm_read_reg(ctx, addr); + + chunk_int = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + chunk_mul = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + if (chunk_int != 0x4 || chunk_mul != 0x4) { + + set_reg_field_value( + value, + 255, + DVMM_PTE_REQ, + MAX_PTEREQ_TO_ISSUE); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + dm_write_reg(ctx, addr, value); + } +} +/**************************************************************************/ + +static void enable_display_pipe_clock_gating( + struct dc_context *ctx, + bool clock_gating) +{ + /*TODO*/ +} + +static bool dce110_enable_display_power_gating( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + struct dc_context *ctx = dc->ctx; + unsigned int underlay_idx = dc->res_pool->underlay_pipe_index; + + if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) + return true; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (controller_id == underlay_idx) + controller_id = CONTROLLER_ID_UNDERLAY0 - 1; + + if (power_gating != PIPE_GATING_CONTROL_INIT || controller_id == 0){ + + bp_result = dcb->funcs->enable_disp_power_gating( + dcb, controller_id + 1, cntl); + + /* Revert MASTER_UPDATE_MODE to 0 because bios sets it 2 + * by default when command table is called + * + * Bios parser accepts controller_id = 6 as indicative of + * underlay pipe in dce110. But we do not support more + * than 3. + */ + if (controller_id < CONTROLLER_ID_MAX - 1) + dm_write_reg(ctx, + HW_REG_CRTC(mmCRTC_MASTER_UPDATE_MODE, controller_id), + 0); + } + + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + dce110_init_pte(ctx); + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +} + +static void build_prescale_params(struct ipp_prescale_params *prescale_params, + const struct dc_plane_state *plane_state) +{ + prescale_params->mode = IPP_PRESCALE_MODE_FIXED_UNSIGNED; + + switch (plane_state->format) { + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + prescale_params->scale = 0x2082; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: + prescale_params->scale = 0x2020; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + prescale_params->scale = 0x2008; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + prescale_params->scale = 0x2000; + break; + default: + ASSERT(false); + break; + } +} + +static bool +dce110_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, + const struct dc_plane_state *plane_state) +{ + struct input_pixel_processor *ipp = pipe_ctx->plane_res.ipp; + const struct dc_transfer_func *tf = NULL; + struct ipp_prescale_params prescale_params = { 0 }; + bool result = true; + + if (ipp == NULL) + return false; + + if (plane_state->in_transfer_func) + tf = plane_state->in_transfer_func; + + build_prescale_params(&prescale_params, plane_state); + ipp->funcs->ipp_program_prescale(ipp, &prescale_params); + + if (plane_state->gamma_correction && + !plane_state->gamma_correction->is_identity && + dce_use_lut(plane_state->format)) + ipp->funcs->ipp_program_input_lut(ipp, plane_state->gamma_correction); + + if (tf == NULL) { + /* Default case if no input transfer function specified */ + ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_HW_sRGB); + } else if (tf->type == TF_TYPE_PREDEFINED) { + switch (tf->tf) { + case TRANSFER_FUNCTION_SRGB: + ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_HW_sRGB); + break; + case TRANSFER_FUNCTION_BT709: + ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_HW_xvYCC); + break; + case TRANSFER_FUNCTION_LINEAR: + ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_BYPASS); + break; + case TRANSFER_FUNCTION_PQ: + default: + result = false; + break; + } + } else if (tf->type == TF_TYPE_BYPASS) { + ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_BYPASS); + } else { + /*TF_TYPE_DISTRIBUTED_POINTS - Not supported in DCE 11*/ + result = false; + } + + return result; +} + +static bool convert_to_custom_float(struct pwl_result_data *rgb_resulted, + struct curve_points *arr_points, + uint32_t hw_points_num) +{ + struct custom_float_format fmt; + + struct pwl_result_data *rgb = rgb_resulted; + + uint32_t i = 0; + + fmt.exponenta_bits = 6; + fmt.mantissa_bits = 12; + fmt.sign = true; + + if (!convert_to_custom_float_format(arr_points[0].x, &fmt, + &arr_points[0].custom_float_x)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format(arr_points[0].offset, &fmt, + &arr_points[0].custom_float_offset)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format(arr_points[0].slope, &fmt, + &arr_points[0].custom_float_slope)) { + BREAK_TO_DEBUGGER(); + return false; + } + + fmt.mantissa_bits = 10; + fmt.sign = false; + + if (!convert_to_custom_float_format(arr_points[1].x, &fmt, + &arr_points[1].custom_float_x)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format(arr_points[1].y, &fmt, + &arr_points[1].custom_float_y)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format(arr_points[1].slope, &fmt, + &arr_points[1].custom_float_slope)) { + BREAK_TO_DEBUGGER(); + return false; + } + + fmt.mantissa_bits = 12; + fmt.sign = true; + + while (i != hw_points_num) { + if (!convert_to_custom_float_format(rgb->red, &fmt, + &rgb->red_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format(rgb->green, &fmt, + &rgb->green_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format(rgb->blue, &fmt, + &rgb->blue_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format(rgb->delta_red, &fmt, + &rgb->delta_red_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format(rgb->delta_green, &fmt, + &rgb->delta_green_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format(rgb->delta_blue, &fmt, + &rgb->delta_blue_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + ++rgb; + ++i; + } + + return true; +} + +#define MAX_LOW_POINT 25 +#define NUMBER_REGIONS 16 +#define NUMBER_SW_SEGMENTS 16 + +static bool +dce110_translate_regamma_to_hw_format(const struct dc_transfer_func *output_tf, + struct pwl_params *regamma_params) +{ + struct curve_points *arr_points; + struct pwl_result_data *rgb_resulted; + struct pwl_result_data *rgb; + struct pwl_result_data *rgb_plus_1; + struct fixed31_32 y_r; + struct fixed31_32 y_g; + struct fixed31_32 y_b; + struct fixed31_32 y1_min; + struct fixed31_32 y3_max; + + int32_t region_start, region_end; + uint32_t i, j, k, seg_distr[NUMBER_REGIONS], increment, start_index, hw_points; + + if (output_tf == NULL || regamma_params == NULL || output_tf->type == TF_TYPE_BYPASS) + return false; + + arr_points = regamma_params->arr_points; + rgb_resulted = regamma_params->rgb_resulted; + hw_points = 0; + + memset(regamma_params, 0, sizeof(struct pwl_params)); + + if (output_tf->tf == TRANSFER_FUNCTION_PQ) { + /* 16 segments + * segments are from 2^-11 to 2^5 + */ + region_start = -11; + region_end = region_start + NUMBER_REGIONS; + + for (i = 0; i < NUMBER_REGIONS; i++) + seg_distr[i] = 4; + + } else { + /* 10 segments + * segment is from 2^-10 to 2^1 + * We include an extra segment for range [2^0, 2^1). This is to + * ensure that colors with normalized values of 1 don't miss the + * LUT. + */ + region_start = -10; + region_end = 1; + + seg_distr[0] = 4; + seg_distr[1] = 4; + seg_distr[2] = 4; + seg_distr[3] = 4; + seg_distr[4] = 4; + seg_distr[5] = 4; + seg_distr[6] = 4; + seg_distr[7] = 4; + seg_distr[8] = 4; + seg_distr[9] = 4; + seg_distr[10] = 0; + seg_distr[11] = -1; + seg_distr[12] = -1; + seg_distr[13] = -1; + seg_distr[14] = -1; + seg_distr[15] = -1; + } + + for (k = 0; k < 16; k++) { + if (seg_distr[k] != -1) + hw_points += (1 << seg_distr[k]); + } + + j = 0; + for (k = 0; k < (region_end - region_start); k++) { + increment = NUMBER_SW_SEGMENTS / (1 << seg_distr[k]); + start_index = (region_start + k + MAX_LOW_POINT) * + NUMBER_SW_SEGMENTS; + for (i = start_index; i < start_index + NUMBER_SW_SEGMENTS; + i += increment) { + if (j == hw_points - 1) + break; + rgb_resulted[j].red = output_tf->tf_pts.red[i]; + rgb_resulted[j].green = output_tf->tf_pts.green[i]; + rgb_resulted[j].blue = output_tf->tf_pts.blue[i]; + j++; + } + } + + /* last point */ + start_index = (region_end + MAX_LOW_POINT) * NUMBER_SW_SEGMENTS; + rgb_resulted[hw_points - 1].red = output_tf->tf_pts.red[start_index]; + rgb_resulted[hw_points - 1].green = output_tf->tf_pts.green[start_index]; + rgb_resulted[hw_points - 1].blue = output_tf->tf_pts.blue[start_index]; + + arr_points[0].x = dc_fixpt_pow(dc_fixpt_from_int(2), + dc_fixpt_from_int(region_start)); + arr_points[1].x = dc_fixpt_pow(dc_fixpt_from_int(2), + dc_fixpt_from_int(region_end)); + + y_r = rgb_resulted[0].red; + y_g = rgb_resulted[0].green; + y_b = rgb_resulted[0].blue; + + y1_min = dc_fixpt_min(y_r, dc_fixpt_min(y_g, y_b)); + + arr_points[0].y = y1_min; + arr_points[0].slope = dc_fixpt_div(arr_points[0].y, + arr_points[0].x); + + y_r = rgb_resulted[hw_points - 1].red; + y_g = rgb_resulted[hw_points - 1].green; + y_b = rgb_resulted[hw_points - 1].blue; + + /* see comment above, m_arrPoints[1].y should be the Y value for the + * region end (m_numOfHwPoints), not last HW point(m_numOfHwPoints - 1) + */ + y3_max = dc_fixpt_max(y_r, dc_fixpt_max(y_g, y_b)); + + arr_points[1].y = y3_max; + + arr_points[1].slope = dc_fixpt_zero; + + if (output_tf->tf == TRANSFER_FUNCTION_PQ) { + /* for PQ, we want to have a straight line from last HW X point, + * and the slope to be such that we hit 1.0 at 10000 nits. + */ + const struct fixed31_32 end_value = dc_fixpt_from_int(125); + + arr_points[1].slope = dc_fixpt_div( + dc_fixpt_sub(dc_fixpt_one, arr_points[1].y), + dc_fixpt_sub(end_value, arr_points[1].x)); + } + + regamma_params->hw_points_num = hw_points; + + k = 0; + for (i = 1; i < 16; i++) { + if (seg_distr[k] != -1) { + regamma_params->arr_curve_points[k].segments_num = seg_distr[k]; + regamma_params->arr_curve_points[i].offset = + regamma_params->arr_curve_points[k].offset + (1 << seg_distr[k]); + } + k++; + } + + if (seg_distr[k] != -1) + regamma_params->arr_curve_points[k].segments_num = seg_distr[k]; + + rgb = rgb_resulted; + rgb_plus_1 = rgb_resulted + 1; + + i = 1; + + while (i != hw_points + 1) { + if (dc_fixpt_lt(rgb_plus_1->red, rgb->red)) + rgb_plus_1->red = rgb->red; + if (dc_fixpt_lt(rgb_plus_1->green, rgb->green)) + rgb_plus_1->green = rgb->green; + if (dc_fixpt_lt(rgb_plus_1->blue, rgb->blue)) + rgb_plus_1->blue = rgb->blue; + + rgb->delta_red = dc_fixpt_sub(rgb_plus_1->red, rgb->red); + rgb->delta_green = dc_fixpt_sub(rgb_plus_1->green, rgb->green); + rgb->delta_blue = dc_fixpt_sub(rgb_plus_1->blue, rgb->blue); + + ++rgb_plus_1; + ++rgb; + ++i; + } + + convert_to_custom_float(rgb_resulted, arr_points, hw_points); + + return true; +} + +static bool +dce110_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, + const struct dc_stream_state *stream) +{ + struct transform *xfm = pipe_ctx->plane_res.xfm; + + xfm->funcs->opp_power_on_regamma_lut(xfm, true); + xfm->regamma_params.hw_points_num = GAMMA_HW_POINTS_NUM; + + if (stream->out_transfer_func && + stream->out_transfer_func->type == TF_TYPE_PREDEFINED && + stream->out_transfer_func->tf == TRANSFER_FUNCTION_SRGB) { + xfm->funcs->opp_set_regamma_mode(xfm, OPP_REGAMMA_SRGB); + } else if (dce110_translate_regamma_to_hw_format(stream->out_transfer_func, + &xfm->regamma_params)) { + xfm->funcs->opp_program_regamma_pwl(xfm, &xfm->regamma_params); + xfm->funcs->opp_set_regamma_mode(xfm, OPP_REGAMMA_USER); + } else { + xfm->funcs->opp_set_regamma_mode(xfm, OPP_REGAMMA_BYPASS); + } + + xfm->funcs->opp_power_on_regamma_lut(xfm, false); + + return true; +} + +void dce110_update_info_frame(struct pipe_ctx *pipe_ctx) +{ + bool is_hdmi_tmds; + bool is_dp; + + ASSERT(pipe_ctx->stream); + + if (pipe_ctx->stream_res.stream_enc == NULL) + return; /* this is not root pipe */ + + is_hdmi_tmds = dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal); + is_dp = dc_is_dp_signal(pipe_ctx->stream->signal); + + if (!is_hdmi_tmds && !is_dp) + return; + + if (is_hdmi_tmds) + pipe_ctx->stream_res.stream_enc->funcs->update_hdmi_info_packets( + pipe_ctx->stream_res.stream_enc, + &pipe_ctx->stream_res.encoder_info_frame); + else + pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets( + pipe_ctx->stream_res.stream_enc, + &pipe_ctx->stream_res.encoder_info_frame); +} + +void dce110_enable_stream(struct pipe_ctx *pipe_ctx) +{ + enum dc_lane_count lane_count = + pipe_ctx->stream->link->cur_link_settings.lane_count; + struct dc_crtc_timing *timing = &pipe_ctx->stream->timing; + struct dc_link *link = pipe_ctx->stream->link; + const struct dc *dc = link->dc; + const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); + uint32_t active_total_with_borders; + uint32_t early_control = 0; + struct timing_generator *tg = pipe_ctx->stream_res.tg; + + link_hwss->setup_stream_encoder(pipe_ctx); + + dc->hwss.update_info_frame(pipe_ctx); + + /* enable early control to avoid corruption on DP monitor*/ + active_total_with_borders = + timing->h_addressable + + timing->h_border_left + + timing->h_border_right; + + if (lane_count != 0) + early_control = active_total_with_borders % lane_count; + + if (early_control == 0) + early_control = lane_count; + + tg->funcs->set_early_control(tg, early_control); +} + +static enum bp_result link_transmitter_control( + struct dc_bios *bios, + struct bp_transmitter_control *cntl) +{ + enum bp_result result; + + result = bios->funcs->transmitter_control(bios, cntl); + + return result; +} + +/* + * @brief + * eDP only. + */ +void dce110_edp_wait_for_hpd_ready( + struct dc_link *link, + bool power_up) +{ + struct dc_context *ctx = link->ctx; + struct graphics_object_id connector = link->link_enc->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(ctx->dc_bios, connector, ctx->gpio_service); + + if (!hpd) { + BREAK_TO_DEBUGGER(); + return; + } + + if (link != NULL) { + if (link->panel_config.pps.extra_t3_ms > 0) { + int extra_t3_in_ms = link->panel_config.pps.extra_t3_ms; + + msleep(extra_t3_in_ms); + } + } + + 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) { + DC_LOG_WARNING( + "%s: wait timed out!\n", __func__); + } +} + +void dce110_edp_power_control( + struct dc_link *link, + bool power_up) +{ + struct dc_context *ctx = link->ctx; + struct bp_transmitter_control cntl = { 0 }; + enum bp_result bp_result; + uint8_t panel_instance; + + + if (dal_graphics_object_id_get_connector_id(link->link_enc->connector) + != CONNECTOR_ID_EDP) { + BREAK_TO_DEBUGGER(); + return; + } + + if (!link->panel_cntl) + return; + if (power_up != + link->panel_cntl->funcs->is_panel_powered_on(link->panel_cntl)) { + + unsigned long long current_ts = dm_get_timestamp(ctx); + unsigned long long time_since_edp_poweroff_ms = + div64_u64(dm_get_elapse_time_in_ns( + ctx, + current_ts, + dp_trace_get_edp_poweroff_timestamp(link)), 1000000); + unsigned long long time_since_edp_poweron_ms = + div64_u64(dm_get_elapse_time_in_ns( + ctx, + current_ts, + dp_trace_get_edp_poweron_timestamp(link)), 1000000); + DC_LOG_HW_RESUME_S3( + "%s: transition: power_up=%d current_ts=%llu edp_poweroff=%llu edp_poweron=%llu time_since_edp_poweroff_ms=%llu time_since_edp_poweron_ms=%llu", + __func__, + power_up, + current_ts, + dp_trace_get_edp_poweroff_timestamp(link), + dp_trace_get_edp_poweron_timestamp(link), + time_since_edp_poweroff_ms, + time_since_edp_poweron_ms); + + /* Send VBIOS command to prompt eDP panel power */ + if (power_up) { + /* edp requires a min of 500ms from LCDVDD off to on */ + unsigned long long remaining_min_edp_poweroff_time_ms = 500; + + /* add time defined by a patch, if any (usually patch extra_t12_ms is 0) */ + if (link->local_sink != NULL) + remaining_min_edp_poweroff_time_ms += + link->panel_config.pps.extra_t12_ms; + + /* Adjust remaining_min_edp_poweroff_time_ms if this is not the first time. */ + if (dp_trace_get_edp_poweroff_timestamp(link) != 0) { + if (time_since_edp_poweroff_ms < remaining_min_edp_poweroff_time_ms) + remaining_min_edp_poweroff_time_ms = + remaining_min_edp_poweroff_time_ms - time_since_edp_poweroff_ms; + else + remaining_min_edp_poweroff_time_ms = 0; + } + + if (remaining_min_edp_poweroff_time_ms) { + DC_LOG_HW_RESUME_S3( + "%s: remaining_min_edp_poweroff_time_ms=%llu: begin wait.\n", + __func__, remaining_min_edp_poweroff_time_ms); + msleep(remaining_min_edp_poweroff_time_ms); + DC_LOG_HW_RESUME_S3( + "%s: remaining_min_edp_poweroff_time_ms=%llu: end wait.\n", + __func__, remaining_min_edp_poweroff_time_ms); + dm_output_to_console("%s: wait %lld ms to power on eDP.\n", + __func__, remaining_min_edp_poweroff_time_ms); + } else { + DC_LOG_HW_RESUME_S3( + "%s: remaining_min_edp_poweroff_time_ms=%llu: no wait required.\n", + __func__, remaining_min_edp_poweroff_time_ms); + } + } + + DC_LOG_HW_RESUME_S3( + "%s: BEGIN: Panel Power action: %s\n", + __func__, (power_up ? "On":"Off")); + + cntl.action = power_up ? + TRANSMITTER_CONTROL_POWER_ON : + TRANSMITTER_CONTROL_POWER_OFF; + cntl.transmitter = link->link_enc->transmitter; + cntl.connector_obj_id = link->link_enc->connector; + cntl.coherent = false; + cntl.lanes_number = LANE_COUNT_FOUR; + cntl.hpd_sel = link->link_enc->hpd_source; + panel_instance = link->panel_cntl->inst; + + if (ctx->dc->ctx->dmub_srv && + ctx->dc->debug.dmub_command_table) { + if (cntl.action == TRANSMITTER_CONTROL_POWER_ON) + bp_result = ctx->dc_bios->funcs->enable_lvtma_control(ctx->dc_bios, + LVTMA_CONTROL_POWER_ON, + panel_instance); + else + bp_result = ctx->dc_bios->funcs->enable_lvtma_control(ctx->dc_bios, + LVTMA_CONTROL_POWER_OFF, + panel_instance); + } + + bp_result = link_transmitter_control(ctx->dc_bios, &cntl); + + DC_LOG_HW_RESUME_S3( + "%s: END: Panel Power action: %s bp_result=%u\n", + __func__, (power_up ? "On":"Off"), + bp_result); + + dp_trace_set_edp_power_timestamp(link, power_up); + + DC_LOG_HW_RESUME_S3( + "%s: updated values: edp_poweroff=%llu edp_poweron=%llu\n", + __func__, + dp_trace_get_edp_poweroff_timestamp(link), + dp_trace_get_edp_poweron_timestamp(link)); + + if (bp_result != BP_RESULT_OK) + DC_LOG_ERROR( + "%s: Panel Power bp_result: %d\n", + __func__, bp_result); + } else { + DC_LOG_HW_RESUME_S3( + "%s: Skipping Panel Power action: %s\n", + __func__, (power_up ? "On":"Off")); + } +} + +void dce110_edp_wait_for_T12( + struct dc_link *link) +{ + struct dc_context *ctx = link->ctx; + + if (dal_graphics_object_id_get_connector_id(link->link_enc->connector) + != CONNECTOR_ID_EDP) { + BREAK_TO_DEBUGGER(); + return; + } + + if (!link->panel_cntl) + return; + + if (!link->panel_cntl->funcs->is_panel_powered_on(link->panel_cntl) && + dp_trace_get_edp_poweroff_timestamp(link) != 0) { + unsigned int t12_duration = 500; // Default T12 as per spec + unsigned long long current_ts = dm_get_timestamp(ctx); + unsigned long long time_since_edp_poweroff_ms = + div64_u64(dm_get_elapse_time_in_ns( + ctx, + current_ts, + dp_trace_get_edp_poweroff_timestamp(link)), 1000000); + + t12_duration += link->panel_config.pps.extra_t12_ms; // Add extra T12 + + if (time_since_edp_poweroff_ms < t12_duration) + msleep(t12_duration - time_since_edp_poweroff_ms); + } +} + +/*todo: cloned in stream enc, fix*/ +/* + * @brief + * eDP only. Control the backlight of the eDP panel + */ +void dce110_edp_backlight_control( + struct dc_link *link, + bool enable) +{ + struct dc_context *ctx = link->ctx; + struct bp_transmitter_control cntl = { 0 }; + uint8_t panel_instance; + unsigned int pre_T11_delay = OLED_PRE_T11_DELAY; + unsigned int post_T7_delay = OLED_POST_T7_DELAY; + + if (dal_graphics_object_id_get_connector_id(link->link_enc->connector) + != CONNECTOR_ID_EDP) { + BREAK_TO_DEBUGGER(); + return; + } + + if (link->panel_cntl) { + bool is_backlight_on = link->panel_cntl->funcs->is_panel_backlight_on(link->panel_cntl); + + if ((enable && is_backlight_on) || (!enable && !is_backlight_on)) { + DC_LOG_HW_RESUME_S3( + "%s: panel already powered up/off. Do nothing.\n", + __func__); + return; + } + } + + /* Send VBIOS command to control eDP panel backlight */ + + DC_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 = link->link_enc->transmitter; + cntl.connector_obj_id = link->link_enc->connector; + /*todo: unhardcode*/ + cntl.lanes_number = LANE_COUNT_FOUR; + cntl.hpd_sel = link->link_enc->hpd_source; + cntl.signal = SIGNAL_TYPE_EDP; + + /* 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); */ + /*edp 1.2*/ + panel_instance = link->panel_cntl->inst; + + if (cntl.action == TRANSMITTER_CONTROL_BACKLIGHT_ON) { + if (!link->dc->config.edp_no_power_sequencing) + /* + * Sometimes, DP receiver chip power-controlled externally by an + * Embedded Controller could be treated and used as eDP, + * if it drives mobile display. In this case, + * we shouldn't be doing power-sequencing, hence we can skip + * waiting for T7-ready. + */ + edp_receiver_ready_T7(link); + else + DC_LOG_DC("edp_receiver_ready_T7 skipped\n"); + } + + if (ctx->dc->ctx->dmub_srv && + ctx->dc->debug.dmub_command_table) { + if (cntl.action == TRANSMITTER_CONTROL_BACKLIGHT_ON) + ctx->dc_bios->funcs->enable_lvtma_control(ctx->dc_bios, + LVTMA_CONTROL_LCD_BLON, + panel_instance); + else + ctx->dc_bios->funcs->enable_lvtma_control(ctx->dc_bios, + LVTMA_CONTROL_LCD_BLOFF, + panel_instance); + } + + link_transmitter_control(ctx->dc_bios, &cntl); + + if (enable && link->dpcd_sink_ext_caps.bits.oled) { + post_T7_delay += link->panel_config.pps.extra_post_t7_ms; + msleep(post_T7_delay); + } + + if (link->dpcd_sink_ext_caps.bits.oled || + link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 || + link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1) + dc_link_backlight_enable_aux(link, enable); + + /*edp 1.2*/ + if (cntl.action == TRANSMITTER_CONTROL_BACKLIGHT_OFF) { + if (!link->dc->config.edp_no_power_sequencing) + /* + * Sometimes, DP receiver chip power-controlled externally by an + * Embedded Controller could be treated and used as eDP, + * if it drives mobile display. In this case, + * we shouldn't be doing power-sequencing, hence we can skip + * waiting for T9-ready. + */ + edp_add_delay_for_T9(link); + else + DC_LOG_DC("edp_receiver_ready_T9 skipped\n"); + } + + if (!enable && link->dpcd_sink_ext_caps.bits.oled) { + pre_T11_delay += link->panel_config.pps.extra_pre_t11_ms; + msleep(pre_T11_delay); + } +} + +void dce110_enable_audio_stream(struct pipe_ctx *pipe_ctx) +{ + /* notify audio driver for audio modes of monitor */ + struct dc *dc; + struct clk_mgr *clk_mgr; + unsigned int i, num_audio = 1; + const struct link_hwss *link_hwss; + + if (!pipe_ctx->stream) + return; + + dc = pipe_ctx->stream->ctx->dc; + clk_mgr = dc->clk_mgr; + link_hwss = get_link_hwss(pipe_ctx->stream->link, &pipe_ctx->link_res); + + if (pipe_ctx->stream_res.audio && pipe_ctx->stream_res.audio->enabled == true) + return; + + if (pipe_ctx->stream_res.audio) { + for (i = 0; i < MAX_PIPES; i++) { + /*current_state not updated yet*/ + if (dc->current_state->res_ctx.pipe_ctx[i].stream_res.audio != NULL) + num_audio++; + } + + pipe_ctx->stream_res.audio->funcs->az_enable(pipe_ctx->stream_res.audio); + + if (num_audio >= 1 && clk_mgr->funcs->enable_pme_wa) + /*this is the first audio. apply the PME w/a in order to wake AZ from D3*/ + clk_mgr->funcs->enable_pme_wa(clk_mgr); + + link_hwss->enable_audio_packet(pipe_ctx); + + if (pipe_ctx->stream_res.audio) + pipe_ctx->stream_res.audio->enabled = true; + } +} + +void dce110_disable_audio_stream(struct pipe_ctx *pipe_ctx) +{ + struct dc *dc; + struct clk_mgr *clk_mgr; + const struct link_hwss *link_hwss; + + if (!pipe_ctx || !pipe_ctx->stream) + return; + + dc = pipe_ctx->stream->ctx->dc; + clk_mgr = dc->clk_mgr; + link_hwss = get_link_hwss(pipe_ctx->stream->link, &pipe_ctx->link_res); + + if (pipe_ctx->stream_res.audio && pipe_ctx->stream_res.audio->enabled == false) + return; + + link_hwss->disable_audio_packet(pipe_ctx); + + if (pipe_ctx->stream_res.audio) { + pipe_ctx->stream_res.audio->enabled = false; + + if (clk_mgr->funcs->enable_pme_wa) + /*this is the first audio. apply the PME w/a in order to wake AZ from D3*/ + clk_mgr->funcs->enable_pme_wa(clk_mgr); + + /* TODO: notify audio driver for if audio modes list changed + * add audio mode list change flag */ + /* dal_audio_disable_azalia_audio_jack_presence(stream->audio, + * stream->stream_engine_id); + */ + } +} + +void dce110_disable_stream(struct pipe_ctx *pipe_ctx) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->link; + struct dc *dc = pipe_ctx->stream->ctx->dc; + const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); + + if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal)) { + pipe_ctx->stream_res.stream_enc->funcs->stop_hdmi_info_packets( + pipe_ctx->stream_res.stream_enc); + pipe_ctx->stream_res.stream_enc->funcs->hdmi_reset_stream_attribute( + pipe_ctx->stream_res.stream_enc); + } + + if (is_dp_128b_132b_signal(pipe_ctx)) { + pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->stop_dp_info_packets( + pipe_ctx->stream_res.hpo_dp_stream_enc); + } else if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->stop_dp_info_packets( + pipe_ctx->stream_res.stream_enc); + + dc->hwss.disable_audio_stream(pipe_ctx); + + link_hwss->reset_stream_encoder(pipe_ctx); + + if (is_dp_128b_132b_signal(pipe_ctx)) { + /* TODO: This looks like a bug to me as we are disabling HPO IO when + * we are just disabling a single HPO stream. Shouldn't we disable HPO + * HW control only when HPOs for all streams are disabled? + */ + if (pipe_ctx->stream->ctx->dc->hwseq->funcs.setup_hpo_hw_control) + pipe_ctx->stream->ctx->dc->hwseq->funcs.setup_hpo_hw_control( + pipe_ctx->stream->ctx->dc->hwseq, false); + } +} + +void dce110_unblank_stream(struct pipe_ctx *pipe_ctx, + struct dc_link_settings *link_settings) +{ + struct encoder_unblank_param params = { { 0 } }; + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->link; + struct dce_hwseq *hws = link->dc->hwseq; + + /* only 3 items below are used by unblank */ + params.timing = pipe_ctx->stream->timing; + params.link_settings.link_rate = link_settings->link_rate; + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(link, pipe_ctx->stream_res.stream_enc, ¶ms); + + if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) { + hws->funcs.edp_backlight_control(link, true); + } +} + +void dce110_blank_stream(struct pipe_ctx *pipe_ctx) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->link; + struct dce_hwseq *hws = link->dc->hwseq; + + if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) { + hws->funcs.edp_backlight_control(link, false); + link->dc->hwss.set_abm_immediate_disable(pipe_ctx); + } + + if (is_dp_128b_132b_signal(pipe_ctx)) { + /* TODO - DP2.0 HW: Set ODM mode in dp hpo encoder here */ + pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->dp_blank( + pipe_ctx->stream_res.hpo_dp_stream_enc); + } else if (dc_is_dp_signal(pipe_ctx->stream->signal)) { + pipe_ctx->stream_res.stream_enc->funcs->dp_blank(link, pipe_ctx->stream_res.stream_enc); + + if (!dc_is_embedded_signal(pipe_ctx->stream->signal)) { + /* + * After output is idle pattern some sinks need time to recognize the stream + * has changed or they enter protection state and hang. + */ + msleep(60); + } else if (pipe_ctx->stream->signal == SIGNAL_TYPE_EDP) { + if (!link->dc->config.edp_no_power_sequencing) { + /* + * Sometimes, DP receiver chip power-controlled externally by an + * Embedded Controller could be treated and used as eDP, + * if it drives mobile display. In this case, + * we shouldn't be doing power-sequencing, hence we can skip + * waiting for T9-ready. + */ + edp_receiver_ready_T9(link); + } + } + } + +} + + +void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) +{ + if (pipe_ctx != NULL && pipe_ctx->stream_res.stream_enc != NULL) + pipe_ctx->stream_res.stream_enc->funcs->set_avmute(pipe_ctx->stream_res.stream_enc, enable); +} + +static enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id) +{ + switch (crtc_id) { + case CONTROLLER_ID_D0: + return DTO_SOURCE_ID0; + case CONTROLLER_ID_D1: + return DTO_SOURCE_ID1; + case CONTROLLER_ID_D2: + return DTO_SOURCE_ID2; + case CONTROLLER_ID_D3: + return DTO_SOURCE_ID3; + case CONTROLLER_ID_D4: + return DTO_SOURCE_ID4; + case CONTROLLER_ID_D5: + return DTO_SOURCE_ID5; + default: + return DTO_SOURCE_UNKNOWN; + } +} + +static void build_audio_output( + struct dc_state *state, + const struct pipe_ctx *pipe_ctx, + struct audio_output *audio_output) +{ + const struct dc_stream_state *stream = pipe_ctx->stream; + audio_output->engine_id = pipe_ctx->stream_res.stream_enc->id; + + audio_output->signal = pipe_ctx->stream->signal; + + /* audio_crtc_info */ + + audio_output->crtc_info.h_total = + stream->timing.h_total; + + /* + * Audio packets are sent during actual CRTC blank physical signal, we + * need to specify actual active signal portion + */ + audio_output->crtc_info.h_active = + stream->timing.h_addressable + + stream->timing.h_border_left + + stream->timing.h_border_right; + + audio_output->crtc_info.v_active = + stream->timing.v_addressable + + stream->timing.v_border_top + + stream->timing.v_border_bottom; + + audio_output->crtc_info.pixel_repetition = 1; + + audio_output->crtc_info.interlaced = + stream->timing.flags.INTERLACE; + + audio_output->crtc_info.refresh_rate = + (stream->timing.pix_clk_100hz*100)/ + (stream->timing.h_total*stream->timing.v_total); + + audio_output->crtc_info.color_depth = + stream->timing.display_color_depth; + + audio_output->crtc_info.requested_pixel_clock_100Hz = + pipe_ctx->stream_res.pix_clk_params.requested_pix_clk_100hz; + + audio_output->crtc_info.calculated_pixel_clock_100Hz = + pipe_ctx->stream_res.pix_clk_params.requested_pix_clk_100hz; + +/*for HDMI, audio ACR is with deep color ratio factor*/ + if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal) && + audio_output->crtc_info.requested_pixel_clock_100Hz == + (stream->timing.pix_clk_100hz)) { + if (pipe_ctx->stream_res.pix_clk_params.pixel_encoding == PIXEL_ENCODING_YCBCR420) { + audio_output->crtc_info.requested_pixel_clock_100Hz = + audio_output->crtc_info.requested_pixel_clock_100Hz/2; + audio_output->crtc_info.calculated_pixel_clock_100Hz = + pipe_ctx->stream_res.pix_clk_params.requested_pix_clk_100hz/2; + + } + } + + if (state->clk_mgr && + (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT || + pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)) { + audio_output->pll_info.dp_dto_source_clock_in_khz = + state->clk_mgr->funcs->get_dp_ref_clk_frequency( + state->clk_mgr); + } + + audio_output->pll_info.feed_back_divider = + pipe_ctx->pll_settings.feedback_divider; + + audio_output->pll_info.dto_source = + translate_to_dto_source( + pipe_ctx->stream_res.tg->inst + 1); + + /* TODO hard code to enable for now. Need get from stream */ + audio_output->pll_info.ss_enabled = true; + + audio_output->pll_info.ss_percentage = + pipe_ctx->pll_settings.ss_percentage; +} + +static void program_scaler(const struct dc *dc, + const struct pipe_ctx *pipe_ctx) +{ + struct tg_color color = {0}; + + /* TOFPGA */ + if (pipe_ctx->plane_res.xfm->funcs->transform_set_pixel_storage_depth == NULL) + return; + + if (dc->debug.visual_confirm == VISUAL_CONFIRM_SURFACE) + get_surface_visual_confirm_color(pipe_ctx, &color); + else + color_space_to_black_color(dc, + pipe_ctx->stream->output_color_space, + &color); + + pipe_ctx->plane_res.xfm->funcs->transform_set_pixel_storage_depth( + pipe_ctx->plane_res.xfm, + pipe_ctx->plane_res.scl_data.lb_params.depth, + &pipe_ctx->stream->bit_depth_params); + + if (pipe_ctx->stream_res.tg->funcs->set_overscan_blank_color) { + /* + * The way 420 is packed, 2 channels carry Y component, 1 channel + * alternate between Cb and Cr, so both channels need the pixel + * value for Y + */ + if (pipe_ctx->stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) + color.color_r_cr = color.color_g_y; + + pipe_ctx->stream_res.tg->funcs->set_overscan_blank_color( + pipe_ctx->stream_res.tg, + &color); + } + + pipe_ctx->plane_res.xfm->funcs->transform_set_scaler(pipe_ctx->plane_res.xfm, + &pipe_ctx->plane_res.scl_data); +} + +static enum dc_status dce110_enable_stream_timing( + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct dc *dc) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct pipe_ctx *pipe_ctx_old = &dc->current_state->res_ctx. + pipe_ctx[pipe_ctx->pipe_idx]; + struct tg_color black_color = {0}; + + if (!pipe_ctx_old->stream) { + + /* program blank color */ + color_space_to_black_color(dc, + stream->output_color_space, &black_color); + pipe_ctx->stream_res.tg->funcs->set_blank_color( + pipe_ctx->stream_res.tg, + &black_color); + + /* + * Must blank CRTC after disabling power gating and before any + * programming, otherwise CRTC will be hung in bad state + */ + pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, true); + + if (false == pipe_ctx->clock_source->funcs->program_pix_clk( + pipe_ctx->clock_source, + &pipe_ctx->stream_res.pix_clk_params, + dp_get_link_encoding_format(&pipe_ctx->link_config.dp_link_settings), + &pipe_ctx->pll_settings)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + + if (dc_is_hdmi_tmds_signal(stream->signal)) { + stream->link->phy_state.symclk_ref_cnts.otg = 1; + if (stream->link->phy_state.symclk_state == SYMCLK_OFF_TX_OFF) + stream->link->phy_state.symclk_state = SYMCLK_ON_TX_OFF; + else + stream->link->phy_state.symclk_state = SYMCLK_ON_TX_ON; + } + + pipe_ctx->stream_res.tg->funcs->program_timing( + pipe_ctx->stream_res.tg, + &stream->timing, + 0, + 0, + 0, + 0, + pipe_ctx->stream->signal, + true); + } + + if (!pipe_ctx_old->stream) { + if (false == pipe_ctx->stream_res.tg->funcs->enable_crtc( + pipe_ctx->stream_res.tg)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + } + + return DC_OK; +} + +static enum dc_status apply_single_controller_ctx_to_hw( + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct dc *dc) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->link; + struct drr_params params = {0}; + unsigned int event_triggers = 0; + struct pipe_ctx *odm_pipe = pipe_ctx->next_odm_pipe; + struct dce_hwseq *hws = dc->hwseq; + const struct link_hwss *link_hwss = get_link_hwss( + link, &pipe_ctx->link_res); + + + if (hws->funcs.disable_stream_gating) { + hws->funcs.disable_stream_gating(dc, pipe_ctx); + } + + if (pipe_ctx->stream_res.audio != NULL) { + struct audio_output audio_output; + + build_audio_output(context, pipe_ctx, &audio_output); + + link_hwss->setup_audio_output(pipe_ctx, &audio_output, + pipe_ctx->stream_res.audio->inst); + + pipe_ctx->stream_res.audio->funcs->az_configure( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &pipe_ctx->stream->audio_info); + } + + /* make sure no pipes syncd to the pipe being enabled */ + if (!pipe_ctx->stream->apply_seamless_boot_optimization && dc->config.use_pipe_ctx_sync_logic) + check_syncd_pipes_for_disabled_master_pipe(dc, context, pipe_ctx->pipe_idx); + + pipe_ctx->stream_res.opp->funcs->opp_program_fmt( + pipe_ctx->stream_res.opp, + &stream->bit_depth_params, + &stream->clamping); + + pipe_ctx->stream_res.opp->funcs->opp_set_dyn_expansion( + pipe_ctx->stream_res.opp, + COLOR_SPACE_YCBCR601, + stream->timing.display_color_depth, + stream->signal); + + while (odm_pipe) { + odm_pipe->stream_res.opp->funcs->opp_set_dyn_expansion( + odm_pipe->stream_res.opp, + COLOR_SPACE_YCBCR601, + stream->timing.display_color_depth, + stream->signal); + + odm_pipe->stream_res.opp->funcs->opp_program_fmt( + odm_pipe->stream_res.opp, + &stream->bit_depth_params, + &stream->clamping); + odm_pipe = odm_pipe->next_odm_pipe; + } + + /* DCN3.1 FPGA Workaround + * Need to enable HPO DP Stream Encoder before setting OTG master enable. + * To do so, move calling function enable_stream_timing to only be done AFTER calling + * function core_link_enable_stream + */ + if (!(hws->wa.dp_hpo_and_otg_sequence && is_dp_128b_132b_signal(pipe_ctx))) + /* */ + /* Do not touch stream timing on seamless boot optimization. */ + if (!pipe_ctx->stream->apply_seamless_boot_optimization) + hws->funcs.enable_stream_timing(pipe_ctx, context, dc); + + if (hws->funcs.setup_vupdate_interrupt) + hws->funcs.setup_vupdate_interrupt(dc, pipe_ctx); + + params.vertical_total_min = stream->adjust.v_total_min; + params.vertical_total_max = stream->adjust.v_total_max; + if (pipe_ctx->stream_res.tg->funcs->set_drr) + pipe_ctx->stream_res.tg->funcs->set_drr( + pipe_ctx->stream_res.tg, ¶ms); + + // DRR should set trigger event to monitor surface update event + if (stream->adjust.v_total_min != 0 && stream->adjust.v_total_max != 0) + event_triggers = 0x80; + /* Event triggers and num frames initialized for DRR, but can be + * later updated for PSR use. Note DRR trigger events are generated + * regardless of whether num frames met. + */ + if (pipe_ctx->stream_res.tg->funcs->set_static_screen_control) + pipe_ctx->stream_res.tg->funcs->set_static_screen_control( + pipe_ctx->stream_res.tg, event_triggers, 2); + + if (!dc_is_virtual_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->dig_connect_to_otg( + pipe_ctx->stream_res.stream_enc, + pipe_ctx->stream_res.tg->inst); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + dp_source_sequence_trace(link, DPCD_SOURCE_SEQ_AFTER_CONNECT_DIG_FE_OTG); + + if (!stream->dpms_off) + core_link_enable_stream(context, pipe_ctx); + + /* DCN3.1 FPGA Workaround + * Need to enable HPO DP Stream Encoder before setting OTG master enable. + * To do so, move calling function enable_stream_timing to only be done AFTER calling + * function core_link_enable_stream + */ + if (hws->wa.dp_hpo_and_otg_sequence && is_dp_128b_132b_signal(pipe_ctx)) { + if (!pipe_ctx->stream->apply_seamless_boot_optimization) + hws->funcs.enable_stream_timing(pipe_ctx, context, dc); + } + + pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != NULL; + + /* Phantom and main stream share the same link (because the stream + * is constructed with the same sink). Make sure not to override + * and link programming on the main. + */ + if (pipe_ctx->stream->mall_stream_config.type != SUBVP_PHANTOM) { + pipe_ctx->stream->link->psr_settings.psr_feature_enabled = false; + } + return DC_OK; +} + +/******************************************************************************/ + +static void power_down_encoders(struct dc *dc) +{ + int i; + + for (i = 0; i < dc->link_count; i++) { + enum signal_type signal = dc->links[i]->connector_signal; + + dc_link_blank_dp_stream(dc->links[i], false); + + if (signal != SIGNAL_TYPE_EDP) + signal = SIGNAL_TYPE_NONE; + + if (dc->links[i]->ep_type == DISPLAY_ENDPOINT_PHY) + dc->links[i]->link_enc->funcs->disable_output( + dc->links[i]->link_enc, signal); + + dc->links[i]->link_status.link_active = false; + memset(&dc->links[i]->cur_link_settings, 0, + sizeof(dc->links[i]->cur_link_settings)); + } +} + +static void power_down_controllers(struct dc *dc) +{ + int i; + + for (i = 0; i < dc->res_pool->timing_generator_count; i++) { + dc->res_pool->timing_generators[i]->funcs->disable_crtc( + dc->res_pool->timing_generators[i]); + } +} + +static void power_down_clock_sources(struct dc *dc) +{ + int i; + + if (dc->res_pool->dp_clock_source->funcs->cs_power_down( + dc->res_pool->dp_clock_source) == false) + dm_error("Failed to power down pll! (dp clk src)\n"); + + for (i = 0; i < dc->res_pool->clk_src_count; i++) { + if (dc->res_pool->clock_sources[i]->funcs->cs_power_down( + dc->res_pool->clock_sources[i]) == false) + dm_error("Failed to power down pll! (clk src index=%d)\n", i); + } +} + +static void power_down_all_hw_blocks(struct dc *dc) +{ + power_down_encoders(dc); + + power_down_controllers(dc); + + power_down_clock_sources(dc); + + if (dc->fbc_compressor) + dc->fbc_compressor->funcs->disable_fbc(dc->fbc_compressor); +} + +static void disable_vga_and_power_gate_all_controllers( + struct dc *dc) +{ + int i; + struct timing_generator *tg; + struct dc_context *ctx = dc->ctx; + + for (i = 0; i < dc->res_pool->timing_generator_count; i++) { + tg = dc->res_pool->timing_generators[i]; + + if (tg->funcs->disable_vga) + tg->funcs->disable_vga(tg); + } + for (i = 0; i < dc->res_pool->pipe_count; i++) { + /* Enable CLOCK gating for each pipe BEFORE controller + * powergating. */ + enable_display_pipe_clock_gating(ctx, + true); + + dc->current_state->res_ctx.pipe_ctx[i].pipe_idx = i; + dc->hwss.disable_plane(dc, + &dc->current_state->res_ctx.pipe_ctx[i]); + } +} + + +static void get_edp_streams(struct dc_state *context, + struct dc_stream_state **edp_streams, + int *edp_stream_num) +{ + int i; + + *edp_stream_num = 0; + for (i = 0; i < context->stream_count; i++) { + if (context->streams[i]->signal == SIGNAL_TYPE_EDP) { + edp_streams[*edp_stream_num] = context->streams[i]; + if (++(*edp_stream_num) == MAX_NUM_EDP) + return; + } + } +} + +static void get_edp_links_with_sink( + struct dc *dc, + struct dc_link **edp_links_with_sink, + int *edp_with_sink_num) +{ + int i; + + /* check if there is an eDP panel not in use */ + *edp_with_sink_num = 0; + for (i = 0; i < dc->link_count; i++) { + if (dc->links[i]->local_sink && + dc->links[i]->local_sink->sink_signal == SIGNAL_TYPE_EDP) { + edp_links_with_sink[*edp_with_sink_num] = dc->links[i]; + if (++(*edp_with_sink_num) == MAX_NUM_EDP) + return; + } + } +} + +/* + * When ASIC goes from VBIOS/VGA mode to driver/accelerated mode we need: + * 1. Power down all DC HW blocks + * 2. Disable VGA engine on all controllers + * 3. Enable power gating for controller + * 4. Set acc_mode_change bit (VBIOS will clear this bit when going to FSDOS) + */ +void dce110_enable_accelerated_mode(struct dc *dc, struct dc_state *context) +{ + struct dc_link *edp_links_with_sink[MAX_NUM_EDP]; + struct dc_link *edp_links[MAX_NUM_EDP]; + struct dc_stream_state *edp_streams[MAX_NUM_EDP]; + struct dc_link *edp_link_with_sink = NULL; + struct dc_link *edp_link = NULL; + struct dce_hwseq *hws = dc->hwseq; + int edp_with_sink_num; + int edp_num; + int edp_stream_num; + int i; + bool can_apply_edp_fast_boot = false; + bool can_apply_seamless_boot = false; + bool keep_edp_vdd_on = false; + DC_LOGGER_INIT(); + + + get_edp_links_with_sink(dc, edp_links_with_sink, &edp_with_sink_num); + get_edp_links(dc, edp_links, &edp_num); + + if (hws->funcs.init_pipes) + hws->funcs.init_pipes(dc, context); + + get_edp_streams(context, edp_streams, &edp_stream_num); + + // Check fastboot support, disable on DCE8 because of blank screens + if (edp_num && edp_stream_num && dc->ctx->dce_version != DCE_VERSION_8_0 && + dc->ctx->dce_version != DCE_VERSION_8_1 && + dc->ctx->dce_version != DCE_VERSION_8_3) { + for (i = 0; i < edp_num; i++) { + edp_link = edp_links[i]; + if (edp_link != edp_streams[0]->link) + continue; + // enable fastboot if backend is enabled on eDP + if (edp_link->link_enc->funcs->is_dig_enabled && + edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc) && + edp_link->link_status.link_active) { + struct dc_stream_state *edp_stream = edp_streams[0]; + + can_apply_edp_fast_boot = dc_validate_boot_timing(dc, + edp_stream->sink, &edp_stream->timing); + edp_stream->apply_edp_fast_boot_optimization = can_apply_edp_fast_boot; + if (can_apply_edp_fast_boot) + DC_LOG_EVENT_LINK_TRAINING("eDP fast boot disabled to optimize link rate\n"); + + break; + } + } + // We are trying to enable eDP, don't power down VDD + if (can_apply_edp_fast_boot) + keep_edp_vdd_on = true; + } + + // Check seamless boot support + for (i = 0; i < context->stream_count; i++) { + if (context->streams[i]->apply_seamless_boot_optimization) { + can_apply_seamless_boot = true; + break; + } + } + + /* eDP should not have stream in resume from S4 and so even with VBios post + * it should get turned off + */ + if (edp_with_sink_num) + edp_link_with_sink = edp_links_with_sink[0]; + + if (!can_apply_edp_fast_boot && !can_apply_seamless_boot) { + if (edp_link_with_sink && !keep_edp_vdd_on) { + /*turn off backlight before DP_blank and encoder powered down*/ + hws->funcs.edp_backlight_control(edp_link_with_sink, false); + } + /*resume from S3, no vbios posting, no need to power down again*/ + power_down_all_hw_blocks(dc); + disable_vga_and_power_gate_all_controllers(dc); + if (edp_link_with_sink && !keep_edp_vdd_on) + dc->hwss.edp_power_control(edp_link_with_sink, false); + } + bios_set_scratch_acc_mode_change(dc->ctx->dc_bios, 1); +} + +static uint32_t compute_pstate_blackout_duration( + struct bw_fixed blackout_duration, + const struct dc_stream_state *stream) +{ + uint32_t total_dest_line_time_ns; + uint32_t pstate_blackout_duration_ns; + + pstate_blackout_duration_ns = 1000 * blackout_duration.value >> 24; + + total_dest_line_time_ns = 1000000UL * + (stream->timing.h_total * 10) / + stream->timing.pix_clk_100hz + + pstate_blackout_duration_ns; + + return total_dest_line_time_ns; +} + +static void dce110_set_displaymarks( + const struct dc *dc, + struct dc_state *context) +{ + uint8_t i, num_pipes; + unsigned int underlay_idx = dc->res_pool->underlay_pipe_index; + + for (i = 0, num_pipes = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + uint32_t total_dest_line_time_ns; + + if (pipe_ctx->stream == NULL) + continue; + + total_dest_line_time_ns = compute_pstate_blackout_duration( + dc->bw_vbios->blackout_duration, pipe_ctx->stream); + pipe_ctx->plane_res.mi->funcs->mem_input_program_display_marks( + pipe_ctx->plane_res.mi, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[num_pipes], + context->bw_ctx.bw.dce.stutter_exit_wm_ns[num_pipes], + context->bw_ctx.bw.dce.stutter_entry_wm_ns[num_pipes], + context->bw_ctx.bw.dce.urgent_wm_ns[num_pipes], + total_dest_line_time_ns); + if (i == underlay_idx) { + num_pipes++; + pipe_ctx->plane_res.mi->funcs->mem_input_program_chroma_display_marks( + pipe_ctx->plane_res.mi, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[num_pipes], + context->bw_ctx.bw.dce.stutter_exit_wm_ns[num_pipes], + context->bw_ctx.bw.dce.urgent_wm_ns[num_pipes], + total_dest_line_time_ns); + } + num_pipes++; + } +} + +void dce110_set_safe_displaymarks( + struct resource_context *res_ctx, + const struct resource_pool *pool) +{ + int i; + int underlay_idx = pool->underlay_pipe_index; + struct dce_watermarks max_marks = { + MAX_WATERMARK, MAX_WATERMARK, MAX_WATERMARK, MAX_WATERMARK }; + struct dce_watermarks nbp_marks = { + SAFE_NBP_MARK, SAFE_NBP_MARK, SAFE_NBP_MARK, SAFE_NBP_MARK }; + struct dce_watermarks min_marks = { 0, 0, 0, 0}; + + for (i = 0; i < MAX_PIPES; i++) { + if (res_ctx->pipe_ctx[i].stream == NULL || res_ctx->pipe_ctx[i].plane_res.mi == NULL) + continue; + + res_ctx->pipe_ctx[i].plane_res.mi->funcs->mem_input_program_display_marks( + res_ctx->pipe_ctx[i].plane_res.mi, + nbp_marks, + max_marks, + min_marks, + max_marks, + MAX_WATERMARK); + + if (i == underlay_idx) + res_ctx->pipe_ctx[i].plane_res.mi->funcs->mem_input_program_chroma_display_marks( + res_ctx->pipe_ctx[i].plane_res.mi, + nbp_marks, + max_marks, + max_marks, + MAX_WATERMARK); + + } +} + +/******************************************************************************* + * Public functions + ******************************************************************************/ + +static void set_drr(struct pipe_ctx **pipe_ctx, + int num_pipes, struct dc_crtc_timing_adjust adjust) +{ + int i = 0; + struct drr_params params = {0}; + // DRR should set trigger event to monitor surface update event + unsigned int event_triggers = 0x80; + // Note DRR trigger events are generated regardless of whether num frames met. + unsigned int num_frames = 2; + + params.vertical_total_max = adjust.v_total_max; + params.vertical_total_min = adjust.v_total_min; + + /* TODO: If multiple pipes are to be supported, you need + * some GSL stuff. Static screen triggers may be programmed differently + * as well. + */ + for (i = 0; i < num_pipes; i++) { + pipe_ctx[i]->stream_res.tg->funcs->set_drr( + pipe_ctx[i]->stream_res.tg, ¶ms); + + if (adjust.v_total_max != 0 && adjust.v_total_min != 0) + pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control( + pipe_ctx[i]->stream_res.tg, + event_triggers, num_frames); + } +} + +static void get_position(struct pipe_ctx **pipe_ctx, + int num_pipes, + struct crtc_position *position) +{ + int i = 0; + + /* TODO: handle pipes > 1 + */ + for (i = 0; i < num_pipes; i++) + pipe_ctx[i]->stream_res.tg->funcs->get_position(pipe_ctx[i]->stream_res.tg, position); +} + +static void set_static_screen_control(struct pipe_ctx **pipe_ctx, + int num_pipes, const struct dc_static_screen_params *params) +{ + unsigned int i; + unsigned int triggers = 0; + + if (params->triggers.overlay_update) + triggers |= 0x100; + if (params->triggers.surface_update) + triggers |= 0x80; + if (params->triggers.cursor_update) + triggers |= 0x2; + if (params->triggers.force_trigger) + triggers |= 0x1; + + if (num_pipes) { + struct dc *dc = pipe_ctx[0]->stream->ctx->dc; + + if (dc->fbc_compressor) + triggers |= 0x84; + } + + for (i = 0; i < num_pipes; i++) + pipe_ctx[i]->stream_res.tg->funcs-> + set_static_screen_control(pipe_ctx[i]->stream_res.tg, + triggers, params->num_frames); +} + +/* + * Check if FBC can be enabled + */ +static bool should_enable_fbc(struct dc *dc, + struct dc_state *context, + uint32_t *pipe_idx) +{ + uint32_t i; + struct pipe_ctx *pipe_ctx = NULL; + struct resource_context *res_ctx = &context->res_ctx; + unsigned int underlay_idx = dc->res_pool->underlay_pipe_index; + + + ASSERT(dc->fbc_compressor); + + /* FBC memory should be allocated */ + if (!dc->ctx->fbc_gpu_addr) + return false; + + /* Only supports single display */ + if (context->stream_count != 1) + return false; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (res_ctx->pipe_ctx[i].stream) { + + pipe_ctx = &res_ctx->pipe_ctx[i]; + + if (!pipe_ctx) + continue; + + /* fbc not applicable on underlay pipe */ + if (pipe_ctx->pipe_idx != underlay_idx) { + *pipe_idx = i; + break; + } + } + } + + if (i == dc->res_pool->pipe_count) + return false; + + if (!pipe_ctx->stream->link) + return false; + + /* Only supports eDP */ + if (pipe_ctx->stream->link->connector_signal != SIGNAL_TYPE_EDP) + return false; + + /* PSR should not be enabled */ + if (pipe_ctx->stream->link->psr_settings.psr_feature_enabled) + return false; + + /* Nothing to compress */ + if (!pipe_ctx->plane_state) + return false; + + /* Only for non-linear tiling */ + if (pipe_ctx->plane_state->tiling_info.gfx8.array_mode == DC_ARRAY_LINEAR_GENERAL) + return false; + + return true; +} + +/* + * Enable FBC + */ +static void enable_fbc( + struct dc *dc, + struct dc_state *context) +{ + uint32_t pipe_idx = 0; + + if (should_enable_fbc(dc, context, &pipe_idx)) { + /* Program GRPH COMPRESSED ADDRESS and PITCH */ + struct compr_addr_and_pitch_params params = {0, 0, 0}; + struct compressor *compr = dc->fbc_compressor; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[pipe_idx]; + + params.source_view_width = pipe_ctx->stream->timing.h_addressable; + params.source_view_height = pipe_ctx->stream->timing.v_addressable; + params.inst = pipe_ctx->stream_res.tg->inst; + compr->compr_surface_address.quad_part = dc->ctx->fbc_gpu_addr; + + compr->funcs->surface_address_and_pitch(compr, ¶ms); + compr->funcs->set_fbc_invalidation_triggers(compr, 1); + + compr->funcs->enable_fbc(compr, ¶ms); + } +} + +static void dce110_reset_hw_ctx_wrap( + struct dc *dc, + struct dc_state *context) +{ + int i; + + /* Reset old context */ + /* look up the targets that have been removed since last commit */ + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + /* Note: We need to disable output if clock sources change, + * since bios does optimization and doesn't apply if changing + * PHY when not already disabled. + */ + + /* Skip underlay pipe since it will be handled in commit surface*/ + if (!pipe_ctx_old->stream || pipe_ctx_old->top_pipe) + continue; + + if (!pipe_ctx->stream || + pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) { + struct clock_source *old_clk = pipe_ctx_old->clock_source; + + /* Disable if new stream is null. O/w, if stream is + * disabled already, no need to disable again. + */ + if (!pipe_ctx->stream || !pipe_ctx->stream->dpms_off) { + core_link_disable_stream(pipe_ctx_old); + + /* free acquired resources*/ + if (pipe_ctx_old->stream_res.audio) { + /*disable az_endpoint*/ + pipe_ctx_old->stream_res.audio->funcs-> + az_disable(pipe_ctx_old->stream_res.audio); + + /*free audio*/ + if (dc->caps.dynamic_audio == true) { + /*we have to dynamic arbitrate the audio endpoints*/ + /*we free the resource, need reset is_audio_acquired*/ + update_audio_usage(&dc->current_state->res_ctx, dc->res_pool, + pipe_ctx_old->stream_res.audio, false); + pipe_ctx_old->stream_res.audio = NULL; + } + } + } + + pipe_ctx_old->stream_res.tg->funcs->set_blank(pipe_ctx_old->stream_res.tg, true); + if (!hwss_wait_for_blank_complete(pipe_ctx_old->stream_res.tg)) { + dm_error("DC: failed to blank crtc!\n"); + BREAK_TO_DEBUGGER(); + } + pipe_ctx_old->stream_res.tg->funcs->disable_crtc(pipe_ctx_old->stream_res.tg); + pipe_ctx_old->stream->link->phy_state.symclk_ref_cnts.otg = 0; + pipe_ctx_old->plane_res.mi->funcs->free_mem_input( + pipe_ctx_old->plane_res.mi, dc->current_state->stream_count); + + if (old_clk && 0 == resource_get_clock_source_reference(&context->res_ctx, + dc->res_pool, + old_clk)) + old_clk->funcs->cs_power_down(old_clk); + + dc->hwss.disable_plane(dc, pipe_ctx_old); + + pipe_ctx_old->stream = NULL; + } + } +} + +static void dce110_setup_audio_dto( + struct dc *dc, + struct dc_state *context) +{ + int i; + + /* program audio wall clock. use HDMI as clock source if HDMI + * audio active. Otherwise, use DP as clock source + * first, loop to find any HDMI audio, if not, loop find DP audio + */ + /* Setup audio rate clock source */ + /* Issue: + * Audio lag happened on DP monitor when unplug a HDMI monitor + * + * Cause: + * In case of DP and HDMI connected or HDMI only, DCCG_AUDIO_DTO_SEL + * is set to either dto0 or dto1, audio should work fine. + * In case of DP connected only, DCCG_AUDIO_DTO_SEL should be dto1, + * set to dto0 will cause audio lag. + * + * Solution: + * Not optimized audio wall dto setup. When mode set, iterate pipe_ctx, + * find first available pipe with audio, setup audio wall DTO per topology + * instead of per pipe. + */ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->top_pipe) + continue; + if (pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_TYPE_A) + continue; + if (pipe_ctx->stream_res.audio != NULL) { + struct audio_output audio_output; + + build_audio_output(context, pipe_ctx, &audio_output); + + if (dc->res_pool->dccg && dc->res_pool->dccg->funcs->set_audio_dtbclk_dto) { + struct dtbclk_dto_params dto_params = {0}; + + dc->res_pool->dccg->funcs->set_audio_dtbclk_dto( + dc->res_pool->dccg, &dto_params); + + pipe_ctx->stream_res.audio->funcs->wall_dto_setup( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &audio_output.pll_info); + } else + pipe_ctx->stream_res.audio->funcs->wall_dto_setup( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &audio_output.pll_info); + break; + } + } + + /* no HDMI audio is found, try DP audio */ + if (i == dc->res_pool->pipe_count) { + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->top_pipe) + continue; + + if (!dc_is_dp_signal(pipe_ctx->stream->signal)) + continue; + + if (pipe_ctx->stream_res.audio != NULL) { + struct audio_output audio_output; + + build_audio_output(context, pipe_ctx, &audio_output); + + pipe_ctx->stream_res.audio->funcs->wall_dto_setup( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &audio_output.pll_info); + break; + } + } + } +} + +enum dc_status dce110_apply_ctx_to_hw( + struct dc *dc, + struct dc_state *context) +{ + struct dce_hwseq *hws = dc->hwseq; + struct dc_bios *dcb = dc->ctx->dc_bios; + enum dc_status status; + int i; + + /* reset syncd pipes from disabled pipes */ + if (dc->config.use_pipe_ctx_sync_logic) + reset_syncd_pipes_from_disabled_pipes(dc, context); + + /* Reset old context */ + /* look up the targets that have been removed since last commit */ + hws->funcs.reset_hw_ctx_wrap(dc, context); + + /* Skip applying if no targets */ + if (context->stream_count <= 0) + return DC_OK; + + /* Apply new context */ + dcb->funcs->set_scratch_critical_state(dcb, true); + + /* below is for real asic only */ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL || pipe_ctx->top_pipe) + continue; + + if (pipe_ctx->stream == pipe_ctx_old->stream) { + if (pipe_ctx_old->clock_source != pipe_ctx->clock_source) + dce_crtc_switch_to_clk_src(dc->hwseq, + pipe_ctx->clock_source, i); + continue; + } + + hws->funcs.enable_display_power_gating( + dc, i, dc->ctx->dc_bios, + PIPE_GATING_CONTROL_DISABLE); + } + + if (dc->fbc_compressor) + dc->fbc_compressor->funcs->disable_fbc(dc->fbc_compressor); + + dce110_setup_audio_dto(dc, context); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->stream == pipe_ctx_old->stream && + pipe_ctx->stream->link->link_state_valid) { + continue; + } + + if (pipe_ctx_old->stream && !pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) + continue; + + if (pipe_ctx->top_pipe || pipe_ctx->prev_odm_pipe) + continue; + + status = apply_single_controller_ctx_to_hw( + pipe_ctx, + context, + dc); + + if (DC_OK != status) + return status; + } + + if (dc->fbc_compressor) + enable_fbc(dc, dc->current_state); + + dcb->funcs->set_scratch_critical_state(dcb, false); + + return DC_OK; +} + +/******************************************************************************* + * Front End programming + ******************************************************************************/ +static void set_default_colors(struct pipe_ctx *pipe_ctx) +{ + struct default_adjustment default_adjust = { 0 }; + + default_adjust.force_hw_default = false; + default_adjust.in_color_space = pipe_ctx->plane_state->color_space; + default_adjust.out_color_space = pipe_ctx->stream->output_color_space; + default_adjust.csc_adjust_type = GRAPHICS_CSC_ADJUST_TYPE_SW; + default_adjust.surface_pixel_format = pipe_ctx->plane_res.scl_data.format; + + /* display color depth */ + default_adjust.color_depth = + pipe_ctx->stream->timing.display_color_depth; + + /* Lb color depth */ + default_adjust.lb_color_depth = pipe_ctx->plane_res.scl_data.lb_params.depth; + + pipe_ctx->plane_res.xfm->funcs->opp_set_csc_default( + pipe_ctx->plane_res.xfm, &default_adjust); +} + + +/******************************************************************************* + * In order to turn on/off specific surface we will program + * Blender + CRTC + * + * In case that we have two surfaces and they have a different visibility + * we can't turn off the CRTC since it will turn off the entire display + * + * |----------------------------------------------- | + * |bottom pipe|curr pipe | | | + * |Surface |Surface | Blender | CRCT | + * |visibility |visibility | Configuration| | + * |------------------------------------------------| + * | off | off | CURRENT_PIPE | blank | + * | off | on | CURRENT_PIPE | unblank | + * | on | off | OTHER_PIPE | unblank | + * | on | on | BLENDING | unblank | + * -------------------------------------------------| + * + ******************************************************************************/ +static void program_surface_visibility(const struct dc *dc, + struct pipe_ctx *pipe_ctx) +{ + enum blnd_mode blender_mode = BLND_MODE_CURRENT_PIPE; + bool blank_target = false; + + if (pipe_ctx->bottom_pipe) { + + /* For now we are supporting only two pipes */ + ASSERT(pipe_ctx->bottom_pipe->bottom_pipe == NULL); + + if (pipe_ctx->bottom_pipe->plane_state->visible) { + if (pipe_ctx->plane_state->visible) + blender_mode = BLND_MODE_BLENDING; + else + blender_mode = BLND_MODE_OTHER_PIPE; + + } else if (!pipe_ctx->plane_state->visible) + blank_target = true; + + } else if (!pipe_ctx->plane_state->visible) + blank_target = true; + + dce_set_blender_mode(dc->hwseq, pipe_ctx->stream_res.tg->inst, blender_mode); + pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, blank_target); + +} + +static void program_gamut_remap(struct pipe_ctx *pipe_ctx) +{ + int i = 0; + struct xfm_grph_csc_adjustment adjust; + memset(&adjust, 0, sizeof(adjust)); + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; + + + if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; + + for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) + adjust.temperature_matrix[i] = + pipe_ctx->stream->gamut_remap_matrix.matrix[i]; + } + + pipe_ctx->plane_res.xfm->funcs->transform_set_gamut_remap(pipe_ctx->plane_res.xfm, &adjust); +} +static void update_plane_addr(const struct dc *dc, + struct pipe_ctx *pipe_ctx) +{ + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + + if (plane_state == NULL) + return; + + pipe_ctx->plane_res.mi->funcs->mem_input_program_surface_flip_and_addr( + pipe_ctx->plane_res.mi, + &plane_state->address, + plane_state->flip_immediate); + + plane_state->status.requested_address = plane_state->address; +} + +static void dce110_update_pending_status(struct pipe_ctx *pipe_ctx) +{ + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + + if (plane_state == NULL) + return; + + plane_state->status.is_flip_pending = + pipe_ctx->plane_res.mi->funcs->mem_input_is_flip_pending( + pipe_ctx->plane_res.mi); + + if (plane_state->status.is_flip_pending && !plane_state->visible) + pipe_ctx->plane_res.mi->current_address = pipe_ctx->plane_res.mi->request_address; + + plane_state->status.current_address = pipe_ctx->plane_res.mi->current_address; + if (pipe_ctx->plane_res.mi->current_address.type == PLN_ADDR_TYPE_GRPH_STEREO && + pipe_ctx->stream_res.tg->funcs->is_stereo_left_eye) { + plane_state->status.is_right_eye =\ + !pipe_ctx->stream_res.tg->funcs->is_stereo_left_eye(pipe_ctx->stream_res.tg); + } +} + +void dce110_power_down(struct dc *dc) +{ + power_down_all_hw_blocks(dc); + disable_vga_and_power_gate_all_controllers(dc); +} + +static bool wait_for_reset_trigger_to_occur( + struct dc_context *dc_ctx, + struct timing_generator *tg) +{ + bool rc = false; + + /* To avoid endless loop we wait at most + * frames_to_wait_on_triggered_reset frames for the reset to occur. */ + const uint32_t frames_to_wait_on_triggered_reset = 10; + uint32_t i; + + for (i = 0; i < frames_to_wait_on_triggered_reset; i++) { + + if (!tg->funcs->is_counter_moving(tg)) { + DC_ERROR("TG counter is not moving!\n"); + break; + } + + if (tg->funcs->did_triggered_reset_occur(tg)) { + rc = true; + /* usually occurs at i=1 */ + DC_SYNC_INFO("GSL: reset occurred at wait count: %d\n", + i); + break; + } + + /* Wait for one frame. */ + tg->funcs->wait_for_state(tg, CRTC_STATE_VACTIVE); + tg->funcs->wait_for_state(tg, CRTC_STATE_VBLANK); + } + + if (false == rc) + DC_ERROR("GSL: Timeout on reset trigger!\n"); + + return rc; +} + +/* Enable timing synchronization for a group of Timing Generators. */ +static void dce110_enable_timing_synchronization( + struct dc *dc, + int group_index, + int group_size, + struct pipe_ctx *grouped_pipes[]) +{ + struct dc_context *dc_ctx = dc->ctx; + struct dcp_gsl_params gsl_params = { 0 }; + int i; + + DC_SYNC_INFO("GSL: Setting-up...\n"); + + /* Designate a single TG in the group as a master. + * Since HW doesn't care which one, we always assign + * the 1st one in the group. */ + gsl_params.gsl_group = 0; + gsl_params.gsl_master = grouped_pipes[0]->stream_res.tg->inst; + + for (i = 0; i < group_size; i++) + grouped_pipes[i]->stream_res.tg->funcs->setup_global_swap_lock( + grouped_pipes[i]->stream_res.tg, &gsl_params); + + /* Reset slave controllers on master VSync */ + DC_SYNC_INFO("GSL: enabling trigger-reset\n"); + + for (i = 1 /* skip the master */; i < group_size; i++) + grouped_pipes[i]->stream_res.tg->funcs->enable_reset_trigger( + grouped_pipes[i]->stream_res.tg, + gsl_params.gsl_group); + + for (i = 1 /* skip the master */; i < group_size; i++) { + DC_SYNC_INFO("GSL: waiting for reset to occur.\n"); + wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[i]->stream_res.tg); + grouped_pipes[i]->stream_res.tg->funcs->disable_reset_trigger( + grouped_pipes[i]->stream_res.tg); + } + + /* GSL Vblank synchronization is a one time sync mechanism, assumption + * is that the sync'ed displays will not drift out of sync over time*/ + DC_SYNC_INFO("GSL: Restoring register states.\n"); + for (i = 0; i < group_size; i++) + grouped_pipes[i]->stream_res.tg->funcs->tear_down_global_swap_lock(grouped_pipes[i]->stream_res.tg); + + DC_SYNC_INFO("GSL: Set-up complete.\n"); +} + +static void dce110_enable_per_frame_crtc_position_reset( + struct dc *dc, + int group_size, + struct pipe_ctx *grouped_pipes[]) +{ + struct dc_context *dc_ctx = dc->ctx; + struct dcp_gsl_params gsl_params = { 0 }; + int i; + + gsl_params.gsl_group = 0; + gsl_params.gsl_master = 0; + + for (i = 0; i < group_size; i++) + grouped_pipes[i]->stream_res.tg->funcs->setup_global_swap_lock( + grouped_pipes[i]->stream_res.tg, &gsl_params); + + DC_SYNC_INFO("GSL: enabling trigger-reset\n"); + + for (i = 1; i < group_size; i++) + grouped_pipes[i]->stream_res.tg->funcs->enable_crtc_reset( + grouped_pipes[i]->stream_res.tg, + gsl_params.gsl_master, + &grouped_pipes[i]->stream->triggered_crtc_reset); + + DC_SYNC_INFO("GSL: waiting for reset to occur.\n"); + for (i = 1; i < group_size; i++) + wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[i]->stream_res.tg); + + for (i = 0; i < group_size; i++) + grouped_pipes[i]->stream_res.tg->funcs->tear_down_global_swap_lock(grouped_pipes[i]->stream_res.tg); + +} + +static void init_pipes(struct dc *dc, struct dc_state *context) +{ + // Do nothing +} + +static void init_hw(struct dc *dc) +{ + int i; + struct dc_bios *bp; + struct transform *xfm; + struct abm *abm; + struct dmcu *dmcu; + struct dce_hwseq *hws = dc->hwseq; + uint32_t backlight = MAX_BACKLIGHT_LEVEL; + + bp = dc->ctx->dc_bios; + for (i = 0; i < dc->res_pool->pipe_count; i++) { + xfm = dc->res_pool->transforms[i]; + xfm->funcs->transform_reset(xfm); + + hws->funcs.enable_display_power_gating( + dc, i, bp, + PIPE_GATING_CONTROL_INIT); + hws->funcs.enable_display_power_gating( + dc, i, bp, + PIPE_GATING_CONTROL_DISABLE); + hws->funcs.enable_display_pipe_clock_gating( + dc->ctx, + true); + } + + dce_clock_gating_power_up(dc->hwseq, false); + /***************************************/ + + for (i = 0; i < dc->link_count; i++) { + /****************************************/ + /* Power up AND update implementation according to the + * required signal (which may be different from the + * default signal on connector). */ + struct dc_link *link = dc->links[i]; + + link->link_enc->funcs->hw_init(link->link_enc); + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct timing_generator *tg = dc->res_pool->timing_generators[i]; + + tg->funcs->disable_vga(tg); + + /* Blank controller using driver code instead of + * command table. */ + tg->funcs->set_blank(tg, true); + hwss_wait_for_blank_complete(tg); + } + + for (i = 0; i < dc->res_pool->audio_count; i++) { + struct audio *audio = dc->res_pool->audios[i]; + audio->funcs->hw_init(audio); + } + + for (i = 0; i < dc->link_count; i++) { + struct dc_link *link = dc->links[i]; + + if (link->panel_cntl) + backlight = link->panel_cntl->funcs->hw_init(link->panel_cntl); + } + + abm = dc->res_pool->abm; + if (abm != NULL) + abm->funcs->abm_init(abm, backlight); + + dmcu = dc->res_pool->dmcu; + if (dmcu != NULL && abm != NULL) + abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu); + + if (dc->fbc_compressor) + dc->fbc_compressor->funcs->power_up_fbc(dc->fbc_compressor); + +} + + +void dce110_prepare_bandwidth( + struct dc *dc, + struct dc_state *context) +{ + struct clk_mgr *dccg = dc->clk_mgr; + + dce110_set_safe_displaymarks(&context->res_ctx, dc->res_pool); + + dccg->funcs->update_clocks( + dccg, + context, + false); +} + +void dce110_optimize_bandwidth( + struct dc *dc, + struct dc_state *context) +{ + struct clk_mgr *dccg = dc->clk_mgr; + + dce110_set_displaymarks(dc, context); + + dccg->funcs->update_clocks( + dccg, + context, + true); +} + +static void dce110_program_front_end_for_pipe( + struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + struct mem_input *mi = pipe_ctx->plane_res.mi; + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + struct xfm_grph_csc_adjustment adjust; + struct out_csc_color_matrix tbl_entry; + unsigned int i; + struct dce_hwseq *hws = dc->hwseq; + + DC_LOGGER_INIT(); + memset(&tbl_entry, 0, sizeof(tbl_entry)); + + memset(&adjust, 0, sizeof(adjust)); + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; + + dce_enable_fe_clock(dc->hwseq, mi->inst, true); + + set_default_colors(pipe_ctx); + if (pipe_ctx->stream->csc_color_matrix.enable_adjustment + == true) { + tbl_entry.color_space = + pipe_ctx->stream->output_color_space; + + for (i = 0; i < 12; i++) + tbl_entry.regval[i] = + pipe_ctx->stream->csc_color_matrix.matrix[i]; + + pipe_ctx->plane_res.xfm->funcs->opp_set_csc_adjustment + (pipe_ctx->plane_res.xfm, &tbl_entry); + } + + if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; + + for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) + adjust.temperature_matrix[i] = + pipe_ctx->stream->gamut_remap_matrix.matrix[i]; + } + + pipe_ctx->plane_res.xfm->funcs->transform_set_gamut_remap(pipe_ctx->plane_res.xfm, &adjust); + + pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != NULL; + + program_scaler(dc, pipe_ctx); + + mi->funcs->mem_input_program_surface_config( + mi, + plane_state->format, + &plane_state->tiling_info, + &plane_state->plane_size, + plane_state->rotation, + NULL, + false); + if (mi->funcs->set_blank) + mi->funcs->set_blank(mi, pipe_ctx->plane_state->visible); + + if (dc->config.gpu_vm_support) + mi->funcs->mem_input_program_pte_vm( + pipe_ctx->plane_res.mi, + plane_state->format, + &plane_state->tiling_info, + plane_state->rotation); + + /* Moved programming gamma from dc to hwss */ + if (pipe_ctx->plane_state->update_flags.bits.full_update || + pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || + pipe_ctx->plane_state->update_flags.bits.gamma_change) + hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); + + if (pipe_ctx->plane_state->update_flags.bits.full_update) + hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); + + DC_LOG_SURFACE( + "Pipe:%d %p: addr hi:0x%x, " + "addr low:0x%x, " + "src: %d, %d, %d," + " %d; dst: %d, %d, %d, %d;" + "clip: %d, %d, %d, %d\n", + pipe_ctx->pipe_idx, + (void *) pipe_ctx->plane_state, + pipe_ctx->plane_state->address.grph.addr.high_part, + pipe_ctx->plane_state->address.grph.addr.low_part, + pipe_ctx->plane_state->src_rect.x, + pipe_ctx->plane_state->src_rect.y, + pipe_ctx->plane_state->src_rect.width, + pipe_ctx->plane_state->src_rect.height, + pipe_ctx->plane_state->dst_rect.x, + pipe_ctx->plane_state->dst_rect.y, + pipe_ctx->plane_state->dst_rect.width, + pipe_ctx->plane_state->dst_rect.height, + pipe_ctx->plane_state->clip_rect.x, + pipe_ctx->plane_state->clip_rect.y, + pipe_ctx->plane_state->clip_rect.width, + pipe_ctx->plane_state->clip_rect.height); + + DC_LOG_SURFACE( + "Pipe %d: width, height, x, y\n" + "viewport:%d, %d, %d, %d\n" + "recout: %d, %d, %d, %d\n", + pipe_ctx->pipe_idx, + pipe_ctx->plane_res.scl_data.viewport.width, + pipe_ctx->plane_res.scl_data.viewport.height, + pipe_ctx->plane_res.scl_data.viewport.x, + pipe_ctx->plane_res.scl_data.viewport.y, + pipe_ctx->plane_res.scl_data.recout.width, + pipe_ctx->plane_res.scl_data.recout.height, + pipe_ctx->plane_res.scl_data.recout.x, + pipe_ctx->plane_res.scl_data.recout.y); +} + +static void dce110_apply_ctx_for_surface( + struct dc *dc, + const struct dc_stream_state *stream, + int num_planes, + struct dc_state *context) +{ + int i; + + if (num_planes == 0) + return; + + if (dc->fbc_compressor) + dc->fbc_compressor->funcs->disable_fbc(dc->fbc_compressor); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream != stream) + continue; + + /* Need to allocate mem before program front end for Fiji */ + pipe_ctx->plane_res.mi->funcs->allocate_mem_input( + pipe_ctx->plane_res.mi, + pipe_ctx->stream->timing.h_total, + pipe_ctx->stream->timing.v_total, + pipe_ctx->stream->timing.pix_clk_100hz / 10, + context->stream_count); + + dce110_program_front_end_for_pipe(dc, pipe_ctx); + + dc->hwss.update_plane_addr(dc, pipe_ctx); + + program_surface_visibility(dc, pipe_ctx); + + } + + if (dc->fbc_compressor) + enable_fbc(dc, context); +} + +static void dce110_post_unlock_program_front_end( + struct dc *dc, + struct dc_state *context) +{ +} + +static void dce110_power_down_fe(struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + struct dce_hwseq *hws = dc->hwseq; + int fe_idx = pipe_ctx->plane_res.mi ? + pipe_ctx->plane_res.mi->inst : pipe_ctx->pipe_idx; + + /* Do not power down fe when stream is active on dce*/ + if (dc->current_state->res_ctx.pipe_ctx[fe_idx].stream) + return; + + hws->funcs.enable_display_power_gating( + dc, fe_idx, dc->ctx->dc_bios, PIPE_GATING_CONTROL_ENABLE); + + dc->res_pool->transforms[fe_idx]->funcs->transform_reset( + dc->res_pool->transforms[fe_idx]); +} + +static void dce110_wait_for_mpcc_disconnect( + struct dc *dc, + struct resource_pool *res_pool, + struct pipe_ctx *pipe_ctx) +{ + /* do nothing*/ +} + +static void program_output_csc(struct dc *dc, + struct pipe_ctx *pipe_ctx, + enum dc_color_space colorspace, + uint16_t *matrix, + int opp_id) +{ + int i; + struct out_csc_color_matrix tbl_entry; + + if (pipe_ctx->stream->csc_color_matrix.enable_adjustment == true) { + enum dc_color_space color_space = pipe_ctx->stream->output_color_space; + + for (i = 0; i < 12; i++) + tbl_entry.regval[i] = pipe_ctx->stream->csc_color_matrix.matrix[i]; + + tbl_entry.color_space = color_space; + + pipe_ctx->plane_res.xfm->funcs->opp_set_csc_adjustment( + pipe_ctx->plane_res.xfm, &tbl_entry); + } +} + +static void dce110_set_cursor_position(struct pipe_ctx *pipe_ctx) +{ + struct dc_cursor_position pos_cpy = pipe_ctx->stream->cursor_position; + struct input_pixel_processor *ipp = pipe_ctx->plane_res.ipp; + struct mem_input *mi = pipe_ctx->plane_res.mi; + struct dc_cursor_mi_param param = { + .pixel_clk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10, + .ref_clk_khz = pipe_ctx->stream->ctx->dc->res_pool->ref_clocks.xtalin_clock_inKhz, + .viewport = pipe_ctx->plane_res.scl_data.viewport, + .h_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.horz, + .v_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.vert, + .rotation = pipe_ctx->plane_state->rotation, + .mirror = pipe_ctx->plane_state->horizontal_mirror + }; + + /** + * If the cursor's source viewport is clipped then we need to + * translate the cursor to appear in the correct position on + * the screen. + * + * This translation isn't affected by scaling so it needs to be + * done *after* we adjust the position for the scale factor. + * + * This is only done by opt-in for now since there are still + * some usecases like tiled display that might enable the + * cursor on both streams while expecting dc to clip it. + */ + if (pos_cpy.translate_by_source) { + pos_cpy.x += pipe_ctx->plane_state->src_rect.x; + pos_cpy.y += pipe_ctx->plane_state->src_rect.y; + } + + if (pipe_ctx->plane_state->address.type + == PLN_ADDR_TYPE_VIDEO_PROGRESSIVE) + pos_cpy.enable = false; + + if (pipe_ctx->top_pipe && pipe_ctx->plane_state != pipe_ctx->top_pipe->plane_state) + pos_cpy.enable = false; + + if (ipp->funcs->ipp_cursor_set_position) + ipp->funcs->ipp_cursor_set_position(ipp, &pos_cpy, ¶m); + if (mi->funcs->set_cursor_position) + mi->funcs->set_cursor_position(mi, &pos_cpy, ¶m); +} + +static void dce110_set_cursor_attribute(struct pipe_ctx *pipe_ctx) +{ + struct dc_cursor_attributes *attributes = &pipe_ctx->stream->cursor_attributes; + + if (pipe_ctx->plane_res.ipp && + pipe_ctx->plane_res.ipp->funcs->ipp_cursor_set_attributes) + pipe_ctx->plane_res.ipp->funcs->ipp_cursor_set_attributes( + pipe_ctx->plane_res.ipp, attributes); + + if (pipe_ctx->plane_res.mi && + pipe_ctx->plane_res.mi->funcs->set_cursor_attributes) + pipe_ctx->plane_res.mi->funcs->set_cursor_attributes( + pipe_ctx->plane_res.mi, attributes); + + if (pipe_ctx->plane_res.xfm && + pipe_ctx->plane_res.xfm->funcs->set_cursor_attributes) + pipe_ctx->plane_res.xfm->funcs->set_cursor_attributes( + pipe_ctx->plane_res.xfm, attributes); +} + +bool dce110_set_backlight_level(struct pipe_ctx *pipe_ctx, + uint32_t backlight_pwm_u16_16, + uint32_t frame_ramp) +{ + struct dc_link *link = pipe_ctx->stream->link; + struct dc *dc = link->ctx->dc; + struct abm *abm = pipe_ctx->stream_res.abm; + struct panel_cntl *panel_cntl = link->panel_cntl; + struct dmcu *dmcu = dc->res_pool->dmcu; + bool fw_set_brightness = true; + /* DMCU -1 for all controller id values, + * therefore +1 here + */ + uint32_t controller_id = pipe_ctx->stream_res.tg->inst + 1; + + if (abm == NULL || panel_cntl == NULL || (abm->funcs->set_backlight_level_pwm == NULL)) + return false; + + if (dmcu) + fw_set_brightness = dmcu->funcs->is_dmcu_initialized(dmcu); + + if (!fw_set_brightness && panel_cntl->funcs->driver_set_backlight) + panel_cntl->funcs->driver_set_backlight(panel_cntl, backlight_pwm_u16_16); + else + abm->funcs->set_backlight_level_pwm( + abm, + backlight_pwm_u16_16, + frame_ramp, + controller_id, + link->panel_cntl->inst); + + return true; +} + +void dce110_set_abm_immediate_disable(struct pipe_ctx *pipe_ctx) +{ + struct abm *abm = pipe_ctx->stream_res.abm; + struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl; + + if (abm) + abm->funcs->set_abm_immediate_disable(abm, + pipe_ctx->stream->link->panel_cntl->inst); + + if (panel_cntl) + panel_cntl->funcs->store_backlight_level(panel_cntl); +} + +void dce110_set_pipe(struct pipe_ctx *pipe_ctx) +{ + struct abm *abm = pipe_ctx->stream_res.abm; + struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl; + uint32_t otg_inst = pipe_ctx->stream_res.tg->inst + 1; + + if (abm && panel_cntl) + abm->funcs->set_pipe(abm, otg_inst, panel_cntl->inst); +} + +void dce110_enable_lvds_link_output(struct dc_link *link, + const struct link_resource *link_res, + enum clock_source_id clock_source, + uint32_t pixel_clock) +{ + link->link_enc->funcs->enable_lvds_output( + link->link_enc, + clock_source, + pixel_clock); + link->phy_state.symclk_state = SYMCLK_ON_TX_ON; +} + +void dce110_enable_tmds_link_output(struct dc_link *link, + const struct link_resource *link_res, + enum signal_type signal, + enum clock_source_id clock_source, + enum dc_color_depth color_depth, + uint32_t pixel_clock) +{ + link->link_enc->funcs->enable_tmds_output( + link->link_enc, + clock_source, + color_depth, + signal, + pixel_clock); + link->phy_state.symclk_state = SYMCLK_ON_TX_ON; +} + +void dce110_enable_dp_link_output( + struct dc_link *link, + const struct link_resource *link_res, + enum signal_type signal, + enum clock_source_id clock_source, + const struct dc_link_settings *link_settings) +{ + struct dc *dc = link->ctx->dc; + struct dmcu *dmcu = dc->res_pool->dmcu; + struct pipe_ctx *pipes = + link->dc->current_state->res_ctx.pipe_ctx; + struct clock_source *dp_cs = + link->dc->res_pool->dp_clock_source; + const struct link_hwss *link_hwss = get_link_hwss(link, link_res); + unsigned int i; + + + if (link->connector_signal == SIGNAL_TYPE_EDP) { + if (!link->dc->config.edp_no_power_sequencing) + link->dc->hwss.edp_power_control(link, true); + link->dc->hwss.edp_wait_for_hpd_ready(link, true); + } + + /* If the current pixel clock source is not DTO(happens after + * switching from HDMI passive dongle to DP on the same connector), + * switch the pixel clock source to DTO. + */ + + for (i = 0; i < MAX_PIPES; i++) { + if (pipes[i].stream != NULL && + pipes[i].stream->link == link) { + if (pipes[i].clock_source != NULL && + pipes[i].clock_source->id != CLOCK_SOURCE_ID_DP_DTO) { + pipes[i].clock_source = dp_cs; + pipes[i].stream_res.pix_clk_params.requested_pix_clk_100hz = + pipes[i].stream->timing.pix_clk_100hz; + pipes[i].clock_source->funcs->program_pix_clk( + pipes[i].clock_source, + &pipes[i].stream_res.pix_clk_params, + dp_get_link_encoding_format(link_settings), + &pipes[i].pll_settings); + } + } + } + + if (dp_get_link_encoding_format(link_settings) == DP_8b_10b_ENCODING) { + if (dc->clk_mgr->funcs->notify_link_rate_change) + dc->clk_mgr->funcs->notify_link_rate_change(dc->clk_mgr, link); + } + + if (dmcu != NULL && dmcu->funcs->lock_phy) + dmcu->funcs->lock_phy(dmcu); + + if (link_hwss->ext.enable_dp_link_output) + link_hwss->ext.enable_dp_link_output(link, link_res, signal, + clock_source, link_settings); + + link->phy_state.symclk_state = SYMCLK_ON_TX_ON; + + if (dmcu != NULL && dmcu->funcs->unlock_phy) + dmcu->funcs->unlock_phy(dmcu); + + dp_source_sequence_trace(link, DPCD_SOURCE_SEQ_AFTER_ENABLE_LINK_PHY); +} + +void dce110_disable_link_output(struct dc_link *link, + const struct link_resource *link_res, + enum signal_type signal) +{ + struct dc *dc = link->ctx->dc; + const struct link_hwss *link_hwss = get_link_hwss(link, link_res); + struct dmcu *dmcu = dc->res_pool->dmcu; + + if (signal == SIGNAL_TYPE_EDP && + link->dc->hwss.edp_backlight_control) + link->dc->hwss.edp_backlight_control(link, false); + else if (dmcu != NULL && dmcu->funcs->lock_phy) + dmcu->funcs->lock_phy(dmcu); + + link_hwss->disable_link_output(link, link_res, signal); + link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF; + + if (signal == SIGNAL_TYPE_EDP && + link->dc->hwss.edp_backlight_control) + link->dc->hwss.edp_power_control(link, false); + else if (dmcu != NULL && dmcu->funcs->lock_phy) + dmcu->funcs->unlock_phy(dmcu); + dp_source_sequence_trace(link, DPCD_SOURCE_SEQ_AFTER_DISABLE_LINK_PHY); +} + +static const struct hw_sequencer_funcs dce110_funcs = { + .program_gamut_remap = program_gamut_remap, + .program_output_csc = program_output_csc, + .init_hw = init_hw, + .apply_ctx_to_hw = dce110_apply_ctx_to_hw, + .apply_ctx_for_surface = dce110_apply_ctx_for_surface, + .post_unlock_program_front_end = dce110_post_unlock_program_front_end, + .update_plane_addr = update_plane_addr, + .update_pending_status = dce110_update_pending_status, + .enable_accelerated_mode = dce110_enable_accelerated_mode, + .enable_timing_synchronization = dce110_enable_timing_synchronization, + .enable_per_frame_crtc_position_reset = dce110_enable_per_frame_crtc_position_reset, + .update_info_frame = dce110_update_info_frame, + .enable_stream = dce110_enable_stream, + .disable_stream = dce110_disable_stream, + .unblank_stream = dce110_unblank_stream, + .blank_stream = dce110_blank_stream, + .enable_audio_stream = dce110_enable_audio_stream, + .disable_audio_stream = dce110_disable_audio_stream, + .disable_plane = dce110_power_down_fe, + .pipe_control_lock = dce_pipe_control_lock, + .interdependent_update_lock = NULL, + .cursor_lock = dce_pipe_control_lock, + .prepare_bandwidth = dce110_prepare_bandwidth, + .optimize_bandwidth = dce110_optimize_bandwidth, + .set_drr = set_drr, + .get_position = get_position, + .set_static_screen_control = set_static_screen_control, + .setup_stereo = NULL, + .set_avmute = dce110_set_avmute, + .wait_for_mpcc_disconnect = dce110_wait_for_mpcc_disconnect, + .edp_backlight_control = dce110_edp_backlight_control, + .edp_power_control = dce110_edp_power_control, + .edp_wait_for_hpd_ready = dce110_edp_wait_for_hpd_ready, + .set_cursor_position = dce110_set_cursor_position, + .set_cursor_attribute = dce110_set_cursor_attribute, + .set_backlight_level = dce110_set_backlight_level, + .set_abm_immediate_disable = dce110_set_abm_immediate_disable, + .set_pipe = dce110_set_pipe, + .enable_lvds_link_output = dce110_enable_lvds_link_output, + .enable_tmds_link_output = dce110_enable_tmds_link_output, + .enable_dp_link_output = dce110_enable_dp_link_output, + .disable_link_output = dce110_disable_link_output, +}; + +static const struct hwseq_private_funcs dce110_private_funcs = { + .init_pipes = init_pipes, + .update_plane_addr = update_plane_addr, + .set_input_transfer_func = dce110_set_input_transfer_func, + .set_output_transfer_func = dce110_set_output_transfer_func, + .power_down = dce110_power_down, + .enable_display_pipe_clock_gating = enable_display_pipe_clock_gating, + .enable_display_power_gating = dce110_enable_display_power_gating, + .reset_hw_ctx_wrap = dce110_reset_hw_ctx_wrap, + .enable_stream_timing = dce110_enable_stream_timing, + .disable_stream_gating = NULL, + .enable_stream_gating = NULL, + .edp_backlight_control = dce110_edp_backlight_control, +}; + +void dce110_hw_sequencer_construct(struct dc *dc) +{ + dc->hwss = dce110_funcs; + dc->hwseq->funcs = dce110_private_funcs; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.h new file mode 100644 index 000000000..758f4b3b0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.h @@ -0,0 +1,113 @@ +/* +* 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_HWSS_DCE110_H__ +#define __DC_HWSS_DCE110_H__ + +#include "core_types.h" +#include "hw_sequencer_private.h" + +struct dc; +struct dc_state; +struct dm_pp_display_configuration; + +void dce110_hw_sequencer_construct(struct dc *dc); + +enum dc_status dce110_apply_ctx_to_hw( + struct dc *dc, + struct dc_state *context); + + +void dce110_enable_stream(struct pipe_ctx *pipe_ctx); + +void dce110_disable_stream(struct pipe_ctx *pipe_ctx); + +void dce110_unblank_stream(struct pipe_ctx *pipe_ctx, + struct dc_link_settings *link_settings); + +void dce110_blank_stream(struct pipe_ctx *pipe_ctx); + +void dce110_enable_audio_stream(struct pipe_ctx *pipe_ctx); +void dce110_disable_audio_stream(struct pipe_ctx *pipe_ctx); + +void dce110_update_info_frame(struct pipe_ctx *pipe_ctx); + +void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable); +void dce110_enable_accelerated_mode(struct dc *dc, struct dc_state *context); + +void dce110_power_down(struct dc *dc); + +void dce110_set_safe_displaymarks( + struct resource_context *res_ctx, + const struct resource_pool *pool); + +void dce110_prepare_bandwidth( + struct dc *dc, + struct dc_state *context); + +void dce110_optimize_bandwidth( + struct dc *dc, + struct dc_state *context); + +void dp_receiver_power_ctrl(struct dc_link *link, bool on); + +void dce110_edp_power_control( + struct dc_link *link, + bool power_up); + +void dce110_edp_backlight_control( + struct dc_link *link, + bool enable); + +void dce110_edp_wait_for_hpd_ready( + struct dc_link *link, + bool power_up); + +bool dce110_set_backlight_level(struct pipe_ctx *pipe_ctx, + uint32_t backlight_pwm_u16_16, + uint32_t frame_ramp); +void dce110_set_abm_immediate_disable(struct pipe_ctx *pipe_ctx); +void dce110_set_pipe(struct pipe_ctx *pipe_ctx); +void dce110_disable_link_output(struct dc_link *link, + const struct link_resource *link_res, + enum signal_type signal); +void dce110_enable_lvds_link_output(struct dc_link *link, + const struct link_resource *link_res, + enum clock_source_id clock_source, + uint32_t pixel_clock); +void dce110_enable_tmds_link_output(struct dc_link *link, + const struct link_resource *link_res, + enum signal_type signal, + enum clock_source_id clock_source, + enum dc_color_depth color_depth, + uint32_t pixel_clock); +void dce110_enable_dp_link_output( + struct dc_link *link, + const struct link_resource *link_res, + enum signal_type signal, + enum clock_source_id clock_source, + const struct dc_link_settings *link_settings); +#endif /* __DC_HWSS_DCE110_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c new file mode 100644 index 000000000..db7557a1c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c @@ -0,0 +1,1043 @@ +/* + * 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 "dm_services.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +/* TODO: this needs to be looked at, used by Stella's workaround*/ +#include "gmc/gmc_8_2_d.h" +#include "gmc/gmc_8_2_sh_mask.h" + +#include "include/logger_interface.h" +#include "inc/dce_calcs.h" + +#include "dce/dce_mem_input.h" +#include "dce110_mem_input_v.h" + +static void set_flip_control( + struct dce_mem_input *mem_input110, + bool immediate) +{ + uint32_t value = 0; + + value = dm_read_reg( + mem_input110->base.ctx, + mmUNP_FLIP_CONTROL); + + set_reg_field_value(value, 1, + UNP_FLIP_CONTROL, + GRPH_SURFACE_UPDATE_PENDING_MODE); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_FLIP_CONTROL, + value); +} + +/* chroma part */ +static void program_pri_addr_c( + struct dce_mem_input *mem_input110, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t value = 0; + uint32_t temp = 0; + /*high register MUST be programmed first*/ + temp = address.high_part & +UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C_MASK; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C, + value); + + temp = 0; + value = 0; + temp = address.low_part >> + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_C__GRPH_PRIMARY_SURFACE_ADDRESS_C__SHIFT; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_C, + GRPH_PRIMARY_SURFACE_ADDRESS_C); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_C, + value); +} + +/* luma part */ +static void program_pri_addr_l( + struct dce_mem_input *mem_input110, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t value = 0; + uint32_t temp = 0; + + /*high register MUST be programmed first*/ + temp = address.high_part & +UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L_MASK; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L, + value); + + temp = 0; + value = 0; + temp = address.low_part >> + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_L__GRPH_PRIMARY_SURFACE_ADDRESS_L__SHIFT; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_L, + GRPH_PRIMARY_SURFACE_ADDRESS_L); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_L, + value); +} + +static void program_addr( + struct dce_mem_input *mem_input110, + const struct dc_plane_address *addr) +{ + switch (addr->type) { + case PLN_ADDR_TYPE_GRAPHICS: + program_pri_addr_l( + mem_input110, + addr->grph.addr); + break; + case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE: + program_pri_addr_c( + mem_input110, + addr->video_progressive.chroma_addr); + program_pri_addr_l( + mem_input110, + addr->video_progressive.luma_addr); + break; + default: + /* not supported */ + BREAK_TO_DEBUGGER(); + } +} + +static void enable(struct dce_mem_input *mem_input110) +{ + uint32_t value = 0; + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_GRPH_ENABLE); + set_reg_field_value(value, 1, UNP_GRPH_ENABLE, GRPH_ENABLE); + dm_write_reg(mem_input110->base.ctx, + mmUNP_GRPH_ENABLE, + value); +} + +static void program_tiling( + struct dce_mem_input *mem_input110, + const union dc_tiling_info *info, + const enum surface_pixel_format pixel_format) +{ + uint32_t value = 0; + + set_reg_field_value(value, info->gfx8.num_banks, + UNP_GRPH_CONTROL, GRPH_NUM_BANKS); + + set_reg_field_value(value, info->gfx8.bank_width, + UNP_GRPH_CONTROL, GRPH_BANK_WIDTH_L); + + set_reg_field_value(value, info->gfx8.bank_height, + UNP_GRPH_CONTROL, GRPH_BANK_HEIGHT_L); + + set_reg_field_value(value, info->gfx8.tile_aspect, + UNP_GRPH_CONTROL, GRPH_MACRO_TILE_ASPECT_L); + + set_reg_field_value(value, info->gfx8.tile_split, + UNP_GRPH_CONTROL, GRPH_TILE_SPLIT_L); + + set_reg_field_value(value, info->gfx8.tile_mode, + UNP_GRPH_CONTROL, GRPH_MICRO_TILE_MODE_L); + + set_reg_field_value(value, info->gfx8.pipe_config, + UNP_GRPH_CONTROL, GRPH_PIPE_CONFIG); + + set_reg_field_value(value, info->gfx8.array_mode, + UNP_GRPH_CONTROL, GRPH_ARRAY_MODE); + + set_reg_field_value(value, 1, + UNP_GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE); + + set_reg_field_value(value, 0, + UNP_GRPH_CONTROL, GRPH_Z); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL, + value); + + value = 0; + + set_reg_field_value(value, info->gfx8.bank_width_c, + UNP_GRPH_CONTROL_C, GRPH_BANK_WIDTH_C); + + set_reg_field_value(value, info->gfx8.bank_height_c, + UNP_GRPH_CONTROL_C, GRPH_BANK_HEIGHT_C); + + set_reg_field_value(value, info->gfx8.tile_aspect_c, + UNP_GRPH_CONTROL_C, GRPH_MACRO_TILE_ASPECT_C); + + set_reg_field_value(value, info->gfx8.tile_split_c, + UNP_GRPH_CONTROL_C, GRPH_TILE_SPLIT_C); + + set_reg_field_value(value, info->gfx8.tile_mode_c, + UNP_GRPH_CONTROL_C, GRPH_MICRO_TILE_MODE_C); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL_C, + value); +} + +static void program_size_and_rotation( + struct dce_mem_input *mem_input110, + enum dc_rotation_angle rotation, + const struct plane_size *plane_size) +{ + uint32_t value = 0; + struct plane_size local_size = *plane_size; + + if (rotation == ROTATION_ANGLE_90 || + rotation == ROTATION_ANGLE_270) { + + swap(local_size.surface_size.x, + local_size.surface_size.y); + swap(local_size.surface_size.width, + local_size.surface_size.height); + swap(local_size.chroma_size.x, + local_size.chroma_size.y); + swap(local_size.chroma_size.width, + local_size.chroma_size.height); + } + + value = 0; + set_reg_field_value(value, local_size.surface_pitch, + UNP_GRPH_PITCH_L, GRPH_PITCH_L); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PITCH_L, + value); + + value = 0; + set_reg_field_value(value, local_size.chroma_pitch, + UNP_GRPH_PITCH_C, GRPH_PITCH_C); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_PITCH_C, + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_X_START_L, GRPH_X_START_L); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_X_START_L, + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_X_START_C, GRPH_X_START_C); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_X_START_C, + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_Y_START_L, GRPH_Y_START_L); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_Y_START_L, + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_Y_START_C, GRPH_Y_START_C); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_Y_START_C, + value); + + value = 0; + set_reg_field_value(value, local_size.surface_size.x + + local_size.surface_size.width, + UNP_GRPH_X_END_L, GRPH_X_END_L); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_X_END_L, + value); + + value = 0; + set_reg_field_value(value, local_size.chroma_size.x + + local_size.chroma_size.width, + UNP_GRPH_X_END_C, GRPH_X_END_C); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_X_END_C, + value); + + value = 0; + set_reg_field_value(value, local_size.surface_size.y + + local_size.surface_size.height, + UNP_GRPH_Y_END_L, GRPH_Y_END_L); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_Y_END_L, + value); + + value = 0; + set_reg_field_value(value, local_size.chroma_size.y + + local_size.chroma_size.height, + UNP_GRPH_Y_END_C, GRPH_Y_END_C); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_Y_END_C, + value); + + value = 0; + switch (rotation) { + case ROTATION_ANGLE_90: + set_reg_field_value(value, 3, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + case ROTATION_ANGLE_180: + set_reg_field_value(value, 2, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + case ROTATION_ANGLE_270: + set_reg_field_value(value, 1, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + default: + set_reg_field_value(value, 0, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + } + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_HW_ROTATION, + value); +} + +static void program_pixel_format( + struct dce_mem_input *mem_input110, + enum surface_pixel_format format) +{ + if (format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + uint32_t value; + uint8_t grph_depth; + uint8_t grph_format; + + value = dm_read_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL); + + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: + grph_depth = 0; + 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_ABGR8888: + 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_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + grph_depth = 3; + grph_format = 0; + break; + default: + grph_depth = 2; + grph_format = 0; + break; + } + + set_reg_field_value( + value, + grph_depth, + UNP_GRPH_CONTROL, + GRPH_DEPTH); + set_reg_field_value( + value, + grph_format, + UNP_GRPH_CONTROL, + GRPH_FORMAT); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL, + value); + + value = dm_read_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL_EXP); + + /* VIDEO FORMAT 0 */ + set_reg_field_value( + value, + 0, + UNP_GRPH_CONTROL_EXP, + VIDEO_FORMAT); + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL_EXP, + value); + + } else { + /* Video 422 and 420 needs UNP_GRPH_CONTROL_EXP programmed */ + uint32_t value; + uint8_t video_format; + + value = dm_read_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL_EXP); + + switch (format) { + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + video_format = 2; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + video_format = 3; + break; + default: + video_format = 0; + break; + } + + set_reg_field_value( + value, + video_format, + UNP_GRPH_CONTROL_EXP, + VIDEO_FORMAT); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL_EXP, + value); + } +} + +static bool dce_mem_input_v_is_surface_pending(struct mem_input *mem_input) +{ + struct dce_mem_input *mem_input110 = TO_DCE_MEM_INPUT(mem_input); + uint32_t value; + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_GRPH_UPDATE); + + if (get_reg_field_value(value, UNP_GRPH_UPDATE, + GRPH_SURFACE_UPDATE_PENDING)) + return true; + + mem_input->current_address = mem_input->request_address; + return false; +} + +static bool dce_mem_input_v_program_surface_flip_and_addr( + struct mem_input *mem_input, + const struct dc_plane_address *address, + bool flip_immediate) +{ + struct dce_mem_input *mem_input110 = TO_DCE_MEM_INPUT(mem_input); + + set_flip_control(mem_input110, flip_immediate); + program_addr(mem_input110, + address); + + mem_input->request_address = *address; + + return true; +} + +/* Scatter Gather param tables */ +static const unsigned int dvmm_Hw_Setting_2DTiling[4][9] = { + { 8, 64, 64, 8, 8, 1, 4, 0, 0}, + { 16, 64, 32, 8, 16, 1, 8, 0, 0}, + { 32, 32, 32, 16, 16, 1, 8, 0, 0}, + { 64, 8, 32, 16, 16, 1, 8, 0, 0}, /* fake */ +}; + +static const unsigned int dvmm_Hw_Setting_1DTiling[4][9] = { + { 8, 512, 8, 1, 0, 1, 0, 0, 0}, /* 0 for invalid */ + { 16, 256, 8, 2, 0, 1, 0, 0, 0}, + { 32, 128, 8, 4, 0, 1, 0, 0, 0}, + { 64, 64, 8, 4, 0, 1, 0, 0, 0}, /* fake */ +}; + +static const unsigned int dvmm_Hw_Setting_Linear[4][9] = { + { 8, 4096, 1, 8, 0, 1, 0, 0, 0}, + { 16, 2048, 1, 8, 0, 1, 0, 0, 0}, + { 32, 1024, 1, 8, 0, 1, 0, 0, 0}, + { 64, 512, 1, 8, 0, 1, 0, 0, 0}, /* new for 64bpp from HW */ +}; + +/* Helper to get table entry from surface info */ +static const unsigned int *get_dvmm_hw_setting( + union dc_tiling_info *tiling_info, + enum surface_pixel_format format, + bool chroma) +{ + enum bits_per_pixel { + bpp_8 = 0, + bpp_16, + bpp_32, + bpp_64 + } bpp; + + if (format >= SURFACE_PIXEL_FORMAT_INVALID) + bpp = bpp_32; + else if (format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + bpp = chroma ? bpp_16 : bpp_8; + else + bpp = bpp_8; + + switch (tiling_info->gfx8.array_mode) { + case DC_ARRAY_1D_TILED_THIN1: + case DC_ARRAY_1D_TILED_THICK: + case DC_ARRAY_PRT_TILED_THIN1: + return dvmm_Hw_Setting_1DTiling[bpp]; + case DC_ARRAY_2D_TILED_THIN1: + case DC_ARRAY_2D_TILED_THICK: + case DC_ARRAY_2D_TILED_X_THICK: + case DC_ARRAY_PRT_2D_TILED_THIN1: + case DC_ARRAY_PRT_2D_TILED_THICK: + return dvmm_Hw_Setting_2DTiling[bpp]; + case DC_ARRAY_LINEAR_GENERAL: + case DC_ARRAY_LINEAR_ALLIGNED: + return dvmm_Hw_Setting_Linear[bpp]; + default: + return dvmm_Hw_Setting_2DTiling[bpp]; + } +} + +static void dce_mem_input_v_program_pte_vm( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + enum dc_rotation_angle rotation) +{ + struct dce_mem_input *mem_input110 = TO_DCE_MEM_INPUT(mem_input); + const unsigned int *pte = get_dvmm_hw_setting(tiling_info, format, false); + const unsigned int *pte_chroma = get_dvmm_hw_setting(tiling_info, format, true); + + unsigned int page_width = 0; + unsigned int page_height = 0; + unsigned int page_width_chroma = 0; + unsigned int page_height_chroma = 0; + unsigned int temp_page_width = pte[1]; + unsigned int temp_page_height = pte[2]; + unsigned int min_pte_before_flip = 0; + unsigned int min_pte_before_flip_chroma = 0; + uint32_t value = 0; + + while ((temp_page_width >>= 1) != 0) + page_width++; + while ((temp_page_height >>= 1) != 0) + page_height++; + + temp_page_width = pte_chroma[1]; + temp_page_height = pte_chroma[2]; + while ((temp_page_width >>= 1) != 0) + page_width_chroma++; + while ((temp_page_height >>= 1) != 0) + page_height_chroma++; + + switch (rotation) { + case ROTATION_ANGLE_90: + case ROTATION_ANGLE_270: + min_pte_before_flip = pte[4]; + min_pte_before_flip_chroma = pte_chroma[4]; + break; + default: + min_pte_before_flip = pte[3]; + min_pte_before_flip_chroma = pte_chroma[3]; + break; + } + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_PIPE_OUTSTANDING_REQUEST_LIMIT); + /* TODO: un-hardcode requestlimit */ + set_reg_field_value(value, 0xff, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT_L); + set_reg_field_value(value, 0xff, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT_C); + dm_write_reg(mem_input110->base.ctx, mmUNP_PIPE_OUTSTANDING_REQUEST_LIMIT, value); + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_CONTROL); + set_reg_field_value(value, page_width, UNP_DVMM_PTE_CONTROL, DVMM_PAGE_WIDTH); + set_reg_field_value(value, page_height, UNP_DVMM_PTE_CONTROL, DVMM_PAGE_HEIGHT); + set_reg_field_value(value, min_pte_before_flip, UNP_DVMM_PTE_CONTROL, DVMM_MIN_PTE_BEFORE_FLIP); + dm_write_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_CONTROL, value); + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_ARB_CONTROL); + set_reg_field_value(value, pte[5], UNP_DVMM_PTE_ARB_CONTROL, DVMM_PTE_REQ_PER_CHUNK); + set_reg_field_value(value, 0xff, UNP_DVMM_PTE_ARB_CONTROL, DVMM_MAX_PTE_REQ_OUTSTANDING); + dm_write_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_ARB_CONTROL, value); + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_CONTROL_C); + set_reg_field_value(value, page_width_chroma, UNP_DVMM_PTE_CONTROL_C, DVMM_PAGE_WIDTH_C); + set_reg_field_value(value, page_height_chroma, UNP_DVMM_PTE_CONTROL_C, DVMM_PAGE_HEIGHT_C); + set_reg_field_value(value, min_pte_before_flip_chroma, UNP_DVMM_PTE_CONTROL_C, DVMM_MIN_PTE_BEFORE_FLIP_C); + dm_write_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_CONTROL_C, value); + + value = dm_read_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_ARB_CONTROL_C); + set_reg_field_value(value, pte_chroma[5], UNP_DVMM_PTE_ARB_CONTROL_C, DVMM_PTE_REQ_PER_CHUNK_C); + set_reg_field_value(value, 0xff, UNP_DVMM_PTE_ARB_CONTROL_C, DVMM_MAX_PTE_REQ_OUTSTANDING_C); + dm_write_reg(mem_input110->base.ctx, mmUNP_DVMM_PTE_ARB_CONTROL_C, value); +} + +static void dce_mem_input_v_program_surface_config( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + struct plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizotal_mirror) +{ + struct dce_mem_input *mem_input110 = TO_DCE_MEM_INPUT(mem_input); + + enable(mem_input110); + program_tiling(mem_input110, tiling_info, format); + program_size_and_rotation(mem_input110, rotation, plane_size); + program_pixel_format(mem_input110, format); +} + +static void program_urgency_watermark( + const struct dc_context *ctx, + const uint32_t urgency_addr, + const uint32_t wm_addr, + struct dce_watermarks marks_low, + uint32_t total_dest_line_time_ns) +{ + /* register value */ + uint32_t urgency_cntl = 0; + uint32_t wm_mask_cntl = 0; + + /*Write mask to enable reading/writing of watermark set A*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 1, + DPGV0_WATERMARK_MASK_CONTROL, + URGENCY_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + urgency_cntl = dm_read_reg(ctx, urgency_addr); + + set_reg_field_value( + urgency_cntl, + marks_low.a_mark, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_LOW_WATERMARK); + + set_reg_field_value( + urgency_cntl, + total_dest_line_time_ns, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_HIGH_WATERMARK); + dm_write_reg(ctx, urgency_addr, urgency_cntl); + + /*Write mask to enable reading/writing of watermark set B*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 2, + DPGV0_WATERMARK_MASK_CONTROL, + URGENCY_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + urgency_cntl = dm_read_reg(ctx, urgency_addr); + + set_reg_field_value(urgency_cntl, + marks_low.b_mark, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_LOW_WATERMARK); + + set_reg_field_value(urgency_cntl, + total_dest_line_time_ns, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_HIGH_WATERMARK); + + dm_write_reg(ctx, urgency_addr, urgency_cntl); +} + +static void program_urgency_watermark_l( + const struct dc_context *ctx, + struct dce_watermarks marks_low, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark( + ctx, + mmDPGV0_PIPE_URGENCY_CONTROL, + mmDPGV0_WATERMARK_MASK_CONTROL, + marks_low, + total_dest_line_time_ns); +} + +static void program_urgency_watermark_c( + const struct dc_context *ctx, + struct dce_watermarks marks_low, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark( + ctx, + mmDPGV1_PIPE_URGENCY_CONTROL, + mmDPGV1_WATERMARK_MASK_CONTROL, + marks_low, + total_dest_line_time_ns); +} + +static void program_stutter_watermark( + const struct dc_context *ctx, + const uint32_t stutter_addr, + const uint32_t wm_addr, + struct dce_watermarks marks) +{ + /* register value */ + uint32_t stutter_cntl = 0; + uint32_t wm_mask_cntl = 0; + + /*Write mask to enable reading/writing of watermark set A*/ + + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 1, + DPGV0_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + stutter_cntl = dm_read_reg(ctx, stutter_addr); + + if (ctx->dc->debug.disable_stutter) { + set_reg_field_value(stutter_cntl, + 0, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_ENABLE); + } else { + set_reg_field_value(stutter_cntl, + 1, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_ENABLE); + } + + set_reg_field_value(stutter_cntl, + 1, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_IGNORE_FBC); + + /*Write watermark set A*/ + set_reg_field_value(stutter_cntl, + marks.a_mark, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK); + dm_write_reg(ctx, stutter_addr, stutter_cntl); + + /*Write mask to enable reading/writing of watermark set B*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 2, + DPGV0_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + stutter_cntl = dm_read_reg(ctx, stutter_addr); + /*Write watermark set B*/ + set_reg_field_value(stutter_cntl, + marks.b_mark, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK); + dm_write_reg(ctx, stutter_addr, stutter_cntl); +} + +static void program_stutter_watermark_l( + const struct dc_context *ctx, + struct dce_watermarks marks) +{ + program_stutter_watermark(ctx, + mmDPGV0_PIPE_STUTTER_CONTROL, + mmDPGV0_WATERMARK_MASK_CONTROL, + marks); +} + +static void program_stutter_watermark_c( + const struct dc_context *ctx, + struct dce_watermarks marks) +{ + program_stutter_watermark(ctx, + mmDPGV1_PIPE_STUTTER_CONTROL, + mmDPGV1_WATERMARK_MASK_CONTROL, + marks); +} + +static void program_nbp_watermark( + const struct dc_context *ctx, + const uint32_t wm_mask_ctrl_addr, + const uint32_t nbp_pstate_ctrl_addr, + struct dce_watermarks marks) +{ + uint32_t value; + + /* Write mask to enable reading/writing of watermark set A */ + + value = dm_read_reg(ctx, wm_mask_ctrl_addr); + + set_reg_field_value( + value, + 1, + DPGV0_WATERMARK_MASK_CONTROL, + NB_PSTATE_CHANGE_WATERMARK_MASK); + dm_write_reg(ctx, wm_mask_ctrl_addr, value); + + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_ENABLE); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_URGENT_DURING_REQUEST); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); + + /* Write watermark set A */ + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + set_reg_field_value( + value, + marks.a_mark, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_WATERMARK); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); + + /* Write mask to enable reading/writing of watermark set B */ + value = dm_read_reg(ctx, wm_mask_ctrl_addr); + set_reg_field_value( + value, + 2, + DPGV0_WATERMARK_MASK_CONTROL, + NB_PSTATE_CHANGE_WATERMARK_MASK); + dm_write_reg(ctx, wm_mask_ctrl_addr, value); + + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_ENABLE); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_URGENT_DURING_REQUEST); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); + + /* Write watermark set B */ + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + set_reg_field_value( + value, + marks.b_mark, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_WATERMARK); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); +} + +static void program_nbp_watermark_l( + const struct dc_context *ctx, + struct dce_watermarks marks) +{ + program_nbp_watermark(ctx, + mmDPGV0_WATERMARK_MASK_CONTROL, + mmDPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + marks); +} + +static void program_nbp_watermark_c( + const struct dc_context *ctx, + struct dce_watermarks marks) +{ + program_nbp_watermark(ctx, + mmDPGV1_WATERMARK_MASK_CONTROL, + mmDPGV1_PIPE_NB_PSTATE_CHANGE_CONTROL, + marks); +} + +static void dce_mem_input_v_program_display_marks( + struct mem_input *mem_input, + struct dce_watermarks nbp, + struct dce_watermarks stutter, + struct dce_watermarks stutter_enter, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark_l( + mem_input->ctx, + urgent, + total_dest_line_time_ns); + + program_nbp_watermark_l( + mem_input->ctx, + nbp); + + program_stutter_watermark_l( + mem_input->ctx, + stutter); + +} + +static void dce_mem_input_program_chroma_display_marks( + struct mem_input *mem_input, + struct dce_watermarks nbp, + struct dce_watermarks stutter, + struct dce_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark_c( + mem_input->ctx, + urgent, + total_dest_line_time_ns); + + program_nbp_watermark_c( + mem_input->ctx, + nbp); + + program_stutter_watermark_c( + mem_input->ctx, + stutter); +} + +static void dce110_allocate_mem_input_v( + struct mem_input *mi, + uint32_t h_total,/* for current stream */ + uint32_t v_total,/* for current stream */ + uint32_t pix_clk_khz,/* for current stream */ + uint32_t total_stream_num) +{ + uint32_t addr; + uint32_t value; + uint32_t pix_dur; + if (pix_clk_khz != 0) { + addr = mmDPGV0_PIPE_ARBITRATION_CONTROL1; + value = dm_read_reg(mi->ctx, addr); + pix_dur = 1000000000ULL / pix_clk_khz; + set_reg_field_value( + value, + pix_dur, + DPGV0_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION); + dm_write_reg(mi->ctx, addr, value); + + addr = mmDPGV1_PIPE_ARBITRATION_CONTROL1; + value = dm_read_reg(mi->ctx, addr); + pix_dur = 1000000000ULL / pix_clk_khz; + set_reg_field_value( + value, + pix_dur, + DPGV1_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION); + dm_write_reg(mi->ctx, addr, value); + + addr = mmDPGV0_PIPE_ARBITRATION_CONTROL2; + value = 0x4000800; + dm_write_reg(mi->ctx, addr, value); + + addr = mmDPGV1_PIPE_ARBITRATION_CONTROL2; + value = 0x4000800; + dm_write_reg(mi->ctx, addr, value); + } + +} + +static void dce110_free_mem_input_v( + struct mem_input *mi, + uint32_t total_stream_num) +{ +} + +static const struct mem_input_funcs dce110_mem_input_v_funcs = { + .mem_input_program_display_marks = + dce_mem_input_v_program_display_marks, + .mem_input_program_chroma_display_marks = + dce_mem_input_program_chroma_display_marks, + .allocate_mem_input = dce110_allocate_mem_input_v, + .free_mem_input = dce110_free_mem_input_v, + .mem_input_program_surface_flip_and_addr = + dce_mem_input_v_program_surface_flip_and_addr, + .mem_input_program_pte_vm = + dce_mem_input_v_program_pte_vm, + .mem_input_program_surface_config = + dce_mem_input_v_program_surface_config, + .mem_input_is_flip_pending = + dce_mem_input_v_is_surface_pending +}; +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +void dce110_mem_input_v_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx) +{ + dce_mi->base.funcs = &dce110_mem_input_v_funcs; + dce_mi->base.ctx = ctx; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.h new file mode 100644 index 000000000..f01d4a607 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.h @@ -0,0 +1,35 @@ +/* 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 __DC_MEM_INPUT_V_DCE110_H__ +#define __DC_MEM_INPUT_V_DCE110_H__ + +#include "mem_input.h" +#include "dce/dce_mem_input.h" + +void dce110_mem_input_v_construct( + struct dce_mem_input *dce_mi, + struct dc_context *ctx); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c new file mode 100644 index 000000000..e096d2b95 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c @@ -0,0 +1,737 @@ +/* + * 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 "dce110_transform_v.h" +#include "basics/conversion.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "dce/dce_11_0_enum.h" + +enum { + OUTPUT_CSC_MATRIX_SIZE = 12 +}; + +/* constrast:0 - 2.0, default 1.0 */ +#define UNDERLAY_CONTRAST_DEFAULT 100 +#define UNDERLAY_CONTRAST_MAX 200 +#define UNDERLAY_CONTRAST_MIN 0 +#define UNDERLAY_CONTRAST_STEP 1 +#define UNDERLAY_CONTRAST_DIVIDER 100 + +/* Saturation: 0 - 2.0; default 1.0 */ +#define UNDERLAY_SATURATION_DEFAULT 100 /*1.00*/ +#define UNDERLAY_SATURATION_MIN 0 +#define UNDERLAY_SATURATION_MAX 200 /* 2.00 */ +#define UNDERLAY_SATURATION_STEP 1 /* 0.01 */ +/*actual max overlay saturation + * value = UNDERLAY_SATURATION_MAX /UNDERLAY_SATURATION_DIVIDER + */ + +/* Hue */ +#define UNDERLAY_HUE_DEFAULT 0 +#define UNDERLAY_HUE_MIN -300 +#define UNDERLAY_HUE_MAX 300 +#define UNDERLAY_HUE_STEP 5 +#define UNDERLAY_HUE_DIVIDER 10 /* HW range: -30 ~ +30 */ +#define UNDERLAY_SATURATION_DIVIDER 100 + +/* Brightness: in DAL usually -.25 ~ .25. + * In MMD is -100 to +100 in 16-235 range; which when scaled to full range is + * ~-116 to +116. When normalized this is about 0.4566. + * With 100 divider this becomes 46, but we may use another for better precision + * The ideal one is 100/219 ((100/255)*(255/219)), + * i.e. min/max = +-100, divider = 219 + * default 0.0 + */ +#define UNDERLAY_BRIGHTNESS_DEFAULT 0 +#define UNDERLAY_BRIGHTNESS_MIN -46 /* ~116/255 */ +#define UNDERLAY_BRIGHTNESS_MAX 46 +#define UNDERLAY_BRIGHTNESS_STEP 1 /* .01 */ +#define UNDERLAY_BRIGHTNESS_DIVIDER 100 + +static const struct out_csc_color_matrix global_color_matrix[] = { +{ COLOR_SPACE_SRGB, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_SRGB_LIMITED, + { 0x1B60, 0, 0, 0x200, 0, 0x1B60, 0, 0x200, 0, 0, 0x1B60, 0x200} }, +{ COLOR_SPACE_YCBCR601, + { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x82F, 0x1012, 0x31F, 0x200, 0xFB47, + 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x5D2, 0x1394, 0x1FA, + 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +/* TODO: correct values below */ +{ COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, + 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } +}; + +enum csc_color_mode { + /* 00 - BITS2:0 Bypass */ + CSC_COLOR_MODE_GRAPHICS_BYPASS, + /* 01 - hard coded coefficient TV RGB */ + CSC_COLOR_MODE_GRAPHICS_PREDEFINED, + /* 04 - programmable OUTPUT CSC coefficient */ + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC, +}; + +enum grph_color_adjust_option { + GRPH_COLOR_MATRIX_HW_DEFAULT = 1, + GRPH_COLOR_MATRIX_SW +}; + +static void program_color_matrix_v( + struct dce_transform *xfm_dce, + const struct out_csc_color_matrix *tbl_entry, + enum grph_color_adjust_option options) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t cntl_value = dm_read_reg(ctx, mmCOL_MAN_OUTPUT_CSC_CONTROL); + bool use_set_a = (get_reg_field_value(cntl_value, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE) != 4); + + set_reg_field_value( + cntl_value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + + if (use_set_a) { + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C11_C12_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[0], + OUTPUT_CSC_C11_C12_A, + OUTPUT_CSC_C11_A); + + set_reg_field_value( + value, + tbl_entry->regval[1], + OUTPUT_CSC_C11_C12_A, + OUTPUT_CSC_C12_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C13_C14_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[2], + OUTPUT_CSC_C13_C14_A, + OUTPUT_CSC_C13_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[3], + OUTPUT_CSC_C13_C14_A, + OUTPUT_CSC_C14_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C21_C22_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[4], + OUTPUT_CSC_C21_C22_A, + OUTPUT_CSC_C21_A); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[5], + OUTPUT_CSC_C21_C22_A, + OUTPUT_CSC_C22_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C23_C24_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[6], + OUTPUT_CSC_C23_C24_A, + OUTPUT_CSC_C23_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[7], + OUTPUT_CSC_C23_C24_A, + OUTPUT_CSC_C24_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C31_C32_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[8], + OUTPUT_CSC_C31_C32_A, + OUTPUT_CSC_C31_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[9], + OUTPUT_CSC_C31_C32_A, + OUTPUT_CSC_C32_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C33_C34_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[10], + OUTPUT_CSC_C33_C34_A, + OUTPUT_CSC_C33_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[11], + OUTPUT_CSC_C33_C34_A, + OUTPUT_CSC_C34_A); + + dm_write_reg(ctx, addr, value); + } + set_reg_field_value( + cntl_value, + 4, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + } else { + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C11_C12_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[0], + OUTPUT_CSC_C11_C12_B, + OUTPUT_CSC_C11_B); + + set_reg_field_value( + value, + tbl_entry->regval[1], + OUTPUT_CSC_C11_C12_B, + OUTPUT_CSC_C12_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C13_C14_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[2], + OUTPUT_CSC_C13_C14_B, + OUTPUT_CSC_C13_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[3], + OUTPUT_CSC_C13_C14_B, + OUTPUT_CSC_C14_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C21_C22_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[4], + OUTPUT_CSC_C21_C22_B, + OUTPUT_CSC_C21_B); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[5], + OUTPUT_CSC_C21_C22_B, + OUTPUT_CSC_C22_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C23_C24_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[6], + OUTPUT_CSC_C23_C24_B, + OUTPUT_CSC_C23_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[7], + OUTPUT_CSC_C23_C24_B, + OUTPUT_CSC_C24_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C31_C32_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[8], + OUTPUT_CSC_C31_C32_B, + OUTPUT_CSC_C31_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[9], + OUTPUT_CSC_C31_C32_B, + OUTPUT_CSC_C32_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C33_C34_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[10], + OUTPUT_CSC_C33_C34_B, + OUTPUT_CSC_C33_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[11], + OUTPUT_CSC_C33_C34_B, + OUTPUT_CSC_C34_B); + + dm_write_reg(ctx, addr, value); + } + set_reg_field_value( + cntl_value, + 5, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + } + + dm_write_reg(ctx, mmCOL_MAN_OUTPUT_CSC_CONTROL, cntl_value); +} + +static bool configure_graphics_mode_v( + struct dce_transform *xfm_dce, + enum csc_color_mode config, + enum graphics_csc_adjust_type csc_adjust_type, + enum dc_color_space color_space) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t addr = mmCOL_MAN_OUTPUT_CSC_CONTROL; + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + + if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_SW) { + if (config == CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC) + return true; + + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED: + /* not supported for underlay on CZ */ + return false; + + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + default: + return false; + } + + } else if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_HW) { + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED: + /* not supported for underlay on CZ */ + return false; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + default: + return false; + } + + } else + /* by pass */ + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + + addr = mmCOL_MAN_OUTPUT_CSC_CONTROL; + dm_write_reg(ctx, addr, value); + + return true; +} + +/*TODO: color depth is not correct when this is called*/ +static void set_Denormalization(struct transform *xfm, + enum dc_color_depth color_depth) +{ + uint32_t value = dm_read_reg(xfm->ctx, mmDENORM_CLAMP_CONTROL); + + switch (color_depth) { + case COLOR_DEPTH_888: + /* 255/256 for 8 bit output color depth */ + set_reg_field_value( + value, + 1, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_101010: + /* 1023/1024 for 10 bit output color depth */ + set_reg_field_value( + value, + 2, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_121212: + /* 4095/4096 for 12 bit output color depth */ + set_reg_field_value( + value, + 3, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + default: + /* not valid case */ + break; + } + + set_reg_field_value( + value, + 1, + DENORM_CLAMP_CONTROL, + DENORM_10BIT_OUT); + + dm_write_reg(xfm->ctx, mmDENORM_CLAMP_CONTROL, value); +} + +struct input_csc_matrix { + enum dc_color_space color_space; + uint32_t regval[12]; +}; + +static const struct input_csc_matrix input_csc_matrix[] = { + {COLOR_SPACE_SRGB, +/*1_1 1_2 1_3 1_4 2_1 2_2 2_3 2_4 3_1 3_2 3_3 3_4 */ + {0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, + {COLOR_SPACE_SRGB_LIMITED, + {0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, + {COLOR_SPACE_YCBCR601, + {0x2cdd, 0x2000, 0x0, 0xe991, 0xe926, 0x2000, 0xf4fd, 0x10ef, + 0x0, 0x2000, 0x38b4, 0xe3a6} }, + {COLOR_SPACE_YCBCR601_LIMITED, + {0x3353, 0x2568, 0x0, 0xe400, 0xe5dc, 0x2568, 0xf367, 0x1108, + 0x0, 0x2568, 0x40de, 0xdd3a} }, + {COLOR_SPACE_YCBCR709, + {0x3265, 0x2000, 0, 0xe6ce, 0xf105, 0x2000, 0xfa01, 0xa7d, 0, + 0x2000, 0x3b61, 0xe24f} }, + {COLOR_SPACE_YCBCR709_LIMITED, + {0x39a6, 0x2568, 0, 0xe0d6, 0xeedd, 0x2568, 0xf925, 0x9a8, 0, + 0x2568, 0x43ee, 0xdbb2} } +}; + +static void program_input_csc( + struct transform *xfm, enum dc_color_space color_space) +{ + int arr_size = sizeof(input_csc_matrix)/sizeof(struct input_csc_matrix); + struct dc_context *ctx = xfm->ctx; + const uint32_t *regval = NULL; + bool use_set_a; + uint32_t value; + int i; + + for (i = 0; i < arr_size; i++) + if (input_csc_matrix[i].color_space == color_space) { + regval = input_csc_matrix[i].regval; + break; + } + if (regval == NULL) { + BREAK_TO_DEBUGGER(); + return; + } + + /* + * 1 == set A, the logic is 'if currently we're not using set A, + * then use set A, otherwise use set B' + */ + value = dm_read_reg(ctx, mmCOL_MAN_INPUT_CSC_CONTROL); + use_set_a = get_reg_field_value( + value, COL_MAN_INPUT_CSC_CONTROL, INPUT_CSC_MODE) != 1; + + if (use_set_a) { + /* fixed S2.13 format */ + value = 0; + set_reg_field_value( + value, regval[0], INPUT_CSC_C11_C12_A, INPUT_CSC_C11_A); + set_reg_field_value( + value, regval[1], INPUT_CSC_C11_C12_A, INPUT_CSC_C12_A); + dm_write_reg(ctx, mmINPUT_CSC_C11_C12_A, value); + + value = 0; + set_reg_field_value( + value, regval[2], INPUT_CSC_C13_C14_A, INPUT_CSC_C13_A); + set_reg_field_value( + value, regval[3], INPUT_CSC_C13_C14_A, INPUT_CSC_C14_A); + dm_write_reg(ctx, mmINPUT_CSC_C13_C14_A, value); + + value = 0; + set_reg_field_value( + value, regval[4], INPUT_CSC_C21_C22_A, INPUT_CSC_C21_A); + set_reg_field_value( + value, regval[5], INPUT_CSC_C21_C22_A, INPUT_CSC_C22_A); + dm_write_reg(ctx, mmINPUT_CSC_C21_C22_A, value); + + value = 0; + set_reg_field_value( + value, regval[6], INPUT_CSC_C23_C24_A, INPUT_CSC_C23_A); + set_reg_field_value( + value, regval[7], INPUT_CSC_C23_C24_A, INPUT_CSC_C24_A); + dm_write_reg(ctx, mmINPUT_CSC_C23_C24_A, value); + + value = 0; + set_reg_field_value( + value, regval[8], INPUT_CSC_C31_C32_A, INPUT_CSC_C31_A); + set_reg_field_value( + value, regval[9], INPUT_CSC_C31_C32_A, INPUT_CSC_C32_A); + dm_write_reg(ctx, mmINPUT_CSC_C31_C32_A, value); + + value = 0; + set_reg_field_value( + value, regval[10], INPUT_CSC_C33_C34_A, INPUT_CSC_C33_A); + set_reg_field_value( + value, regval[11], INPUT_CSC_C33_C34_A, INPUT_CSC_C34_A); + dm_write_reg(ctx, mmINPUT_CSC_C33_C34_A, value); + } else { + /* fixed S2.13 format */ + value = 0; + set_reg_field_value( + value, regval[0], INPUT_CSC_C11_C12_B, INPUT_CSC_C11_B); + set_reg_field_value( + value, regval[1], INPUT_CSC_C11_C12_B, INPUT_CSC_C12_B); + dm_write_reg(ctx, mmINPUT_CSC_C11_C12_B, value); + + value = 0; + set_reg_field_value( + value, regval[2], INPUT_CSC_C13_C14_B, INPUT_CSC_C13_B); + set_reg_field_value( + value, regval[3], INPUT_CSC_C13_C14_B, INPUT_CSC_C14_B); + dm_write_reg(ctx, mmINPUT_CSC_C13_C14_B, value); + + value = 0; + set_reg_field_value( + value, regval[4], INPUT_CSC_C21_C22_B, INPUT_CSC_C21_B); + set_reg_field_value( + value, regval[5], INPUT_CSC_C21_C22_B, INPUT_CSC_C22_B); + dm_write_reg(ctx, mmINPUT_CSC_C21_C22_B, value); + + value = 0; + set_reg_field_value( + value, regval[6], INPUT_CSC_C23_C24_B, INPUT_CSC_C23_B); + set_reg_field_value( + value, regval[7], INPUT_CSC_C23_C24_B, INPUT_CSC_C24_B); + dm_write_reg(ctx, mmINPUT_CSC_C23_C24_B, value); + + value = 0; + set_reg_field_value( + value, regval[8], INPUT_CSC_C31_C32_B, INPUT_CSC_C31_B); + set_reg_field_value( + value, regval[9], INPUT_CSC_C31_C32_B, INPUT_CSC_C32_B); + dm_write_reg(ctx, mmINPUT_CSC_C31_C32_B, value); + + value = 0; + set_reg_field_value( + value, regval[10], INPUT_CSC_C33_C34_B, INPUT_CSC_C33_B); + set_reg_field_value( + value, regval[11], INPUT_CSC_C33_C34_B, INPUT_CSC_C34_B); + dm_write_reg(ctx, mmINPUT_CSC_C33_C34_B, value); + } + + /* KK: leave INPUT_CSC_CONVERSION_MODE at default */ + value = 0; + /* + * select 8.4 input type instead of default 12.0. From the discussion + * with HW team, this format depends on the UNP surface format, so for + * 8-bit we should select 8.4 (4 bits truncated). For 10 it should be + * 10.2. For Carrizo we only support 8-bit surfaces on underlay pipe + * so we can always keep this at 8.4 (input_type=2). If the later asics + * start supporting 10+ bits, we will have a problem: surface + * programming including UNP_GRPH* is being done in DalISR after this, + * so either we pass surface format to here, or move this logic to ISR + */ + + set_reg_field_value( + value, 2, COL_MAN_INPUT_CSC_CONTROL, INPUT_CSC_INPUT_TYPE); + set_reg_field_value( + value, + use_set_a ? 1 : 2, + COL_MAN_INPUT_CSC_CONTROL, + INPUT_CSC_MODE); + + dm_write_reg(ctx, mmCOL_MAN_INPUT_CSC_CONTROL, value); +} + +void dce110_opp_v_set_csc_default( + struct transform *xfm, + const struct default_adjustment *default_adjust) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_PREDEFINED; + + if (default_adjust->force_hw_default == false) { + const struct out_csc_color_matrix *elm; + /* currently parameter not in use */ + enum grph_color_adjust_option option; + uint32_t i; + /* + * HW default false we program locally defined matrix + * HW default true we use predefined hw matrix and we + * do not need to program matrix + * OEM wants the HW default via runtime parameter. + */ + option = GRPH_COLOR_MATRIX_SW; + + for (i = 0; i < ARRAY_SIZE(global_color_matrix); ++i) { + elm = &global_color_matrix[i]; + if (elm->color_space != default_adjust->out_color_space) + continue; + /* program the matrix with default values from this + * file + */ + program_color_matrix_v(xfm_dce, elm, option); + config = CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + break; + } + } + + program_input_csc(xfm, default_adjust->in_color_space); + + /* configure the what we programmed : + * 1. Default values from this file + * 2. Use hardware default from ROM_A and we do not need to program + * matrix + */ + + configure_graphics_mode_v(xfm_dce, config, + default_adjust->csc_adjust_type, + default_adjust->out_color_space); + + set_Denormalization(xfm, default_adjust->color_depth); +} + +void dce110_opp_v_set_csc_adjustment( + struct transform *xfm, + const struct out_csc_color_matrix *tbl_entry) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + + program_color_matrix_v( + xfm_dce, tbl_entry, GRPH_COLOR_MATRIX_SW); + + /* We did everything ,now program DxOUTPUT_CSC_CONTROL */ + configure_graphics_mode_v(xfm_dce, config, GRAPHICS_CSC_ADJUST_TYPE_SW, + tbl_entry->color_space); + + /*TODO: Check if denormalization is needed*/ + /*set_Denormalization(opp, adjust->color_depth);*/ +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c new file mode 100644 index 000000000..9b65b77e8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c @@ -0,0 +1,555 @@ +/* + * 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 DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_transform_v.h" + +static void power_on_lut(struct transform *xfm, + bool power_on, bool inputgamma, bool regamma) +{ + uint32_t value = dm_read_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL); + int i; + + if (power_on) { + if (inputgamma) + set_reg_field_value( + value, + 1, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_DIS); + if (regamma) + set_reg_field_value( + value, + 1, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_DIS); + } else { + if (inputgamma) + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_DIS); + if (regamma) + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_DIS); + } + + dm_write_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL, value); + + for (i = 0; i < 3; i++) { + value = dm_read_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL); + if (get_reg_field_value(value, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_DIS) && + get_reg_field_value(value, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_DIS)) + break; + + udelay(2); + } +} + +static void set_bypass_input_gamma(struct dce_transform *xfm_dce) +{ + uint32_t value; + + value = dm_read_reg(xfm_dce->base.ctx, + mmCOL_MAN_INPUT_GAMMA_CONTROL1); + + set_reg_field_value( + value, + 0, + COL_MAN_INPUT_GAMMA_CONTROL1, + INPUT_GAMMA_MODE); + + dm_write_reg(xfm_dce->base.ctx, + mmCOL_MAN_INPUT_GAMMA_CONTROL1, value); +} + +static void configure_regamma_mode(struct dce_transform *xfm_dce, uint32_t mode) +{ + uint32_t value = 0; + + set_reg_field_value( + value, + mode, + GAMMA_CORR_CONTROL, + GAMMA_CORR_MODE); + + dm_write_reg(xfm_dce->base.ctx, mmGAMMA_CORR_CONTROL, 0); +} + +/* + ***************************************************************************** + * Function: regamma_config_regions_and_segments + * + * build regamma curve by using predefined hw points + * uses interface parameters ,like EDID coeff. + * + * @param : parameters interface parameters + * @return void + * + * @note + * + * @see + * + ***************************************************************************** + */ +static void regamma_config_regions_and_segments( + struct dce_transform *xfm_dce, const struct pwl_params *params) +{ + const struct gamma_curve *curve; + uint32_t value = 0; + + { + set_reg_field_value( + value, + params->arr_points[0].custom_float_x, + GAMMA_CORR_CNTLA_START_CNTL, + GAMMA_CORR_CNTLA_EXP_REGION_START); + + set_reg_field_value( + value, + 0, + GAMMA_CORR_CNTLA_START_CNTL, + GAMMA_CORR_CNTLA_EXP_REGION_START_SEGMENT); + + dm_write_reg(xfm_dce->base.ctx, mmGAMMA_CORR_CNTLA_START_CNTL, + value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[0].custom_float_slope, + GAMMA_CORR_CNTLA_SLOPE_CNTL, + GAMMA_CORR_CNTLA_EXP_REGION_LINEAR_SLOPE); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_SLOPE_CNTL, value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[1].custom_float_x, + GAMMA_CORR_CNTLA_END_CNTL1, + GAMMA_CORR_CNTLA_EXP_REGION_END); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_END_CNTL1, value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[1].custom_float_slope, + GAMMA_CORR_CNTLA_END_CNTL2, + GAMMA_CORR_CNTLA_EXP_REGION_END_BASE); + + set_reg_field_value( + value, + params->arr_points[1].custom_float_y, + GAMMA_CORR_CNTLA_END_CNTL2, + GAMMA_CORR_CNTLA_EXP_REGION_END_SLOPE); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_END_CNTL2, value); + } + + curve = params->arr_curve_points; + + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_0_1, + GAMMA_CORR_CNTLA_EXP_REGION0_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_0_1, + GAMMA_CORR_CNTLA_EXP_REGION0_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_0_1, + GAMMA_CORR_CNTLA_EXP_REGION1_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_0_1, + GAMMA_CORR_CNTLA_EXP_REGION1_NUM_SEGMENTS); + + dm_write_reg( + xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_0_1, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_2_3, + GAMMA_CORR_CNTLA_EXP_REGION2_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_2_3, + GAMMA_CORR_CNTLA_EXP_REGION2_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_2_3, + GAMMA_CORR_CNTLA_EXP_REGION3_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_2_3, + GAMMA_CORR_CNTLA_EXP_REGION3_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_2_3, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_4_5, + GAMMA_CORR_CNTLA_EXP_REGION4_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_4_5, + GAMMA_CORR_CNTLA_EXP_REGION4_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_4_5, + GAMMA_CORR_CNTLA_EXP_REGION5_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_4_5, + GAMMA_CORR_CNTLA_EXP_REGION5_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_4_5, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_6_7, + GAMMA_CORR_CNTLA_EXP_REGION6_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_6_7, + GAMMA_CORR_CNTLA_EXP_REGION6_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_6_7, + GAMMA_CORR_CNTLA_EXP_REGION7_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_6_7, + GAMMA_CORR_CNTLA_EXP_REGION7_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_6_7, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_8_9, + GAMMA_CORR_CNTLA_EXP_REGION8_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_8_9, + GAMMA_CORR_CNTLA_EXP_REGION8_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_8_9, + GAMMA_CORR_CNTLA_EXP_REGION9_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_8_9, + GAMMA_CORR_CNTLA_EXP_REGION9_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_8_9, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_10_11, + GAMMA_CORR_CNTLA_EXP_REGION10_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_10_11, + GAMMA_CORR_CNTLA_EXP_REGION10_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_10_11, + GAMMA_CORR_CNTLA_EXP_REGION11_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_10_11, + GAMMA_CORR_CNTLA_EXP_REGION11_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_10_11, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_12_13, + GAMMA_CORR_CNTLA_EXP_REGION12_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_12_13, + GAMMA_CORR_CNTLA_EXP_REGION12_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_12_13, + GAMMA_CORR_CNTLA_EXP_REGION13_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_12_13, + GAMMA_CORR_CNTLA_EXP_REGION13_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_12_13, + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + GAMMA_CORR_CNTLA_REGION_14_15, + GAMMA_CORR_CNTLA_EXP_REGION14_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + GAMMA_CORR_CNTLA_REGION_14_15, + GAMMA_CORR_CNTLA_EXP_REGION14_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + GAMMA_CORR_CNTLA_REGION_14_15, + GAMMA_CORR_CNTLA_EXP_REGION15_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + GAMMA_CORR_CNTLA_REGION_14_15, + GAMMA_CORR_CNTLA_EXP_REGION15_NUM_SEGMENTS); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_14_15, + value); + } +} + +static void program_pwl(struct dce_transform *xfm_dce, + const struct pwl_params *params) +{ + uint32_t value = 0; + + set_reg_field_value( + value, + 7, + GAMMA_CORR_LUT_WRITE_EN_MASK, + GAMMA_CORR_LUT_WRITE_EN_MASK); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_LUT_WRITE_EN_MASK, value); + + dm_write_reg(xfm_dce->base.ctx, + mmGAMMA_CORR_LUT_INDEX, 0); + + /* Program REGAMMA_LUT_DATA */ + { + const uint32_t addr = mmGAMMA_CORR_LUT_DATA; + uint32_t i = 0; + const struct pwl_result_data *rgb = + params->rgb_resulted; + + while (i != params->hw_points_num) { + dm_write_reg(xfm_dce->base.ctx, addr, rgb->red_reg); + dm_write_reg(xfm_dce->base.ctx, addr, rgb->green_reg); + dm_write_reg(xfm_dce->base.ctx, addr, rgb->blue_reg); + + dm_write_reg(xfm_dce->base.ctx, addr, + rgb->delta_red_reg); + dm_write_reg(xfm_dce->base.ctx, addr, + rgb->delta_green_reg); + dm_write_reg(xfm_dce->base.ctx, addr, + rgb->delta_blue_reg); + + ++rgb; + ++i; + } + } +} + +void dce110_opp_program_regamma_pwl_v( + struct transform *xfm, + const struct pwl_params *params) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + /* Setup regions */ + regamma_config_regions_and_segments(xfm_dce, params); + + set_bypass_input_gamma(xfm_dce); + + /* Power on gamma LUT memory */ + power_on_lut(xfm, true, false, true); + + /* Program PWL */ + program_pwl(xfm_dce, params); + + /* program regamma config */ + configure_regamma_mode(xfm_dce, 1); + + /* Power return to auto back */ + power_on_lut(xfm, false, false, true); +} + +void dce110_opp_power_on_regamma_lut_v( + struct transform *xfm, + bool power_on) +{ + uint32_t value = dm_read_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL); + + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_FORCE); + + set_reg_field_value( + value, + power_on, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_DIS); + + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_FORCE); + + set_reg_field_value( + value, + power_on, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_DIS); + + dm_write_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL, value); +} + +void dce110_opp_set_regamma_mode_v( + struct transform *xfm, + enum opp_regamma mode) +{ + // TODO: need to implement the function +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.c new file mode 100644 index 000000000..3545e43a4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.c @@ -0,0 +1,54 @@ +/* + * 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 DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce/dce_opp.h" +#include "dce110_opp_v.h" + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +static const struct opp_funcs funcs = { + .opp_set_dyn_expansion = dce110_opp_set_dyn_expansion, + .opp_destroy = dce110_opp_destroy, + .opp_program_fmt = dce110_opp_program_fmt, + .opp_program_bit_depth_reduction = + dce110_opp_program_bit_depth_reduction +}; + +void dce110_opp_v_construct(struct dce110_opp *opp110, + struct dc_context *ctx) +{ + opp110->base.funcs = &funcs; + + opp110->base.ctx = ctx; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.h new file mode 100644 index 000000000..152af4c41 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.h @@ -0,0 +1,39 @@ +/* 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_OPP_DCE110_V_H__ +#define __DC_OPP_DCE110_V_H__ + +#include "dc_types.h" +#include "opp.h" +#include "core_types.h" + +void dce110_opp_v_construct(struct dce110_opp *opp110, + struct dc_context *ctx); + +/* underlay callbacks */ + + + +#endif /* __DC_OPP_DCE110_V_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c new file mode 100644 index 000000000..f808315b2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c @@ -0,0 +1,1547 @@ +/* + * 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 "link_encoder.h" +#include "stream_encoder.h" + +#include "resource.h" +#include "dce110/dce110_resource.h" +#include "include/irq_service_interface.h" +#include "dce/dce_audio.h" +#include "dce110/dce110_timing_generator.h" +#include "irq/dce110/irq_service_dce110.h" +#include "dce110/dce110_timing_generator_v.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_mem_input.h" +#include "dce110/dce110_mem_input_v.h" +#include "dce/dce_ipp.h" +#include "dce/dce_transform.h" +#include "dce110/dce110_transform_v.h" +#include "dce/dce_opp.h" +#include "dce110/dce110_opp_v.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_hwseq.h" +#include "dce110/dce110_hw_sequencer.h" +#include "dce/dce_aux.h" +#include "dce/dce_abm.h" +#include "dce/dce_dmcu.h" +#include "dce/dce_i2c.h" +#include "dce/dce_panel_cntl.h" + +#define DC_LOGGER \ + dc->ctx->logger + +#include "dce110/dce110_compressor.h" + +#include "reg_helper.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#ifndef mmMC_HUB_RDREQ_DMIF_LIMIT +#include "gmc/gmc_8_2_d.h" +#include "gmc/gmc_8_2_sh_mask.h" +#endif + +#ifndef mmDP_DPHY_INTERNAL_CTRL + #define mmDP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x4ba7 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x4ca7 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x4da7 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x4ea7 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4fa7 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x54a7 + #define mmDP7_DP_DPHY_INTERNAL_CTRL 0x56a7 + #define mmDP8_DP_DPHY_INTERNAL_CTRL 0x57a7 +#endif + +#ifndef mmBIOS_SCRATCH_2 + #define mmBIOS_SCRATCH_2 0x05CB + #define mmBIOS_SCRATCH_3 0x05CC + #define mmBIOS_SCRATCH_6 0x05CF +#endif + +#ifndef mmDP_DPHY_BS_SR_SWAP_CNTL + #define mmDP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP0_DP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP1_DP_DPHY_BS_SR_SWAP_CNTL 0x4BDC + #define mmDP2_DP_DPHY_BS_SR_SWAP_CNTL 0x4CDC + #define mmDP3_DP_DPHY_BS_SR_SWAP_CNTL 0x4DDC + #define mmDP4_DP_DPHY_BS_SR_SWAP_CNTL 0x4EDC + #define mmDP5_DP_DPHY_BS_SR_SWAP_CNTL 0x4FDC + #define mmDP6_DP_DPHY_BS_SR_SWAP_CNTL 0x54DC +#endif + +#ifndef mmDP_DPHY_FAST_TRAINING + #define mmDP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP0_DP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP1_DP_DPHY_FAST_TRAINING 0x4BBC + #define mmDP2_DP_DPHY_FAST_TRAINING 0x4CBC + #define mmDP3_DP_DPHY_FAST_TRAINING 0x4DBC + #define mmDP4_DP_DPHY_FAST_TRAINING 0x4EBC + #define mmDP5_DP_DPHY_FAST_TRAINING 0x4FBC + #define mmDP6_DP_DPHY_FAST_TRAINING 0x54BC +#endif + +#ifndef DPHY_RX_FAST_TRAINING_CAPABLE + #define DPHY_RX_FAST_TRAINING_CAPABLE 0x1 +#endif + +static const struct dce110_timing_generator_offsets dce110_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + } +}; + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + +static const struct dce_dmcu_registers dmcu_regs = { + DMCU_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_dmcu_shift dmcu_shift = { + DMCU_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_dmcu_mask dmcu_mask = { + DMCU_MASK_SH_LIST_DCE110(_MASK) +}; + +static const struct dce_abm_registers abm_regs = { + ABM_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCE110(_MASK) +}; + +#define ipp_regs(id)\ +[id] = {\ + IPP_DCE110_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE110(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + + +#define link_regs(id)\ +[id] = {\ + LE_DCE110_REG_LIST(id)\ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST(id),\ + .TMDS_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +static const struct dce_panel_cntl_registers panel_cntl_regs[] = { + { DCE_PANEL_CNTL_REG_LIST() } +}; + +static const struct dce_panel_cntl_shift panel_cntl_shift = { + DCE_PANEL_CNTL_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_panel_cntl_mask panel_cntl_mask = { + DCE_PANEL_CNTL_MASK_SH_LIST(_MASK) +}; + +static const struct dce110_aux_registers_shift aux_shift = { + DCE_AUX_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce110_aux_registers_mask aux_mask = { + DCE_AUX_MASK_SH_LIST(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_110_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_110(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_110(_MASK) +}; + +#define aux_engine_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = 0 \ +} + +static const struct dce110_aux_registers aux_engine_regs[] = { + aux_engine_regs(0), + aux_engine_regs(1), + aux_engine_regs(2), + aux_engine_regs(3), + aux_engine_regs(4), + aux_engine_regs(5) +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5), + audio_regs(6), +}; + +static const struct dce_audio_shift audio_shift = { + AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_audio_mask audio_mask = { + AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +/* AG TBD Needs to be reduced back to 3 pipes once dce10 hw sequencer implemented. */ + + +#define clk_src_regs(id)\ +[id] = {\ + CS_COMMON_REG_LIST_DCE_100_110(id),\ +} + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0), + clk_src_regs(1), + clk_src_regs(2) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3, + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 +}; + +static const struct resource_caps carrizo_resource_cap = { + .num_timing_generator = 3, + .num_video_plane = 1, + .num_audio = 3, + .num_stream_encoder = 3, + .num_pll = 2, + .num_ddc = 3, +}; + +static const struct resource_caps stoney_resource_cap = { + .num_timing_generator = 2, + .num_video_plane = 1, + .num_audio = 3, + .num_stream_encoder = 3, + .num_pll = 2, + .num_ddc = 3, +}; + +static const struct dc_plane_cap plane_cap = { + .type = DC_PLANE_TYPE_DCE_RGB, + .blends_with_below = true, + .blends_with_above = true, + .per_pixel_alpha = 1, + + .pixel_format_support = { + .argb8888 = true, + .nv12 = false, + .fp16 = true + }, + + .max_upscale_factor = { + .argb8888 = 16000, + .nv12 = 1, + .fp16 = 1 + }, + + .max_downscale_factor = { + .argb8888 = 250, + .nv12 = 1, + .fp16 = 1 + }, + 64, + 64 +}; + +static const struct dc_plane_cap underlay_plane_cap = { + .type = DC_PLANE_TYPE_DCE_UNDERLAY, + .blends_with_above = true, + .per_pixel_alpha = 1, + + .pixel_format_support = { + .argb8888 = false, + .nv12 = true, + .fp16 = false + }, + + .max_upscale_factor = { + .argb8888 = 1, + .nv12 = 16000, + .fp16 = 1 + }, + + .max_downscale_factor = { + .argb8888 = 1, + .nv12 = 250, + .fp16 = 1 + }, + 64, + 64 +}; + +#define CTX ctx +#define REG(reg) mm ## reg + +#ifndef mmCC_DC_HDMI_STRAPS +#define mmCC_DC_HDMI_STRAPS 0x4819 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE_MASK 0x40 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE__SHIFT 0x6 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x700 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 +#endif + +static int map_transmitter_id_to_phy_instance( + enum transmitter transmitter) +{ + switch (transmitter) { + case TRANSMITTER_UNIPHY_A: + return 0; + case TRANSMITTER_UNIPHY_B: + return 1; + case TRANSMITTER_UNIPHY_C: + return 2; + case TRANSMITTER_UNIPHY_D: + return 3; + case TRANSMITTER_UNIPHY_E: + return 4; + case TRANSMITTER_UNIPHY_F: + return 5; + case TRANSMITTER_UNIPHY_G: + return 6; + default: + ASSERT(0); + return 0; + } +} + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + REG_GET_2(CC_DC_HDMI_STRAPS, + HDMI_DISABLE, &straps->hdmi_disable, + AUDIO_STREAM_NUMBER, &straps->audio_stream_number); + + REG_GET(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO, &straps->dc_pinstraps_audio); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static struct timing_generator *dce110_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce110_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static struct stream_encoder *dce110_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_stoney_reg = { + HWSEQ_ST_REG_LIST() +}; + +static const struct dce_hwseq_registers hwseq_cz_reg = { + HWSEQ_CZ_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE11_MASK_SH_LIST(__SHIFT), +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE11_MASK_SH_LIST(_MASK), +}; + +static struct dce_hwseq *dce110_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = ASIC_REV_IS_STONEY(ctx->asic_id.hw_internal_rev) ? + &hwseq_stoney_reg : &hwseq_cz_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + hws->wa.blnd_crtc_trigger = true; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce110_stream_encoder_create, + .create_hwseq = dce110_hwseq_create, +}; + +#define mi_inst_regs(id) { \ + MI_DCE11_REG_LIST(id), \ + .MC_HUB_RDREQ_DMIF_LIMIT = mmMC_HUB_RDREQ_DMIF_LIMIT \ +} +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE11_MASK_SH_LIST(__SHIFT), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE__SHIFT +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE11_MASK_SH_LIST(_MASK), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE_MASK +}; + + +static struct mem_input *dce110_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + dce_mi->wa.single_head_rdreq_dmif_limit = 3; + return &dce_mi->base; +} + +static void dce110_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce110_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + return &transform->base; +} + +static struct input_pixel_processor *dce110_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 300000, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true +}; + +static struct link_encoder *dce110_link_encoder_create( + struct dc_context *ctx, + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + int link_regs_id; + + if (!enc110) + return NULL; + + link_regs_id = + map_transmitter_id_to_phy_instance(enc_init_data->transmitter); + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[link_regs_id], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + return &enc110->base; +} + +static struct panel_cntl *dce110_panel_cntl_create(const struct panel_cntl_init_data *init_data) +{ + struct dce_panel_cntl *panel_cntl = + kzalloc(sizeof(struct dce_panel_cntl), GFP_KERNEL); + + if (!panel_cntl) + return NULL; + + dce_panel_cntl_construct(panel_cntl, + init_data, + &panel_cntl_regs[init_data->inst], + &panel_cntl_shift, + &panel_cntl_mask); + + return &panel_cntl->base; +} + +static struct output_pixel_processor *dce110_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} + +static struct dce_aux *dce110_aux_engine_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct aux_engine_dce110 *aux_engine = + kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); + + if (!aux_engine) + return NULL; + + dce110_aux_engine_construct(aux_engine, ctx, inst, + SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, + &aux_engine_regs[inst], + &aux_mask, + &aux_shift, + ctx->dc->caps.extended_aux_timeout_support); + + return &aux_engine->base; +} +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +static struct dce_i2c_hw *dce110_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + + dce100_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} +static struct clock_source *dce110_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + kfree(clk_src); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static void dce110_clock_source_destroy(struct clock_source **clk_src) +{ + struct dce110_clk_src *dce110_clk_src; + + if (!clk_src) + return; + + dce110_clk_src = TO_DCE110_CLK_SRC(*clk_src); + + kfree(dce110_clk_src->dp_ss_params); + kfree(dce110_clk_src->hdmi_ss_params); + kfree(dce110_clk_src->dvi_ss_params); + + kfree(dce110_clk_src); + *clk_src = NULL; +} + +static void dce110_resource_destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce110_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + if (pool->base.engines[i] != NULL) + dce110_engine_destroy(&pool->base.engines[i]); + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) { + dce110_clock_source_destroy(&pool->base.clock_sources[i]); + } + } + + if (pool->base.dp_clock_source != NULL) + dce110_clock_source_destroy(&pool->base.dp_clock_source); + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i] != NULL) { + dce_aud_destroy(&pool->base.audios[i]); + } + } + + if (pool->base.abm != NULL) + dce_abm_destroy(&pool->base.abm); + + if (pool->base.dmcu != NULL) + dce_dmcu_destroy(&pool->base.dmcu); + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } +} + + +static void get_pixel_clock_parameters( + const struct pipe_ctx *pipe_ctx, + struct pixel_clk_params *pixel_clk_params) +{ + const struct dc_stream_state *stream = pipe_ctx->stream; + + /*TODO: is this halved for YCbCr 420? in that case we might want to move + * the pixel clock normalization for hdmi up to here instead of doing it + * in pll_adjust_pix_clk + */ + pixel_clk_params->requested_pix_clk_100hz = stream->timing.pix_clk_100hz; + pixel_clk_params->encoder_object_id = stream->link->link_enc->id; + pixel_clk_params->signal_type = pipe_ctx->stream->signal; + pixel_clk_params->controller_id = pipe_ctx->stream_res.tg->inst + 1; + /* TODO: un-hardcode*/ + pixel_clk_params->requested_sym_clk = LINK_RATE_LOW * + LINK_RATE_REF_FREQ_IN_KHZ; + pixel_clk_params->flags.ENABLE_SS = 0; + pixel_clk_params->color_depth = + stream->timing.display_color_depth; + pixel_clk_params->flags.DISPLAY_BLANKED = 1; + pixel_clk_params->flags.SUPPORT_YCBCR420 = (stream->timing.pixel_encoding == + PIXEL_ENCODING_YCBCR420); + pixel_clk_params->pixel_encoding = stream->timing.pixel_encoding; + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) { + pixel_clk_params->color_depth = COLOR_DEPTH_888; + } + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) { + pixel_clk_params->requested_pix_clk_100hz = pixel_clk_params->requested_pix_clk_100hz / 2; + } + if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING) + pixel_clk_params->requested_pix_clk_100hz *= 2; + +} + +void dce110_resource_build_pipe_hw_param(struct pipe_ctx *pipe_ctx) +{ + get_pixel_clock_parameters(pipe_ctx, &pipe_ctx->stream_res.pix_clk_params); + pipe_ctx->clock_source->funcs->get_pix_clk_dividers( + pipe_ctx->clock_source, + &pipe_ctx->stream_res.pix_clk_params, + &pipe_ctx->pll_settings); + resource_build_bit_depth_reduction_params(pipe_ctx->stream, + &pipe_ctx->stream->bit_depth_params); + pipe_ctx->stream->clamping.pixel_encoding = pipe_ctx->stream->timing.pixel_encoding; +} + +static bool is_surface_pixel_format_supported(struct pipe_ctx *pipe_ctx, unsigned int underlay_idx) +{ + if (pipe_ctx->pipe_idx != underlay_idx) + return true; + if (!pipe_ctx->plane_state) + return false; + if (pipe_ctx->plane_state->format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + return true; +} + +static enum dc_status build_mapped_resource( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + struct pipe_ctx *pipe_ctx = resource_get_head_pipe_for_stream(&context->res_ctx, stream); + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; + + if (!is_surface_pixel_format_supported(pipe_ctx, + dc->res_pool->underlay_pipe_index)) + return DC_SURFACE_PIXEL_FORMAT_UNSUPPORTED; + + dce110_resource_build_pipe_hw_param(pipe_ctx); + + /* TODO: validate audio ASIC caps, encoder */ + + resource_build_info_frame(pipe_ctx); + + return DC_OK; +} + +static bool dce110_validate_bandwidth( + struct dc *dc, + struct dc_state *context, + bool fast_validate) +{ + bool result = false; + + DC_LOG_BANDWIDTH_CALCS( + "%s: start", + __func__); + + if (bw_calcs( + dc->ctx, + dc->bw_dceip, + dc->bw_vbios, + context->res_ctx.pipe_ctx, + dc->res_pool->pipe_count, + &context->bw_ctx.bw.dce)) + result = true; + + if (!result) + DC_LOG_BANDWIDTH_VALIDATION("%s: %dx%d@%d Bandwidth validation failed!\n", + __func__, + context->streams[0]->timing.h_addressable, + context->streams[0]->timing.v_addressable, + context->streams[0]->timing.pix_clk_100hz / 10); + + if (memcmp(&dc->current_state->bw_ctx.bw.dce, + &context->bw_ctx.bw.dce, sizeof(context->bw_ctx.bw.dce))) { + + DC_LOG_BANDWIDTH_CALCS( + "%s: finish,\n" + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n" + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n" + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d stutter_mode_enable: %d\n" + "cstate: %d pstate: %d nbpstate: %d sync: %d dispclk: %d\n" + "sclk: %d sclk_sleep: %d yclk: %d blackout_recovery_time_us: %d\n" + , + __func__, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[0].b_mark, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[0].a_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[0].b_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[0].a_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[0].b_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[0].a_mark, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[1].b_mark, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[1].a_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[1].b_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[1].a_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[1].b_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[1].a_mark, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[2].b_mark, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[2].a_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[2].b_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[2].a_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[2].b_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[2].a_mark, + context->bw_ctx.bw.dce.stutter_mode_enable, + context->bw_ctx.bw.dce.cpuc_state_change_enable, + context->bw_ctx.bw.dce.cpup_state_change_enable, + context->bw_ctx.bw.dce.nbp_state_change_enable, + context->bw_ctx.bw.dce.all_displays_in_sync, + context->bw_ctx.bw.dce.dispclk_khz, + context->bw_ctx.bw.dce.sclk_khz, + context->bw_ctx.bw.dce.sclk_deep_sleep_khz, + context->bw_ctx.bw.dce.yclk_khz, + context->bw_ctx.bw.dce.blackout_recovery_time_us); + } + return result; +} + +static enum dc_status dce110_validate_plane(const struct dc_plane_state *plane_state, + struct dc_caps *caps) +{ + if (((plane_state->dst_rect.width * 2) < plane_state->src_rect.width) || + ((plane_state->dst_rect.height * 2) < plane_state->src_rect.height)) + return DC_FAIL_SURFACE_VALIDATE; + + return DC_OK; +} + +static bool dce110_validate_surface_sets( + struct dc_state *context) +{ + int i, j; + + for (i = 0; i < context->stream_count; i++) { + if (context->stream_status[i].plane_count == 0) + continue; + + if (context->stream_status[i].plane_count > 2) + return false; + + for (j = 0; j < context->stream_status[i].plane_count; j++) { + struct dc_plane_state *plane = + context->stream_status[i].plane_states[j]; + + /* underlay validation */ + if (plane->format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + + if ((plane->src_rect.width > 1920 || + plane->src_rect.height > 1080)) + return false; + + /* we don't have the logic to support underlay + * only yet so block the use case where we get + * NV12 plane as top layer + */ + if (j == 0) + return false; + + /* irrespective of plane format, + * stream should be RGB encoded + */ + if (context->streams[i]->timing.pixel_encoding + != PIXEL_ENCODING_RGB) + return false; + + } + + } + } + + return true; +} + +static enum dc_status dce110_validate_global( + struct dc *dc, + struct dc_state *context) +{ + if (!dce110_validate_surface_sets(context)) + return DC_FAIL_SURFACE_VALIDATE; + + return DC_OK; +} + +static enum dc_status dce110_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + result = resource_map_pool_resources(dc, new_ctx, dc_stream); + + if (result == DC_OK) + result = resource_map_clock_resources(dc, new_ctx, dc_stream); + + + if (result == DC_OK) + result = build_mapped_resource(dc, new_ctx, dc_stream); + + return result; +} + +static struct pipe_ctx *dce110_acquire_underlay( + struct dc_state *context, + const struct resource_pool *pool, + struct dc_stream_state *stream) +{ + struct dc *dc = stream->ctx->dc; + struct dce_hwseq *hws = dc->hwseq; + struct resource_context *res_ctx = &context->res_ctx; + unsigned int underlay_idx = pool->underlay_pipe_index; + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[underlay_idx]; + + if (res_ctx->pipe_ctx[underlay_idx].stream) + return NULL; + + pipe_ctx->stream_res.tg = pool->timing_generators[underlay_idx]; + pipe_ctx->plane_res.mi = pool->mis[underlay_idx]; + /*pipe_ctx->plane_res.ipp = res_ctx->pool->ipps[underlay_idx];*/ + pipe_ctx->plane_res.xfm = pool->transforms[underlay_idx]; + pipe_ctx->stream_res.opp = pool->opps[underlay_idx]; + pipe_ctx->pipe_idx = underlay_idx; + + pipe_ctx->stream = stream; + + if (!dc->current_state->res_ctx.pipe_ctx[underlay_idx].stream) { + struct tg_color black_color = {0}; + struct dc_bios *dcb = dc->ctx->dc_bios; + + hws->funcs.enable_display_power_gating( + dc, + pipe_ctx->stream_res.tg->inst, + dcb, PIPE_GATING_CONTROL_DISABLE); + + /* + * This is for powering on underlay, so crtc does not + * need to be enabled + */ + + pipe_ctx->stream_res.tg->funcs->program_timing(pipe_ctx->stream_res.tg, + &stream->timing, + 0, + 0, + 0, + 0, + pipe_ctx->stream->signal, + false); + + pipe_ctx->stream_res.tg->funcs->enable_advanced_request( + pipe_ctx->stream_res.tg, + true, + &stream->timing); + + pipe_ctx->plane_res.mi->funcs->allocate_mem_input(pipe_ctx->plane_res.mi, + stream->timing.h_total, + stream->timing.v_total, + stream->timing.pix_clk_100hz / 10, + context->stream_count); + + color_space_to_black_color(dc, + COLOR_SPACE_YCBCR601, &black_color); + pipe_ctx->stream_res.tg->funcs->set_blank_color( + pipe_ctx->stream_res.tg, + &black_color); + } + + return pipe_ctx; +} + +static void dce110_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + dce110_resource_destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + +struct stream_encoder *dce110_find_first_free_match_stream_enc_for_link( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct dc_stream_state *stream) +{ + int i; + int j = -1; + struct dc_link *link = stream->link; + + for (i = 0; i < pool->stream_enc_count; i++) { + if (!res_ctx->is_stream_enc_acquired[i] && + pool->stream_enc[i]) { + /* Store first available for MST second display + * in daisy chain use case + */ + j = i; + if (pool->stream_enc[i]->id == + link->link_enc->preferred_engine) + return pool->stream_enc[i]; + } + } + + /* + * For CZ and later, we can allow DIG FE and BE to differ for all display types + */ + + if (j >= 0) + return pool->stream_enc[j]; + + return NULL; +} + + +static const struct resource_funcs dce110_res_pool_funcs = { + .destroy = dce110_destroy_resource_pool, + .link_enc_create = dce110_link_encoder_create, + .panel_cntl_create = dce110_panel_cntl_create, + .validate_bandwidth = dce110_validate_bandwidth, + .validate_plane = dce110_validate_plane, + .acquire_idle_pipe_for_layer = dce110_acquire_underlay, + .add_stream_to_ctx = dce110_add_stream_to_ctx, + .validate_global = dce110_validate_global, + .find_first_free_match_stream_enc_for_link = dce110_find_first_free_match_stream_enc_for_link +}; + +static bool underlay_create(struct dc_context *ctx, struct resource_pool *pool) +{ + struct dce110_timing_generator *dce110_tgv = kzalloc(sizeof(*dce110_tgv), + GFP_KERNEL); + struct dce_transform *dce110_xfmv = kzalloc(sizeof(*dce110_xfmv), + GFP_KERNEL); + struct dce_mem_input *dce110_miv = kzalloc(sizeof(*dce110_miv), + GFP_KERNEL); + struct dce110_opp *dce110_oppv = kzalloc(sizeof(*dce110_oppv), + GFP_KERNEL); + + if (!dce110_tgv || !dce110_xfmv || !dce110_miv || !dce110_oppv) { + kfree(dce110_tgv); + kfree(dce110_xfmv); + kfree(dce110_miv); + kfree(dce110_oppv); + return false; + } + + dce110_opp_v_construct(dce110_oppv, ctx); + + dce110_timing_generator_v_construct(dce110_tgv, ctx); + dce110_mem_input_v_construct(dce110_miv, ctx); + dce110_transform_v_construct(dce110_xfmv, ctx); + + pool->opps[pool->pipe_count] = &dce110_oppv->base; + pool->timing_generators[pool->pipe_count] = &dce110_tgv->base; + pool->mis[pool->pipe_count] = &dce110_miv->base; + pool->transforms[pool->pipe_count] = &dce110_xfmv->base; + pool->pipe_count++; + + /* update the public caps to indicate an underlay is available */ + ctx->dc->caps.max_slave_planes = 1; + ctx->dc->caps.max_slave_yuv_planes = 1; + ctx->dc->caps.max_slave_rgb_planes = 0; + + return true; +} + +static void bw_calcs_data_update_from_pplib(struct dc *dc) +{ + struct dm_pp_clock_levels clks = {0}; + + /*do system clock*/ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, + &clks); + /* convert all the clock fro kHz to fix point mHz */ + dc->bw_vbios->high_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1], 1000); + dc->bw_vbios->mid1_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels/8], 1000); + dc->bw_vbios->mid2_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*2/8], 1000); + dc->bw_vbios->mid3_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*3/8], 1000); + dc->bw_vbios->mid4_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*4/8], 1000); + dc->bw_vbios->mid5_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*5/8], 1000); + dc->bw_vbios->mid6_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*6/8], 1000); + dc->bw_vbios->low_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[0], 1000); + dc->sclk_lvls = clks; + + /*do display clock*/ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_DISPLAY_CLK, + &clks); + dc->bw_vbios->high_voltage_max_dispclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1], 1000); + dc->bw_vbios->mid_voltage_max_dispclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels>>1], 1000); + dc->bw_vbios->low_voltage_max_dispclk = bw_frc_to_fixed( + clks.clocks_in_khz[0], 1000); + + /*do memory clock*/ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, + &clks); + + dc->bw_vbios->low_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[0] * MEMORY_TYPE_MULTIPLIER_CZ, 1000); + dc->bw_vbios->mid_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels>>1] * MEMORY_TYPE_MULTIPLIER_CZ, + 1000); + dc->bw_vbios->high_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1] * MEMORY_TYPE_MULTIPLIER_CZ, + 1000); +} + +static const struct resource_caps *dce110_resource_cap( + struct hw_asic_id *asic_id) +{ + if (ASIC_REV_IS_STONEY(asic_id->hw_internal_rev)) + return &stoney_resource_cap; + else + return &carrizo_resource_cap; +} + +static bool dce110_resource_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool, + struct hw_asic_id asic_id) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_bios *bp; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = dce110_resource_cap(&ctx->asic_id); + pool->base.funcs = &dce110_res_pool_funcs; + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + + pool->base.pipe_count = pool->base.res_cap->num_timing_generator; + pool->base.underlay_pipe_index = pool->base.pipe_count; + pool->base.timing_generator_count = pool->base.res_cap->num_timing_generator; + dc->caps.max_downscale_ratio = 150; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.i2c_speed_in_khz_hdcp = 40; + dc->caps.max_cursor_size = 128; + dc->caps.min_horizontal_blanking_period = 80; + dc->caps.is_apu = true; + dc->caps.extended_aux_timeout_support = false; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce110_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce110_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, + &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce110_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, + &clk_src_regs[1], false); + + pool->base.clk_src_count = 2; + + /* TODO: find out if CZ support 3 PLLs */ + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce110_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce110_timing_generator_create( + ctx, i, &dce110_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce110_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce110_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce110_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce110_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce110_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce110_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = NULL; + } + + if (dc->config.fbc_support) + dc->fbc_compressor = dce110_compressor_create(ctx); + + if (!underlay_create(ctx, &pool->base)) + goto res_create_fail; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce110_hw_sequencer_construct(dc); + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < pool->base.underlay_pipe_index; ++i) + dc->caps.planes[i] = plane_cap; + + dc->caps.planes[pool->base.underlay_pipe_index] = underlay_plane_cap; + + bw_calcs_init(dc->bw_dceip, dc->bw_vbios, dc->ctx->asic_id); + + bw_calcs_data_update_from_pplib(dc); + + return true; + +res_create_fail: + dce110_resource_destruct(pool); + return false; +} + +struct resource_pool *dce110_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc, + struct hw_asic_id asic_id) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce110_resource_construct(num_virtual_links, dc, pool, asic_id)) + return &pool->base; + + kfree(pool); + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.h new file mode 100644 index 000000000..aa4531e08 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.h @@ -0,0 +1,54 @@ +/* +* 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_RESOURCE_DCE110_H__ +#define __DC_RESOURCE_DCE110_H__ + +#include "core_types.h" + +struct dc; +struct resource_pool; + +#define TO_DCE110_RES_POOL(pool)\ + container_of(pool, struct dce110_resource_pool, base) + +struct dce110_resource_pool { + struct resource_pool base; +}; + +void dce110_resource_build_pipe_hw_param(struct pipe_ctx *pipe_ctx); + +struct resource_pool *dce110_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc, + struct hw_asic_id asic_id); + +struct stream_encoder *dce110_find_first_free_match_stream_enc_for_link( + struct resource_context *res_ctx, + const struct resource_pool *pool, + struct dc_stream_state *stream); + +#endif /* __DC_RESOURCE_DCE110_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c new file mode 100644 index 000000000..27cbb5b42 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c @@ -0,0 +1,2266 @@ +/* + * 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 DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dc_types.h" +#include "dc_bios_types.h" +#include "dc.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "dce110_timing_generator.h" + +#include "timing_generator.h" + + +#define NUMBER_OF_FRAME_TO_WAIT_ON_TRIGGERED_RESET 10 + +#define MAX_H_TOTAL (CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1) +#define MAX_V_TOTAL (CRTC_V_TOTAL__CRTC_V_TOTAL_MASKhw + 1) + +#define CRTC_REG(reg) (reg + tg110->offsets.crtc) +#define DCP_REG(reg) (reg + tg110->offsets.dcp) + +/* Flowing register offsets are same in files of + * dce/dce_11_0_d.h + * dce/vi_polaris10_p/vi_polaris10_d.h + * + * So we can create dce110 timing generator to use it. + */ + + +/* +* apply_front_porch_workaround +* +* This is a workaround for a bug that has existed since R5xx and has not been +* fixed keep Front porch at minimum 2 for Interlaced mode or 1 for progressive. +*/ +static void dce110_timing_generator_apply_front_porch_workaround( + struct timing_generator *tg, + struct dc_crtc_timing *timing) +{ + if (timing->flags.INTERLACE == 1) { + if (timing->v_front_porch < 2) + timing->v_front_porch = 2; + } else { + if (timing->v_front_porch < 1) + timing->v_front_porch = 1; + } +} + +/* + ***************************************************************************** + * Function: is_in_vertical_blank + * + * @brief + * check the current status of CRTC to check if we are in Vertical Blank + * regioneased" state + * + * @return + * true if currently in blank region, false otherwise + * + ***************************************************************************** + */ +static bool dce110_timing_generator_is_in_vertical_blank( + struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + addr = CRTC_REG(mmCRTC_STATUS); + value = dm_read_reg(tg->ctx, addr); + field = get_reg_field_value(value, CRTC_STATUS, CRTC_V_BLANK); + return field == 1; +} + +void dce110_timing_generator_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl) +{ + uint32_t regval; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t address = CRTC_REG(mmCRTC_CONTROL); + + regval = dm_read_reg(tg->ctx, address); + set_reg_field_value(regval, early_cntl, + CRTC_CONTROL, CRTC_HBLANK_EARLY_CONTROL); + dm_write_reg(tg->ctx, address, regval); +} + +/* + * Enable CRTC + * Enable CRTC - call ASIC Control Object to enable Timing generator. + */ +bool dce110_timing_generator_enable_crtc(struct timing_generator *tg) +{ + enum bp_result result; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = 0; + + /* + * 3 is used to make sure V_UPDATE occurs at the beginning of the first + * line of vertical front porch + */ + set_reg_field_value( + value, + 0, + CRTC_MASTER_UPDATE_MODE, + MASTER_UPDATE_MODE); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_MASTER_UPDATE_MODE), value); + + /* TODO: may want this on to catch underflow */ + value = 0; + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_MASTER_UPDATE_LOCK), value); + + result = tg->bp->funcs->enable_crtc(tg->bp, tg110->controller_id, true); + + return result == BP_RESULT_OK; +} + +void dce110_timing_generator_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_BLACK_COLOR); + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); +} + +/* + ***************************************************************************** + * Function: disable_stereo + * + * @brief + * Disables active stereo on controller + * Frame Packing need to be disabled in vBlank or when CRTC not running + ***************************************************************************** + */ +#if 0 +@TODOSTEREO +static void disable_stereo(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_3D_STRUCTURE_CONTROL); + uint32_t value = 0; + uint32_t test = 0; + uint32_t field = 0; + uint32_t struc_en = 0; + uint32_t struc_stereo_sel_ovr = 0; + + value = dm_read_reg(tg->ctx, addr); + struc_en = get_reg_field_value( + value, + CRTC_3D_STRUCTURE_CONTROL, + CRTC_3D_STRUCTURE_EN); + + struc_stereo_sel_ovr = get_reg_field_value( + value, + CRTC_3D_STRUCTURE_CONTROL, + CRTC_3D_STRUCTURE_STEREO_SEL_OVR); + + /* + * When disabling Frame Packing in 2 step mode, we need to program both + * registers at the same frame + * Programming it in the beginning of VActive makes sure we are ok + */ + + if (struc_en != 0 && struc_stereo_sel_ovr == 0) { + tg->funcs->wait_for_vblank(tg); + tg->funcs->wait_for_vactive(tg); + } + + value = 0; + dm_write_reg(tg->ctx, addr, value); + + addr = tg->regs[IDX_CRTC_STEREO_CONTROL]; + dm_write_reg(tg->ctx, addr, value); +} +#endif + +/* + * disable_crtc - call ASIC Control Object to disable Timing generator. + */ +bool dce110_timing_generator_disable_crtc(struct timing_generator *tg) +{ + enum bp_result result; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + result = tg->bp->funcs->enable_crtc(tg->bp, tg110->controller_id, false); + + /* Need to make sure stereo is disabled according to the DCE5.0 spec */ + + /* + * @TODOSTEREO call this when adding stereo support + * tg->funcs->disable_stereo(tg); + */ + + return result == BP_RESULT_OK; +} + +/* + * program_horz_count_by_2 + * Programs DxCRTC_HORZ_COUNT_BY2_EN - 1 for DVI 30bpp mode, 0 otherwise + */ +static void program_horz_count_by_2( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t regval; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + regval = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_COUNT_CONTROL)); + + set_reg_field_value(regval, 0, CRTC_COUNT_CONTROL, + CRTC_HORZ_COUNT_BY2_EN); + + if (timing->flags.HORZ_COUNT_BY_TWO) + set_reg_field_value(regval, 1, CRTC_COUNT_CONTROL, + CRTC_HORZ_COUNT_BY2_EN); + + dm_write_reg(tg->ctx, + CRTC_REG(mmCRTC_COUNT_CONTROL), regval); +} + +/* + * program_timing_generator + * Program CRTC Timing Registers - DxCRTC_H_*, DxCRTC_V_*, Pixel repetition. + * Call ASIC Control Object to program Timings. + */ +bool dce110_timing_generator_program_timing_generator( + struct timing_generator *tg, + const struct dc_crtc_timing *dc_crtc_timing) +{ + enum bp_result result; + struct bp_hw_crtc_timing_parameters bp_params; + struct dc_crtc_timing patched_crtc_timing; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + uint32_t vsync_offset = dc_crtc_timing->v_border_bottom + + dc_crtc_timing->v_front_porch; + uint32_t v_sync_start =dc_crtc_timing->v_addressable + vsync_offset; + + uint32_t hsync_offset = dc_crtc_timing->h_border_right + + dc_crtc_timing->h_front_porch; + uint32_t h_sync_start = dc_crtc_timing->h_addressable + hsync_offset; + + memset(&bp_params, 0, sizeof(struct bp_hw_crtc_timing_parameters)); + + /* Due to an asic bug we need to apply the Front Porch workaround prior + * to programming the timing. + */ + + patched_crtc_timing = *dc_crtc_timing; + + dce110_timing_generator_apply_front_porch_workaround(tg, &patched_crtc_timing); + + bp_params.controller_id = tg110->controller_id; + + bp_params.h_total = patched_crtc_timing.h_total; + bp_params.h_addressable = + patched_crtc_timing.h_addressable; + bp_params.v_total = patched_crtc_timing.v_total; + bp_params.v_addressable = patched_crtc_timing.v_addressable; + + bp_params.h_sync_start = h_sync_start; + bp_params.h_sync_width = patched_crtc_timing.h_sync_width; + bp_params.v_sync_start = v_sync_start; + bp_params.v_sync_width = patched_crtc_timing.v_sync_width; + + /* Set overscan */ + bp_params.h_overscan_left = + patched_crtc_timing.h_border_left; + bp_params.h_overscan_right = + patched_crtc_timing.h_border_right; + bp_params.v_overscan_top = patched_crtc_timing.v_border_top; + bp_params.v_overscan_bottom = + patched_crtc_timing.v_border_bottom; + + /* Set flags */ + if (patched_crtc_timing.flags.HSYNC_POSITIVE_POLARITY == 1) + bp_params.flags.HSYNC_POSITIVE_POLARITY = 1; + + if (patched_crtc_timing.flags.VSYNC_POSITIVE_POLARITY == 1) + bp_params.flags.VSYNC_POSITIVE_POLARITY = 1; + + if (patched_crtc_timing.flags.INTERLACE == 1) + bp_params.flags.INTERLACE = 1; + + if (patched_crtc_timing.flags.HORZ_COUNT_BY_TWO == 1) + bp_params.flags.HORZ_COUNT_BY_TWO = 1; + + result = tg->bp->funcs->program_crtc_timing(tg->bp, &bp_params); + + program_horz_count_by_2(tg, &patched_crtc_timing); + + tg110->base.funcs->enable_advanced_request(tg, true, &patched_crtc_timing); + + /* Enable stereo - only when we need to pack 3D frame. Other types + * of stereo handled in explicit call */ + + return result == BP_RESULT_OK; +} + +/* + ***************************************************************************** + * Function: set_drr + * + * @brief + * Program dynamic refresh rate registers m_DxCRTC_V_TOTAL_*. + * + * @param [in] pHwCrtcTiming: point to H + * wCrtcTiming struct + ***************************************************************************** + */ +void dce110_timing_generator_set_drr( + struct timing_generator *tg, + const struct drr_params *params) +{ + /* register values */ + uint32_t v_total_min = 0; + uint32_t v_total_max = 0; + uint32_t v_total_cntl = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + uint32_t addr = 0; + + addr = CRTC_REG(mmCRTC_V_TOTAL_MIN); + v_total_min = dm_read_reg(tg->ctx, addr); + + addr = CRTC_REG(mmCRTC_V_TOTAL_MAX); + v_total_max = dm_read_reg(tg->ctx, addr); + + addr = CRTC_REG(mmCRTC_V_TOTAL_CONTROL); + v_total_cntl = dm_read_reg(tg->ctx, addr); + + if (params != NULL && + params->vertical_total_max > 0 && + params->vertical_total_min > 0) { + + set_reg_field_value(v_total_max, + params->vertical_total_max - 1, + CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX); + + set_reg_field_value(v_total_min, + params->vertical_total_min - 1, + CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN); + + set_reg_field_value(v_total_cntl, + 1, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MIN_SEL); + + set_reg_field_value(v_total_cntl, + 1, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MAX_SEL); + + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_ON_EVENT); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_TO_MASTER_VSYNC); + + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK_EN); + + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK); + } else { + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MIN_SEL); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MAX_SEL); + set_reg_field_value(v_total_min, + 0, + CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN); + set_reg_field_value(v_total_max, + 0, + CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_ON_EVENT); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_TO_MASTER_VSYNC); + } + + addr = CRTC_REG(mmCRTC_V_TOTAL_MIN); + dm_write_reg(tg->ctx, addr, v_total_min); + + addr = CRTC_REG(mmCRTC_V_TOTAL_MAX); + dm_write_reg(tg->ctx, addr, v_total_max); + + addr = CRTC_REG(mmCRTC_V_TOTAL_CONTROL); + dm_write_reg(tg->ctx, addr, v_total_cntl); +} + +void dce110_timing_generator_set_static_screen_control( + struct timing_generator *tg, + uint32_t event_triggers, + uint32_t num_frames) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t static_screen_cntl = 0; + uint32_t addr = 0; + + // By register spec, it only takes 8 bit value + if (num_frames > 0xFF) + num_frames = 0xFF; + + addr = CRTC_REG(mmCRTC_STATIC_SCREEN_CONTROL); + static_screen_cntl = dm_read_reg(tg->ctx, addr); + + set_reg_field_value(static_screen_cntl, + event_triggers, + CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK); + + set_reg_field_value(static_screen_cntl, + num_frames, + CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_FRAME_COUNT); + + dm_write_reg(tg->ctx, addr, static_screen_cntl); +} + +/* + * get_vblank_counter + * + * @brief + * Get counter for vertical blanks. use register CRTC_STATUS_FRAME_COUNT which + * holds the counter of frames. + * + * @param + * struct timing_generator *tg - [in] timing generator which controls the + * desired CRTC + * + * @return + * Counter of frames, which should equal to number of vblanks. + */ +uint32_t dce110_timing_generator_get_vblank_counter(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_STATUS_FRAME_COUNT); + uint32_t value = dm_read_reg(tg->ctx, addr); + uint32_t field = get_reg_field_value( + value, CRTC_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); + + return field; +} + +/* + ***************************************************************************** + * Function: dce110_timing_generator_get_position + * + * @brief + * Returns CRTC vertical/horizontal counters + * + * @param [out] position + ***************************************************************************** + */ +void dce110_timing_generator_get_position(struct timing_generator *tg, + struct crtc_position *position) +{ + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_STATUS_POSITION)); + + position->horizontal_count = get_reg_field_value( + value, + CRTC_STATUS_POSITION, + CRTC_HORZ_COUNT); + + position->vertical_count = get_reg_field_value( + value, + CRTC_STATUS_POSITION, + CRTC_VERT_COUNT); + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_NOM_VERT_POSITION)); + + position->nominal_vcount = get_reg_field_value( + value, + CRTC_NOM_VERT_POSITION, + CRTC_VERT_COUNT_NOM); +} + +/* + ***************************************************************************** + * Function: get_crtc_scanoutpos + * + * @brief + * Returns CRTC vertical/horizontal counters + * + * @param [out] vpos, hpos + ***************************************************************************** + */ +void dce110_timing_generator_get_crtc_scanoutpos( + struct timing_generator *tg, + uint32_t *v_blank_start, + uint32_t *v_blank_end, + uint32_t *h_position, + uint32_t *v_position) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + struct crtc_position position; + + uint32_t value = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_V_BLANK_START_END)); + + *v_blank_start = get_reg_field_value(value, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_START); + *v_blank_end = get_reg_field_value(value, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END); + + dce110_timing_generator_get_position( + tg, &position); + + *h_position = position.horizontal_count; + *v_position = position.vertical_count; +} + +/* TODO: is it safe to assume that mask/shift of Primary and Underlay + * are the same? + * For example: today CRTC_H_TOTAL == CRTCV_H_TOTAL but is it always + * guaranteed? */ +void dce110_timing_generator_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t vsync_offset = timing->v_border_bottom + + timing->v_front_porch; + uint32_t v_sync_start =timing->v_addressable + vsync_offset; + + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + uint32_t h_sync_start = timing->h_addressable + hsync_offset; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr = 0; + uint32_t tmp = 0; + + addr = CRTC_REG(mmCRTC_H_TOTAL); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->h_total - 1, + CRTC_H_TOTAL, + CRTC_H_TOTAL); + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_V_TOTAL); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTC_V_TOTAL, + CRTC_V_TOTAL); + dm_write_reg(ctx, addr, value); + + /* In case of V_TOTAL_CONTROL is on, make sure V_TOTAL_MAX and + * V_TOTAL_MIN are equal to V_TOTAL. + */ + addr = CRTC_REG(mmCRTC_V_TOTAL_MAX); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX); + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_V_TOTAL_MIN); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN); + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_H_BLANK_START_END); + value = dm_read_reg(ctx, addr); + + tmp = timing->h_total - + (h_sync_start + timing->h_border_left); + + set_reg_field_value( + value, + tmp, + CRTC_H_BLANK_START_END, + CRTC_H_BLANK_END); + + tmp = tmp + timing->h_addressable + + timing->h_border_left + timing->h_border_right; + + set_reg_field_value( + value, + tmp, + CRTC_H_BLANK_START_END, + CRTC_H_BLANK_START); + + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_V_BLANK_START_END); + value = dm_read_reg(ctx, addr); + + tmp = timing->v_total - (v_sync_start + timing->v_border_top); + + set_reg_field_value( + value, + tmp, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END); + + tmp = tmp + timing->v_addressable + timing->v_border_top + + timing->v_border_bottom; + + set_reg_field_value( + value, + tmp, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_START); + + dm_write_reg(ctx, addr, value); +} + +void dce110_timing_generator_set_test_pattern( + struct timing_generator *tg, + /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' + * because this is not DP-specific (which is probably somewhere in DP + * encoder) */ + enum controller_dp_test_pattern test_pattern, + enum dc_color_depth color_depth) +{ + struct dc_context *ctx = tg->ctx; + uint32_t value; + uint32_t addr; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + enum test_pattern_color_format bit_depth; + enum test_pattern_dyn_range dyn_range; + enum test_pattern_mode mode; + /* color ramp generator mixes 16-bits color */ + uint32_t src_bpc = 16; + /* requested bpc */ + uint32_t dst_bpc; + uint32_t index; + /* RGB values of the color bars. + * Produce two RGB colors: RGB0 - white (all Fs) + * and RGB1 - black (all 0s) + * (three RGB components for two colors) + */ + uint16_t src_color[6] = {0xFFFF, 0xFFFF, 0xFFFF, 0x0000, + 0x0000, 0x0000}; + /* dest color (converted to the specified color format) */ + uint16_t dst_color[6]; + uint32_t inc_base; + + /* translate to bit depth */ + switch (color_depth) { + case COLOR_DEPTH_666: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_6; + break; + case COLOR_DEPTH_888: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + case COLOR_DEPTH_101010: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_10; + break; + case COLOR_DEPTH_121212: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_12; + break; + default: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + } + + switch (test_pattern) { + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES: + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA: + { + dyn_range = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA ? + TEST_PATTERN_DYN_RANGE_CEA : + TEST_PATTERN_DYN_RANGE_VESA); + mode = TEST_PATTERN_MODE_COLORSQUARES_RGB; + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS); + + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL); + value = 0; + + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN); + + set_reg_field_value( + value, + mode, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_MODE); + + set_reg_field_value( + value, + dyn_range, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_DYNAMIC_RANGE); + set_reg_field_value( + value, + bit_depth, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_COLOR_FORMAT); + dm_write_reg(ctx, addr, value); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_VERTICALBARS: + case CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS: + { + mode = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_VERTICALBARS ? + TEST_PATTERN_MODE_VERTICALBARS : + TEST_PATTERN_MODE_HORIZONTALBARS); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* adjust color to the required colorFormat */ + for (index = 0; index < 6; index++) { + /* dst = 2^dstBpc * src / 2^srcBpc = src >> + * (srcBpc - dstBpc); + */ + dst_color[index] = + src_color[index] >> (src_bpc - dst_bpc); + /* CRTC_TEST_PATTERN_DATA has 16 bits, + * lowest 6 are hardwired to ZERO + * color bits should be left aligned aligned to MSB + * XXXXXXXXXX000000 for 10 bit, + * XXXXXXXX00000000 for 8 bit and XXXXXX0000000000 for 6 + */ + dst_color[index] <<= (16 - dst_bpc); + } + + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS); + dm_write_reg(ctx, addr, value); + + /* We have to write the mask before data, similar to pipeline. + * For example, for 8 bpc, if we want RGB0 to be magenta, + * and RGB1 to be cyan, + * we need to make 7 writes: + * MASK DATA + * 000001 00000000 00000000 set mask to R0 + * 000010 11111111 00000000 R0 255, 0xFF00, set mask to G0 + * 000100 00000000 00000000 G0 0, 0x0000, set mask to B0 + * 001000 11111111 00000000 B0 255, 0xFF00, set mask to R1 + * 010000 00000000 00000000 R1 0, 0x0000, set mask to G1 + * 100000 11111111 00000000 G1 255, 0xFF00, set mask to B1 + * 100000 11111111 00000000 B1 255, 0xFF00 + * + * we will make a loop of 6 in which we prepare the mask, + * then write, then prepare the color for next write. + * first iteration will write mask only, + * but each next iteration color prepared in + * previous iteration will be written within new mask, + * the last component will written separately, + * mask is not changing between 6th and 7th write + * and color will be prepared by last iteration + */ + + /* write color, color values mask in CRTC_TEST_PATTERN_MASK + * is B1, G1, R1, B0, G0, R0 + */ + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_COLOR); + for (index = 0; index < 6; index++) { + /* prepare color mask, first write PATTERN_DATA + * will have all zeros + */ + set_reg_field_value( + value, + (1 << index), + CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_MASK); + /* write color component */ + dm_write_reg(ctx, addr, value); + /* prepare next color component, + * will be written in the next iteration + */ + set_reg_field_value( + value, + dst_color[index], + CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_DATA); + } + /* write last color component, + * it's been already prepared in the loop + */ + dm_write_reg(ctx, addr, value); + + /* enable test pattern */ + addr = CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL); + value = 0; + + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN); + + set_reg_field_value( + value, + mode, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_MODE); + + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_DYNAMIC_RANGE); + + set_reg_field_value( + value, + bit_depth, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_COLOR_FORMAT); + + dm_write_reg(ctx, addr, value); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_COLORRAMP: + { + mode = (bit_depth == + TEST_PATTERN_COLOR_FORMAT_BPC_10 ? + TEST_PATTERN_MODE_DUALRAMP_RGB : + TEST_PATTERN_MODE_SINGLERAMP_RGB); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* increment for the first ramp for one color gradation + * 1 gradation for 6-bit color is 2^10 + * gradations in 16-bit color + */ + inc_base = (src_bpc - dst_bpc); + + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + { + set_reg_field_value( + value, + inc_base, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC1); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_RAMP0_OFFSET); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + { + set_reg_field_value( + value, + inc_base, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC1); + set_reg_field_value( + value, + 8, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_RAMP0_OFFSET); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + { + set_reg_field_value( + value, + inc_base, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0); + set_reg_field_value( + value, + inc_base + 2, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC1); + set_reg_field_value( + value, + 8, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + set_reg_field_value( + value, + 5, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 384 << 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_RAMP0_OFFSET); + } + break; + default: + break; + } + dm_write_reg(ctx, addr, value); + + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_COLOR); + dm_write_reg(ctx, addr, value); + + /* enable test pattern */ + addr = CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL); + value = 0; + + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN); + + set_reg_field_value( + value, + mode, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_MODE); + + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_DYNAMIC_RANGE); + /* add color depth translation here */ + set_reg_field_value( + value, + bit_depth, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_COLOR_FORMAT); + + dm_write_reg(ctx, addr, value); + } + break; + case CONTROLLER_DP_TEST_PATTERN_VIDEOMODE: + { + value = 0; + dm_write_reg(ctx, CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL), value); + dm_write_reg(ctx, CRTC_REG(mmCRTC_TEST_PATTERN_COLOR), value); + dm_write_reg(ctx, CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS), + value); + } + break; + default: + break; + } +} + +/* + * dce110_timing_generator_validate_timing + * The timing generators support a maximum display size of is 8192 x 8192 pixels, + * including both active display and blanking periods. Check H Total and V Total. + */ +bool dce110_timing_generator_validate_timing( + struct timing_generator *tg, + const struct dc_crtc_timing *timing, + enum signal_type signal) +{ + uint32_t h_blank; + uint32_t h_back_porch, hsync_offset, h_sync_start; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + ASSERT(timing != NULL); + + if (!timing) + return false; + + hsync_offset = timing->h_border_right + timing->h_front_porch; + h_sync_start = timing->h_addressable + hsync_offset; + + /* Currently we don't support 3D, so block all 3D timings */ + if (timing->timing_3d_format != TIMING_3D_FORMAT_NONE) + return false; + + /* Temporarily blocking interlacing mode until it's supported */ + if (timing->flags.INTERLACE == 1) + return false; + + /* Check maximum number of pixels supported by Timing Generator + * (Currently will never fail, in order to fail needs display which + * needs more than 8192 horizontal and + * more than 8192 vertical total pixels) + */ + if (timing->h_total > tg110->max_h_total || + timing->v_total > tg110->max_v_total) + return false; + + h_blank = (timing->h_total - timing->h_addressable - + timing->h_border_right - + timing->h_border_left); + + if (h_blank < tg110->min_h_blank) + return false; + + if (timing->h_front_porch < tg110->min_h_front_porch) + return false; + + h_back_porch = h_blank - (h_sync_start - + timing->h_addressable - + timing->h_border_right - + timing->h_sync_width); + + if (h_back_porch < tg110->min_h_back_porch) + return false; + + return true; +} + +/* + * Wait till we are at the beginning of VBlank. + */ +void dce110_timing_generator_wait_for_vblank(struct timing_generator *tg) +{ + /* We want to catch beginning of VBlank here, so if the first try are + * in VBlank, we might be very close to Active, in this case wait for + * another frame + */ + while (dce110_timing_generator_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } + + while (!dce110_timing_generator_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/* + * Wait till we are in VActive (anywhere in VActive) + */ +void dce110_timing_generator_wait_for_vactive(struct timing_generator *tg) +{ + while (dce110_timing_generator_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/* + ***************************************************************************** + * Function: dce110_timing_generator_setup_global_swap_lock + * + * @brief + * Setups Global Swap Lock group for current pipe + * Pipe can join or leave GSL group, become a TimingServer or TimingClient + * + * @param [in] gsl_params: setup data + ***************************************************************************** + */ +void dce110_timing_generator_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params) +{ + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t address = DCP_REG(mmDCP_GSL_CONTROL); + uint32_t check_point = FLIP_READY_BACK_LOOKUP; + + value = dm_read_reg(tg->ctx, address); + + /* This pipe will belong to GSL Group zero. */ + set_reg_field_value(value, + 1, + DCP_GSL_CONTROL, + DCP_GSL0_EN); + + set_reg_field_value(value, + gsl_params->gsl_master == tg->inst, + DCP_GSL_CONTROL, + DCP_GSL_MASTER_EN); + + set_reg_field_value(value, + HFLIP_READY_DELAY, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_FORCE_DELAY); + + /* Keep signal low (pending high) during 6 lines. + * Also defines minimum interval before re-checking signal. */ + set_reg_field_value(value, + HFLIP_CHECK_DELAY, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_CHECK_DELAY); + + dm_write_reg(tg->ctx, CRTC_REG(mmDCP_GSL_CONTROL), value); + value = 0; + + set_reg_field_value(value, + gsl_params->gsl_master, + DCIO_GSL0_CNTL, + DCIO_GSL0_VSYNC_SEL); + + set_reg_field_value(value, + 0, + DCIO_GSL0_CNTL, + DCIO_GSL0_TIMING_SYNC_SEL); + + set_reg_field_value(value, + 0, + DCIO_GSL0_CNTL, + DCIO_GSL0_GLOBAL_UNLOCK_SEL); + + dm_write_reg(tg->ctx, CRTC_REG(mmDCIO_GSL0_CNTL), value); + + + { + uint32_t value_crtc_vtotal; + + value_crtc_vtotal = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_V_TOTAL)); + + set_reg_field_value(value, + 0,/* DCP_GSL_PURPOSE_SURFACE_FLIP */ + DCP_GSL_CONTROL, + DCP_GSL_SYNC_SOURCE); + + /* Checkpoint relative to end of frame */ + check_point = get_reg_field_value(value_crtc_vtotal, + CRTC_V_TOTAL, + CRTC_V_TOTAL); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_GSL_WINDOW), 0); + } + + set_reg_field_value(value, + 1, + DCP_GSL_CONTROL, + DCP_GSL_DELAY_SURFACE_UPDATE_PENDING); + + dm_write_reg(tg->ctx, address, value); + + /********************************************************************/ + address = CRTC_REG(mmCRTC_GSL_CONTROL); + + value = dm_read_reg(tg->ctx, address); + set_reg_field_value(value, + check_point - FLIP_READY_BACK_LOOKUP, + CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM); + + set_reg_field_value(value, + VFLIP_READY_DELAY, + CRTC_GSL_CONTROL, + CRTC_GSL_FORCE_DELAY); + + dm_write_reg(tg->ctx, address, value); +} + +void dce110_timing_generator_tear_down_global_swap_lock( + struct timing_generator *tg) +{ + /* Clear all the register writes done by + * dce110_timing_generator_setup_global_swap_lock + */ + + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t address = DCP_REG(mmDCP_GSL_CONTROL); + + value = 0; + + /* This pipe will belong to GSL Group zero. */ + /* Settig HW default values from reg specs */ + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL0_EN); + + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL_MASTER_EN); + + set_reg_field_value(value, + 0x2, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_FORCE_DELAY); + + set_reg_field_value(value, + 0x6, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_CHECK_DELAY); + + /* Restore DCP_GSL_PURPOSE_SURFACE_FLIP */ + { + dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_V_TOTAL)); + + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL_SYNC_SOURCE); + } + + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL_DELAY_SURFACE_UPDATE_PENDING); + + dm_write_reg(tg->ctx, address, value); + + /********************************************************************/ + address = CRTC_REG(mmCRTC_GSL_CONTROL); + + value = 0; + set_reg_field_value(value, + 0, + CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM); + + set_reg_field_value(value, + 0x2, + CRTC_GSL_CONTROL, + CRTC_GSL_FORCE_DELAY); + + dm_write_reg(tg->ctx, address, value); +} +/* + ***************************************************************************** + * Function: is_counter_moving + * + * @brief + * check if the timing generator is currently going + * + * @return + * true if currently going, false if currently paused or stopped. + * + ***************************************************************************** + */ +bool dce110_timing_generator_is_counter_moving(struct timing_generator *tg) +{ + struct crtc_position position1, position2; + + tg->funcs->get_position(tg, &position1); + tg->funcs->get_position(tg, &position2); + + if (position1.horizontal_count == position2.horizontal_count && + position1.vertical_count == position2.vertical_count) + return false; + else + return true; +} + +void dce110_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_START_LINE_CONTROL); + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (enable) { + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } else { + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } + + if ((timing->v_sync_width + timing->v_front_porch) <= 3) { + set_reg_field_value( + value, + 3, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } else { + set_reg_field_value( + value, + 4, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PROGRESSIVE_START_LINE_EARLY); + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_INTERLACE_START_LINE_EARLY); + + dm_write_reg(tg->ctx, addr, value); +} + +/*TODO: Figure out if we need this function. */ +void dce110_timing_generator_set_lock_master(struct timing_generator *tg, + bool lock) +{ + struct dc_context *ctx = tg->ctx; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_MASTER_UPDATE_LOCK); + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + lock ? 1 : 0, + CRTC_MASTER_UPDATE_LOCK, + MASTER_UPDATE_LOCK); + + dm_write_reg(ctx, addr, value); +} + +void dce110_timing_generator_enable_reset_trigger( + struct timing_generator *tg, + int source_tg_inst) +{ + uint32_t value; + uint32_t rising_edge = 0; + uint32_t falling_edge = 0; + enum trigger_source_select trig_src_select = TRIGGER_SOURCE_SELECT_LOGIC_ZERO; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Setup trigger edge */ + { + uint32_t pol_value = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_V_SYNC_A_CNTL)); + + /* Register spec has reversed definition: + * 0 for positive, 1 for negative */ + if (get_reg_field_value(pol_value, + CRTC_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL) == 0) { + rising_edge = 1; + } else { + falling_edge = 1; + } + } + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL)); + + trig_src_select = TRIGGER_SOURCE_SELECT_GSL_GROUP0; + + set_reg_field_value(value, + trig_src_select, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_SOURCE_SELECT); + + set_reg_field_value(value, + TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_POLARITY_SELECT); + + set_reg_field_value(value, + rising_edge, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_RISING_EDGE_DETECT_CNTL); + + set_reg_field_value(value, + falling_edge, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_FALLING_EDGE_DETECT_CNTL); + + set_reg_field_value(value, + 0, /* send every signal */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_FREQUENCY_SELECT); + + set_reg_field_value(value, + 0, /* no delay */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_DELAY); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL), value); + + /**************************************************************/ + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL)); + + set_reg_field_value(value, + 2, /* force H count to H_TOTAL and V count to V_TOTAL */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE); + + set_reg_field_value(value, + 1, /* TriggerB - we never use TriggerA */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_TRIG_SEL); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL), value); +} + +void dce110_timing_generator_enable_crtc_reset( + struct timing_generator *tg, + int source_tg_inst, + struct crtc_trigger_info *crtc_tp) +{ + uint32_t value = 0; + uint32_t rising_edge = 0; + uint32_t falling_edge = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Setup trigger edge */ + switch (crtc_tp->event) { + case CRTC_EVENT_VSYNC_RISING: + rising_edge = 1; + break; + + case CRTC_EVENT_VSYNC_FALLING: + falling_edge = 1; + break; + } + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL)); + + set_reg_field_value(value, + source_tg_inst, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_SOURCE_SELECT); + + set_reg_field_value(value, + TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_POLARITY_SELECT); + + set_reg_field_value(value, + rising_edge, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_RISING_EDGE_DETECT_CNTL); + + set_reg_field_value(value, + falling_edge, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_FALLING_EDGE_DETECT_CNTL); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL), value); + + /**************************************************************/ + + switch (crtc_tp->delay) { + case TRIGGER_DELAY_NEXT_LINE: + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL)); + + set_reg_field_value(value, + 0, /* force H count to H_TOTAL and V count to V_TOTAL */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE); + + set_reg_field_value(value, + 0, /* TriggerB - we never use TriggerA */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_TRIG_SEL); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL), value); + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_VERT_SYNC_CONTROL)); + + set_reg_field_value(value, + 1, + CRTC_VERT_SYNC_CONTROL, + CRTC_FORCE_VSYNC_NEXT_LINE_CLEAR); + + set_reg_field_value(value, + 2, + CRTC_VERT_SYNC_CONTROL, + CRTC_AUTO_FORCE_VSYNC_MODE); + + break; + + case TRIGGER_DELAY_NEXT_PIXEL: + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_VERT_SYNC_CONTROL)); + + set_reg_field_value(value, + 1, + CRTC_VERT_SYNC_CONTROL, + CRTC_FORCE_VSYNC_NEXT_LINE_CLEAR); + + set_reg_field_value(value, + 0, + CRTC_VERT_SYNC_CONTROL, + CRTC_AUTO_FORCE_VSYNC_MODE); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_VERT_SYNC_CONTROL), value); + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL)); + + set_reg_field_value(value, + 2, /* force H count to H_TOTAL and V count to V_TOTAL */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE); + + set_reg_field_value(value, + 1, /* TriggerB - we never use TriggerA */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_TRIG_SEL); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL), value); + break; + } + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_MASTER_UPDATE_MODE)); + + set_reg_field_value(value, + 2, + CRTC_MASTER_UPDATE_MODE, + MASTER_UPDATE_MODE); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_MASTER_UPDATE_MODE), value); +} +void dce110_timing_generator_disable_reset_trigger( + struct timing_generator *tg) +{ + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL)); + + set_reg_field_value(value, + 0, /* force counter now mode is disabled */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL), value); + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_VERT_SYNC_CONTROL)); + + set_reg_field_value(value, + 1, + CRTC_VERT_SYNC_CONTROL, + CRTC_FORCE_VSYNC_NEXT_LINE_CLEAR); + + set_reg_field_value(value, + 0, + CRTC_VERT_SYNC_CONTROL, + CRTC_AUTO_FORCE_VSYNC_MODE); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_VERT_SYNC_CONTROL), value); + + /********************************************************************/ + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL)); + + set_reg_field_value(value, + TRIGGER_SOURCE_SELECT_LOGIC_ZERO, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_SOURCE_SELECT); + + set_reg_field_value(value, + TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_POLARITY_SELECT); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL), value); +} + +/* + ***************************************************************************** + * @brief + * Checks whether CRTC triggered reset occurred + * + * @return + * true if triggered reset occurred, false otherwise + ***************************************************************************** + */ +bool dce110_timing_generator_did_triggered_reset_occur( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL)); + uint32_t value1 = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_VERT_SYNC_CONTROL)); + bool force = get_reg_field_value(value, + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_OCCURRED) != 0; + bool vert_sync = get_reg_field_value(value1, + CRTC_VERT_SYNC_CONTROL, + CRTC_FORCE_VSYNC_NEXT_LINE_OCCURRED) != 0; + + return (force || vert_sync); +} + +/* + * dce110_timing_generator_disable_vga + * Turn OFF VGA Mode and Timing - DxVGA_CONTROL + * VGA Mode and VGA Timing is used by VBIOS on CRT Monitors; + */ +void dce110_timing_generator_disable_vga( + struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + switch (tg110->controller_id) { + case CONTROLLER_ID_D0: + addr = mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D1: + addr = mmD2VGA_CONTROL; + break; + case CONTROLLER_ID_D2: + addr = mmD3VGA_CONTROL; + break; + case CONTROLLER_ID_D3: + addr = mmD4VGA_CONTROL; + break; + case CONTROLLER_ID_D4: + addr = mmD5VGA_CONTROL; + break; + case CONTROLLER_ID_D5: + addr = mmD6VGA_CONTROL; + break; + default: + break; + } + value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_MODE_ENABLE); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_TIMING_SELECT); + set_reg_field_value( + value, 0, D1VGA_CONTROL, D1VGA_SYNC_POLARITY_SELECT); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_OVERSCAN_COLOR_EN); + + dm_write_reg(tg->ctx, addr, value); +} + +/* + * set_overscan_color_black + * + * @param :black_color is one of the color space + * :this routine will set overscan black color according to the color space. + * @return none + */ +void dce110_timing_generator_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t addr; + uint32_t value = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + set_reg_field_value( + value, + color->color_b_cb, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + color->color_r_cr, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + set_reg_field_value( + value, + color->color_g_y, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + addr = CRTC_REG(mmCRTC_OVERSCAN_COLOR); + dm_write_reg(ctx, addr, value); + addr = CRTC_REG(mmCRTC_BLACK_COLOR); + dm_write_reg(ctx, addr, value); + /* This is desirable to have a constant DAC output voltage during the + * blank time that is higher than the 0 volt reference level that the + * DAC outputs when the NBLANK signal + * is asserted low, such as for output to an analog TV. */ + addr = CRTC_REG(mmCRTC_BLANK_DATA_COLOR); + dm_write_reg(ctx, addr, value); + + /* TO DO we have to program EXT registers and we need to know LB DATA + * format because it is used when more 10 , i.e. 12 bits per color + * + * m_mmDxCRTC_OVERSCAN_COLOR_EXT + * m_mmDxCRTC_BLACK_COLOR_EXT + * m_mmDxCRTC_BLANK_DATA_COLOR_EXT + */ + +} + +void dce110_tg_program_blank_color(struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_BLACK_COLOR); + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); + + addr = CRTC_REG(mmCRTC_BLANK_DATA_COLOR); + dm_write_reg(tg->ctx, addr, value); +} + +void dce110_tg_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + set_reg_field_value( + value, + overscan_color->color_b_cb, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + overscan_color->color_g_y, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + overscan_color->color_r_cr, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + addr = CRTC_REG(mmCRTC_OVERSCAN_COLOR); + dm_write_reg(ctx, addr, value); +} + +void dce110_tg_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + int vready_offset, + int vstartup_start, + int vupdate_offset, + int vupdate_width, + const enum signal_type signal, + bool use_vbios) +{ + if (use_vbios) + dce110_timing_generator_program_timing_generator(tg, timing); + else + dce110_timing_generator_program_blanking(tg, timing); +} + +bool dce110_tg_is_blanked(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_BLANK_CONTROL)); + + if (get_reg_field_value( + value, + CRTC_BLANK_CONTROL, + CRTC_BLANK_DATA_EN) == 1 && + get_reg_field_value( + value, + CRTC_BLANK_CONTROL, + CRTC_CURRENT_BLANK_STATE) == 1) + return true; + return false; +} + +void dce110_tg_set_blank(struct timing_generator *tg, + bool enable_blanking) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = 0; + + set_reg_field_value( + value, + 1, + CRTC_DOUBLE_BUFFER_CONTROL, + CRTC_BLANK_DATA_DOUBLE_BUFFER_EN); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_DOUBLE_BUFFER_CONTROL), value); + value = 0; + + if (enable_blanking) { + set_reg_field_value( + value, + 1, + CRTC_BLANK_CONTROL, + CRTC_BLANK_DATA_EN); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_BLANK_CONTROL), value); + + } else + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_BLANK_CONTROL), 0); +} + +bool dce110_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + return dce110_timing_generator_validate_timing(tg, timing, SIGNAL_TYPE_NONE); +} + +void dce110_tg_wait_for_state(struct timing_generator *tg, + enum crtc_state state) +{ + switch (state) { + case CRTC_STATE_VBLANK: + dce110_timing_generator_wait_for_vblank(tg); + break; + + case CRTC_STATE_VACTIVE: + dce110_timing_generator_wait_for_vactive(tg); + break; + + default: + break; + } +} + +void dce110_tg_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color) +{ + if (blank_color != NULL) + dce110_tg_program_blank_color(tg, blank_color); + if (overscan_color != NULL) + dce110_tg_set_overscan_color(tg, overscan_color); +} + +/* Gets first line of blank region of the display timing for CRTC + * and programms is as a trigger to fire vertical interrupt + */ +bool dce110_arm_vert_intr(struct timing_generator *tg, uint8_t width) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t v_blank_start = 0; + uint32_t v_blank_end = 0; + uint32_t val = 0; + uint32_t h_position, v_position; + + tg->funcs->get_scanoutpos( + tg, + &v_blank_start, + &v_blank_end, + &h_position, + &v_position); + + if (v_blank_start == 0 || v_blank_end == 0) + return false; + + set_reg_field_value( + val, + v_blank_start, + CRTC_VERTICAL_INTERRUPT0_POSITION, + CRTC_VERTICAL_INTERRUPT0_LINE_START); + + /* Set interval width for interrupt to fire to 1 scanline */ + set_reg_field_value( + val, + v_blank_start + width, + CRTC_VERTICAL_INTERRUPT0_POSITION, + CRTC_VERTICAL_INTERRUPT0_LINE_END); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_VERTICAL_INTERRUPT0_POSITION), val); + + return true; +} + +static bool dce110_is_tg_enabled(struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + addr = CRTC_REG(mmCRTC_CONTROL); + value = dm_read_reg(tg->ctx, addr); + field = get_reg_field_value(value, CRTC_CONTROL, + CRTC_CURRENT_MASTER_EN_STATE); + return field == 1; +} + +bool dce110_configure_crc(struct timing_generator *tg, + const struct crc_params *params) +{ + uint32_t cntl_addr = 0; + uint32_t addr = 0; + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Cannot configure crc on a CRTC that is disabled */ + if (!dce110_is_tg_enabled(tg)) + return false; + + cntl_addr = CRTC_REG(mmCRTC_CRC_CNTL); + + /* First, disable CRC before we configure it. */ + dm_write_reg(tg->ctx, cntl_addr, 0); + + if (!params->enable) + return true; + + /* Program frame boundaries */ + /* Window A x axis start and end. */ + value = 0; + addr = CRTC_REG(mmCRTC_CRC0_WINDOWA_X_CONTROL); + set_reg_field_value(value, params->windowa_x_start, + CRTC_CRC0_WINDOWA_X_CONTROL, + CRTC_CRC0_WINDOWA_X_START); + set_reg_field_value(value, params->windowa_x_end, + CRTC_CRC0_WINDOWA_X_CONTROL, + CRTC_CRC0_WINDOWA_X_END); + dm_write_reg(tg->ctx, addr, value); + + /* Window A y axis start and end. */ + value = 0; + addr = CRTC_REG(mmCRTC_CRC0_WINDOWA_Y_CONTROL); + set_reg_field_value(value, params->windowa_y_start, + CRTC_CRC0_WINDOWA_Y_CONTROL, + CRTC_CRC0_WINDOWA_Y_START); + set_reg_field_value(value, params->windowa_y_end, + CRTC_CRC0_WINDOWA_Y_CONTROL, + CRTC_CRC0_WINDOWA_Y_END); + dm_write_reg(tg->ctx, addr, value); + + /* Window B x axis start and end. */ + value = 0; + addr = CRTC_REG(mmCRTC_CRC0_WINDOWB_X_CONTROL); + set_reg_field_value(value, params->windowb_x_start, + CRTC_CRC0_WINDOWB_X_CONTROL, + CRTC_CRC0_WINDOWB_X_START); + set_reg_field_value(value, params->windowb_x_end, + CRTC_CRC0_WINDOWB_X_CONTROL, + CRTC_CRC0_WINDOWB_X_END); + dm_write_reg(tg->ctx, addr, value); + + /* Window B y axis start and end. */ + value = 0; + addr = CRTC_REG(mmCRTC_CRC0_WINDOWB_Y_CONTROL); + set_reg_field_value(value, params->windowb_y_start, + CRTC_CRC0_WINDOWB_Y_CONTROL, + CRTC_CRC0_WINDOWB_Y_START); + set_reg_field_value(value, params->windowb_y_end, + CRTC_CRC0_WINDOWB_Y_CONTROL, + CRTC_CRC0_WINDOWB_Y_END); + dm_write_reg(tg->ctx, addr, value); + + /* Set crc mode and selection, and enable. Only using CRC0*/ + value = 0; + set_reg_field_value(value, params->continuous_mode ? 1 : 0, + CRTC_CRC_CNTL, CRTC_CRC_CONT_EN); + set_reg_field_value(value, params->selection, + CRTC_CRC_CNTL, CRTC_CRC0_SELECT); + set_reg_field_value(value, 1, CRTC_CRC_CNTL, CRTC_CRC_EN); + dm_write_reg(tg->ctx, cntl_addr, value); + + return true; +} + +bool dce110_get_crc(struct timing_generator *tg, + uint32_t *r_cr, uint32_t *g_y, uint32_t *b_cb) +{ + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + addr = CRTC_REG(mmCRTC_CRC_CNTL); + value = dm_read_reg(tg->ctx, addr); + field = get_reg_field_value(value, CRTC_CRC_CNTL, CRTC_CRC_EN); + + /* Early return if CRC is not enabled for this CRTC */ + if (!field) + return false; + + addr = CRTC_REG(mmCRTC_CRC0_DATA_RG); + value = dm_read_reg(tg->ctx, addr); + *r_cr = get_reg_field_value(value, CRTC_CRC0_DATA_RG, CRC0_R_CR); + *g_y = get_reg_field_value(value, CRTC_CRC0_DATA_RG, CRC0_G_Y); + + addr = CRTC_REG(mmCRTC_CRC0_DATA_B); + value = dm_read_reg(tg->ctx, addr); + *b_cb = get_reg_field_value(value, CRTC_CRC0_DATA_B, CRC0_B_CB); + + return true; +} + +static const struct timing_generator_funcs dce110_tg_funcs = { + .validate_timing = dce110_tg_validate_timing, + .program_timing = dce110_tg_program_timing, + .enable_crtc = dce110_timing_generator_enable_crtc, + .disable_crtc = dce110_timing_generator_disable_crtc, + .is_counter_moving = dce110_timing_generator_is_counter_moving, + .get_position = dce110_timing_generator_get_position, + .get_frame_count = dce110_timing_generator_get_vblank_counter, + .get_scanoutpos = dce110_timing_generator_get_crtc_scanoutpos, + .set_early_control = dce110_timing_generator_set_early_control, + .wait_for_state = dce110_tg_wait_for_state, + .set_blank = dce110_tg_set_blank, + .is_blanked = dce110_tg_is_blanked, + .set_colors = dce110_tg_set_colors, + .set_overscan_blank_color = + dce110_timing_generator_set_overscan_color_black, + .set_blank_color = dce110_timing_generator_program_blank_color, + .disable_vga = dce110_timing_generator_disable_vga, + .did_triggered_reset_occur = + dce110_timing_generator_did_triggered_reset_occur, + .setup_global_swap_lock = + dce110_timing_generator_setup_global_swap_lock, + .enable_reset_trigger = dce110_timing_generator_enable_reset_trigger, + .enable_crtc_reset = dce110_timing_generator_enable_crtc_reset, + .disable_reset_trigger = dce110_timing_generator_disable_reset_trigger, + .tear_down_global_swap_lock = + dce110_timing_generator_tear_down_global_swap_lock, + .enable_advanced_request = + dce110_timing_generator_enable_advanced_request, + .set_drr = + dce110_timing_generator_set_drr, + .get_last_used_drr_vtotal = NULL, + .set_static_screen_control = + dce110_timing_generator_set_static_screen_control, + .set_test_pattern = dce110_timing_generator_set_test_pattern, + .arm_vert_intr = dce110_arm_vert_intr, + .is_tg_enabled = dce110_is_tg_enabled, + .configure_crc = dce110_configure_crc, + .get_crc = dce110_get_crc, +}; + +void dce110_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + tg110->controller_id = CONTROLLER_ID_D0 + instance; + tg110->base.inst = instance; + + tg110->offsets = *offsets; + + tg110->base.funcs = &dce110_tg_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + tg110->min_h_blank = 56; + tg110->min_h_front_porch = 4; + tg110->min_h_back_porch = 4; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.h new file mode 100644 index 000000000..d8a5ed7b4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.h @@ -0,0 +1,291 @@ +/* + * 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_TIMING_GENERATOR_DCE110_H__ +#define __DC_TIMING_GENERATOR_DCE110_H__ + +#include "timing_generator.h" +#include "../include/grph_object_id.h" + +/* GSL Sync related values */ + +/* In VSync mode, after 4 units of time, master pipe will generate + * flip_ready signal */ +#define VFLIP_READY_DELAY 4 +/* In HSync mode, after 2 units of time, master pipe will generate + * flip_ready signal */ +#define HFLIP_READY_DELAY 2 +/* 6 lines delay between forcing flip and checking all pipes ready */ +#define HFLIP_CHECK_DELAY 6 +/* 3 lines before end of frame */ +#define FLIP_READY_BACK_LOOKUP 3 + +/* Trigger Source Select - ASIC-defendant, actual values for the + * register programming */ +enum trigger_source_select { + TRIGGER_SOURCE_SELECT_LOGIC_ZERO = 0, + TRIGGER_SOURCE_SELECT_CRTC_VSYNCA = 1, + TRIGGER_SOURCE_SELECT_CRTC_HSYNCA = 2, + TRIGGER_SOURCE_SELECT_CRTC_VSYNCB = 3, + TRIGGER_SOURCE_SELECT_CRTC_HSYNCB = 4, + TRIGGER_SOURCE_SELECT_GENERICF = 5, + TRIGGER_SOURCE_SELECT_GENERICE = 6, + TRIGGER_SOURCE_SELECT_VSYNCA = 7, + TRIGGER_SOURCE_SELECT_HSYNCA = 8, + TRIGGER_SOURCE_SELECT_VSYNCB = 9, + TRIGGER_SOURCE_SELECT_HSYNCB = 10, + TRIGGER_SOURCE_SELECT_HPD1 = 11, + TRIGGER_SOURCE_SELECT_HPD2 = 12, + TRIGGER_SOURCE_SELECT_GENERICD = 13, + TRIGGER_SOURCE_SELECT_GENERICC = 14, + TRIGGER_SOURCE_SELECT_VIDEO_CAPTURE = 15, + TRIGGER_SOURCE_SELECT_GSL_GROUP0 = 16, + TRIGGER_SOURCE_SELECT_GSL_GROUP1 = 17, + TRIGGER_SOURCE_SELECT_GSL_GROUP2 = 18, + TRIGGER_SOURCE_SELECT_BLONY = 19, + TRIGGER_SOURCE_SELECT_GENERICA = 20, + TRIGGER_SOURCE_SELECT_GENERICB = 21, + TRIGGER_SOURCE_SELECT_GSL_ALLOW_FLIP = 22, + TRIGGER_SOURCE_SELECT_MANUAL_TRIGGER = 23 +}; + +/* Trigger Source Select - ASIC-dependant, actual values for the + * register programming */ +enum trigger_polarity_select { + TRIGGER_POLARITY_SELECT_LOGIC_ZERO = 0, + TRIGGER_POLARITY_SELECT_CRTC = 1, + TRIGGER_POLARITY_SELECT_GENERICA = 2, + TRIGGER_POLARITY_SELECT_GENERICB = 3, + TRIGGER_POLARITY_SELECT_HSYNCA = 4, + TRIGGER_POLARITY_SELECT_HSYNCB = 5, + TRIGGER_POLARITY_SELECT_VIDEO_CAPTURE = 6, + TRIGGER_POLARITY_SELECT_GENERICC = 7 +}; + + +struct dce110_timing_generator_offsets { + int32_t crtc; + int32_t dcp; + + /* DCE80 use only */ + int32_t dmif; +}; + +struct dce110_timing_generator { + struct timing_generator base; + struct dce110_timing_generator_offsets offsets; + struct dce110_timing_generator_offsets derived_offsets; + + enum controller_id controller_id; + + uint32_t max_h_total; + uint32_t max_v_total; + + uint32_t min_h_blank; + uint32_t min_h_front_porch; + uint32_t min_h_back_porch; + + /* DCE 12 */ + uint32_t min_h_sync_width; + uint32_t min_v_sync_width; + uint32_t min_v_blank; + +}; + +#define DCE110TG_FROM_TG(tg)\ + container_of(tg, struct dce110_timing_generator, base) + +void dce110_timing_generator_construct( + struct dce110_timing_generator *tg, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets); + +/* determine if given timing can be supported by TG */ +bool dce110_timing_generator_validate_timing( + struct timing_generator *tg, + const struct dc_crtc_timing *timing, + enum signal_type signal); + +/******** HW programming ************/ + +/* Program timing generator with given timing */ +bool dce110_timing_generator_program_timing_generator( + struct timing_generator *tg, + const struct dc_crtc_timing *dc_crtc_timing); + +/* Disable/Enable Timing Generator */ +bool dce110_timing_generator_enable_crtc(struct timing_generator *tg); +bool dce110_timing_generator_disable_crtc(struct timing_generator *tg); + +void dce110_timing_generator_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl); + +/**************** TG current status ******************/ + +/* return the current frame counter. Used by Linux kernel DRM */ +uint32_t dce110_timing_generator_get_vblank_counter( + struct timing_generator *tg); + +void dce110_timing_generator_get_position( + struct timing_generator *tg, + struct crtc_position *position); + +/* return true if TG counter is moving. false if TG is stopped */ +bool dce110_timing_generator_is_counter_moving(struct timing_generator *tg); + +/* wait until TG is in beginning of vertical blank region */ +void dce110_timing_generator_wait_for_vblank(struct timing_generator *tg); + +/* wait until TG is in beginning of active region */ +void dce110_timing_generator_wait_for_vactive(struct timing_generator *tg); + +/*********** Timing Generator Synchronization routines ****/ + +/* Setups Global Swap Lock group, TimingServer or TimingClient*/ +void dce110_timing_generator_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params); + +/* Clear all the register writes done by setup_global_swap_lock */ +void dce110_timing_generator_tear_down_global_swap_lock( + struct timing_generator *tg); + +/* Reset crtc position on master VSync */ +void dce110_timing_generator_enable_crtc_reset( + struct timing_generator *tg, + int source, + struct crtc_trigger_info *crtc_tp); + +/* Reset slave controllers on master VSync */ +void dce110_timing_generator_enable_reset_trigger( + struct timing_generator *tg, + int source); + +/* disabling trigger-reset */ +void dce110_timing_generator_disable_reset_trigger( + struct timing_generator *tg); + +/* Checks whether CRTC triggered reset occurred */ +bool dce110_timing_generator_did_triggered_reset_occur( + struct timing_generator *tg); + +/******** Stuff to move to other virtual HW objects *****************/ +/* Move to enable accelerated mode */ +void dce110_timing_generator_disable_vga(struct timing_generator *tg); +/* TODO: Should we move it to transform */ +/* Fully program CRTC timing in timing generator */ +void dce110_timing_generator_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing); + +/* TODO: Should we move it to opp? */ +/* Combine with below and move YUV/RGB color conversion to SW layer */ +void dce110_timing_generator_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color); +/* Combine with above and move YUV/RGB color conversion to SW layer */ +void dce110_timing_generator_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color); +void dce110_timing_generator_color_space_to_black_color( + enum dc_color_space colorspace, + struct tg_color *black_color); +/*************** End-of-move ********************/ + +/* Not called yet */ +void dce110_timing_generator_set_test_pattern( + struct timing_generator *tg, + /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' + * because this is not DP-specific (which is probably somewhere in DP + * encoder) */ + enum controller_dp_test_pattern test_pattern, + enum dc_color_depth color_depth); + +void dce110_timing_generator_set_drr( + struct timing_generator *tg, + const struct drr_params *params); + +void dce110_timing_generator_set_static_screen_control( + struct timing_generator *tg, + uint32_t event_triggers, + uint32_t num_frames); + +void dce110_timing_generator_get_crtc_scanoutpos( + struct timing_generator *tg, + uint32_t *v_blank_start, + uint32_t *v_blank_end, + uint32_t *h_position, + uint32_t *v_position); + +void dce110_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing); + +void dce110_timing_generator_set_lock_master(struct timing_generator *tg, + bool lock); + +void dce110_tg_program_blank_color(struct timing_generator *tg, + const struct tg_color *black_color); + +void dce110_tg_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color); + +void dce110_tg_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + int vready_offset, + int vstartup_start, + int vupdate_offset, + int vupdate_width, + const enum signal_type signal, + bool use_vbios); + +bool dce110_tg_is_blanked(struct timing_generator *tg); + +void dce110_tg_set_blank(struct timing_generator *tg, + bool enable_blanking); + +bool dce110_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing); + +void dce110_tg_wait_for_state(struct timing_generator *tg, + enum crtc_state state); + +void dce110_tg_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color); + +bool dce110_arm_vert_intr( + struct timing_generator *tg, uint8_t width); + +bool dce110_configure_crc(struct timing_generator *tg, + const struct crc_params *params); + +bool dce110_get_crc(struct timing_generator *tg, + uint32_t *r_cr, uint32_t *g_y, uint32_t *b_cb); + +#endif /* __DC_TIMING_GENERATOR_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c new file mode 100644 index 000000000..c509384ff --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c @@ -0,0 +1,705 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "dm_services.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dc_types.h" +#include "dc_bios_types.h" +#include "dc.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "dce110_timing_generator.h" +#include "dce110_timing_generator_v.h" + +#include "timing_generator.h" + +#define DC_LOGGER \ + tg->ctx->logger +/** ******************************************************************************** + * + * DCE11 Timing Generator Implementation + * + **********************************************************************************/ + +/* + * Enable CRTCV + */ + +static bool dce110_timing_generator_v_enable_crtc(struct timing_generator *tg) +{ +/* + * Set MASTER_UPDATE_MODE to 0 + * This is needed for DRR, and also suggested to be default value by Syed. + */ + uint32_t value; + + value = 0; + set_reg_field_value(value, 0, + CRTCV_MASTER_UPDATE_MODE, MASTER_UPDATE_MODE); + dm_write_reg(tg->ctx, + mmCRTCV_MASTER_UPDATE_MODE, value); + + /* TODO: may want this on for looking for underflow */ + value = 0; + dm_write_reg(tg->ctx, mmCRTCV_MASTER_UPDATE_MODE, value); + + value = 0; + set_reg_field_value(value, 1, + CRTCV_MASTER_EN, CRTC_MASTER_EN); + dm_write_reg(tg->ctx, + mmCRTCV_MASTER_EN, value); + + return true; +} + +static bool dce110_timing_generator_v_disable_crtc(struct timing_generator *tg) +{ + uint32_t value; + + value = dm_read_reg(tg->ctx, + mmCRTCV_CONTROL); + set_reg_field_value(value, 0, + CRTCV_CONTROL, CRTC_DISABLE_POINT_CNTL); + set_reg_field_value(value, 0, + CRTCV_CONTROL, CRTC_MASTER_EN); + dm_write_reg(tg->ctx, + mmCRTCV_CONTROL, value); + /* + * TODO: call this when adding stereo support + * tg->funcs->disable_stereo(tg); + */ + return true; +} + +static void dce110_timing_generator_v_blank_crtc(struct timing_generator *tg) +{ + uint32_t addr = mmCRTCV_BLANK_CONTROL; + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + 1, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DATA_EN); + + set_reg_field_value( + value, + 0, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DE_MODE); + + dm_write_reg(tg->ctx, addr, value); +} + +static void dce110_timing_generator_v_unblank_crtc(struct timing_generator *tg) +{ + uint32_t addr = mmCRTCV_BLANK_CONTROL; + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + 0, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DATA_EN); + + set_reg_field_value( + value, + 0, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DE_MODE); + + dm_write_reg(tg->ctx, addr, value); +} + +static bool dce110_timing_generator_v_is_in_vertical_blank( + struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + + addr = mmCRTCV_STATUS; + value = dm_read_reg(tg->ctx, addr); + field = get_reg_field_value(value, CRTCV_STATUS, CRTC_V_BLANK); + return field == 1; +} + +static bool dce110_timing_generator_v_is_counter_moving(struct timing_generator *tg) +{ + uint32_t value; + uint32_t h1 = 0; + uint32_t h2 = 0; + uint32_t v1 = 0; + uint32_t v2 = 0; + + value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); + + h1 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_HORZ_COUNT); + + v1 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_VERT_COUNT); + + value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); + + h2 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_HORZ_COUNT); + + v2 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_VERT_COUNT); + + if (h1 == h2 && v1 == v2) + return false; + else + return true; +} + +static void dce110_timing_generator_v_wait_for_vblank(struct timing_generator *tg) +{ + /* We want to catch beginning of VBlank here, so if the first try are + * in VBlank, we might be very close to Active, in this case wait for + * another frame + */ + while (dce110_timing_generator_v_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_v_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } + + while (!dce110_timing_generator_v_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_v_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/* + * Wait till we are in VActive (anywhere in VActive) + */ +static void dce110_timing_generator_v_wait_for_vactive(struct timing_generator *tg) +{ + while (dce110_timing_generator_v_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_v_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +static void dce110_timing_generator_v_wait_for_state(struct timing_generator *tg, + enum crtc_state state) +{ + switch (state) { + case CRTC_STATE_VBLANK: + dce110_timing_generator_v_wait_for_vblank(tg); + break; + + case CRTC_STATE_VACTIVE: + dce110_timing_generator_v_wait_for_vactive(tg); + break; + + default: + break; + } +} + +static void dce110_timing_generator_v_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t vsync_offset = timing->v_border_bottom + + timing->v_front_porch; + uint32_t v_sync_start = timing->v_addressable + vsync_offset; + + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + uint32_t h_sync_start = timing->h_addressable + hsync_offset; + + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr = 0; + uint32_t tmp = 0; + + addr = mmCRTCV_H_TOTAL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->h_total - 1, + CRTCV_H_TOTAL, + CRTC_H_TOTAL); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_TOTAL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTCV_V_TOTAL, + CRTC_V_TOTAL); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_H_BLANK_START_END; + value = dm_read_reg(ctx, addr); + + tmp = timing->h_total - + (h_sync_start + timing->h_border_left); + + set_reg_field_value( + value, + tmp, + CRTCV_H_BLANK_START_END, + CRTC_H_BLANK_END); + + tmp = tmp + timing->h_addressable + + timing->h_border_left + timing->h_border_right; + + set_reg_field_value( + value, + tmp, + CRTCV_H_BLANK_START_END, + CRTC_H_BLANK_START); + + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_BLANK_START_END; + value = dm_read_reg(ctx, addr); + + tmp = timing->v_total - (v_sync_start + timing->v_border_top); + + set_reg_field_value( + value, + tmp, + CRTCV_V_BLANK_START_END, + CRTC_V_BLANK_END); + + tmp = tmp + timing->v_addressable + timing->v_border_top + + timing->v_border_bottom; + + set_reg_field_value( + value, + tmp, + CRTCV_V_BLANK_START_END, + CRTC_V_BLANK_START); + + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_H_SYNC_A; + value = 0; + set_reg_field_value( + value, + timing->h_sync_width, + CRTCV_H_SYNC_A, + CRTC_H_SYNC_A_END); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_H_SYNC_A_CNTL; + value = dm_read_reg(ctx, addr); + if (timing->flags.HSYNC_POSITIVE_POLARITY) { + set_reg_field_value( + value, + 0, + CRTCV_H_SYNC_A_CNTL, + CRTC_H_SYNC_A_POL); + } else { + set_reg_field_value( + value, + 1, + CRTCV_H_SYNC_A_CNTL, + CRTC_H_SYNC_A_POL); + } + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_SYNC_A; + value = 0; + set_reg_field_value( + value, + timing->v_sync_width, + CRTCV_V_SYNC_A, + CRTC_V_SYNC_A_END); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_SYNC_A_CNTL; + value = dm_read_reg(ctx, addr); + if (timing->flags.VSYNC_POSITIVE_POLARITY) { + set_reg_field_value( + value, + 0, + CRTCV_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL); + } else { + set_reg_field_value( + value, + 1, + CRTCV_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL); + } + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_INTERLACE_CONTROL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->flags.INTERLACE, + CRTCV_INTERLACE_CONTROL, + CRTC_INTERLACE_ENABLE); + dm_write_reg(ctx, addr, value); +} + +static void dce110_timing_generator_v_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + uint32_t addr = mmCRTCV_START_LINE_CONTROL; + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (enable) { + if ((timing->v_sync_width + timing->v_front_porch) <= 3) { + set_reg_field_value( + value, + 3, + CRTCV_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + } else { + set_reg_field_value( + value, + 4, + CRTCV_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + } + set_reg_field_value( + value, + 0, + CRTCV_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } else { + set_reg_field_value( + value, + 2, + CRTCV_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 1, + CRTCV_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } + + dm_write_reg(tg->ctx, addr, value); +} + +static void dce110_timing_generator_v_set_blank(struct timing_generator *tg, + bool enable_blanking) +{ + if (enable_blanking) + dce110_timing_generator_v_blank_crtc(tg); + else + dce110_timing_generator_v_unblank_crtc(tg); +} + +static void dce110_timing_generator_v_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + int vready_offset, + int vstartup_start, + int vupdate_offset, + int vupdate_width, + const enum signal_type signal, + bool use_vbios) +{ + if (use_vbios) + dce110_timing_generator_program_timing_generator(tg, timing); + else + dce110_timing_generator_v_program_blanking(tg, timing); +} + +static void dce110_timing_generator_v_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color) +{ + uint32_t addr = mmCRTCV_BLACK_COLOR; + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); +} + +static void dce110_timing_generator_v_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t addr; + uint32_t value = 0; + + set_reg_field_value( + value, + color->color_b_cb, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + color->color_r_cr, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + set_reg_field_value( + value, + color->color_g_y, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + addr = mmCRTCV_OVERSCAN_COLOR; + dm_write_reg(ctx, addr, value); + addr = mmCRTCV_BLACK_COLOR; + dm_write_reg(ctx, addr, value); + /* This is desirable to have a constant DAC output voltage during the + * blank time that is higher than the 0 volt reference level that the + * DAC outputs when the NBLANK signal + * is asserted low, such as for output to an analog TV. */ + addr = mmCRTCV_BLANK_DATA_COLOR; + dm_write_reg(ctx, addr, value); + + /* TO DO we have to program EXT registers and we need to know LB DATA + * format because it is used when more 10 , i.e. 12 bits per color + * + * m_mmDxCRTC_OVERSCAN_COLOR_EXT + * m_mmDxCRTC_BLACK_COLOR_EXT + * m_mmDxCRTC_BLANK_DATA_COLOR_EXT + */ +} + +static void dce110_tg_v_program_blank_color(struct timing_generator *tg, + const struct tg_color *black_color) +{ + uint32_t addr = mmCRTCV_BLACK_COLOR; + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); + + addr = mmCRTCV_BLANK_DATA_COLOR; + dm_write_reg(tg->ctx, addr, value); +} + +static void dce110_timing_generator_v_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr; + + set_reg_field_value( + value, + overscan_color->color_b_cb, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + overscan_color->color_g_y, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + overscan_color->color_r_cr, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + addr = mmCRTCV_OVERSCAN_COLOR; + dm_write_reg(ctx, addr, value); +} + +static void dce110_timing_generator_v_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color) +{ + if (blank_color != NULL) + dce110_tg_v_program_blank_color(tg, blank_color); + if (overscan_color != NULL) + dce110_timing_generator_v_set_overscan_color(tg, overscan_color); +} + +static void dce110_timing_generator_v_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl) +{ + uint32_t regval; + uint32_t address = mmCRTC_CONTROL; + + regval = dm_read_reg(tg->ctx, address); + set_reg_field_value(regval, early_cntl, + CRTCV_CONTROL, CRTC_HBLANK_EARLY_CONTROL); + dm_write_reg(tg->ctx, address, regval); +} + +static uint32_t dce110_timing_generator_v_get_vblank_counter(struct timing_generator *tg) +{ + uint32_t addr = mmCRTCV_STATUS_FRAME_COUNT; + uint32_t value = dm_read_reg(tg->ctx, addr); + uint32_t field = get_reg_field_value( + value, CRTCV_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); + + return field; +} + +static bool dce110_timing_generator_v_did_triggered_reset_occur( + struct timing_generator *tg) +{ + DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); + return false; +} + +static void dce110_timing_generator_v_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params) +{ + DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_enable_reset_trigger( + struct timing_generator *tg, + int source_tg_inst) +{ + DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_disable_reset_trigger( + struct timing_generator *tg) +{ + DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_tear_down_global_swap_lock( + struct timing_generator *tg) +{ + DC_LOG_ERROR("Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_disable_vga( + struct timing_generator *tg) +{ + return; +} + +/** ******************************************************************************************** + * + * DCE11 Timing Generator Constructor / Destructor + * + *********************************************************************************************/ +static const struct timing_generator_funcs dce110_tg_v_funcs = { + .validate_timing = dce110_tg_validate_timing, + .program_timing = dce110_timing_generator_v_program_timing, + .enable_crtc = dce110_timing_generator_v_enable_crtc, + .disable_crtc = dce110_timing_generator_v_disable_crtc, + .is_counter_moving = dce110_timing_generator_v_is_counter_moving, + .get_position = NULL, /* Not to be implemented for underlay*/ + .get_frame_count = dce110_timing_generator_v_get_vblank_counter, + .set_early_control = dce110_timing_generator_v_set_early_control, + .wait_for_state = dce110_timing_generator_v_wait_for_state, + .set_blank = dce110_timing_generator_v_set_blank, + .set_colors = dce110_timing_generator_v_set_colors, + .set_overscan_blank_color = + dce110_timing_generator_v_set_overscan_color_black, + .set_blank_color = dce110_timing_generator_v_program_blank_color, + .disable_vga = dce110_timing_generator_v_disable_vga, + .did_triggered_reset_occur = + dce110_timing_generator_v_did_triggered_reset_occur, + .setup_global_swap_lock = + dce110_timing_generator_v_setup_global_swap_lock, + .enable_reset_trigger = dce110_timing_generator_v_enable_reset_trigger, + .disable_reset_trigger = dce110_timing_generator_v_disable_reset_trigger, + .tear_down_global_swap_lock = + dce110_timing_generator_v_tear_down_global_swap_lock, + .enable_advanced_request = + dce110_timing_generator_v_enable_advanced_request +}; + +void dce110_timing_generator_v_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx) +{ + tg110->controller_id = CONTROLLER_ID_UNDERLAY0; + + tg110->base.funcs = &dce110_tg_v_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + tg110->min_h_blank = 56; + tg110->min_h_front_porch = 4; + tg110->min_h_back_porch = 4; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.h new file mode 100644 index 000000000..d2623a599 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.h @@ -0,0 +1,33 @@ +/* + * 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_TIMING_GENERATOR_V_DCE110_H__ +#define __DC_TIMING_GENERATOR_V_DCE110_H__ + +void dce110_timing_generator_v_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx); + +#endif /* __DC_TIMING_GENERATOR_V_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c new file mode 100644 index 000000000..28d3b2663 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c @@ -0,0 +1,717 @@ +/* + * 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 "dce110_transform_v.h" +#include "dm_services.h" +#include "dc.h" +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#define SCLV_PHASES 64 +#define DC_LOGGER \ + xfm->ctx->logger + +struct sclv_ratios_inits { + uint32_t h_int_scale_ratio_luma; + uint32_t h_int_scale_ratio_chroma; + uint32_t v_int_scale_ratio_luma; + uint32_t v_int_scale_ratio_chroma; + struct init_int_and_frac h_init_luma; + struct init_int_and_frac h_init_chroma; + struct init_int_and_frac v_init_luma; + struct init_int_and_frac v_init_chroma; +}; + +static void calculate_viewport( + const struct scaler_data *scl_data, + struct rect *luma_viewport, + struct rect *chroma_viewport) +{ + /*Do not set chroma vp for rgb444 pixel format*/ + luma_viewport->x = scl_data->viewport.x - scl_data->viewport.x % 2; + luma_viewport->y = scl_data->viewport.y - scl_data->viewport.y % 2; + luma_viewport->width = + scl_data->viewport.width - scl_data->viewport.width % 2; + luma_viewport->height = + scl_data->viewport.height - scl_data->viewport.height % 2; + chroma_viewport->x = luma_viewport->x; + chroma_viewport->y = luma_viewport->y; + chroma_viewport->height = luma_viewport->height; + chroma_viewport->width = luma_viewport->width; + + if (scl_data->format == PIXEL_FORMAT_420BPP8) { + luma_viewport->height += luma_viewport->height % 2; + luma_viewport->width += luma_viewport->width % 2; + /*for 420 video chroma is 1/4 the area of luma, scaled + *vertically and horizontally + */ + chroma_viewport->x = luma_viewport->x / 2; + chroma_viewport->y = luma_viewport->y / 2; + chroma_viewport->height = luma_viewport->height / 2; + chroma_viewport->width = luma_viewport->width / 2; + } +} + +static void program_viewport( + struct dce_transform *xfm_dce, + struct rect *luma_view_port, + struct rect *chroma_view_port) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t value = 0; + uint32_t addr = 0; + + if (luma_view_port->width != 0 && luma_view_port->height != 0) { + addr = mmSCLV_VIEWPORT_START; + value = 0; + set_reg_field_value( + value, + luma_view_port->x, + SCLV_VIEWPORT_START, + VIEWPORT_X_START); + set_reg_field_value( + value, + luma_view_port->y, + SCLV_VIEWPORT_START, + VIEWPORT_Y_START); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VIEWPORT_SIZE; + value = 0; + set_reg_field_value( + value, + luma_view_port->height, + SCLV_VIEWPORT_SIZE, + VIEWPORT_HEIGHT); + set_reg_field_value( + value, + luma_view_port->width, + SCLV_VIEWPORT_SIZE, + VIEWPORT_WIDTH); + dm_write_reg(ctx, addr, value); + } + + if (chroma_view_port->width != 0 && chroma_view_port->height != 0) { + addr = mmSCLV_VIEWPORT_START_C; + value = 0; + set_reg_field_value( + value, + chroma_view_port->x, + SCLV_VIEWPORT_START_C, + VIEWPORT_X_START_C); + set_reg_field_value( + value, + chroma_view_port->y, + SCLV_VIEWPORT_START_C, + VIEWPORT_Y_START_C); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VIEWPORT_SIZE_C; + value = 0; + set_reg_field_value( + value, + chroma_view_port->height, + SCLV_VIEWPORT_SIZE_C, + VIEWPORT_HEIGHT_C); + set_reg_field_value( + value, + chroma_view_port->width, + SCLV_VIEWPORT_SIZE_C, + VIEWPORT_WIDTH_C); + dm_write_reg(ctx, addr, value); + } +} + +/* + * Function: + * void setup_scaling_configuration + * + * Purpose: setup scaling mode : bypass, RGb, YCbCr and nummber of taps + * Input: data + * + * Output: + * void + */ +static bool setup_scaling_configuration( + struct dce_transform *xfm_dce, + const struct scaler_data *data) +{ + bool is_scaling_needed = false; + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t value = 0; + + set_reg_field_value(value, data->taps.h_taps - 1, + SCLV_TAP_CONTROL, SCL_H_NUM_OF_TAPS); + set_reg_field_value(value, data->taps.v_taps - 1, + SCLV_TAP_CONTROL, SCL_V_NUM_OF_TAPS); + set_reg_field_value(value, data->taps.h_taps_c - 1, + SCLV_TAP_CONTROL, SCL_H_NUM_OF_TAPS_C); + set_reg_field_value(value, data->taps.v_taps_c - 1, + SCLV_TAP_CONTROL, SCL_V_NUM_OF_TAPS_C); + dm_write_reg(ctx, mmSCLV_TAP_CONTROL, value); + + value = 0; + if (data->taps.h_taps + data->taps.v_taps > 2) { + set_reg_field_value(value, 1, SCLV_MODE, SCL_MODE); + set_reg_field_value(value, 1, SCLV_MODE, SCL_PSCL_EN); + is_scaling_needed = true; + } else { + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN); + } + + if (data->taps.h_taps_c + data->taps.v_taps_c > 2) { + set_reg_field_value(value, 1, SCLV_MODE, SCL_MODE_C); + set_reg_field_value(value, 1, SCLV_MODE, SCL_PSCL_EN_C); + is_scaling_needed = true; + } else if (data->format != PIXEL_FORMAT_420BPP8) { + set_reg_field_value( + value, + get_reg_field_value(value, SCLV_MODE, SCL_MODE), + SCLV_MODE, + SCL_MODE_C); + set_reg_field_value( + value, + get_reg_field_value(value, SCLV_MODE, SCL_PSCL_EN), + SCLV_MODE, + SCL_PSCL_EN_C); + } else { + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE_C); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN_C); + } + dm_write_reg(ctx, mmSCLV_MODE, value); + + value = 0; + /* + * 0 - Replaced out of bound pixels with black pixel + * (or any other required color) + * 1 - Replaced out of bound pixels with the edge pixel + */ + set_reg_field_value(value, 1, SCLV_CONTROL, SCL_BOUNDARY_MODE); + dm_write_reg(ctx, mmSCLV_CONTROL, value); + + return is_scaling_needed; +} + +/* + * Function: + * void program_overscan + * + * Purpose: Programs overscan border + * Input: overscan + * + * Output: void + */ +static void program_overscan( + struct dce_transform *xfm_dce, + const struct scaler_data *data) +{ + uint32_t overscan_left_right = 0; + uint32_t overscan_top_bottom = 0; + + 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 (xfm_dce->base.ctx->dc->debug.visual_confirm != VISUAL_CONFIRM_DISABLE) { + overscan_bottom += 2; + overscan_right += 2; + } + + if (overscan_right < 0) { + BREAK_TO_DEBUGGER(); + overscan_right = 0; + } + if (overscan_bottom < 0) { + BREAK_TO_DEBUGGER(); + overscan_bottom = 0; + } + + set_reg_field_value(overscan_left_right, data->recout.x, + EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT); + + set_reg_field_value(overscan_left_right, overscan_right, + EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT); + + set_reg_field_value(overscan_top_bottom, data->recout.y, + EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP); + + set_reg_field_value(overscan_top_bottom, overscan_bottom, + EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM); + + dm_write_reg(xfm_dce->base.ctx, + mmSCLV_EXT_OVERSCAN_LEFT_RIGHT, + overscan_left_right); + + dm_write_reg(xfm_dce->base.ctx, + mmSCLV_EXT_OVERSCAN_TOP_BOTTOM, + overscan_top_bottom); +} + +static void set_coeff_update_complete( + struct dce_transform *xfm_dce) +{ + uint32_t value; + + value = dm_read_reg(xfm_dce->base.ctx, mmSCLV_UPDATE); + set_reg_field_value(value, 1, SCLV_UPDATE, SCL_COEF_UPDATE_COMPLETE); + dm_write_reg(xfm_dce->base.ctx, mmSCLV_UPDATE, value); +} + +static void program_multi_taps_filter( + struct dce_transform *xfm_dce, + int taps, + const uint16_t *coeffs, + enum ram_filter_type filter_type) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + int i, phase, pair; + int array_idx = 0; + int taps_pairs = (taps + 1) / 2; + int phases_to_program = SCLV_PHASES / 2 + 1; + + uint32_t select = 0; + uint32_t power_ctl, power_ctl_off; + + if (!coeffs) + return; + + /*We need to disable power gating on coeff memory to do programming*/ + power_ctl = dm_read_reg(ctx, mmDCFEV_MEM_PWR_CTRL); + power_ctl_off = power_ctl; + set_reg_field_value(power_ctl_off, 1, DCFEV_MEM_PWR_CTRL, SCLV_COEFF_MEM_PWR_DIS); + dm_write_reg(ctx, mmDCFEV_MEM_PWR_CTRL, power_ctl_off); + + /*Wait to disable gating:*/ + for (i = 0; i < 10; i++) { + if (get_reg_field_value( + dm_read_reg(ctx, mmDCFEV_MEM_PWR_STATUS), + DCFEV_MEM_PWR_STATUS, + SCLV_COEFF_MEM_PWR_STATE) == 0) + break; + + udelay(1); + } + + set_reg_field_value(select, filter_type, SCLV_COEF_RAM_SELECT, SCL_C_RAM_FILTER_TYPE); + + 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*/ + set_reg_field_value(select, phase, SCLV_COEF_RAM_SELECT, SCL_C_RAM_PHASE); + for (pair = 0; pair < taps_pairs; pair++) { + uint32_t data = 0; + + set_reg_field_value(select, pair, + SCLV_COEF_RAM_SELECT, SCL_C_RAM_TAP_PAIR_IDX); + + dm_write_reg(ctx, mmSCLV_COEF_RAM_SELECT, select); + + set_reg_field_value( + data, 1, + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF_EN); + set_reg_field_value( + data, coeffs[array_idx], + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF); + + if (taps % 2 && pair == taps_pairs - 1) { + set_reg_field_value( + data, 0, + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + array_idx++; + } else { + set_reg_field_value( + data, 1, + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + set_reg_field_value( + data, coeffs[array_idx + 1], + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF); + + array_idx += 2; + } + + dm_write_reg(ctx, mmSCLV_COEF_RAM_TAP_DATA, data); + } + } + + /*We need to restore power gating on coeff memory to initial state*/ + dm_write_reg(ctx, mmDCFEV_MEM_PWR_CTRL, power_ctl); +} + +static void calculate_inits( + struct dce_transform *xfm_dce, + const struct scaler_data *data, + struct sclv_ratios_inits *inits, + struct rect *luma_viewport, + struct rect *chroma_viewport) +{ + inits->h_int_scale_ratio_luma = + dc_fixpt_u2d19(data->ratios.horz) << 5; + inits->v_int_scale_ratio_luma = + dc_fixpt_u2d19(data->ratios.vert) << 5; + inits->h_int_scale_ratio_chroma = + dc_fixpt_u2d19(data->ratios.horz_c) << 5; + inits->v_int_scale_ratio_chroma = + dc_fixpt_u2d19(data->ratios.vert_c) << 5; + + inits->h_init_luma.integer = 1; + inits->v_init_luma.integer = 1; + inits->h_init_chroma.integer = 1; + inits->v_init_chroma.integer = 1; +} + +static void program_scl_ratios_inits( + struct dce_transform *xfm_dce, + struct sclv_ratios_inits *inits) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t addr = mmSCLV_HORZ_FILTER_SCALE_RATIO; + uint32_t value = 0; + + set_reg_field_value( + value, + inits->h_int_scale_ratio_luma, + SCLV_HORZ_FILTER_SCALE_RATIO, + SCL_H_SCALE_RATIO); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_SCALE_RATIO; + value = 0; + set_reg_field_value( + value, + inits->v_int_scale_ratio_luma, + SCLV_VERT_FILTER_SCALE_RATIO, + SCL_V_SCALE_RATIO); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_HORZ_FILTER_SCALE_RATIO_C; + value = 0; + set_reg_field_value( + value, + inits->h_int_scale_ratio_chroma, + SCLV_HORZ_FILTER_SCALE_RATIO_C, + SCL_H_SCALE_RATIO_C); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_SCALE_RATIO_C; + value = 0; + set_reg_field_value( + value, + inits->v_int_scale_ratio_chroma, + SCLV_VERT_FILTER_SCALE_RATIO_C, + SCL_V_SCALE_RATIO_C); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_HORZ_FILTER_INIT; + value = 0; + set_reg_field_value( + value, + inits->h_init_luma.fraction, + SCLV_HORZ_FILTER_INIT, + SCL_H_INIT_FRAC); + set_reg_field_value( + value, + inits->h_init_luma.integer, + SCLV_HORZ_FILTER_INIT, + SCL_H_INIT_INT); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_INIT; + value = 0; + set_reg_field_value( + value, + inits->v_init_luma.fraction, + SCLV_VERT_FILTER_INIT, + SCL_V_INIT_FRAC); + set_reg_field_value( + value, + inits->v_init_luma.integer, + SCLV_VERT_FILTER_INIT, + SCL_V_INIT_INT); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_HORZ_FILTER_INIT_C; + value = 0; + set_reg_field_value( + value, + inits->h_init_chroma.fraction, + SCLV_HORZ_FILTER_INIT_C, + SCL_H_INIT_FRAC_C); + set_reg_field_value( + value, + inits->h_init_chroma.integer, + SCLV_HORZ_FILTER_INIT_C, + SCL_H_INIT_INT_C); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_INIT_C; + value = 0; + set_reg_field_value( + value, + inits->v_init_chroma.fraction, + SCLV_VERT_FILTER_INIT_C, + SCL_V_INIT_FRAC_C); + set_reg_field_value( + value, + inits->v_init_chroma.integer, + SCLV_VERT_FILTER_INIT_C, + SCL_V_INIT_INT_C); + dm_write_reg(ctx, addr, value); +} + +static const uint16_t *get_filter_coeffs_64p(int taps, struct fixed31_32 ratio) +{ + if (taps == 4) + return get_filter_4tap_64p(ratio); + else if (taps == 2) + return get_filter_2tap_64p(); + else if (taps == 1) + return NULL; + else { + /* should never happen, bug */ + BREAK_TO_DEBUGGER(); + return NULL; + } +} + +static bool dce110_xfmv_power_up_line_buffer(struct transform *xfm) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + uint32_t value; + + value = dm_read_reg(xfm_dce->base.ctx, mmLBV_MEMORY_CTRL); + + /*Use all three pieces of memory always*/ + set_reg_field_value(value, 0, LBV_MEMORY_CTRL, LB_MEMORY_CONFIG); + /*hard coded number DCE11 1712(0x6B0) Partitions: 720/960/1712*/ + set_reg_field_value(value, xfm_dce->lb_memory_size, LBV_MEMORY_CTRL, + LB_MEMORY_SIZE); + + dm_write_reg(xfm_dce->base.ctx, mmLBV_MEMORY_CTRL, value); + + return true; +} + +static void dce110_xfmv_set_scaler( + struct transform *xfm, + const struct scaler_data *data) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + bool is_scaling_required = false; + bool filter_updated = false; + const uint16_t *coeffs_v, *coeffs_h, *coeffs_h_c, *coeffs_v_c; + struct rect luma_viewport = {0}; + struct rect chroma_viewport = {0}; + + dce110_xfmv_power_up_line_buffer(xfm); + /* 1. Calculate viewport, viewport programming should happen after init + * calculations as they may require an adjustment in the viewport. + */ + + calculate_viewport(data, &luma_viewport, &chroma_viewport); + + /* 2. Program overscan */ + program_overscan(xfm_dce, data); + + /* 3. Program taps and configuration */ + is_scaling_required = setup_scaling_configuration(xfm_dce, data); + + if (is_scaling_required) { + /* 4. Calculate and program ratio, filter initialization */ + + struct sclv_ratios_inits inits = { 0 }; + + calculate_inits( + xfm_dce, + data, + &inits, + &luma_viewport, + &chroma_viewport); + + program_scl_ratios_inits(xfm_dce, &inits); + + coeffs_v = get_filter_coeffs_64p(data->taps.v_taps, data->ratios.vert); + coeffs_h = get_filter_coeffs_64p(data->taps.h_taps, data->ratios.horz); + coeffs_v_c = get_filter_coeffs_64p(data->taps.v_taps_c, data->ratios.vert_c); + coeffs_h_c = get_filter_coeffs_64p(data->taps.h_taps_c, data->ratios.horz_c); + + if (coeffs_v != xfm_dce->filter_v + || coeffs_v_c != xfm_dce->filter_v_c + || coeffs_h != xfm_dce->filter_h + || coeffs_h_c != xfm_dce->filter_h_c) { + /* 5. Program vertical filters */ + 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_c, + coeffs_v_c, + FILTER_TYPE_CBCR_VERTICAL); + + /* 6. Program horizontal filters */ + 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_c, + coeffs_h_c, + FILTER_TYPE_CBCR_HORIZONTAL); + + xfm_dce->filter_v = coeffs_v; + xfm_dce->filter_v_c = coeffs_v_c; + xfm_dce->filter_h = coeffs_h; + xfm_dce->filter_h_c = coeffs_h_c; + filter_updated = true; + } + } + + /* 7. Program the viewport */ + program_viewport(xfm_dce, &luma_viewport, &chroma_viewport); + + /* 8. Set bit to flip to new coefficient memory */ + if (filter_updated) + set_coeff_update_complete(xfm_dce); +} + +static void dce110_xfmv_reset(struct transform *xfm) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + xfm_dce->filter_h = NULL; + xfm_dce->filter_v = NULL; + xfm_dce->filter_h_c = NULL; + xfm_dce->filter_v_c = NULL; +} + +static void dce110_xfmv_set_gamut_remap( + struct transform *xfm, + const struct xfm_grph_csc_adjustment *adjust) +{ + /* DO NOTHING*/ +} + +static void dce110_xfmv_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 = 0; + int expan_mode = 0; + uint32_t reg_data = 0; + + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + pixel_depth = 2; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_24BPP: + pixel_depth = 1; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_30BPP: + pixel_depth = 0; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_36BPP: + pixel_depth = 3; + expan_mode = 0; + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + + set_reg_field_value( + reg_data, + expan_mode, + LBV_DATA_FORMAT, + PIXEL_EXPAN_MODE); + + set_reg_field_value( + reg_data, + pixel_depth, + LBV_DATA_FORMAT, + PIXEL_DEPTH); + + dm_write_reg(xfm->ctx, mmLBV_DATA_FORMAT, reg_data); + + if (!(xfm_dce->lb_pixel_depth_supported & depth)) { + /*we should use unsupported capabilities + * unless it is required by w/a*/ + DC_LOG_WARNING("%s: Capability not supported", + __func__); + } +} + +static const struct transform_funcs dce110_xfmv_funcs = { + .transform_reset = dce110_xfmv_reset, + .transform_set_scaler = dce110_xfmv_set_scaler, + .transform_set_gamut_remap = + dce110_xfmv_set_gamut_remap, + .opp_set_csc_default = dce110_opp_v_set_csc_default, + .opp_set_csc_adjustment = dce110_opp_v_set_csc_adjustment, + .opp_power_on_regamma_lut = dce110_opp_power_on_regamma_lut_v, + .opp_program_regamma_pwl = dce110_opp_program_regamma_pwl_v, + .opp_set_regamma_mode = dce110_opp_set_regamma_mode_v, + .transform_set_pixel_storage_depth = + dce110_xfmv_set_pixel_storage_depth, + .transform_get_optimal_number_of_taps = + dce_transform_get_optimal_number_of_taps +}; +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +bool dce110_transform_v_construct( + struct dce_transform *xfm_dce, + struct dc_context *ctx) +{ + xfm_dce->base.ctx = ctx; + + xfm_dce->base.funcs = &dce110_xfmv_funcs; + + xfm_dce->lb_pixel_depth_supported = + LB_PIXEL_DEPTH_18BPP | + LB_PIXEL_DEPTH_24BPP | + LB_PIXEL_DEPTH_30BPP | + LB_PIXEL_DEPTH_36BPP; + + xfm_dce->prescaler_on = true; + 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/dce110/dce110_transform_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.h new file mode 100644 index 000000000..b70780210 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.h @@ -0,0 +1,58 @@ +/* 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_TRANSFORM_V_DCE110_H__ +#define __DAL_TRANSFORM_V_DCE110_H__ + +#include "../dce/dce_transform.h" + +#define LB_TOTAL_NUMBER_OF_ENTRIES 1712 +#define LB_BITS_PER_ENTRY 144 + +bool dce110_transform_v_construct( + struct dce_transform *xfm110, + struct dc_context *ctx); + +void dce110_opp_v_set_csc_default( + struct transform *xfm, + const struct default_adjustment *default_adjust); + +void dce110_opp_v_set_csc_adjustment( + struct transform *xfm, + const struct out_csc_color_matrix *tbl_entry); + + +void dce110_opp_program_regamma_pwl_v( + struct transform *xfm, + const struct pwl_params *params); + +void dce110_opp_power_on_regamma_lut_v( + struct transform *xfm, + bool power_on); + +void dce110_opp_set_regamma_mode_v( + struct transform *xfm, + enum opp_regamma mode); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce112/Makefile b/drivers/gpu/drm/amd/display/dc/dce112/Makefile new file mode 100644 index 000000000..9de650170 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/Makefile @@ -0,0 +1,33 @@ +# +# Copyright 2017 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. +# +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +CFLAGS_$(AMDDALPATH)/dc/dce112/dce112_resource.o = $(call cc-disable-warning, override-init) + +DCE112 = dce112_compressor.o dce112_hw_sequencer.o \ +dce112_resource.o + +AMD_DAL_DCE112 = $(addprefix $(AMDDALPATH)/dc/dce112/,$(DCE112)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE112) diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c new file mode 100644 index 000000000..faae12cf7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c @@ -0,0 +1,847 @@ +/* + * 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 "dce/dce_11_2_d.h" +#include "dce/dce_11_2_sh_mask.h" +#include "gmc/gmc_8_1_sh_mask.h" +#include "gmc/gmc_8_1_d.h" + +#include "include/logger_interface.h" + +#include "dce112_compressor.h" +#define DC_LOGGER \ + cp110->base.ctx->logger +#define DCP_REG(reg)\ + (reg + cp110->offsets.dcp_offset) +#define DMIF_REG(reg)\ + (reg + cp110->offsets.dmif_offset) + +static const struct dce112_compressor_reg_offsets reg_offsets[] = { +{ + .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG0_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG1_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG2_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +} +}; + +static const uint32_t dce11_one_lpt_channel_max_resolution = 2560 * 1600; + +enum fbc_idle_force { + /* Bit 0 - Display registers updated */ + FBC_IDLE_FORCE_DISPLAY_REGISTER_UPDATE = 0x00000001, + + /* Bit 2 - FBC_GRPH_COMP_EN register updated */ + FBC_IDLE_FORCE_GRPH_COMP_EN = 0x00000002, + /* Bit 3 - FBC_SRC_SEL register updated */ + FBC_IDLE_FORCE_SRC_SEL_CHANGE = 0x00000004, + /* Bit 4 - FBC_MIN_COMPRESSION register updated */ + FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE = 0x00000008, + /* Bit 5 - FBC_ALPHA_COMP_EN register updated */ + FBC_IDLE_FORCE_ALPHA_COMP_EN = 0x00000010, + /* Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated */ + FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN = 0x00000020, + /* Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated */ + FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF = 0x00000040, + + /* Bit 24 - Memory write to region 0 defined by MC registers. */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION0 = 0x01000000, + /* Bit 25 - Memory write to region 1 defined by MC registers */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION1 = 0x02000000, + /* Bit 26 - Memory write to region 2 defined by MC registers */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION2 = 0x04000000, + /* Bit 27 - Memory write to region 3 defined by MC registers. */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION3 = 0x08000000, + + /* Bit 28 - Memory write from any client other than MCIF */ + FBC_IDLE_FORCE_MEMORY_WRITE_OTHER_THAN_MCIF = 0x10000000, + /* Bit 29 - CG statics screen signal is inactive */ + FBC_IDLE_FORCE_CG_STATIC_SCREEN_IS_INACTIVE = 0x20000000, +}; + +static uint32_t lpt_size_alignment(struct dce112_compressor *cp110) +{ + /*LPT_ALIGNMENT (in bytes) = ROW_SIZE * #BANKS * # DRAM CHANNELS. */ + return cp110->base.raw_size * cp110->base.banks_num * + cp110->base.dram_channels_num; +} + +static uint32_t lpt_memory_control_config(struct dce112_compressor *cp110, + uint32_t lpt_control) +{ + /*LPT MC Config */ + if (cp110->base.options.bits.LPT_MC_CONFIG == 1) { + /* POSSIBLE VALUES for LPT NUM_PIPES (DRAM CHANNELS): + * 00 - 1 CHANNEL + * 01 - 2 CHANNELS + * 02 - 4 OR 6 CHANNELS + * (Only for discrete GPU, N/A for CZ) + * 03 - 8 OR 12 CHANNELS + * (Only for discrete GPU, N/A for CZ) */ + switch (cp110->base.dram_channels_num) { + case 2: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_PIPES); + break; + case 1: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_PIPES); + break; + default: + DC_LOG_WARNING( + "%s: Invalid LPT NUM_PIPES!!!", + __func__); + break; + } + + /* The mapping for LPT NUM_BANKS is in + * GRPH_CONTROL.GRPH_NUM_BANKS register field + * Specifies the number of memory banks for tiling + * purposes. Only applies to 2D and 3D tiling modes. + * POSSIBLE VALUES: + * 00 - DCP_GRPH_NUM_BANKS_2BANK: ADDR_SURF_2_BANK + * 01 - DCP_GRPH_NUM_BANKS_4BANK: ADDR_SURF_4_BANK + * 02 - DCP_GRPH_NUM_BANKS_8BANK: ADDR_SURF_8_BANK + * 03 - DCP_GRPH_NUM_BANKS_16BANK: ADDR_SURF_16_BANK */ + switch (cp110->base.banks_num) { + case 16: + set_reg_field_value( + lpt_control, + 3, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 8: + set_reg_field_value( + lpt_control, + 2, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 4: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 2: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + default: + DC_LOG_WARNING( + "%s: Invalid LPT NUM_BANKS!!!", + __func__); + break; + } + + /* The mapping is in DMIF_ADDR_CALC. + * ADDR_CONFIG_PIPE_INTERLEAVE_SIZE register field for + * Carrizo specifies the memory interleave per pipe. + * It effectively specifies the location of pipe bits in + * the memory address. + * POSSIBLE VALUES: + * 00 - ADDR_CONFIG_PIPE_INTERLEAVE_256B: 256 byte + * interleave + * 01 - ADDR_CONFIG_PIPE_INTERLEAVE_512B: 512 byte + * interleave + */ + switch (cp110->base.channel_interleave_size) { + case 256: /*256B */ + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); + break; + case 512: /*512B */ + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); + break; + default: + DC_LOG_WARNING( + "%s: Invalid LPT INTERLEAVE_SIZE!!!", + __func__); + break; + } + + /* The mapping for LOW_POWER_TILING_ROW_SIZE is in + * DMIF_ADDR_CALC.ADDR_CONFIG_ROW_SIZE register field + * for Carrizo. Specifies the size of dram row in bytes. + * This should match up with NOOFCOLS field in + * MC_ARB_RAMCFG (ROW_SIZE = 4 * 2 ^^ columns). + * This register DMIF_ADDR_CALC is not used by the + * hardware as it is only used for addrlib assertions. + * POSSIBLE VALUES: + * 00 - ADDR_CONFIG_1KB_ROW: Treat 1KB as DRAM row + * boundary + * 01 - ADDR_CONFIG_2KB_ROW: Treat 2KB as DRAM row + * boundary + * 02 - ADDR_CONFIG_4KB_ROW: Treat 4KB as DRAM row + * boundary */ + switch (cp110->base.raw_size) { + case 4096: /*4 KB */ + set_reg_field_value( + lpt_control, + 2, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + case 2048: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + case 1024: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + default: + DC_LOG_WARNING( + "%s: Invalid LPT ROW_SIZE!!!", + __func__); + break; + } + } else { + DC_LOG_WARNING( + "%s: LPT MC Configuration is not provided", + __func__); + } + + return lpt_control; +} + +static bool is_source_bigger_than_epanel_size( + struct dce112_compressor *cp110, + uint32_t source_view_width, + uint32_t source_view_height) +{ + if (cp110->base.embedded_panel_h_size != 0 && + cp110->base.embedded_panel_v_size != 0 && + ((source_view_width * source_view_height) > + (cp110->base.embedded_panel_h_size * + cp110->base.embedded_panel_v_size))) + return true; + + return false; +} + +static uint32_t align_to_chunks_number_per_line( + struct dce112_compressor *cp110, + uint32_t pixels) +{ + return 256 * ((pixels + 255) / 256); +} + +static void wait_for_fbc_state_changed( + struct dce112_compressor *cp110, + bool enabled) +{ + uint8_t counter = 0; + uint32_t addr = mmFBC_STATUS; + uint32_t value; + + while (counter < 10) { + value = dm_read_reg(cp110->base.ctx, addr); + if (get_reg_field_value( + value, + FBC_STATUS, + FBC_ENABLE_STATUS) == enabled) + break; + udelay(10); + counter++; + } + + if (counter == 10) { + DC_LOG_WARNING( + "%s: wait counter exceeded, changes to HW not applied", + __func__); + } +} + +void dce112_compressor_power_up_fbc(struct compressor *compressor) +{ + uint32_t value; + uint32_t addr; + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value(value, 1, FBC_CNTL, FBC_EN); + set_reg_field_value(value, 2, FBC_CNTL, FBC_COHERENCY_MODE); + if (compressor->options.bits.CLK_GATING_DISABLED == 1) { + /* HW needs to do power measurement comparison. */ + set_reg_field_value( + value, + 0, + FBC_CNTL, + FBC_COMP_CLK_GATE_EN); + } + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_MODE; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_RLE_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_DPCM4_RGB_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_IND_EN); + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_CNTL, FBC_DEPTH_RGB08_EN); + dm_write_reg(compressor->ctx, addr, value); + /*FBC_MIN_COMPRESSION 0 ==> 2:1 */ + /* 1 ==> 4:1 */ + /* 2 ==> 8:1 */ + /* 0xF ==> 1:1 */ + set_reg_field_value(value, 0xF, FBC_COMP_CNTL, FBC_MIN_COMPRESSION); + dm_write_reg(compressor->ctx, addr, value); + compressor->min_compress_ratio = FBC_COMPRESS_RATIO_1TO1; + + value = 0; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT0, value); + + value = 0xFFFFFF; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT1, value); +} + +void dce112_compressor_enable_fbc( + struct compressor *compressor, + uint32_t paths_num, + struct compr_addr_and_pitch_params *params) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + (compressor->options.bits.DUMMY_BACKEND == 0) && + (!dce112_compressor_is_fbc_enabled_in_hw(compressor, NULL)) && + (!is_source_bigger_than_epanel_size( + cp110, + params->source_view_width, + params->source_view_height))) { + + uint32_t addr; + uint32_t value; + + /* Before enabling FBC first need to enable LPT if applicable + * LPT state should always be changed (enable/disable) while FBC + * is disabled */ + if (compressor->options.bits.LPT_SUPPORT && (paths_num < 2) && + (params->source_view_width * + params->source_view_height <= + dce11_one_lpt_channel_max_resolution)) { + dce112_compressor_enable_lpt(compressor); + } + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value( + value, + params->inst, + FBC_CNTL, FBC_SRC_SEL); + dm_write_reg(compressor->ctx, addr, value); + + /* Keep track of enum controller_id FBC is attached to */ + compressor->is_enabled = true; + compressor->attached_inst = params->inst; + cp110->offsets = reg_offsets[params->inst]; + + /*Toggle it as there is bug in HW */ + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + + wait_for_fbc_state_changed(cp110, true); + } +} + +void dce112_compressor_disable_fbc(struct compressor *compressor) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + dce112_compressor_is_fbc_enabled_in_hw(compressor, NULL)) { + uint32_t reg_data; + /* Turn off compression */ + reg_data = dm_read_reg(compressor->ctx, mmFBC_CNTL); + set_reg_field_value(reg_data, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, mmFBC_CNTL, reg_data); + + /* Reset enum controller_id to undefined */ + compressor->attached_inst = 0; + compressor->is_enabled = false; + + /* Whenever disabling FBC make sure LPT is disabled if LPT + * supported */ + if (compressor->options.bits.LPT_SUPPORT) + dce112_compressor_disable_lpt(compressor); + + wait_for_fbc_state_changed(cp110, false); + } +} + +bool dce112_compressor_is_fbc_enabled_in_hw( + struct compressor *compressor, + uint32_t *inst) +{ + /* Check the hardware register */ + uint32_t value; + + value = dm_read_reg(compressor->ctx, mmFBC_STATUS); + if (get_reg_field_value(value, FBC_STATUS, FBC_ENABLE_STATUS)) { + if (inst != NULL) + *inst = compressor->attached_inst; + return true; + } + + value = dm_read_reg(compressor->ctx, mmFBC_MISC); + if (get_reg_field_value(value, FBC_MISC, FBC_STOP_ON_HFLIP_EVENT)) { + value = dm_read_reg(compressor->ctx, mmFBC_CNTL); + + if (get_reg_field_value(value, FBC_CNTL, FBC_GRPH_COMP_EN)) { + if (inst != NULL) + *inst = + compressor->attached_inst; + return true; + } + } + return false; +} + +bool dce112_compressor_is_lpt_enabled_in_hw(struct compressor *compressor) +{ + /* Check the hardware register */ + uint32_t value = dm_read_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL); + + return get_reg_field_value( + value, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); +} + +void dce112_compressor_program_compressed_surface_address_and_pitch( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + uint32_t value = 0; + uint32_t fbc_pitch = 0; + uint32_t compressed_surf_address_low_part = + compressor->compr_surface_address.addr.low_part; + + /* Clear content first. */ + dm_write_reg( + compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + 0); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), 0); + + if (compressor->options.bits.LPT_SUPPORT) { + uint32_t lpt_alignment = lpt_size_alignment(cp110); + + if (lpt_alignment != 0) { + compressed_surf_address_low_part = + ((compressed_surf_address_low_part + + (lpt_alignment - 1)) / lpt_alignment) + * lpt_alignment; + } + } + + /* Write address, HIGH has to be first. */ + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + compressor->compr_surface_address.addr.high_part); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), + compressed_surf_address_low_part); + + fbc_pitch = align_to_chunks_number_per_line( + cp110, + params->source_view_width); + + if (compressor->min_compress_ratio == FBC_COMPRESS_RATIO_1TO1) + fbc_pitch = fbc_pitch / 8; + else + DC_LOG_WARNING( + "%s: Unexpected DCE11 compression ratio", + __func__); + + /* Clear content first. */ + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), 0); + + /* Write FBC Pitch. */ + set_reg_field_value( + value, + fbc_pitch, + GRPH_COMPRESS_PITCH, + GRPH_COMPRESS_PITCH); + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), value); + +} + +void dce112_compressor_disable_lpt(struct compressor *compressor) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + uint32_t value; + uint32_t addr; + uint32_t inx; + + /* Disable all pipes LPT Stutter */ + for (inx = 0; inx < 3; inx++) { + value = + dm_read_reg( + compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH)); + set_reg_field_value( + value, + 0, + DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg( + compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), + value); + } + /* Disable Underlay pipe LPT Stutter */ + addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 0, + DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg(compressor->ctx, addr, value); + + /* Disable LPT */ + addr = mmLOW_POWER_TILING_CONTROL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); + dm_write_reg(compressor->ctx, addr, value); + + /* Clear selection of Channel(s) containing Compressed Surface */ + addr = mmGMCON_LPT_TARGET; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 0xFFFFFFFF, + GMCON_LPT_TARGET, + STCTRL_LPT_TARGET); + dm_write_reg(compressor->ctx, mmGMCON_LPT_TARGET, value); +} + +void dce112_compressor_enable_lpt(struct compressor *compressor) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + uint32_t value; + uint32_t addr; + uint32_t value_control; + uint32_t channels; + + /* Enable LPT Stutter from Display pipe */ + value = dm_read_reg(compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH)); + set_reg_field_value( + value, + 1, + DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg(compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), value); + + /* Enable Underlay pipe LPT Stutter */ + addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg(compressor->ctx, addr, value); + + /* Selection of Channel(s) containing Compressed Surface: 0xfffffff + * will disable LPT. + * STCTRL_LPT_TARGETn corresponds to channel n. */ + addr = mmLOW_POWER_TILING_CONTROL; + value_control = dm_read_reg(compressor->ctx, addr); + channels = get_reg_field_value(value_control, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_MODE); + + addr = mmGMCON_LPT_TARGET; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + channels + 1, /* not mentioned in programming guide, + but follow DCE8.1 */ + GMCON_LPT_TARGET, + STCTRL_LPT_TARGET); + dm_write_reg(compressor->ctx, addr, value); + + /* Enable LPT */ + addr = mmLOW_POWER_TILING_CONTROL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); + dm_write_reg(compressor->ctx, addr, value); +} + +void dce112_compressor_program_lpt_control( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce112_compressor *cp110 = TO_DCE112_COMPRESSOR(compressor); + uint32_t rows_per_channel; + uint32_t lpt_alignment; + uint32_t source_view_width; + uint32_t source_view_height; + uint32_t lpt_control = 0; + + if (!compressor->options.bits.LPT_SUPPORT) + return; + + lpt_control = dm_read_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL); + + /* POSSIBLE VALUES for Low Power Tiling Mode: + * 00 - Use channel 0 + * 01 - Use Channel 0 and 1 + * 02 - Use Channel 0,1,2,3 + * 03 - reserved */ + switch (compressor->lpt_channels_num) { + /* case 2: + * Use Channel 0 & 1 / Not used for DCE 11 */ + case 1: + /*Use Channel 0 for LPT for DCE 11 */ + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_MODE); + break; + default: + DC_LOG_WARNING( + "%s: Invalid selected DRAM channels for LPT!!!", + __func__); + break; + } + + lpt_control = lpt_memory_control_config(cp110, lpt_control); + + /* Program LOW_POWER_TILING_ROWS_PER_CHAN field which depends on + * FBC compressed surface pitch. + * LOW_POWER_TILING_ROWS_PER_CHAN = Roundup ((Surface Height * + * Surface Pitch) / (Row Size * Number of Channels * + * Number of Banks)). */ + rows_per_channel = 0; + lpt_alignment = lpt_size_alignment(cp110); + source_view_width = + align_to_chunks_number_per_line( + cp110, + params->source_view_width); + source_view_height = (params->source_view_height + 1) & (~0x1); + + if (lpt_alignment != 0) { + rows_per_channel = source_view_width * source_view_height * 4; + rows_per_channel = + (rows_per_channel % lpt_alignment) ? + (rows_per_channel / lpt_alignment + 1) : + rows_per_channel / lpt_alignment; + } + + set_reg_field_value( + lpt_control, + rows_per_channel, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROWS_PER_CHAN); + + dm_write_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL, lpt_control); +} + +/* + * DCE 11 Frame Buffer Compression Implementation + */ + +void dce112_compressor_set_fbc_invalidation_triggers( + struct compressor *compressor, + uint32_t fbc_trigger) +{ + /* Disable region hit event, FBC_MEMORY_REGION_MASK = 0 (bits 16-19) + * for DCE 11 regions cannot be used - does not work with S/G + */ + uint32_t addr = mmFBC_CLIENT_REGION_MASK; + uint32_t value = dm_read_reg(compressor->ctx, addr); + + set_reg_field_value( + value, + 0, + FBC_CLIENT_REGION_MASK, + FBC_MEMORY_REGION_MASK); + dm_write_reg(compressor->ctx, addr, value); + + /* Setup events when to clear all CSM entries (effectively marking + * current compressed data invalid) + * For DCE 11 CSM metadata 11111 means - "Not Compressed" + * Used as the initial value of the metadata sent to the compressor + * after invalidation, to indicate that the compressor should attempt + * to compress all chunks on the current pass. Also used when the chunk + * is not successfully written to memory. + * When this CSM value is detected, FBC reads from the uncompressed + * buffer. Set events according to passed in value, these events are + * valid for DCE11: + * - bit 0 - display register updated + * - bit 28 - memory write from any client except from MCIF + * - bit 29 - CG static screen signal is inactive + * In addition, DCE11.1 also needs to set new DCE11.1 specific events + * that are used to trigger invalidation on certain register changes, + * for example enabling of Alpha Compression may trigger invalidation of + * FBC once bit is set. These events are as follows: + * - Bit 2 - FBC_GRPH_COMP_EN register updated + * - Bit 3 - FBC_SRC_SEL register updated + * - Bit 4 - FBC_MIN_COMPRESSION register updated + * - Bit 5 - FBC_ALPHA_COMP_EN register updated + * - Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated + * - Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated + */ + addr = mmFBC_IDLE_FORCE_CLEAR_MASK; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + fbc_trigger | + FBC_IDLE_FORCE_GRPH_COMP_EN | + FBC_IDLE_FORCE_SRC_SEL_CHANGE | + FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE | + FBC_IDLE_FORCE_ALPHA_COMP_EN | + FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN | + FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF, + FBC_IDLE_FORCE_CLEAR_MASK, + FBC_IDLE_FORCE_CLEAR_MASK); + dm_write_reg(compressor->ctx, addr, value); +} + +void dce112_compressor_construct(struct dce112_compressor *compressor, + struct dc_context *ctx) +{ + struct dc_bios *bp = ctx->dc_bios; + struct embedded_panel_info panel_info; + + compressor->base.options.raw = 0; + compressor->base.options.bits.FBC_SUPPORT = true; + compressor->base.options.bits.LPT_SUPPORT = true; + /* For DCE 11 always use one DRAM channel for LPT */ + compressor->base.lpt_channels_num = 1; + compressor->base.options.bits.DUMMY_BACKEND = false; + + /* Check if this system has more than 1 DRAM channel; if only 1 then LPT + * should not be supported */ + if (compressor->base.memory_bus_width == 64) + compressor->base.options.bits.LPT_SUPPORT = false; + + compressor->base.options.bits.CLK_GATING_DISABLED = false; + + compressor->base.ctx = ctx; + compressor->base.embedded_panel_h_size = 0; + compressor->base.embedded_panel_v_size = 0; + compressor->base.memory_bus_width = ctx->asic_id.vram_width; + compressor->base.allocated_size = 0; + compressor->base.preferred_requested_size = 0; + compressor->base.min_compress_ratio = FBC_COMPRESS_RATIO_INVALID; + compressor->base.banks_num = 0; + compressor->base.raw_size = 0; + compressor->base.channel_interleave_size = 0; + compressor->base.dram_channels_num = 0; + compressor->base.lpt_channels_num = 0; + compressor->base.attached_inst = 0; + compressor->base.is_enabled = false; + + if (BP_RESULT_OK == + bp->funcs->get_embedded_panel_info(bp, &panel_info)) { + compressor->base.embedded_panel_h_size = + panel_info.lcd_timing.horizontal_addressable; + compressor->base.embedded_panel_v_size = + panel_info.lcd_timing.vertical_addressable; + } +} + +struct compressor *dce112_compressor_create(struct dc_context *ctx) +{ + struct dce112_compressor *cp110 = + kzalloc(sizeof(struct dce112_compressor), GFP_KERNEL); + + if (!cp110) + return NULL; + + dce112_compressor_construct(cp110, ctx); + return &cp110->base; +} + +void dce112_compressor_destroy(struct compressor **compressor) +{ + kfree(TO_DCE112_COMPRESSOR(*compressor)); + *compressor = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.h b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.h new file mode 100644 index 000000000..f1227133f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.h @@ -0,0 +1,78 @@ +/* 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_COMPRESSOR_DCE112_H__ +#define __DC_COMPRESSOR_DCE112_H__ + +#include "../inc/compressor.h" + +#define TO_DCE112_COMPRESSOR(compressor)\ + container_of(compressor, struct dce112_compressor, base) + +struct dce112_compressor_reg_offsets { + uint32_t dcp_offset; + uint32_t dmif_offset; +}; + +struct dce112_compressor { + struct compressor base; + struct dce112_compressor_reg_offsets offsets; +}; + +struct compressor *dce112_compressor_create(struct dc_context *ctx); + +void dce112_compressor_construct(struct dce112_compressor *cp110, + struct dc_context *ctx); + +void dce112_compressor_destroy(struct compressor **cp); + +/* FBC RELATED */ +void dce112_compressor_power_up_fbc(struct compressor *cp); + +void dce112_compressor_enable_fbc(struct compressor *cp, uint32_t paths_num, + struct compr_addr_and_pitch_params *params); + +void dce112_compressor_disable_fbc(struct compressor *cp); + +void dce112_compressor_set_fbc_invalidation_triggers(struct compressor *cp, + uint32_t fbc_trigger); + +void dce112_compressor_program_compressed_surface_address_and_pitch( + struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce112_compressor_is_fbc_enabled_in_hw(struct compressor *cp, + uint32_t *fbc_mapped_crtc_id); + +/* LPT RELATED */ +void dce112_compressor_enable_lpt(struct compressor *cp); + +void dce112_compressor_disable_lpt(struct compressor *cp); + +void dce112_compressor_program_lpt_control(struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce112_compressor_is_lpt_enabled_in_hw(struct compressor *cp); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.c new file mode 100644 index 000000000..19873ee1f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.c @@ -0,0 +1,163 @@ +/* + * Copyright 2015 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.h" +#include "core_types.h" +#include "dce112_hw_sequencer.h" + +#include "dce110/dce110_hw_sequencer.h" + +/* include DCE11.2 register header files */ +#include "dce/dce_11_2_d.h" +#include "dce/dce_11_2_sh_mask.h" + +struct dce112_hw_seq_reg_offsets { + uint32_t crtc; +}; + + +static const struct dce112_hw_seq_reg_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC3_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC4_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC5_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +} +}; +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + +/******************************************************************************* + * Private definitions + ******************************************************************************/ + +static void dce112_init_pte(struct dc_context *ctx) +{ + uint32_t addr; + uint32_t value = 0; + uint32_t chunk_int = 0; + uint32_t chunk_mul = 0; + + addr = mmDVMM_PTE_REQ; + value = dm_read_reg(ctx, addr); + + chunk_int = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + chunk_mul = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + if (chunk_int != 0x4 || chunk_mul != 0x4) { + + set_reg_field_value( + value, + 255, + DVMM_PTE_REQ, + MAX_PTEREQ_TO_ISSUE); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + dm_write_reg(ctx, addr, value); + } +} + +static bool dce112_enable_display_power_gating( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + struct dc_context *ctx = dc->ctx; + + if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) + return true; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (power_gating != PIPE_GATING_CONTROL_INIT || controller_id == 0){ + + bp_result = dcb->funcs->enable_disp_power_gating( + dcb, controller_id + 1, cntl); + + /* Revert MASTER_UPDATE_MODE to 0 because bios sets it 2 + * by default when command table is called + */ + dm_write_reg(ctx, + HW_REG_CRTC(mmCRTC_MASTER_UPDATE_MODE, controller_id), + 0); + } + + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + dce112_init_pte(ctx); + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +} + +void dce112_hw_sequencer_construct(struct dc *dc) +{ + /* All registers used by dce11.2 match those in dce11 in offset and + * structure + */ + dce110_hw_sequencer_construct(dc); + dc->hwseq->funcs.enable_display_power_gating = dce112_enable_display_power_gating; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.h new file mode 100644 index 000000000..943f1b2c5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_hw_sequencer.h @@ -0,0 +1,37 @@ +/* +* 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_HWSS_DCE112_H__ +#define __DC_HWSS_DCE112_H__ + +#include "core_types.h" +#include "hw_sequencer_private.h" + +struct dc; + +void dce112_hw_sequencer_construct(struct dc *dc); + +#endif /* __DC_HWSS_DCE112_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c new file mode 100644 index 000000000..e179e8066 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c @@ -0,0 +1,1424 @@ +/* +* 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 "link_encoder.h" +#include "stream_encoder.h" + +#include "resource.h" +#include "include/irq_service_interface.h" +#include "dce110/dce110_resource.h" +#include "dce110/dce110_timing_generator.h" + +#include "irq/dce110/irq_service_dce110.h" +#include "dce/dce_mem_input.h" +#include "dce/dce_transform.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_audio.h" +#include "dce/dce_opp.h" +#include "dce/dce_ipp.h" +#include "dce/dce_clock_source.h" + +#include "dce/dce_hwseq.h" +#include "dce112/dce112_hw_sequencer.h" +#include "dce/dce_abm.h" +#include "dce/dce_dmcu.h" +#include "dce/dce_aux.h" +#include "dce/dce_i2c.h" +#include "dce/dce_panel_cntl.h" + +#include "reg_helper.h" + +#include "dce/dce_11_2_d.h" +#include "dce/dce_11_2_sh_mask.h" + +#include "dce100/dce100_resource.h" +#include "dce112_resource.h" + +#define DC_LOGGER \ + dc->ctx->logger + +#ifndef mmDP_DPHY_INTERNAL_CTRL + #define mmDP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x4ba7 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x4ca7 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x4da7 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x4ea7 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4fa7 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x54a7 + #define mmDP7_DP_DPHY_INTERNAL_CTRL 0x56a7 + #define mmDP8_DP_DPHY_INTERNAL_CTRL 0x57a7 +#endif + +#ifndef mmBIOS_SCRATCH_2 + #define mmBIOS_SCRATCH_2 0x05CB + #define mmBIOS_SCRATCH_3 0x05CC + #define mmBIOS_SCRATCH_6 0x05CF +#endif + +#ifndef mmDP_DPHY_BS_SR_SWAP_CNTL + #define mmDP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP0_DP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP1_DP_DPHY_BS_SR_SWAP_CNTL 0x4BDC + #define mmDP2_DP_DPHY_BS_SR_SWAP_CNTL 0x4CDC + #define mmDP3_DP_DPHY_BS_SR_SWAP_CNTL 0x4DDC + #define mmDP4_DP_DPHY_BS_SR_SWAP_CNTL 0x4EDC + #define mmDP5_DP_DPHY_BS_SR_SWAP_CNTL 0x4FDC + #define mmDP6_DP_DPHY_BS_SR_SWAP_CNTL 0x54DC +#endif + +#ifndef mmDP_DPHY_FAST_TRAINING + #define mmDP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP0_DP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP1_DP_DPHY_FAST_TRAINING 0x4BBC + #define mmDP2_DP_DPHY_FAST_TRAINING 0x4CBC + #define mmDP3_DP_DPHY_FAST_TRAINING 0x4DBC + #define mmDP4_DP_DPHY_FAST_TRAINING 0x4EBC + #define mmDP5_DP_DPHY_FAST_TRAINING 0x4FBC + #define mmDP6_DP_DPHY_FAST_TRAINING 0x54BC +#endif + +enum dce112_clk_src_array_id { + DCE112_CLK_SRC_PLL0, + DCE112_CLK_SRC_PLL1, + DCE112_CLK_SRC_PLL2, + DCE112_CLK_SRC_PLL3, + DCE112_CLK_SRC_PLL4, + DCE112_CLK_SRC_PLL5, + + DCE112_CLK_SRC_TOTAL +}; + +static const struct dce110_timing_generator_offsets dce112_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + } +}; + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + +static const struct dce_dmcu_registers dmcu_regs = { + DMCU_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_dmcu_shift dmcu_shift = { + DMCU_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_dmcu_mask dmcu_mask = { + DMCU_MASK_SH_LIST_DCE110(_MASK) +}; + +static const struct dce_abm_registers abm_regs = { + ABM_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCE110(_MASK) +}; + +static const struct dce110_aux_registers_shift aux_shift = { + DCE_AUX_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce110_aux_registers_mask aux_mask = { + DCE_AUX_MASK_SH_LIST(_MASK) +}; + +#define ipp_regs(id)\ +[id] = {\ + IPP_DCE110_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2), + ipp_regs(3), + ipp_regs(4), + ipp_regs(5) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_DCE100_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE110(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2), + transform_regs(3), + transform_regs(4), + transform_regs(5) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +static const struct dce_panel_cntl_registers panel_cntl_regs[] = { + { DCE_PANEL_CNTL_REG_LIST() } +}; + +static const struct dce_panel_cntl_shift panel_cntl_shift = { + DCE_PANEL_CNTL_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_panel_cntl_mask panel_cntl_mask = { + DCE_PANEL_CNTL_MASK_SH_LIST(_MASK) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +#define link_regs(id)\ +[id] = {\ + LE_DCE110_REG_LIST(id)\ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST(id),\ + .TMDS_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2), + stream_enc_regs(3), + stream_enc_regs(4), + stream_enc_regs(5) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE112(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE112(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_112_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_112(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_112(_MASK) +}; + +#define aux_engine_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = 0 \ +} + +static const struct dce110_aux_registers aux_engine_regs[] = { + aux_engine_regs(0), + aux_engine_regs(1), + aux_engine_regs(2), + aux_engine_regs(3), + aux_engine_regs(4), + aux_engine_regs(5) +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5) +}; + +static const struct dce_audio_shift audio_shift = { + AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_audio_mask audio_mask = { + AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +#define clk_src_regs(index, id)\ +[index] = {\ + CS_COMMON_REG_LIST_DCE_112(id),\ +} + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0, A), + clk_src_regs(1, B), + clk_src_regs(2, C), + clk_src_regs(3, D), + clk_src_regs(4, E), + clk_src_regs(5, F) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_112(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_112(_MASK) +}; + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3, + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 +}; + +static const struct resource_caps polaris_10_resource_cap = { + .num_timing_generator = 6, + .num_audio = 6, + .num_stream_encoder = 6, + .num_pll = 8, /* why 8? 6 combo PHY PLL + 2 regular PLLs? */ + .num_ddc = 6, +}; + +static const struct resource_caps polaris_11_resource_cap = { + .num_timing_generator = 5, + .num_audio = 5, + .num_stream_encoder = 5, + .num_pll = 8, /* why 8? 6 combo PHY PLL + 2 regular PLLs? */ + .num_ddc = 5, +}; + +static const struct dc_plane_cap plane_cap = { + .type = DC_PLANE_TYPE_DCE_RGB, + + .pixel_format_support = { + .argb8888 = true, + .nv12 = false, + .fp16 = true + }, + + .max_upscale_factor = { + .argb8888 = 16000, + .nv12 = 1, + .fp16 = 1 + }, + + .max_downscale_factor = { + .argb8888 = 250, + .nv12 = 1, + .fp16 = 1 + }, + 64, + 64 +}; + +#define CTX ctx +#define REG(reg) mm ## reg + +#ifndef mmCC_DC_HDMI_STRAPS +#define mmCC_DC_HDMI_STRAPS 0x4819 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE_MASK 0x40 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE__SHIFT 0x6 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x700 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 +#endif + +static int map_transmitter_id_to_phy_instance( + enum transmitter transmitter) +{ + switch (transmitter) { + case TRANSMITTER_UNIPHY_A: + return 0; + case TRANSMITTER_UNIPHY_B: + return 1; + case TRANSMITTER_UNIPHY_C: + return 2; + case TRANSMITTER_UNIPHY_D: + return 3; + case TRANSMITTER_UNIPHY_E: + return 4; + case TRANSMITTER_UNIPHY_F: + return 5; + case TRANSMITTER_UNIPHY_G: + return 6; + default: + ASSERT(0); + return 0; + } +} + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + REG_GET_2(CC_DC_HDMI_STRAPS, + HDMI_DISABLE, &straps->hdmi_disable, + AUDIO_STREAM_NUMBER, &straps->audio_stream_number); + + REG_GET(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO, &straps->dc_pinstraps_audio); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + + +static struct timing_generator *dce112_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce110_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static struct stream_encoder *dce112_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_reg = { + HWSEQ_DCE112_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE112_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE112_MASK_SH_LIST(_MASK) +}; + +static struct dce_hwseq *dce112_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce112_stream_encoder_create, + .create_hwseq = dce112_hwseq_create, +}; + +#define mi_inst_regs(id) { MI_DCE11_2_REG_LIST(id) } +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), + mi_inst_regs(3), + mi_inst_regs(4), + mi_inst_regs(5), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE11_2_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE11_2_MASK_SH_LIST(_MASK) +}; + +static struct mem_input *dce112_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce112_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + return &dce_mi->base; +} + +static void dce112_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce112_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + transform->lb_memory_size = 0x1404; /*5124*/ + return &transform->base; +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 600000, + .hdmi_ycbcr420_supported = true, + .dp_ycbcr420_supported = false, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_HBR3_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true, + .flags.bits.IS_TPS4_CAPABLE = true +}; + +static struct link_encoder *dce112_link_encoder_create( + struct dc_context *ctx, + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + int link_regs_id; + + if (!enc110) + return NULL; + + link_regs_id = + map_transmitter_id_to_phy_instance(enc_init_data->transmitter); + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[link_regs_id], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + return &enc110->base; +} + +static struct panel_cntl *dce112_panel_cntl_create(const struct panel_cntl_init_data *init_data) +{ + struct dce_panel_cntl *panel_cntl = + kzalloc(sizeof(struct dce_panel_cntl), GFP_KERNEL); + + if (!panel_cntl) + return NULL; + + dce_panel_cntl_construct(panel_cntl, + init_data, + &panel_cntl_regs[init_data->inst], + &panel_cntl_shift, + &panel_cntl_mask); + + return &panel_cntl->base; +} + +static struct input_pixel_processor *dce112_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +static struct output_pixel_processor *dce112_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} + +static struct dce_aux *dce112_aux_engine_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct aux_engine_dce110 *aux_engine = + kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); + + if (!aux_engine) + return NULL; + + dce110_aux_engine_construct(aux_engine, ctx, inst, + SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, + &aux_engine_regs[inst], + &aux_mask, + &aux_shift, + ctx->dc->caps.extended_aux_timeout_support); + + return &aux_engine->base; +} +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +static struct dce_i2c_hw *dce112_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + + dce112_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} +static struct clock_source *dce112_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce112_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + kfree(clk_src); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static void dce112_clock_source_destroy(struct clock_source **clk_src) +{ + kfree(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + +static void dce112_resource_destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce112_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + if (pool->base.engines[i] != NULL) + dce110_engine_destroy(&pool->base.engines[i]); + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) { + dce112_clock_source_destroy(&pool->base.clock_sources[i]); + } + } + + if (pool->base.dp_clock_source != NULL) + dce112_clock_source_destroy(&pool->base.dp_clock_source); + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i] != NULL) { + dce_aud_destroy(&pool->base.audios[i]); + } + } + + if (pool->base.abm != NULL) + dce_abm_destroy(&pool->base.abm); + + if (pool->base.dmcu != NULL) + dce_dmcu_destroy(&pool->base.dmcu); + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } +} + +static struct clock_source *find_matching_pll( + struct resource_context *res_ctx, + const struct resource_pool *pool, + const struct dc_stream_state *const stream) +{ + switch (stream->link->link_enc->transmitter) { + case TRANSMITTER_UNIPHY_A: + return pool->clock_sources[DCE112_CLK_SRC_PLL0]; + case TRANSMITTER_UNIPHY_B: + return pool->clock_sources[DCE112_CLK_SRC_PLL1]; + case TRANSMITTER_UNIPHY_C: + return pool->clock_sources[DCE112_CLK_SRC_PLL2]; + case TRANSMITTER_UNIPHY_D: + return pool->clock_sources[DCE112_CLK_SRC_PLL3]; + case TRANSMITTER_UNIPHY_E: + return pool->clock_sources[DCE112_CLK_SRC_PLL4]; + case TRANSMITTER_UNIPHY_F: + return pool->clock_sources[DCE112_CLK_SRC_PLL5]; + default: + return NULL; + } + + return NULL; +} + +static enum dc_status build_mapped_resource( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + struct pipe_ctx *pipe_ctx = resource_get_head_pipe_for_stream(&context->res_ctx, stream); + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; + + dce110_resource_build_pipe_hw_param(pipe_ctx); + + resource_build_info_frame(pipe_ctx); + + return DC_OK; +} + +bool dce112_validate_bandwidth( + struct dc *dc, + struct dc_state *context, + bool fast_validate) +{ + bool result = false; + + DC_LOG_BANDWIDTH_CALCS( + "%s: start", + __func__); + + if (bw_calcs( + dc->ctx, + dc->bw_dceip, + dc->bw_vbios, + context->res_ctx.pipe_ctx, + dc->res_pool->pipe_count, + &context->bw_ctx.bw.dce)) + result = true; + + if (!result) + DC_LOG_BANDWIDTH_VALIDATION( + "%s: Bandwidth validation failed!", + __func__); + + if (memcmp(&dc->current_state->bw_ctx.bw.dce, + &context->bw_ctx.bw.dce, sizeof(context->bw_ctx.bw.dce))) { + + DC_LOG_BANDWIDTH_CALCS( + "%s: finish,\n" + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n" + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n" + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d stutter_mode_enable: %d\n" + "cstate: %d pstate: %d nbpstate: %d sync: %d dispclk: %d\n" + "sclk: %d sclk_sleep: %d yclk: %d blackout_recovery_time_us: %d\n" + , + __func__, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[0].b_mark, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[0].a_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[0].b_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[0].a_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[0].b_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[0].a_mark, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[1].b_mark, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[1].a_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[1].b_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[1].a_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[1].b_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[1].a_mark, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[2].b_mark, + context->bw_ctx.bw.dce.nbp_state_change_wm_ns[2].a_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[2].b_mark, + context->bw_ctx.bw.dce.urgent_wm_ns[2].a_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[2].b_mark, + context->bw_ctx.bw.dce.stutter_exit_wm_ns[2].a_mark, + context->bw_ctx.bw.dce.stutter_mode_enable, + context->bw_ctx.bw.dce.cpuc_state_change_enable, + context->bw_ctx.bw.dce.cpup_state_change_enable, + context->bw_ctx.bw.dce.nbp_state_change_enable, + context->bw_ctx.bw.dce.all_displays_in_sync, + context->bw_ctx.bw.dce.dispclk_khz, + context->bw_ctx.bw.dce.sclk_khz, + context->bw_ctx.bw.dce.sclk_deep_sleep_khz, + context->bw_ctx.bw.dce.yclk_khz, + context->bw_ctx.bw.dce.blackout_recovery_time_us); + } + return result; +} + +enum dc_status resource_map_phy_clock_resources( + const struct dc *dc, + struct dc_state *context, + struct dc_stream_state *stream) +{ + + /* acquire new resources */ + struct pipe_ctx *pipe_ctx = resource_get_head_pipe_for_stream( + &context->res_ctx, stream); + + if (!pipe_ctx) + return DC_ERROR_UNEXPECTED; + + if (dc_is_dp_signal(pipe_ctx->stream->signal) + || dc_is_virtual_signal(pipe_ctx->stream->signal)) + pipe_ctx->clock_source = + dc->res_pool->dp_clock_source; + else + pipe_ctx->clock_source = find_matching_pll( + &context->res_ctx, dc->res_pool, + stream); + + if (pipe_ctx->clock_source == NULL) + return DC_NO_CLOCK_SOURCE_RESOURCE; + + resource_reference_clock_source( + &context->res_ctx, + dc->res_pool, + pipe_ctx->clock_source); + + return DC_OK; +} + +static bool dce112_validate_surface_sets( + struct dc_state *context) +{ + int i; + + for (i = 0; i < context->stream_count; i++) { + if (context->stream_status[i].plane_count == 0) + continue; + + if (context->stream_status[i].plane_count > 1) + return false; + + if (context->stream_status[i].plane_states[0]->format + >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + } + + return true; +} + +enum dc_status dce112_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream) +{ + enum dc_status result; + + result = resource_map_pool_resources(dc, new_ctx, dc_stream); + + if (result == DC_OK) + result = resource_map_phy_clock_resources(dc, new_ctx, dc_stream); + + + if (result == DC_OK) + result = build_mapped_resource(dc, new_ctx, dc_stream); + + return result; +} + +static enum dc_status dce112_validate_global( + struct dc *dc, + struct dc_state *context) +{ + if (!dce112_validate_surface_sets(context)) + return DC_FAIL_SURFACE_VALIDATE; + + return DC_OK; +} + +static void dce112_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + dce112_resource_destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + +static const struct resource_funcs dce112_res_pool_funcs = { + .destroy = dce112_destroy_resource_pool, + .link_enc_create = dce112_link_encoder_create, + .panel_cntl_create = dce112_panel_cntl_create, + .validate_bandwidth = dce112_validate_bandwidth, + .validate_plane = dce100_validate_plane, + .add_stream_to_ctx = dce112_add_stream_to_ctx, + .validate_global = dce112_validate_global, + .find_first_free_match_stream_enc_for_link = dce110_find_first_free_match_stream_enc_for_link +}; + +static void bw_calcs_data_update_from_pplib(struct dc *dc) +{ + struct dm_pp_clock_levels_with_latency eng_clks = {0}; + struct dm_pp_clock_levels_with_latency mem_clks = {0}; + struct dm_pp_wm_sets_with_clock_ranges clk_ranges = {0}; + struct dm_pp_clock_levels clks = {0}; + int memory_type_multiplier = MEMORY_TYPE_MULTIPLIER_CZ; + + if (dc->bw_vbios && dc->bw_vbios->memory_type == bw_def_hbm) + memory_type_multiplier = MEMORY_TYPE_HBM; + + /*do system clock TODO PPLIB: after PPLIB implement, + * then remove old way + */ + if (!dm_pp_get_clock_levels_by_type_with_latency( + dc->ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, + &eng_clks)) { + + /* This is only for temporary */ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, + &clks); + /* convert all the clock fro kHz to fix point mHz */ + dc->bw_vbios->high_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1], 1000); + dc->bw_vbios->mid1_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels/8], 1000); + dc->bw_vbios->mid2_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*2/8], 1000); + dc->bw_vbios->mid3_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*3/8], 1000); + dc->bw_vbios->mid4_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*4/8], 1000); + dc->bw_vbios->mid5_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*5/8], 1000); + dc->bw_vbios->mid6_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*6/8], 1000); + dc->bw_vbios->low_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[0], 1000); + + /*do memory clock*/ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, + &clks); + + dc->bw_vbios->low_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[0] * memory_type_multiplier, 1000); + dc->bw_vbios->mid_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels>>1] * memory_type_multiplier, + 1000); + dc->bw_vbios->high_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1] * memory_type_multiplier, + 1000); + + return; + } + + /* convert all the clock fro kHz to fix point mHz TODO: wloop data */ + dc->bw_vbios->high_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels-1].clocks_in_khz, 1000); + dc->bw_vbios->mid1_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels/8].clocks_in_khz, 1000); + dc->bw_vbios->mid2_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*2/8].clocks_in_khz, 1000); + dc->bw_vbios->mid3_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz, 1000); + dc->bw_vbios->mid4_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*4/8].clocks_in_khz, 1000); + dc->bw_vbios->mid5_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*5/8].clocks_in_khz, 1000); + dc->bw_vbios->mid6_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*6/8].clocks_in_khz, 1000); + dc->bw_vbios->low_sclk = bw_frc_to_fixed( + eng_clks.data[0].clocks_in_khz, 1000); + + /*do memory clock*/ + dm_pp_get_clock_levels_by_type_with_latency( + dc->ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, + &mem_clks); + + /* we don't need to call PPLIB for validation clock since they + * also give us the highest sclk and highest mclk (UMA clock). + * ALSO always convert UMA clock (from PPLIB) to YCLK (HW formula): + * YCLK = UMACLK*m_memoryTypeMultiplier + */ + dc->bw_vbios->low_yclk = bw_frc_to_fixed( + mem_clks.data[0].clocks_in_khz * memory_type_multiplier, 1000); + dc->bw_vbios->mid_yclk = bw_frc_to_fixed( + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz * memory_type_multiplier, + 1000); + dc->bw_vbios->high_yclk = bw_frc_to_fixed( + mem_clks.data[mem_clks.num_levels-1].clocks_in_khz * memory_type_multiplier, + 1000); + + /* Now notify PPLib/SMU about which Watermarks sets they should select + * depending on DPM state they are in. And update BW MGR GFX Engine and + * Memory clock member variables for Watermarks calculations for each + * Watermark Set + */ + clk_ranges.num_wm_sets = 4; + clk_ranges.wm_clk_ranges[0].wm_set_id = WM_SET_A; + clk_ranges.wm_clk_ranges[0].wm_min_eng_clk_in_khz = + eng_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[0].wm_max_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz - 1; + clk_ranges.wm_clk_ranges[0].wm_min_mem_clk_in_khz = + mem_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[0].wm_max_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz - 1; + + clk_ranges.wm_clk_ranges[1].wm_set_id = WM_SET_B; + clk_ranges.wm_clk_ranges[1].wm_min_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz; + /* 5 GHz instead of data[7].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[1].wm_max_eng_clk_in_khz = 5000000; + clk_ranges.wm_clk_ranges[1].wm_min_mem_clk_in_khz = + mem_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[1].wm_max_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz - 1; + + clk_ranges.wm_clk_ranges[2].wm_set_id = WM_SET_C; + clk_ranges.wm_clk_ranges[2].wm_min_eng_clk_in_khz = + eng_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[2].wm_max_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz - 1; + clk_ranges.wm_clk_ranges[2].wm_min_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz; + /* 5 GHz instead of data[2].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[2].wm_max_mem_clk_in_khz = 5000000; + + clk_ranges.wm_clk_ranges[3].wm_set_id = WM_SET_D; + clk_ranges.wm_clk_ranges[3].wm_min_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz; + /* 5 GHz instead of data[7].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[3].wm_max_eng_clk_in_khz = 5000000; + clk_ranges.wm_clk_ranges[3].wm_min_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz; + /* 5 GHz instead of data[2].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[3].wm_max_mem_clk_in_khz = 5000000; + + /* Notify PP Lib/SMU which Watermarks to use for which clock ranges */ + dm_pp_notify_wm_clock_changes(dc->ctx, &clk_ranges); +} + +static const struct resource_caps *dce112_resource_cap( + struct hw_asic_id *asic_id) +{ + if (ASIC_REV_IS_POLARIS11_M(asic_id->hw_internal_rev) || + ASIC_REV_IS_POLARIS12_V(asic_id->hw_internal_rev)) + return &polaris_11_resource_cap; + else + return &polaris_10_resource_cap; +} + +static bool dce112_resource_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = dce112_resource_cap(&ctx->asic_id); + pool->base.funcs = &dce112_res_pool_funcs; + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = pool->base.res_cap->num_timing_generator; + pool->base.timing_generator_count = pool->base.res_cap->num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 100; + dc->caps.i2c_speed_in_khz_hdcp = 100; /*1.4 w/a not applied by default*/ + dc->caps.max_cursor_size = 128; + dc->caps.min_horizontal_blanking_period = 80; + dc->caps.dual_link_dvi = true; + dc->caps.extended_aux_timeout_support = false; + + /************************************************* + * Create resources * + *************************************************/ + + pool->base.clock_sources[DCE112_CLK_SRC_PLL0] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL0, + &clk_src_regs[0], false); + pool->base.clock_sources[DCE112_CLK_SRC_PLL1] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL1, + &clk_src_regs[1], false); + pool->base.clock_sources[DCE112_CLK_SRC_PLL2] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL2, + &clk_src_regs[2], false); + pool->base.clock_sources[DCE112_CLK_SRC_PLL3] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL3, + &clk_src_regs[3], false); + pool->base.clock_sources[DCE112_CLK_SRC_PLL4] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL4, + &clk_src_regs[4], false); + pool->base.clock_sources[DCE112_CLK_SRC_PLL5] = + dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL5, + &clk_src_regs[5], false); + pool->base.clk_src_count = DCE112_CLK_SRC_TOTAL; + + pool->base.dp_clock_source = dce112_clock_source_create( + ctx, ctx->dc_bios, + CLOCK_SOURCE_ID_DP_DTO, &clk_src_regs[0], true); + + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce110_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = + dce112_timing_generator_create( + ctx, + i, + &dce112_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce112_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce112_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce112_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce112_opp_create( + ctx, + i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce112_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce112_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = NULL; + } + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < dc->caps.max_planes; ++i) + dc->caps.planes[i] = plane_cap; + + /* Create hardware sequencer */ + dce112_hw_sequencer_construct(dc); + + bw_calcs_init(dc->bw_dceip, dc->bw_vbios, dc->ctx->asic_id); + + bw_calcs_data_update_from_pplib(dc); + + return true; + +res_create_fail: + dce112_resource_destruct(pool); + return false; +} + +struct resource_pool *dce112_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce112_resource_construct(num_virtual_links, dc, pool)) + return &pool->base; + + kfree(pool); + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.h b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.h new file mode 100644 index 000000000..1f57ebc6f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.h @@ -0,0 +1,57 @@ +/* +* 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_RESOURCE_DCE112_H__ +#define __DC_RESOURCE_DCE112_H__ + +#include "core_types.h" + +struct dc; +struct resource_pool; + +struct resource_pool *dce112_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +enum dc_status dce112_validate_with_context( + struct dc *dc, + const struct dc_validation_set set[], + int set_count, + struct dc_state *context, + struct dc_state *old_context); + +bool dce112_validate_bandwidth( + struct dc *dc, + struct dc_state *context, + bool fast_validate); + +enum dc_status dce112_add_stream_to_ctx( + struct dc *dc, + struct dc_state *new_ctx, + struct dc_stream_state *dc_stream); + + +#endif /* __DC_RESOURCE_DCE112_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/Makefile b/drivers/gpu/drm/amd/display/dc/dce120/Makefile new file mode 100644 index 000000000..a9cc4b732 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/Makefile @@ -0,0 +1,34 @@ +# +# Copyright 2017 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. +# +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + + +CFLAGS_$(AMDDALPATH)/dc/dce120/dce120_resource.o = $(call cc-disable-warning, override-init) + +DCE120 = dce120_resource.o dce120_timing_generator.o \ +dce120_hw_sequencer.o + +AMD_DAL_DCE120 = $(addprefix $(AMDDALPATH)/dc/dce120/,$(DCE120)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE120) diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c new file mode 100644 index 000000000..d4afe6c82 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c @@ -0,0 +1,271 @@ +/* + * Copyright 2015 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.h" +#include "core_types.h" +#include "dce120_hw_sequencer.h" +#include "dce/dce_hwseq.h" + +#include "dce110/dce110_hw_sequencer.h" + +#include "dce/dce_12_0_offset.h" +#include "dce/dce_12_0_sh_mask.h" +#include "soc15_hw_ip.h" +#include "vega10_ip_offset.h" +#include "reg_helper.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 + +struct dce120_hw_seq_reg_offsets { + uint32_t crtc; +}; + +#if 0 +static const struct dce120_hw_seq_reg_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC3_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC4_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC5_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +} +}; + +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + +#define CNTL_ID(controller_id)\ + controller_id +/******************************************************************************* + * Private definitions + ******************************************************************************/ +static void dce120_init_pte(struct dc_context *ctx, uint8_t controller_id) +{ + uint32_t addr; + uint32_t value = 0; + uint32_t chunk_int = 0; + uint32_t chunk_mul = 0; +/* + addr = mmDCP0_DVMM_PTE_CONTROL + controller_id * + (mmDCP1_DVMM_PTE_CONTROL- mmDCP0_DVMM_PTE_CONTROL); + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, 0, DCP, controller_id, + DVMM_PTE_CONTROL, + DVMM_USE_SINGLE_PTE); + + set_reg_field_value_soc15( + value, 1, DCP, controller_id, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE0); + + set_reg_field_value_soc15( + value, 1, DCP, controller_id, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE1); + + dm_write_reg(ctx, addr, value);*/ + + addr = mmDVMM_PTE_REQ; + value = dm_read_reg(ctx, addr); + + chunk_int = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + chunk_mul = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + if (chunk_int != 0x4 || chunk_mul != 0x4) { + + set_reg_field_value( + value, + 255, + DVMM_PTE_REQ, + MAX_PTEREQ_TO_ISSUE); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + dm_write_reg(ctx, addr, value); + } +} +#endif + +static bool dce120_enable_display_power_gating( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + /* disable for bringup */ +#if 0 + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + struct dc_context *ctx = dc->ctx; + + if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) + return true; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (power_gating != PIPE_GATING_CONTROL_INIT || controller_id == 0) { + + bp_result = dcb->funcs->enable_disp_power_gating( + dcb, controller_id + 1, cntl); + + /* Revert MASTER_UPDATE_MODE to 0 because bios sets it 2 + * by default when command table is called + */ + dm_write_reg(ctx, + HW_REG_CRTC(mmCRTC0_CRTC_MASTER_UPDATE_MODE, controller_id), + 0); + } + + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + dce120_init_pte(ctx, controller_id); + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +#endif + return false; +} + +static void dce120_update_dchub( + struct dce_hwseq *hws, + struct dchub_init_data *dh_data) +{ + /* TODO: port code from dal2 */ + switch (dh_data->fb_mode) { + case FRAME_BUFFER_MODE_ZFB_ONLY: + /*For ZFB case need to put DCHUB FB BASE and TOP upside down to indicate ZFB mode*/ + REG_UPDATE_2(DCHUB_FB_LOCATION, + FB_TOP, 0, + FB_BASE, 0x0FFFF); + + REG_UPDATE(DCHUB_AGP_BASE, + AGP_BASE, dh_data->zfb_phys_addr_base >> 22); + + REG_UPDATE(DCHUB_AGP_BOT, + AGP_BOT, dh_data->zfb_mc_base_addr >> 22); + + REG_UPDATE(DCHUB_AGP_TOP, + AGP_TOP, (dh_data->zfb_mc_base_addr + dh_data->zfb_size_in_byte - 1) >> 22); + break; + case FRAME_BUFFER_MODE_MIXED_ZFB_AND_LOCAL: + /*Should not touch FB LOCATION (done by VBIOS on AsicInit table)*/ + REG_UPDATE(DCHUB_AGP_BASE, + AGP_BASE, dh_data->zfb_phys_addr_base >> 22); + + REG_UPDATE(DCHUB_AGP_BOT, + AGP_BOT, dh_data->zfb_mc_base_addr >> 22); + + REG_UPDATE(DCHUB_AGP_TOP, + AGP_TOP, (dh_data->zfb_mc_base_addr + dh_data->zfb_size_in_byte - 1) >> 22); + break; + case FRAME_BUFFER_MODE_LOCAL_ONLY: + /*Should not touch FB LOCATION (done by VBIOS on AsicInit table)*/ + REG_UPDATE(DCHUB_AGP_BASE, + AGP_BASE, 0); + + REG_UPDATE(DCHUB_AGP_BOT, + AGP_BOT, 0x03FFFF); + + REG_UPDATE(DCHUB_AGP_TOP, + AGP_TOP, 0); + break; + default: + break; + } + + dh_data->dchub_initialzied = true; + dh_data->dchub_info_valid = false; +} + +/** + * dce121_xgmi_enabled() - Check if xGMI is enabled + * @hws: DCE hardware sequencer object + * + * Return true if xGMI is enabled. False otherwise. + */ +bool dce121_xgmi_enabled(struct dce_hwseq *hws) +{ + uint32_t pf_max_region; + + REG_GET(MC_VM_XGMI_LFB_CNTL, PF_MAX_REGION, &pf_max_region); + /* PF_MAX_REGION == 0 means xgmi is disabled */ + return !!pf_max_region; +} + +void dce120_hw_sequencer_construct(struct dc *dc) +{ + /* All registers used by dce11.2 match those in dce11 in offset and + * structure + */ + dce110_hw_sequencer_construct(dc); + dc->hwseq->funcs.enable_display_power_gating = dce120_enable_display_power_gating; + dc->hwss.update_dchub = dce120_update_dchub; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h new file mode 100644 index 000000000..bc0245347 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h @@ -0,0 +1,38 @@ +/* +* 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_HWSS_DCE120_H__ +#define __DC_HWSS_DCE120_H__ + +#include "core_types.h" +#include "hw_sequencer_private.h" + +struct dc; + +bool dce121_xgmi_enabled(struct dce_hwseq *hws); +void dce120_hw_sequencer_construct(struct dc *dc); + +#endif /* __DC_HWSS_DCE112_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c new file mode 100644 index 000000000..af631085e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c @@ -0,0 +1,1287 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc.cls +* + * + * 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 "stream_encoder.h" +#include "resource.h" +#include "include/irq_service_interface.h" +#include "dce120_resource.h" + +#include "dce112/dce112_resource.h" + +#include "dce110/dce110_resource.h" +#include "../virtual/virtual_stream_encoder.h" +#include "dce120_timing_generator.h" +#include "irq/dce120/irq_service_dce120.h" +#include "dce/dce_opp.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_ipp.h" +#include "dce/dce_mem_input.h" +#include "dce/dce_panel_cntl.h" + +#include "dce110/dce110_hw_sequencer.h" +#include "dce120/dce120_hw_sequencer.h" +#include "dce/dce_transform.h" +#include "clk_mgr.h" +#include "dce/dce_audio.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_hwseq.h" +#include "dce/dce_abm.h" +#include "dce/dce_dmcu.h" +#include "dce/dce_aux.h" +#include "dce/dce_i2c.h" + +#include "dce/dce_12_0_offset.h" +#include "dce/dce_12_0_sh_mask.h" +#include "soc15_hw_ip.h" +#include "vega10_ip_offset.h" +#include "nbio/nbio_6_1_offset.h" +#include "mmhub/mmhub_1_0_offset.h" +#include "mmhub/mmhub_1_0_sh_mask.h" +#include "reg_helper.h" + +#include "dce100/dce100_resource.h" + +#ifndef mmDP0_DP_DPHY_INTERNAL_CTRL + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x210f + #define mmDP0_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x220f + #define mmDP1_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x230f + #define mmDP2_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x240f + #define mmDP3_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x250f + #define mmDP4_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x260f + #define mmDP5_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x270f + #define mmDP6_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 +#endif + +enum dce120_clk_src_array_id { + DCE120_CLK_SRC_PLL0, + DCE120_CLK_SRC_PLL1, + DCE120_CLK_SRC_PLL2, + DCE120_CLK_SRC_PLL3, + DCE120_CLK_SRC_PLL4, + DCE120_CLK_SRC_PLL5, + + DCE120_CLK_SRC_TOTAL +}; + +static const struct dce110_timing_generator_offsets dce120_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + } +}; + +/* begin ********************* + * macros to expend register list macro defined in HW object header file */ + +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +#define NBIO_BASE_INNER(seg) \ + NBIF_BASE__INST0_SEG ## seg + +#define NBIO_BASE(seg) \ + NBIO_BASE_INNER(seg) + +/* compile time expand base address. */ +#define BASE(seg) \ + BASE_INNER(seg) + +#define SR(reg_name)\ + .reg_name = BASE(mm ## reg_name ## _BASE_IDX) + \ + mm ## reg_name + +#define SRI(reg_name, block, id)\ + .reg_name = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + +/* MMHUB */ +#define MMHUB_BASE_INNER(seg) \ + MMHUB_BASE__INST0_SEG ## seg + +#define MMHUB_BASE(seg) \ + MMHUB_BASE_INNER(seg) + +#define MMHUB_SR(reg_name)\ + .reg_name = MMHUB_BASE(mm ## reg_name ## _BASE_IDX) + \ + mm ## reg_name + +/* macros to expend register list macro defined in HW object header file + * end *********************/ + + +static const struct dce_dmcu_registers dmcu_regs = { + DMCU_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_dmcu_shift dmcu_shift = { + DMCU_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_dmcu_mask dmcu_mask = { + DMCU_MASK_SH_LIST_DCE110(_MASK) +}; + +static const struct dce_abm_registers abm_regs = { + ABM_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCE110(_MASK) +}; + +#define ipp_regs(id)\ +[id] = {\ + IPP_DCE110_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2), + ipp_regs(3), + ipp_regs(4), + ipp_regs(5) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_DCE120_MASK_SH_LIST_SOC_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_DCE120_MASK_SH_LIST_SOC_BASE(_MASK) +}; + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE110(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2), + transform_regs(3), + transform_regs(4), + transform_regs(5) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_SOC_BASE(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_SOC_BASE(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +#define link_regs(id)\ +[id] = {\ + LE_DCE120_REG_LIST(id), \ + SRI(DP_DPHY_INTERNAL_CTRL, DP, id) \ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST(id),\ + .TMDS_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2), + stream_enc_regs(3), + stream_enc_regs(4), + stream_enc_regs(5) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE120(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE120(_MASK) +}; + +static const struct dce_panel_cntl_registers panel_cntl_regs[] = { + { DCE_PANEL_CNTL_REG_LIST() } +}; + +static const struct dce_panel_cntl_shift panel_cntl_shift = { + DCE_PANEL_CNTL_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_panel_cntl_mask panel_cntl_mask = { + DCE_PANEL_CNTL_MASK_SH_LIST(_MASK) +}; + +static const struct dce110_aux_registers_shift aux_shift = { + DCE12_AUX_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce110_aux_registers_mask aux_mask = { + DCE12_AUX_MASK_SH_LIST(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_120_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_120(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_120(_MASK) +}; + #define aux_engine_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = 0 \ +} + +static const struct dce110_aux_registers aux_engine_regs[] = { + aux_engine_regs(0), + aux_engine_regs(1), + aux_engine_regs(2), + aux_engine_regs(3), + aux_engine_regs(4), + aux_engine_regs(5) +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5), + audio_regs(6), +}; + +#define DCE120_AUD_COMMON_MASK_SH_LIST(mask_sh)\ + SF(AZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_INDEX, AZALIA_ENDPOINT_REG_INDEX, mask_sh),\ + SF(AZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_DATA, AZALIA_ENDPOINT_REG_DATA, mask_sh),\ + AUD_COMMON_MASK_SH_LIST_BASE(mask_sh) + +static const struct dce_audio_shift audio_shift = { + DCE120_AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_audio_mask audio_mask = { + DCE120_AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +static int map_transmitter_id_to_phy_instance( + enum transmitter transmitter) +{ + switch (transmitter) { + case TRANSMITTER_UNIPHY_A: + return 0; + case TRANSMITTER_UNIPHY_B: + return 1; + case TRANSMITTER_UNIPHY_C: + return 2; + case TRANSMITTER_UNIPHY_D: + return 3; + case TRANSMITTER_UNIPHY_E: + return 4; + case TRANSMITTER_UNIPHY_F: + return 5; + case TRANSMITTER_UNIPHY_G: + return 6; + default: + ASSERT(0); + return 0; + } +} + +#define clk_src_regs(index, id)\ +[index] = {\ + CS_COMMON_REG_LIST_DCE_112(id),\ +} + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0, A), + clk_src_regs(1, B), + clk_src_regs(2, C), + clk_src_regs(3, D), + clk_src_regs(4, E), + clk_src_regs(5, F) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_112(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_112(_MASK) +}; + +static struct output_pixel_processor *dce120_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} +static struct dce_aux *dce120_aux_engine_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct aux_engine_dce110 *aux_engine = + kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); + + if (!aux_engine) + return NULL; + + dce110_aux_engine_construct(aux_engine, ctx, inst, + SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, + &aux_engine_regs[inst], + &aux_mask, + &aux_shift, + ctx->dc->caps.extended_aux_timeout_support); + + return &aux_engine->base; +} +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +static struct dce_i2c_hw *dce120_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + + dce112_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3 + NBIO_BASE(mmBIOS_SCRATCH_3_BASE_IDX), + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 + NBIO_BASE(mmBIOS_SCRATCH_6_BASE_IDX) +}; + +static const struct resource_caps res_cap = { + .num_timing_generator = 6, + .num_audio = 7, + .num_stream_encoder = 6, + .num_pll = 6, + .num_ddc = 6, +}; + +static const struct dc_plane_cap plane_cap = { + .type = DC_PLANE_TYPE_DCE_RGB, + + .pixel_format_support = { + .argb8888 = true, + .nv12 = false, + .fp16 = true + }, + + .max_upscale_factor = { + .argb8888 = 16000, + .nv12 = 1, + .fp16 = 1 + }, + + .max_downscale_factor = { + .argb8888 = 250, + .nv12 = 1, + .fp16 = 1 + } +}; + +static const struct dc_debug_options debug_defaults = { + .disable_clock_gate = true, +}; + +static struct clock_source *dce120_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(*clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce112_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + kfree(clk_src); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static void dce120_clock_source_destroy(struct clock_source **clk_src) +{ + kfree(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + + +static bool dce120_hw_sequencer_create(struct dc *dc) +{ + /* All registers used by dce11.2 match those in dce11 in offset and + * structure + */ + dce120_hw_sequencer_construct(dc); + + /*TODO Move to separate file and Override what is needed */ + + return true; +} + +static struct timing_generator *dce120_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce120_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static void dce120_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static void dce120_resource_destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce120_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + if (pool->base.engines[i] != NULL) + dce110_engine_destroy(&pool->base.engines[i]); + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } + } + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i]) + dce_aud_destroy(&pool->base.audios[i]); + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) + dce120_clock_source_destroy( + &pool->base.clock_sources[i]); + } + + if (pool->base.dp_clock_source != NULL) + dce120_clock_source_destroy(&pool->base.dp_clock_source); + + if (pool->base.abm != NULL) + dce_abm_destroy(&pool->base.abm); + + if (pool->base.dmcu != NULL) + dce_dmcu_destroy(&pool->base.dmcu); +} + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + uint32_t reg_val = dm_read_reg_soc15(ctx, mmCC_DC_MISC_STRAPS, 0); + + straps->audio_stream_number = get_reg_field_value(reg_val, + CC_DC_MISC_STRAPS, + AUDIO_STREAM_NUMBER); + straps->hdmi_disable = get_reg_field_value(reg_val, + CC_DC_MISC_STRAPS, + HDMI_DISABLE); + + reg_val = dm_read_reg_soc15(ctx, mmDC_PINSTRAPS, 0); + straps->dc_pinstraps_audio = get_reg_field_value(reg_val, + DC_PINSTRAPS, + DC_PINSTRAPS_AUDIO); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 600000, + .hdmi_ycbcr420_supported = true, + .dp_ycbcr420_supported = false, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_HBR3_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true, + .flags.bits.IS_TPS4_CAPABLE = true, +}; + +static struct link_encoder *dce120_link_encoder_create( + struct dc_context *ctx, + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + int link_regs_id; + + if (!enc110) + return NULL; + + link_regs_id = + map_transmitter_id_to_phy_instance(enc_init_data->transmitter); + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[link_regs_id], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + + return &enc110->base; +} + +static struct panel_cntl *dce120_panel_cntl_create(const struct panel_cntl_init_data *init_data) +{ + struct dce_panel_cntl *panel_cntl = + kzalloc(sizeof(struct dce_panel_cntl), GFP_KERNEL); + + if (!panel_cntl) + return NULL; + + dce_panel_cntl_construct(panel_cntl, + init_data, + &panel_cntl_regs[init_data->inst], + &panel_cntl_shift, + &panel_cntl_mask); + + return &panel_cntl->base; +} + +static struct input_pixel_processor *dce120_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +static struct stream_encoder *dce120_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_reg = { + HWSEQ_DCE120_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE12_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE12_MASK_SH_LIST(_MASK) +}; + +/* HWSEQ regs for VG20 */ +static const struct dce_hwseq_registers dce121_hwseq_reg = { + HWSEQ_VG20_REG_LIST() +}; + +static const struct dce_hwseq_shift dce121_hwseq_shift = { + HWSEQ_VG20_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask dce121_hwseq_mask = { + HWSEQ_VG20_MASK_SH_LIST(_MASK) +}; + +static struct dce_hwseq *dce120_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + return hws; +} + +static struct dce_hwseq *dce121_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &dce121_hwseq_reg; + hws->shifts = &dce121_hwseq_shift; + hws->masks = &dce121_hwseq_mask; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce120_stream_encoder_create, + .create_hwseq = dce120_hwseq_create, +}; + +static const struct resource_create_funcs dce121_res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce120_stream_encoder_create, + .create_hwseq = dce121_hwseq_create, +}; + + +#define mi_inst_regs(id) { MI_DCE12_REG_LIST(id) } +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), + mi_inst_regs(3), + mi_inst_regs(4), + mi_inst_regs(5), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE12_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE12_MASK_SH_LIST(_MASK) +}; + +static struct mem_input *dce120_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce120_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + return &dce_mi->base; +} + +static struct transform *dce120_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + transform->lb_memory_size = 0x1404; /*5124*/ + return &transform->base; +} + +static void dce120_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + dce120_resource_destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + +static const struct resource_funcs dce120_res_pool_funcs = { + .destroy = dce120_destroy_resource_pool, + .link_enc_create = dce120_link_encoder_create, + .panel_cntl_create = dce120_panel_cntl_create, + .validate_bandwidth = dce112_validate_bandwidth, + .validate_plane = dce100_validate_plane, + .add_stream_to_ctx = dce112_add_stream_to_ctx, + .find_first_free_match_stream_enc_for_link = dce110_find_first_free_match_stream_enc_for_link +}; + +static void bw_calcs_data_update_from_pplib(struct dc *dc) +{ + struct dm_pp_clock_levels_with_latency eng_clks = {0}; + struct dm_pp_clock_levels_with_latency mem_clks = {0}; + struct dm_pp_wm_sets_with_clock_ranges clk_ranges = {0}; + int i; + unsigned int clk; + unsigned int latency; + /*original logic in dal3*/ + int memory_type_multiplier = MEMORY_TYPE_MULTIPLIER_CZ; + + /*do system clock*/ + if (!dm_pp_get_clock_levels_by_type_with_latency( + dc->ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, + &eng_clks) || eng_clks.num_levels == 0) { + + eng_clks.num_levels = 8; + clk = 300000; + + for (i = 0; i < eng_clks.num_levels; i++) { + eng_clks.data[i].clocks_in_khz = clk; + clk += 100000; + } + } + + /* convert all the clock fro kHz to fix point mHz TODO: wloop data */ + dc->bw_vbios->high_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels-1].clocks_in_khz, 1000); + dc->bw_vbios->mid1_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels/8].clocks_in_khz, 1000); + dc->bw_vbios->mid2_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*2/8].clocks_in_khz, 1000); + dc->bw_vbios->mid3_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz, 1000); + dc->bw_vbios->mid4_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*4/8].clocks_in_khz, 1000); + dc->bw_vbios->mid5_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*5/8].clocks_in_khz, 1000); + dc->bw_vbios->mid6_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*6/8].clocks_in_khz, 1000); + dc->bw_vbios->low_sclk = bw_frc_to_fixed( + eng_clks.data[0].clocks_in_khz, 1000); + + /*do memory clock*/ + if (!dm_pp_get_clock_levels_by_type_with_latency( + dc->ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, + &mem_clks) || mem_clks.num_levels == 0) { + + mem_clks.num_levels = 3; + clk = 250000; + latency = 45; + + for (i = 0; i < eng_clks.num_levels; i++) { + mem_clks.data[i].clocks_in_khz = clk; + mem_clks.data[i].latency_in_us = latency; + clk += 500000; + latency -= 5; + } + + } + + /* we don't need to call PPLIB for validation clock since they + * also give us the highest sclk and highest mclk (UMA clock). + * ALSO always convert UMA clock (from PPLIB) to YCLK (HW formula): + * YCLK = UMACLK*m_memoryTypeMultiplier + */ + if (dc->bw_vbios->memory_type == bw_def_hbm) + memory_type_multiplier = MEMORY_TYPE_HBM; + + dc->bw_vbios->low_yclk = bw_frc_to_fixed( + mem_clks.data[0].clocks_in_khz * memory_type_multiplier, 1000); + dc->bw_vbios->mid_yclk = bw_frc_to_fixed( + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz * memory_type_multiplier, + 1000); + dc->bw_vbios->high_yclk = bw_frc_to_fixed( + mem_clks.data[mem_clks.num_levels-1].clocks_in_khz * memory_type_multiplier, + 1000); + + /* Now notify PPLib/SMU about which Watermarks sets they should select + * depending on DPM state they are in. And update BW MGR GFX Engine and + * Memory clock member variables for Watermarks calculations for each + * Watermark Set + */ + clk_ranges.num_wm_sets = 4; + clk_ranges.wm_clk_ranges[0].wm_set_id = WM_SET_A; + clk_ranges.wm_clk_ranges[0].wm_min_eng_clk_in_khz = + eng_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[0].wm_max_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz - 1; + clk_ranges.wm_clk_ranges[0].wm_min_mem_clk_in_khz = + mem_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[0].wm_max_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz - 1; + + clk_ranges.wm_clk_ranges[1].wm_set_id = WM_SET_B; + clk_ranges.wm_clk_ranges[1].wm_min_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz; + /* 5 GHz instead of data[7].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[1].wm_max_eng_clk_in_khz = 5000000; + clk_ranges.wm_clk_ranges[1].wm_min_mem_clk_in_khz = + mem_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[1].wm_max_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz - 1; + + clk_ranges.wm_clk_ranges[2].wm_set_id = WM_SET_C; + clk_ranges.wm_clk_ranges[2].wm_min_eng_clk_in_khz = + eng_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[2].wm_max_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz - 1; + clk_ranges.wm_clk_ranges[2].wm_min_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz; + /* 5 GHz instead of data[2].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[2].wm_max_mem_clk_in_khz = 5000000; + + clk_ranges.wm_clk_ranges[3].wm_set_id = WM_SET_D; + clk_ranges.wm_clk_ranges[3].wm_min_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz; + /* 5 GHz instead of data[7].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[3].wm_max_eng_clk_in_khz = 5000000; + clk_ranges.wm_clk_ranges[3].wm_min_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz; + /* 5 GHz instead of data[2].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[3].wm_max_mem_clk_in_khz = 5000000; + + /* Notify PP Lib/SMU which Watermarks to use for which clock ranges */ + dm_pp_notify_wm_clock_changes(dc->ctx, &clk_ranges); +} + +static uint32_t read_pipe_fuses(struct dc_context *ctx) +{ + uint32_t value = dm_read_reg_soc15(ctx, mmCC_DC_PIPE_DIS, 0); + /* VG20 support max 6 pipes */ + value = value & 0x3f; + return value; +} + +static bool dce120_resource_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + int j; + struct dc_context *ctx = dc->ctx; + struct irq_service_init_data irq_init_data; + static const struct resource_create_funcs *res_funcs; + bool is_vg20 = ASICREV_IS_VEGA20_P(ctx->asic_id.hw_internal_rev); + uint32_t pipe_fuses; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap; + pool->base.funcs = &dce120_res_pool_funcs; + + /* TODO: Fill more data from GreenlandAsicCapability.cpp */ + pool->base.pipe_count = res_cap.num_timing_generator; + pool->base.timing_generator_count = pool->base.res_cap->num_timing_generator; + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 100; + dc->caps.i2c_speed_in_khz_hdcp = 100; /*1.4 w/a not applied by default*/ + dc->caps.max_cursor_size = 128; + dc->caps.min_horizontal_blanking_period = 80; + dc->caps.dual_link_dvi = true; + dc->caps.psp_setup_panel_mode = true; + dc->caps.extended_aux_timeout_support = false; + dc->debug = debug_defaults; + + /************************************************* + * Create resources * + *************************************************/ + + pool->base.clock_sources[DCE120_CLK_SRC_PLL0] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL0, + &clk_src_regs[0], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL1] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL1, + &clk_src_regs[1], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL2] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL2, + &clk_src_regs[2], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL3] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL3, + &clk_src_regs[3], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL4] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL4, + &clk_src_regs[4], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL5] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL5, + &clk_src_regs[5], false); + pool->base.clk_src_count = DCE120_CLK_SRC_TOTAL; + + pool->base.dp_clock_source = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_ID_DP_DTO, + &clk_src_regs[0], true); + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto clk_src_create_fail; + } + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + + irq_init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce120_create(&irq_init_data); + if (!pool->base.irqs) + goto irqs_create_fail; + + /* VG20: Pipe harvesting enabled, retrieve valid pipe fuses */ + if (is_vg20) + pipe_fuses = read_pipe_fuses(ctx); + + /* index to valid pipe resource */ + j = 0; + for (i = 0; i < pool->base.pipe_count; i++) { + if (is_vg20) { + if ((pipe_fuses & (1 << i)) != 0) { + dm_error("DC: skip invalid pipe %d!\n", i); + continue; + } + } + + pool->base.timing_generators[j] = + dce120_timing_generator_create( + ctx, + i, + &dce120_tg_offsets[i]); + if (pool->base.timing_generators[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto controller_create_fail; + } + + pool->base.mis[j] = dce120_mem_input_create(ctx, i); + + if (pool->base.mis[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto controller_create_fail; + } + + pool->base.ipps[j] = dce120_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create input pixel processor!\n"); + goto controller_create_fail; + } + + pool->base.transforms[j] = dce120_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[j] = dce120_opp_create( + ctx, + i); + if (pool->base.opps[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + } + + /* check next valid pipe */ + j++; + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce120_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce120_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = NULL; + } + + /* valid pipe num */ + pool->base.pipe_count = j; + pool->base.timing_generator_count = j; + + if (is_vg20) + res_funcs = &dce121_res_create_funcs; + else + res_funcs = &res_create_funcs; + + if (!resource_construct(num_virtual_links, dc, &pool->base, res_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + if (!dce120_hw_sequencer_create(dc)) + goto controller_create_fail; + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < dc->caps.max_planes; ++i) + dc->caps.planes[i] = plane_cap; + + bw_calcs_init(dc->bw_dceip, dc->bw_vbios, dc->ctx->asic_id); + + bw_calcs_data_update_from_pplib(dc); + + return true; + +irqs_create_fail: +controller_create_fail: +clk_src_create_fail: +res_create_fail: + + dce120_resource_destruct(pool); + + return false; +} + +struct resource_pool *dce120_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce120_resource_construct(num_virtual_links, dc, pool)) + return &pool->base; + + kfree(pool); + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.h b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.h new file mode 100644 index 000000000..3d1f3cf01 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.h @@ -0,0 +1,39 @@ +/* +* 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_RESOURCE_DCE120_H__ +#define __DC_RESOURCE_DCE120_H__ + +#include "core_types.h" + +struct dc; +struct resource_pool; + +struct resource_pool *dce120_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +#endif /* __DC_RESOURCE_DCE120_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c new file mode 100644 index 000000000..4af0c7009 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c @@ -0,0 +1,1232 @@ +/* + * 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 "dce/dce_12_0_offset.h" +#include "dce/dce_12_0_sh_mask.h" +#include "soc15_hw_ip.h" +#include "vega10_ip_offset.h" + +#include "dc_types.h" +#include "dc_bios_types.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "dce120_timing_generator.h" + +#include "timing_generator.h" + +#define CRTC_REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_soc15(tg110->base.ctx, tg110->offsets.crtc, reg_name, n, __VA_ARGS__) + +#define CRTC_REG_SET_N(reg_name, n, ...) \ + generic_reg_set_soc15(tg110->base.ctx, tg110->offsets.crtc, reg_name, n, __VA_ARGS__) + +#define CRTC_REG_UPDATE(reg, field, val) \ + CRTC_REG_UPDATE_N(reg, 1, FD(reg##__##field), val) + +#define CRTC_REG_UPDATE_2(reg, field1, val1, field2, val2) \ + CRTC_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define CRTC_REG_UPDATE_3(reg, field1, val1, field2, val2, field3, val3) \ + CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +#define CRTC_REG_UPDATE_4(reg, field1, val1, field2, val2, field3, val3, field4, val4) \ + CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3, FD(reg##__##field4), val4) + +#define CRTC_REG_UPDATE_5(reg, field1, val1, field2, val2, field3, val3, field4, val4, field5, val5) \ + CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3, FD(reg##__##field4), val4, FD(reg##__##field5), val5) + +#define CRTC_REG_SET(reg, field, val) \ + CRTC_REG_SET_N(reg, 1, FD(reg##__##field), val) + +#define CRTC_REG_SET_2(reg, field1, val1, field2, val2) \ + CRTC_REG_SET_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define CRTC_REG_SET_3(reg, field1, val1, field2, val2, field3, val3) \ + CRTC_REG_SET_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +/* + ***************************************************************************** + * Function: is_in_vertical_blank + * + * @brief + * check the current status of CRTC to check if we are in Vertical Blank + * regioneased" state + * + * @return + * true if currently in blank region, false otherwise + * + ***************************************************************************** + */ +static bool dce120_timing_generator_is_in_vertical_blank( + struct timing_generator *tg) +{ + uint32_t field = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_STATUS, + tg110->offsets.crtc); + + field = get_reg_field_value(value, CRTC0_CRTC_STATUS, CRTC_V_BLANK); + return field == 1; +} + + +/* determine if given timing can be supported by TG */ +static bool dce120_timing_generator_validate_timing( + struct timing_generator *tg, + const struct dc_crtc_timing *timing, + enum signal_type signal) +{ + uint32_t interlace_factor = timing->flags.INTERLACE ? 2 : 1; + uint32_t v_blank = + (timing->v_total - timing->v_addressable - + timing->v_border_top - timing->v_border_bottom) * + interlace_factor; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + if (!dce110_timing_generator_validate_timing( + tg, + timing, + signal)) + return false; + + + if (v_blank < tg110->min_v_blank || + timing->h_sync_width < tg110->min_h_sync_width || + timing->v_sync_width < tg110->min_v_sync_width) + return false; + + return true; +} + +static bool dce120_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + return dce120_timing_generator_validate_timing(tg, timing, SIGNAL_TYPE_NONE); +} + +/******** HW programming ************/ +/* Disable/Enable Timing Generator */ +static bool dce120_timing_generator_enable_crtc(struct timing_generator *tg) +{ + enum bp_result result; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Set MASTER_UPDATE_MODE to 0 + * This is needed for DRR, and also suggested to be default value by Syed.*/ + + CRTC_REG_UPDATE(CRTC0_CRTC_MASTER_UPDATE_MODE, + MASTER_UPDATE_MODE, 0); + + CRTC_REG_UPDATE(CRTC0_CRTC_MASTER_UPDATE_LOCK, + UNDERFLOW_UPDATE_LOCK, 0); + + /* TODO API for AtomFirmware didn't change*/ + result = tg->bp->funcs->enable_crtc(tg->bp, tg110->controller_id, true); + + return result == BP_RESULT_OK; +} + +static void dce120_timing_generator_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE(CRTC0_CRTC_CONTROL, + CRTC_HBLANK_EARLY_CONTROL, early_cntl); +} + +/**************** TG current status ******************/ + +/* return the current frame counter. Used by Linux kernel DRM */ +static uint32_t dce120_timing_generator_get_vblank_counter( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_STATUS_FRAME_COUNT, + tg110->offsets.crtc); + uint32_t field = get_reg_field_value( + value, CRTC0_CRTC_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); + + return field; +} + +/* Get current H and V position */ +static void dce120_timing_generator_get_crtc_position( + struct timing_generator *tg, + struct crtc_position *position) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_STATUS_POSITION, + tg110->offsets.crtc); + + position->horizontal_count = get_reg_field_value(value, + CRTC0_CRTC_STATUS_POSITION, CRTC_HORZ_COUNT); + + position->vertical_count = get_reg_field_value(value, + CRTC0_CRTC_STATUS_POSITION, CRTC_VERT_COUNT); + + value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_NOM_VERT_POSITION, + tg110->offsets.crtc); + + position->nominal_vcount = get_reg_field_value(value, + CRTC0_CRTC_NOM_VERT_POSITION, CRTC_VERT_COUNT_NOM); +} + +/* wait until TG is in beginning of vertical blank region */ +static void dce120_timing_generator_wait_for_vblank(struct timing_generator *tg) +{ + /* We want to catch beginning of VBlank here, so if the first try are + * in VBlank, we might be very close to Active, in this case wait for + * another frame + */ + while (dce120_timing_generator_is_in_vertical_blank(tg)) { + if (!tg->funcs->is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } + + while (!dce120_timing_generator_is_in_vertical_blank(tg)) { + if (!tg->funcs->is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/* wait until TG is in beginning of active region */ +static void dce120_timing_generator_wait_for_vactive(struct timing_generator *tg) +{ + while (dce120_timing_generator_is_in_vertical_blank(tg)) { + if (!tg->funcs->is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/*********** Timing Generator Synchronization routines ****/ + +/* Setups Global Swap Lock group, TimingServer or TimingClient*/ +static void dce120_timing_generator_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value_crtc_vtotal = + dm_read_reg_soc15(tg->ctx, + mmCRTC0_CRTC_V_TOTAL, + tg110->offsets.crtc); + /* Checkpoint relative to end of frame */ + uint32_t check_point = + get_reg_field_value(value_crtc_vtotal, + CRTC0_CRTC_V_TOTAL, + CRTC_V_TOTAL); + + + dm_write_reg_soc15(tg->ctx, mmCRTC0_CRTC_GSL_WINDOW, tg110->offsets.crtc, 0); + + CRTC_REG_UPDATE_N(DCP0_DCP_GSL_CONTROL, 6, + /* This pipe will belong to GSL Group zero. */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL0_EN), 1, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_MASTER_EN), gsl_params->gsl_master == tg->inst, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_FORCE_DELAY), HFLIP_READY_DELAY, + /* Keep signal low (pending high) during 6 lines. + * Also defines minimum interval before re-checking signal. */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_CHECK_DELAY), HFLIP_CHECK_DELAY, + /* DCP_GSL_PURPOSE_SURFACE_FLIP */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_SYNC_SOURCE), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_DELAY_SURFACE_UPDATE_PENDING), 1); + + CRTC_REG_SET_2( + CRTC0_CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM, check_point - FLIP_READY_BACK_LOOKUP, + CRTC_GSL_FORCE_DELAY, VFLIP_READY_DELAY); +} + +/* Clear all the register writes done by setup_global_swap_lock */ +static void dce120_timing_generator_tear_down_global_swap_lock( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Settig HW default values from reg specs */ + CRTC_REG_SET_N(DCP0_DCP_GSL_CONTROL, 6, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL0_EN), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_MASTER_EN), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_FORCE_DELAY), HFLIP_READY_DELAY, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_CHECK_DELAY), HFLIP_CHECK_DELAY, + /* DCP_GSL_PURPOSE_SURFACE_FLIP */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_SYNC_SOURCE), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_DELAY_SURFACE_UPDATE_PENDING), 0); + + CRTC_REG_SET_2(CRTC0_CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM, 0, + CRTC_GSL_FORCE_DELAY, 0x2); /*TODO Why this value here ?*/ +} + +/* Reset slave controllers on master VSync */ +static void dce120_timing_generator_enable_reset_trigger( + struct timing_generator *tg, + int source) +{ + enum trigger_source_select trig_src_select = TRIGGER_SOURCE_SELECT_LOGIC_ZERO; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t rising_edge = 0; + uint32_t falling_edge = 0; + /* Setup trigger edge */ + uint32_t pol_value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_V_SYNC_A_CNTL, + tg110->offsets.crtc); + + /* Register spec has reversed definition: + * 0 for positive, 1 for negative */ + if (get_reg_field_value(pol_value, + CRTC0_CRTC_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL) == 0) { + rising_edge = 1; + } else { + falling_edge = 1; + } + + /* TODO What about other sources ?*/ + trig_src_select = TRIGGER_SOURCE_SELECT_GSL_GROUP0; + + CRTC_REG_UPDATE_N(CRTC0_CRTC_TRIGB_CNTL, 7, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_SOURCE_SELECT), trig_src_select, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_POLARITY_SELECT), TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_RISING_EDGE_DETECT_CNTL), rising_edge, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_FALLING_EDGE_DETECT_CNTL), falling_edge, + /* send every signal */ + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_FREQUENCY_SELECT), 0, + /* no delay */ + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_DELAY), 0, + /* clear trigger status */ + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_CLEAR), 1); + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE, 2, + CRTC_FORCE_COUNT_NOW_TRIG_SEL, 1, + CRTC_FORCE_COUNT_NOW_CLEAR, 1); +} + +/* disabling trigger-reset */ +static void dce120_timing_generator_disable_reset_trigger( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE_2( + CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE, 0, + CRTC_FORCE_COUNT_NOW_CLEAR, 1); + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_TRIGB_CNTL, + CRTC_TRIGB_SOURCE_SELECT, TRIGGER_SOURCE_SELECT_LOGIC_ZERO, + CRTC_TRIGB_POLARITY_SELECT, TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + /* clear trigger status */ + CRTC_TRIGB_CLEAR, 1); + +} + +/* Checks whether CRTC triggered reset occurred */ +static bool dce120_timing_generator_did_triggered_reset_occur( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + tg110->offsets.crtc); + + return get_reg_field_value(value, + CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_OCCURRED) != 0; +} + + +/******** Stuff to move to other virtual HW objects *****************/ +/* Move to enable accelerated mode */ +static void dce120_timing_generator_disable_vga(struct timing_generator *tg) +{ + uint32_t offset = 0; + uint32_t value = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + switch (tg110->controller_id) { + case CONTROLLER_ID_D0: + offset = 0; + break; + case CONTROLLER_ID_D1: + offset = mmD2VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D2: + offset = mmD3VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D3: + offset = mmD4VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D4: + offset = mmD5VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D5: + offset = mmD6VGA_CONTROL - mmD1VGA_CONTROL; + break; + default: + break; + } + + value = dm_read_reg_soc15(tg->ctx, mmD1VGA_CONTROL, offset); + + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_MODE_ENABLE); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_TIMING_SELECT); + set_reg_field_value( + value, 0, D1VGA_CONTROL, D1VGA_SYNC_POLARITY_SELECT); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_OVERSCAN_COLOR_EN); + + dm_write_reg_soc15(tg->ctx, mmD1VGA_CONTROL, offset, value); +} +/* TODO: Should we move it to transform */ +/* Fully program CRTC timing in timing generator */ +static void dce120_timing_generator_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t tmp1 = 0; + uint32_t tmp2 = 0; + uint32_t vsync_offset = timing->v_border_bottom + + timing->v_front_porch; + uint32_t v_sync_start = timing->v_addressable + vsync_offset; + + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + uint32_t h_sync_start = timing->h_addressable + hsync_offset; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE( + CRTC0_CRTC_H_TOTAL, + CRTC_H_TOTAL, + timing->h_total - 1); + + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL, + CRTC_V_TOTAL, + timing->v_total - 1); + + /* In case of V_TOTAL_CONTROL is on, make sure V_TOTAL_MAX and + * V_TOTAL_MIN are equal to V_TOTAL. + */ + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX, + timing->v_total - 1); + + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN, + timing->v_total - 1); + + tmp1 = timing->h_total - + (h_sync_start + timing->h_border_left); + tmp2 = tmp1 + timing->h_addressable + + timing->h_border_left + timing->h_border_right; + + CRTC_REG_UPDATE_2( + CRTC0_CRTC_H_BLANK_START_END, + CRTC_H_BLANK_END, tmp1, + CRTC_H_BLANK_START, tmp2); + + tmp1 = timing->v_total - (v_sync_start + timing->v_border_top); + tmp2 = tmp1 + timing->v_addressable + timing->v_border_top + + timing->v_border_bottom; + + CRTC_REG_UPDATE_2( + CRTC0_CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END, tmp1, + CRTC_V_BLANK_START, tmp2); +} + +/* TODO: Should we move it to opp? */ +/* Combine with below and move YUV/RGB color conversion to SW layer */ +static void dce120_timing_generator_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB, black_color->color_b_cb, + CRTC_BLACK_COLOR_G_Y, black_color->color_g_y, + CRTC_BLACK_COLOR_R_CR, black_color->color_r_cr); +} +/* Combine with above and move YUV/RGB color conversion to SW layer */ +static void dce120_timing_generator_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = 0; + CRTC_REG_SET_3( + CRTC0_CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE, color->color_b_cb, + CRTC_OVERSCAN_COLOR_GREEN, color->color_g_y, + CRTC_OVERSCAN_COLOR_RED, color->color_r_cr); + + value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_OVERSCAN_COLOR, + tg110->offsets.crtc); + + dm_write_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLACK_COLOR, + tg110->offsets.crtc, + value); + + /* This is desirable to have a constant DAC output voltage during the + * blank time that is higher than the 0 volt reference level that the + * DAC outputs when the NBLANK signal + * is asserted low, such as for output to an analog TV. */ + dm_write_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLANK_DATA_COLOR, + tg110->offsets.crtc, + value); + + /* TO DO we have to program EXT registers and we need to know LB DATA + * format because it is used when more 10 , i.e. 12 bits per color + * + * m_mmDxCRTC_OVERSCAN_COLOR_EXT + * m_mmDxCRTC_BLACK_COLOR_EXT + * m_mmDxCRTC_BLANK_DATA_COLOR_EXT + */ +} + +static void dce120_timing_generator_set_drr( + struct timing_generator *tg, + const struct drr_params *params) +{ + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + if (params != NULL && + params->vertical_total_max > 0 && + params->vertical_total_min > 0) { + + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN, params->vertical_total_min - 1); + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX, params->vertical_total_max - 1); + CRTC_REG_SET_N(CRTC0_CRTC_V_TOTAL_CONTROL, 6, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MIN_SEL), 1, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MAX_SEL), 1, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_ON_EVENT), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_TO_MASTER_VSYNC), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK_EN), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK), 0); + CRTC_REG_UPDATE( + CRTC0_CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK, + 0x180); + + } else { + CRTC_REG_SET_N(CRTC0_CRTC_V_TOTAL_CONTROL, 5, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MIN_SEL), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MAX_SEL), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_ON_EVENT), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_TO_MASTER_VSYNC), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK), 0); + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN, 0); + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX, 0); + CRTC_REG_UPDATE( + CRTC0_CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK, + 0); + } +} + +static void dce120_timing_generator_get_crtc_scanoutpos( + struct timing_generator *tg, + uint32_t *v_blank_start, + uint32_t *v_blank_end, + uint32_t *h_position, + uint32_t *v_position) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + struct crtc_position position; + + uint32_t v_blank_start_end = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_V_BLANK_START_END, + tg110->offsets.crtc); + + *v_blank_start = get_reg_field_value(v_blank_start_end, + CRTC0_CRTC_V_BLANK_START_END, + CRTC_V_BLANK_START); + *v_blank_end = get_reg_field_value(v_blank_start_end, + CRTC0_CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END); + + dce120_timing_generator_get_crtc_position( + tg, &position); + + *h_position = position.horizontal_count; + *v_position = position.vertical_count; +} + +static void dce120_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t v_sync_width_and_b_porch = + timing->v_total - timing->v_addressable - + timing->v_border_bottom - timing->v_front_porch; + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_START_LINE_CONTROL, + tg110->offsets.crtc); + + set_reg_field_value( + value, + enable ? 0 : 1, + CRTC0_CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + + /* Program advanced line position acc.to the best case from fetching data perspective to hide MC latency + * and prefilling Line Buffer in V Blank (to 10 lines as LB can store max 10 lines) + */ + if (v_sync_width_and_b_porch > 10) + v_sync_width_and_b_porch = 10; + + set_reg_field_value( + value, + v_sync_width_and_b_porch, + CRTC0_CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + + dm_write_reg_soc15(tg->ctx, + mmCRTC0_CRTC_START_LINE_CONTROL, + tg110->offsets.crtc, + value); +} + +static void dce120_tg_program_blank_color(struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = 0; + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB, black_color->color_b_cb, + CRTC_BLACK_COLOR_G_Y, black_color->color_g_y, + CRTC_BLACK_COLOR_R_CR, black_color->color_r_cr); + + value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLACK_COLOR, + tg110->offsets.crtc); + dm_write_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLANK_DATA_COLOR, + tg110->offsets.crtc, + value); +} + +static void dce120_tg_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_SET_3( + CRTC0_CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE, overscan_color->color_b_cb, + CRTC_OVERSCAN_COLOR_GREEN, overscan_color->color_g_y, + CRTC_OVERSCAN_COLOR_RED, overscan_color->color_r_cr); +} + +static void dce120_tg_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + int vready_offset, + int vstartup_start, + int vupdate_offset, + int vupdate_width, + const enum signal_type signal, + bool use_vbios) +{ + if (use_vbios) + dce110_timing_generator_program_timing_generator(tg, timing); + else + dce120_timing_generator_program_blanking(tg, timing); +} + +static bool dce120_tg_is_blanked(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLANK_CONTROL, + tg110->offsets.crtc); + + if (get_reg_field_value( + value, + CRTC0_CRTC_BLANK_CONTROL, + CRTC_BLANK_DATA_EN) == 1 && + get_reg_field_value( + value, + CRTC0_CRTC_BLANK_CONTROL, + CRTC_CURRENT_BLANK_STATE) == 1) + return true; + + return false; +} + +static void dce120_tg_set_blank(struct timing_generator *tg, + bool enable_blanking) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_SET( + CRTC0_CRTC_DOUBLE_BUFFER_CONTROL, + CRTC_BLANK_DATA_DOUBLE_BUFFER_EN, 1); + + if (enable_blanking) + CRTC_REG_SET(CRTC0_CRTC_BLANK_CONTROL, CRTC_BLANK_DATA_EN, 1); + else + dm_write_reg_soc15(tg->ctx, mmCRTC0_CRTC_BLANK_CONTROL, + tg110->offsets.crtc, 0); +} + +bool dce120_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing); + +static void dce120_tg_wait_for_state(struct timing_generator *tg, + enum crtc_state state) +{ + switch (state) { + case CRTC_STATE_VBLANK: + dce120_timing_generator_wait_for_vblank(tg); + break; + + case CRTC_STATE_VACTIVE: + dce120_timing_generator_wait_for_vactive(tg); + break; + + default: + break; + } +} + +static void dce120_tg_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color) +{ + if (blank_color != NULL) + dce120_tg_program_blank_color(tg, blank_color); + + if (overscan_color != NULL) + dce120_tg_set_overscan_color(tg, overscan_color); +} + +static void dce120_timing_generator_set_static_screen_control( + struct timing_generator *tg, + uint32_t event_triggers, + uint32_t num_frames) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + // By register spec, it only takes 8 bit value + if (num_frames > 0xFF) + num_frames = 0xFF; + + CRTC_REG_UPDATE_2(CRTC0_CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK, event_triggers, + CRTC_STATIC_SCREEN_FRAME_COUNT, num_frames); +} + +static void dce120_timing_generator_set_test_pattern( + struct timing_generator *tg, + /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' + * because this is not DP-specific (which is probably somewhere in DP + * encoder) */ + enum controller_dp_test_pattern test_pattern, + enum dc_color_depth color_depth) +{ + struct dc_context *ctx = tg->ctx; + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + enum test_pattern_color_format bit_depth; + enum test_pattern_dyn_range dyn_range; + enum test_pattern_mode mode; + /* color ramp generator mixes 16-bits color */ + uint32_t src_bpc = 16; + /* requested bpc */ + uint32_t dst_bpc; + uint32_t index; + /* RGB values of the color bars. + * Produce two RGB colors: RGB0 - white (all Fs) + * and RGB1 - black (all 0s) + * (three RGB components for two colors) + */ + uint16_t src_color[6] = {0xFFFF, 0xFFFF, 0xFFFF, 0x0000, + 0x0000, 0x0000}; + /* dest color (converted to the specified color format) */ + uint16_t dst_color[6]; + uint32_t inc_base; + + /* translate to bit depth */ + switch (color_depth) { + case COLOR_DEPTH_666: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_6; + break; + case COLOR_DEPTH_888: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + case COLOR_DEPTH_101010: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_10; + break; + case COLOR_DEPTH_121212: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_12; + break; + default: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + } + + switch (test_pattern) { + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES: + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA: + { + dyn_range = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA ? + TEST_PATTERN_DYN_RANGE_CEA : + TEST_PATTERN_DYN_RANGE_VESA); + mode = TEST_PATTERN_MODE_COLORSQUARES_RGB; + + CRTC_REG_UPDATE_2(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES, 6, + CRTC_TEST_PATTERN_HRES, 6); + + CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN, 1, + CRTC_TEST_PATTERN_MODE, mode, + CRTC_TEST_PATTERN_DYNAMIC_RANGE, dyn_range, + CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_VERTICALBARS: + case CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS: + { + mode = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_VERTICALBARS ? + TEST_PATTERN_MODE_VERTICALBARS : + TEST_PATTERN_MODE_HORIZONTALBARS); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* adjust color to the required colorFormat */ + for (index = 0; index < 6; index++) { + /* dst = 2^dstBpc * src / 2^srcBpc = src >> + * (srcBpc - dstBpc); + */ + dst_color[index] = + src_color[index] >> (src_bpc - dst_bpc); + /* CRTC_TEST_PATTERN_DATA has 16 bits, + * lowest 6 are hardwired to ZERO + * color bits should be left aligned aligned to MSB + * XXXXXXXXXX000000 for 10 bit, + * XXXXXXXX00000000 for 8 bit and XXXXXX0000000000 for 6 + */ + dst_color[index] <<= (16 - dst_bpc); + } + + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_PARAMETERS, tg110->offsets.crtc, 0); + + /* We have to write the mask before data, similar to pipeline. + * For example, for 8 bpc, if we want RGB0 to be magenta, + * and RGB1 to be cyan, + * we need to make 7 writes: + * MASK DATA + * 000001 00000000 00000000 set mask to R0 + * 000010 11111111 00000000 R0 255, 0xFF00, set mask to G0 + * 000100 00000000 00000000 G0 0, 0x0000, set mask to B0 + * 001000 11111111 00000000 B0 255, 0xFF00, set mask to R1 + * 010000 00000000 00000000 R1 0, 0x0000, set mask to G1 + * 100000 11111111 00000000 G1 255, 0xFF00, set mask to B1 + * 100000 11111111 00000000 B1 255, 0xFF00 + * + * we will make a loop of 6 in which we prepare the mask, + * then write, then prepare the color for next write. + * first iteration will write mask only, + * but each next iteration color prepared in + * previous iteration will be written within new mask, + * the last component will written separately, + * mask is not changing between 6th and 7th write + * and color will be prepared by last iteration + */ + + /* write color, color values mask in CRTC_TEST_PATTERN_MASK + * is B1, G1, R1, B0, G0, R0 + */ + value = 0; + for (index = 0; index < 6; index++) { + /* prepare color mask, first write PATTERN_DATA + * will have all zeros + */ + set_reg_field_value( + value, + (1 << index), + CRTC0_CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_MASK); + /* write color component */ + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); + /* prepare next color component, + * will be written in the next iteration + */ + set_reg_field_value( + value, + dst_color[index], + CRTC0_CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_DATA); + } + /* write last color component, + * it's been already prepared in the loop + */ + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); + + /* enable test pattern */ + CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN, 1, + CRTC_TEST_PATTERN_MODE, mode, + CRTC_TEST_PATTERN_DYNAMIC_RANGE, 0, + CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_COLORRAMP: + { + mode = (bit_depth == + TEST_PATTERN_COLOR_FORMAT_BPC_10 ? + TEST_PATTERN_MODE_DUALRAMP_RGB : + TEST_PATTERN_MODE_SINGLERAMP_RGB); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* increment for the first ramp for one color gradation + * 1 gradation for 6-bit color is 2^10 + * gradations in 16-bit color + */ + inc_base = (src_bpc - dst_bpc); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + { + CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0, inc_base, + CRTC_TEST_PATTERN_INC1, 0, + CRTC_TEST_PATTERN_HRES, 6, + CRTC_TEST_PATTERN_VRES, 6, + CRTC_TEST_PATTERN_RAMP0_OFFSET, 0); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + { + CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0, inc_base, + CRTC_TEST_PATTERN_INC1, 0, + CRTC_TEST_PATTERN_HRES, 8, + CRTC_TEST_PATTERN_VRES, 6, + CRTC_TEST_PATTERN_RAMP0_OFFSET, 0); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + { + CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0, inc_base, + CRTC_TEST_PATTERN_INC1, inc_base + 2, + CRTC_TEST_PATTERN_HRES, 8, + CRTC_TEST_PATTERN_VRES, 5, + CRTC_TEST_PATTERN_RAMP0_OFFSET, 384 << 6); + } + break; + default: + break; + } + + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, 0); + + /* enable test pattern */ + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_CONTROL, tg110->offsets.crtc, 0); + + CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN, 1, + CRTC_TEST_PATTERN_MODE, mode, + CRTC_TEST_PATTERN_DYNAMIC_RANGE, 0, + CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + break; + case CONTROLLER_DP_TEST_PATTERN_VIDEOMODE: + { + value = 0; + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_CONTROL, tg110->offsets.crtc, value); + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_PARAMETERS, tg110->offsets.crtc, value); + } + break; + default: + break; + } +} + +static bool dce120_arm_vert_intr( + struct timing_generator *tg, + uint8_t width) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t v_blank_start, v_blank_end, h_position, v_position; + + tg->funcs->get_scanoutpos( + tg, + &v_blank_start, + &v_blank_end, + &h_position, + &v_position); + + if (v_blank_start == 0 || v_blank_end == 0) + return false; + + CRTC_REG_SET_2( + CRTC0_CRTC_VERTICAL_INTERRUPT0_POSITION, + CRTC_VERTICAL_INTERRUPT0_LINE_START, v_blank_start, + CRTC_VERTICAL_INTERRUPT0_LINE_END, v_blank_start + width); + + return true; +} + + +static bool dce120_is_tg_enabled(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value, field; + + value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CONTROL, + tg110->offsets.crtc); + field = get_reg_field_value(value, CRTC0_CRTC_CONTROL, + CRTC_CURRENT_MASTER_EN_STATE); + + return field == 1; +} + +static bool dce120_configure_crc(struct timing_generator *tg, + const struct crc_params *params) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Cannot configure crc on a CRTC that is disabled */ + if (!dce120_is_tg_enabled(tg)) + return false; + + /* First, disable CRC before we configure it. */ + dm_write_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC_CNTL, + tg110->offsets.crtc, 0); + + if (!params->enable) + return true; + + /* Program frame boundaries */ + /* Window A x axis start and end. */ + CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWA_X_CONTROL, + CRTC_CRC0_WINDOWA_X_START, params->windowa_x_start, + CRTC_CRC0_WINDOWA_X_END, params->windowa_x_end); + + /* Window A y axis start and end. */ + CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWA_Y_CONTROL, + CRTC_CRC0_WINDOWA_Y_START, params->windowa_y_start, + CRTC_CRC0_WINDOWA_Y_END, params->windowa_y_end); + + /* Window B x axis start and end. */ + CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWB_X_CONTROL, + CRTC_CRC0_WINDOWB_X_START, params->windowb_x_start, + CRTC_CRC0_WINDOWB_X_END, params->windowb_x_end); + + /* Window B y axis start and end. */ + CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWB_Y_CONTROL, + CRTC_CRC0_WINDOWB_Y_START, params->windowb_y_start, + CRTC_CRC0_WINDOWB_Y_END, params->windowb_y_end); + + /* Set crc mode and selection, and enable. Only using CRC0*/ + CRTC_REG_UPDATE_3(CRTC0_CRTC_CRC_CNTL, + CRTC_CRC_EN, params->continuous_mode ? 1 : 0, + CRTC_CRC0_SELECT, params->selection, + CRTC_CRC_EN, 1); + + return true; +} + +static bool dce120_get_crc(struct timing_generator *tg, uint32_t *r_cr, + uint32_t *g_y, uint32_t *b_cb) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value, field; + + value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC_CNTL, + tg110->offsets.crtc); + field = get_reg_field_value(value, CRTC0_CRTC_CRC_CNTL, CRTC_CRC_EN); + + /* Early return if CRC is not enabled for this CRTC */ + if (!field) + return false; + + value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC0_DATA_RG, + tg110->offsets.crtc); + *r_cr = get_reg_field_value(value, CRTC0_CRTC_CRC0_DATA_RG, CRC0_R_CR); + *g_y = get_reg_field_value(value, CRTC0_CRTC_CRC0_DATA_RG, CRC0_G_Y); + + value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC0_DATA_B, + tg110->offsets.crtc); + *b_cb = get_reg_field_value(value, CRTC0_CRTC_CRC0_DATA_B, CRC0_B_CB); + + return true; +} + +static const struct timing_generator_funcs dce120_tg_funcs = { + .validate_timing = dce120_tg_validate_timing, + .program_timing = dce120_tg_program_timing, + .enable_crtc = dce120_timing_generator_enable_crtc, + .disable_crtc = dce110_timing_generator_disable_crtc, + /* used by enable_timing_synchronization. Not need for FPGA */ + .is_counter_moving = dce110_timing_generator_is_counter_moving, + /* never be called */ + .get_position = dce120_timing_generator_get_crtc_position, + .get_frame_count = dce120_timing_generator_get_vblank_counter, + .get_scanoutpos = dce120_timing_generator_get_crtc_scanoutpos, + .set_early_control = dce120_timing_generator_set_early_control, + /* used by enable_timing_synchronization. Not need for FPGA */ + .wait_for_state = dce120_tg_wait_for_state, + .set_blank = dce120_tg_set_blank, + .is_blanked = dce120_tg_is_blanked, + /* never be called */ + .set_colors = dce120_tg_set_colors, + .set_overscan_blank_color = dce120_timing_generator_set_overscan_color_black, + .set_blank_color = dce120_timing_generator_program_blank_color, + .disable_vga = dce120_timing_generator_disable_vga, + .did_triggered_reset_occur = dce120_timing_generator_did_triggered_reset_occur, + .setup_global_swap_lock = dce120_timing_generator_setup_global_swap_lock, + .enable_reset_trigger = dce120_timing_generator_enable_reset_trigger, + .disable_reset_trigger = dce120_timing_generator_disable_reset_trigger, + .tear_down_global_swap_lock = dce120_timing_generator_tear_down_global_swap_lock, + .enable_advanced_request = dce120_timing_generator_enable_advanced_request, + .set_drr = dce120_timing_generator_set_drr, + .get_last_used_drr_vtotal = NULL, + .set_static_screen_control = dce120_timing_generator_set_static_screen_control, + .set_test_pattern = dce120_timing_generator_set_test_pattern, + .arm_vert_intr = dce120_arm_vert_intr, + .is_tg_enabled = dce120_is_tg_enabled, + .configure_crc = dce120_configure_crc, + .get_crc = dce120_get_crc, +}; + + +void dce120_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + tg110->controller_id = CONTROLLER_ID_D0 + instance; + tg110->base.inst = instance; + + tg110->offsets = *offsets; + + tg110->base.funcs = &dce120_tg_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC0_CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC0_CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + /*//CRTC requires a minimum HBLANK = 32 pixels and o + * Minimum HSYNC = 8 pixels*/ + tg110->min_h_blank = 32; + /*DCE12_CRTC_Block_ARch.doc*/ + tg110->min_h_front_porch = 0; + tg110->min_h_back_porch = 0; + + tg110->min_h_sync_width = 4; + tg110->min_v_sync_width = 1; + tg110->min_v_blank = 3; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.h b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.h new file mode 100644 index 000000000..549d70b23 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.h @@ -0,0 +1,40 @@ +/* + * 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_TIMING_GENERATOR_DCE120_H__ +#define __DC_TIMING_GENERATOR_DCE120_H__ + +#include "timing_generator.h" +#include "../include/grph_object_id.h" +#include "dce110/dce110_timing_generator.h" + + +void dce120_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets); + +#endif /* __DC_TIMING_GENERATOR_DCE120_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce60/Makefile b/drivers/gpu/drm/amd/display/dc/dce60/Makefile new file mode 100644 index 000000000..dda596fa1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce60/Makefile @@ -0,0 +1,36 @@ +# +# Copyright 2020 Mauro Rossi <issor.oruam@gmail.com> +# +# 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. +# +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +CFLAGS_AMDDALPATH)/dc/dce60/dce60_resource.o = $(call cc-disable-warning, override-init) + +DCE60 = dce60_timing_generator.o dce60_hw_sequencer.o \ + dce60_resource.o + +AMD_DAL_DCE60 = $(addprefix $(AMDDALPATH)/dc/dce60/,$(DCE60)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE60) + + + diff --git a/drivers/gpu/drm/amd/display/dc/dce60/dce60_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce60/dce60_hw_sequencer.c new file mode 100644 index 000000000..920c7ae29 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce60/dce60_hw_sequencer.c @@ -0,0 +1,432 @@ +/* + * Copyright 2020 Mauro Rossi <issor.oruam@gmail.com> + * + * 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.h" +#include "core_types.h" +#include "dce60_hw_sequencer.h" + +#include "dce/dce_hwseq.h" +#include "dce110/dce110_hw_sequencer.h" +#include "dce100/dce100_hw_sequencer.h" + +/* include DCE6 register header files */ +#include "dce/dce_6_0_d.h" +#include "dce/dce_6_0_sh_mask.h" + +#define DC_LOGGER_INIT() + +/******************************************************************************* + * Private definitions + ******************************************************************************/ + +/***************************PIPE_CONTROL***********************************/ + +/* + * Check if FBC can be enabled + */ +static bool dce60_should_enable_fbc(struct dc *dc, + struct dc_state *context, + uint32_t *pipe_idx) +{ + uint32_t i; + struct pipe_ctx *pipe_ctx = NULL; + struct resource_context *res_ctx = &context->res_ctx; + unsigned int underlay_idx = dc->res_pool->underlay_pipe_index; + + + ASSERT(dc->fbc_compressor); + + /* FBC memory should be allocated */ + if (!dc->ctx->fbc_gpu_addr) + return false; + + /* Only supports single display */ + if (context->stream_count != 1) + return false; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (res_ctx->pipe_ctx[i].stream) { + + pipe_ctx = &res_ctx->pipe_ctx[i]; + + if (!pipe_ctx) + continue; + + /* fbc not applicable on underlay pipe */ + if (pipe_ctx->pipe_idx != underlay_idx) { + *pipe_idx = i; + break; + } + } + } + + if (i == dc->res_pool->pipe_count) + return false; + + if (!pipe_ctx->stream->link) + return false; + + /* Only supports eDP */ + if (pipe_ctx->stream->link->connector_signal != SIGNAL_TYPE_EDP) + return false; + + /* PSR should not be enabled */ + if (pipe_ctx->stream->link->psr_settings.psr_feature_enabled) + return false; + + /* Nothing to compress */ + if (!pipe_ctx->plane_state) + return false; + + /* Only for non-linear tiling */ + if (pipe_ctx->plane_state->tiling_info.gfx8.array_mode == DC_ARRAY_LINEAR_GENERAL) + return false; + + return true; +} + +/* + * Enable FBC + */ +static void dce60_enable_fbc( + struct dc *dc, + struct dc_state *context) +{ + uint32_t pipe_idx = 0; + + if (dce60_should_enable_fbc(dc, context, &pipe_idx)) { + /* Program GRPH COMPRESSED ADDRESS and PITCH */ + struct compr_addr_and_pitch_params params = {0, 0, 0}; + struct compressor *compr = dc->fbc_compressor; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[pipe_idx]; + + params.source_view_width = pipe_ctx->stream->timing.h_addressable; + params.source_view_height = pipe_ctx->stream->timing.v_addressable; + params.inst = pipe_ctx->stream_res.tg->inst; + compr->compr_surface_address.quad_part = dc->ctx->fbc_gpu_addr; + + compr->funcs->surface_address_and_pitch(compr, ¶ms); + compr->funcs->set_fbc_invalidation_triggers(compr, 1); + + compr->funcs->enable_fbc(compr, ¶ms); + } +} + + +/******************************************************************************* + * Front End programming + ******************************************************************************/ + +static void dce60_set_default_colors(struct pipe_ctx *pipe_ctx) +{ + struct default_adjustment default_adjust = { 0 }; + + default_adjust.force_hw_default = false; + default_adjust.in_color_space = pipe_ctx->plane_state->color_space; + default_adjust.out_color_space = pipe_ctx->stream->output_color_space; + default_adjust.csc_adjust_type = GRAPHICS_CSC_ADJUST_TYPE_SW; + default_adjust.surface_pixel_format = pipe_ctx->plane_res.scl_data.format; + + /* display color depth */ + default_adjust.color_depth = + pipe_ctx->stream->timing.display_color_depth; + + /* Lb color depth */ + default_adjust.lb_color_depth = pipe_ctx->plane_res.scl_data.lb_params.depth; + + pipe_ctx->plane_res.xfm->funcs->opp_set_csc_default( + pipe_ctx->plane_res.xfm, &default_adjust); +} + +/******************************************************************************* + * In order to turn on surface we will program + * CRTC + * + * DCE6 has no bottom_pipe and no Blender HW + * We need to set 'blank_target' to false in order to turn on the display + * + * |-----------|------------|---------| + * |curr pipe | set_blank | | + * |Surface |blank_target| CRCT | + * |visibility | argument | | + * |-----------|------------|---------| + * | off | true | blank | + * | on | false | unblank | + * |-----------|------------|---------| + * + ******************************************************************************/ +static void dce60_program_surface_visibility(const struct dc *dc, + struct pipe_ctx *pipe_ctx) +{ + bool blank_target = false; + + /* DCE6 has no bottom_pipe and no Blender HW */ + + if (!pipe_ctx->plane_state->visible) + blank_target = true; + + /* DCE6 skip dce_set_blender_mode() but then proceed to 'unblank' CRTC */ + pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, blank_target); + +} + + +static void dce60_get_surface_visual_confirm_color(const struct pipe_ctx *pipe_ctx, + struct tg_color *color) +{ + uint32_t color_value = MAX_TG_COLOR_VALUE * (4 - pipe_ctx->stream_res.tg->inst) / 4; + + switch (pipe_ctx->plane_res.scl_data.format) { + case PIXEL_FORMAT_ARGB8888: + /* set boarder color to red */ + color->color_r_cr = color_value; + break; + + case PIXEL_FORMAT_ARGB2101010: + /* set boarder color to blue */ + color->color_b_cb = color_value; + break; + case PIXEL_FORMAT_420BPP8: + /* set boarder color to green */ + color->color_g_y = color_value; + break; + case PIXEL_FORMAT_420BPP10: + /* set boarder color to yellow */ + color->color_g_y = color_value; + color->color_r_cr = color_value; + break; + case PIXEL_FORMAT_FP16: + /* set boarder color to white */ + color->color_r_cr = color_value; + color->color_b_cb = color_value; + color->color_g_y = color_value; + break; + default: + break; + } +} + +static void dce60_program_scaler(const struct dc *dc, + const struct pipe_ctx *pipe_ctx) +{ + struct tg_color color = {0}; + + /* DCE6 skips DCN TOFPGA check for transform_set_pixel_storage_depth == NULL */ + + if (dc->debug.visual_confirm == VISUAL_CONFIRM_SURFACE) + dce60_get_surface_visual_confirm_color(pipe_ctx, &color); + else + color_space_to_black_color(dc, + pipe_ctx->stream->output_color_space, + &color); + + pipe_ctx->plane_res.xfm->funcs->transform_set_pixel_storage_depth( + pipe_ctx->plane_res.xfm, + pipe_ctx->plane_res.scl_data.lb_params.depth, + &pipe_ctx->stream->bit_depth_params); + + if (pipe_ctx->stream_res.tg->funcs->set_overscan_blank_color) { + /* + * The way 420 is packed, 2 channels carry Y component, 1 channel + * alternate between Cb and Cr, so both channels need the pixel + * value for Y + */ + if (pipe_ctx->stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) + color.color_r_cr = color.color_g_y; + + pipe_ctx->stream_res.tg->funcs->set_overscan_blank_color( + pipe_ctx->stream_res.tg, + &color); + } + + pipe_ctx->plane_res.xfm->funcs->transform_set_scaler(pipe_ctx->plane_res.xfm, + &pipe_ctx->plane_res.scl_data); +} + +static void +dce60_program_front_end_for_pipe( + struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + struct mem_input *mi = pipe_ctx->plane_res.mi; + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + struct xfm_grph_csc_adjustment adjust; + struct out_csc_color_matrix tbl_entry; + unsigned int i; + struct dce_hwseq *hws = dc->hwseq; + + DC_LOGGER_INIT(); + memset(&tbl_entry, 0, sizeof(tbl_entry)); + + memset(&adjust, 0, sizeof(adjust)); + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; + + dce_enable_fe_clock(dc->hwseq, mi->inst, true); + + dce60_set_default_colors(pipe_ctx); + if (pipe_ctx->stream->csc_color_matrix.enable_adjustment + == true) { + tbl_entry.color_space = + pipe_ctx->stream->output_color_space; + + for (i = 0; i < 12; i++) + tbl_entry.regval[i] = + pipe_ctx->stream->csc_color_matrix.matrix[i]; + + pipe_ctx->plane_res.xfm->funcs->opp_set_csc_adjustment + (pipe_ctx->plane_res.xfm, &tbl_entry); + } + + if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; + + for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) + adjust.temperature_matrix[i] = + pipe_ctx->stream->gamut_remap_matrix.matrix[i]; + } + + pipe_ctx->plane_res.xfm->funcs->transform_set_gamut_remap(pipe_ctx->plane_res.xfm, &adjust); + + pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != 0; + + dce60_program_scaler(dc, pipe_ctx); + + mi->funcs->mem_input_program_surface_config( + mi, + plane_state->format, + &plane_state->tiling_info, + &plane_state->plane_size, + plane_state->rotation, + NULL, + false); + if (mi->funcs->set_blank) + mi->funcs->set_blank(mi, pipe_ctx->plane_state->visible); + + if (dc->config.gpu_vm_support) + mi->funcs->mem_input_program_pte_vm( + pipe_ctx->plane_res.mi, + plane_state->format, + &plane_state->tiling_info, + plane_state->rotation); + + /* Moved programming gamma from dc to hwss */ + if (pipe_ctx->plane_state->update_flags.bits.full_update || + pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || + pipe_ctx->plane_state->update_flags.bits.gamma_change) + hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); + + if (pipe_ctx->plane_state->update_flags.bits.full_update) + hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); + + DC_LOG_SURFACE( + "Pipe:%d %p: addr hi:0x%x, " + "addr low:0x%x, " + "src: %d, %d, %d," + " %d; dst: %d, %d, %d, %d;" + "clip: %d, %d, %d, %d\n", + pipe_ctx->pipe_idx, + (void *) pipe_ctx->plane_state, + pipe_ctx->plane_state->address.grph.addr.high_part, + pipe_ctx->plane_state->address.grph.addr.low_part, + pipe_ctx->plane_state->src_rect.x, + pipe_ctx->plane_state->src_rect.y, + pipe_ctx->plane_state->src_rect.width, + pipe_ctx->plane_state->src_rect.height, + pipe_ctx->plane_state->dst_rect.x, + pipe_ctx->plane_state->dst_rect.y, + pipe_ctx->plane_state->dst_rect.width, + pipe_ctx->plane_state->dst_rect.height, + pipe_ctx->plane_state->clip_rect.x, + pipe_ctx->plane_state->clip_rect.y, + pipe_ctx->plane_state->clip_rect.width, + pipe_ctx->plane_state->clip_rect.height); + + DC_LOG_SURFACE( + "Pipe %d: width, height, x, y\n" + "viewport:%d, %d, %d, %d\n" + "recout: %d, %d, %d, %d\n", + pipe_ctx->pipe_idx, + pipe_ctx->plane_res.scl_data.viewport.width, + pipe_ctx->plane_res.scl_data.viewport.height, + pipe_ctx->plane_res.scl_data.viewport.x, + pipe_ctx->plane_res.scl_data.viewport.y, + pipe_ctx->plane_res.scl_data.recout.width, + pipe_ctx->plane_res.scl_data.recout.height, + pipe_ctx->plane_res.scl_data.recout.x, + pipe_ctx->plane_res.scl_data.recout.y); +} + +static void dce60_apply_ctx_for_surface( + struct dc *dc, + const struct dc_stream_state *stream, + int num_planes, + struct dc_state *context) +{ + int i; + + if (num_planes == 0) + return; + + if (dc->fbc_compressor) + dc->fbc_compressor->funcs->disable_fbc(dc->fbc_compressor); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream != stream) + continue; + + /* Need to allocate mem before program front end for Fiji */ + pipe_ctx->plane_res.mi->funcs->allocate_mem_input( + pipe_ctx->plane_res.mi, + pipe_ctx->stream->timing.h_total, + pipe_ctx->stream->timing.v_total, + pipe_ctx->stream->timing.pix_clk_100hz / 10, + context->stream_count); + + dce60_program_front_end_for_pipe(dc, pipe_ctx); + + dc->hwss.update_plane_addr(dc, pipe_ctx); + + dce60_program_surface_visibility(dc, pipe_ctx); + + } + + if (dc->fbc_compressor) + dce60_enable_fbc(dc, context); +} + +void dce60_hw_sequencer_construct(struct dc *dc) +{ + dce110_hw_sequencer_construct(dc); + + dc->hwseq->funcs.enable_display_power_gating = dce100_enable_display_power_gating; + dc->hwss.apply_ctx_for_surface = dce60_apply_ctx_for_surface; + dc->hwss.cursor_lock = dce60_pipe_control_lock; + dc->hwss.pipe_control_lock = dce60_pipe_control_lock; + dc->hwss.prepare_bandwidth = dce100_prepare_bandwidth; + dc->hwss.optimize_bandwidth = dce100_optimize_bandwidth; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce60/dce60_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce60/dce60_hw_sequencer.h new file mode 100644 index 000000000..f3b2d8b60 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce60/dce60_hw_sequencer.h @@ -0,0 +1,37 @@ +/* + * Copyright 2020 Mauro Rossi <issor.oruam@gmail.com> + * + * 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_HWSS_DCE60_H__ +#define __DC_HWSS_DCE60_H__ + +#include "core_types.h" +#include "hw_sequencer_private.h" + +struct dc; + +void dce60_hw_sequencer_construct(struct dc *dc); + +#endif /* __DC_HWSS_DCE60_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c b/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c new file mode 100644 index 000000000..8db9f7514 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c @@ -0,0 +1,1526 @@ +/* + * Copyright 2020 Mauro Rossi <issor.oruam@gmail.com> + * + * 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 <linux/slab.h> + +#include "dce/dce_6_0_d.h" +#include "dce/dce_6_0_sh_mask.h" + +#include "dm_services.h" + +#include "link_encoder.h" +#include "stream_encoder.h" + +#include "resource.h" +#include "include/irq_service_interface.h" +#include "irq/dce60/irq_service_dce60.h" +#include "dce110/dce110_timing_generator.h" +#include "dce110/dce110_resource.h" +#include "dce60/dce60_timing_generator.h" +#include "dce/dce_mem_input.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_ipp.h" +#include "dce/dce_transform.h" +#include "dce/dce_opp.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_audio.h" +#include "dce/dce_hwseq.h" +#include "dce60/dce60_hw_sequencer.h" +#include "dce100/dce100_resource.h" +#include "dce/dce_panel_cntl.h" + +#include "reg_helper.h" + +#include "dce/dce_dmcu.h" +#include "dce/dce_aux.h" +#include "dce/dce_abm.h" +#include "dce/dce_i2c.h" +/* TODO remove this include */ + +#include "dce60_resource.h" + +#ifndef mmMC_HUB_RDREQ_DMIF_LIMIT +#include "gmc/gmc_6_0_d.h" +#include "gmc/gmc_6_0_sh_mask.h" +#endif + +#ifndef mmDP_DPHY_INTERNAL_CTRL +#define mmDP_DPHY_INTERNAL_CTRL 0x1CDE +#define mmDP0_DP_DPHY_INTERNAL_CTRL 0x1CDE +#define mmDP1_DP_DPHY_INTERNAL_CTRL 0x1FDE +#define mmDP2_DP_DPHY_INTERNAL_CTRL 0x42DE +#define mmDP3_DP_DPHY_INTERNAL_CTRL 0x45DE +#define mmDP4_DP_DPHY_INTERNAL_CTRL 0x48DE +#define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4BDE +#endif + + +#ifndef mmBIOS_SCRATCH_2 + #define mmBIOS_SCRATCH_2 0x05CB + #define mmBIOS_SCRATCH_3 0x05CC + #define mmBIOS_SCRATCH_6 0x05CF +#endif + +#ifndef mmDP_DPHY_FAST_TRAINING + #define mmDP_DPHY_FAST_TRAINING 0x1CCE + #define mmDP0_DP_DPHY_FAST_TRAINING 0x1CCE + #define mmDP1_DP_DPHY_FAST_TRAINING 0x1FCE + #define mmDP2_DP_DPHY_FAST_TRAINING 0x42CE + #define mmDP3_DP_DPHY_FAST_TRAINING 0x45CE + #define mmDP4_DP_DPHY_FAST_TRAINING 0x48CE + #define mmDP5_DP_DPHY_FAST_TRAINING 0x4BCE +#endif + + +#ifndef mmHPD_DC_HPD_CONTROL + #define mmHPD_DC_HPD_CONTROL 0x189A + #define mmHPD0_DC_HPD_CONTROL 0x189A + #define mmHPD1_DC_HPD_CONTROL 0x18A2 + #define mmHPD2_DC_HPD_CONTROL 0x18AA + #define mmHPD3_DC_HPD_CONTROL 0x18B2 + #define mmHPD4_DC_HPD_CONTROL 0x18BA + #define mmHPD5_DC_HPD_CONTROL 0x18C2 +#endif + +#define DCE11_DIG_FE_CNTL 0x4a00 +#define DCE11_DIG_BE_CNTL 0x4a47 +#define DCE11_DP_SEC 0x4ac3 + +static const struct dce110_timing_generator_offsets dce60_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmGRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG0_DPG_PIPE_ARBITRATION_CONTROL3 + - mmDPG_PIPE_ARBITRATION_CONTROL3), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG1_DPG_PIPE_ARBITRATION_CONTROL3 + - mmDPG_PIPE_ARBITRATION_CONTROL3), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG2_DPG_PIPE_ARBITRATION_CONTROL3 + - mmDPG_PIPE_ARBITRATION_CONTROL3), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG3_DPG_PIPE_ARBITRATION_CONTROL3 + - mmDPG_PIPE_ARBITRATION_CONTROL3), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG4_DPG_PIPE_ARBITRATION_CONTROL3 + - mmDPG_PIPE_ARBITRATION_CONTROL3), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG5_DPG_PIPE_ARBITRATION_CONTROL3 + - mmDPG_PIPE_ARBITRATION_CONTROL3), + } +}; + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + +#define ipp_regs(id)\ +[id] = {\ + IPP_COMMON_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2), + ipp_regs(3), + ipp_regs(4), + ipp_regs(5) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_DCE60_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_DCE60_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE60(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2), + transform_regs(3), + transform_regs(4), + transform_regs(5) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_DCE60(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_DCE60(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +#define link_regs(id)\ +[id] = {\ + LE_DCE60_REG_LIST(id)\ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5) +}; + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST_DCE_BASE(id),\ + .AFMT_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2), + stream_enc_regs(3), + stream_enc_regs(4), + stream_enc_regs(5) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE80_100(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE80_100(_MASK) +}; + +static const struct dce_panel_cntl_registers panel_cntl_regs[] = { + { DCE_PANEL_CNTL_REG_LIST() } +}; + +static const struct dce_panel_cntl_shift panel_cntl_shift = { + DCE_PANEL_CNTL_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_panel_cntl_mask panel_cntl_mask = { + DCE_PANEL_CNTL_MASK_SH_LIST(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_60_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_60(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_60(_MASK) +}; + +static const struct dce110_aux_registers_shift aux_shift = { + DCE10_AUX_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce110_aux_registers_mask aux_mask = { + DCE10_AUX_MASK_SH_LIST(_MASK) +}; + +#define aux_engine_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = 0 \ +} + +static const struct dce110_aux_registers aux_engine_regs[] = { + aux_engine_regs(0), + aux_engine_regs(1), + aux_engine_regs(2), + aux_engine_regs(3), + aux_engine_regs(4), + aux_engine_regs(5) +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5), +}; + +static const struct dce_audio_shift audio_shift = { + AUD_DCE60_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_audio_mask audio_mask = { + AUD_DCE60_MASK_SH_LIST(_MASK) +}; + +#define clk_src_regs(id)\ +[id] = {\ + CS_COMMON_REG_LIST_DCE_80(id),\ +} + + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0), + clk_src_regs(1), + clk_src_regs(2) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3, + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 +}; + +static const struct resource_caps res_cap = { + .num_timing_generator = 6, + .num_audio = 6, + .num_stream_encoder = 6, + .num_pll = 2, + .num_ddc = 6, +}; + +static const struct resource_caps res_cap_61 = { + .num_timing_generator = 4, + .num_audio = 6, + .num_stream_encoder = 6, + .num_pll = 3, + .num_ddc = 6, +}; + +static const struct resource_caps res_cap_64 = { + .num_timing_generator = 2, + .num_audio = 2, + .num_stream_encoder = 2, + .num_pll = 2, + .num_ddc = 2, +}; + +static const struct dc_plane_cap plane_cap = { + .type = DC_PLANE_TYPE_DCE_RGB, + + .pixel_format_support = { + .argb8888 = true, + .nv12 = false, + .fp16 = false + }, + + .max_upscale_factor = { + .argb8888 = 16000, + .nv12 = 1, + .fp16 = 1 + }, + + .max_downscale_factor = { + .argb8888 = 250, + .nv12 = 1, + .fp16 = 1 + } +}; + +static const struct dce_dmcu_registers dmcu_regs = { + DMCU_DCE60_REG_LIST() +}; + +static const struct dce_dmcu_shift dmcu_shift = { + DMCU_MASK_SH_LIST_DCE60(__SHIFT) +}; + +static const struct dce_dmcu_mask dmcu_mask = { + DMCU_MASK_SH_LIST_DCE60(_MASK) +}; +static const struct dce_abm_registers abm_regs = { + ABM_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCE110(_MASK) +}; + +#define CTX ctx +#define REG(reg) mm ## reg + +#ifndef mmCC_DC_HDMI_STRAPS +#define mmCC_DC_HDMI_STRAPS 0x1918 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE_MASK 0x40 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE__SHIFT 0x6 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x700 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 +#endif + +static int map_transmitter_id_to_phy_instance( + enum transmitter transmitter) +{ + switch (transmitter) { + case TRANSMITTER_UNIPHY_A: + return 0; + case TRANSMITTER_UNIPHY_B: + return 1; + case TRANSMITTER_UNIPHY_C: + return 2; + case TRANSMITTER_UNIPHY_D: + return 3; + case TRANSMITTER_UNIPHY_E: + return 4; + case TRANSMITTER_UNIPHY_F: + return 5; + case TRANSMITTER_UNIPHY_G: + return 6; + default: + ASSERT(0); + return 0; + } +} + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + REG_GET_2(CC_DC_HDMI_STRAPS, + HDMI_DISABLE, &straps->hdmi_disable, + AUDIO_STREAM_NUMBER, &straps->audio_stream_number); + + REG_GET(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO, &straps->dc_pinstraps_audio); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce60_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static struct timing_generator *dce60_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce60_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static struct output_pixel_processor *dce60_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce60_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} + +static struct dce_aux *dce60_aux_engine_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct aux_engine_dce110 *aux_engine = + kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); + + if (!aux_engine) + return NULL; + + dce110_aux_engine_construct(aux_engine, ctx, inst, + SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, + &aux_engine_regs[inst], + &aux_mask, + &aux_shift, + ctx->dc->caps.extended_aux_timeout_support); + + return &aux_engine->base; +} +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static struct dce_i2c_hw *dce60_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + + dce_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} + +static struct dce_i2c_sw *dce60_i2c_sw_create( + struct dc_context *ctx) +{ + struct dce_i2c_sw *dce_i2c_sw = + kzalloc(sizeof(struct dce_i2c_sw), GFP_KERNEL); + + if (!dce_i2c_sw) + return NULL; + + dce_i2c_sw_construct(dce_i2c_sw, ctx); + + return dce_i2c_sw; +} +static struct stream_encoder *dce60_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_reg = { + HWSEQ_DCE6_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE6_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE6_MASK_SH_LIST(_MASK) +}; + +static struct dce_hwseq *dce60_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce60_stream_encoder_create, + .create_hwseq = dce60_hwseq_create, +}; + +#define mi_inst_regs(id) { \ + MI_DCE6_REG_LIST(id), \ + .MC_HUB_RDREQ_DMIF_LIMIT = mmMC_HUB_RDREQ_DMIF_LIMIT \ +} +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), + mi_inst_regs(3), + mi_inst_regs(4), + mi_inst_regs(5), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE6_MASK_SH_LIST(__SHIFT), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE__SHIFT +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE6_MASK_SH_LIST(_MASK), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE_MASK +}; + +static struct mem_input *dce60_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce60_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + dce_mi->wa.single_head_rdreq_dmif_limit = 2; + return &dce_mi->base; +} + +static void dce60_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce60_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce60_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + transform->prescaler_on = false; + return &transform->base; +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 297000, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true +}; + +static struct link_encoder *dce60_link_encoder_create( + struct dc_context *ctx, + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + int link_regs_id; + + if (!enc110) + return NULL; + + link_regs_id = + map_transmitter_id_to_phy_instance(enc_init_data->transmitter); + + dce60_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[link_regs_id], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + return &enc110->base; +} + +static struct panel_cntl *dce60_panel_cntl_create(const struct panel_cntl_init_data *init_data) +{ + struct dce_panel_cntl *panel_cntl = + kzalloc(sizeof(struct dce_panel_cntl), GFP_KERNEL); + + if (!panel_cntl) + return NULL; + + dce_panel_cntl_construct(panel_cntl, + init_data, + &panel_cntl_regs[init_data->inst], + &panel_cntl_shift, + &panel_cntl_mask); + + return &panel_cntl->base; +} + +static struct clock_source *dce60_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + kfree(clk_src); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static void dce60_clock_source_destroy(struct clock_source **clk_src) +{ + kfree(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + +static struct input_pixel_processor *dce60_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce60_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +static void dce60_resource_destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce60_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + if (pool->base.engines[i] != NULL) + dce110_engine_destroy(&pool->base.engines[i]); + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) { + dce60_clock_source_destroy(&pool->base.clock_sources[i]); + } + } + + if (pool->base.abm != NULL) + dce_abm_destroy(&pool->base.abm); + + if (pool->base.dmcu != NULL) + dce_dmcu_destroy(&pool->base.dmcu); + + if (pool->base.dp_clock_source != NULL) + dce60_clock_source_destroy(&pool->base.dp_clock_source); + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i] != NULL) { + dce_aud_destroy(&pool->base.audios[i]); + } + } + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } +} + +static bool dce60_validate_bandwidth( + struct dc *dc, + struct dc_state *context, + bool fast_validate) +{ + int i; + bool at_least_one_pipe = false; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (context->res_ctx.pipe_ctx[i].stream) + at_least_one_pipe = true; + } + + if (at_least_one_pipe) { + /* TODO implement when needed but for now hardcode max value*/ + context->bw_ctx.bw.dce.dispclk_khz = 681000; + context->bw_ctx.bw.dce.yclk_khz = 250000 * MEMORY_TYPE_MULTIPLIER_CZ; + } else { + context->bw_ctx.bw.dce.dispclk_khz = 0; + context->bw_ctx.bw.dce.yclk_khz = 0; + } + + return true; +} + +static bool dce60_validate_surface_sets( + struct dc_state *context) +{ + int i; + + for (i = 0; i < context->stream_count; i++) { + if (context->stream_status[i].plane_count == 0) + continue; + + if (context->stream_status[i].plane_count > 1) + return false; + + if (context->stream_status[i].plane_states[0]->format + >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + } + + return true; +} + +static enum dc_status dce60_validate_global( + struct dc *dc, + struct dc_state *context) +{ + if (!dce60_validate_surface_sets(context)) + return DC_FAIL_SURFACE_VALIDATE; + + return DC_OK; +} + +static void dce60_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + dce60_resource_destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + +static const struct resource_funcs dce60_res_pool_funcs = { + .destroy = dce60_destroy_resource_pool, + .link_enc_create = dce60_link_encoder_create, + .panel_cntl_create = dce60_panel_cntl_create, + .validate_bandwidth = dce60_validate_bandwidth, + .validate_plane = dce100_validate_plane, + .add_stream_to_ctx = dce100_add_stream_to_ctx, + .validate_global = dce60_validate_global, + .find_first_free_match_stream_enc_for_link = dce100_find_first_free_match_stream_enc_for_link +}; + +static bool dce60_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_bios *bp; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap; + pool->base.funcs = &dce60_res_pool_funcs; + + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = res_cap.num_timing_generator; + pool->base.timing_generator_count = res_cap.num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.max_cursor_size = 64; + dc->caps.dual_link_dvi = true; + dc->caps.extended_aux_timeout_support = false; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clk_src_count = 2; + + } else { + pool->base.dp_clock_source = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clk_src_count = 1; + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce60_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce60_timing_generator_create( + ctx, i, &dce60_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce60_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce60_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce60_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce60_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce60_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce60_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = dce60_i2c_sw_create(ctx); + if (pool->base.sw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create sw i2c!!\n"); + goto res_create_fail; + } + } + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < dc->caps.max_planes; ++i) + dc->caps.planes[i] = plane_cap; + + dc->caps.disable_dp_clk_share = true; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce60_hw_sequencer_construct(dc); + + return true; + +res_create_fail: + dce60_resource_destruct(pool); + return false; +} + +struct resource_pool *dce60_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce60_construct(num_virtual_links, dc, pool)) + return &pool->base; + + kfree(pool); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static bool dce61_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_bios *bp; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap_61; + pool->base.funcs = &dce60_res_pool_funcs; + + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = res_cap_61.num_timing_generator; + pool->base.timing_generator_count = res_cap_61.num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.max_cursor_size = 64; + dc->caps.is_apu = true; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[2] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 3; + + } else { + pool->base.dp_clock_source = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[1] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 2; + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce60_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce60_timing_generator_create( + ctx, i, &dce60_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce60_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce60_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce60_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce60_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce60_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce60_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = dce60_i2c_sw_create(ctx); + if (pool->base.sw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create sw i2c!!\n"); + goto res_create_fail; + } + } + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < dc->caps.max_planes; ++i) + dc->caps.planes[i] = plane_cap; + + dc->caps.disable_dp_clk_share = true; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce60_hw_sequencer_construct(dc); + + return true; + +res_create_fail: + dce60_resource_destruct(pool); + return false; +} + +struct resource_pool *dce61_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce61_construct(num_virtual_links, dc, pool)) + return &pool->base; + + kfree(pool); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static bool dce64_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_bios *bp; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap_64; + pool->base.funcs = &dce60_res_pool_funcs; + + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = res_cap_64.num_timing_generator; + pool->base.timing_generator_count = res_cap_64.num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.max_cursor_size = 64; + dc->caps.is_apu = true; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[1], false); + pool->base.clk_src_count = 2; + + } else { + pool->base.dp_clock_source = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = + dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[1], false); + pool->base.clk_src_count = 1; + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce60_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce60_timing_generator_create( + ctx, i, &dce60_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce60_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce60_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce60_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce60_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce60_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce60_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = dce60_i2c_sw_create(ctx); + if (pool->base.sw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create sw i2c!!\n"); + goto res_create_fail; + } + } + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < dc->caps.max_planes; ++i) + dc->caps.planes[i] = plane_cap; + + dc->caps.disable_dp_clk_share = true; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce60_hw_sequencer_construct(dc); + + return true; + +res_create_fail: + dce60_resource_destruct(pool); + return false; +} + +struct resource_pool *dce64_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce64_construct(num_virtual_links, dc, pool)) + return &pool->base; + + kfree(pool); + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.h b/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.h new file mode 100644 index 000000000..5d653a76b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.h @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Mauro Rossi <issor.oruam@gmail.com> + * + * 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_RESOURCE_DCE60_H__ +#define __DC_RESOURCE_DCE60_H__ + +#include "core_types.h" + +struct dc; +struct resource_pool; + +struct resource_pool *dce60_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +struct resource_pool *dce61_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +struct resource_pool *dce64_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +#endif /* __DC_RESOURCE_DCE60_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce60/dce60_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce60/dce60_timing_generator.c new file mode 100644 index 000000000..c1a85ee37 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce60/dce60_timing_generator.c @@ -0,0 +1,266 @@ +/* + * Copyright 2020 Mauro Rossi <issor.oruam@gmail.com> + * + * 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 DCE6 register header files */ +#include "dce/dce_6_0_d.h" +#include "dce/dce_6_0_sh_mask.h" + +#include "dc_types.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "../dce110/dce110_timing_generator.h" +#include "dce60_timing_generator.h" + +#include "timing_generator.h" + +enum black_color_format { + BLACK_COLOR_FORMAT_RGB_FULLRANGE = 0, /* used as index in array */ + BLACK_COLOR_FORMAT_RGB_LIMITED, + BLACK_COLOR_FORMAT_YUV_TV, + BLACK_COLOR_FORMAT_YUV_CV, + BLACK_COLOR_FORMAT_YUV_SUPER_AA, + + BLACK_COLOR_FORMAT_COUNT +}; + +static const struct dce110_timing_generator_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC1_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC2_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC3_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC4_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC5_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +} +}; + +#define NUMBER_OF_FRAME_TO_WAIT_ON_TRIGGERED_RESET 10 + +#define MAX_H_TOTAL (CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1) +#define MAX_V_TOTAL (CRTC_V_TOTAL__CRTC_V_TOTAL_MASKhw + 1) + +#define CRTC_REG(reg) (reg + tg110->offsets.crtc) +#define DCP_REG(reg) (reg + tg110->offsets.dcp) +#define DMIF_REG(reg) (reg + tg110->offsets.dmif) + +static void program_pix_dur(struct timing_generator *tg, uint32_t pix_clk_100hz) +{ + uint64_t pix_dur; + uint32_t addr = mmDMIF_PG0_DPG_PIPE_ARBITRATION_CONTROL1 + + DCE110TG_FROM_TG(tg)->offsets.dmif; + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (pix_clk_100hz == 0) + return; + + pix_dur = div_u64(10000000000ull, pix_clk_100hz); + + set_reg_field_value( + value, + pix_dur, + DPG_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION); + + dm_write_reg(tg->ctx, addr, value); +} + +static void program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + int vready_offset, + int vstartup_start, + int vupdate_offset, + int vupdate_width, + const enum signal_type signal, + bool use_vbios) +{ + if (!use_vbios) + program_pix_dur(tg, timing->pix_clk_100hz); + + dce110_tg_program_timing(tg, timing, 0, 0, 0, 0, 0, use_vbios); +} + +static void dce60_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_START_LINE_CONTROL); + uint32_t value = dm_read_reg(tg->ctx, addr); + /* DCE6 has CRTC_PREFETCH_EN bit in CRTC_CONTROL register */ + uint32_t addr2 = CRTC_REG(mmCRTC_CONTROL); + uint32_t value2 = dm_read_reg(tg->ctx, addr2); + + /* DCE6 does not support CRTC_LEGACY_REQUESTOR_EN bit + so here is not possible to set bit based on enable argument */ + + if ((timing->v_sync_width + timing->v_front_porch) <= 3) { + set_reg_field_value( + value, + 3, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value2, + 0, + CRTC_CONTROL, + CRTC_PREFETCH_EN); + } else { + set_reg_field_value( + value, + 4, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value2, + 1, + CRTC_CONTROL, + CRTC_PREFETCH_EN); + } + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PROGRESSIVE_START_LINE_EARLY); + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_INTERLACE_START_LINE_EARLY); + + dm_write_reg(tg->ctx, addr, value); + dm_write_reg(tg->ctx, addr2, value2); +} + +static bool dce60_is_tg_enabled(struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + addr = CRTC_REG(mmCRTC_CONTROL); + value = dm_read_reg(tg->ctx, addr); + field = get_reg_field_value(value, CRTC_CONTROL, + CRTC_CURRENT_MASTER_EN_STATE); + return field == 1; +} + +static bool dce60_configure_crc(struct timing_generator *tg, + const struct crc_params *params) +{ + /* Cannot configure crc on a CRTC that is disabled */ + if (!dce60_is_tg_enabled(tg)) + return false; + + /* DCE6 has no CRTC_CRC_CNTL register, nothing to do */ + + return true; +} + +static const struct timing_generator_funcs dce60_tg_funcs = { + .validate_timing = dce110_tg_validate_timing, + .program_timing = program_timing, + .enable_crtc = dce110_timing_generator_enable_crtc, + .disable_crtc = dce110_timing_generator_disable_crtc, + .is_counter_moving = dce110_timing_generator_is_counter_moving, + .get_position = dce110_timing_generator_get_position, + .get_frame_count = dce110_timing_generator_get_vblank_counter, + .get_scanoutpos = dce110_timing_generator_get_crtc_scanoutpos, + .set_early_control = dce110_timing_generator_set_early_control, + .wait_for_state = dce110_tg_wait_for_state, + .set_blank = dce110_tg_set_blank, + .is_blanked = dce110_tg_is_blanked, + .set_colors = dce110_tg_set_colors, + .set_overscan_blank_color = + dce110_timing_generator_set_overscan_color_black, + .set_blank_color = dce110_timing_generator_program_blank_color, + .disable_vga = dce110_timing_generator_disable_vga, + .did_triggered_reset_occur = + dce110_timing_generator_did_triggered_reset_occur, + .setup_global_swap_lock = + dce110_timing_generator_setup_global_swap_lock, + .enable_reset_trigger = dce110_timing_generator_enable_reset_trigger, + .disable_reset_trigger = dce110_timing_generator_disable_reset_trigger, + .tear_down_global_swap_lock = + dce110_timing_generator_tear_down_global_swap_lock, + .set_drr = dce110_timing_generator_set_drr, + .set_static_screen_control = + dce110_timing_generator_set_static_screen_control, + .set_test_pattern = dce110_timing_generator_set_test_pattern, + .arm_vert_intr = dce110_arm_vert_intr, + + /* DCE6.0 overrides */ + .enable_advanced_request = + dce60_timing_generator_enable_advanced_request, + .configure_crc = dce60_configure_crc, + .get_crc = dce110_get_crc, +}; + +void dce60_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + tg110->controller_id = CONTROLLER_ID_D0 + instance; + tg110->base.inst = instance; + tg110->offsets = *offsets; + tg110->derived_offsets = reg_offsets[instance]; + + tg110->base.funcs = &dce60_tg_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + tg110->min_h_blank = 56; + tg110->min_h_front_porch = 4; + tg110->min_h_back_porch = 4; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce60/dce60_timing_generator.h b/drivers/gpu/drm/amd/display/dc/dce60/dce60_timing_generator.h new file mode 100644 index 000000000..81d831233 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce60/dce60_timing_generator.h @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Mauro Rossi <issor.oruam@gmail.com> + * + * 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_TIMING_GENERATOR_DCE60_H__ +#define __DC_TIMING_GENERATOR_DCE60_H__ + +#include "timing_generator.h" +#include "../include/grph_object_id.h" + +/* DCE6.0 implementation inherits from DCE11.0 */ +void dce60_timing_generator_construct( + struct dce110_timing_generator *tg, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets); + +#endif /* __DC_TIMING_GENERATOR_DCE60_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce80/Makefile b/drivers/gpu/drm/amd/display/dc/dce80/Makefile new file mode 100644 index 000000000..0a9d1a350 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/Makefile @@ -0,0 +1,36 @@ +# +# Copyright 2017 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. +# +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +CFLAGS_$(AMDDALPATH)/dc/dce80/dce80_resource.o = $(call cc-disable-warning, override-init) + +DCE80 = dce80_timing_generator.o dce80_hw_sequencer.o \ + dce80_resource.o + +AMD_DAL_DCE80 = $(addprefix $(AMDDALPATH)/dc/dce80/,$(DCE80)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE80) + + + diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.c new file mode 100644 index 000000000..d2ceebdbd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.c @@ -0,0 +1,54 @@ +/* + * Copyright 2015 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.h" +#include "core_types.h" +#include "dce80_hw_sequencer.h" + +#include "dce/dce_hwseq.h" +#include "dce110/dce110_hw_sequencer.h" +#include "dce100/dce100_hw_sequencer.h" + +/* include DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +/******************************************************************************* + * Private definitions + ******************************************************************************/ + +/***************************PIPE_CONTROL***********************************/ + +void dce80_hw_sequencer_construct(struct dc *dc) +{ + dce110_hw_sequencer_construct(dc); + + dc->hwseq->funcs.enable_display_power_gating = dce100_enable_display_power_gating; + dc->hwss.pipe_control_lock = dce_pipe_control_lock; + dc->hwss.prepare_bandwidth = dce100_prepare_bandwidth; + dc->hwss.optimize_bandwidth = dce100_optimize_bandwidth; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.h new file mode 100644 index 000000000..e43af832d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_hw_sequencer.h @@ -0,0 +1,37 @@ +/* +* 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_HWSS_DCE80_H__ +#define __DC_HWSS_DCE80_H__ + +#include "core_types.h" +#include "hw_sequencer_private.h" + +struct dc; + +void dce80_hw_sequencer_construct(struct dc *dc); + +#endif /* __DC_HWSS_DCE80_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c new file mode 100644 index 000000000..5825e6f41 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c @@ -0,0 +1,1538 @@ +/* + * 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 "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dm_services.h" + +#include "link_encoder.h" +#include "stream_encoder.h" + +#include "resource.h" +#include "include/irq_service_interface.h" +#include "irq/dce80/irq_service_dce80.h" +#include "dce110/dce110_timing_generator.h" +#include "dce110/dce110_resource.h" +#include "dce80/dce80_timing_generator.h" +#include "dce/dce_mem_input.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_ipp.h" +#include "dce/dce_transform.h" +#include "dce/dce_opp.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_audio.h" +#include "dce/dce_hwseq.h" +#include "dce80/dce80_hw_sequencer.h" +#include "dce100/dce100_resource.h" +#include "dce/dce_panel_cntl.h" + +#include "reg_helper.h" + +#include "dce/dce_dmcu.h" +#include "dce/dce_aux.h" +#include "dce/dce_abm.h" +#include "dce/dce_i2c.h" +/* TODO remove this include */ + +#include "dce80_resource.h" + +#ifndef mmMC_HUB_RDREQ_DMIF_LIMIT +#include "gmc/gmc_7_1_d.h" +#include "gmc/gmc_7_1_sh_mask.h" +#endif + +#ifndef mmDP_DPHY_INTERNAL_CTRL +#define mmDP_DPHY_INTERNAL_CTRL 0x1CDE +#define mmDP0_DP_DPHY_INTERNAL_CTRL 0x1CDE +#define mmDP1_DP_DPHY_INTERNAL_CTRL 0x1FDE +#define mmDP2_DP_DPHY_INTERNAL_CTRL 0x42DE +#define mmDP3_DP_DPHY_INTERNAL_CTRL 0x45DE +#define mmDP4_DP_DPHY_INTERNAL_CTRL 0x48DE +#define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4BDE +#define mmDP6_DP_DPHY_INTERNAL_CTRL 0x4EDE +#endif + + +#ifndef mmBIOS_SCRATCH_2 + #define mmBIOS_SCRATCH_2 0x05CB + #define mmBIOS_SCRATCH_3 0x05CC + #define mmBIOS_SCRATCH_6 0x05CF +#endif + +#ifndef mmDP_DPHY_FAST_TRAINING + #define mmDP_DPHY_FAST_TRAINING 0x1CCE + #define mmDP0_DP_DPHY_FAST_TRAINING 0x1CCE + #define mmDP1_DP_DPHY_FAST_TRAINING 0x1FCE + #define mmDP2_DP_DPHY_FAST_TRAINING 0x42CE + #define mmDP3_DP_DPHY_FAST_TRAINING 0x45CE + #define mmDP4_DP_DPHY_FAST_TRAINING 0x48CE + #define mmDP5_DP_DPHY_FAST_TRAINING 0x4BCE + #define mmDP6_DP_DPHY_FAST_TRAINING 0x4ECE +#endif + + +#ifndef mmHPD_DC_HPD_CONTROL + #define mmHPD_DC_HPD_CONTROL 0x189A + #define mmHPD0_DC_HPD_CONTROL 0x189A + #define mmHPD1_DC_HPD_CONTROL 0x18A2 + #define mmHPD2_DC_HPD_CONTROL 0x18AA + #define mmHPD3_DC_HPD_CONTROL 0x18B2 + #define mmHPD4_DC_HPD_CONTROL 0x18BA + #define mmHPD5_DC_HPD_CONTROL 0x18C2 +#endif + +#define DCE11_DIG_FE_CNTL 0x4a00 +#define DCE11_DIG_BE_CNTL 0x4a47 +#define DCE11_DP_SEC 0x4ac3 + +static const struct dce110_timing_generator_offsets dce80_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmGRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG1_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG2_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG3_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG4_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG5_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + } +}; + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + +#define ipp_regs(id)\ +[id] = {\ + IPP_COMMON_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2), + ipp_regs(3), + ipp_regs(4), + ipp_regs(5) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE80(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2), + transform_regs(3), + transform_regs(4), + transform_regs(5) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_DCE80(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_DCE80(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + +#define link_regs(id)\ +[id] = {\ + LE_DCE80_REG_LIST(id)\ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST_DCE_BASE(id),\ + .AFMT_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2), + stream_enc_regs(3), + stream_enc_regs(4), + stream_enc_regs(5), + stream_enc_regs(6) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE80_100(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE80_100(_MASK) +}; + +static const struct dce_panel_cntl_registers panel_cntl_regs[] = { + { DCE_PANEL_CNTL_REG_LIST() } +}; + +static const struct dce_panel_cntl_shift panel_cntl_shift = { + DCE_PANEL_CNTL_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_panel_cntl_mask panel_cntl_mask = { + DCE_PANEL_CNTL_MASK_SH_LIST(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_80_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_80(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_80(_MASK) +}; + +static const struct dce110_aux_registers_shift aux_shift = { + DCE10_AUX_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce110_aux_registers_mask aux_mask = { + DCE10_AUX_MASK_SH_LIST(_MASK) +}; + +#define aux_engine_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = 0 \ +} + +static const struct dce110_aux_registers aux_engine_regs[] = { + aux_engine_regs(0), + aux_engine_regs(1), + aux_engine_regs(2), + aux_engine_regs(3), + aux_engine_regs(4), + aux_engine_regs(5) +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5), + audio_regs(6), +}; + +static const struct dce_audio_shift audio_shift = { + AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_audio_mask audio_mask = { + AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +#define clk_src_regs(id)\ +[id] = {\ + CS_COMMON_REG_LIST_DCE_80(id),\ +} + + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0), + clk_src_regs(1), + clk_src_regs(2) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3, + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 +}; + +static const struct resource_caps res_cap = { + .num_timing_generator = 6, + .num_audio = 6, + .num_stream_encoder = 6, + .num_pll = 3, + .num_ddc = 6, +}; + +static const struct resource_caps res_cap_81 = { + .num_timing_generator = 4, + .num_audio = 7, + .num_stream_encoder = 7, + .num_pll = 3, + .num_ddc = 6, +}; + +static const struct resource_caps res_cap_83 = { + .num_timing_generator = 2, + .num_audio = 6, + .num_stream_encoder = 6, + .num_pll = 2, + .num_ddc = 2, +}; + +static const struct dc_plane_cap plane_cap = { + .type = DC_PLANE_TYPE_DCE_RGB, + + .pixel_format_support = { + .argb8888 = true, + .nv12 = false, + .fp16 = true + }, + + .max_upscale_factor = { + .argb8888 = 16000, + .nv12 = 1, + .fp16 = 1 + }, + + .max_downscale_factor = { + .argb8888 = 250, + .nv12 = 1, + .fp16 = 1 + } +}; + +static const struct dce_dmcu_registers dmcu_regs = { + DMCU_DCE80_REG_LIST() +}; + +static const struct dce_dmcu_shift dmcu_shift = { + DMCU_MASK_SH_LIST_DCE80(__SHIFT) +}; + +static const struct dce_dmcu_mask dmcu_mask = { + DMCU_MASK_SH_LIST_DCE80(_MASK) +}; +static const struct dce_abm_registers abm_regs = { + ABM_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCE110(_MASK) +}; + +#define CTX ctx +#define REG(reg) mm ## reg + +#ifndef mmCC_DC_HDMI_STRAPS +#define mmCC_DC_HDMI_STRAPS 0x1918 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE_MASK 0x40 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE__SHIFT 0x6 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x700 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 +#endif + +static int map_transmitter_id_to_phy_instance( + enum transmitter transmitter) +{ + switch (transmitter) { + case TRANSMITTER_UNIPHY_A: + return 0; + case TRANSMITTER_UNIPHY_B: + return 1; + case TRANSMITTER_UNIPHY_C: + return 2; + case TRANSMITTER_UNIPHY_D: + return 3; + case TRANSMITTER_UNIPHY_E: + return 4; + case TRANSMITTER_UNIPHY_F: + return 5; + case TRANSMITTER_UNIPHY_G: + return 6; + default: + ASSERT(0); + return 0; + } +} + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + REG_GET_2(CC_DC_HDMI_STRAPS, + HDMI_DISABLE, &straps->hdmi_disable, + AUDIO_STREAM_NUMBER, &straps->audio_stream_number); + + REG_GET(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO, &straps->dc_pinstraps_audio); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static struct timing_generator *dce80_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce80_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static struct output_pixel_processor *dce80_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} + +static struct dce_aux *dce80_aux_engine_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct aux_engine_dce110 *aux_engine = + kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); + + if (!aux_engine) + return NULL; + + dce110_aux_engine_construct(aux_engine, ctx, inst, + SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, + &aux_engine_regs[inst], + &aux_mask, + &aux_shift, + ctx->dc->caps.extended_aux_timeout_support); + + return &aux_engine->base; +} +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static struct dce_i2c_hw *dce80_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + + dce_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} + +static struct dce_i2c_sw *dce80_i2c_sw_create( + struct dc_context *ctx) +{ + struct dce_i2c_sw *dce_i2c_sw = + kzalloc(sizeof(struct dce_i2c_sw), GFP_KERNEL); + + if (!dce_i2c_sw) + return NULL; + + dce_i2c_sw_construct(dce_i2c_sw, ctx); + + return dce_i2c_sw; +} +static struct stream_encoder *dce80_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_reg = { + HWSEQ_DCE8_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE8_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE8_MASK_SH_LIST(_MASK) +}; + +static struct dce_hwseq *dce80_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce80_stream_encoder_create, + .create_hwseq = dce80_hwseq_create, +}; + +#define mi_inst_regs(id) { \ + MI_DCE8_REG_LIST(id), \ + .MC_HUB_RDREQ_DMIF_LIMIT = mmMC_HUB_RDREQ_DMIF_LIMIT \ +} +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), + mi_inst_regs(3), + mi_inst_regs(4), + mi_inst_regs(5), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE8_MASK_SH_LIST(__SHIFT), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE__SHIFT +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE8_MASK_SH_LIST(_MASK), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE_MASK +}; + +static struct mem_input *dce80_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + dce_mi->wa.single_head_rdreq_dmif_limit = 2; + return &dce_mi->base; +} + +static void dce80_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce80_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + transform->prescaler_on = false; + return &transform->base; +} + +static const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 297000, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true +}; + +static struct link_encoder *dce80_link_encoder_create( + struct dc_context *ctx, + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + int link_regs_id; + + if (!enc110) + return NULL; + + link_regs_id = + map_transmitter_id_to_phy_instance(enc_init_data->transmitter); + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[link_regs_id], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + return &enc110->base; +} + +static struct panel_cntl *dce80_panel_cntl_create(const struct panel_cntl_init_data *init_data) +{ + struct dce_panel_cntl *panel_cntl = + kzalloc(sizeof(struct dce_panel_cntl), GFP_KERNEL); + + if (!panel_cntl) + return NULL; + + dce_panel_cntl_construct(panel_cntl, + init_data, + &panel_cntl_regs[init_data->inst], + &panel_cntl_shift, + &panel_cntl_mask); + + return &panel_cntl->base; +} + +static struct clock_source *dce80_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + kfree(clk_src); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static void dce80_clock_source_destroy(struct clock_source **clk_src) +{ + kfree(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + +static struct input_pixel_processor *dce80_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +static void dce80_resource_destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce80_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + if (pool->base.engines[i] != NULL) + dce110_engine_destroy(&pool->base.engines[i]); + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) { + dce80_clock_source_destroy(&pool->base.clock_sources[i]); + } + } + + if (pool->base.abm != NULL) + dce_abm_destroy(&pool->base.abm); + + if (pool->base.dmcu != NULL) + dce_dmcu_destroy(&pool->base.dmcu); + + if (pool->base.dp_clock_source != NULL) + dce80_clock_source_destroy(&pool->base.dp_clock_source); + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i] != NULL) { + dce_aud_destroy(&pool->base.audios[i]); + } + } + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } +} + +static bool dce80_validate_bandwidth( + struct dc *dc, + struct dc_state *context, + bool fast_validate) +{ + int i; + bool at_least_one_pipe = false; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (context->res_ctx.pipe_ctx[i].stream) + at_least_one_pipe = true; + } + + if (at_least_one_pipe) { + /* TODO implement when needed but for now hardcode max value*/ + context->bw_ctx.bw.dce.dispclk_khz = 681000; + context->bw_ctx.bw.dce.yclk_khz = 250000 * MEMORY_TYPE_MULTIPLIER_CZ; + } else { + context->bw_ctx.bw.dce.dispclk_khz = 0; + context->bw_ctx.bw.dce.yclk_khz = 0; + } + + return true; +} + +static bool dce80_validate_surface_sets( + struct dc_state *context) +{ + int i; + + for (i = 0; i < context->stream_count; i++) { + if (context->stream_status[i].plane_count == 0) + continue; + + if (context->stream_status[i].plane_count > 1) + return false; + + if (context->stream_status[i].plane_states[0]->format + >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + } + + return true; +} + +static enum dc_status dce80_validate_global( + struct dc *dc, + struct dc_state *context) +{ + if (!dce80_validate_surface_sets(context)) + return DC_FAIL_SURFACE_VALIDATE; + + return DC_OK; +} + +static void dce80_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + dce80_resource_destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + +static const struct resource_funcs dce80_res_pool_funcs = { + .destroy = dce80_destroy_resource_pool, + .link_enc_create = dce80_link_encoder_create, + .panel_cntl_create = dce80_panel_cntl_create, + .validate_bandwidth = dce80_validate_bandwidth, + .validate_plane = dce100_validate_plane, + .add_stream_to_ctx = dce100_add_stream_to_ctx, + .validate_global = dce80_validate_global, + .find_first_free_match_stream_enc_for_link = dce100_find_first_free_match_stream_enc_for_link +}; + +static bool dce80_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_bios *bp; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap; + pool->base.funcs = &dce80_res_pool_funcs; + + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = res_cap.num_timing_generator; + pool->base.timing_generator_count = res_cap.num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.i2c_speed_in_khz_hdcp = 40; + dc->caps.max_cursor_size = 128; + dc->caps.min_horizontal_blanking_period = 80; + dc->caps.dual_link_dvi = true; + dc->caps.extended_aux_timeout_support = false; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[2] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 3; + + } else { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[1] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 2; + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce80_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce80_timing_generator_create( + ctx, i, &dce80_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce80_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce80_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce80_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce80_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce80_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce80_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = dce80_i2c_sw_create(ctx); + if (pool->base.sw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create sw i2c!!\n"); + goto res_create_fail; + } + } + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < dc->caps.max_planes; ++i) + dc->caps.planes[i] = plane_cap; + + dc->caps.disable_dp_clk_share = true; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce80_hw_sequencer_construct(dc); + + return true; + +res_create_fail: + dce80_resource_destruct(pool); + return false; +} + +struct resource_pool *dce80_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce80_construct(num_virtual_links, dc, pool)) + return &pool->base; + + kfree(pool); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static bool dce81_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_bios *bp; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap_81; + pool->base.funcs = &dce80_res_pool_funcs; + + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = res_cap_81.num_timing_generator; + pool->base.timing_generator_count = res_cap_81.num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.i2c_speed_in_khz_hdcp = 40; + dc->caps.max_cursor_size = 128; + dc->caps.min_horizontal_blanking_period = 80; + dc->caps.is_apu = true; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[2] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 3; + + } else { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[1] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 2; + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce80_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce80_timing_generator_create( + ctx, i, &dce80_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce80_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce80_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce80_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce80_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce80_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce80_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = dce80_i2c_sw_create(ctx); + if (pool->base.sw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create sw i2c!!\n"); + goto res_create_fail; + } + } + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < dc->caps.max_planes; ++i) + dc->caps.planes[i] = plane_cap; + + dc->caps.disable_dp_clk_share = true; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce80_hw_sequencer_construct(dc); + + return true; + +res_create_fail: + dce80_resource_destruct(pool); + return false; +} + +struct resource_pool *dce81_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce81_construct(num_virtual_links, dc, pool)) + return &pool->base; + + kfree(pool); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static bool dce83_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct dc_bios *bp; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap_83; + pool->base.funcs = &dce80_res_pool_funcs; + + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + pool->base.pipe_count = res_cap_83.num_timing_generator; + pool->base.timing_generator_count = res_cap_83.num_timing_generator; + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 40; + dc->caps.i2c_speed_in_khz_hdcp = 40; + dc->caps.max_cursor_size = 128; + dc->caps.min_horizontal_blanking_period = 80; + dc->caps.is_apu = true; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[1], false); + pool->base.clk_src_count = 2; + + } else { + pool->base.dp_clock_source = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = + dce80_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[1], false); + pool->base.clk_src_count = 1; + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce80_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce80_timing_generator_create( + ctx, i, &dce80_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce80_mem_input_create(ctx, i); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce80_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce80_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce80_opp_create(ctx, i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce80_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce80_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = dce80_i2c_sw_create(ctx); + if (pool->base.sw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create sw i2c!!\n"); + goto res_create_fail; + } + } + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < dc->caps.max_planes; ++i) + dc->caps.planes[i] = plane_cap; + + dc->caps.disable_dp_clk_share = true; + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + dce80_hw_sequencer_construct(dc); + + return true; + +res_create_fail: + dce80_resource_destruct(pool); + return false; +} + +struct resource_pool *dce83_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce83_construct(num_virtual_links, dc, pool)) + return &pool->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.h b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.h new file mode 100644 index 000000000..eff31ab83 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.h @@ -0,0 +1,47 @@ +/* +* 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_RESOURCE_DCE80_H__ +#define __DC_RESOURCE_DCE80_H__ + +#include "core_types.h" + +struct dc; +struct resource_pool; + +struct resource_pool *dce80_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +struct resource_pool *dce81_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +struct resource_pool *dce83_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +#endif /* __DC_RESOURCE_DCE80_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c new file mode 100644 index 000000000..b8fd43dc0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.c @@ -0,0 +1,248 @@ +/* + * 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 DCE8 register header files */ +#include "dce/dce_8_0_d.h" +#include "dce/dce_8_0_sh_mask.h" + +#include "dc_types.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "../dce110/dce110_timing_generator.h" +#include "dce80_timing_generator.h" + +#include "timing_generator.h" + +enum black_color_format { + BLACK_COLOR_FORMAT_RGB_FULLRANGE = 0, /* used as index in array */ + BLACK_COLOR_FORMAT_RGB_LIMITED, + BLACK_COLOR_FORMAT_YUV_TV, + BLACK_COLOR_FORMAT_YUV_CV, + BLACK_COLOR_FORMAT_YUV_SUPER_AA, + + BLACK_COLOR_FORMAT_COUNT +}; + +static const struct dce110_timing_generator_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC1_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC2_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC3_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC4_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .crtc = (mmCRTC5_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), + .dcp = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +} +}; + +#define NUMBER_OF_FRAME_TO_WAIT_ON_TRIGGERED_RESET 10 + +#define MAX_H_TOTAL (CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1) +#define MAX_V_TOTAL (CRTC_V_TOTAL__CRTC_V_TOTAL_MASKhw + 1) + +#define CRTC_REG(reg) (reg + tg110->offsets.crtc) +#define DCP_REG(reg) (reg + tg110->offsets.dcp) +#define DMIF_REG(reg) (reg + tg110->offsets.dmif) + +static void program_pix_dur(struct timing_generator *tg, uint32_t pix_clk_100hz) +{ + uint64_t pix_dur; + uint32_t addr = mmDMIF_PG0_DPG_PIPE_ARBITRATION_CONTROL1 + + DCE110TG_FROM_TG(tg)->offsets.dmif; + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (pix_clk_100hz == 0) + return; + + pix_dur = div_u64(10000000000ull, pix_clk_100hz); + + set_reg_field_value( + value, + pix_dur, + DPG_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION); + + dm_write_reg(tg->ctx, addr, value); +} + +static void program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + int vready_offset, + int vstartup_start, + int vupdate_offset, + int vupdate_width, + const enum signal_type signal, + bool use_vbios) +{ + if (!use_vbios) + program_pix_dur(tg, timing->pix_clk_100hz); + + dce110_tg_program_timing(tg, timing, 0, 0, 0, 0, 0, use_vbios); +} + +static void dce80_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_START_LINE_CONTROL); + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (enable) { + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } else { + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } + + if ((timing->v_sync_width + timing->v_front_porch) <= 3) { + set_reg_field_value( + value, + 3, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } else { + set_reg_field_value( + value, + 4, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PROGRESSIVE_START_LINE_EARLY); + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_INTERLACE_START_LINE_EARLY); + + dm_write_reg(tg->ctx, addr, value); +} + +static const struct timing_generator_funcs dce80_tg_funcs = { + .validate_timing = dce110_tg_validate_timing, + .program_timing = program_timing, + .enable_crtc = dce110_timing_generator_enable_crtc, + .disable_crtc = dce110_timing_generator_disable_crtc, + .is_counter_moving = dce110_timing_generator_is_counter_moving, + .get_position = dce110_timing_generator_get_position, + .get_frame_count = dce110_timing_generator_get_vblank_counter, + .get_scanoutpos = dce110_timing_generator_get_crtc_scanoutpos, + .set_early_control = dce110_timing_generator_set_early_control, + .wait_for_state = dce110_tg_wait_for_state, + .set_blank = dce110_tg_set_blank, + .is_blanked = dce110_tg_is_blanked, + .set_colors = dce110_tg_set_colors, + .set_overscan_blank_color = + dce110_timing_generator_set_overscan_color_black, + .set_blank_color = dce110_timing_generator_program_blank_color, + .disable_vga = dce110_timing_generator_disable_vga, + .did_triggered_reset_occur = + dce110_timing_generator_did_triggered_reset_occur, + .setup_global_swap_lock = + dce110_timing_generator_setup_global_swap_lock, + .enable_reset_trigger = dce110_timing_generator_enable_reset_trigger, + .disable_reset_trigger = dce110_timing_generator_disable_reset_trigger, + .tear_down_global_swap_lock = + dce110_timing_generator_tear_down_global_swap_lock, + .set_drr = dce110_timing_generator_set_drr, + .get_last_used_drr_vtotal = NULL, + .set_static_screen_control = + dce110_timing_generator_set_static_screen_control, + .set_test_pattern = dce110_timing_generator_set_test_pattern, + .arm_vert_intr = dce110_arm_vert_intr, + + /* DCE8.0 overrides */ + .enable_advanced_request = + dce80_timing_generator_enable_advanced_request, + .configure_crc = dce110_configure_crc, + .get_crc = dce110_get_crc, +}; + +void dce80_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + tg110->controller_id = CONTROLLER_ID_D0 + instance; + tg110->base.inst = instance; + tg110->offsets = *offsets; + tg110->derived_offsets = reg_offsets[instance]; + + tg110->base.funcs = &dce80_tg_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + tg110->min_h_blank = 56; + tg110->min_h_front_porch = 4; + tg110->min_h_back_porch = 4; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.h b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.h new file mode 100644 index 000000000..8ff1b06bc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_timing_generator.h @@ -0,0 +1,39 @@ +/* + * 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_TIMING_GENERATOR_DCE80_H__ +#define __DC_TIMING_GENERATOR_DCE80_H__ + +#include "timing_generator.h" +#include "../include/grph_object_id.h" + +/* DCE8.0 implementation inherits from DCE11.0 */ +void dce80_timing_generator_construct( + struct dce110_timing_generator *tg, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets); + +#endif /* __DC_TIMING_GENERATOR_DCE80_H__ */ |