diff options
Diffstat (limited to 'common/recipes-kernel/linux/linux-yocto-4.14.71/5001-drm-amd-display-Decouple-aux-from-i2c.patch')
-rw-r--r-- | common/recipes-kernel/linux/linux-yocto-4.14.71/5001-drm-amd-display-Decouple-aux-from-i2c.patch | 1949 |
1 files changed, 1949 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/linux-yocto-4.14.71/5001-drm-amd-display-Decouple-aux-from-i2c.patch b/common/recipes-kernel/linux/linux-yocto-4.14.71/5001-drm-amd-display-Decouple-aux-from-i2c.patch new file mode 100644 index 00000000..27ed2499 --- /dev/null +++ b/common/recipes-kernel/linux/linux-yocto-4.14.71/5001-drm-amd-display-Decouple-aux-from-i2c.patch @@ -0,0 +1,1949 @@ +From 2e972f9d807a5d585a1d05bea81e34cf21389ee4 Mon Sep 17 00:00:00 2001 +From: Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com> +Date: Tue, 10 Jul 2018 17:20:17 -0400 +Subject: [PATCH 5001/5725] drm/amd/display: Decouple aux from i2c + +[Why] +Aux engine is created from i2caux layer. We want to remove this layer +and use the engine directly. + +[How] +Decouple aux engine from i2caux. Move aux engine related code to dce folder and use +dc resource pool to manage the engine. And use the engine functions directly + +Signed-off-by: Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com> +Reviewed-by: Harry Wentland <Harry.Wentland@amd.com> +Acked-by: Leo Li <sunpeng.li@amd.com> +Signed-off-by: Alex Deucher <alexander.deucher@amd.com> +--- + drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c | 22 +- + drivers/gpu/drm/amd/display/dc/dce/Makefile | 2 +- + drivers/gpu/drm/amd/display/dc/dce/dce_aux.c | 942 +++++++++++++++++++++ + drivers/gpu/drm/amd/display/dc/dce/dce_aux.h | 111 +++ + .../drm/amd/display/dc/dce100/dce100_resource.c | 42 + + .../drm/amd/display/dc/dce110/dce110_resource.c | 45 + + .../drm/amd/display/dc/dce112/dce112_resource.c | 47 + + .../drm/amd/display/dc/dce120/dce120_resource.c | 42 + + .../gpu/drm/amd/display/dc/dce80/dce80_resource.c | 44 + + .../gpu/drm/amd/display/dc/dcn10/dcn10_resource.c | 44 + + drivers/gpu/drm/amd/display/dc/i2caux/engine.h | 1 + + drivers/gpu/drm/amd/display/dc/inc/core_types.h | 2 +- + drivers/gpu/drm/amd/display/dc/inc/hw/aux_engine.h | 113 +++ + drivers/gpu/drm/amd/display/dc/inc/hw/engine.h | 106 +++ + 14 files changed, 1549 insertions(+), 14 deletions(-) + create mode 100644 drivers/gpu/drm/amd/display/dc/dce/dce_aux.c + create mode 100644 drivers/gpu/drm/amd/display/dc/dce/dce_aux.h + create mode 100644 drivers/gpu/drm/amd/display/dc/inc/hw/aux_engine.h + create mode 100644 drivers/gpu/drm/amd/display/dc/inc/hw/engine.h + +diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c +index 08c9d73..4019fe07 100644 +--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c ++++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c +@@ -33,10 +33,8 @@ + #include "include/vector.h" + #include "core_types.h" + #include "dc_link_ddc.h" +-#include "i2caux/engine.h" +-#include "i2caux/i2c_engine.h" +-#include "i2caux/aux_engine.h" +-#include "i2caux/i2caux.h" ++#include "engine.h" ++#include "aux_engine.h" + + #define AUX_POWER_UP_WA_DELAY 500 + #define I2C_OVER_AUX_DEFER_WA_DELAY 70 +@@ -641,9 +639,9 @@ int dc_link_aux_transfer(struct ddc_service *ddc, + enum aux_transaction_type type, + enum i2caux_transaction_action action) + { +- struct i2caux *i2caux = ddc->ctx->i2caux; + struct ddc *ddc_pin = ddc->ddc_pin; +- struct aux_engine *engine; ++ struct engine *engine; ++ struct aux_engine *aux_engine; + enum aux_channel_operation_result operation_result; + struct aux_request_transaction_data aux_req; + struct aux_reply_transaction_data aux_rep; +@@ -654,7 +652,8 @@ int dc_link_aux_transfer(struct ddc_service *ddc, + memset(&aux_req, 0, sizeof(aux_req)); + memset(&aux_rep, 0, sizeof(aux_rep)); + +- engine = i2caux->funcs->acquire_aux_engine(i2caux, ddc_pin); ++ engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; ++ aux_engine = engine->funcs->acquire(engine, ddc_pin); + + aux_req.type = type; + aux_req.action = action; +@@ -664,15 +663,15 @@ int dc_link_aux_transfer(struct ddc_service *ddc, + aux_req.length = size; + aux_req.data = buffer; + +- engine->funcs->submit_channel_request(engine, &aux_req); +- operation_result = engine->funcs->get_channel_status(engine, &returned_bytes); ++ aux_engine->funcs->submit_channel_request(aux_engine, &aux_req); ++ operation_result = aux_engine->funcs->get_channel_status(aux_engine, &returned_bytes); + + switch (operation_result) { + case AUX_CHANNEL_OPERATION_SUCCEEDED: + res = returned_bytes; + + if (res <= size && res >= 0) +- res = engine->funcs->read_channel_reply(engine, size, ++ res = aux_engine->funcs->read_channel_reply(aux_engine, size, + buffer, reply, + &status); + +@@ -686,8 +685,7 @@ int dc_link_aux_transfer(struct ddc_service *ddc, + res = -1; + break; + } +- +- i2caux->funcs->release_engine(i2caux, &engine->base); ++ aux_engine->base.funcs->release_engine(&aux_engine->base); + return res; + } + +diff --git a/drivers/gpu/drm/amd/display/dc/dce/Makefile b/drivers/gpu/drm/amd/display/dc/dce/Makefile +index 8abec0b..67b0852 100644 +--- a/drivers/gpu/drm/amd/display/dc/dce/Makefile ++++ b/drivers/gpu/drm/amd/display/dc/dce/Makefile +@@ -7,7 +7,7 @@ + + DCE = dce_audio.o dce_stream_encoder.o dce_link_encoder.o dce_hwseq.o \ + dce_mem_input.o dce_clock_source.o dce_scl_filters.o dce_transform.o \ +-dce_clocks.o dce_opp.o dce_dmcu.o dce_abm.o dce_ipp.o ++dce_clocks.o dce_opp.o dce_dmcu.o dce_abm.o dce_ipp.o dce_aux.o + + + AMD_DAL_DCE = $(addprefix $(AMDDALPATH)/dc/dce/,$(DCE)) +diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c +new file mode 100644 +index 0000000..b28e212 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c +@@ -0,0 +1,942 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#include "dm_services.h" ++#include "dce_aux.h" ++#include "dce/dce_11_0_sh_mask.h" ++ ++#define CTX \ ++ aux110->base.base.ctx ++#define REG(reg_name)\ ++ (aux110->regs->reg_name) ++ ++#define DC_LOGGER \ ++ engine->base.ctx->logger ++ ++#include "reg_helper.h" ++ ++#define FROM_AUX_ENGINE(ptr) \ ++ container_of((ptr), struct aux_engine_dce110, base) ++ ++#define FROM_ENGINE(ptr) \ ++ FROM_AUX_ENGINE(container_of((ptr), struct aux_engine, base)) ++ ++#define FROM_AUX_ENGINE_ENGINE(ptr) \ ++ container_of((ptr), struct aux_engine, base) ++enum { ++ AUX_INVALID_REPLY_RETRY_COUNTER = 1, ++ AUX_TIMED_OUT_RETRY_COUNTER = 2, ++ AUX_DEFER_RETRY_COUNTER = 6 ++}; ++static void release_engine( ++ struct engine *engine) ++{ ++ struct aux_engine_dce110 *aux110 = FROM_ENGINE(engine); ++ ++ dal_ddc_close(engine->ddc); ++ ++ engine->ddc = NULL; ++ ++ REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, 1); ++} ++ ++#define SW_CAN_ACCESS_AUX 1 ++#define DMCU_CAN_ACCESS_AUX 2 ++ ++static bool is_engine_available( ++ struct aux_engine *engine) ++{ ++ struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); ++ ++ uint32_t value = REG_READ(AUX_ARB_CONTROL); ++ uint32_t field = get_reg_field_value( ++ value, ++ AUX_ARB_CONTROL, ++ AUX_REG_RW_CNTL_STATUS); ++ ++ return (field != DMCU_CAN_ACCESS_AUX); ++} ++static bool acquire_engine( ++ struct aux_engine *engine) ++{ ++ struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); ++ ++ uint32_t value = REG_READ(AUX_ARB_CONTROL); ++ uint32_t field = get_reg_field_value( ++ value, ++ AUX_ARB_CONTROL, ++ AUX_REG_RW_CNTL_STATUS); ++ if (field == DMCU_CAN_ACCESS_AUX) ++ return false; ++ /* enable AUX before request SW to access AUX */ ++ value = REG_READ(AUX_CONTROL); ++ field = get_reg_field_value(value, ++ AUX_CONTROL, ++ AUX_EN); ++ ++ if (field == 0) { ++ set_reg_field_value( ++ value, ++ 1, ++ AUX_CONTROL, ++ AUX_EN); ++ ++ if (REG(AUX_RESET_MASK)) { ++ /*DP_AUX block as part of the enable sequence*/ ++ set_reg_field_value( ++ value, ++ 1, ++ AUX_CONTROL, ++ AUX_RESET); ++ } ++ ++ REG_WRITE(AUX_CONTROL, value); ++ ++ if (REG(AUX_RESET_MASK)) { ++ /*poll HW to make sure reset it done*/ ++ ++ REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 1, ++ 1, 11); ++ ++ set_reg_field_value( ++ value, ++ 0, ++ AUX_CONTROL, ++ AUX_RESET); ++ ++ REG_WRITE(AUX_CONTROL, value); ++ ++ REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 0, ++ 1, 11); ++ } ++ } /*if (field)*/ ++ ++ /* request SW to access AUX */ ++ REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, 1); ++ ++ value = REG_READ(AUX_ARB_CONTROL); ++ field = get_reg_field_value( ++ value, ++ AUX_ARB_CONTROL, ++ AUX_REG_RW_CNTL_STATUS); ++ ++ return (field == SW_CAN_ACCESS_AUX); ++} ++ ++#define COMPOSE_AUX_SW_DATA_16_20(command, address) \ ++ ((command) | ((0xF0000 & (address)) >> 16)) ++ ++#define COMPOSE_AUX_SW_DATA_8_15(address) \ ++ ((0xFF00 & (address)) >> 8) ++ ++#define COMPOSE_AUX_SW_DATA_0_7(address) \ ++ (0xFF & (address)) ++ ++static void submit_channel_request( ++ struct aux_engine *engine, ++ struct aux_request_transaction_data *request) ++{ ++ struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); ++ uint32_t value; ++ uint32_t length; ++ ++ bool is_write = ++ ((request->type == AUX_TRANSACTION_TYPE_DP) && ++ (request->action == I2CAUX_TRANSACTION_ACTION_DP_WRITE)) || ++ ((request->type == AUX_TRANSACTION_TYPE_I2C) && ++ ((request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || ++ (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT))); ++ if (REG(AUXN_IMPCAL)) { ++ /* clear_aux_error */ ++ REG_UPDATE_SEQ(AUXN_IMPCAL, AUXN_CALOUT_ERROR_AK, ++ 1, ++ 0); ++ ++ REG_UPDATE_SEQ(AUXP_IMPCAL, AUXP_CALOUT_ERROR_AK, ++ 1, ++ 0); ++ ++ /* force_default_calibrate */ ++ REG_UPDATE_1BY1_2(AUXN_IMPCAL, ++ AUXN_IMPCAL_ENABLE, 1, ++ AUXN_IMPCAL_OVERRIDE_ENABLE, 0); ++ ++ /* bug? why AUXN update EN and OVERRIDE_EN 1 by 1 while AUX P toggles OVERRIDE? */ ++ ++ REG_UPDATE_SEQ(AUXP_IMPCAL, AUXP_IMPCAL_OVERRIDE_ENABLE, ++ 1, ++ 0); ++ } ++ /* set the delay and the number of bytes to write */ ++ ++ /* The length include ++ * the 4 bit header and the 20 bit address ++ * (that is 3 byte). ++ * If the requested length is non zero this means ++ * an addition byte specifying the length is required. ++ */ ++ ++ length = request->length ? 4 : 3; ++ if (is_write) ++ length += request->length; ++ ++ REG_UPDATE_2(AUX_SW_CONTROL, ++ AUX_SW_START_DELAY, request->delay, ++ AUX_SW_WR_BYTES, length); ++ ++ /* program action and address and payload data (if 'is_write') */ ++ value = REG_UPDATE_4(AUX_SW_DATA, ++ AUX_SW_INDEX, 0, ++ AUX_SW_DATA_RW, 0, ++ AUX_SW_AUTOINCREMENT_DISABLE, 1, ++ AUX_SW_DATA, COMPOSE_AUX_SW_DATA_16_20(request->action, request->address)); ++ ++ value = REG_SET_2(AUX_SW_DATA, value, ++ AUX_SW_AUTOINCREMENT_DISABLE, 0, ++ AUX_SW_DATA, COMPOSE_AUX_SW_DATA_8_15(request->address)); ++ ++ value = REG_SET(AUX_SW_DATA, value, ++ AUX_SW_DATA, COMPOSE_AUX_SW_DATA_0_7(request->address)); ++ ++ if (request->length) { ++ value = REG_SET(AUX_SW_DATA, value, ++ AUX_SW_DATA, request->length - 1); ++ } ++ ++ if (is_write) { ++ /* Load the HW buffer with the Data to be sent. ++ * This is relevant for write operation. ++ * For read, the data recived data will be ++ * processed in process_channel_reply(). ++ */ ++ uint32_t i = 0; ++ ++ while (i < request->length) { ++ value = REG_SET(AUX_SW_DATA, value, ++ AUX_SW_DATA, request->data[i]); ++ ++ ++i; ++ } ++ } ++ ++ REG_UPDATE(AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, 1); ++ REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 0, ++ 10, aux110->timeout_period/10); ++ REG_UPDATE(AUX_SW_CONTROL, AUX_SW_GO, 1); ++} ++ ++static int read_channel_reply(struct aux_engine *engine, uint32_t size, ++ uint8_t *buffer, uint8_t *reply_result, ++ uint32_t *sw_status) ++{ ++ struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); ++ uint32_t bytes_replied; ++ uint32_t reply_result_32; ++ ++ *sw_status = REG_GET(AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT, ++ &bytes_replied); ++ ++ /* In case HPD is LOW, exit AUX transaction */ ++ if ((*sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) ++ return -1; ++ ++ /* Need at least the status byte */ ++ if (!bytes_replied) ++ return -1; ++ ++ REG_UPDATE_1BY1_3(AUX_SW_DATA, ++ AUX_SW_INDEX, 0, ++ AUX_SW_AUTOINCREMENT_DISABLE, 1, ++ AUX_SW_DATA_RW, 1); ++ ++ REG_GET(AUX_SW_DATA, AUX_SW_DATA, &reply_result_32); ++ reply_result_32 = reply_result_32 >> 4; ++ *reply_result = (uint8_t)reply_result_32; ++ ++ if (reply_result_32 == 0) { /* ACK */ ++ uint32_t i = 0; ++ ++ /* First byte was already used to get the command status */ ++ --bytes_replied; ++ ++ /* Do not overflow buffer */ ++ if (bytes_replied > size) ++ return -1; ++ ++ while (i < bytes_replied) { ++ uint32_t aux_sw_data_val; ++ ++ REG_GET(AUX_SW_DATA, AUX_SW_DATA, &aux_sw_data_val); ++ buffer[i] = aux_sw_data_val; ++ ++i; ++ } ++ ++ return i; ++ } ++ ++ return 0; ++} ++ ++static void process_channel_reply( ++ struct aux_engine *engine, ++ struct aux_reply_transaction_data *reply) ++{ ++ int bytes_replied; ++ uint8_t reply_result; ++ uint32_t sw_status; ++ ++ bytes_replied = read_channel_reply(engine, reply->length, reply->data, ++ &reply_result, &sw_status); ++ ++ /* in case HPD is LOW, exit AUX transaction */ ++ if ((sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) { ++ reply->status = AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON; ++ return; ++ } ++ ++ if (bytes_replied < 0) { ++ /* Need to handle an error case... ++ * Hopefully, upper layer function won't call this function if ++ * the number of bytes in the reply was 0, because there was ++ * surely an error that was asserted that should have been ++ * handled for hot plug case, this could happens ++ */ ++ if (!(sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) { ++ reply->status = AUX_TRANSACTION_REPLY_INVALID; ++ ASSERT_CRITICAL(false); ++ return; ++ } ++ } else { ++ ++ switch (reply_result) { ++ case 0: /* ACK */ ++ reply->status = AUX_TRANSACTION_REPLY_AUX_ACK; ++ break; ++ case 1: /* NACK */ ++ reply->status = AUX_TRANSACTION_REPLY_AUX_NACK; ++ break; ++ case 2: /* DEFER */ ++ reply->status = AUX_TRANSACTION_REPLY_AUX_DEFER; ++ break; ++ case 4: /* AUX ACK / I2C NACK */ ++ reply->status = AUX_TRANSACTION_REPLY_I2C_NACK; ++ break; ++ case 8: /* AUX ACK / I2C DEFER */ ++ reply->status = AUX_TRANSACTION_REPLY_I2C_DEFER; ++ break; ++ default: ++ reply->status = AUX_TRANSACTION_REPLY_INVALID; ++ } ++ } ++} ++ ++static enum aux_channel_operation_result get_channel_status( ++ struct aux_engine *engine, ++ uint8_t *returned_bytes) ++{ ++ struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); ++ ++ uint32_t value; ++ ++ if (returned_bytes == NULL) { ++ /*caller pass NULL pointer*/ ++ ASSERT_CRITICAL(false); ++ return AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN; ++ } ++ *returned_bytes = 0; ++ ++ /* poll to make sure that SW_DONE is asserted */ ++ value = REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 1, ++ 10, aux110->timeout_period/10); ++ ++ /* in case HPD is LOW, exit AUX transaction */ ++ if ((value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) ++ return AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON; ++ ++ /* Note that the following bits are set in 'status.bits' ++ * during CTS 4.2.1.2 (FW 3.3.1): ++ * AUX_SW_RX_MIN_COUNT_VIOL, AUX_SW_RX_INVALID_STOP, ++ * AUX_SW_RX_RECV_NO_DET, AUX_SW_RX_RECV_INVALID_H. ++ * ++ * AUX_SW_RX_MIN_COUNT_VIOL is an internal, ++ * HW debugging bit and should be ignored. ++ */ ++ if (value & AUX_SW_STATUS__AUX_SW_DONE_MASK) { ++ if ((value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_STATE_MASK) || ++ (value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_MASK)) ++ return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; ++ ++ else if ((value & AUX_SW_STATUS__AUX_SW_RX_INVALID_STOP_MASK) || ++ (value & AUX_SW_STATUS__AUX_SW_RX_RECV_NO_DET_MASK) || ++ (value & ++ AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_H_MASK) || ++ (value & AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_L_MASK)) ++ return AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; ++ ++ *returned_bytes = get_reg_field_value(value, ++ AUX_SW_STATUS, ++ AUX_SW_REPLY_BYTE_COUNT); ++ ++ if (*returned_bytes == 0) ++ return ++ AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; ++ else { ++ *returned_bytes -= 1; ++ return AUX_CHANNEL_OPERATION_SUCCEEDED; ++ } ++ } else { ++ /*time_elapsed >= aux_engine->timeout_period ++ * AUX_SW_STATUS__AUX_SW_HPD_DISCON = at this point ++ */ ++ ASSERT_CRITICAL(false); ++ return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; ++ } ++} ++static void process_read_reply( ++ struct aux_engine *engine, ++ struct read_command_context *ctx) ++{ ++ engine->funcs->process_channel_reply(engine, &ctx->reply); ++ ++ switch (ctx->reply.status) { ++ case AUX_TRANSACTION_REPLY_AUX_ACK: ++ ctx->defer_retry_aux = 0; ++ if (ctx->returned_byte > ctx->current_read_length) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; ++ ctx->operation_succeeded = false; ++ } else if (ctx->returned_byte < ctx->current_read_length) { ++ ctx->current_read_length -= ctx->returned_byte; ++ ++ ctx->offset += ctx->returned_byte; ++ ++ ++ctx->invalid_reply_retry_aux_on_ack; ++ ++ if (ctx->invalid_reply_retry_aux_on_ack > ++ AUX_INVALID_REPLY_RETRY_COUNTER) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; ++ ctx->operation_succeeded = false; ++ } ++ } else { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED; ++ ctx->transaction_complete = true; ++ ctx->operation_succeeded = true; ++ } ++ break; ++ case AUX_TRANSACTION_REPLY_AUX_NACK: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK; ++ ctx->operation_succeeded = false; ++ break; ++ case AUX_TRANSACTION_REPLY_AUX_DEFER: ++ ++ctx->defer_retry_aux; ++ ++ if (ctx->defer_retry_aux > AUX_DEFER_RETRY_COUNTER) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } ++ break; ++ case AUX_TRANSACTION_REPLY_I2C_DEFER: ++ ctx->defer_retry_aux = 0; ++ ++ ++ctx->defer_retry_i2c; ++ ++ if (ctx->defer_retry_i2c > AUX_DEFER_RETRY_COUNTER) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } ++ break; ++ case AUX_TRANSACTION_REPLY_HPD_DISCON: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON; ++ ctx->operation_succeeded = false; ++ break; ++ default: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; ++ ctx->operation_succeeded = false; ++ } ++} ++static void process_read_request( ++ struct aux_engine *engine, ++ struct read_command_context *ctx) ++{ ++ enum aux_channel_operation_result operation_result; ++ ++ engine->funcs->submit_channel_request(engine, &ctx->request); ++ ++ operation_result = engine->funcs->get_channel_status( ++ engine, &ctx->returned_byte); ++ ++ switch (operation_result) { ++ case AUX_CHANNEL_OPERATION_SUCCEEDED: ++ if (ctx->returned_byte > ctx->current_read_length) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; ++ ctx->operation_succeeded = false; ++ } else { ++ ctx->timed_out_retry_aux = 0; ++ ctx->invalid_reply_retry_aux = 0; ++ ++ ctx->reply.length = ctx->returned_byte; ++ ctx->reply.data = ctx->buffer; ++ ++ process_read_reply(engine, ctx); ++ } ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: ++ ++ctx->invalid_reply_retry_aux; ++ ++ if (ctx->invalid_reply_retry_aux > ++ AUX_INVALID_REPLY_RETRY_COUNTER) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; ++ ctx->operation_succeeded = false; ++ } else ++ udelay(400); ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: ++ ++ctx->timed_out_retry_aux; ++ ++ if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } else { ++ /* DP 1.2a, table 2-58: ++ * "S3: AUX Request CMD PENDING: ++ * retry 3 times, with 400usec wait on each" ++ * The HW timeout is set to 550usec, ++ * so we should not wait here ++ */ ++ } ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON; ++ ctx->operation_succeeded = false; ++ break; ++ default: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; ++ ctx->operation_succeeded = false; ++ } ++} ++static bool read_command( ++ struct aux_engine *engine, ++ struct i2caux_transaction_request *request, ++ bool middle_of_transaction) ++{ ++ struct read_command_context ctx; ++ ++ ctx.buffer = request->payload.data; ++ ctx.current_read_length = request->payload.length; ++ ctx.offset = 0; ++ ctx.timed_out_retry_aux = 0; ++ ctx.invalid_reply_retry_aux = 0; ++ ctx.defer_retry_aux = 0; ++ ctx.defer_retry_i2c = 0; ++ ctx.invalid_reply_retry_aux_on_ack = 0; ++ ctx.transaction_complete = false; ++ ctx.operation_succeeded = true; ++ ++ if (request->payload.address_space == ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { ++ ctx.request.type = AUX_TRANSACTION_TYPE_DP; ++ ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_READ; ++ ctx.request.address = request->payload.address; ++ } else if (request->payload.address_space == ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) { ++ ctx.request.type = AUX_TRANSACTION_TYPE_I2C; ++ ctx.request.action = middle_of_transaction ? ++ I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_READ; ++ ctx.request.address = request->payload.address >> 1; ++ } else { ++ /* in DAL2, there was no return in such case */ ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ ctx.request.delay = 0; ++ ++ do { ++ memset(ctx.buffer + ctx.offset, 0, ctx.current_read_length); ++ ++ ctx.request.data = ctx.buffer + ctx.offset; ++ ctx.request.length = ctx.current_read_length; ++ ++ process_read_request(engine, &ctx); ++ ++ request->status = ctx.status; ++ ++ if (ctx.operation_succeeded && !ctx.transaction_complete) ++ if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C) ++ msleep(engine->delay); ++ } while (ctx.operation_succeeded && !ctx.transaction_complete); ++ ++ if (request->payload.address_space == ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { ++ DC_LOG_I2C_AUX("READ: addr:0x%x value:0x%x Result:%d", ++ request->payload.address, ++ request->payload.data[0], ++ ctx.operation_succeeded); ++ } ++ ++ return ctx.operation_succeeded; ++} ++ ++static void process_write_reply( ++ struct aux_engine *engine, ++ struct write_command_context *ctx) ++{ ++ engine->funcs->process_channel_reply(engine, &ctx->reply); ++ ++ switch (ctx->reply.status) { ++ case AUX_TRANSACTION_REPLY_AUX_ACK: ++ ctx->operation_succeeded = true; ++ ++ if (ctx->returned_byte) { ++ ctx->request.action = ctx->mot ? ++ I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST; ++ ++ ctx->current_write_length = 0; ++ ++ ++ctx->ack_m_retry; ++ ++ if (ctx->ack_m_retry > AUX_DEFER_RETRY_COUNTER) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } else ++ udelay(300); ++ } else { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED; ++ ctx->defer_retry_aux = 0; ++ ctx->ack_m_retry = 0; ++ ctx->transaction_complete = true; ++ } ++ break; ++ case AUX_TRANSACTION_REPLY_AUX_NACK: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK; ++ ctx->operation_succeeded = false; ++ break; ++ case AUX_TRANSACTION_REPLY_AUX_DEFER: ++ ++ctx->defer_retry_aux; ++ ++ if (ctx->defer_retry_aux > ctx->max_defer_retry) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } ++ break; ++ case AUX_TRANSACTION_REPLY_I2C_DEFER: ++ ctx->defer_retry_aux = 0; ++ ctx->current_write_length = 0; ++ ++ ctx->request.action = ctx->mot ? ++ I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST; ++ ++ ++ctx->defer_retry_i2c; ++ ++ if (ctx->defer_retry_i2c > ctx->max_defer_retry) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } ++ break; ++ case AUX_TRANSACTION_REPLY_HPD_DISCON: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON; ++ ctx->operation_succeeded = false; ++ break; ++ default: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; ++ ctx->operation_succeeded = false; ++ } ++} ++static void process_write_request( ++ struct aux_engine *engine, ++ struct write_command_context *ctx) ++{ ++ enum aux_channel_operation_result operation_result; ++ ++ engine->funcs->submit_channel_request(engine, &ctx->request); ++ ++ operation_result = engine->funcs->get_channel_status( ++ engine, &ctx->returned_byte); ++ ++ switch (operation_result) { ++ case AUX_CHANNEL_OPERATION_SUCCEEDED: ++ ctx->timed_out_retry_aux = 0; ++ ctx->invalid_reply_retry_aux = 0; ++ ++ ctx->reply.length = ctx->returned_byte; ++ ctx->reply.data = ctx->reply_data; ++ ++ process_write_reply(engine, ctx); ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: ++ ++ctx->invalid_reply_retry_aux; ++ ++ if (ctx->invalid_reply_retry_aux > ++ AUX_INVALID_REPLY_RETRY_COUNTER) { ++ ctx->status = ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; ++ ctx->operation_succeeded = false; ++ } else ++ udelay(400); ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: ++ ++ctx->timed_out_retry_aux; ++ ++ if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) { ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; ++ ctx->operation_succeeded = false; ++ } else { ++ /* DP 1.2a, table 2-58: ++ * "S3: AUX Request CMD PENDING: ++ * retry 3 times, with 400usec wait on each" ++ * The HW timeout is set to 550usec, ++ * so we should not wait here ++ */ ++ } ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON; ++ ctx->operation_succeeded = false; ++ break; ++ default: ++ ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; ++ ctx->operation_succeeded = false; ++ } ++} ++static bool write_command( ++ struct aux_engine *engine, ++ struct i2caux_transaction_request *request, ++ bool middle_of_transaction) ++{ ++ struct write_command_context ctx; ++ ++ ctx.mot = middle_of_transaction; ++ ctx.buffer = request->payload.data; ++ ctx.current_write_length = request->payload.length; ++ ctx.timed_out_retry_aux = 0; ++ ctx.invalid_reply_retry_aux = 0; ++ ctx.defer_retry_aux = 0; ++ ctx.defer_retry_i2c = 0; ++ ctx.ack_m_retry = 0; ++ ctx.transaction_complete = false; ++ ctx.operation_succeeded = true; ++ ++ if (request->payload.address_space == ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { ++ ctx.request.type = AUX_TRANSACTION_TYPE_DP; ++ ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_WRITE; ++ ctx.request.address = request->payload.address; ++ } else if (request->payload.address_space == ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) { ++ ctx.request.type = AUX_TRANSACTION_TYPE_I2C; ++ ctx.request.action = middle_of_transaction ? ++ I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT : ++ I2CAUX_TRANSACTION_ACTION_I2C_WRITE; ++ ctx.request.address = request->payload.address >> 1; ++ } else { ++ /* in DAL2, there was no return in such case */ ++ BREAK_TO_DEBUGGER(); ++ return false; ++ } ++ ++ ctx.request.delay = 0; ++ ++ ctx.max_defer_retry = ++ (engine->max_defer_write_retry > AUX_DEFER_RETRY_COUNTER) ? ++ engine->max_defer_write_retry : AUX_DEFER_RETRY_COUNTER; ++ ++ do { ++ ctx.request.data = ctx.buffer; ++ ctx.request.length = ctx.current_write_length; ++ ++ process_write_request(engine, &ctx); ++ ++ request->status = ctx.status; ++ ++ if (ctx.operation_succeeded && !ctx.transaction_complete) ++ if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C) ++ msleep(engine->delay); ++ } while (ctx.operation_succeeded && !ctx.transaction_complete); ++ ++ if (request->payload.address_space == ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { ++ DC_LOG_I2C_AUX("WRITE: addr:0x%x value:0x%x Result:%d", ++ request->payload.address, ++ request->payload.data[0], ++ ctx.operation_succeeded); ++ } ++ ++ return ctx.operation_succeeded; ++} ++static bool end_of_transaction_command( ++ struct aux_engine *engine, ++ struct i2caux_transaction_request *request) ++{ ++ struct i2caux_transaction_request dummy_request; ++ uint8_t dummy_data; ++ ++ /* [tcheng] We only need to send the stop (read with MOT = 0) ++ * for I2C-over-Aux, not native AUX ++ */ ++ ++ if (request->payload.address_space != ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) ++ return false; ++ ++ dummy_request.operation = request->operation; ++ dummy_request.payload.address_space = request->payload.address_space; ++ dummy_request.payload.address = request->payload.address; ++ ++ /* ++ * Add a dummy byte due to some receiver quirk ++ * where one byte is sent along with MOT = 0. ++ * Ideally this should be 0. ++ */ ++ ++ dummy_request.payload.length = 0; ++ dummy_request.payload.data = &dummy_data; ++ ++ if (request->operation == I2CAUX_TRANSACTION_READ) ++ return read_command(engine, &dummy_request, false); ++ else ++ return write_command(engine, &dummy_request, false); ++ ++ /* according Syed, it does not need now DoDummyMOT */ ++} ++bool submit_request( ++ struct engine *engine, ++ struct i2caux_transaction_request *request, ++ bool middle_of_transaction) ++{ ++ struct aux_engine *aux_engine = FROM_AUX_ENGINE_ENGINE(engine); ++ ++ bool result; ++ bool mot_used = true; ++ ++ switch (request->operation) { ++ case I2CAUX_TRANSACTION_READ: ++ result = read_command(aux_engine, request, mot_used); ++ break; ++ case I2CAUX_TRANSACTION_WRITE: ++ result = write_command(aux_engine, request, mot_used); ++ break; ++ default: ++ result = false; ++ } ++ ++ /* [tcheng] ++ * need to send stop for the last transaction to free up the AUX ++ * if the above command fails, this would be the last transaction ++ */ ++ ++ if (!middle_of_transaction || !result) ++ end_of_transaction_command(aux_engine, request); ++ ++ /* mask AUX interrupt */ ++ ++ return result; ++} ++enum i2caux_engine_type get_engine_type( ++ const struct engine *engine) ++{ ++ return I2CAUX_ENGINE_TYPE_AUX; ++} ++ ++static struct aux_engine *acquire( ++ struct engine *engine, ++ struct ddc *ddc) ++{ ++ struct aux_engine *aux_engine = FROM_AUX_ENGINE_ENGINE(engine); ++ enum gpio_result result; ++ ++ if (aux_engine->funcs->is_engine_available) { ++ /*check whether SW could use the engine*/ ++ if (!aux_engine->funcs->is_engine_available(aux_engine)) ++ return NULL; ++ } ++ ++ result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, ++ GPIO_DDC_CONFIG_TYPE_MODE_AUX); ++ ++ if (result != GPIO_RESULT_OK) ++ return NULL; ++ ++ if (!aux_engine->funcs->acquire_engine(aux_engine)) { ++ dal_ddc_close(ddc); ++ return NULL; ++ } ++ ++ engine->ddc = ddc; ++ ++ return aux_engine; ++} ++ ++static const struct aux_engine_funcs aux_engine_funcs = { ++ .acquire_engine = acquire_engine, ++ .submit_channel_request = submit_channel_request, ++ .process_channel_reply = process_channel_reply, ++ .read_channel_reply = read_channel_reply, ++ .get_channel_status = get_channel_status, ++ .is_engine_available = is_engine_available, ++}; ++ ++static const struct engine_funcs engine_funcs = { ++ .release_engine = release_engine, ++ .destroy_engine = dce110_engine_destroy, ++ .submit_request = submit_request, ++ .get_engine_type = get_engine_type, ++ .acquire = acquire, ++}; ++ ++void dce110_engine_destroy(struct engine **engine) ++{ ++ ++ struct aux_engine_dce110 *engine110 = FROM_ENGINE(*engine); ++ ++ kfree(engine110); ++ *engine = NULL; ++ ++} ++struct aux_engine *dce110_aux_engine_construct(struct aux_engine_dce110 *aux_engine110, ++ struct dc_context *ctx, ++ uint32_t inst, ++ uint32_t timeout_period, ++ const struct dce110_aux_registers *regs) ++{ ++ aux_engine110->base.base.ddc = NULL; ++ aux_engine110->base.base.ctx = ctx; ++ aux_engine110->base.delay = 0; ++ aux_engine110->base.max_defer_write_retry = 0; ++ aux_engine110->base.base.funcs = &engine_funcs; ++ aux_engine110->base.funcs = &aux_engine_funcs; ++ aux_engine110->base.base.inst = inst; ++ aux_engine110->timeout_period = timeout_period; ++ aux_engine110->regs = regs; ++ ++ return &aux_engine110->base; ++} ++ +diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.h b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.h +new file mode 100644 +index 0000000..c6b2aec +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.h +@@ -0,0 +1,111 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_AUX_ENGINE_DCE110_H__ ++#define __DAL_AUX_ENGINE_DCE110_H__ ++#include "aux_engine.h" ++ ++#define AUX_COMMON_REG_LIST(id)\ ++ SRI(AUX_CONTROL, DP_AUX, id), \ ++ SRI(AUX_ARB_CONTROL, DP_AUX, id), \ ++ SRI(AUX_SW_DATA, DP_AUX, id), \ ++ SRI(AUX_SW_CONTROL, DP_AUX, id), \ ++ SRI(AUX_INTERRUPT_CONTROL, DP_AUX, id), \ ++ SRI(AUX_SW_STATUS, DP_AUX, id), \ ++ SR(AUXN_IMPCAL), \ ++ SR(AUXP_IMPCAL) ++ ++struct dce110_aux_registers { ++ uint32_t AUX_CONTROL; ++ uint32_t AUX_ARB_CONTROL; ++ uint32_t AUX_SW_DATA; ++ uint32_t AUX_SW_CONTROL; ++ uint32_t AUX_INTERRUPT_CONTROL; ++ uint32_t AUX_SW_STATUS; ++ uint32_t AUXN_IMPCAL; ++ uint32_t AUXP_IMPCAL; ++ ++ uint32_t AUX_RESET_MASK; ++}; ++ ++enum { /* This is the timeout as defined in DP 1.2a, ++ * 2.3.4 "Detailed uPacket TX AUX CH State Description". ++ */ ++ AUX_TIMEOUT_PERIOD = 400, ++ ++ /* Ideally, the SW timeout should be just above 550usec ++ * which is programmed in HW. ++ * But the SW timeout of 600usec is not reliable, ++ * because on some systems, delay_in_microseconds() ++ * returns faster than it should. ++ * EPR #379763: by trial-and-error on different systems, ++ * 700usec is the minimum reliable SW timeout for polling ++ * the AUX_SW_STATUS.AUX_SW_DONE bit. ++ * This timeout expires *only* when there is ++ * AUX Error or AUX Timeout conditions - not during normal operation. ++ * During normal operation, AUX_SW_STATUS.AUX_SW_DONE bit is set ++ * at most within ~240usec. That means, ++ * increasing this timeout will not affect normal operation, ++ * and we'll timeout after ++ * SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD = 1600usec. ++ * This timeout is especially important for ++ * resume from S3 and CTS. ++ */ ++ SW_AUX_TIMEOUT_PERIOD_MULTIPLIER = 4 ++}; ++struct aux_engine_dce110 { ++ struct aux_engine base; ++ const struct dce110_aux_registers *regs; ++ struct { ++ uint32_t aux_control; ++ uint32_t aux_arb_control; ++ uint32_t aux_sw_data; ++ uint32_t aux_sw_control; ++ uint32_t aux_interrupt_control; ++ uint32_t aux_sw_status; ++ } addr; ++ uint32_t timeout_period; ++}; ++ ++struct aux_engine_dce110_init_data { ++ uint32_t engine_id; ++ uint32_t timeout_period; ++ struct dc_context *ctx; ++ const struct dce110_aux_registers *regs; ++}; ++ ++struct aux_engine *dce110_aux_engine_construct( ++ struct aux_engine_dce110 *aux_engine110, ++ struct dc_context *ctx, ++ uint32_t inst, ++ uint32_t timeout_period, ++ const struct dce110_aux_registers *regs); ++ ++void dce110_engine_destroy(struct engine **engine); ++ ++bool dce110_aux_engine_acquire( ++ struct engine *aux_engine, ++ struct ddc *ddc); ++#endif +diff --git a/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c +index ad8ad4e..c34c953 100644 +--- a/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c +@@ -52,6 +52,7 @@ + #include "dce/dce_10_0_sh_mask.h" + + #include "dce/dce_dmcu.h" ++#include "dce/dce_aux.h" + #include "dce/dce_abm.h" + + #ifndef mmMC_HUB_RDREQ_DMIF_LIMIT +@@ -279,7 +280,20 @@ static const struct dce_opp_shift opp_shift = { + static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_100(_MASK) + }; ++#define aux_engine_regs(id)\ ++[id] = {\ ++ AUX_COMMON_REG_LIST(id), \ ++ .AUX_RESET_MASK = 0 \ ++} + ++static const struct dce110_aux_registers aux_engine_regs[] = { ++ aux_engine_regs(0), ++ aux_engine_regs(1), ++ aux_engine_regs(2), ++ aux_engine_regs(3), ++ aux_engine_regs(4), ++ aux_engine_regs(5) ++}; + + #define audio_regs(id)\ + [id] = {\ +@@ -572,6 +586,23 @@ struct output_pixel_processor *dce100_opp_create( + return &opp->base; + } + ++struct engine *dce100_aux_engine_create( ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ struct aux_engine_dce110 *aux_engine = ++ kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); ++ ++ if (!aux_engine) ++ return NULL; ++ ++ dce110_aux_engine_construct(aux_engine, ctx, inst, ++ SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, ++ &aux_engine_regs[inst]); ++ ++ return &aux_engine->base.base; ++} ++ + struct clock_source *dce100_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, +@@ -624,6 +655,10 @@ static void destruct(struct dce110_resource_pool *pool) + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } ++ ++ if (pool->base.engines[i] != NULL) ++ dce110_engine_destroy(&pool->base.engines[i]); ++ + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { +@@ -928,6 +963,13 @@ static bool construct( + "DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } ++ pool->base.engines[i] = dce100_aux_engine_create(ctx, i); ++ if (pool->base.engines[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error( ++ "DC:failed to create aux engine!!\n"); ++ goto res_create_fail; ++ } + } + + dc->caps.max_planes = pool->base.pipe_count; +diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c +index 1c902e4..4a665a2 100644 +--- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c +@@ -49,6 +49,7 @@ + #include "dce/dce_clock_source.h" + #include "dce/dce_hwseq.h" + #include "dce110/dce110_hw_sequencer.h" ++#include "dce/dce_aux.h" + #include "dce/dce_abm.h" + #include "dce/dce_dmcu.h" + +@@ -306,6 +307,21 @@ static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_110(_MASK) + }; + ++#define aux_engine_regs(id)\ ++[id] = {\ ++ AUX_COMMON_REG_LIST(id), \ ++ .AUX_RESET_MASK = 0 \ ++} ++ ++static const struct dce110_aux_registers aux_engine_regs[] = { ++ aux_engine_regs(0), ++ aux_engine_regs(1), ++ aux_engine_regs(2), ++ aux_engine_regs(3), ++ aux_engine_regs(4), ++ aux_engine_regs(5) ++}; ++ + #define audio_regs(id)\ + [id] = {\ + AUD_COMMON_REG_LIST(id)\ +@@ -588,6 +604,23 @@ static struct output_pixel_processor *dce110_opp_create( + return &opp->base; + } + ++struct engine *dce110_aux_engine_create( ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ struct aux_engine_dce110 *aux_engine = ++ kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); ++ ++ if (!aux_engine) ++ return NULL; ++ ++ dce110_aux_engine_construct(aux_engine, ctx, inst, ++ SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, ++ &aux_engine_regs[inst]); ++ ++ return &aux_engine->base.base; ++} ++ + struct clock_source *dce110_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, +@@ -651,6 +684,10 @@ static void destruct(struct dce110_resource_pool *pool) + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } ++ ++ if (pool->base.engines[i] != NULL) ++ dce110_engine_destroy(&pool->base.engines[i]); ++ + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { +@@ -1258,6 +1295,14 @@ static bool construct( + "DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } ++ ++ pool->base.engines[i] = dce110_aux_engine_create(ctx, i); ++ if (pool->base.engines[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error( ++ "DC:failed to create aux engine!!\n"); ++ goto res_create_fail; ++ } + } + + dc->fbc_compressor = dce110_compressor_create(ctx); +diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c +index 30d5b32..caf90ae 100644 +--- a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c +@@ -49,6 +49,7 @@ + #include "dce112/dce112_hw_sequencer.h" + #include "dce/dce_abm.h" + #include "dce/dce_dmcu.h" ++#include "dce/dce_aux.h" + + #include "reg_helper.h" + +@@ -314,6 +315,21 @@ static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_112(_MASK) + }; + ++#define aux_engine_regs(id)\ ++[id] = {\ ++ AUX_COMMON_REG_LIST(id), \ ++ .AUX_RESET_MASK = 0 \ ++} ++ ++static const struct dce110_aux_registers aux_engine_regs[] = { ++ aux_engine_regs(0), ++ aux_engine_regs(1), ++ aux_engine_regs(2), ++ aux_engine_regs(3), ++ aux_engine_regs(4), ++ aux_engine_regs(5) ++}; ++ + #define audio_regs(id)\ + [id] = {\ + AUD_COMMON_REG_LIST(id)\ +@@ -588,6 +604,23 @@ struct output_pixel_processor *dce112_opp_create( + return &opp->base; + } + ++struct engine *dce112_aux_engine_create( ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ struct aux_engine_dce110 *aux_engine = ++ kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); ++ ++ if (!aux_engine) ++ return NULL; ++ ++ dce110_aux_engine_construct(aux_engine, ctx, inst, ++ SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, ++ &aux_engine_regs[inst]); ++ ++ return &aux_engine->base.base; ++} ++ + struct clock_source *dce112_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, +@@ -625,6 +658,9 @@ static void destruct(struct dce110_resource_pool *pool) + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + ++ if (pool->base.engines[i] != NULL) ++ dce110_engine_destroy(&pool->base.engines[i]); ++ + if (pool->base.transforms[i] != NULL) + dce112_transform_destroy(&pool->base.transforms[i]); + +@@ -640,6 +676,10 @@ static void destruct(struct dce110_resource_pool *pool) + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } ++ ++ if (pool->base.engines[i] != NULL) ++ dce110_engine_destroy(&pool->base.engines[i]); ++ + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { +@@ -1208,6 +1248,13 @@ static bool construct( + "DC:failed to create output pixel processor!\n"); + goto res_create_fail; + } ++ pool->base.engines[i] = dce112_aux_engine_create(ctx, i); ++ if (pool->base.engines[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error( ++ "DC:failed to create aux engine!!\n"); ++ goto res_create_fail; ++ } + } + + if (!resource_construct(num_virtual_links, dc, &pool->base, +diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c +index 8381f27..e389832 100644 +--- a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c +@@ -53,6 +53,7 @@ + #include "dce/dce_hwseq.h" + #include "dce/dce_abm.h" + #include "dce/dce_dmcu.h" ++#include "dce/dce_aux.h" + + #include "dce/dce_12_0_offset.h" + #include "dce/dce_12_0_sh_mask.h" +@@ -297,6 +298,20 @@ static const struct dce_opp_shift opp_shift = { + static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_120(_MASK) + }; ++ #define aux_engine_regs(id)\ ++[id] = {\ ++ AUX_COMMON_REG_LIST(id), \ ++ .AUX_RESET_MASK = 0 \ ++} ++ ++static const struct dce110_aux_registers aux_engine_regs[] = { ++ aux_engine_regs(0), ++ aux_engine_regs(1), ++ aux_engine_regs(2), ++ aux_engine_regs(3), ++ aux_engine_regs(4), ++ aux_engine_regs(5) ++}; + + #define audio_regs(id)\ + [id] = {\ +@@ -361,6 +376,22 @@ struct output_pixel_processor *dce120_opp_create( + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; + } ++struct engine *dce120_aux_engine_create( ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ struct aux_engine_dce110 *aux_engine = ++ kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); ++ ++ if (!aux_engine) ++ return NULL; ++ ++ dce110_aux_engine_construct(aux_engine, ctx, inst, ++ SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, ++ &aux_engine_regs[inst]); ++ ++ return &aux_engine->base.base; ++} + + static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 + NBIO_BASE(mmBIOS_SCRATCH_6_BASE_IDX) +@@ -467,6 +498,10 @@ static void destruct(struct dce110_resource_pool *pool) + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } ++ ++ if (pool->base.engines[i] != NULL) ++ dce110_engine_destroy(&pool->base.engines[i]); ++ + } + + for (i = 0; i < pool->base.audio_count; i++) { +@@ -984,6 +1019,13 @@ static bool construct( + dm_error( + "DC: failed to create output pixel processor!\n"); + } ++ pool->base.engines[i] = dce120_aux_engine_create(ctx, i); ++ if (pool->base.engines[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error( ++ "DC:failed to create aux engine!!\n"); ++ goto res_create_fail; ++ } + + /* check next valid pipe */ + j++; +diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c +index 2ac95ec..6fb33ad 100644 +--- a/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c +@@ -54,6 +54,7 @@ + #include "reg_helper.h" + + #include "dce/dce_dmcu.h" ++#include "dce/dce_aux.h" + #include "dce/dce_abm.h" + /* TODO remove this include */ + +@@ -298,6 +299,21 @@ static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_80(_MASK) + }; + ++#define aux_engine_regs(id)\ ++[id] = {\ ++ AUX_COMMON_REG_LIST(id), \ ++ .AUX_RESET_MASK = 0 \ ++} ++ ++static const struct dce110_aux_registers aux_engine_regs[] = { ++ aux_engine_regs(0), ++ aux_engine_regs(1), ++ aux_engine_regs(2), ++ aux_engine_regs(3), ++ aux_engine_regs(4), ++ aux_engine_regs(5) ++}; ++ + #define audio_regs(id)\ + [id] = {\ + AUD_COMMON_REG_LIST(id)\ +@@ -448,6 +464,23 @@ static struct output_pixel_processor *dce80_opp_create( + return &opp->base; + } + ++struct engine *dce80_aux_engine_create( ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ struct aux_engine_dce110 *aux_engine = ++ kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); ++ ++ if (!aux_engine) ++ return NULL; ++ ++ dce110_aux_engine_construct(aux_engine, ctx, inst, ++ SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, ++ &aux_engine_regs[inst]); ++ ++ return &aux_engine->base.base; ++} ++ + static struct stream_encoder *dce80_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +@@ -655,6 +688,9 @@ static void destruct(struct dce110_resource_pool *pool) + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } ++ ++ if (pool->base.engines[i] != NULL) ++ dce110_engine_destroy(&pool->base.engines[i]); + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { +@@ -899,6 +935,14 @@ static bool dce80_construct( + dm_error("DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } ++ ++ pool->base.engines[i] = dce80_aux_engine_create(ctx, i); ++ if (pool->base.engines[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error( ++ "DC:failed to create aux engine!!\n"); ++ goto res_create_fail; ++ } + } + + dc->caps.max_planes = pool->base.pipe_count; +diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c +index f9246d4..3f793a3 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c +@@ -64,6 +64,7 @@ + #include "reg_helper.h" + #include "dce/dce_abm.h" + #include "dce/dce_dmcu.h" ++#include "dce/dce_aux.h" + + const struct _vcs_dpi_ip_params_st dcn1_0_ip = { + .rob_buffer_size_kbytes = 64, +@@ -356,6 +357,21 @@ static const struct dcn10_opp_mask opp_mask = { + OPP_MASK_SH_LIST_DCN10(_MASK), + }; + ++#define aux_engine_regs(id)\ ++[id] = {\ ++ AUX_COMMON_REG_LIST(id), \ ++ .AUX_RESET_MASK = 0 \ ++} ++ ++static const struct dce110_aux_registers aux_engine_regs[] = { ++ aux_engine_regs(0), ++ aux_engine_regs(1), ++ aux_engine_regs(2), ++ aux_engine_regs(3), ++ aux_engine_regs(4), ++ aux_engine_regs(5) ++}; ++ + #define tf_regs(id)\ + [id] = {\ + TF_REG_LIST_DCN10(id),\ +@@ -578,6 +594,23 @@ static struct output_pixel_processor *dcn10_opp_create( + return &opp->base; + } + ++struct engine *dcn10_aux_engine_create( ++ struct dc_context *ctx, ++ uint32_t inst) ++{ ++ struct aux_engine_dce110 *aux_engine = ++ kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); ++ ++ if (!aux_engine) ++ return NULL; ++ ++ dce110_aux_engine_construct(aux_engine, ctx, inst, ++ SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, ++ &aux_engine_regs[inst]); ++ ++ return &aux_engine->base.base; ++} ++ + static struct mpc *dcn10_mpc_create(struct dc_context *ctx) + { + struct dcn10_mpc *mpc10 = kzalloc(sizeof(struct dcn10_mpc), +@@ -826,6 +859,9 @@ static void destruct(struct dcn10_resource_pool *pool) + kfree(DCN10TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } ++ ++ if (pool->base.engines[i] != NULL) ++ pool->base.engines[i]->funcs->destroy_engine(&pool->base.engines[i]); + } + + for (i = 0; i < pool->base.stream_enc_count; i++) +@@ -1257,6 +1293,14 @@ static bool construct( + goto fail; + } + ++ pool->base.engines[i] = dcn10_aux_engine_create(ctx, i); ++ if (pool->base.engines[i] == NULL) { ++ BREAK_TO_DEBUGGER(); ++ dm_error( ++ "DC:failed to create aux engine!!\n"); ++ goto fail; ++ } ++ + /* check next valid pipe */ + j++; + } +diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/engine.h b/drivers/gpu/drm/amd/display/dc/i2caux/engine.h +index 1e8a158..b16fb1f 100644 +--- a/drivers/gpu/drm/amd/display/dc/i2caux/engine.h ++++ b/drivers/gpu/drm/amd/display/dc/i2caux/engine.h +@@ -96,6 +96,7 @@ struct engine_funcs { + + struct engine { + const struct engine_funcs *funcs; ++ uint32_t inst; + struct ddc *ddc; + struct dc_context *ctx; + }; +diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h +index 816da02..3b7e9aa 100644 +--- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h ++++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h +@@ -138,7 +138,7 @@ struct resource_pool { + struct output_pixel_processor *opps[MAX_PIPES]; + struct timing_generator *timing_generators[MAX_PIPES]; + struct stream_encoder *stream_enc[MAX_PIPES * 2]; +- ++ struct engine *engines[MAX_PIPES]; + struct hubbub *hubbub; + struct mpc *mpc; + struct pp_smu_funcs_rv *pp_smu; +diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/aux_engine.h b/drivers/gpu/drm/amd/display/dc/inc/hw/aux_engine.h +new file mode 100644 +index 0000000..06d7e5d +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dc/inc/hw/aux_engine.h +@@ -0,0 +1,113 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_AUX_ENGINE_H__ ++#define __DAL_AUX_ENGINE_H__ ++ ++#include "engine.h" ++#include "include/i2caux_interface.h" ++ ++struct aux_engine; ++union aux_config; ++struct aux_engine_funcs { ++ void (*destroy)( ++ struct aux_engine **ptr); ++ bool (*acquire_engine)( ++ struct aux_engine *engine); ++ void (*configure)( ++ struct aux_engine *engine, ++ union aux_config cfg); ++ void (*submit_channel_request)( ++ struct aux_engine *engine, ++ struct aux_request_transaction_data *request); ++ void (*process_channel_reply)( ++ struct aux_engine *engine, ++ struct aux_reply_transaction_data *reply); ++ int (*read_channel_reply)( ++ struct aux_engine *engine, ++ uint32_t size, ++ uint8_t *buffer, ++ uint8_t *reply_result, ++ uint32_t *sw_status); ++ enum aux_channel_operation_result (*get_channel_status)( ++ struct aux_engine *engine, ++ uint8_t *returned_bytes); ++ bool (*is_engine_available)(struct aux_engine *engine); ++}; ++struct engine; ++struct aux_engine { ++ struct engine base; ++ const struct aux_engine_funcs *funcs; ++ /* following values are expressed in milliseconds */ ++ uint32_t delay; ++ uint32_t max_defer_write_retry; ++ ++ bool acquire_reset; ++}; ++struct read_command_context { ++ uint8_t *buffer; ++ uint32_t current_read_length; ++ uint32_t offset; ++ enum i2caux_transaction_status status; ++ ++ struct aux_request_transaction_data request; ++ struct aux_reply_transaction_data reply; ++ ++ uint8_t returned_byte; ++ ++ uint32_t timed_out_retry_aux; ++ uint32_t invalid_reply_retry_aux; ++ uint32_t defer_retry_aux; ++ uint32_t defer_retry_i2c; ++ uint32_t invalid_reply_retry_aux_on_ack; ++ ++ bool transaction_complete; ++ bool operation_succeeded; ++}; ++struct write_command_context { ++ bool mot; ++ ++ uint8_t *buffer; ++ uint32_t current_write_length; ++ enum i2caux_transaction_status status; ++ ++ struct aux_request_transaction_data request; ++ struct aux_reply_transaction_data reply; ++ ++ uint8_t returned_byte; ++ ++ uint32_t timed_out_retry_aux; ++ uint32_t invalid_reply_retry_aux; ++ uint32_t defer_retry_aux; ++ uint32_t defer_retry_i2c; ++ uint32_t max_defer_retry; ++ uint32_t ack_m_retry; ++ ++ uint8_t reply_data[DEFAULT_AUX_MAX_DATA_SIZE]; ++ ++ bool transaction_complete; ++ bool operation_succeeded; ++}; ++#endif +diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/engine.h b/drivers/gpu/drm/amd/display/dc/inc/hw/engine.h +new file mode 100644 +index 0000000..1f5476f +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dc/inc/hw/engine.h +@@ -0,0 +1,106 @@ ++/* ++ * Copyright 2012-15 Advanced Micro Devices, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: AMD ++ * ++ */ ++ ++#ifndef __DAL_ENGINE_H__ ++#define __DAL_ENGINE_H__ ++ ++#include "dc_ddc_types.h" ++ ++enum i2caux_transaction_operation { ++ I2CAUX_TRANSACTION_READ, ++ I2CAUX_TRANSACTION_WRITE ++}; ++ ++enum i2caux_transaction_address_space { ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C = 1, ++ I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD ++}; ++ ++struct i2caux_transaction_payload { ++ enum i2caux_transaction_address_space address_space; ++ uint32_t address; ++ uint32_t length; ++ uint8_t *data; ++}; ++ ++enum i2caux_transaction_status { ++ I2CAUX_TRANSACTION_STATUS_UNKNOWN = (-1L), ++ I2CAUX_TRANSACTION_STATUS_SUCCEEDED, ++ I2CAUX_TRANSACTION_STATUS_FAILED_CHANNEL_BUSY, ++ I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT, ++ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR, ++ I2CAUX_TRANSACTION_STATUS_FAILED_NACK, ++ I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE, ++ I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION, ++ I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION, ++ I2CAUX_TRANSACTION_STATUS_FAILED_BUFFER_OVERFLOW, ++ I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON ++}; ++ ++struct i2caux_transaction_request { ++ enum i2caux_transaction_operation operation; ++ struct i2caux_transaction_payload payload; ++ enum i2caux_transaction_status status; ++}; ++ ++enum i2caux_engine_type { ++ I2CAUX_ENGINE_TYPE_UNKNOWN = (-1L), ++ I2CAUX_ENGINE_TYPE_AUX, ++ I2CAUX_ENGINE_TYPE_I2C_DDC_HW, ++ I2CAUX_ENGINE_TYPE_I2C_GENERIC_HW, ++ I2CAUX_ENGINE_TYPE_I2C_SW ++}; ++ ++enum i2c_default_speed { ++ I2CAUX_DEFAULT_I2C_HW_SPEED = 50, ++ I2CAUX_DEFAULT_I2C_SW_SPEED = 50 ++}; ++ ++struct engine; ++ ++struct engine_funcs { ++ enum i2caux_engine_type (*get_engine_type)( ++ const struct engine *engine); ++ struct aux_engine* (*acquire)( ++ struct engine *engine, ++ struct ddc *ddc); ++ bool (*submit_request)( ++ struct engine *engine, ++ struct i2caux_transaction_request *request, ++ bool middle_of_transaction); ++ void (*release_engine)( ++ struct engine *engine); ++ void (*destroy_engine)( ++ struct engine **engine); ++}; ++ ++struct engine { ++ const struct engine_funcs *funcs; ++ uint32_t inst; ++ struct ddc *ddc; ++ struct dc_context *ctx; ++}; ++ ++#endif +-- +2.7.4 + |