summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarry Wentland <harry.wentland@amd.com>2016-02-04 12:04:37 -0500
committerAlex Deucher <alexander.deucher@amd.com>2016-03-09 14:39:39 -0500
commitad407eb90235239917ec1558923ddfd3cd09f95b (patch)
treea491d7494ff0ae555c3be183b7df4ec75f673f48
parentbf489a5fcca812f800f704ed92abbd8b16cdf529 (diff)
drm/amd/dal: Add surface HW programming
Adds watermark, DMIF, and surface programming. Signed-off-by: Harry Wentland <harry.wentland@amd.com> Reviewed-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.c965
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.h117
2 files changed, 1082 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.c
new file mode 100644
index 000000000000..3a928e63e647
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.c
@@ -0,0 +1,965 @@
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+#include "dm_services.h"
+
+#include "dce/dce_11_0_d.h"
+#include "dce/dce_11_0_sh_mask.h"
+/* TODO: this needs to be looked at, used by Stella's workaround*/
+#include "gmc/gmc_8_2_d.h"
+#include "gmc/gmc_8_2_sh_mask.h"
+
+#include "include/logger_interface.h"
+#include "adapter_service_interface.h"
+#include "inc/bandwidth_calcs.h"
+
+#include "dce110_mem_input.h"
+
+#define MAX_WATERMARK 0xFFFF
+#define SAFE_NBP_MARK 0x7FFF
+
+#define DCP_REG(reg) (reg + mem_input110->offsets.dcp)
+#define DMIF_REG(reg) (reg + mem_input110->offsets.dmif)
+#define PIPE_REG(reg) (reg + mem_input110->offsets.pipe)
+
+static void set_flip_control(
+ struct dce110_mem_input *mem_input110,
+ bool immediate)
+{
+ uint32_t value = 0;
+
+ value = dm_read_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_FLIP_CONTROL));
+ set_reg_field_value(value, 0,
+ GRPH_FLIP_CONTROL,
+ GRPH_SURFACE_UPDATE_IMMEDIATE_EN);
+ set_reg_field_value(value, 0,
+ GRPH_FLIP_CONTROL,
+ GRPH_SURFACE_UPDATE_H_RETRACE_EN);
+ if (immediate == true)
+ set_reg_field_value(value, 1,
+ GRPH_FLIP_CONTROL,
+ GRPH_SURFACE_UPDATE_IMMEDIATE_EN);
+
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_FLIP_CONTROL),
+ value);
+}
+
+static void program_sec_addr(
+ struct dce110_mem_input *mem_input110,
+ PHYSICAL_ADDRESS_LOC address)
+{
+ uint32_t value = 0;
+ uint32_t temp = 0;
+ /*high register MUST be programmed first*/
+ temp = address.high_part &
+GRPH_SECONDARY_SURFACE_ADDRESS_HIGH__GRPH_SECONDARY_SURFACE_ADDRESS_HIGH_MASK;
+
+ set_reg_field_value(value, temp,
+ GRPH_SECONDARY_SURFACE_ADDRESS_HIGH,
+ GRPH_SECONDARY_SURFACE_ADDRESS_HIGH);
+
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_SECONDARY_SURFACE_ADDRESS_HIGH),
+ value);
+
+ temp = 0;
+ value = 0;
+ temp = address.low_part >>
+ GRPH_SECONDARY_SURFACE_ADDRESS__GRPH_SECONDARY_SURFACE_ADDRESS__SHIFT;
+
+ set_reg_field_value(value, temp,
+ GRPH_SECONDARY_SURFACE_ADDRESS,
+ GRPH_SECONDARY_SURFACE_ADDRESS);
+
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_SECONDARY_SURFACE_ADDRESS),
+ value);
+}
+
+static void program_pri_addr(
+ struct dce110_mem_input *mem_input110,
+ PHYSICAL_ADDRESS_LOC address)
+{
+ uint32_t value = 0;
+ uint32_t temp = 0;
+
+ /*high register MUST be programmed first*/
+ temp = address.high_part &
+GRPH_PRIMARY_SURFACE_ADDRESS_HIGH__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_MASK;
+
+ set_reg_field_value(value, temp,
+ GRPH_PRIMARY_SURFACE_ADDRESS_HIGH,
+ GRPH_PRIMARY_SURFACE_ADDRESS_HIGH);
+
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_PRIMARY_SURFACE_ADDRESS_HIGH),
+ value);
+
+ temp = 0;
+ value = 0;
+ temp = address.low_part >>
+ GRPH_PRIMARY_SURFACE_ADDRESS__GRPH_PRIMARY_SURFACE_ADDRESS__SHIFT;
+
+ set_reg_field_value(value, temp,
+ GRPH_PRIMARY_SURFACE_ADDRESS,
+ GRPH_PRIMARY_SURFACE_ADDRESS);
+
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_PRIMARY_SURFACE_ADDRESS),
+ value);
+}
+
+static void program_addr(
+ struct dce110_mem_input *mem_input110,
+ const struct dc_plane_address *addr)
+{
+ switch (addr->type) {
+ case PLN_ADDR_TYPE_GRAPHICS:
+ program_pri_addr(
+ mem_input110,
+ addr->grph.addr);
+ break;
+ case PLN_ADDR_TYPE_GRPH_STEREO:
+ program_pri_addr(
+ mem_input110,
+ addr->grph_stereo.left_addr);
+ program_sec_addr(
+ mem_input110,
+ addr->grph_stereo.right_addr);
+ break;
+ case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE:
+ default:
+ /* not supported */
+ BREAK_TO_DEBUGGER();
+ }
+}
+
+static void enable(struct dce110_mem_input *mem_input110)
+{
+ uint32_t value = 0;
+
+ value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmGRPH_ENABLE));
+ set_reg_field_value(value, 1, GRPH_ENABLE, GRPH_ENABLE);
+ dm_write_reg(mem_input110->base.ctx,
+ DCP_REG(mmGRPH_ENABLE),
+ value);
+}
+
+static void program_tiling(
+ struct dce110_mem_input *mem_input110,
+ const struct dc_tiling_info *info,
+ const enum surface_pixel_format pixel_format)
+{
+ uint32_t value = 0;
+
+ value = dm_read_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_CONTROL));
+
+ set_reg_field_value(value, info->num_banks,
+ GRPH_CONTROL, GRPH_NUM_BANKS);
+
+ set_reg_field_value(value, info->bank_width,
+ GRPH_CONTROL, GRPH_BANK_WIDTH);
+
+ set_reg_field_value(value, info->bank_height,
+ GRPH_CONTROL, GRPH_BANK_HEIGHT);
+
+ set_reg_field_value(value, info->tile_aspect,
+ GRPH_CONTROL, GRPH_MACRO_TILE_ASPECT);
+
+ set_reg_field_value(value, info->tile_split,
+ GRPH_CONTROL, GRPH_TILE_SPLIT);
+
+ set_reg_field_value(value, info->tile_mode,
+ GRPH_CONTROL, GRPH_MICRO_TILE_MODE);
+
+ set_reg_field_value(value, info->pipe_config,
+ GRPH_CONTROL, GRPH_PIPE_CONFIG);
+
+ set_reg_field_value(value, info->array_mode,
+ GRPH_CONTROL, GRPH_ARRAY_MODE);
+
+ set_reg_field_value(value, 1,
+ GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE);
+
+ set_reg_field_value(value, 0,
+ GRPH_CONTROL, GRPH_Z);
+
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_CONTROL),
+ value);
+}
+
+static void program_size_and_rotation(
+ struct dce110_mem_input *mem_input110,
+ enum dc_rotation_angle rotation,
+ const union plane_size *plane_size)
+{
+ uint32_t value = 0;
+ union plane_size local_size = *plane_size;
+
+ if (rotation == ROTATION_ANGLE_90 ||
+ rotation == ROTATION_ANGLE_270) {
+
+ uint32_t swap;
+
+ swap = local_size.grph.surface_size.x;
+ local_size.grph.surface_size.x =
+ local_size.grph.surface_size.y;
+ local_size.grph.surface_size.y = swap;
+
+ swap = local_size.grph.surface_size.width;
+ local_size.grph.surface_size.width =
+ local_size.grph.surface_size.height;
+ local_size.grph.surface_size.height = swap;
+ }
+
+ set_reg_field_value(value, local_size.grph.surface_size.x,
+ GRPH_X_START, GRPH_X_START);
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_X_START),
+ value);
+
+ value = 0;
+ set_reg_field_value(value, local_size.grph.surface_size.y,
+ GRPH_Y_START, GRPH_Y_START);
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_Y_START),
+ value);
+
+ value = 0;
+ set_reg_field_value(value, local_size.grph.surface_size.width,
+ GRPH_X_END, GRPH_X_END);
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_X_END),
+ value);
+
+ value = 0;
+ set_reg_field_value(value, local_size.grph.surface_size.height,
+ GRPH_Y_END, GRPH_Y_END);
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_Y_END),
+ value);
+
+ value = 0;
+ set_reg_field_value(value, local_size.grph.surface_pitch,
+ GRPH_PITCH, GRPH_PITCH);
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_PITCH),
+ value);
+
+
+ value = 0;
+ switch (rotation) {
+ case ROTATION_ANGLE_90:
+ set_reg_field_value(value, 3,
+ HW_ROTATION, GRPH_ROTATION_ANGLE);
+ break;
+ case ROTATION_ANGLE_180:
+ set_reg_field_value(value, 2,
+ HW_ROTATION, GRPH_ROTATION_ANGLE);
+ break;
+ case ROTATION_ANGLE_270:
+ set_reg_field_value(value, 1,
+ HW_ROTATION, GRPH_ROTATION_ANGLE);
+ break;
+ default:
+ set_reg_field_value(value, 0,
+ HW_ROTATION, GRPH_ROTATION_ANGLE);
+ break;
+ }
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmHW_ROTATION),
+ value);
+}
+
+static void program_pixel_format(
+ struct dce110_mem_input *mem_input110,
+ enum surface_pixel_format format)
+{
+ if (format >= SURFACE_PIXEL_FORMAT_GRPH_BEGIN &&
+ format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) {
+ uint32_t value = 0;
+
+ /* handle colour twizzle formats, swapping R and B */
+ if (format == SURFACE_PIXEL_FORMAT_GRPH_BGRA8888 ||
+ format == SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010 ||
+ format ==
+ SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS ||
+ format == SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F) {
+ set_reg_field_value(
+ value, 2, GRPH_SWAP_CNTL, GRPH_RED_CROSSBAR);
+ set_reg_field_value(
+ value, 2, GRPH_SWAP_CNTL, GRPH_BLUE_CROSSBAR);
+ }
+
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_SWAP_CNTL),
+ value);
+
+
+ value = dm_read_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_CONTROL));
+
+ switch (format) {
+ case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS:
+ set_reg_field_value(
+ value, 0, GRPH_CONTROL, GRPH_DEPTH);
+ set_reg_field_value(
+ value, 0, GRPH_CONTROL, GRPH_FORMAT);
+ break;
+ case SURFACE_PIXEL_FORMAT_GRPH_RGB565:
+ set_reg_field_value(
+ value, 1, GRPH_CONTROL, GRPH_DEPTH);
+ set_reg_field_value(
+ value, 1, GRPH_CONTROL, GRPH_FORMAT);
+ break;
+ case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888:
+ case SURFACE_PIXEL_FORMAT_GRPH_BGRA8888:
+ set_reg_field_value(
+ value, 2, GRPH_CONTROL, GRPH_DEPTH);
+ set_reg_field_value(
+ value, 0, GRPH_CONTROL, GRPH_FORMAT);
+ break;
+ case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010:
+ case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010:
+ case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS:
+ set_reg_field_value(
+ value, 2, GRPH_CONTROL, GRPH_DEPTH);
+ set_reg_field_value(
+ value, 1, GRPH_CONTROL, GRPH_FORMAT);
+ break;
+ case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616:
+ case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F:
+ set_reg_field_value(
+ value, 3, GRPH_CONTROL, GRPH_DEPTH);
+ set_reg_field_value(
+ value, 0, GRPH_CONTROL, GRPH_FORMAT);
+ break;
+ default:
+ break;
+ }
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmGRPH_CONTROL),
+ value);
+
+ /*TODO [hwentlan] MOVE THIS TO CONTROLLER GAMMA!!!!!*/
+ value = dm_read_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmPRESCALE_GRPH_CONTROL));
+
+ if (format == SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F) {
+ set_reg_field_value(
+ value, 1, PRESCALE_GRPH_CONTROL,
+ GRPH_PRESCALE_SELECT);
+ set_reg_field_value(
+ value, 1, PRESCALE_GRPH_CONTROL,
+ GRPH_PRESCALE_R_SIGN);
+ set_reg_field_value(
+ value, 1, PRESCALE_GRPH_CONTROL,
+ GRPH_PRESCALE_G_SIGN);
+ set_reg_field_value(
+ value, 1, PRESCALE_GRPH_CONTROL,
+ GRPH_PRESCALE_B_SIGN);
+ } else {
+ set_reg_field_value(
+ value, 0, PRESCALE_GRPH_CONTROL,
+ GRPH_PRESCALE_SELECT);
+ set_reg_field_value(
+ value, 0, PRESCALE_GRPH_CONTROL,
+ GRPH_PRESCALE_R_SIGN);
+ set_reg_field_value(
+ value, 0, PRESCALE_GRPH_CONTROL,
+ GRPH_PRESCALE_G_SIGN);
+ set_reg_field_value(
+ value, 0, PRESCALE_GRPH_CONTROL,
+ GRPH_PRESCALE_B_SIGN);
+ }
+ dm_write_reg(
+ mem_input110->base.ctx,
+ DCP_REG(mmPRESCALE_GRPH_CONTROL),
+ value);
+ }
+}
+
+static void wait_for_no_surface_update_pending(
+ struct dce110_mem_input *mem_input110)
+{
+ uint32_t value;
+
+ do {
+ value = dm_read_reg(mem_input110->base.ctx,
+ DCP_REG(mmGRPH_UPDATE));
+
+ } while (get_reg_field_value(value, GRPH_UPDATE,
+ GRPH_SURFACE_UPDATE_PENDING));
+}
+
+bool dce110_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);
+
+ set_flip_control(mem_input110, flip_immediate);
+ program_addr(mem_input110,
+ address);
+
+ if (flip_immediate)
+ wait_for_no_surface_update_pending(mem_input110);
+
+ return true;
+}
+
+bool dce110_mem_input_program_surface_config(
+ struct mem_input *mem_input,
+ enum surface_pixel_format format,
+ struct dc_tiling_info *tiling_info,
+ union plane_size *plane_size,
+ enum dc_rotation_angle rotation)
+{
+ struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input);
+
+ enable(mem_input110);
+ program_tiling(mem_input110, tiling_info, format);
+ program_size_and_rotation(mem_input110, rotation, plane_size);
+ program_pixel_format(mem_input110, format);
+
+ return true;
+}
+
+static void program_urgency_watermark(
+ const struct dc_context *ctx,
+ const uint32_t offset,
+ struct bw_watermarks marks_low,
+ uint32_t total_dest_line_time_ns)
+{
+ /* register value */
+ uint32_t urgency_cntl = 0;
+ uint32_t wm_mask_cntl = 0;
+
+ uint32_t urgency_addr = offset + mmDPG_PIPE_URGENCY_CONTROL;
+ uint32_t wm_addr = offset + mmDPG_WATERMARK_MASK_CONTROL;
+
+ /*Write mask to enable reading/writing of watermark set A*/
+ wm_mask_cntl = dm_read_reg(ctx, wm_addr);
+ set_reg_field_value(wm_mask_cntl,
+ 1,
+ DPG_WATERMARK_MASK_CONTROL,
+ URGENCY_WATERMARK_MASK);
+ dm_write_reg(ctx, wm_addr, wm_mask_cntl);
+
+ urgency_cntl = dm_read_reg(ctx, urgency_addr);
+
+ set_reg_field_value(
+ urgency_cntl,
+ marks_low.a_mark,
+ DPG_PIPE_URGENCY_CONTROL,
+ URGENCY_LOW_WATERMARK);
+
+ set_reg_field_value(
+ urgency_cntl,
+ total_dest_line_time_ns,
+ DPG_PIPE_URGENCY_CONTROL,
+ URGENCY_HIGH_WATERMARK);
+ dm_write_reg(ctx, urgency_addr, urgency_cntl);
+
+
+ /*Write mask to enable reading/writing of watermark set B*/
+ wm_mask_cntl = dm_read_reg(ctx, wm_addr);
+ set_reg_field_value(wm_mask_cntl,
+ 2,
+ DPG_WATERMARK_MASK_CONTROL,
+ URGENCY_WATERMARK_MASK);
+ dm_write_reg(ctx, wm_addr, wm_mask_cntl);
+
+ urgency_cntl = dm_read_reg(ctx, urgency_addr);
+
+ set_reg_field_value(urgency_cntl,
+ marks_low.b_mark,
+ DPG_PIPE_URGENCY_CONTROL,
+ URGENCY_LOW_WATERMARK);
+
+ set_reg_field_value(urgency_cntl,
+ total_dest_line_time_ns,
+ DPG_PIPE_URGENCY_CONTROL,
+ URGENCY_HIGH_WATERMARK);
+ dm_write_reg(ctx, urgency_addr, urgency_cntl);
+}
+
+static void program_stutter_watermark(
+ const struct dc_context *ctx,
+ const uint32_t offset,
+ struct bw_watermarks marks)
+{
+ /* register value */
+ uint32_t stutter_cntl = 0;
+ uint32_t wm_mask_cntl = 0;
+
+ uint32_t stutter_addr = offset + mmDPG_PIPE_STUTTER_CONTROL;
+ uint32_t wm_addr = offset + mmDPG_WATERMARK_MASK_CONTROL;
+
+ /*Write mask to enable reading/writing of watermark set A*/
+
+ wm_mask_cntl = dm_read_reg(ctx, wm_addr);
+ set_reg_field_value(wm_mask_cntl,
+ 1,
+ DPG_WATERMARK_MASK_CONTROL,
+ STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK);
+ dm_write_reg(ctx, wm_addr, wm_mask_cntl);
+
+ stutter_cntl = dm_read_reg(ctx, stutter_addr);
+
+ set_reg_field_value(stutter_cntl,
+ 1,
+ DPG_PIPE_STUTTER_CONTROL,
+ STUTTER_ENABLE);
+ set_reg_field_value(stutter_cntl,
+ 1,
+ DPG_PIPE_STUTTER_CONTROL,
+ STUTTER_IGNORE_FBC);
+
+ /*Write watermark set A*/
+ set_reg_field_value(stutter_cntl,
+ marks.a_mark,
+ DPG_PIPE_STUTTER_CONTROL,
+ STUTTER_EXIT_SELF_REFRESH_WATERMARK);
+ dm_write_reg(ctx, stutter_addr, stutter_cntl);
+
+ /*Write mask to enable reading/writing of watermark set B*/
+ wm_mask_cntl = dm_read_reg(ctx, wm_addr);
+ set_reg_field_value(wm_mask_cntl,
+ 2,
+ DPG_WATERMARK_MASK_CONTROL,
+ STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK);
+ dm_write_reg(ctx, wm_addr, wm_mask_cntl);
+
+ stutter_cntl = dm_read_reg(ctx, stutter_addr);
+ set_reg_field_value(stutter_cntl,
+ 1,
+ DPG_PIPE_STUTTER_CONTROL,
+ STUTTER_ENABLE);
+ set_reg_field_value(stutter_cntl,
+ 1,
+ DPG_PIPE_STUTTER_CONTROL,
+ STUTTER_IGNORE_FBC);
+
+ /*Write watermark set B*/
+ set_reg_field_value(stutter_cntl,
+ marks.b_mark,
+ DPG_PIPE_STUTTER_CONTROL,
+ STUTTER_EXIT_SELF_REFRESH_WATERMARK);
+ dm_write_reg(ctx, stutter_addr, stutter_cntl);
+}
+
+static void program_nbp_watermark(
+ const struct dc_context *ctx,
+ const uint32_t offset,
+ struct bw_watermarks marks)
+{
+ uint32_t value;
+ uint32_t addr;
+ /* Write mask to enable reading/writing of watermark set A */
+ addr = offset + mmDPG_WATERMARK_MASK_CONTROL;
+ value = dm_read_reg(ctx, addr);
+ set_reg_field_value(
+ value,
+ 1,
+ DPG_WATERMARK_MASK_CONTROL,
+ NB_PSTATE_CHANGE_WATERMARK_MASK);
+ dm_write_reg(ctx, addr, value);
+
+ addr = offset + mmDPG_PIPE_NB_PSTATE_CHANGE_CONTROL;
+ value = dm_read_reg(ctx, addr);
+ set_reg_field_value(
+ value,
+ 1,
+ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL,
+ NB_PSTATE_CHANGE_ENABLE);
+ set_reg_field_value(
+ value,
+ 1,
+ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL,
+ NB_PSTATE_CHANGE_URGENT_DURING_REQUEST);
+ set_reg_field_value(
+ value,
+ 1,
+ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL,
+ NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST);
+ dm_write_reg(ctx, addr, value);
+
+ /* Write watermark set A */
+ value = dm_read_reg(ctx, addr);
+ set_reg_field_value(
+ value,
+ marks.a_mark,
+ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL,
+ NB_PSTATE_CHANGE_WATERMARK);
+ dm_write_reg(ctx, addr, value);
+
+ /* Write mask to enable reading/writing of watermark set B */
+ addr = offset + mmDPG_WATERMARK_MASK_CONTROL;
+ value = dm_read_reg(ctx, addr);
+ set_reg_field_value(
+ value,
+ 2,
+ DPG_WATERMARK_MASK_CONTROL,
+ NB_PSTATE_CHANGE_WATERMARK_MASK);
+ dm_write_reg(ctx, addr, value);
+
+ addr = offset + mmDPG_PIPE_NB_PSTATE_CHANGE_CONTROL;
+ value = dm_read_reg(ctx, addr);
+ set_reg_field_value(
+ value,
+ 1,
+ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL,
+ NB_PSTATE_CHANGE_ENABLE);
+ set_reg_field_value(
+ value,
+ 1,
+ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL,
+ NB_PSTATE_CHANGE_URGENT_DURING_REQUEST);
+ set_reg_field_value(
+ value,
+ 1,
+ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL,
+ NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST);
+ dm_write_reg(ctx, addr, value);
+
+ /* Write watermark set B */
+ value = dm_read_reg(ctx, addr);
+ set_reg_field_value(
+ value,
+ marks.b_mark,
+ DPG_PIPE_NB_PSTATE_CHANGE_CONTROL,
+ NB_PSTATE_CHANGE_WATERMARK);
+ dm_write_reg(ctx, addr, value);
+}
+
+void dce110_mem_input_program_safe_display_marks(struct mem_input *mi)
+{
+ struct dce110_mem_input *bm_dce110 = TO_DCE110_MEM_INPUT(mi);
+ struct bw_watermarks max_marks = { MAX_WATERMARK, MAX_WATERMARK };
+ struct bw_watermarks nbp_marks = { SAFE_NBP_MARK, SAFE_NBP_MARK };
+
+ program_urgency_watermark(
+ mi->ctx, bm_dce110->offsets.dmif, max_marks, MAX_WATERMARK);
+ program_stutter_watermark(mi->ctx, bm_dce110->offsets.dmif, max_marks);
+ program_nbp_watermark(mi->ctx, bm_dce110->offsets.dmif, nbp_marks);
+}
+
+void dce110_mem_input_program_display_marks(
+ struct mem_input *mem_input,
+ struct bw_watermarks nbp,
+ struct bw_watermarks stutter,
+ struct bw_watermarks urgent,
+ uint32_t h_total,
+ uint32_t pixel_clk_in_khz,
+ uint32_t pstate_blackout_duration_ns)
+{
+ struct dce110_mem_input *bm_dce110 = TO_DCE110_MEM_INPUT(mem_input);
+ uint32_t total_dest_line_time_ns = 1000000UL * h_total
+ / pixel_clk_in_khz + pstate_blackout_duration_ns;
+
+ program_urgency_watermark(
+ mem_input->ctx,
+ bm_dce110->offsets.dmif,
+ urgent,
+ total_dest_line_time_ns);
+ program_nbp_watermark(
+ mem_input->ctx,
+ bm_dce110->offsets.dmif,
+ nbp);
+ program_stutter_watermark(
+ mem_input->ctx,
+ bm_dce110->offsets.dmif,
+ stutter);
+}
+
+static uint32_t get_dmif_switch_time_us(struct dc_crtc_timing *timing)
+{
+ uint32_t frame_time;
+ uint32_t pixels_per_second;
+ uint32_t pixels_per_frame;
+ uint32_t refresh_rate;
+ const uint32_t us_in_sec = 1000000;
+ const uint32_t min_single_frame_time_us = 30000;
+ /*return double of frame time*/
+ const uint32_t single_frame_time_multiplier = 2;
+
+ if (timing == NULL)
+ return single_frame_time_multiplier * min_single_frame_time_us;
+
+ /*TODO: should we use pixel format normalized pixel clock here?*/
+ pixels_per_second = timing->pix_clk_khz * 1000;
+ pixels_per_frame = timing->h_total * timing->v_total;
+
+ if (!pixels_per_second || !pixels_per_frame) {
+ /* avoid division by zero */
+ ASSERT(pixels_per_frame);
+ ASSERT(pixels_per_second);
+ return single_frame_time_multiplier * min_single_frame_time_us;
+ }
+
+ refresh_rate = pixels_per_second / pixels_per_frame;
+
+ if (!refresh_rate) {
+ /* avoid division by zero*/
+ ASSERT(refresh_rate);
+ return single_frame_time_multiplier * min_single_frame_time_us;
+ }
+
+ frame_time = us_in_sec / refresh_rate;
+
+ if (frame_time < min_single_frame_time_us)
+ frame_time = min_single_frame_time_us;
+
+ frame_time *= single_frame_time_multiplier;
+
+ return frame_time;
+}
+
+void dce110_mem_input_allocate_dmif_buffer(
+ struct mem_input *mi,
+ struct dc_crtc_timing *timing,
+ uint32_t paths_num)
+{
+ const uint32_t retry_delay = 10;
+ uint32_t retry_count = get_dmif_switch_time_us(timing) / retry_delay;
+
+ struct dce110_mem_input *bm110 = TO_DCE110_MEM_INPUT(mi);
+ uint32_t addr = bm110->offsets.pipe + mmPIPE0_DMIF_BUFFER_CONTROL;
+ uint32_t value;
+ uint32_t field;
+ uint32_t pix_dur;
+
+ if (bm110->supported_stutter_mode
+ & STUTTER_MODE_NO_DMIF_BUFFER_ALLOCATION)
+ goto register_underflow_int;
+
+ /*Allocate DMIF buffer*/
+ value = dm_read_reg(mi->ctx, addr);
+ field = get_reg_field_value(
+ value, PIPE0_DMIF_BUFFER_CONTROL, DMIF_BUFFERS_ALLOCATED);
+ if (field == 2)
+ goto register_underflow_int;
+
+ set_reg_field_value(
+ value,
+ 2,
+ PIPE0_DMIF_BUFFER_CONTROL,
+ DMIF_BUFFERS_ALLOCATED);
+
+ dm_write_reg(mi->ctx, addr, value);
+
+ do {
+ value = dm_read_reg(mi->ctx, addr);
+ field = get_reg_field_value(
+ value,
+ PIPE0_DMIF_BUFFER_CONTROL,
+ DMIF_BUFFERS_ALLOCATION_COMPLETED);
+
+ if (field)
+ break;
+
+ dm_delay_in_microseconds(mi->ctx, retry_delay);
+ retry_count--;
+
+ } while (retry_count > 0);
+
+ if (field == 0)
+ dal_logger_write(mi->ctx->logger,
+ LOG_MAJOR_ERROR,
+ LOG_MINOR_COMPONENT_GPU,
+ "%s: DMIF allocation failed",
+ __func__);
+
+
+ if (timing->pix_clk_khz != 0) {
+ addr = mmDPG_PIPE_ARBITRATION_CONTROL1 + bm110->offsets.dmif;
+ value = dm_read_reg(mi->ctx, addr);
+ pix_dur = 1000000000ULL / timing->pix_clk_khz;
+
+ set_reg_field_value(
+ value,
+ pix_dur,
+ DPG_PIPE_ARBITRATION_CONTROL1,
+ PIXEL_DURATION);
+
+ dm_write_reg(mi->ctx, addr, value);
+ }
+
+ /*
+ * Stella Wong proposed the following change
+ *
+ * Value of mcHubRdReqDmifLimit.ENABLE:
+ * 00 - disable DMIF rdreq limit
+ * 01 - enable DMIF rdreq limit, disabled by DMIF stall = 1 || urg != 0
+ * 02 - enable DMIF rdreq limit, disable by DMIF stall = 1
+ * 03 - force enable DMIF rdreq limit, ignore DMIF stall / urgent
+ */
+ if (!IS_FPGA_MAXIMUS_DC(mi->ctx->dce_environment)) {
+ addr = mmMC_HUB_RDREQ_DMIF_LIMIT;
+ value = dm_read_reg(mi->ctx, addr);
+
+ if (paths_num > 1)
+ set_reg_field_value(value, 0, MC_HUB_RDREQ_DMIF_LIMIT, ENABLE);
+ else
+ set_reg_field_value(value, 3, MC_HUB_RDREQ_DMIF_LIMIT, ENABLE);
+ dm_write_reg(mi->ctx, addr, value);
+ }
+
+register_underflow_int:
+ /*todo*/;
+ /*register_interrupt(bm110, irq_source, ctrl_id);*/
+}
+
+static void deallocate_dmif_buffer_helper(
+ struct dc_context *ctx, uint32_t offset)
+{
+ uint32_t value;
+ uint32_t count = 0xBB8; /* max retry count */
+
+ value = dm_read_reg(ctx, mmPIPE0_DMIF_BUFFER_CONTROL + offset);
+
+ if (!get_reg_field_value(
+ value, PIPE0_DMIF_BUFFER_CONTROL, DMIF_BUFFERS_ALLOCATED))
+ return;
+
+ set_reg_field_value(
+ value, 0, PIPE0_DMIF_BUFFER_CONTROL, DMIF_BUFFERS_ALLOCATED);
+
+ dm_write_reg(
+ ctx, mmPIPE0_DMIF_BUFFER_CONTROL + offset, value);
+
+ do {
+ value = dm_read_reg(ctx, mmPIPE0_DMIF_BUFFER_CONTROL + offset);
+ dm_delay_in_microseconds(ctx, 10);
+ count--;
+ } while (count > 0 &&
+ !get_reg_field_value(
+ value,
+ PIPE0_DMIF_BUFFER_CONTROL,
+ DMIF_BUFFERS_ALLOCATION_COMPLETED));
+}
+
+void dce110_mem_input_deallocate_dmif_buffer(
+ struct mem_input *mi, uint32_t paths_num)
+{
+ struct dce110_mem_input *bm_dce110 = TO_DCE110_MEM_INPUT(mi);
+ uint32_t value;
+
+ if (!(bm_dce110->supported_stutter_mode &
+ STUTTER_MODE_NO_DMIF_BUFFER_ALLOCATION)) {
+
+ /* De-allocate DMIF buffer first */
+ if (mmPIPE0_DMIF_BUFFER_CONTROL + bm_dce110->offsets.pipe != 0)
+ deallocate_dmif_buffer_helper(
+ mi->ctx, bm_dce110->offsets.pipe);
+ }
+
+ /* TODO: unregister underflow interrupt
+ unregisterInterrupt();
+ */
+
+ /* Value of mcHubRdReqDmifLimit.ENABLE.
+ * 00 - disable dmif rdreq limit
+ * 01 - enable dmif rdreq limit, disable by dmif stall=1||urg!=0
+ * 02 - enable dmif rdreq limit, disable by dmif stall=1
+ * 03 - force enable dmif rdreq limit, ignore dmif stall/urgent
+ * Stella Wong proposed this change. */
+ if (!IS_FPGA_MAXIMUS_DC(mi->ctx->dce_environment)) {
+ value = dm_read_reg(mi->ctx, mmMC_HUB_RDREQ_DMIF_LIMIT);
+ if (paths_num > 1)
+ set_reg_field_value(value, 0, MC_HUB_RDREQ_DMIF_LIMIT, ENABLE);
+ else
+ set_reg_field_value(value, 3, MC_HUB_RDREQ_DMIF_LIMIT, ENABLE);
+
+ dm_write_reg(mi->ctx, mmMC_HUB_RDREQ_DMIF_LIMIT, value);
+ }
+}
+
+static struct mem_input_funcs dce110_mem_input_funcs = {
+ .mem_input_program_safe_display_marks =
+ dce110_mem_input_program_safe_display_marks,
+ .mem_input_program_display_marks =
+ dce110_mem_input_program_display_marks,
+ .mem_input_allocate_dmif_buffer = dce110_mem_input_allocate_dmif_buffer,
+ .mem_input_deallocate_dmif_buffer =
+ dce110_mem_input_deallocate_dmif_buffer,
+ .mem_input_program_surface_flip_and_addr =
+ dce110_mem_input_program_surface_flip_and_addr,
+ .mem_input_program_surface_config =
+ dce110_mem_input_program_surface_config,
+};
+/*****************************************/
+/* Constructor, Destructor */
+/*****************************************/
+
+bool dce110_mem_input_construct(
+ struct dce110_mem_input *mem_input110,
+ struct dc_context *ctx,
+ uint32_t inst,
+ const struct dce110_mem_input_reg_offsets *offsets)
+{
+ mem_input110->base.funcs = &dce110_mem_input_funcs;
+ mem_input110->base.ctx = ctx;
+
+ mem_input110->base.inst = inst;
+
+ mem_input110->offsets = *offsets;
+
+ mem_input110->supported_stutter_mode = 0;
+ dal_adapter_service_get_feature_value(FEATURE_STUTTER_MODE,
+ &(mem_input110->supported_stutter_mode),
+ sizeof(mem_input110->supported_stutter_mode));
+
+ return true;
+}
+
+void dce110_mem_input_destroy(struct mem_input **mem_input)
+{
+ dm_free((*mem_input)->ctx, TO_DCE110_MEM_INPUT(*mem_input));
+ *mem_input = NULL;
+}
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.h
new file mode 100644
index 000000000000..5a4e5fe33a79
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_mem_input.h
@@ -0,0 +1,117 @@
+/* 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_DCE110_H__
+#define __DC_MEM_INPUT_DCE110_H__
+
+#include "inc/mem_input.h"
+
+#define TO_DCE110_MEM_INPUT(mi)\
+ container_of(mi, struct dce110_mem_input, base)
+
+struct dce110_mem_input_reg_offsets {
+ uint32_t dcp;
+ uint32_t dmif;
+ uint32_t pipe;
+};
+
+struct dce110_mem_input {
+ struct mem_input base;
+ struct dce110_mem_input_reg_offsets offsets;
+ uint32_t supported_stutter_mode;
+};
+
+bool dce110_mem_input_construct(
+ struct dce110_mem_input *mem_input110,
+ struct dc_context *ctx,
+ uint32_t inst,
+ const struct dce110_mem_input_reg_offsets *offsets);
+
+/*
+ * dce110_mem_input_program_display_marks
+ *
+ * This function will program nbp stutter and urgency watermarks to maximum
+ * safe values
+ */
+void dce110_mem_input_program_safe_display_marks(struct mem_input *mi);
+
+/*
+ * dce110_mem_input_program_display_marks
+ *
+ * This function will program nbp stutter and urgency watermarks to minimum
+ * allowable values
+ */
+void dce110_mem_input_program_display_marks(
+ struct mem_input *mem_input,
+ struct bw_watermarks nbp,
+ struct bw_watermarks stutter,
+ struct bw_watermarks urgent,
+ uint32_t h_total,
+ uint32_t pixel_clk_in_khz,
+ uint32_t pstate_blackout_duration_ns);
+
+/*
+ * dce110_mem_input_allocate_dmif_buffer
+ *
+ * This function will allocate a dmif buffer and program required
+ * pixel duration for pipe
+ */
+void dce110_mem_input_allocate_dmif_buffer(
+ struct mem_input *mem_input,
+ struct dc_crtc_timing *timing,
+ uint32_t paths_num);
+
+/*
+ * dce110_mem_input_deallocate_dmif_buffer
+ *
+ * This function will deallocate a dmif buffer from pipe
+ */
+void dce110_mem_input_deallocate_dmif_buffer(
+ struct mem_input *mem_input, uint32_t paths_num);
+
+/*
+ * dce110_mem_input_program_surface_flip_and_addr
+ *
+ * This function programs hsync/vsync mode and surface address
+ */
+bool dce110_mem_input_program_surface_flip_and_addr(
+ struct mem_input *mem_input,
+ const struct dc_plane_address *address,
+ bool flip_immediate);
+
+/*
+ * dce110_mem_input_program_surface_config
+ *
+ * This function will program surface tiling, size, rotation and pixel format
+ * to corresponding dcp registers.
+ */
+bool dce110_mem_input_program_surface_config(
+ struct mem_input *mem_input,
+ enum surface_pixel_format format,
+ struct dc_tiling_info *tiling_info,
+ union plane_size *plane_size,
+ enum dc_rotation_angle rotation);
+
+
+#endif