staticinlinevoid msm_dp_write_aux(struct msm_dp_aux_private *aux,
u32 offset, u32 data)
{ /* * To make sure aux reg writes happens before any other operation, * this function uses writel() instread of writel_relaxed()
*/
writel(data, aux->aux_base + offset);
}
/* * NOTE: resetting AUX controller will also clear any pending HPD related interrupts
*/ staticvoid msm_dp_aux_reset(struct msm_dp_aux_private *aux)
{
u32 aux_ctrl;
/* poll for hpd connected status every 2ms and timeout after wait_us */ return readl_poll_timeout(aux->aux_base +
REG_DP_DP_HPD_INT_STATUS,
state, state & DP_DP_HPD_STATE_STATUS_CONNECTED,
min(wait_us, 2000), wait_us);
}
/* * cmd fifo only has depth of 144 bytes * limit buf length to 128 bytes here
*/ if (len > AUX_CMD_FIFO_LEN - 4) {
DRM_ERROR("buf size greater than allowed size of 128 bytes\n"); return -EINVAL;
}
/* Pack cmd and write to HW */
data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */ if (aux->read)
data[0] |= BIT(4); /* R/W */
data = input_msg->buffer; if (input_msg->address == segment_address)
aux->segment = *data; else
aux->offset = *data;
}
/** * msm_dp_aux_transfer_helper() - helper function for EDID read transactions * * @aux: DP AUX private structure * @input_msg: input message from DRM upstream APIs * @send_seg: send the segment to sink * * return: void * * This helper function is used to fix EDID reads for non-compliant * sinks that do not handle the i2c middle-of-transaction flag correctly.
*/ staticvoid msm_dp_aux_transfer_helper(struct msm_dp_aux_private *aux, struct drm_dp_aux_msg *input_msg, bool send_seg)
{ struct drm_dp_aux_msg helper_msg;
u32 message_size = 0x10;
u32 segment_address = 0x30;
u32 const edid_block_length = 0x80; bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT; bool i2c_read = input_msg->request &
(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
if (!i2c_mot || !i2c_read || (input_msg->size == 0)) return;
/* * Sending the segment value and EDID offset will be performed * from the DRM upstream EDID driver for each block. Avoid * duplicate AUX transactions related to this while reading the * first 16 bytes of each block.
*/ if (!(aux->offset % edid_block_length) || !send_seg) goto end;
/* * Send the segment address for every i2c read in which the * middle-of-tranaction flag is set. This is required to support EDID * reads of more than 2 blocks as the segment address is reset to 0 * since we are overriding the middle-of-transaction flag for read * transactions.
*/
/* * Send the offset address for every i2c read in which the * middle-of-transaction flag is set. This will ensure that the sink * will update its read pointer and return the correct portion of the * EDID buffer in the subsequent i2c read trasntion triggered in the * native AUX transfer function.
*/
memset(&helper_msg, 0, sizeof(helper_msg));
helper_msg.address = input_msg->address;
helper_msg.buffer = &aux->offset;
helper_msg.size = 1;
msm_dp_aux_cmd_fifo_tx(aux, &helper_msg);
end:
aux->offset += message_size; if (aux->offset == 0x80 || aux->offset == 0x100)
aux->segment = 0x0; /* reset segment at end of block */
}
/* * This function does the real job to process an AUX transaction. * It will call aux_reset() function to reset the AUX channel, * if the waiting is timeout.
*/ static ssize_t msm_dp_aux_transfer(struct drm_dp_aux *msm_dp_aux, struct drm_dp_aux_msg *msg)
{
ssize_t ret; intconst aux_cmd_native_max = 16; intconst aux_cmd_i2c_max = 128; struct msm_dp_aux_private *aux;
aux = container_of(msm_dp_aux, struct msm_dp_aux_private, msm_dp_aux);
ret = pm_runtime_resume_and_get(msm_dp_aux->dev); if (ret) return ret;
mutex_lock(&aux->mutex); if (!aux->initted) {
ret = -EIO; gotoexit;
}
/* * If we're using DP and an external display isn't connected then the * transfer won't succeed. Return right away. If we don't do this we * can end up with long timeouts if someone tries to access the DP AUX * character device when no DP device is connected.
*/ if (!aux->is_edp && !aux->enable_xfers) {
ret = -ENXIO; gotoexit;
}
ret = msm_dp_aux_cmd_fifo_tx(aux, msg); if (ret < 0) { if (aux->native) {
aux->retry_cnt++; if (!(aux->retry_cnt % MAX_AUX_RETRIES))
phy_calibrate(aux->phy);
} /* reset aux if link is in connected state */ if (msm_dp_aux_is_link_connected(msm_dp_aux))
msm_dp_aux_reset(aux);
} else {
aux->retry_cnt = 0; switch (aux->aux_error_num) { case DP_AUX_ERR_NONE: if (aux->read)
ret = msm_dp_aux_cmd_fifo_rx(aux, msg);
msg->reply = aux->native ? DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK; break; case DP_AUX_ERR_DEFER:
msg->reply = aux->native ? DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER; break; case DP_AUX_ERR_PHY: case DP_AUX_ERR_ADDR: case DP_AUX_ERR_NACK: case DP_AUX_ERR_NACK_DEFER:
msg->reply = aux->native ? DP_AUX_NATIVE_REPLY_NACK : DP_AUX_I2C_REPLY_NACK; break; case DP_AUX_ERR_TOUT:
ret = -ETIMEDOUT; break;
}
}
if (!msm_dp_aux) {
DRM_ERROR("invalid input\n"); return IRQ_NONE;
}
aux = container_of(msm_dp_aux, struct msm_dp_aux_private, msm_dp_aux);
if (!aux->cmd_busy) {
DRM_ERROR("Unexpected DP AUX IRQ %#010x when not busy\n", isr); return IRQ_NONE;
}
/* * The logic below assumes only one error bit is set (other than "done" * which can apparently be set at the same time as some of the other * bits). Warn if more than one get set so we know we need to improve * the logic.
*/ if (hweight32(isr & ~DP_INTR_AUX_XFER_DONE) > 1)
DRM_WARN("Some DP AUX interrupts unhandled: %#010x\n", isr);
/* * We only want to return interrupts that are unmasked to the caller. * However, the interrupt status field also contains other * informational bits about the HPD state status, so we only mask * out the part of the register that tells us about which interrupts * are pending.
*/ return isr & (mask | ~DP_DP_HPD_INT_MASK);
}
/* * Use the drm_dp_aux_init() to use the aux adapter * before registering AUX with the DRM device so that * msm eDP panel can be detected by generic_dep_panel_probe().
*/
aux->msm_dp_aux.name = "dpu_dp_aux";
aux->msm_dp_aux.dev = dev;
aux->msm_dp_aux.transfer = msm_dp_aux_transfer;
aux->msm_dp_aux.wait_hpd_asserted = msm_dp_wait_hpd_asserted;
drm_dp_aux_init(&aux->msm_dp_aux);
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.