aboutsummaryrefslogtreecommitdiffstats
path: root/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4331-drm-amd-display-Add-DMUB-support-to-DC.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4331-drm-amd-display-Add-DMUB-support-to-DC.patch')
-rw-r--r--meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4331-drm-amd-display-Add-DMUB-support-to-DC.patch968
1 files changed, 968 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4331-drm-amd-display-Add-DMUB-support-to-DC.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4331-drm-amd-display-Add-DMUB-support-to-DC.patch
new file mode 100644
index 00000000..1580bb36
--- /dev/null
+++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4331-drm-amd-display-Add-DMUB-support-to-DC.patch
@@ -0,0 +1,968 @@
+From 23983b55e0d07ee00b2331b9c67f643281542f59 Mon Sep 17 00:00:00 2001
+From: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
+Date: Fri, 25 Oct 2019 15:03:58 -0400
+Subject: [PATCH 4331/4736] drm/amd/display: Add DMUB support to DC
+
+DC will use DMUB for command submission and flow control during
+initialization.
+
+Register offloading as well as submitting some BIOS commands are part
+of the DC internal interface but are guarded behind debug options.
+
+It won't be functional in amdgpu_dm yet since we don't pass the
+DMUB service to DC for use.
+
+Change-Id: Ib341af1d0d5569271cb1964de5b6ef6ba8e9d8f3
+Signed-off-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
+Reviewed-by: Hersen Wu <hersenxs.wu@amd.com>
+Acked-by: Harry Wentland <harry.wentland@amd.com>
+---
+ drivers/gpu/drm/amd/display/dc/Makefile | 6 +-
+ .../drm/amd/display/dc/bios/command_table2.c | 91 ++++++
+ drivers/gpu/drm/amd/display/dc/core/dc.c | 8 +
+ drivers/gpu/drm/amd/display/dc/dc.h | 12 +
+ drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c | 119 ++++++++
+ drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h | 60 ++++
+ drivers/gpu/drm/amd/display/dc/dc_helper.c | 273 ++++++++++++++++++
+ drivers/gpu/drm/amd/display/dc/dc_types.h | 6 +
+ .../drm/amd/display/dc/dcn10/dcn10_dpp_cm.c | 7 +
+ .../gpu/drm/amd/display/dc/dcn20/dcn20_mpc.c | 11 +
+ .../drm/amd/display/dc/dcn21/dcn21_resource.c | 3 +
+ drivers/gpu/drm/amd/display/dc/dm_services.h | 14 +
+ .../gpu/drm/amd/display/dc/inc/reg_helper.h | 22 ++
+ drivers/gpu/drm/amd/display/dc/os_types.h | 2 +
+ 14 files changed, 633 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
+ create mode 100644 drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
+
+diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile
+index a160512a2f04..6fe39f6392c7 100644
+--- a/drivers/gpu/drm/amd/display/dc/Makefile
++++ b/drivers/gpu/drm/amd/display/dc/Makefile
+@@ -70,5 +70,9 @@ AMD_DM_REG_UPDATE = $(addprefix $(AMDDALPATH)/dc/,dc_helper.o)
+ AMD_DISPLAY_FILES += $(AMD_DISPLAY_CORE)
+ AMD_DISPLAY_FILES += $(AMD_DM_REG_UPDATE)
+
+-
++ifdef CONFIG_DRM_AMD_DC_DMUB
++DC_DMUB += dc_dmub_srv.o
++AMD_DISPLAY_DMUB = $(addprefix $(AMDDALPATH)/dc/,$(DC_DMUB))
++AMD_DISPLAY_FILES += $(AMD_DISPLAY_DMUB)
++endif
+
+diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table2.c b/drivers/gpu/drm/amd/display/dc/bios/command_table2.c
+index bb2e8105e6ab..a3d890050e39 100644
+--- a/drivers/gpu/drm/amd/display/dc/bios/command_table2.c
++++ b/drivers/gpu/drm/amd/display/dc/bios/command_table2.c
+@@ -37,6 +37,10 @@
+ #include "bios_parser_types_internal2.h"
+ #include "amdgpu.h"
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++#include "dc_dmub_srv.h"
++#include "dc.h"
++#endif
+
+ #define DC_LOGGER \
+ bp->base.ctx->logger
+@@ -103,6 +107,21 @@ static void init_dig_encoder_control(struct bios_parser *bp)
+ }
+ }
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++static void encoder_control_dmcub(
++ struct dc_dmub_srv *dmcub,
++ struct dig_encoder_stream_setup_parameters_v1_5 *dig)
++{
++ struct dmub_rb_cmd_digx_encoder_control encoder_control = { 0 };
++
++ encoder_control.header.type = DMUB_CMD__DIGX_ENCODER_CONTROL;
++ encoder_control.encoder_control.dig.stream_param = *dig;
++
++ dc_dmub_srv_cmd_queue(dmcub, &encoder_control.header);
++ dc_dmub_srv_cmd_execute(dmcub);
++ dc_dmub_srv_wait_idle(dmcub);
++}
++#endif
+ static enum bp_result encoder_control_digx_v1_5(
+ struct bios_parser *bp,
+ struct bp_encoder_control *cntl)
+@@ -154,6 +173,13 @@ static enum bp_result encoder_control_digx_v1_5(
+ default:
+ break;
+ }
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ if (bp->base.ctx->dc->ctx->dmub_srv &&
++ bp->base.ctx->dc->debug.dmub_command_table) {
++ encoder_control_dmcub(bp->base.ctx->dmub_srv, &params);
++ return BP_RESULT_OK;
++ }
++#endif
+
+ if (EXEC_BIOS_CMD_TABLE(digxencodercontrol, params))
+ result = BP_RESULT_OK;
+@@ -190,7 +216,21 @@ static void init_transmitter_control(struct bios_parser *bp)
+ break;
+ }
+ }
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++static void transmitter_control_dmcub(
++ struct dc_dmub_srv *dmcub,
++ struct dig_transmitter_control_parameters_v1_6 *dig)
++{
++ struct dmub_rb_cmd_dig1_transmitter_control transmitter_control;
++
++ transmitter_control.header.type = DMUB_CMD__DIG1_TRANSMITTER_CONTROL;
++ transmitter_control.transmitter_control.dig = *dig;
+
++ dc_dmub_srv_cmd_queue(dmcub, &transmitter_control.header);
++ dc_dmub_srv_cmd_execute(dmcub);
++ dc_dmub_srv_wait_idle(dmcub);
++}
++#endif
+ static enum bp_result transmitter_control_v1_6(
+ struct bios_parser *bp,
+ struct bp_transmitter_control *cntl)
+@@ -223,6 +263,14 @@ static enum bp_result transmitter_control_v1_6(
+ }
+
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ if (bp->base.ctx->dc->ctx->dmub_srv &&
++ bp->base.ctx->dc->debug.dmub_command_table) {
++ transmitter_control_dmcub(bp->base.ctx->dmub_srv, &ps.param);
++ return BP_RESULT_OK;
++ }
++#endif
++
+ /*color_depth not used any more, driver has deep color factor in the Phyclk*/
+ if (EXEC_BIOS_CMD_TABLE(dig1transmittercontrol, ps))
+ result = BP_RESULT_OK;
+@@ -255,7 +303,21 @@ static void init_set_pixel_clock(struct bios_parser *bp)
+ }
+ }
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++static void set_pixel_clock_dmcub(
++ struct dc_dmub_srv *dmcub,
++ struct set_pixel_clock_parameter_v1_7 *clk)
++{
++ struct dmub_rb_cmd_set_pixel_clock pixel_clock = { 0 };
+
++ pixel_clock.header.type = DMUB_CMD__SET_PIXEL_CLOCK;
++ pixel_clock.pixel_clock.clk = *clk;
++
++ dc_dmub_srv_cmd_queue(dmcub, &pixel_clock.header);
++ dc_dmub_srv_cmd_execute(dmcub);
++ dc_dmub_srv_wait_idle(dmcub);
++}
++#endif
+
+ static enum bp_result set_pixel_clock_v7(
+ struct bios_parser *bp,
+@@ -331,6 +393,13 @@ static enum bp_result set_pixel_clock_v7(
+ if (bp_params->signal_type == SIGNAL_TYPE_DVI_DUAL_LINK)
+ clk.miscinfo |= PIXEL_CLOCK_V7_MISC_DVI_DUALLINK_EN;
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ if (bp->base.ctx->dc->ctx->dmub_srv &&
++ bp->base.ctx->dc->debug.dmub_command_table) {
++ set_pixel_clock_dmcub(bp->base.ctx->dmub_srv, &clk);
++ return BP_RESULT_OK;
++ }
++#endif
+ if (EXEC_BIOS_CMD_TABLE(setpixelclock, clk))
+ result = BP_RESULT_OK;
+ }
+@@ -584,7 +653,21 @@ static void init_enable_disp_power_gating(
+ break;
+ }
+ }
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++static void enable_disp_power_gating_dmcub(
++ struct dc_dmub_srv *dmcub,
++ struct enable_disp_power_gating_parameters_v2_1 *pwr)
++{
++ struct dmub_rb_cmd_enable_disp_power_gating power_gating;
++
++ power_gating.header.type = DMUB_CMD__ENABLE_DISP_POWER_GATING;
++ power_gating.power_gating.pwr = *pwr;
+
++ dc_dmub_srv_cmd_queue(dmcub, &power_gating.header);
++ dc_dmub_srv_cmd_execute(dmcub);
++ dc_dmub_srv_wait_idle(dmcub);
++}
++#endif
+ static enum bp_result enable_disp_power_gating_v2_1(
+ struct bios_parser *bp,
+ enum controller_id crtc_id,
+@@ -604,6 +687,14 @@ static enum bp_result enable_disp_power_gating_v2_1(
+ ps.param.enable =
+ bp->cmd_helper->disp_power_gating_action_to_atom(action);
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ if (bp->base.ctx->dc->ctx->dmub_srv &&
++ bp->base.ctx->dc->debug.dmub_command_table) {
++ enable_disp_power_gating_dmcub(bp->base.ctx->dmub_srv,
++ &ps.param);
++ return BP_RESULT_OK;
++ }
++#endif
+ if (EXEC_BIOS_CMD_TABLE(enabledisppowergating, ps.param))
+ result = BP_RESULT_OK;
+
+diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
+index 3d38e7e071a4..9ddc0124cda1 100644
+--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
++++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
+@@ -56,6 +56,10 @@
+
+ #include "dc_link_dp.h"
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++#include "dc_dmub_srv.h"
++#endif
++
+ #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
+ #include "dsc.h"
+ #endif
+@@ -2399,6 +2403,10 @@ void dc_set_power_state(
+ switch (power_state) {
+ case DC_ACPI_CM_POWER_STATE_D0:
+ dc_resource_state_construct(dc, dc->current_state);
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ if (dc->ctx->dmub_srv)
++ dc_dmub_srv_wait_phy_init(dc->ctx->dmub_srv);
++#endif
+
+ dc->hwss.init_hw(dc);
+
+diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
+index 0416a17b0897..33828f03fe9e 100644
+--- a/drivers/gpu/drm/amd/display/dc/dc.h
++++ b/drivers/gpu/drm/amd/display/dc/dc.h
+@@ -112,6 +112,9 @@ struct dc_caps {
+ bool disable_dp_clk_share;
+ bool psp_setup_panel_mode;
+ bool extended_aux_timeout_support;
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ bool dmcub_support;
++#endif
+ #ifdef CONFIG_DRM_AMD_DC_DCN2_0
+ bool hw_3d_lut;
+ #endif
+@@ -401,6 +404,11 @@ struct dc_debug_options {
+ unsigned int force_odm_combine; //bit vector based on otg inst
+ unsigned int force_fclk_khz;
+ bool disable_tri_buf;
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ bool dmub_offload_enabled;
++ bool dmcub_emulation;
++ bool dmub_command_table; /* for testing only */
++#endif
+ struct dc_bw_validation_profile bw_val_profile;
+ #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
+ bool disable_fec;
+@@ -558,6 +566,10 @@ struct dc_init_data {
+ struct dc_bios *vbios_override;
+ enum dce_environment dce_environment;
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ struct dmub_offload_funcs *dmub_if;
++ struct dc_reg_helper_state *dmub_offload;
++#endif
+ struct dc_config flags;
+ uint32_t log_mask;
+ #ifdef CONFIG_DRM_AMD_DC_DCN2_0
+diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
+new file mode 100644
+index 000000000000..61cefe0a3790
+--- /dev/null
++++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
+@@ -0,0 +1,119 @@
++/*
++ * Copyright 2019 Advanced Micro Devices, Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * Authors: AMD
++ *
++ */
++
++#include "dc.h"
++#include "dc_dmub_srv.h"
++#include "../dmub/inc/dmub_srv.h"
++
++static void dc_dmub_srv_construct(struct dc_dmub_srv *dc_srv, struct dc *dc,
++ struct dmub_srv *dmub)
++{
++ dc_srv->dmub = dmub;
++ dc_srv->ctx = dc->ctx;
++}
++
++struct dc_dmub_srv *dc_dmub_srv_create(struct dc *dc, struct dmub_srv *dmub)
++{
++ struct dc_dmub_srv *dc_srv =
++ kzalloc(sizeof(struct dc_dmub_srv), GFP_KERNEL);
++
++ if (dc_srv == NULL) {
++ BREAK_TO_DEBUGGER();
++ return NULL;
++ }
++
++ dc_dmub_srv_construct(dc_srv, dc, dmub);
++
++ return dc_srv;
++}
++
++void dc_dmub_srv_destroy(struct dc_dmub_srv **dmub_srv)
++{
++ if (*dmub_srv) {
++ kfree(*dmub_srv);
++ *dmub_srv = NULL;
++ }
++}
++
++void dc_dmub_srv_cmd_queue(struct dc_dmub_srv *dc_dmub_srv,
++ struct dmub_cmd_header *cmd)
++{
++ struct dmub_srv *dmub = dc_dmub_srv->dmub;
++ struct dc_context *dc_ctx = dc_dmub_srv->ctx;
++ enum dmub_status status;
++
++ status = dmub_srv_cmd_queue(dmub, cmd);
++ if (status == DMUB_STATUS_OK)
++ return;
++
++ if (status != DMUB_STATUS_QUEUE_FULL)
++ goto error;
++
++ /* Execute and wait for queue to become empty again. */
++ dc_dmub_srv_cmd_execute(dc_dmub_srv);
++ dc_dmub_srv_wait_idle(dc_dmub_srv);
++
++ /* Requeue the command. */
++ status = dmub_srv_cmd_queue(dmub, cmd);
++ if (status == DMUB_STATUS_OK)
++ return;
++
++error:
++ DC_ERROR("Error queuing DMUB command: status=%d\n", status);
++}
++
++void dc_dmub_srv_cmd_execute(struct dc_dmub_srv *dc_dmub_srv)
++{
++ struct dmub_srv *dmub = dc_dmub_srv->dmub;
++ struct dc_context *dc_ctx = dc_dmub_srv->ctx;
++ enum dmub_status status;
++
++ status = dmub_srv_cmd_execute(dmub);
++ if (status != DMUB_STATUS_OK)
++ DC_ERROR("Error starting DMUB exeuction: status=%d\n", status);
++}
++
++void dc_dmub_srv_wait_idle(struct dc_dmub_srv *dc_dmub_srv)
++{
++ struct dmub_srv *dmub = dc_dmub_srv->dmub;
++ struct dc_context *dc_ctx = dc_dmub_srv->ctx;
++ enum dmub_status status;
++
++ status = dmub_srv_wait_for_idle(dmub, 100000);
++ if (status != DMUB_STATUS_OK)
++ DC_ERROR("Error waiting for DMUB idle: status=%d\n", status);
++}
++
++void dc_dmub_srv_wait_phy_init(struct dc_dmub_srv *dc_dmub_srv)
++{
++ struct dmub_srv *dmub = dc_dmub_srv->dmub;
++ struct dc_context *dc_ctx = dc_dmub_srv->ctx;
++ enum dmub_status status;
++
++ status = dmub_srv_wait_for_phy_init(dmub, 1000000);
++ if (status != DMUB_STATUS_OK)
++ DC_ERROR("Error waiting for DMUB phy init: status=%d\n",
++ status);
++}
+diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
+new file mode 100644
+index 000000000000..754b6077539c
+--- /dev/null
++++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
+@@ -0,0 +1,60 @@
++/*
++ * Copyright 2018 Advanced Micro Devices, Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * Authors: AMD
++ *
++ */
++
++#ifndef _DMUB_DC_SRV_H_
++#define _DMUB_DC_SRV_H_
++
++#include "os_types.h"
++#include "../dmub/inc/dmub_cmd.h"
++
++struct dmub_srv;
++struct dmub_cmd_header;
++
++struct dc_reg_helper_state {
++ bool gather_in_progress;
++ uint32_t same_addr_count;
++ bool should_burst_write;
++ union dmub_rb_cmd cmd_data;
++ unsigned int reg_seq_count;
++};
++
++struct dc_dmub_srv {
++ struct dmub_srv *dmub;
++ struct dc_reg_helper_state reg_helper_offload;
++
++ struct dc_context *ctx;
++ void *dm;
++};
++
++void dc_dmub_srv_cmd_queue(struct dc_dmub_srv *dc_dmub_srv,
++ struct dmub_cmd_header *cmd);
++
++void dc_dmub_srv_cmd_execute(struct dc_dmub_srv *dc_dmub_srv);
++
++void dc_dmub_srv_wait_idle(struct dc_dmub_srv *dc_dmub_srv);
++
++void dc_dmub_srv_wait_phy_init(struct dc_dmub_srv *dc_dmub_srv);
++
++#endif /* _DMUB_DC_SRV_H_ */
+diff --git a/drivers/gpu/drm/amd/display/dc/dc_helper.c b/drivers/gpu/drm/amd/display/dc/dc_helper.c
+index 2d0acf109360..adfc6e9b59b1 100644
+--- a/drivers/gpu/drm/amd/display/dc/dc_helper.c
++++ b/drivers/gpu/drm/amd/display/dc/dc_helper.c
+@@ -29,6 +29,76 @@
+ #include "dm_services.h"
+ #include <stdarg.h>
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++#include "dc.h"
++#include "dc_dmub_srv.h"
++
++static inline void submit_dmub_read_modify_write(
++ struct dc_reg_helper_state *offload,
++ const struct dc_context *ctx)
++{
++ struct dmub_rb_cmd_read_modify_write *cmd_buf = &offload->cmd_data.read_modify_write;
++ bool gather = false;
++
++ offload->should_burst_write =
++ (offload->same_addr_count == (DMUB_READ_MODIFY_WRITE_SEQ__MAX - 1));
++ cmd_buf->header.payload_bytes =
++ sizeof(struct dmub_cmd_read_modify_write_sequence) * offload->reg_seq_count;
++
++ gather = ctx->dmub_srv->reg_helper_offload.gather_in_progress;
++ ctx->dmub_srv->reg_helper_offload.gather_in_progress = false;
++
++ dc_dmub_srv_cmd_queue(ctx->dmub_srv, &cmd_buf->header);
++
++ ctx->dmub_srv->reg_helper_offload.gather_in_progress = gather;
++
++ memset(cmd_buf, 0, sizeof(*cmd_buf));
++
++ offload->reg_seq_count = 0;
++ offload->same_addr_count = 0;
++}
++
++static inline void submit_dmub_burst_write(
++ struct dc_reg_helper_state *offload,
++ const struct dc_context *ctx)
++{
++ struct dmub_rb_cmd_burst_write *cmd_buf = &offload->cmd_data.burst_write;
++ bool gather = false;
++
++ cmd_buf->header.payload_bytes =
++ sizeof(uint32_t) * offload->reg_seq_count;
++
++ gather = ctx->dmub_srv->reg_helper_offload.gather_in_progress;
++ ctx->dmub_srv->reg_helper_offload.gather_in_progress = false;
++
++ dc_dmub_srv_cmd_queue(ctx->dmub_srv, &cmd_buf->header);
++
++ ctx->dmub_srv->reg_helper_offload.gather_in_progress = gather;
++
++ memset(cmd_buf, 0, sizeof(*cmd_buf));
++
++ offload->reg_seq_count = 0;
++}
++
++static inline void submit_dmub_reg_wait(
++ struct dc_reg_helper_state *offload,
++ const struct dc_context *ctx)
++{
++ struct dmub_rb_cmd_reg_wait *cmd_buf = &offload->cmd_data.reg_wait;
++ bool gather = false;
++
++ gather = ctx->dmub_srv->reg_helper_offload.gather_in_progress;
++ ctx->dmub_srv->reg_helper_offload.gather_in_progress = false;
++
++ dc_dmub_srv_cmd_queue(ctx->dmub_srv, &cmd_buf->header);
++
++ memset(cmd_buf, 0, sizeof(*cmd_buf));
++ offload->reg_seq_count = 0;
++
++ ctx->dmub_srv->reg_helper_offload.gather_in_progress = gather;
++}
++#endif
++
+ struct dc_reg_value_masks {
+ uint32_t value;
+ uint32_t mask;
+@@ -74,6 +144,100 @@ static void set_reg_field_values(struct dc_reg_value_masks *field_value_mask,
+ }
+ }
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++static void dmub_flush_buffer_execute(
++ struct dc_reg_helper_state *offload,
++ const struct dc_context *ctx)
++{
++ submit_dmub_read_modify_write(offload, ctx);
++ dc_dmub_srv_cmd_execute(ctx->dmub_srv);
++}
++
++static void dmub_flush_burst_write_buffer_execute(
++ struct dc_reg_helper_state *offload,
++ const struct dc_context *ctx)
++{
++ submit_dmub_burst_write(offload, ctx);
++ dc_dmub_srv_cmd_execute(ctx->dmub_srv);
++}
++
++static bool dmub_reg_value_burst_set_pack(const struct dc_context *ctx, uint32_t addr,
++ uint32_t reg_val)
++{
++ struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload;
++ struct dmub_rb_cmd_burst_write *cmd_buf = &offload->cmd_data.burst_write;
++
++ /* flush command if buffer is full */
++ if (offload->reg_seq_count == DMUB_BURST_WRITE_VALUES__MAX)
++ dmub_flush_burst_write_buffer_execute(offload, ctx);
++
++ if (offload->cmd_data.cmd_common.header.type == DMUB_CMD__REG_SEQ_BURST_WRITE &&
++ addr != cmd_buf->addr) {
++ dmub_flush_burst_write_buffer_execute(offload, ctx);
++ return false;
++ }
++
++ cmd_buf->header.type = DMUB_CMD__REG_SEQ_BURST_WRITE;
++ cmd_buf->addr = addr;
++ cmd_buf->write_values[offload->reg_seq_count] = reg_val;
++ offload->reg_seq_count++;
++
++ return true;
++}
++
++static uint32_t dmub_reg_value_pack(const struct dc_context *ctx, uint32_t addr,
++ struct dc_reg_value_masks *field_value_mask)
++{
++ struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload;
++ struct dmub_rb_cmd_read_modify_write *cmd_buf = &offload->cmd_data.read_modify_write;
++ struct dmub_cmd_read_modify_write_sequence *seq;
++
++ /* flush command if buffer is full */
++ if (offload->cmd_data.cmd_common.header.type != DMUB_CMD__REG_SEQ_BURST_WRITE &&
++ offload->reg_seq_count == DMUB_READ_MODIFY_WRITE_SEQ__MAX)
++ dmub_flush_buffer_execute(offload, ctx);
++
++ if (offload->should_burst_write) {
++ if (dmub_reg_value_burst_set_pack(ctx, addr, field_value_mask->value))
++ return field_value_mask->value;
++ else
++ offload->should_burst_write = false;
++ }
++
++ /* pack commands */
++ cmd_buf->header.type = DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE;
++ seq = &cmd_buf->seq[offload->reg_seq_count];
++
++ if (offload->reg_seq_count) {
++ if (cmd_buf->seq[offload->reg_seq_count - 1].addr == addr)
++ offload->same_addr_count++;
++ else
++ offload->same_addr_count = 0;
++ }
++
++ seq->addr = addr;
++ seq->modify_mask = field_value_mask->mask;
++ seq->modify_value = field_value_mask->value;
++ offload->reg_seq_count++;
++
++ return field_value_mask->value;
++}
++
++static void dmub_reg_wait_done_pack(const struct dc_context *ctx, uint32_t addr,
++ uint32_t mask, uint32_t shift, uint32_t condition_value, uint32_t time_out_us)
++{
++ struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload;
++ struct dmub_rb_cmd_reg_wait *cmd_buf = &offload->cmd_data.reg_wait;
++
++ cmd_buf->header.type = DMUB_CMD__REG_REG_WAIT;
++ cmd_buf->reg_wait.addr = addr;
++ cmd_buf->reg_wait.condition_field_value = mask & (condition_value << shift);
++ cmd_buf->reg_wait.mask = mask;
++ cmd_buf->reg_wait.time_out_us = time_out_us;
++}
++
++#endif
++
+ uint32_t generic_reg_update_ex(const struct dc_context *ctx,
+ uint32_t addr, int n,
+ uint8_t shift1, uint32_t mask1, uint32_t field_value1,
+@@ -90,6 +254,13 @@ uint32_t generic_reg_update_ex(const struct dc_context *ctx,
+
+ va_end(ap);
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ if (ctx->dmub_srv &&
++ ctx->dmub_srv->reg_helper_offload.gather_in_progress)
++ return dmub_reg_value_pack(ctx, addr, &field_value_mask);
++ /* todo: return void so we can decouple code running in driver from register states */
++#endif
++
+ /* mmio write directly */
+ reg_val = dm_read_reg(ctx, addr);
+ reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value;
+@@ -115,6 +286,13 @@ uint32_t generic_reg_set_ex(const struct dc_context *ctx,
+
+ /* mmio write directly */
+ reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value;
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ if (ctx->dmub_srv &&
++ ctx->dmub_srv->reg_helper_offload.gather_in_progress) {
++ return dmub_reg_value_burst_set_pack(ctx, addr, reg_val);
++ /* todo: return void so we can decouple code running in driver from register states */
++ }
++#endif
+ dm_write_reg(ctx, addr, reg_val);
+ return reg_val;
+ }
+@@ -131,6 +309,16 @@ uint32_t dm_read_reg_func(
+ return 0;
+ }
+ #endif
++
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ if (ctx->dmub_srv &&
++ ctx->dmub_srv->reg_helper_offload.gather_in_progress &&
++ !ctx->dmub_srv->reg_helper_offload.should_burst_write) {
++ ASSERT(false);
++ return 0;
++ }
++#endif
++
+ value = cgs_read_register(ctx->cgs_device, address);
+ trace_amdgpu_dc_rreg(&ctx->perf_trace->read_count, address, value);
+
+@@ -296,6 +484,15 @@ void generic_reg_wait(const struct dc_context *ctx,
+ uint32_t reg_val;
+ int i;
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ if (ctx->dmub_srv &&
++ ctx->dmub_srv->reg_helper_offload.gather_in_progress) {
++ dmub_reg_wait_done_pack(ctx, addr, mask, shift, condition_value,
++ delay_between_poll_us * time_out_num_tries);
++ return;
++ }
++#endif
++
+ /* something is terribly wrong if time out is > 200ms. (5Hz) */
+ ASSERT(delay_between_poll_us * time_out_num_tries <= 3000000);
+
+@@ -342,6 +539,13 @@ uint32_t generic_read_indirect_reg(const struct dc_context *ctx,
+ uint32_t index)
+ {
+ uint32_t value = 0;
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ // when reg read, there should not be any offload.
++ if (ctx->dmub_srv &&
++ ctx->dmub_srv->reg_helper_offload.gather_in_progress) {
++ ASSERT(false);
++ }
++#endif
+
+ dm_write_reg(ctx, addr_index, index);
+ value = dm_read_reg(ctx, addr_data);
+@@ -379,3 +583,72 @@ uint32_t generic_indirect_reg_update_ex(const struct dc_context *ctx,
+
+ return reg_val;
+ }
++
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++void reg_sequence_start_gather(const struct dc_context *ctx)
++{
++ /* if reg sequence is supported and enabled, set flag to
++ * indicate we want to have REG_SET, REG_UPDATE macro build
++ * reg sequence command buffer rather than MMIO directly.
++ */
++
++ if (ctx->dmub_srv && ctx->dc->debug.dmub_offload_enabled) {
++ struct dc_reg_helper_state *offload =
++ &ctx->dmub_srv->reg_helper_offload;
++
++ /* caller sequence mismatch. need to debug caller. offload will not work!!! */
++ ASSERT(!offload->gather_in_progress);
++
++ offload->gather_in_progress = true;
++ }
++}
++
++void reg_sequence_start_execute(const struct dc_context *ctx)
++{
++ struct dc_reg_helper_state *offload;
++
++ if (!ctx->dmub_srv)
++ return;
++
++ offload = &ctx->dmub_srv->reg_helper_offload;
++
++ if (offload && offload->gather_in_progress) {
++ offload->gather_in_progress = false;
++ offload->should_burst_write = false;
++ switch (offload->cmd_data.cmd_common.header.type) {
++ case DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE:
++ submit_dmub_read_modify_write(offload, ctx);
++ break;
++ case DMUB_CMD__REG_REG_WAIT:
++ submit_dmub_reg_wait(offload, ctx);
++ break;
++ case DMUB_CMD__REG_SEQ_BURST_WRITE:
++ submit_dmub_burst_write(offload, ctx);
++ break;
++ default:
++ return;
++ }
++
++ dc_dmub_srv_cmd_execute(ctx->dmub_srv);
++ }
++}
++
++void reg_sequence_wait_done(const struct dc_context *ctx)
++{
++ /* callback to DM to poll for last submission done*/
++ struct dc_reg_helper_state *offload;
++
++ if (!ctx->dmub_srv)
++ return;
++
++ offload = &ctx->dmub_srv->reg_helper_offload;
++
++ if (offload &&
++ ctx->dc->debug.dmub_offload_enabled &&
++ !ctx->dc->debug.dmcub_emulation) {
++ dc_dmub_srv_wait_idle(ctx->dmub_srv);
++ }
++}
++
++
++#endif
+diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h
+index d9be8fc3889f..fb70ed9b351f 100644
+--- a/drivers/gpu/drm/amd/display/dc/dc_types.h
++++ b/drivers/gpu/drm/amd/display/dc/dc_types.h
+@@ -48,6 +48,9 @@ struct dc_stream_state;
+ struct dc_link;
+ struct dc_sink;
+ struct dal;
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++struct dc_dmub_srv;
++#endif
+
+ /********************************
+ * Environment definitions
+@@ -109,6 +112,9 @@ struct dc_context {
+ uint32_t dc_sink_id_count;
+ uint32_t dc_stream_id_count;
+ uint64_t fbc_gpu_addr;
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ struct dc_dmub_srv *dmub_srv;
++#endif
+ #ifdef CONFIG_DRM_AMD_DC_HDCP
+ struct cp_psp cp_psp;
+ #endif
+diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_cm.c
+index aa0c7a7d13a0..41a0e53d2ba4 100644
+--- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_cm.c
++++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp_cm.c
+@@ -352,6 +352,9 @@ void dpp1_cm_program_regamma_lut(struct dpp *dpp_base,
+ uint32_t i;
+ struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base);
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ REG_SEQ_START();
++#endif
+ for (i = 0 ; i < num; i++) {
+ REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].red_reg);
+ REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].green_reg);
+@@ -630,6 +633,10 @@ void dpp1_set_degamma(
+ BREAK_TO_DEBUGGER();
+ break;
+ }
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ REG_SEQ_SUBMIT();
++ REG_SEQ_WAIT_DONE();
++#endif
+ }
+
+ void dpp1_degamma_ram_select(
+diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_mpc.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_mpc.c
+index 5a188b2bc033..2417d933ef2b 100644
+--- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_mpc.c
++++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_mpc.c
+@@ -345,6 +345,11 @@ static void mpc20_program_ogam_pwl(
+ uint32_t i;
+ struct dcn20_mpc *mpc20 = TO_DCN20_MPC(mpc);
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ PERF_TRACE();
++ REG_SEQ_START();
++#endif
++
+ for (i = 0 ; i < num; i++) {
+ REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, rgb[i].red_reg);
+ REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, rgb[i].green_reg);
+@@ -463,6 +468,12 @@ void mpc2_assert_mpcc_idle_before_connect(struct mpc *mpc, int mpcc_id)
+ ASSERT(!mpc_disabled);
+ ASSERT(!mpc_idle);
+ }
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ REG_SEQ_SUBMIT();
++ PERF_TRACE();
++ REG_SEQ_WAIT_DONE();
++ PERF_TRACE();
++#endif
+ }
+
+ static void mpc2_init_mpcc(struct mpcc *mpcc, int mpcc_inst)
+diff --git a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c
+index 0792b1c2e673..30a246ebe842 100644
+--- a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c
++++ b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c
+@@ -1699,6 +1699,9 @@ static bool construct(
+ dc->caps.post_blend_color_processing = true;
+ dc->caps.force_dp_tps4_for_cp2520 = true;
+ dc->caps.extended_aux_timeout_support = true;
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++ dc->caps.dmcub_support = true;
++#endif
+
+ if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
+ dc->debug = debug_defaults_drv;
+diff --git a/drivers/gpu/drm/amd/display/dc/dm_services.h b/drivers/gpu/drm/amd/display/dc/dm_services.h
+index 1a0429744630..0a3891edfd94 100644
+--- a/drivers/gpu/drm/amd/display/dc/dm_services.h
++++ b/drivers/gpu/drm/amd/display/dc/dm_services.h
+@@ -40,6 +40,11 @@
+
+ #undef DEPRECATED
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++struct dmub_srv;
++struct dc_dmub_srv;
++
++#endif
+ irq_handler_idx dm_register_interrupt(
+ struct dc_context *ctx,
+ struct dc_interrupt_params *int_params,
+@@ -139,6 +144,15 @@ uint32_t generic_reg_update_ex(const struct dc_context *ctx,
+ uint32_t addr, int n,
+ uint8_t shift1, uint32_t mask1, uint32_t field_value1, ...);
+
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++struct dc_dmub_srv *dc_dmub_srv_create(struct dc *dc, struct dmub_srv *dmub);
++void dc_dmub_srv_destroy(struct dc_dmub_srv **dmub_srv);
++
++void reg_sequence_start_gather(const struct dc_context *ctx);
++void reg_sequence_start_execute(const struct dc_context *ctx);
++void reg_sequence_wait_done(const struct dc_context *ctx);
++#endif
++
+ #define FD(reg_field) reg_field ## __SHIFT, \
+ reg_field ## _MASK
+
+diff --git a/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h b/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h
+index 8503d9cc4763..a9a9657c095a 100644
+--- a/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h
++++ b/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h
+@@ -485,4 +485,26 @@ uint32_t generic_indirect_reg_update_ex(const struct dc_context *ctx,
+ uint8_t shift1, uint32_t mask1, uint32_t field_value1,
+ ...);
+
++
++#ifdef CONFIG_DRM_AMD_DC_DMUB
++/* register offload macros
++ *
++ * instead of MMIO to register directly, in some cases we want
++ * to gather register sequence and execute the register sequence
++ * from another thread so we optimize time required for lengthy ops
++ */
++
++/* start gathering register sequence */
++#define REG_SEQ_START() \
++ reg_sequence_start_gather(CTX)
++
++/* start execution of register sequence gathered since REG_SEQ_START */
++#define REG_SEQ_SUBMIT() \
++ reg_sequence_start_execute(CTX)
++
++/* wait for the last REG_SEQ_SUBMIT to finish */
++#define REG_SEQ_WAIT_DONE() \
++ reg_sequence_wait_done(CTX)
++#endif
++
+ #endif /* DRIVERS_GPU_DRM_AMD_DC_DEV_DC_INC_REG_HELPER_H_ */
+diff --git a/drivers/gpu/drm/amd/display/dc/os_types.h b/drivers/gpu/drm/amd/display/dc/os_types.h
+index f20996e71274..77b97559e847 100644
+--- a/drivers/gpu/drm/amd/display/dc/os_types.h
++++ b/drivers/gpu/drm/amd/display/dc/os_types.h
+@@ -26,6 +26,8 @@
+ #ifndef _OS_TYPES_H_
+ #define _OS_TYPES_H_
+
++#include <linux/slab.h>
++
+ #include <asm/byteorder.h>
+ #include <linux/types.h>
+ #include <drm/drmP.h>
+--
+2.17.1
+