/* * Copyright 2018 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: AMD *
*/ #include"resource.h" #include"dce_i2c.h" #include"dce_i2c_hw.h" #include"reg_helper.h" #include"include/gpio_service_interface.h"
/* start I2C transfer */
REG_UPDATE(DC_I2C_CONTROL, DC_I2C_GO, 1);
/* all transactions were executed and HW buffer became empty * (even though it actually happens when status becomes DONE)
*/
dce_i2c_hw->transaction_count = 0;
dce_i2c_hw->buffer_used_bytes = 0;
}
while (length) { /* after reading the status, * if the I2C operation executed successfully * (i.e. DC_I2C_STATUS_DONE = 1) then the I2C controller * should read data bytes from I2C circular data buffer
*/
/* Write the I2C address and I2C data * into the hardware circular buffer, one byte per entry. * As an example, the 7-bit I2C slave address for CRT monitor * for reading DDC/EDID information is 0b1010001. * For an I2C send operation, the LSB must be programmed to 0; * for I2C receive operation, the LSB must be programmed to 1.
*/ if (dce_i2c_hw->transaction_count == 0) {
value = REG_SET_4(DC_I2C_DATA, 0,
DC_I2C_DATA_RW, false,
DC_I2C_DATA, request->address,
DC_I2C_INDEX, 0,
DC_I2C_INDEX_WRITE, 1);
dce_i2c_hw->buffer_used_write = 0;
} else
value = REG_SET_2(DC_I2C_DATA, 0,
DC_I2C_DATA_RW, false,
DC_I2C_DATA, request->address);
dce_i2c_hw->buffer_used_write++;
if (!(request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ)) { while (length) {
REG_SET_2(DC_I2C_DATA, value,
DC_I2C_INDEX_WRITE, 0,
DC_I2C_DATA, *buffer++);
dce_i2c_hw->buffer_used_write++;
--length;
}
}
if (dce_i2c_hw->ctx->dc->debug.enable_mem_low_power.bits.i2c) { if (dce_i2c_hw->regs->DIO_MEM_PWR_CTRL) {
REG_UPDATE(DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, 0);
REG_WAIT(DIO_MEM_PWR_STATUS, I2C_MEM_PWR_STATE, 0, 0, 5);
}
}
if (dce_i2c_hw->masks->DC_I2C_DDC1_CLK_EN)
REG_UPDATE_N(SETUP, 1,
FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_CLK_EN), 1);
if (!acquire_engine(dce_i2c_hw)) returnfalse;
/*set SW requested I2c speed to default, if API calls in it will be override later*/
set_speed(dce_i2c_hw, dce_i2c_hw->ctx->dc->caps.i2c_speed_in_khz);
if (dce_i2c_hw->setup_limit != 0)
i2c_setup_limit = dce_i2c_hw->setup_limit;
/* Program time limit */ if (dce_i2c_hw->send_reset_length == 0) { /*pre-dcn*/
REG_UPDATE_N(SETUP, 2,
FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_TIME_LIMIT), i2c_setup_limit,
FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE), 1);
} else {
reset_length = dce_i2c_hw->send_reset_length;
REG_UPDATE_N(SETUP, 3,
FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_TIME_LIMIT), i2c_setup_limit,
FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_SEND_RESET_LENGTH), reset_length,
FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE), 1);
} /* Program HW priority * set to High - interrupt software I2C at any time * Enable restart of SW I2C that was interrupted by HW * disable queuing of software while I2C is in use by HW
*/
REG_UPDATE(DC_I2C_ARBITRATION,
DC_I2C_NO_QUEUED_SW_GO, 0);
returntrue;
}
/** * cntl_stuck_hw_workaround - Workaround for I2C engine stuck state * @dce_i2c_hw: Pointer to dce_i2c_hw structure * * If we boot without an HDMI display, the I2C engine does not get initialized * correctly. One of its symptoms is that SW_USE_I2C does not get cleared after * acquire. After setting SW_DONE_USING_I2C on release, the engine gets * immediately reacquired by SW, preventing DMUB from using it. * * This function checks the I2C arbitration status and applies a release * workaround if necessary.
*/ staticvoid cntl_stuck_hw_workaround(struct dce_i2c_hw *dce_i2c_hw)
{
uint32_t arbitrate = 0;
REG_GET(DC_I2C_ARBITRATION, DC_I2C_REG_RW_CNTL_STATUS, &arbitrate); if (arbitrate != DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW) return;
// Still acquired after release, release again as a workaround
REG_UPDATE(DC_I2C_ARBITRATION, DC_I2C_SW_DONE_USING_I2C_REG, true);
REG_GET(DC_I2C_ARBITRATION, DC_I2C_REG_RW_CNTL_STATUS, &arbitrate);
ASSERT(arbitrate != DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW);
}
/*for HW HDCP Ri polling failure w/a test*/
set_speed(dce_i2c_hw, dce_i2c_hw->ctx->dc->caps.i2c_speed_in_khz_hdcp); // Release I2C engine so it can be used by HW or DMCU, automatically clears SW_USE_I2C
REG_UPDATE(DC_I2C_ARBITRATION, DC_I2C_SW_DONE_USING_I2C_REG, true);
cntl_stuck_hw_workaround(dce_i2c_hw);
if (dce_i2c_hw->ctx->dc->debug.enable_mem_low_power.bits.i2c) { if (dce_i2c_hw->regs->DIO_MEM_PWR_CTRL)
REG_UPDATE(DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, 1);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.