diff options
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4326-drm-amd-display-Add-the-DMUB-service.patch')
-rw-r--r-- | meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4326-drm-amd-display-Add-the-DMUB-service.patch | 2227 |
1 files changed, 2227 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4326-drm-amd-display-Add-the-DMUB-service.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4326-drm-amd-display-Add-the-DMUB-service.patch new file mode 100644 index 00000000..0ea61b42 --- /dev/null +++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/4326-drm-amd-display-Add-the-DMUB-service.patch @@ -0,0 +1,2227 @@ +From cee54bb1116f95c9283ccb627465db4f030f457f Mon Sep 17 00:00:00 2001 +From: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com> +Date: Fri, 25 Oct 2019 11:28:35 -0400 +Subject: [PATCH 4326/4736] drm/amd/display: Add the DMUB service + +The DMUB service is the interface to the DMCUB. + +It's required to support Renoir features so it will be enabled and +compiled automatically when the Renoir display engine is enabled via +CONFIG_DRM_AMD_DC_DCN2_1. + +DMUB code will initially be guarded by CONFIG_DRM_AMD_DC_DMUB and later +switched to CONFIG_DRM_AMD_DC_DCN2_1 with the config option dropped. + +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/Kconfig | 6 + + drivers/gpu/drm/amd/display/Makefile | 8 + + .../gpu/drm/amd/display/dmub/inc/dmub_cmd.h | 256 +++++++++ + .../gpu/drm/amd/display/dmub/inc/dmub_rb.h | 129 +++++ + .../gpu/drm/amd/display/dmub/inc/dmub_srv.h | 505 ++++++++++++++++++ + .../amd/display/dmub/inc/dmub_trace_buffer.h | 51 ++ + .../gpu/drm/amd/display/dmub/inc/dmub_types.h | 64 +++ + drivers/gpu/drm/amd/display/dmub/src/Makefile | 29 + + .../gpu/drm/amd/display/dmub/src/dmub_dcn20.c | 137 +++++ + .../gpu/drm/amd/display/dmub/src/dmub_dcn20.h | 62 +++ + .../gpu/drm/amd/display/dmub/src/dmub_dcn21.c | 126 +++++ + .../gpu/drm/amd/display/dmub/src/dmub_dcn21.h | 45 ++ + .../gpu/drm/amd/display/dmub/src/dmub_reg.c | 109 ++++ + .../gpu/drm/amd/display/dmub/src/dmub_reg.h | 120 +++++ + .../gpu/drm/amd/display/dmub/src/dmub_srv.c | 415 ++++++++++++++ + 15 files changed, 2062 insertions(+) + create mode 100644 drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h + create mode 100644 drivers/gpu/drm/amd/display/dmub/inc/dmub_rb.h + create mode 100644 drivers/gpu/drm/amd/display/dmub/inc/dmub_srv.h + create mode 100644 drivers/gpu/drm/amd/display/dmub/inc/dmub_trace_buffer.h + create mode 100644 drivers/gpu/drm/amd/display/dmub/inc/dmub_types.h + create mode 100644 drivers/gpu/drm/amd/display/dmub/src/Makefile + create mode 100644 drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c + create mode 100644 drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.h + create mode 100644 drivers/gpu/drm/amd/display/dmub/src/dmub_dcn21.c + create mode 100644 drivers/gpu/drm/amd/display/dmub/src/dmub_dcn21.h + create mode 100644 drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c + create mode 100644 drivers/gpu/drm/amd/display/dmub/src/dmub_reg.h + create mode 100644 drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c + +diff --git a/drivers/gpu/drm/amd/display/Kconfig b/drivers/gpu/drm/amd/display/Kconfig +index 9eae7c67ceb5..d9ee189aebf0 100644 +--- a/drivers/gpu/drm/amd/display/Kconfig ++++ b/drivers/gpu/drm/amd/display/Kconfig +@@ -29,6 +29,7 @@ config DRM_AMD_DC_DCN2_1 + bool "DCN 2.1 family" + depends on DRM_AMD_DC && X86 + depends on DRM_AMD_DC_DCN2_0 ++ select DRM_AMD_DC_DMUB + help + Choose this option if you want to have + Renoir support for display engine +@@ -51,6 +52,11 @@ config DRM_AMD_DC_HDCP + if you want to support + HDCP authentication + ++config DRM_AMD_DC_DMUB ++ def_bool n ++ help ++ DMUB support for display engine ++ + config DEBUG_KERNEL_DC + bool "Enable kgdb break in DC" + depends on DRM_AMD_DC +diff --git a/drivers/gpu/drm/amd/display/Makefile b/drivers/gpu/drm/amd/display/Makefile +index 36b3d6a5d04d..3c7332be4a89 100644 +--- a/drivers/gpu/drm/amd/display/Makefile ++++ b/drivers/gpu/drm/amd/display/Makefile +@@ -38,6 +38,10 @@ ifdef CONFIG_DRM_AMD_DC_HDCP + subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/hdcp + endif + ++ifdef CONFIG_DRM_AMD_DC_DMUB ++subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dmub/inc ++endif ++ + #TODO: remove when Timing Sync feature is complete + subdir-ccflags-y += -DBUILD_FEATURE_TIMING_SYNC=0 + +@@ -47,6 +51,10 @@ ifdef CONFIG_DRM_AMD_DC_HDCP + DAL_LIBS += modules/hdcp + endif + ++ifdef CONFIG_DRM_AMD_DC_DMUB ++DAL_LIBS += dmub/src ++endif ++ + AMD_DAL = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DISPLAY_PATH)/,$(DAL_LIBS))) + + include $(AMD_DAL) +diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h +new file mode 100644 +index 000000000000..b25f92e3280d +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h +@@ -0,0 +1,256 @@ ++/* ++ * Copyright 2019 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef _DMUB_CMD_H_ ++#define _DMUB_CMD_H_ ++ ++#include "dmub_types.h" ++#include "atomfirmware.h" ++ ++#define DMUB_RB_CMD_SIZE 64 ++#define DMUB_RB_MAX_ENTRY 128 ++#define DMUB_RB_SIZE (DMUB_RB_CMD_SIZE * DMUB_RB_MAX_ENTRY) ++#define REG_SET_MASK 0xFFFF ++ ++enum dmub_cmd_type { ++ DMUB_CMD__NULL, ++ DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE, ++ DMUB_CMD__REG_SEQ_FIELD_UPDATE_SEQ, ++ DMUB_CMD__REG_SEQ_BURST_WRITE, ++ DMUB_CMD__REG_REG_WAIT, ++ DMUB_CMD__DIGX_ENCODER_CONTROL, ++ DMUB_CMD__SET_PIXEL_CLOCK, ++ DMUB_CMD__ENABLE_DISP_POWER_GATING, ++ DMUB_CMD__DPPHY_INIT, ++ DMUB_CMD__DIG1_TRANSMITTER_CONTROL, ++ ++ // PSR ++ DMUB_CMD__PSR_ENABLE, ++ DMUB_CMD__PSR_DISABLE, ++ DMUB_CMD__PSR_COPY_SETTINGS, ++ DMUB_CMD__PSR_SET_LEVEL, ++}; ++ ++#pragma pack(push, 1) ++ ++struct dmub_cmd_header { ++ enum dmub_cmd_type type : 8; ++ unsigned int reserved0 : 16; ++ unsigned int payload_bytes : 6; /* up to 60 bytes */ ++ unsigned int reserved : 2; ++}; ++ ++/* ++ * Read modify write ++ * ++ * 60 payload bytes can hold up to 5 sets of read modify writes, ++ * each take 3 dwords. ++ * ++ * number of sequences = header.payload_bytes / sizeof(struct dmub_cmd_read_modify_write_sequence) ++ * ++ * modify_mask = 0xffff'ffff means all fields are going to be updated. in this case ++ * command parser will skip the read and we can use modify_mask = 0xffff'ffff as reg write ++ */ ++struct dmub_cmd_read_modify_write_sequence { ++ uint32_t addr; ++ uint32_t modify_mask; ++ uint32_t modify_value; ++}; ++ ++#define DMUB_READ_MODIFY_WRITE_SEQ__MAX 5 ++struct dmub_rb_cmd_read_modify_write { ++ struct dmub_cmd_header header; // type = DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE ++ struct dmub_cmd_read_modify_write_sequence seq[DMUB_READ_MODIFY_WRITE_SEQ__MAX]; ++}; ++ ++/* ++ * Update a register with specified masks and values sequeunce ++ * ++ * 60 payload bytes can hold address + up to 7 sets of mask/value combo, each take 2 dword ++ * ++ * number of field update sequence = (header.payload_bytes - sizeof(addr)) / sizeof(struct read_modify_write_sequence) ++ * ++ * ++ * USE CASE: ++ * 1. auto-increment register where additional read would update pointer and produce wrong result ++ * 2. toggle a bit without read in the middle ++ */ ++ ++struct dmub_cmd_reg_field_update_sequence { ++ uint32_t modify_mask; // 0xffff'ffff to skip initial read ++ uint32_t modify_value; ++}; ++ ++#define DMUB_REG_FIELD_UPDATE_SEQ__MAX 7 ++ ++struct dmub_rb_cmd_reg_field_update_sequence { ++ struct dmub_cmd_header header; ++ uint32_t addr; ++ struct dmub_cmd_reg_field_update_sequence seq[DMUB_REG_FIELD_UPDATE_SEQ__MAX]; ++}; ++ ++ ++/* ++ * Burst write ++ * ++ * support use case such as writing out LUTs. ++ * ++ * 60 payload bytes can hold up to 14 values to write to given address ++ * ++ * number of payload = header.payload_bytes / sizeof(struct read_modify_write_sequence) ++ */ ++#define DMUB_BURST_WRITE_VALUES__MAX 14 ++struct dmub_rb_cmd_burst_write { ++ struct dmub_cmd_header header; // type = DMUB_CMD__REG_SEQ_BURST_WRITE ++ uint32_t addr; ++ uint32_t write_values[DMUB_BURST_WRITE_VALUES__MAX]; ++}; ++ ++ ++struct dmub_rb_cmd_common { ++ struct dmub_cmd_header header; ++ uint8_t cmd_buffer[DMUB_RB_CMD_SIZE - sizeof(struct dmub_cmd_header)]; ++}; ++ ++struct dmub_cmd_reg_wait_data { ++ uint32_t addr; ++ uint32_t mask; ++ uint32_t condition_field_value; ++ uint32_t time_out_us; ++}; ++ ++struct dmub_rb_cmd_reg_wait { ++ struct dmub_cmd_header header; ++ struct dmub_cmd_reg_wait_data reg_wait; ++}; ++ ++struct dmub_cmd_digx_encoder_control_data { ++ union dig_encoder_control_parameters_v1_5 dig; ++}; ++ ++struct dmub_rb_cmd_digx_encoder_control { ++ struct dmub_cmd_header header; ++ struct dmub_cmd_digx_encoder_control_data encoder_control; ++}; ++ ++struct dmub_cmd_set_pixel_clock_data { ++ struct set_pixel_clock_parameter_v1_7 clk; ++}; ++ ++struct dmub_rb_cmd_set_pixel_clock { ++ struct dmub_cmd_header header; ++ struct dmub_cmd_set_pixel_clock_data pixel_clock; ++}; ++ ++struct dmub_cmd_enable_disp_power_gating_data { ++ struct enable_disp_power_gating_parameters_v2_1 pwr; ++}; ++ ++struct dmub_rb_cmd_enable_disp_power_gating { ++ struct dmub_cmd_header header; ++ struct dmub_cmd_enable_disp_power_gating_data power_gating; ++}; ++ ++struct dmub_cmd_dig1_transmitter_control_data { ++ struct dig_transmitter_control_parameters_v1_6 dig; ++}; ++ ++struct dmub_rb_cmd_dig1_transmitter_control { ++ struct dmub_cmd_header header; ++ struct dmub_cmd_dig1_transmitter_control_data transmitter_control; ++}; ++ ++struct dmub_rb_cmd_dpphy_init { ++ struct dmub_cmd_header header; ++ uint8_t reserved[60]; ++}; ++ ++struct dmub_cmd_psr_copy_settings_data { ++ uint32_t reg1; ++ uint32_t reg2; ++ uint32_t reg3; ++}; ++ ++struct dmub_rb_cmd_psr_copy_settings { ++ struct dmub_cmd_header header; ++ struct dmub_cmd_psr_copy_settings_data psr_copy_settings_data; ++}; ++ ++struct dmub_cmd_psr_set_level_data { ++ uint16_t psr_level; ++}; ++ ++struct dmub_rb_cmd_psr_set_level { ++ struct dmub_cmd_header header; ++ struct dmub_cmd_psr_set_level_data psr_set_level_data; ++}; ++ ++struct dmub_rb_cmd_psr_disable { ++ struct dmub_cmd_header header; ++}; ++ ++struct dmub_rb_cmd_psr_enable { ++ struct dmub_cmd_header header; ++}; ++ ++struct dmub_cmd_psr_notify_vblank_data { ++ uint32_t vblank_int; // Which vblank interrupt was triggered ++}; ++ ++struct dmub_rb_cmd_notify_vblank { ++ struct dmub_cmd_header header; ++ struct dmub_cmd_psr_notify_vblank_data psr_notify_vblank_data; ++}; ++ ++struct dmub_cmd_psr_notify_static_state_data { ++ uint32_t ss_int; // Which static screen interrupt was triggered ++ uint32_t ss_enter; // Enter (1) or exit (0) static screen ++}; ++ ++struct dmub_rb_cmd_psr_notify_static_state { ++ struct dmub_cmd_header header; ++ struct dmub_cmd_psr_notify_static_state_data psr_notify_static_state_data; ++}; ++ ++union dmub_rb_cmd { ++ struct dmub_rb_cmd_read_modify_write read_modify_write; ++ struct dmub_rb_cmd_reg_field_update_sequence reg_field_update_seq; ++ struct dmub_rb_cmd_burst_write burst_write; ++ struct dmub_rb_cmd_reg_wait reg_wait; ++ struct dmub_rb_cmd_common cmd_common; ++ struct dmub_rb_cmd_digx_encoder_control digx_encoder_control; ++ struct dmub_rb_cmd_set_pixel_clock set_pixel_clock; ++ struct dmub_rb_cmd_enable_disp_power_gating enable_disp_power_gating; ++ struct dmub_rb_cmd_dpphy_init dpphy_init; ++ struct dmub_rb_cmd_dig1_transmitter_control dig1_transmitter_control; ++ struct dmub_rb_cmd_psr_enable psr_enable; ++ struct dmub_rb_cmd_psr_disable psr_disable; ++ struct dmub_rb_cmd_psr_copy_settings psr_copy_settings; ++ struct dmub_rb_cmd_psr_set_level psr_set_level; ++}; ++ ++#pragma pack(pop) ++ ++#endif /* _DMUB_CMD_H_ */ +diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_rb.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_rb.h +new file mode 100644 +index 000000000000..ac22744eaa94 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_rb.h +@@ -0,0 +1,129 @@ ++/* ++ * Copyright 2019 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef _DMUB_RB_H_ ++#define _DMUB_RB_H_ ++ ++#include "dmub_types.h" ++#include "dmub_cmd.h" ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++struct dmub_cmd_header; ++ ++struct dmub_rb_init_params { ++ void *ctx; ++ void *base_address; ++ uint32_t capacity; ++}; ++ ++struct dmub_rb { ++ void *base_address; ++ uint32_t data_count; ++ uint32_t rptr; ++ uint32_t wrpt; ++ uint32_t capacity; ++ ++ void *ctx; ++ void *dmub; ++}; ++ ++ ++static inline bool dmub_rb_empty(struct dmub_rb *rb) ++{ ++ return (rb->wrpt == rb->rptr); ++} ++ ++static inline bool dmub_rb_full(struct dmub_rb *rb) ++{ ++ uint32_t data_count; ++ ++ if (rb->wrpt >= rb->rptr) ++ data_count = rb->wrpt - rb->rptr; ++ else ++ data_count = rb->capacity - (rb->rptr - rb->wrpt); ++ ++ return (data_count == (rb->capacity - DMUB_RB_CMD_SIZE)); ++} ++ ++static inline bool dmub_rb_push_front(struct dmub_rb *rb, ++ const struct dmub_cmd_header *cmd) ++{ ++ uint8_t *wt_ptr = (uint8_t *)(rb->base_address) + rb->wrpt; ++ ++ if (dmub_rb_full(rb)) ++ return false; ++ ++ dmub_memcpy(wt_ptr, cmd, DMUB_RB_CMD_SIZE); ++ rb->wrpt += DMUB_RB_CMD_SIZE; ++ ++ if (rb->wrpt >= rb->capacity) ++ rb->wrpt %= rb->capacity; ++ ++ return true; ++} ++ ++static inline bool dmub_rb_front(struct dmub_rb *rb, ++ struct dmub_cmd_header *cmd) ++{ ++ uint8_t *rd_ptr = (uint8_t *)rb->base_address + rb->rptr; ++ ++ if (dmub_rb_empty(rb)) ++ return false; ++ ++ dmub_memcpy(cmd, rd_ptr, DMUB_RB_CMD_SIZE); ++ ++ return true; ++} ++ ++static inline bool dmub_rb_pop_front(struct dmub_rb *rb) ++{ ++ if (dmub_rb_empty(rb)) ++ return false; ++ ++ rb->rptr += DMUB_RB_CMD_SIZE; ++ ++ if (rb->rptr >= rb->capacity) ++ rb->rptr %= rb->capacity; ++ ++ return true; ++} ++ ++static inline void dmub_rb_init(struct dmub_rb *rb, ++ struct dmub_rb_init_params *init_params) ++{ ++ rb->base_address = init_params->base_address; ++ rb->capacity = init_params->capacity; ++ rb->rptr = 0; ++ rb->wrpt = 0; ++} ++ ++#if defined(__cplusplus) ++} ++#endif ++ ++#endif /* _DMUB_RB_H_ */ +diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_srv.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_srv.h +new file mode 100644 +index 000000000000..aa8f0396616d +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_srv.h +@@ -0,0 +1,505 @@ ++/* ++ * Copyright 2019 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef _DMUB_SRV_H_ ++#define _DMUB_SRV_H_ ++ ++/** ++ * DOC: DMUB interface and operation ++ * ++ * DMUB is the interface to the display DMCUB microcontroller on DCN hardware. ++ * It delegates hardware initialization and command submission to the ++ * microcontroller. DMUB is the shortname for DMCUB. ++ * ++ * This interface is not thread-safe. Ensure that all access to the interface ++ * is properly synchronized by the caller. ++ * ++ * Initialization and usage of the DMUB service should be done in the ++ * steps given below: ++ * ++ * 1. dmub_srv_create() ++ * 2. dmub_srv_has_hw_support() ++ * 3. dmub_srv_calc_region_info() ++ * 4. dmub_srv_hw_init() ++ * ++ * The call to dmub_srv_create() is required to use the server. ++ * ++ * The calls to dmub_srv_has_hw_support() and dmub_srv_calc_region_info() ++ * are helpers to query cache window size and allocate framebuffer(s) ++ * for the cache windows. ++ * ++ * The call to dmub_srv_hw_init() programs the DMCUB registers to prepare ++ * for command submission. Commands can be queued via dmub_srv_cmd_queue() ++ * and executed via dmub_srv_cmd_execute(). ++ * ++ * If the queue is full the dmub_srv_wait_for_idle() call can be used to ++ * wait until the queue has been cleared. ++ * ++ * Destroying the DMUB service can be done by calling dmub_srv_destroy(). ++ * This does not clear DMUB hardware state, only software state. ++ * ++ * The interface is intended to be standalone and should not depend on any ++ * other component within DAL. ++ */ ++ ++#include "dmub_types.h" ++#include "dmub_cmd.h" ++#include "dmub_rb.h" ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++/* Forward declarations */ ++struct dmub_srv; ++struct dmub_cmd_header; ++struct dmcu; ++ ++/* enum dmub_status - return code for dmcub functions */ ++enum dmub_status { ++ DMUB_STATUS_OK = 0, ++ DMUB_STATUS_NO_CTX, ++ DMUB_STATUS_QUEUE_FULL, ++ DMUB_STATUS_TIMEOUT, ++ DMUB_STATUS_INVALID, ++}; ++ ++/* enum dmub_asic - dmub asic identifier */ ++enum dmub_asic { ++ DMUB_ASIC_NONE = 0, ++ DMUB_ASIC_DCN20, ++ DMUB_ASIC_DCN21, ++ DMUB_ASIC_MAX, ++}; ++ ++/* enum dmub_window_id - dmub window identifier */ ++enum dmub_window_id { ++ DMUB_WINDOW_0_INST_CONST = 0, ++ DMUB_WINDOW_1_STACK, ++ DMUB_WINDOW_2_BSS_DATA, ++ DMUB_WINDOW_3_VBIOS, ++ DMUB_WINDOW_4_MAILBOX, ++ DMUB_WINDOW_5_TRACEBUFF, ++ DMUB_WINDOW_6_RESERVED, ++ DMUB_WINDOW_7_RESERVED, ++ DMUB_WINDOW_TOTAL, ++}; ++ ++/** ++ * struct dmub_region - dmub hw memory region ++ * @base: base address for region, must be 256 byte aligned ++ * @top: top address for region ++ */ ++struct dmub_region { ++ uint32_t base; ++ uint32_t top; ++}; ++ ++/** ++ * struct dmub_window - dmub hw cache window ++ * @off: offset to the fb memory in gpu address space ++ * @r: region in uc address space for cache window ++ */ ++struct dmub_window { ++ union dmub_addr offset; ++ struct dmub_region region; ++}; ++ ++/** ++ * struct dmub_fb - defines a dmub framebuffer memory region ++ * @cpu_addr: cpu virtual address for the region, NULL if invalid ++ * @gpu_addr: gpu virtual address for the region, NULL if invalid ++ * @size: size of the region in bytes, zero if invalid ++ */ ++struct dmub_fb { ++ void *cpu_addr; ++ uint64_t gpu_addr; ++ uint32_t size; ++}; ++ ++/** ++ * struct dmub_srv_region_params - params used for calculating dmub regions ++ * @inst_const_size: size of the fw inst const section ++ * @bss_data_size: size of the fw bss data section ++ * @vbios_size: size of the vbios data ++ */ ++struct dmub_srv_region_params { ++ uint32_t inst_const_size; ++ uint32_t bss_data_size; ++ uint32_t vbios_size; ++}; ++ ++/** ++ * struct dmub_srv_region_info - output region info from the dmub service ++ * @fb_size: required minimum fb size for all regions, aligned to 4096 bytes ++ * @num_regions: number of regions used by the dmub service ++ * @regions: region info ++ * ++ * The regions are aligned such that they can be all placed within the ++ * same framebuffer but they can also be placed into different framebuffers. ++ * ++ * The size of each region can be calculated by the caller: ++ * size = reg.top - reg.base ++ * ++ * Care must be taken when performing custom allocations to ensure that each ++ * region base address is 256 byte aligned. ++ */ ++struct dmub_srv_region_info { ++ uint32_t fb_size; ++ uint8_t num_regions; ++ struct dmub_region regions[DMUB_WINDOW_TOTAL]; ++}; ++ ++/** ++ * struct dmub_srv_fb_params - parameters used for driver fb setup ++ * @region_info: region info calculated by dmub service ++ * @cpu_addr: base cpu address for the framebuffer ++ * @gpu_addr: base gpu virtual address for the framebuffer ++ */ ++struct dmub_srv_fb_params { ++ const struct dmub_srv_region_info *region_info; ++ void *cpu_addr; ++ uint64_t gpu_addr; ++}; ++ ++/** ++ * struct dmub_srv_fb_info - output fb info from the dmub service ++ * @num_fbs: number of required dmub framebuffers ++ * @fbs: fb data for each region ++ * ++ * Output from the dmub service helper that can be used by the ++ * driver to prepare dmub_fb that can be passed into the dmub ++ * hw init service. ++ * ++ * Assumes that all regions are within the same framebuffer ++ * and have been setup according to the region_info generated ++ * by the dmub service. ++ */ ++struct dmub_srv_fb_info { ++ uint8_t num_fb; ++ struct dmub_fb fb[DMUB_WINDOW_TOTAL]; ++}; ++ ++/** ++ * struct dmub_srv_base_funcs - Driver specific base callbacks ++ */ ++struct dmub_srv_base_funcs { ++ /** ++ * @reg_read: ++ * ++ * Hook for reading a register. ++ * ++ * Return: The 32-bit register value from the given address. ++ */ ++ uint32_t (*reg_read)(void *ctx, uint32_t address); ++ ++ /** ++ * @reg_write: ++ * ++ * Hook for writing a value to the register specified by address. ++ */ ++ void (*reg_write)(void *ctx, uint32_t address, uint32_t value); ++}; ++ ++/** ++ * struct dmub_srv_hw_funcs - hardware sequencer funcs for dmub ++ */ ++struct dmub_srv_hw_funcs { ++ /* private: internal use only */ ++ ++ void (*reset)(struct dmub_srv *dmub); ++ ++ void (*reset_release)(struct dmub_srv *dmub); ++ ++ void (*backdoor_load)(struct dmub_srv *dmub, ++ const struct dmub_window *cw0, ++ const struct dmub_window *cw1); ++ ++ void (*setup_windows)(struct dmub_srv *dmub, ++ const struct dmub_window *cw2, ++ const struct dmub_window *cw3, ++ const struct dmub_window *cw4, ++ const struct dmub_window *cw5); ++ ++ void (*setup_mailbox)(struct dmub_srv *dmub, ++ const struct dmub_region *inbox1); ++ ++ uint32_t (*get_inbox1_rptr)(struct dmub_srv *dmub); ++ ++ void (*set_inbox1_wptr)(struct dmub_srv *dmub, uint32_t wptr_offset); ++ ++ bool (*is_supported)(struct dmub_srv *dmub); ++ ++ bool (*is_phy_init)(struct dmub_srv *dmub); ++ ++ bool (*is_auto_load_done)(struct dmub_srv *dmub); ++}; ++ ++/** ++ * struct dmub_srv_create_params - params for dmub service creation ++ * @base_funcs: driver supplied base routines ++ * @hw_funcs: optional overrides for hw funcs ++ * @user_ctx: context data for callback funcs ++ * @asic: driver supplied asic ++ * @is_virtual: false for hw support only ++ */ ++struct dmub_srv_create_params { ++ struct dmub_srv_base_funcs funcs; ++ struct dmub_srv_hw_funcs *hw_funcs; ++ void *user_ctx; ++ enum dmub_asic asic; ++ bool is_virtual; ++}; ++ ++/* ++ * struct dmub_srv_hw_params - params for dmub hardware initialization ++ * @fb: framebuffer info for each region ++ * @fb_base: base of the framebuffer aperture ++ * @fb_offset: offset of the framebuffer aperture ++ * @psp_version: psp version to pass for DMCU init ++ */ ++struct dmub_srv_hw_params { ++ struct dmub_fb *fb[DMUB_WINDOW_TOTAL]; ++ uint64_t fb_base; ++ uint64_t fb_offset; ++ uint32_t psp_version; ++}; ++ ++/** ++ * struct dmub_srv - software state for dmcub ++ * @asic: dmub asic identifier ++ * @user_ctx: user provided context for the dmub_srv ++ * @is_virtual: false if hardware support only ++ */ ++struct dmub_srv { ++ enum dmub_asic asic; ++ void *user_ctx; ++ bool is_virtual; ++ ++ /* private: internal use only */ ++ struct dmub_srv_base_funcs funcs; ++ struct dmub_srv_hw_funcs hw_funcs; ++ struct dmub_rb inbox1_rb; ++ ++ bool sw_init; ++ bool hw_init; ++ ++ uint64_t fb_base; ++ uint64_t fb_offset; ++ uint32_t psp_version; ++}; ++ ++/** ++ * dmub_srv_create() - creates the DMUB service. ++ * @dmub: the dmub service ++ * @params: creation parameters for the service ++ * ++ * Return: ++ * DMUB_STATUS_OK - success ++ * DMUB_STATUS_INVALID - unspecified error ++ */ ++enum dmub_status dmub_srv_create(struct dmub_srv *dmub, ++ const struct dmub_srv_create_params *params); ++ ++/** ++ * dmub_srv_destroy() - destroys the DMUB service. ++ * @dmub: the dmub service ++ */ ++void dmub_srv_destroy(struct dmub_srv *dmub); ++ ++/** ++ * dmub_srv_calc_region_info() - retreives region info from the dmub service ++ * @dmub: the dmub service ++ * @params: parameters used to calculate region locations ++ * @info_out: the output region info from dmub ++ * ++ * Calculates the base and top address for all relevant dmub regions ++ * using the parameters given (if any). ++ * ++ * Return: ++ * DMUB_STATUS_OK - success ++ * DMUB_STATUS_INVALID - unspecified error ++ */ ++enum dmub_status ++dmub_srv_calc_region_info(struct dmub_srv *dmub, ++ const struct dmub_srv_region_params *params, ++ struct dmub_srv_region_info *out); ++ ++/** ++ * dmub_srv_calc_region_info() - retreives fb info from the dmub service ++ * @dmub: the dmub service ++ * @params: parameters used to calculate fb locations ++ * @info_out: the output fb info from dmub ++ * ++ * Calculates the base and top address for all relevant dmub regions ++ * using the parameters given (if any). ++ * ++ * Return: ++ * DMUB_STATUS_OK - success ++ * DMUB_STATUS_INVALID - unspecified error ++ */ ++enum dmub_status dmub_srv_calc_fb_info(struct dmub_srv *dmub, ++ const struct dmub_srv_fb_params *params, ++ struct dmub_srv_fb_info *out); ++ ++/** ++ * dmub_srv_has_hw_support() - returns hw support state for dmcub ++ * @dmub: the dmub service ++ * @is_supported: hw support state ++ * ++ * Queries the hardware for DMCUB support and returns the result. ++ * ++ * Can be called before dmub_srv_hw_init(). ++ * ++ * Return: ++ * DMUB_STATUS_OK - success ++ * DMUB_STATUS_INVALID - unspecified error ++ */ ++enum dmub_status dmub_srv_has_hw_support(struct dmub_srv *dmub, ++ bool *is_supported); ++ ++/** ++ * dmub_srv_hw_init() - initializes the underlying DMUB hardware ++ * @dmub: the dmub service ++ * @params: params for hardware initialization ++ * ++ * Resets the DMUB hardware and performs backdoor loading of the ++ * required cache regions based on the input framebuffer regions. ++ * ++ * Return: ++ * DMUB_STATUS_OK - success ++ * DMUB_STATUS_NO_CTX - dmcub context not initialized ++ * DMUB_STATUS_INVALID - unspecified error ++ */ ++enum dmub_status dmub_srv_hw_init(struct dmub_srv *dmub, ++ const struct dmub_srv_hw_params *params); ++ ++/** ++ * dmub_srv_cmd_queue() - queues a command to the DMUB ++ * @dmub: the dmub service ++ * @cmd: the command to queue ++ * ++ * Queues a command to the DMUB service but does not begin execution ++ * immediately. ++ * ++ * Return: ++ * DMUB_STATUS_OK - success ++ * DMUB_STATUS_QUEUE_FULL - no remaining room in queue ++ * DMUB_STATUS_INVALID - unspecified error ++ */ ++enum dmub_status dmub_srv_cmd_queue(struct dmub_srv *dmub, ++ const struct dmub_cmd_header *cmd); ++ ++/** ++ * dmub_srv_cmd_execute() - Executes a queued sequence to the dmub ++ * @dmub: the dmub service ++ * ++ * Begins exeuction of queued commands on the dmub. ++ * ++ * Return: ++ * DMUB_STATUS_OK - success ++ * DMUB_STATUS_INVALID - unspecified error ++ */ ++enum dmub_status dmub_srv_cmd_execute(struct dmub_srv *dmub); ++ ++/** ++ * dmub_srv_cmd_submit() - submits a command to the DMUB immediately ++ * @dmub: the dmub service ++ * @cmd: the command to submit ++ * @timeout_us: the maximum number of microseconds to wait ++ * ++ * Submits a command to the DMUB with an optional timeout. ++ * If timeout_us is given then the service will attempt to ++ * resubmit for the given number of microseconds. ++ * ++ * Return: ++ * DMUB_STATUS_OK - success ++ * DMUB_STATUS_TIMEOUT - wait for submit timed out ++ * DMUB_STATUS_INVALID - unspecified error ++ */ ++enum dmub_status dmub_srv_cmd_submit(struct dmub_srv *dmub, ++ const struct dmub_cmd_header *cmd, ++ uint32_t timeout_us); ++ ++/** ++ * dmub_srv_wait_for_auto_load() - Waits for firmware auto load to complete ++ * @dmub: the dmub service ++ * @timeout_us: the maximum number of microseconds to wait ++ * ++ * Waits until firmware has been autoloaded by the DMCUB. The maximum ++ * wait time is given in microseconds to prevent spinning forever. ++ * ++ * On ASICs without firmware autoload support this function will return ++ * immediately. ++ * ++ * Return: ++ * DMUB_STATUS_OK - success ++ * DMUB_STATUS_TIMEOUT - wait for phy init timed out ++ * DMUB_STATUS_INVALID - unspecified error ++ */ ++enum dmub_status dmub_srv_wait_for_auto_load(struct dmub_srv *dmub, ++ uint32_t timeout_us); ++ ++/** ++ * dmub_srv_wait_for_phy_init() - Waits for DMUB PHY init to complete ++ * @dmub: the dmub service ++ * @timeout_us: the maximum number of microseconds to wait ++ * ++ * Waits until the PHY has been initialized by the DMUB. The maximum ++ * wait time is given in microseconds to prevent spinning forever. ++ * ++ * On ASICs without PHY init support this function will return ++ * immediately. ++ * ++ * Return: ++ * DMUB_STATUS_OK - success ++ * DMUB_STATUS_TIMEOUT - wait for phy init timed out ++ * DMUB_STATUS_INVALID - unspecified error ++ */ ++enum dmub_status dmub_srv_wait_for_phy_init(struct dmub_srv *dmub, ++ uint32_t timeout_us); ++ ++/** ++ * dmub_srv_wait_for_idle() - Waits for the DMUB to be idle ++ * @dmub: the dmub service ++ * @timeout_us: the maximum number of microseconds to wait ++ * ++ * Waits until the DMUB buffer is empty and all commands have ++ * finished processing. The maximum wait time is given in ++ * microseconds to prevent spinning forever. ++ * ++ * Return: ++ * DMUB_STATUS_OK - success ++ * DMUB_STATUS_TIMEOUT - wait for buffer to flush timed out ++ * DMUB_STATUS_INVALID - unspecified error ++ */ ++enum dmub_status dmub_srv_wait_for_idle(struct dmub_srv *dmub, ++ uint32_t timeout_us); ++ ++#if defined(__cplusplus) ++} ++#endif ++ ++#endif /* _DMUB_SRV_H_ */ +diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_trace_buffer.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_trace_buffer.h +new file mode 100644 +index 000000000000..9707706ba8ce +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_trace_buffer.h +@@ -0,0 +1,51 @@ ++/* ++ * Copyright 2019 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++#ifndef _DMUB_TRACE_BUFFER_H_ ++#define _DMUB_TRACE_BUFFER_H_ ++ ++#include "dmub_types.h" ++ ++#define LOAD_DMCU_FW 1 ++#define LOAD_PHY_FW 2 ++ ++struct dmcub_trace_buf_entry { ++ uint32_t trace_code; ++ uint32_t tick_count; ++ uint32_t param0; ++ uint32_t param1; ++}; ++ ++#define TRACE_BUF_SIZE (1024) //1 kB ++#define PERF_TRACE_MAX_ENTRY ((TRACE_BUF_SIZE - 8)/sizeof(struct dmcub_trace_buf_entry)) ++ ++struct dmcub_trace_buf { ++ uint32_t entry_count; ++ uint32_t clk_freq; ++ struct dmcub_trace_buf_entry entries[PERF_TRACE_MAX_ENTRY]; ++}; ++ ++ ++ ++#endif /* _DMUB_TRACE_BUFFER_H_ */ +diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_types.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_types.h +new file mode 100644 +index 000000000000..41d524b0db2f +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_types.h +@@ -0,0 +1,64 @@ ++/* ++ * Copyright 2019 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef _DMUB_TYPES_H_ ++#define _DMUB_TYPES_H_ ++ ++/* Basic type definitions. */ ++#include <asm/byteorder.h> ++#include <linux/types.h> ++#include <linux/string.h> ++#include <linux/delay.h> ++#include <stdarg.h> ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++#ifndef dmub_memcpy ++#define dmub_memcpy(dest, source, bytes) memcpy((dest), (source), (bytes)) ++#endif ++ ++#ifndef dmub_memset ++#define dmub_memset(dest, val, bytes) memset((dest), (val), (bytes)) ++#endif ++ ++#ifndef dmub_udelay ++#define dmub_udelay(microseconds) udelay(microseconds) ++#endif ++ ++union dmub_addr { ++ struct { ++ uint32_t low_part; ++ uint32_t high_part; ++ } u; ++ uint64_t quad_part; ++}; ++ ++#if defined(__cplusplus) ++} ++#endif ++ ++#endif /* _DMUB_TYPES_H_ */ +diff --git a/drivers/gpu/drm/amd/display/dmub/src/Makefile b/drivers/gpu/drm/amd/display/dmub/src/Makefile +new file mode 100644 +index 000000000000..f3b844f474fd +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/src/Makefile +@@ -0,0 +1,29 @@ ++# ++# 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. ++# ++ ++ifdef CONFIG_DRM_AMD_DC_DMUB ++DMUB = dmub_srv.o dmub_reg.o dmub_dcn20.o dmub_dcn21.o ++ ++AMD_DAL_DMUB = $(addprefix $(AMDDALPATH)/dmub/src/,$(DMUB)) ++ ++AMD_DISPLAY_FILES += $(AMD_DAL_DMUB) ++endif +diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c +new file mode 100644 +index 000000000000..236a4156bbe1 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.c +@@ -0,0 +1,137 @@ ++/* ++ * 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 "../inc/dmub_srv.h" ++#include "dmub_reg.h" ++ ++#include "dcn/dcn_2_0_0_offset.h" ++#include "dcn/dcn_2_0_0_sh_mask.h" ++#include "soc15_hw_ip.h" ++#include "vega10_ip_offset.h" ++ ++#define BASE_INNER(seg) DCN_BASE__INST0_SEG##seg ++#define CTX dmub ++ ++void dmub_dcn20_reset(struct dmub_srv *dmub) ++{ ++ REG_UPDATE(DMCUB_CNTL, DMCUB_SOFT_RESET, 1); ++ REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0); ++} ++ ++void dmub_dcn20_reset_release(struct dmub_srv *dmub) ++{ ++ REG_WRITE(DMCUB_SCRATCH15, dmub->psp_version & 0x001100FF); ++ REG_UPDATE_2(DMCUB_CNTL, DMCUB_ENABLE, 1, DMCUB_TRACEPORT_EN, 1); ++ REG_UPDATE(DMCUB_CNTL, DMCUB_SOFT_RESET, 0); ++} ++ ++void dmub_dcn20_backdoor_load(struct dmub_srv *dmub, struct dmub_window *cw0, ++ struct dmub_window *cw1) ++{ ++ REG_UPDATE(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 1); ++ REG_UPDATE_2(DMCUB_MEM_CNTL, DMCUB_MEM_READ_SPACE, 0x4, ++ DMCUB_MEM_WRITE_SPACE, 0x4); ++ ++ REG_WRITE(DMCUB_REGION3_CW0_OFFSET, cw0->offset.u.low_part); ++ REG_WRITE(DMCUB_REGION3_CW0_OFFSET_HIGH, cw0->offset.u.high_part); ++ REG_WRITE(DMCUB_REGION3_CW0_BASE_ADDRESS, cw0->region.base); ++ REG_SET_2(DMCUB_REGION3_CW0_TOP_ADDRESS, 0, ++ DMCUB_REGION3_CW0_TOP_ADDRESS, cw0->region.top, ++ DMCUB_REGION3_CW0_ENABLE, 1); ++ ++ REG_WRITE(DMCUB_REGION3_CW1_OFFSET, cw1->offset.u.low_part); ++ REG_WRITE(DMCUB_REGION3_CW1_OFFSET_HIGH, cw1->offset.u.high_part); ++ REG_WRITE(DMCUB_REGION3_CW1_BASE_ADDRESS, cw1->region.base); ++ REG_SET_2(DMCUB_REGION3_CW1_TOP_ADDRESS, 0, ++ DMCUB_REGION3_CW1_TOP_ADDRESS, cw1->region.top, ++ DMCUB_REGION3_CW1_ENABLE, 1); ++ ++ REG_UPDATE_2(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 0, DMCUB_MEM_UNIT_ID, ++ 0x20); ++} ++ ++void dmub_dcn20_setup_windows(struct dmub_srv *dmub, ++ const struct dmub_window *cw2, ++ const struct dmub_window *cw3, ++ const struct dmub_window *cw4, ++ const struct dmub_window *cw5) ++{ ++ REG_WRITE(DMCUB_REGION3_CW2_OFFSET, cw2->offset.u.low_part); ++ REG_WRITE(DMCUB_REGION3_CW2_OFFSET_HIGH, cw2->offset.u.high_part); ++ REG_WRITE(DMCUB_REGION3_CW2_BASE_ADDRESS, cw2->region.base); ++ REG_SET_2(DMCUB_REGION3_CW2_TOP_ADDRESS, 0, ++ DMCUB_REGION3_CW2_TOP_ADDRESS, cw2->region.top, ++ DMCUB_REGION3_CW2_ENABLE, 1); ++ ++ REG_WRITE(DMCUB_REGION3_CW3_OFFSET, cw3->offset.u.low_part); ++ REG_WRITE(DMCUB_REGION3_CW3_OFFSET_HIGH, cw3->offset.u.high_part); ++ REG_WRITE(DMCUB_REGION3_CW3_BASE_ADDRESS, cw3->region.base); ++ REG_SET_2(DMCUB_REGION3_CW3_TOP_ADDRESS, 0, ++ DMCUB_REGION3_CW3_TOP_ADDRESS, cw3->region.top, ++ DMCUB_REGION3_CW3_ENABLE, 1); ++ ++ /* TODO: Move this to CW4. */ ++ ++ REG_WRITE(DMCUB_REGION4_OFFSET, cw4->offset.u.low_part); ++ REG_WRITE(DMCUB_REGION4_OFFSET_HIGH, cw4->offset.u.high_part); ++ REG_SET_2(DMCUB_REGION4_TOP_ADDRESS, 0, DMCUB_REGION4_TOP_ADDRESS, ++ cw4->region.top - cw4->region.base - 1, DMCUB_REGION4_ENABLE, ++ 1); ++} ++ ++void dmub_dcn20_setup_mailbox(struct dmub_srv *dmub, ++ const struct dmub_region *inbox1) ++{ ++ /* TODO: Use CW4 instead of region 4. */ ++ ++ REG_WRITE(DMCUB_INBOX1_BASE_ADDRESS, 0x80000000); ++ REG_WRITE(DMCUB_INBOX1_SIZE, inbox1->top - inbox1->base); ++ REG_WRITE(DMCUB_INBOX1_RPTR, 0); ++ REG_WRITE(DMCUB_INBOX1_WPTR, 0); ++} ++ ++uint32_t dmub_dcn20_get_inbox1_rptr(struct dmub_srv *dmub) ++{ ++ return REG_READ(DMCUB_INBOX1_RPTR); ++} ++ ++void dmub_dcn20_set_inbox1_wptr(struct dmub_srv *dmub, uint32_t wptr_offset) ++{ ++ REG_WRITE(DMCUB_INBOX1_WPTR, wptr_offset); ++} ++ ++bool dmub_dcn20_is_supported(struct dmub_srv *dmub) ++{ ++ uint32_t supported = 0; ++ ++ REG_GET(CC_DC_PIPE_DIS, DC_DMCUB_ENABLE, &supported); ++ ++ return supported; ++} ++ ++bool dmub_dcn20_is_phy_init(struct dmub_srv *dmub) ++{ ++ return REG_READ(DMCUB_SCRATCH10) != 0; ++} +diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.h b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.h +new file mode 100644 +index 000000000000..41269da40363 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn20.h +@@ -0,0 +1,62 @@ ++/* ++ * Copyright 2019 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef _DMUB_DCN20_H_ ++#define _DMUB_DCN20_H_ ++ ++#include "../inc/dmub_types.h" ++ ++struct dmub_srv; ++ ++/* Hardware functions. */ ++ ++void dmub_dcn20_init(struct dmub_srv *dmub); ++ ++void dmub_dcn20_reset(struct dmub_srv *dmub); ++ ++void dmub_dcn20_reset_release(struct dmub_srv *dmub); ++ ++void dmub_dcn20_backdoor_load(struct dmub_srv *dmub, ++ const struct dmub_window *cw0, ++ const struct dmub_window *cw1); ++ ++void dmub_dcn20_setup_windows(struct dmub_srv *dmub, ++ const struct dmub_window *cw2, ++ const struct dmub_window *cw3, ++ const struct dmub_window *cw4, ++ const struct dmub_window *cw5); ++ ++void dmub_dcn20_setup_mailbox(struct dmub_srv *dmub, ++ const struct dmub_region *inbox1); ++ ++uint32_t dmub_dcn20_get_inbox1_rptr(struct dmub_srv *dmub); ++ ++void dmub_dcn20_set_inbox1_wptr(struct dmub_srv *dmub, uint32_t wptr_offset); ++ ++bool dmub_dcn20_is_supported(struct dmub_srv *dmub); ++ ++bool dmub_dcn20_is_phy_init(struct dmub_srv *dmub); ++ ++#endif /* _DMUB_DCN20_H_ */ +diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn21.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn21.c +new file mode 100644 +index 000000000000..d40a808112e7 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn21.c +@@ -0,0 +1,126 @@ ++/* ++ * 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 "../inc/dmub_srv.h" ++#include "dmub_reg.h" ++ ++#include "dcn/dcn_2_1_0_offset.h" ++#include "dcn/dcn_2_1_0_sh_mask.h" ++#include "renoir_ip_offset.h" ++ ++#define BASE_INNER(seg) DMU_BASE__INST0_SEG##seg ++#define CTX dmub ++ ++static inline void dmub_dcn21_translate_addr(const union dmub_addr *addr_in, ++ uint64_t fb_base, ++ uint64_t fb_offset, ++ union dmub_addr *addr_out) ++{ ++ addr_out->quad_part = addr_in->quad_part - fb_base + fb_offset; ++} ++ ++void dmub_dcn21_backdoor_load(struct dmub_srv *dmub, ++ const struct dmub_window *cw0, ++ const struct dmub_window *cw1) ++{ ++ union dmub_addr offset; ++ uint64_t fb_base = dmub->fb_base, fb_offset = dmub->fb_offset; ++ ++ REG_UPDATE(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 1); ++ REG_UPDATE_2(DMCUB_MEM_CNTL, DMCUB_MEM_READ_SPACE, 0x3, ++ DMCUB_MEM_WRITE_SPACE, 0x3); ++ ++ dmub_dcn21_translate_addr(&cw0->offset, fb_base, fb_offset, &offset); ++ ++ REG_WRITE(DMCUB_REGION3_CW0_OFFSET, offset.u.low_part); ++ REG_WRITE(DMCUB_REGION3_CW0_OFFSET_HIGH, offset.u.high_part); ++ REG_WRITE(DMCUB_REGION3_CW0_BASE_ADDRESS, cw0->region.base); ++ REG_SET_2(DMCUB_REGION3_CW0_TOP_ADDRESS, 0, ++ DMCUB_REGION3_CW0_TOP_ADDRESS, cw0->region.top, ++ DMCUB_REGION3_CW0_ENABLE, 1); ++ ++ dmub_dcn21_translate_addr(&cw1->offset, fb_base, fb_offset, &offset); ++ ++ REG_WRITE(DMCUB_REGION3_CW1_OFFSET, offset.u.low_part); ++ REG_WRITE(DMCUB_REGION3_CW1_OFFSET_HIGH, offset.u.high_part); ++ REG_WRITE(DMCUB_REGION3_CW1_BASE_ADDRESS, cw1->region.base); ++ REG_SET_2(DMCUB_REGION3_CW1_TOP_ADDRESS, 0, ++ DMCUB_REGION3_CW1_TOP_ADDRESS, cw1->region.top, ++ DMCUB_REGION3_CW1_ENABLE, 1); ++ ++ REG_UPDATE_2(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 0, DMCUB_MEM_UNIT_ID, ++ 0x20); ++} ++ ++void dmub_dcn21_setup_windows(struct dmub_srv *dmub, ++ const struct dmub_window *cw2, ++ const struct dmub_window *cw3, ++ const struct dmub_window *cw4, ++ const struct dmub_window *cw5) ++{ ++ union dmub_addr offset; ++ uint64_t fb_base = dmub->fb_base, fb_offset = dmub->fb_offset; ++ ++ dmub_dcn21_translate_addr(&cw2->offset, fb_base, fb_offset, &offset); ++ ++ REG_WRITE(DMCUB_REGION3_CW2_OFFSET, offset.u.low_part); ++ REG_WRITE(DMCUB_REGION3_CW2_OFFSET_HIGH, offset.u.high_part); ++ REG_WRITE(DMCUB_REGION3_CW2_BASE_ADDRESS, cw2->region.base); ++ REG_SET_2(DMCUB_REGION3_CW2_TOP_ADDRESS, 0, ++ DMCUB_REGION3_CW2_TOP_ADDRESS, cw2->region.top, ++ DMCUB_REGION3_CW2_ENABLE, 1); ++ ++ dmub_dcn21_translate_addr(&cw3->offset, fb_base, fb_offset, &offset); ++ ++ REG_WRITE(DMCUB_REGION3_CW3_OFFSET, offset.u.low_part); ++ REG_WRITE(DMCUB_REGION3_CW3_OFFSET_HIGH, offset.u.high_part); ++ REG_WRITE(DMCUB_REGION3_CW3_BASE_ADDRESS, cw3->region.base); ++ REG_SET_2(DMCUB_REGION3_CW3_TOP_ADDRESS, 0, ++ DMCUB_REGION3_CW3_TOP_ADDRESS, cw3->region.top, ++ DMCUB_REGION3_CW3_ENABLE, 1); ++ ++ /* TODO: Move this to CW4. */ ++ dmub_dcn21_translate_addr(&cw4->offset, fb_base, fb_offset, &offset); ++ ++ REG_WRITE(DMCUB_REGION4_OFFSET, offset.u.low_part); ++ REG_WRITE(DMCUB_REGION4_OFFSET_HIGH, offset.u.high_part); ++ REG_SET_2(DMCUB_REGION4_TOP_ADDRESS, 0, DMCUB_REGION4_TOP_ADDRESS, ++ cw4->region.top - cw4->region.base - 1, DMCUB_REGION4_ENABLE, ++ 1); ++ ++ dmub_dcn21_translate_addr(&cw5->offset, fb_base, fb_offset, &offset); ++ ++ REG_WRITE(DMCUB_REGION3_CW5_OFFSET, offset.u.low_part); ++ REG_WRITE(DMCUB_REGION3_CW5_OFFSET_HIGH, offset.u.high_part); ++ REG_WRITE(DMCUB_REGION3_CW5_BASE_ADDRESS, cw5->region.base); ++ REG_SET_2(DMCUB_REGION3_CW5_TOP_ADDRESS, 0, ++ DMCUB_REGION3_CW5_TOP_ADDRESS, cw5->region.top, ++ DMCUB_REGION3_CW5_ENABLE, 1); ++} ++ ++bool dmub_dcn21_is_auto_load_done(struct dmub_srv *dmub) ++{ ++ return (REG_READ(DMCUB_SCRATCH0) == 3); ++} +diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn21.h b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn21.h +new file mode 100644 +index 000000000000..f57969d8d56f +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn21.h +@@ -0,0 +1,45 @@ ++/* ++ * Copyright 2019 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef _DMUB_DCN21_H_ ++#define _DMUB_DCN21_H_ ++ ++#include "dmub_dcn20.h" ++ ++/* Hardware functions. */ ++ ++void dmub_dcn21_backdoor_load(struct dmub_srv *dmub, ++ const struct dmub_window *cw0, ++ const struct dmub_window *cw1); ++ ++void dmub_dcn21_setup_windows(struct dmub_srv *dmub, ++ const struct dmub_window *cw2, ++ const struct dmub_window *cw3, ++ const struct dmub_window *cw4, ++ const struct dmub_window *cw5); ++ ++bool dmub_dcn21_is_auto_load_done(struct dmub_srv *dmub); ++ ++#endif /* _DMUB_DCN21_H_ */ +diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c +new file mode 100644 +index 000000000000..4094eca212f0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.c +@@ -0,0 +1,109 @@ ++/* ++ * Copyright 2019 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dmub_reg.h" ++#include "../inc/dmub_srv.h" ++ ++struct dmub_reg_value_masks { ++ uint32_t value; ++ uint32_t mask; ++}; ++ ++static inline void ++set_reg_field_value_masks(struct dmub_reg_value_masks *field_value_mask, ++ uint32_t value, uint32_t mask, uint8_t shift) ++{ ++ field_value_mask->value = ++ (field_value_mask->value & ~mask) | (mask & (value << shift)); ++ field_value_mask->mask = field_value_mask->mask | mask; ++} ++ ++static void set_reg_field_values(struct dmub_reg_value_masks *field_value_mask, ++ uint32_t addr, int n, uint8_t shift1, ++ uint32_t mask1, uint32_t field_value1, ++ va_list ap) ++{ ++ uint32_t shift, mask, field_value; ++ int i = 1; ++ ++ /* gather all bits value/mask getting updated in this register */ ++ set_reg_field_value_masks(field_value_mask, field_value1, mask1, ++ shift1); ++ ++ while (i < n) { ++ shift = va_arg(ap, uint32_t); ++ mask = va_arg(ap, uint32_t); ++ field_value = va_arg(ap, uint32_t); ++ ++ set_reg_field_value_masks(field_value_mask, field_value, mask, ++ shift); ++ i++; ++ } ++} ++ ++static inline uint32_t get_reg_field_value_ex(uint32_t reg_value, uint32_t mask, ++ uint8_t shift) ++{ ++ return (mask & reg_value) >> shift; ++} ++ ++void dmub_reg_update(struct dmub_srv *srv, uint32_t addr, int n, uint8_t shift1, ++ uint32_t mask1, uint32_t field_value1, ...) ++{ ++ struct dmub_reg_value_masks field_value_mask = { 0 }; ++ uint32_t reg_val; ++ va_list ap; ++ ++ va_start(ap, field_value1); ++ set_reg_field_values(&field_value_mask, addr, n, shift1, mask1, ++ field_value1, ap); ++ va_end(ap); ++ ++ reg_val = srv->funcs.reg_read(srv->user_ctx, addr); ++ reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value; ++ srv->funcs.reg_write(srv->user_ctx, addr, reg_val); ++} ++ ++void dmub_reg_set(struct dmub_srv *srv, uint32_t addr, uint32_t reg_val, int n, ++ uint8_t shift1, uint32_t mask1, uint32_t field_value1, ...) ++{ ++ struct dmub_reg_value_masks field_value_mask = { 0 }; ++ va_list ap; ++ ++ va_start(ap, field_value1); ++ set_reg_field_values(&field_value_mask, addr, n, shift1, mask1, ++ field_value1, ap); ++ va_end(ap); ++ ++ reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value; ++ srv->funcs.reg_write(srv->user_ctx, addr, reg_val); ++} ++ ++void dmub_reg_get(struct dmub_srv *srv, uint32_t addr, uint8_t shift, ++ uint32_t mask, uint32_t *field_value) ++{ ++ uint32_t reg_val = srv->funcs.reg_read(srv->user_ctx, addr); ++ *field_value = get_reg_field_value_ex(reg_val, mask, shift); ++} +diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.h b/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.h +new file mode 100644 +index 000000000000..bac4ee8f745f +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_reg.h +@@ -0,0 +1,120 @@ ++/* ++ * Copyright 2019 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef _DMUB_REG_H_ ++#define _DMUB_REG_H_ ++ ++#include "../inc/dmub_types.h" ++ ++struct dmub_srv; ++ ++/* Register offset and field lookup. */ ++ ++#define BASE(seg) BASE_INNER(seg) ++ ++#define REG_OFFSET(base_index, addr) (BASE(base_index) + addr) ++ ++#define REG(reg_name) REG_OFFSET(mm ## reg_name ## _BASE_IDX, mm ## reg_name) ++ ++#define FD(reg_field) reg_field ## __SHIFT, reg_field ## _MASK ++ ++#define FN(reg_name, field) FD(reg_name##__##field) ++ ++/* Register reads and writes. */ ++ ++#define REG_READ(reg) ((CTX)->funcs.reg_read((CTX)->user_ctx, REG(reg))) ++ ++#define REG_WRITE(reg, val) \ ++ ((CTX)->funcs.reg_write((CTX)->user_ctx, REG(reg), (val))) ++ ++/* Register field setting. */ ++ ++#define REG_SET_N(reg_name, n, initial_val, ...) \ ++ dmub_reg_set(CTX, REG(reg_name), initial_val, n, __VA_ARGS__) ++ ++#define REG_SET(reg_name, initial_val, field, val) \ ++ REG_SET_N(reg_name, 1, initial_val, \ ++ FN(reg_name, field), val) ++ ++#define REG_SET_2(reg, init_value, f1, v1, f2, v2) \ ++ REG_SET_N(reg, 2, init_value, \ ++ FN(reg, f1), v1, \ ++ FN(reg, f2), v2) ++ ++#define REG_SET_3(reg, init_value, f1, v1, f2, v2, f3, v3) \ ++ REG_SET_N(reg, 3, init_value, \ ++ FN(reg, f1), v1, \ ++ FN(reg, f2), v2, \ ++ FN(reg, f3), v3) ++ ++#define REG_SET_4(reg, init_value, f1, v1, f2, v2, f3, v3, f4, v4) \ ++ REG_SET_N(reg, 4, init_value, \ ++ FN(reg, f1), v1, \ ++ FN(reg, f2), v2, \ ++ FN(reg, f3), v3, \ ++ FN(reg, f4), v4) ++ ++/* Register field updating. */ ++ ++#define REG_UPDATE_N(reg_name, n, ...)\ ++ dmub_reg_update(CTX, REG(reg_name), n, __VA_ARGS__) ++ ++#define REG_UPDATE(reg_name, field, val) \ ++ REG_UPDATE_N(reg_name, 1, \ ++ FN(reg_name, field), val) ++ ++#define REG_UPDATE_2(reg, f1, v1, f2, v2) \ ++ REG_UPDATE_N(reg, 2,\ ++ FN(reg, f1), v1,\ ++ FN(reg, f2), v2) ++ ++#define REG_UPDATE_3(reg, f1, v1, f2, v2, f3, v3) \ ++ REG_UPDATE_N(reg, 3, \ ++ FN(reg, f1), v1, \ ++ FN(reg, f2), v2, \ ++ FN(reg, f3), v3) ++ ++#define REG_UPDATE_4(reg, f1, v1, f2, v2, f3, v3, f4, v4) \ ++ REG_UPDATE_N(reg, 4, \ ++ FN(reg, f1), v1, \ ++ FN(reg, f2), v2, \ ++ FN(reg, f3), v3, \ ++ FN(reg, f4), v4) ++ ++/* Register field getting. */ ++ ++#define REG_GET(reg_name, field, val) \ ++ dmub_reg_get(CTX, REG(reg_name), FN(reg_name, field), val) ++ ++void dmub_reg_set(struct dmub_srv *srv, uint32_t addr, uint32_t reg_val, int n, ++ uint8_t shift1, uint32_t mask1, uint32_t field_value1, ...); ++ ++void dmub_reg_update(struct dmub_srv *srv, uint32_t addr, int n, uint8_t shift1, ++ uint32_t mask1, uint32_t field_value1, ...); ++ ++void dmub_reg_get(struct dmub_srv *srv, uint32_t addr, uint8_t shift, ++ uint32_t mask, uint32_t *field_value); ++ ++#endif /* _DMUB_REG_H_ */ +diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c +new file mode 100644 +index 000000000000..229eab7277d1 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c +@@ -0,0 +1,415 @@ ++/* ++ * 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 "../inc/dmub_srv.h" ++#include "dmub_dcn20.h" ++#include "dmub_dcn21.h" ++/* ++ * Note: the DMUB service is standalone. No additional headers should be ++ * added below or above this line unless they reside within the DMUB ++ * folder. ++ */ ++ ++/* Alignment for framebuffer memory. */ ++#define DMUB_FB_ALIGNMENT (1024 * 1024) ++ ++/* Stack size. */ ++#define DMUB_STACK_SIZE (128 * 1024) ++ ++/* Context size. */ ++#define DMUB_CONTEXT_SIZE (512 * 1024) ++ ++/* Mailbox size */ ++#define DMUB_MAILBOX_SIZE (DMUB_RB_SIZE) ++ ++/* Tracebuffer size */ ++#define DMUB_TRACEBUFF_SIZE (1024) //1kB buffer ++ ++/* Number of windows in use. */ ++#define DMUB_NUM_WINDOWS (DMUB_WINDOW_5_TRACEBUFF + 1) ++/* Base addresses. */ ++ ++#define DMUB_CW0_BASE (0x60000000) ++#define DMUB_CW1_BASE (0x61000000) ++#define DMUB_CW5_BASE (0x65000000) ++ ++static inline uint32_t dmub_align(uint32_t val, uint32_t factor) ++{ ++ return (val + factor - 1) / factor * factor; ++} ++ ++static bool dmub_srv_hw_setup(struct dmub_srv *dmub, enum dmub_asic asic) ++{ ++ struct dmub_srv_hw_funcs *funcs = &dmub->hw_funcs; ++ ++ switch (asic) { ++ case DMUB_ASIC_DCN20: ++ case DMUB_ASIC_DCN21: ++ funcs->reset = dmub_dcn20_reset; ++ funcs->reset_release = dmub_dcn20_reset_release; ++ funcs->backdoor_load = dmub_dcn20_backdoor_load; ++ funcs->setup_windows = dmub_dcn20_setup_windows; ++ funcs->setup_mailbox = dmub_dcn20_setup_mailbox; ++ funcs->get_inbox1_rptr = dmub_dcn20_get_inbox1_rptr; ++ funcs->set_inbox1_wptr = dmub_dcn20_set_inbox1_wptr; ++ funcs->is_supported = dmub_dcn20_is_supported; ++ funcs->is_phy_init = dmub_dcn20_is_phy_init; ++ ++ if (asic == DMUB_ASIC_DCN21) { ++ funcs->backdoor_load = dmub_dcn21_backdoor_load; ++ funcs->setup_windows = dmub_dcn21_setup_windows; ++ funcs->is_auto_load_done = dmub_dcn21_is_auto_load_done; ++ } ++ break; ++ ++ default: ++ return false; ++ } ++ ++ return true; ++} ++ ++enum dmub_status dmub_srv_create(struct dmub_srv *dmub, ++ const struct dmub_srv_create_params *params) ++{ ++ enum dmub_status status = DMUB_STATUS_OK; ++ ++ dmub_memset(dmub, 0, sizeof(*dmub)); ++ ++ dmub->funcs = params->funcs; ++ dmub->user_ctx = params->user_ctx; ++ dmub->asic = params->asic; ++ dmub->is_virtual = params->is_virtual; ++ ++ /* Setup asic dependent hardware funcs. */ ++ if (!dmub_srv_hw_setup(dmub, params->asic)) { ++ status = DMUB_STATUS_INVALID; ++ goto cleanup; ++ } ++ ++ /* Override (some) hardware funcs based on user params. */ ++ if (params->hw_funcs) { ++ if (params->hw_funcs->get_inbox1_rptr) ++ dmub->hw_funcs.get_inbox1_rptr = ++ params->hw_funcs->get_inbox1_rptr; ++ ++ if (params->hw_funcs->set_inbox1_wptr) ++ dmub->hw_funcs.set_inbox1_wptr = ++ params->hw_funcs->set_inbox1_wptr; ++ ++ if (params->hw_funcs->is_supported) ++ dmub->hw_funcs.is_supported = ++ params->hw_funcs->is_supported; ++ } ++ ++ /* Sanity checks for required hw func pointers. */ ++ if (!dmub->hw_funcs.get_inbox1_rptr || ++ !dmub->hw_funcs.set_inbox1_wptr) { ++ status = DMUB_STATUS_INVALID; ++ goto cleanup; ++ } ++ ++cleanup: ++ if (status == DMUB_STATUS_OK) ++ dmub->sw_init = true; ++ else ++ dmub_srv_destroy(dmub); ++ ++ return status; ++} ++ ++void dmub_srv_destroy(struct dmub_srv *dmub) ++{ ++ dmub_memset(dmub, 0, sizeof(*dmub)); ++} ++ ++enum dmub_status ++dmub_srv_calc_region_info(struct dmub_srv *dmub, ++ const struct dmub_srv_region_params *params, ++ struct dmub_srv_region_info *out) ++{ ++ struct dmub_region *inst = &out->regions[DMUB_WINDOW_0_INST_CONST]; ++ struct dmub_region *stack = &out->regions[DMUB_WINDOW_1_STACK]; ++ struct dmub_region *data = &out->regions[DMUB_WINDOW_2_BSS_DATA]; ++ struct dmub_region *bios = &out->regions[DMUB_WINDOW_3_VBIOS]; ++ struct dmub_region *mail = &out->regions[DMUB_WINDOW_4_MAILBOX]; ++ struct dmub_region *trace_buff = &out->regions[DMUB_WINDOW_5_TRACEBUFF]; ++ ++ if (!dmub->sw_init) ++ return DMUB_STATUS_INVALID; ++ ++ memset(out, 0, sizeof(*out)); ++ ++ out->num_regions = DMUB_NUM_WINDOWS; ++ ++ inst->base = 0x0; ++ inst->top = inst->base + params->inst_const_size; ++ ++ data->base = dmub_align(inst->top, 256); ++ data->top = data->base + params->bss_data_size; ++ ++ stack->base = dmub_align(data->top, 256); ++ stack->top = stack->base + DMUB_STACK_SIZE + DMUB_CONTEXT_SIZE; ++ ++ bios->base = dmub_align(stack->top, 256); ++ bios->top = bios->base + params->vbios_size; ++ ++ mail->base = dmub_align(bios->top, 256); ++ mail->top = mail->base + DMUB_MAILBOX_SIZE; ++ ++ trace_buff->base = dmub_align(mail->top, 256); ++ trace_buff->top = trace_buff->base + DMUB_TRACEBUFF_SIZE; ++ ++ out->fb_size = dmub_align(trace_buff->top, 4096); ++ ++ return DMUB_STATUS_OK; ++} ++ ++enum dmub_status dmub_srv_calc_fb_info(struct dmub_srv *dmub, ++ const struct dmub_srv_fb_params *params, ++ struct dmub_srv_fb_info *out) ++{ ++ uint8_t *cpu_base; ++ uint64_t gpu_base; ++ uint32_t i; ++ ++ if (!dmub->sw_init) ++ return DMUB_STATUS_INVALID; ++ ++ memset(out, 0, sizeof(*out)); ++ ++ if (params->region_info->num_regions != DMUB_NUM_WINDOWS) ++ return DMUB_STATUS_INVALID; ++ ++ cpu_base = (uint8_t *)params->cpu_addr; ++ gpu_base = params->gpu_addr; ++ ++ for (i = 0; i < DMUB_NUM_WINDOWS; ++i) { ++ const struct dmub_region *reg = ++ ¶ms->region_info->regions[i]; ++ ++ out->fb[i].cpu_addr = cpu_base + reg->base; ++ out->fb[i].gpu_addr = gpu_base + reg->base; ++ out->fb[i].size = reg->top - reg->base; ++ } ++ ++ out->num_fb = DMUB_NUM_WINDOWS; ++ ++ return DMUB_STATUS_OK; ++} ++ ++enum dmub_status dmub_srv_has_hw_support(struct dmub_srv *dmub, ++ bool *is_supported) ++{ ++ *is_supported = false; ++ ++ if (!dmub->sw_init) ++ return DMUB_STATUS_INVALID; ++ ++ if (dmub->hw_funcs.is_supported) ++ *is_supported = dmub->hw_funcs.is_supported(dmub); ++ ++ return DMUB_STATUS_OK; ++} ++ ++enum dmub_status dmub_srv_hw_init(struct dmub_srv *dmub, ++ const struct dmub_srv_hw_params *params) ++{ ++ struct dmub_fb *inst_fb = params->fb[DMUB_WINDOW_0_INST_CONST]; ++ struct dmub_fb *stack_fb = params->fb[DMUB_WINDOW_1_STACK]; ++ struct dmub_fb *data_fb = params->fb[DMUB_WINDOW_2_BSS_DATA]; ++ struct dmub_fb *bios_fb = params->fb[DMUB_WINDOW_3_VBIOS]; ++ struct dmub_fb *mail_fb = params->fb[DMUB_WINDOW_4_MAILBOX]; ++ struct dmub_fb *tracebuff_fb = params->fb[DMUB_WINDOW_5_TRACEBUFF]; ++ ++ struct dmub_rb_init_params rb_params; ++ struct dmub_window cw0, cw1, cw2, cw3, cw4, cw5; ++ struct dmub_region inbox1; ++ ++ if (!dmub->sw_init) ++ return DMUB_STATUS_INVALID; ++ ++ dmub->fb_base = params->fb_base; ++ dmub->fb_offset = params->fb_offset; ++ dmub->psp_version = params->psp_version; ++ ++ if (inst_fb && data_fb) { ++ cw0.offset.quad_part = inst_fb->gpu_addr; ++ cw0.region.base = DMUB_CW0_BASE; ++ cw0.region.top = cw0.region.base + inst_fb->size - 1; ++ ++ cw1.offset.quad_part = stack_fb->gpu_addr; ++ cw1.region.base = DMUB_CW1_BASE; ++ cw1.region.top = cw1.region.base + stack_fb->size - 1; ++ ++ if (dmub->hw_funcs.backdoor_load) ++ dmub->hw_funcs.backdoor_load(dmub, &cw0, &cw1); ++ } ++ ++ if (dmub->hw_funcs.reset) ++ dmub->hw_funcs.reset(dmub); ++ ++ if (inst_fb && data_fb && bios_fb && mail_fb) { ++ cw2.offset.quad_part = data_fb->gpu_addr; ++ cw2.region.base = DMUB_CW0_BASE + inst_fb->size; ++ cw2.region.top = cw2.region.base + data_fb->size; ++ ++ cw3.offset.quad_part = bios_fb->gpu_addr; ++ cw3.region.base = DMUB_CW1_BASE + stack_fb->size; ++ cw3.region.top = cw3.region.base + bios_fb->size; ++ ++ cw4.offset.quad_part = mail_fb->gpu_addr; ++ cw4.region.base = cw3.region.top + 1; ++ cw4.region.top = cw4.region.base + mail_fb->size; ++ ++ inbox1.base = cw4.region.base; ++ inbox1.top = cw4.region.top; ++ ++ cw5.offset.quad_part = tracebuff_fb->gpu_addr; ++ cw5.region.base = DMUB_CW5_BASE; ++ cw5.region.top = cw5.region.base + tracebuff_fb->size; ++ ++ if (dmub->hw_funcs.setup_windows) ++ dmub->hw_funcs.setup_windows(dmub, &cw2, &cw3, &cw4, &cw5); ++ ++ if (dmub->hw_funcs.setup_mailbox) ++ dmub->hw_funcs.setup_mailbox(dmub, &inbox1); ++ } ++ ++ if (mail_fb) { ++ dmub_memset(&rb_params, 0, sizeof(rb_params)); ++ rb_params.ctx = dmub; ++ rb_params.base_address = mail_fb->cpu_addr; ++ rb_params.capacity = DMUB_RB_SIZE; ++ ++ dmub_rb_init(&dmub->inbox1_rb, &rb_params); ++ } ++ ++ if (dmub->hw_funcs.reset_release) ++ dmub->hw_funcs.reset_release(dmub); ++ ++ dmub->hw_init = true; ++ ++ return DMUB_STATUS_OK; ++} ++ ++enum dmub_status dmub_srv_cmd_queue(struct dmub_srv *dmub, ++ const struct dmub_cmd_header *cmd) ++{ ++ if (!dmub->hw_init) ++ return DMUB_STATUS_INVALID; ++ ++ if (dmub_rb_push_front(&dmub->inbox1_rb, cmd)) ++ return DMUB_STATUS_OK; ++ ++ return DMUB_STATUS_QUEUE_FULL; ++} ++ ++enum dmub_status dmub_srv_cmd_execute(struct dmub_srv *dmub) ++{ ++ if (!dmub->hw_init) ++ return DMUB_STATUS_INVALID; ++ ++ dmub->hw_funcs.set_inbox1_wptr(dmub, dmub->inbox1_rb.wrpt); ++ return DMUB_STATUS_OK; ++} ++ ++enum dmub_status dmub_srv_cmd_submit(struct dmub_srv *dmub, ++ const struct dmub_cmd_header *cmd, ++ uint32_t timeout_us) ++{ ++ uint32_t i = 0; ++ ++ if (!dmub->hw_init) ++ return DMUB_STATUS_INVALID; ++ ++ for (i = 0; i <= timeout_us; ++i) { ++ dmub->inbox1_rb.rptr = dmub->hw_funcs.get_inbox1_rptr(dmub); ++ if (dmub_rb_push_front(&dmub->inbox1_rb, cmd)) { ++ dmub->hw_funcs.set_inbox1_wptr(dmub, ++ dmub->inbox1_rb.wrpt); ++ return DMUB_STATUS_OK; ++ } ++ ++ udelay(1); ++ } ++ ++ return DMUB_STATUS_TIMEOUT; ++} ++ ++enum dmub_status dmub_srv_wait_for_auto_load(struct dmub_srv *dmub, ++ uint32_t timeout_us) ++{ ++ uint32_t i; ++ ++ if (!dmub->hw_init || !dmub->hw_funcs.is_auto_load_done) ++ return DMUB_STATUS_INVALID; ++ ++ for (i = 0; i <= timeout_us; i += 100) { ++ if (dmub->hw_funcs.is_auto_load_done(dmub)) ++ return DMUB_STATUS_OK; ++ ++ udelay(100); ++ } ++ ++ return DMUB_STATUS_TIMEOUT; ++} ++ ++enum dmub_status dmub_srv_wait_for_phy_init(struct dmub_srv *dmub, ++ uint32_t timeout_us) ++{ ++ uint32_t i; ++ ++ if (!dmub->hw_init || !dmub->hw_funcs.is_phy_init) ++ return DMUB_STATUS_INVALID; ++ ++ for (i = 0; i <= timeout_us; i += 10) { ++ if (dmub->hw_funcs.is_phy_init(dmub)) ++ return DMUB_STATUS_OK; ++ ++ udelay(10); ++ } ++ ++ return DMUB_STATUS_TIMEOUT; ++} ++ ++enum dmub_status dmub_srv_wait_for_idle(struct dmub_srv *dmub, ++ uint32_t timeout_us) ++{ ++ uint32_t i; ++ ++ if (!dmub->hw_init) ++ return DMUB_STATUS_INVALID; ++ ++ for (i = 0; i <= timeout_us; ++i) { ++ dmub->inbox1_rb.rptr = dmub->hw_funcs.get_inbox1_rptr(dmub); ++ if (dmub_rb_empty(&dmub->inbox1_rb)) ++ return DMUB_STATUS_OK; ++ ++ udelay(1); ++ } ++ ++ return DMUB_STATUS_TIMEOUT; ++} +-- +2.17.1 + |