From 0babc74552e1f38dce837f82f72ecf0d189e171c Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Mon, 6 Mar 2017 14:36:02 -0500 Subject: [PATCH 0276/4131] drm/amd/display: Add DCE12 core support Signed-off-by: Harry Wentland Signed-off-by: Alex Deucher --- .../amd/display/dc/dce120/dce120_hw_sequencer.c | 197 ++++ .../amd/display/dc/dce120/dce120_hw_sequencer.h | 36 + drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp.c | 58 + drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp.h | 62 ++ .../drm/amd/display/dc/dce120/dce120_ipp_cursor.c | 202 ++++ .../drm/amd/display/dc/dce120/dce120_ipp_gamma.c | 167 +++ .../drm/amd/display/dc/dce120/dce120_mem_input.c | 340 ++++++ .../drm/amd/display/dc/dce120/dce120_mem_input.h | 37 + .../drm/amd/display/dc/dce120/dce120_resource.c | 1099 +++++++++++++++++++ .../drm/amd/display/dc/dce120/dce120_resource.h | 39 + .../display/dc/dce120/dce120_timing_generator.c | 1109 ++++++++++++++++++++ .../display/dc/dce120/dce120_timing_generator.h | 41 + 12 files changed, 3387 insertions(+) create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp.c create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp.h create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp_cursor.c create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp_gamma.c create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_mem_input.c create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_mem_input.h create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.h create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c create mode 100644 drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.h 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 0000000..f5ffd8f6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c @@ -0,0 +1,197 @@ +/* + * 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_dc.h" +#include "core_types.h" +#include "dce120_hw_sequencer.h" + +#include "dce110/dce110_hw_sequencer.h" + +/* include DCE12.0 register header files */ +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" +#include "reg_helper.h" + +struct dce120_hw_seq_reg_offsets { + uint32_t crtc; +}; + +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 + ******************************************************************************/ +#if 0 +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 core_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; +} + +bool dce120_hw_sequencer_construct(struct core_dc *dc) +{ + /* All registers used by dce11.2 match those in dce11 in offset and + * structure + */ + dce110_hw_sequencer_construct(dc); + dc->hwss.enable_display_power_gating = dce120_enable_display_power_gating; + + return true; +} + 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 0000000..3402413 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h @@ -0,0 +1,36 @@ +/* +* 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" + +struct core_dc; + +bool dce120_hw_sequencer_construct(struct core_dc *dc); + +#endif /* __DC_HWSS_DCE112_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp.c new file mode 100644 index 0000000..f450569 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp.c @@ -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 + * + */ + +#include "dm_services.h" +#include "include/logger_interface.h" + +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" + +#include "dce120_ipp.h" + +static const struct ipp_funcs funcs = { + .ipp_cursor_set_attributes = dce120_ipp_cursor_set_attributes, + .ipp_cursor_set_position = dce120_ipp_cursor_set_position, + .ipp_program_prescale = dce120_ipp_program_prescale, + .ipp_program_input_lut = dce120_ipp_program_input_lut, + .ipp_set_degamma = dce120_ipp_set_degamma, +}; + +bool dce120_ipp_construct( + struct dce110_ipp *ipp, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_ipp_reg_offsets *offset) +{ + if (!dce110_ipp_construct(ipp, ctx, inst, offset)) { + ASSERT_CRITICAL(false); + return false; + } + + ipp->base.funcs = &funcs; + + return true; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp.h b/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp.h new file mode 100644 index 0000000..4b326bc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp.h @@ -0,0 +1,62 @@ +/* + * 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_IPP_DCE120_H__ +#define __DC_IPP_DCE120_H__ + +#include "ipp.h" +#include "../dce110/dce110_ipp.h" + + +bool dce120_ipp_construct( + struct dce110_ipp *ipp, + struct dc_context *ctx, + enum controller_id id, + const struct dce110_ipp_reg_offsets *offset); + +/* CURSOR RELATED */ +void dce120_ipp_cursor_set_position( + struct input_pixel_processor *ipp, + const struct dc_cursor_position *position, + const struct dc_cursor_mi_param *param); + +bool dce120_ipp_cursor_set_attributes( + struct input_pixel_processor *ipp, + const struct dc_cursor_attributes *attributes); + +/* DEGAMMA RELATED */ +bool dce120_ipp_set_degamma( + struct input_pixel_processor *ipp, + enum ipp_degamma_mode mode); + +void dce120_ipp_program_prescale( + struct input_pixel_processor *ipp, + struct ipp_prescale_params *params); + +void dce120_ipp_program_input_lut( + struct input_pixel_processor *ipp, + const struct dc_gamma *gamma); + +#endif /*__DC_IPP_DCE120_H__*/ diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp_cursor.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp_cursor.c new file mode 100644 index 0000000..d520b5d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp_cursor.c @@ -0,0 +1,202 @@ +/* + * 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 "include/logger_interface.h" + +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" + +#include "../dce110/dce110_ipp.h" + + +#define DCP_REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_soc15(ipp110->base.ctx, ipp110->offsets.dcp_offset, reg_name, n, __VA_ARGS__) + +#define DCP_REG_SET_N(reg_name, n, ...) \ + generic_reg_set_soc15(ipp110->base.ctx, ipp110->offsets.dcp_offset, reg_name, n, __VA_ARGS__) + +#define DCP_REG_UPDATE(reg, field, val) \ + DCP_REG_UPDATE_N(reg, 1, FD(reg##__##field), val) + +#define DCP_REG_UPDATE_2(reg, field1, val1, field2, val2) \ + DCP_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define DCP_REG_UPDATE_3(reg, field1, val1, field2, val2, field3, val3) \ + DCP_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +#define DCP_REG_SET(reg, field, val) \ + DCP_REG_SET_N(reg, 1, FD(reg##__##field), val) + +#define DCP_REG_SET_2(reg, field1, val1, field2, val2) \ + DCP_REG_SET_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define DCP_REG_SET_3(reg, field1, val1, field2, val2, field3, val3) \ + DCP_REG_SET_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +/* TODO: DAL3 does not implement cursor memory control + * MCIF_MEM_CONTROL, DMIF_CURSOR_MEM_CONTROL + */ +static void lock( + struct dce110_ipp *ipp110, bool lock) +{ + DCP_REG_UPDATE(DCP0_CUR_UPDATE, CURSOR_UPDATE_LOCK, lock); +} + +static bool program_control( + struct dce110_ipp *ipp110, + enum dc_cursor_color_format color_format, + bool enable_magnification, + bool inverse_transparent_clamping) +{ + uint32_t mode = 0; + + switch (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: + return false; + } + + DCP_REG_UPDATE_3( + DCP0_CUR_CONTROL, + CURSOR_MODE, mode, + CURSOR_2X_MAGNIFY, enable_magnification, + CUR_INV_TRANS_CLAMP, inverse_transparent_clamping); + + if (color_format == CURSOR_MODE_MONO) { + DCP_REG_SET_3( + DCP0_CUR_COLOR1, + CUR_COLOR1_BLUE, 0, + CUR_COLOR1_GREEN, 0, + CUR_COLOR1_RED, 0); + + DCP_REG_SET_3( + DCP0_CUR_COLOR2, + CUR_COLOR2_BLUE, 0xff, + CUR_COLOR2_GREEN, 0xff, + CUR_COLOR2_RED, 0xff); + } + return true; +} + +static void program_address( + struct dce110_ipp *ipp110, + PHYSICAL_ADDRESS_LOC 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 + */ + + DCP_REG_SET( + DCP0_CUR_SURFACE_ADDRESS_HIGH, + CURSOR_SURFACE_ADDRESS_HIGH, address.high_part); + + DCP_REG_SET( + DCP0_CUR_SURFACE_ADDRESS, + CURSOR_SURFACE_ADDRESS, address.low_part); +} + +void dce120_ipp_cursor_set_position( + struct input_pixel_processor *ipp, + const struct dc_cursor_position *position, + const struct dc_cursor_mi_param *param) +{ + struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); + + /* lock cursor registers */ + lock(ipp110, true); + + /* Flag passed in structure differentiates cursor enable/disable. */ + /* Update if it differs from cached state. */ + DCP_REG_UPDATE(DCP0_CUR_CONTROL, CURSOR_EN, position->enable); + + DCP_REG_SET_2( + DCP0_CUR_POSITION, + CURSOR_X_POSITION, position->x, + CURSOR_Y_POSITION, position->y); + + if (position->hot_spot_enable) + DCP_REG_SET_2( + DCP0_CUR_HOT_SPOT, + CURSOR_HOT_SPOT_X, position->x_hotspot, + CURSOR_HOT_SPOT_Y, position->y_hotspot); + + /* unlock cursor registers */ + lock(ipp110, false); +} + +bool dce120_ipp_cursor_set_attributes( + struct input_pixel_processor *ipp, + const struct dc_cursor_attributes *attributes) +{ + struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); + /* Lock cursor registers */ + lock(ipp110, true); + + /* Program cursor control */ + program_control( + ipp110, + attributes->color_format, + attributes->attribute_flags.bits.ENABLE_MAGNIFICATION, + attributes->attribute_flags.bits.INVERSE_TRANSPARENT_CLAMPING); + + /* Program hot spot coordinates */ + DCP_REG_SET_2( + DCP0_CUR_HOT_SPOT, + CURSOR_HOT_SPOT_X, attributes->x_hot, + CURSOR_HOT_SPOT_Y, attributes->y_hot); + + /* + * Program cursor size -- NOTE: HW spec specifies that HW register + * stores size as (height - 1, width - 1) + */ + DCP_REG_SET_2( + DCP0_CUR_SIZE, + CURSOR_WIDTH, attributes->width-1, + CURSOR_HEIGHT, attributes->height-1); + + /* Program cursor surface address */ + program_address(ipp110, attributes->address); + + /* Unlock Cursor registers. */ + lock(ipp110, false); + + return true; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp_gamma.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp_gamma.c new file mode 100644 index 0000000..7aa5a49 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_ipp_gamma.c @@ -0,0 +1,167 @@ +/* + * 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 "include/logger_interface.h" +#include "include/fixed31_32.h" +#include "basics/conversion.h" + +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" + +#include "../dce110/dce110_ipp.h" + +#define DCP_REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_soc15(ipp110->base.ctx, ipp110->offsets.dcp_offset, reg_name, n, __VA_ARGS__) + +#define DCP_REG_SET_N(reg_name, n, ...) \ + generic_reg_set_soc15(ipp110->base.ctx, ipp110->offsets.dcp_offset, reg_name, n, __VA_ARGS__) + +#define DCP_REG_UPDATE(reg, field, val) \ + DCP_REG_UPDATE_N(reg, 1, FD(reg##__##field), val) + +#define DCP_REG_UPDATE_2(reg, field1, val1, field2, val2) \ + DCP_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define DCP_REG_UPDATE_3(reg, field1, val1, field2, val2, field3, val3) \ + DCP_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +#define DCP_REG_SET(reg, field, val) \ + DCP_REG_SET_N(reg, 1, FD(reg##__##field), val) + +#define DCP_REG_SET_2(reg, field1, val1, field2, val2) \ + DCP_REG_SET_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define DCP_REG_SET_3(reg, field1, val1, field2, val2, field3, val3) \ + DCP_REG_SET_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + + +bool dce120_ipp_set_degamma( + struct input_pixel_processor *ipp, + enum ipp_degamma_mode mode) +{ + struct dce110_ipp *ipp110 = TO_DCE110_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); + + DCP_REG_SET_3( + DCP0_DEGAMMA_CONTROL, + GRPH_DEGAMMA_MODE, degamma_type, + CURSOR_DEGAMMA_MODE, degamma_type, + CURSOR2_DEGAMMA_MODE, degamma_type); + + return true; +} + +void dce120_ipp_program_prescale( + struct input_pixel_processor *ipp, + struct ipp_prescale_params *params) +{ + struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); + + /* set to bypass mode first before change */ + DCP_REG_UPDATE( + DCP0_PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_BYPASS, + 1); + + DCP_REG_SET_2( + DCP0_PRESCALE_VALUES_GRPH_R, + GRPH_PRESCALE_SCALE_R, params->scale, + GRPH_PRESCALE_BIAS_R, params->bias); + + DCP_REG_SET_2( + DCP0_PRESCALE_VALUES_GRPH_G, + GRPH_PRESCALE_SCALE_G, params->scale, + GRPH_PRESCALE_BIAS_G, params->bias); + + DCP_REG_SET_2( + DCP0_PRESCALE_VALUES_GRPH_B, + GRPH_PRESCALE_SCALE_B, params->scale, + GRPH_PRESCALE_BIAS_B, params->bias); + + if (params->mode != IPP_PRESCALE_MODE_BYPASS) { + DCP_REG_UPDATE(DCP0_PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_BYPASS, 0); + + /* If prescale is in use, then legacy lut should be bypassed */ + DCP_REG_UPDATE(DCP0_INPUT_GAMMA_CONTROL, + GRPH_INPUT_GAMMA_MODE, 1); + } +} + +static void dce120_helper_select_lut(struct dce110_ipp *ipp110) +{ + /* enable all */ + DCP_REG_SET( + DCP0_DC_LUT_WRITE_EN_MASK, + DC_LUT_WRITE_EN_MASK, + 0x7); + + /* 256 entry mode */ + DCP_REG_UPDATE(DCP0_DC_LUT_RW_MODE, DC_LUT_RW_MODE, 0); + + /* LUT-256, unsigned, integer, new u0.12 format */ + DCP_REG_SET_3( + DCP0_DC_LUT_CONTROL, + DC_LUT_DATA_R_FORMAT, 3, + DC_LUT_DATA_G_FORMAT, 3, + DC_LUT_DATA_B_FORMAT, 3); + + /* start from index 0 */ + DCP_REG_SET( + DCP0_DC_LUT_RW_INDEX, + DC_LUT_RW_INDEX, + 0); +} + +void dce120_ipp_program_input_lut( + struct input_pixel_processor *ipp, + const struct dc_gamma *gamma) +{ + int i; + struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); + + /* power on LUT memory */ + DCP_REG_SET(DCFE0_DCFE_MEM_PWR_CTRL, DCP_LUT_MEM_PWR_DIS, 1); + + dce120_helper_select_lut(ipp110); + + for (i = 0; i < INPUT_LUT_ENTRIES; i++) { + DCP_REG_SET(DCP0_DC_LUT_SEQ_COLOR, DC_LUT_SEQ_COLOR, gamma->red[i]); + DCP_REG_SET(DCP0_DC_LUT_SEQ_COLOR, DC_LUT_SEQ_COLOR, gamma->green[i]); + DCP_REG_SET(DCP0_DC_LUT_SEQ_COLOR, DC_LUT_SEQ_COLOR, gamma->blue[i]); + } + + /* power off LUT memory */ + DCP_REG_SET(DCFE0_DCFE_MEM_PWR_CTRL, DCP_LUT_MEM_PWR_DIS, 0); + + /* bypass prescale, enable legacy LUT */ + DCP_REG_UPDATE(DCP0_PRESCALE_GRPH_CONTROL, GRPH_PRESCALE_BYPASS, 1); + DCP_REG_UPDATE(DCP0_INPUT_GAMMA_CONTROL, GRPH_INPUT_GAMMA_MODE, 0); +} diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_mem_input.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_mem_input.c new file mode 100644 index 0000000..c067721 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_mem_input.c @@ -0,0 +1,340 @@ +/* + * 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 "dce120_mem_input.h" + + +#include "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" + +#define GENERAL_REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_soc15(mem_input110->base.ctx, 0, reg_name, n, __VA_ARGS__) + +#define GENERAL_REG_UPDATE(reg, field, val) \ + GENERAL_REG_UPDATE_N(reg, 1, FD(reg##__##field), val) + +#define GENERAL_REG_UPDATE_2(reg, field1, val1, field2, val2) \ + GENERAL_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + + + +#define DCP_REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_soc15(mem_input110->base.ctx, mem_input110->offsets.dcp, reg_name, n, __VA_ARGS__) + +#define DCP_REG_SET_N(reg_name, n, ...) \ + generic_reg_set_soc15(mem_input110->base.ctx, mem_input110->offsets.dcp, reg_name, n, __VA_ARGS__) + +#define DCP_REG_UPDATE(reg, field, val) \ + DCP_REG_UPDATE_N(reg, 1, FD(reg##__##field), val) + +#define DCP_REG_UPDATE_2(reg, field1, val1, field2, val2) \ + DCP_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define DCP_REG_UPDATE_3(reg, field1, val1, field2, val2, field3, val3) \ + DCP_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +#define DCP_REG_SET(reg, field, val) \ + DCP_REG_SET_N(reg, 1, FD(reg##__##field), val) + +#define DCP_REG_SET_2(reg, field1, val1, field2, val2) \ + DCP_REG_SET_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define DCP_REG_SET_3(reg, field1, val1, field2, val2, field3, val3) \ + DCP_REG_SET_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + + + +#define DMIF_REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_soc15(mem_input110->base.ctx, mem_input110->offsets.dmif, reg_name, n, __VA_ARGS__) + +#define DMIF_REG_SET_N(reg_name, n, ...) \ + generic_reg_set_soc15(mem_input110->base.ctx, mem_input110->offsets.dmif, reg_name, n, __VA_ARGS__) + +#define DMIF_REG_UPDATE(reg, field, val) \ + DMIF_REG_UPDATE_N(reg, 1, FD(reg##__##field), val) + +#define DMIF_REG_UPDATE_2(reg, field1, val1, field2, val2) \ + DMIF_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define DMIF_REG_UPDATE_3(reg, field1, val1, field2, val2, field3, val3) \ + DMIF_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +#define DMIF_REG_SET(reg, field, val) \ + DMIF_REG_SET_N(reg, 1, FD(reg##__##field), val) + +#define DMIF_REG_SET_2(reg, field1, val1, field2, val2) \ + DMIF_REG_SET_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define DMIF_REG_SET_3(reg, field1, val1, field2, val2, field3, val3) \ + DMIF_REG_SET_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + + + +#define PIPE_REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_soc15(mem_input110->base.ctx, mem_input110->offsets.pipe, reg_name, n, __VA_ARGS__) + +#define PIPE_REG_SET_N(reg_name, n, ...) \ + generic_reg_set_soc15(mem_input110->base.ctx, mem_input110->offsets.pipe, reg_name, n, __VA_ARGS__) + +#define PIPE_REG_UPDATE(reg, field, val) \ + PIPE_REG_UPDATE_N(reg, 1, FD(reg##__##field), val) + +#define PIPE_REG_UPDATE_2(reg, field1, val1, field2, val2) \ + PIPE_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define PIPE_REG_UPDATE_3(reg, field1, val1, field2, val2, field3, val3) \ + PIPE_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +#define PIPE_REG_SET(reg, field, val) \ + PIPE_REG_SET_N(reg, 1, FD(reg##__##field), val) + +#define PIPE_REG_SET_2(reg, field1, val1, field2, val2) \ + PIPE_REG_SET_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define PIPE_REG_SET_3(reg, field1, val1, field2, val2, field3, val3) \ + PIPE_REG_SET_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + + + +static void program_sec_addr( + struct dce110_mem_input *mem_input110, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t temp; + + /*high register MUST be programmed first*/ + temp = address.high_part & + DCP0_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH__GRPH_SECONDARY_SURFACE_ADDRESS_HIGH_MASK; + + DCP_REG_SET( + DCP0_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, + GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, + temp); + + temp = address.low_part >> + DCP0_GRPH_SECONDARY_SURFACE_ADDRESS__GRPH_SECONDARY_SURFACE_ADDRESS__SHIFT; + + DCP_REG_SET_2( + DCP0_GRPH_SECONDARY_SURFACE_ADDRESS, + GRPH_SECONDARY_SURFACE_ADDRESS, temp, + GRPH_SECONDARY_DFQ_ENABLE, 0); +} + +static void program_pri_addr( + struct dce110_mem_input *mem_input110, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t temp; + + /*high register MUST be programmed first*/ + temp = address.high_part & + DCP0_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_MASK; + + DCP_REG_SET( + DCP0_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, + temp); + + temp = address.low_part >> + DCP0_GRPH_PRIMARY_SURFACE_ADDRESS__GRPH_PRIMARY_SURFACE_ADDRESS__SHIFT; + + DCP_REG_SET( + DCP0_GRPH_PRIMARY_SURFACE_ADDRESS, + GRPH_PRIMARY_SURFACE_ADDRESS, + temp); +} + + +static bool mem_input_is_flip_pending(struct mem_input *mem_input) +{ + struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input); + uint32_t value; + + value = dm_read_reg_soc15(mem_input110->base.ctx, + mmDCP0_GRPH_UPDATE, mem_input110->offsets.dcp); + + if (get_reg_field_value(value, DCP0_GRPH_UPDATE, + GRPH_SURFACE_UPDATE_PENDING)) + return true; + + mem_input->current_address = mem_input->request_address; + return false; +} + +static bool mem_input_program_surface_flip_and_addr( + struct mem_input *mem_input, + const struct dc_plane_address *address, + bool flip_immediate) +{ + struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input); + + /* TODO: Figure out if two modes are needed: + * non-XDMA Mode: GRPH_SURFACE_UPDATE_IMMEDIATE_EN = 1 + * XDMA Mode: GRPH_SURFACE_UPDATE_H_RETRACE_EN = 1 + */ + DCP_REG_UPDATE(DCP0_GRPH_UPDATE, + GRPH_UPDATE_LOCK, 1); + + if (flip_immediate) { + DCP_REG_UPDATE_2( + DCP0_GRPH_FLIP_CONTROL, + GRPH_SURFACE_UPDATE_IMMEDIATE_EN, 0, + GRPH_SURFACE_UPDATE_H_RETRACE_EN, 1); + } else { + DCP_REG_UPDATE_2( + DCP0_GRPH_FLIP_CONTROL, + GRPH_SURFACE_UPDATE_IMMEDIATE_EN, 0, + GRPH_SURFACE_UPDATE_H_RETRACE_EN, 0); + } + + switch (address->type) { + case PLN_ADDR_TYPE_GRAPHICS: + if (address->grph.addr.quad_part == 0) + break; + program_pri_addr(mem_input110, 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(mem_input110, address->grph_stereo.left_addr); + program_sec_addr(mem_input110, 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; + + DCP_REG_UPDATE(DCP0_GRPH_UPDATE, + GRPH_UPDATE_LOCK, 0); + + return true; +} + +static void mem_input_update_dchub(struct mem_input *mi, + struct dchub_init_data *dh_data) +{ + struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mi); + /* 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*/ + GENERAL_REG_UPDATE_2( + DCHUB_FB_LOCATION, + FB_TOP, 0, + FB_BASE, 0x0FFFF); + + GENERAL_REG_UPDATE( + DCHUB_AGP_BASE, + AGP_BASE, dh_data->zfb_phys_addr_base >> 22); + + GENERAL_REG_UPDATE( + DCHUB_AGP_BOT, + AGP_BOT, dh_data->zfb_mc_base_addr >> 22); + + GENERAL_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)*/ + GENERAL_REG_UPDATE( + DCHUB_AGP_BASE, + AGP_BASE, dh_data->zfb_phys_addr_base >> 22); + + GENERAL_REG_UPDATE( + DCHUB_AGP_BOT, + AGP_BOT, dh_data->zfb_mc_base_addr >> 22); + + GENERAL_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)*/ + GENERAL_REG_UPDATE( + DCHUB_AGP_BASE, + AGP_BASE, 0); + + GENERAL_REG_UPDATE( + DCHUB_AGP_BOT, + AGP_BOT, 0x03FFFF); + + GENERAL_REG_UPDATE( + DCHUB_AGP_TOP, + AGP_TOP, 0); + break; + default: + break; + } + + dh_data->dchub_initialzied = true; + dh_data->dchub_info_valid = false; +} + +static struct mem_input_funcs dce120_mem_input_funcs = { + .mem_input_program_display_marks = dce_mem_input_program_display_marks, + .allocate_mem_input = dce_mem_input_allocate_dmif, + .free_mem_input = dce_mem_input_free_dmif, + .mem_input_program_surface_flip_and_addr = + mem_input_program_surface_flip_and_addr, + .mem_input_program_pte_vm = dce_mem_input_program_pte_vm, + .mem_input_program_surface_config = + dce_mem_input_program_surface_config, + .mem_input_is_flip_pending = mem_input_is_flip_pending, + .mem_input_update_dchub = mem_input_update_dchub +}; + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +bool dce120_mem_input_construct( + struct dce110_mem_input *mem_input110, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_mem_input_reg_offsets *offsets) +{ + /* supported stutter method + * STUTTER_MODE_ENHANCED + * STUTTER_MODE_QUAD_DMIF_BUFFER + * STUTTER_MODE_WATERMARK_NBP_STATE + */ + + if (!dce110_mem_input_construct(mem_input110, ctx, inst, offsets)) + return false; + + mem_input110->base.funcs = &dce120_mem_input_funcs; + mem_input110->offsets = *offsets; + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_mem_input.h b/drivers/gpu/drm/amd/display/dc/dce120/dce120_mem_input.h new file mode 100644 index 0000000..379fd72 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_mem_input.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_MEM_INPUT_DCE120_H__ +#define __DC_MEM_INPUT_DCE120_H__ + +#include "mem_input.h" +#include "dce110/dce110_mem_input.h" + +bool dce120_mem_input_construct( + struct dce110_mem_input *mem_input110, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_mem_input_reg_offsets *offsets); + +#endif 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 0000000..9a1984b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c @@ -0,0 +1,1099 @@ +/* +* 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_clocks.h" +#include "dce120_ipp.h" +#include "dce110/dce110_mem_input.h" +#include "dce120/dce120_mem_input.h" + +#include "dce110/dce110_hw_sequencer.h" +#include "dce120/dce120_hw_sequencer.h" +#include "dce/dce_transform.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 "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.h" +#include "vega10/NBIO/nbio_6_1_offset.h" +#include "reg_helper.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 + +/* macros to expend register list macro defined in HW object header file + * end *********************/ + + +static const struct dce_disp_clk_registers disp_clk_regs = { + CLK_COMMON_REG_LIST_DCE_BASE() +}; + +static const struct dce_disp_clk_shift disp_clk_shift = { + CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_disp_clk_mask disp_clk_mask = { + CLK_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 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) +}; + +#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 audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5) +}; + +#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_aduio_mask audio_mask = { + DCE120_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) +}; + +struct output_pixel_processor *dce120_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + dm_alloc(sizeof(struct dce110_opp)); + + if (!opp) + return NULL; + + if (dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask)) + return &opp->base; + + BREAK_TO_DEBUGGER(); + dm_free(opp); + return NULL; +} + +static const struct dce110_ipp_reg_offsets dce120_ipp_reg_offsets[] = { + { + .dcp_offset = (mmDCP0_CUR_CONTROL - mmDCP0_CUR_CONTROL), + }, + { + .dcp_offset = (mmDCP1_CUR_CONTROL - mmDCP0_CUR_CONTROL), + }, + { + .dcp_offset = (mmDCP2_CUR_CONTROL - mmDCP0_CUR_CONTROL), + }, + { + .dcp_offset = (mmDCP3_CUR_CONTROL - mmDCP0_CUR_CONTROL), + }, + { + .dcp_offset = (mmDCP4_CUR_CONTROL - mmDCP0_CUR_CONTROL), + }, + { + .dcp_offset = (mmDCP5_CUR_CONTROL - mmDCP0_CUR_CONTROL), + } +}; + +static const struct dce110_mem_input_reg_offsets dce120_mi_reg_offsets[] = { + { + .dcp = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif = (mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL + - mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE0_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + }, + { + .dcp = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif = (mmDMIF_PG1_DPG_WATERMARK_MASK_CONTROL + - mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE1_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + }, + { + .dcp = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif = (mmDMIF_PG2_DPG_WATERMARK_MASK_CONTROL + - mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE2_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + }, + { + .dcp = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif = (mmDMIF_PG3_DPG_WATERMARK_MASK_CONTROL + - mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE3_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + }, + { + .dcp = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif = (mmDMIF_PG4_DPG_WATERMARK_MASK_CONTROL + - mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE4_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + }, + { + .dcp = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif = (mmDMIF_PG5_DPG_WATERMARK_MASK_CONTROL + - mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE5_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + } +}; + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 + NBIO_BASE(mmBIOS_SCRATCH_6_BASE_IDX) +}; + +static const struct resource_caps res_cap = { + .num_timing_generator = 3, + .num_audio = 7, + .num_stream_encoder = 6, + .num_pll = 6, +}; + +static const struct dc_debug debug_defaults = { + .disable_clock_gate = true, +}; + +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 = + dm_alloc(sizeof(struct dce110_clk_src)); + + 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; + } + + BREAK_TO_DEBUGGER(); + return NULL; +} + +void dce120_clock_source_destroy(struct clock_source **clk_src) +{ + dm_free(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + + +bool dce120_hw_sequencer_create(struct core_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 = + dm_alloc(sizeof(struct dce110_timing_generator)); + + if (!tg110) + return NULL; + + if (dce120_timing_generator_construct(tg110, ctx, instance, offsets)) + return &tg110->base; + + BREAK_TO_DEBUGGER(); + dm_free(tg110); + return NULL; +} + +static void dce120_ipp_destroy(struct input_pixel_processor **ipp) +{ + dm_free(TO_DCE110_IPP(*ipp)); + *ipp = NULL; +} + +static void dce120_transform_destroy(struct transform **xfm) +{ + dm_free(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static void 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) + dce120_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + dm_free(TO_DCE110_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) { + dm_free(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[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) + dm_free(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); + + if (pool->base.display_clock != NULL) + dce_disp_clk_destroy(&pool->base.display_clock); +} + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + /* TODO: Registers are missing */ + /*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 const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 600000, + .ycbcr420_supported = true, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_HBR3_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true, + .flags.bits.IS_TPS4_CAPABLE = true, + .flags.bits.IS_YCBCR_CAPABLE = true +}; + +struct link_encoder *dce120_link_encoder_create( + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + dm_alloc(sizeof(struct dce110_link_encoder)); + + if (!enc110) + return NULL; + + if (dce110_link_encoder_construct( + enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[enc_init_data->transmitter], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source])) { + + return &enc110->base; + } + + BREAK_TO_DEBUGGER(); + dm_free(enc110); + return NULL; +} + +static struct input_pixel_processor *dce120_ipp_create( + struct dc_context *ctx, + uint32_t inst, + const struct dce110_ipp_reg_offsets *offset) +{ + struct dce110_ipp *ipp = dm_alloc(sizeof(struct dce110_ipp)); + + if (!ipp) + return NULL; + + if (dce120_ipp_construct(ipp, ctx, inst, offset)) + return &ipp->base; + + BREAK_TO_DEBUGGER(); + dm_free(ipp); + return NULL; +} + +static struct stream_encoder *dce120_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + dm_alloc(sizeof(struct dce110_stream_encoder)); + + if (!enc110) + return NULL; + + if (dce110_stream_encoder_construct( + enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], &se_shift, &se_mask)) + return &enc110->base; + + BREAK_TO_DEBUGGER(); + dm_free(enc110); + return NULL; +} + +#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_DCE112_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) +}; + +static struct dce_hwseq *dce120_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = dm_alloc(sizeof(struct dce_hwseq)); + + 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 = dce120_stream_encoder_create, + .create_hwseq = dce120_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, + const struct dce110_mem_input_reg_offsets *offset) +{ + struct dce110_mem_input *mem_input110 = + dm_alloc(sizeof(struct dce110_mem_input)); + + if (!mem_input110) + return NULL; + + if (dce120_mem_input_construct(mem_input110, ctx, inst, offset)) { + struct mem_input *mi = &mem_input110->base; + + mi->regs = &mi_regs[inst]; + mi->shifts = &mi_shifts; + mi->masks = &mi_masks; + return mi; + } + + BREAK_TO_DEBUGGER(); + dm_free(mem_input110); + return NULL; +} + +static struct transform *dce120_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + dm_alloc(sizeof(struct dce_transform)); + + if (!transform) + return NULL; + + if (dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask)) { + transform->lb_memory_size = 0x1404; /*5124*/ + return &transform->base; + } + + BREAK_TO_DEBUGGER(); + dm_free(transform); + return NULL; +} + +static void dce120_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + destruct(dce110_pool); + dm_free(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, + .validate_with_context = dce112_validate_with_context, + .validate_guaranteed = dce112_validate_guaranteed, + .validate_bandwidth = dce112_validate_bandwidth +}; + +static void bw_calcs_data_update_from_pplib(struct core_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; + + /*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 + */ + 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_memg_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_memg_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_memg_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_memg_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 bool construct( + uint8_t num_virtual_links, + struct core_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 = &res_cap; + pool->base.funcs = &dce120_res_pool_funcs; + + /* TODO: Fill more data from GreenlandAsicCapability.cpp */ + pool->base.pipe_count = 6; + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + + dc->public.caps.max_downscale_ratio = 200; + dc->public.caps.i2c_speed_in_khz = 100; + dc->public.caps.max_cursor_size = 128; + dc->public.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.display_clock = dce120_disp_clk_create(ctx, + &disp_clk_regs, + &disp_clk_shift, + &disp_clk_mask); + if (pool->base.display_clock == NULL) { + dm_error("DC: failed to create display clock!\n"); + BREAK_TO_DEBUGGER(); + goto disp_clk_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; + } + + { + #if defined(CONFIG_DRM_AMD_DC_DCE12_0) + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce120_create(&init_data); + if (!pool->base.irqs) + goto irqs_create_fail; + #endif + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = + dce120_timing_generator_create( + ctx, + i, + &dce120_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto controller_create_fail; + } + + pool->base.mis[i] = dce120_mem_input_create(ctx, + i, &dce120_mi_reg_offsets[i]); + + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto controller_create_fail; + } + + pool->base.ipps[i] = dce120_ipp_create(ctx, i, + &dce120_ipp_reg_offsets[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[i] = 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[i] = dce120_opp_create( + ctx, + i); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + } + } + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + if (!dce120_hw_sequencer_create(dc)) + goto controller_create_fail; + + 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: +disp_clk_create_fail: +clk_src_create_fail: +res_create_fail: + + destruct(pool); + + return false; +} + +struct resource_pool *dce120_create_resource_pool( + uint8_t num_virtual_links, + struct core_dc *dc) +{ + struct dce110_resource_pool *pool = + dm_alloc(sizeof(struct dce110_resource_pool)); + + if (!pool) + return NULL; + + if (construct(num_virtual_links, dc, pool)) + return &pool->base; + + 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 0000000..038c78d --- /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 core_dc; +struct resource_pool; + +struct resource_pool *dce120_create_resource_pool( + uint8_t num_virtual_links, + struct core_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 0000000..d7e787b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c @@ -0,0 +1,1109 @@ +/* + * 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 "vega10/DC/dce_12_0_offset.h" +#include "vega10/DC/dce_12_0_sh_mask.h" +#include "vega10/soc15ip.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 */ +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; +} + +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 */ +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; +} + +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 */ +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 */ +void dce120_timing_generator_get_crtc_positions( + struct timing_generator *tg, + int32_t *h_position, + int32_t *v_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); + + *h_position = get_reg_field_value( + value, CRTC0_CRTC_STATUS_POSITION, CRTC_HORZ_COUNT); + + *v_position = get_reg_field_value( + value, CRTC0_CRTC_STATUS_POSITION, CRTC_VERT_COUNT); +} + +/* wait until TG is in beginning of vertical blank region */ +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 */ +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*/ +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 */ +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 */ +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 */ +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 */ +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 */ +void dce120_timing_generator_disable_vga(struct timing_generator *tg) +{ + uint32_t addr = 0; + 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: + addr = mmD1VGA_CONTROL; + offset = 0; + break; + case CONTROLLER_ID_D1: + addr = mmD2VGA_CONTROL; + offset = mmD2VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D2: + addr = mmD3VGA_CONTROL; + offset = mmD3VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D3: + addr = mmD4VGA_CONTROL; + offset = mmD4VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D4: + addr = mmD1VGA_CONTROL; + offset = mmD1VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D5: + addr = mmD6VGA_CONTROL; + 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 */ +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); + + 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 */ +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 */ +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 + */ +} + +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_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_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_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK, + 0); + } +} + +uint32_t dce120_timing_generator_get_crtc_scanoutpos( + struct timing_generator *tg, + uint32_t *vbl, + uint32_t *position) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + *vbl = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_V_BLANK_START_END, + tg110->offsets.crtc); + + *position = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_STATUS_POSITION, + tg110->offsets.crtc); + + return 0; +} + +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); + + + if (enable) { + set_reg_field_value( + value, + 0, + CRTC0_CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } else { + set_reg_field_value( + value, + 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) + set_reg_field_value( + value, + 10, + CRTC0_CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + else + 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); +} + +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); +} + +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); +} + +void dce120_tg_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + bool use_vbios) +{ + if (use_vbios) + dce110_timing_generator_program_timing_generator(tg, timing); + else + dce120_timing_generator_program_blanking(tg, timing); +} + +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; +} + +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, 0); + + 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); + +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; + } +} + +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 value) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE_2(CRTC0_CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK, value, + CRTC_STATIC_SCREEN_FRAME_COUNT, 2); +} + +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 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_positions, + .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, + .set_static_screen_control = dce120_timing_generator_set_static_screen_control, + .set_test_pattern = dce120_timing_generator_set_test_pattern +}; + + +bool dce120_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + if (!tg110) + return false; + + 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 = 8; + tg110->min_v_sync_width = 1; + tg110->min_v_blank = 3; + + return true; +} 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 0000000..243c0a3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.h @@ -0,0 +1,41 @@ +/* + * 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 "../include/hw_sequencer_types.h" +#include "dce110/dce110_timing_generator.h" + + +bool 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__ */ -- 2.7.4