/* * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. * * 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 (including the next * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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: * Ke Yu * Zhiyuan Lv <zhiyuan.lv@intel.com> * * Contributors: * Terrence Xu <terrence.xu@intel.com> * Changbin Du <changbin.du@intel.com> * Bing Niu <bing.niu@intel.com> * Zhi Wang <zhi.a.wang@intel.com> *
*/
if (IS_BROXTON(i915))
port = bxt_get_port_from_gmbus0(pin_select); elseif (IS_COFFEELAKE(i915) || IS_COMETLAKE(i915))
port = cnp_get_port_from_gmbus0(pin_select); else
port = get_port_from_gmbus0(pin_select); if (drm_WARN_ON(&i915->drm, port < 0)) return 0;
if (vgpu_vreg(vgpu, offset) & GMBUS_SW_CLR_INT) { if (!(wvalue & GMBUS_SW_CLR_INT)) {
vgpu_vreg(vgpu, offset) &= ~GMBUS_SW_CLR_INT;
reset_gmbus_controller(vgpu);
} /* * TODO: "This bit is cleared to zero when an event * causes the HW_RDY bit transition to occur "
*/
} else { /* * per bspec setting this bit can cause: * 1) INT status bit cleared * 2) HW_RDY bit asserted
*/ if (wvalue & GMBUS_SW_CLR_INT) {
vgpu_vreg_t(vgpu, PCH_GMBUS2) &= ~GMBUS_INT;
vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_HW_RDY;
}
/* For virtualization, we suppose that HW is always ready, * so GMBUS_SW_RDY should always be cleared
*/ if (wvalue & GMBUS_SW_RDY)
wvalue &= ~GMBUS_SW_RDY;
/* vgpu gmbus only support EDID */ if (target_addr == EDID_ADDR) {
i2c_edid->target_selected = true;
} elseif (target_addr != 0) {
gvt_dbg_dpy( "vgpu%d: unsupported gmbus target addr(0x%x)\n" " gmbus operations will be ignored.\n",
vgpu->id, target_addr);
}
if (wvalue & GMBUS_CYCLE_INDEX)
i2c_edid->current_edid_read =
gmbus1_target_index(wvalue);
i2c_edid->gmbus.cycle_type = gmbus1_bus_cycle(wvalue); switch (gmbus1_bus_cycle(wvalue)) { case GMBUS_NOCYCLE: break; case GMBUS_STOP: /* From spec: * This can only cause a STOP to be generated * if a GMBUS cycle is generated, the GMBUS is * currently in a data/wait/idle phase, or it is in a * WAIT phase
*/ if (gmbus1_bus_cycle(vgpu_vreg(vgpu, offset))
!= GMBUS_NOCYCLE) {
intel_vgpu_init_i2c_edid(vgpu); /* After the 'stop' cycle, hw state would become * 'stop phase' and then 'idle phase' after a * few milliseconds. In emulation, we just set * it as 'idle phase' ('stop phase' is not * visible in gmbus interface)
*/
i2c_edid->gmbus.phase = GMBUS_IDLE_PHASE;
vgpu_vreg_t(vgpu, PCH_GMBUS2) &= ~GMBUS_ACTIVE;
} break; case NIDX_NS_W: case IDX_NS_W: case NIDX_STOP: case IDX_STOP: /* From hw spec the GMBUS phase * transition like this: * START (-->INDEX) -->DATA
*/
i2c_edid->gmbus.phase = GMBUS_DATA_PHASE;
vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_ACTIVE; break; default:
gvt_vgpu_err("Unknown/reserved GMBUS cycle detected!\n"); break;
} /* * From hw spec the WAIT state will be * cleared: * (1) in a new GMBUS cycle * (2) by generating a stop
*/
vgpu_vreg(vgpu, offset) = wvalue;
} return 0;
}
/* Data can only be received if previous settings correct */ if (vgpu_vreg_t(vgpu, PCH_GMBUS1) & GMBUS_SLAVE_READ) { if (byte_left <= 0) {
memcpy(p_data, &vgpu_vreg(vgpu, offset), bytes); return 0;
}
if (byte_count > 4)
byte_count = 4; for (i = 0; i < byte_count; i++) {
byte_data = edid_get_byte(vgpu);
reg_data |= (byte_data << (i << 3));
}
// check the msg in DATA register.
msg = vgpu_vreg(vgpu, offset + 4);
addr = (msg >> 8) & 0xffff;
ctrl = (msg >> 24) & 0xff;
op = ctrl >> 4; if (!(value & DP_AUX_CH_CTL_SEND_BUSY)) { /* The ctl write to clear some states */ return;
}
/* Always set the wanted value for vms. */
ret_msg_size = (((op & 0x1) == DP_AUX_I2C_READ) ? 2 : 1);
vgpu_vreg(vgpu, offset) =
DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_MESSAGE_SIZE(ret_msg_size);
if (msg_length == 3) { if (!(op & DP_AUX_I2C_MOT)) { /* stop */
intel_vgpu_init_i2c_edid(vgpu);
} else { /* start or restart */
i2c_edid->aux_ch.i2c_over_aux_ch = true;
i2c_edid->aux_ch.aux_ch_mot = true; if (addr == 0) { /* reset the address */
intel_vgpu_init_i2c_edid(vgpu);
} elseif (addr == EDID_ADDR) {
i2c_edid->state = I2C_AUX_CH;
i2c_edid->port = port_idx;
i2c_edid->target_selected = true; if (intel_vgpu_has_monitor_on_port(vgpu,
port_idx) &&
intel_vgpu_port_is_dp(vgpu, port_idx))
i2c_edid->edid_available = true;
}
}
} elseif ((op & 0x1) == DP_AUX_I2C_WRITE) { /* TODO * We only support EDID reading from I2C_over_AUX. And * we do not expect the index mode to be used. Right now * the WRITE operation is ignored. It is good enough to * support the gfx driver to do EDID access.
*/
} else { if (drm_WARN_ON(&i915->drm, (op & 0x1) != DP_AUX_I2C_READ)) return; if (drm_WARN_ON(&i915->drm, msg_length != 4)) return; if (i2c_edid->edid_available && i2c_edid->target_selected) { unsignedchar val = edid_get_byte(vgpu);
aux_data_for_write = (val << 16);
} else
aux_data_for_write = (0xff << 16);
} /* write the return value in AUX_CH_DATA reg which includes: * ACK of I2C_WRITE * returned byte if it is READ
*/
aux_data_for_write |= DP_AUX_I2C_REPLY_ACK << 24;
vgpu_vreg(vgpu, offset + 4) = aux_data_for_write;
}
/** * intel_vgpu_init_i2c_edid - initialize vGPU i2c edid emulation * @vgpu: a vGPU * * This function is used to initialize vGPU i2c edid emulation stuffs *
*/ void intel_vgpu_init_i2c_edid(struct intel_vgpu *vgpu)
{ struct intel_vgpu_i2c_edid *edid = &vgpu->display.i2c_edid;
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.