Commit b8fdfcc6 authored by Harry Wentland's avatar Harry Wentland Committed by Alex Deucher

drm/amd/display: Add DCE12 core support

Signed-off-by: default avatarHarry Wentland <harry.wentland@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 9a48d684
/*
* 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;
}
/*
* 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__ */
/*
* 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;
}
/*
* 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__*/
/*
* 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;
}
/*
* 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);
}
/*
* 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;
}
/* 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
/*
* 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;
}
/*
* 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__ */
/*
* 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;
}
/*
* 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__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment