162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next 1262306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 1362306a36Sopenharmony_ci * Software. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1862306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1962306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2062306a36Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 2162306a36Sopenharmony_ci * SOFTWARE. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Authors: 2462306a36Sopenharmony_ci * Ke Yu 2562306a36Sopenharmony_ci * Zhiyuan Lv <zhiyuan.lv@intel.com> 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Contributors: 2862306a36Sopenharmony_ci * Terrence Xu <terrence.xu@intel.com> 2962306a36Sopenharmony_ci * Changbin Du <changbin.du@intel.com> 3062306a36Sopenharmony_ci * Bing Niu <bing.niu@intel.com> 3162306a36Sopenharmony_ci * Zhi Wang <zhi.a.wang@intel.com> 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include "display/intel_dp_aux_regs.h" 3662306a36Sopenharmony_ci#include "display/intel_gmbus_regs.h" 3762306a36Sopenharmony_ci#include "gvt.h" 3862306a36Sopenharmony_ci#include "i915_drv.h" 3962306a36Sopenharmony_ci#include "i915_reg.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define GMBUS1_TOTAL_BYTES_SHIFT 16 4262306a36Sopenharmony_ci#define GMBUS1_TOTAL_BYTES_MASK 0x1ff 4362306a36Sopenharmony_ci#define gmbus1_total_byte_count(v) (((v) >> \ 4462306a36Sopenharmony_ci GMBUS1_TOTAL_BYTES_SHIFT) & GMBUS1_TOTAL_BYTES_MASK) 4562306a36Sopenharmony_ci#define gmbus1_slave_addr(v) (((v) & 0xff) >> 1) 4662306a36Sopenharmony_ci#define gmbus1_slave_index(v) (((v) >> 8) & 0xff) 4762306a36Sopenharmony_ci#define gmbus1_bus_cycle(v) (((v) >> 25) & 0x7) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* GMBUS0 bits definitions */ 5062306a36Sopenharmony_ci#define _GMBUS_PIN_SEL_MASK (0x7) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic unsigned char edid_get_byte(struct intel_vgpu *vgpu) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct intel_vgpu_i2c_edid *edid = &vgpu->display.i2c_edid; 5562306a36Sopenharmony_ci unsigned char chr = 0; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (edid->state == I2C_NOT_SPECIFIED || !edid->slave_selected) { 5862306a36Sopenharmony_ci gvt_vgpu_err("Driver tries to read EDID without proper sequence!\n"); 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci if (edid->current_edid_read >= EDID_SIZE) { 6262306a36Sopenharmony_ci gvt_vgpu_err("edid_get_byte() exceeds the size of EDID!\n"); 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (!edid->edid_available) { 6762306a36Sopenharmony_ci gvt_vgpu_err("Reading EDID but EDID is not available!\n"); 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (intel_vgpu_has_monitor_on_port(vgpu, edid->port)) { 7262306a36Sopenharmony_ci struct intel_vgpu_edid_data *edid_data = 7362306a36Sopenharmony_ci intel_vgpu_port(vgpu, edid->port)->edid; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci chr = edid_data->edid_block[edid->current_edid_read]; 7662306a36Sopenharmony_ci edid->current_edid_read++; 7762306a36Sopenharmony_ci } else { 7862306a36Sopenharmony_ci gvt_vgpu_err("No EDID available during the reading?\n"); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci return chr; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic inline int cnp_get_port_from_gmbus0(u32 gmbus0) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; 8662306a36Sopenharmony_ci int port = -EINVAL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (port_select == GMBUS_PIN_1_BXT) 8962306a36Sopenharmony_ci port = PORT_B; 9062306a36Sopenharmony_ci else if (port_select == GMBUS_PIN_2_BXT) 9162306a36Sopenharmony_ci port = PORT_C; 9262306a36Sopenharmony_ci else if (port_select == GMBUS_PIN_3_BXT) 9362306a36Sopenharmony_ci port = PORT_D; 9462306a36Sopenharmony_ci else if (port_select == GMBUS_PIN_4_CNP) 9562306a36Sopenharmony_ci port = PORT_E; 9662306a36Sopenharmony_ci return port; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic inline int bxt_get_port_from_gmbus0(u32 gmbus0) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; 10262306a36Sopenharmony_ci int port = -EINVAL; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (port_select == GMBUS_PIN_1_BXT) 10562306a36Sopenharmony_ci port = PORT_B; 10662306a36Sopenharmony_ci else if (port_select == GMBUS_PIN_2_BXT) 10762306a36Sopenharmony_ci port = PORT_C; 10862306a36Sopenharmony_ci else if (port_select == GMBUS_PIN_3_BXT) 10962306a36Sopenharmony_ci port = PORT_D; 11062306a36Sopenharmony_ci return port; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline int get_port_from_gmbus0(u32 gmbus0) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; 11662306a36Sopenharmony_ci int port = -EINVAL; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (port_select == GMBUS_PIN_VGADDC) 11962306a36Sopenharmony_ci port = PORT_E; 12062306a36Sopenharmony_ci else if (port_select == GMBUS_PIN_DPC) 12162306a36Sopenharmony_ci port = PORT_C; 12262306a36Sopenharmony_ci else if (port_select == GMBUS_PIN_DPB) 12362306a36Sopenharmony_ci port = PORT_B; 12462306a36Sopenharmony_ci else if (port_select == GMBUS_PIN_DPD) 12562306a36Sopenharmony_ci port = PORT_D; 12662306a36Sopenharmony_ci return port; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void reset_gmbus_controller(struct intel_vgpu *vgpu) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) = GMBUS_HW_RDY; 13262306a36Sopenharmony_ci if (!vgpu->display.i2c_edid.edid_available) 13362306a36Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_SATOER; 13462306a36Sopenharmony_ci vgpu->display.i2c_edid.gmbus.phase = GMBUS_IDLE_PHASE; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* GMBUS0 */ 13862306a36Sopenharmony_cistatic int gmbus0_mmio_write(struct intel_vgpu *vgpu, 13962306a36Sopenharmony_ci unsigned int offset, void *p_data, unsigned int bytes) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct drm_i915_private *i915 = vgpu->gvt->gt->i915; 14262306a36Sopenharmony_ci int port, pin_select; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci memcpy(&vgpu_vreg(vgpu, offset), p_data, bytes); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci pin_select = vgpu_vreg(vgpu, offset) & _GMBUS_PIN_SEL_MASK; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci intel_vgpu_init_i2c_edid(vgpu); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (pin_select == 0) 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (IS_BROXTON(i915)) 15462306a36Sopenharmony_ci port = bxt_get_port_from_gmbus0(pin_select); 15562306a36Sopenharmony_ci else if (IS_COFFEELAKE(i915) || IS_COMETLAKE(i915)) 15662306a36Sopenharmony_ci port = cnp_get_port_from_gmbus0(pin_select); 15762306a36Sopenharmony_ci else 15862306a36Sopenharmony_ci port = get_port_from_gmbus0(pin_select); 15962306a36Sopenharmony_ci if (drm_WARN_ON(&i915->drm, port < 0)) 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci vgpu->display.i2c_edid.state = I2C_GMBUS; 16362306a36Sopenharmony_ci vgpu->display.i2c_edid.gmbus.phase = GMBUS_IDLE_PHASE; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) &= ~GMBUS_ACTIVE; 16662306a36Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_HW_RDY | GMBUS_HW_WAIT_PHASE; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (intel_vgpu_has_monitor_on_port(vgpu, port) && 16962306a36Sopenharmony_ci !intel_vgpu_port_is_dp(vgpu, port)) { 17062306a36Sopenharmony_ci vgpu->display.i2c_edid.port = port; 17162306a36Sopenharmony_ci vgpu->display.i2c_edid.edid_available = true; 17262306a36Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) &= ~GMBUS_SATOER; 17362306a36Sopenharmony_ci } else 17462306a36Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_SATOER; 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int gmbus1_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, 17962306a36Sopenharmony_ci void *p_data, unsigned int bytes) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct intel_vgpu_i2c_edid *i2c_edid = &vgpu->display.i2c_edid; 18262306a36Sopenharmony_ci u32 slave_addr; 18362306a36Sopenharmony_ci u32 wvalue = *(u32 *)p_data; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (vgpu_vreg(vgpu, offset) & GMBUS_SW_CLR_INT) { 18662306a36Sopenharmony_ci if (!(wvalue & GMBUS_SW_CLR_INT)) { 18762306a36Sopenharmony_ci vgpu_vreg(vgpu, offset) &= ~GMBUS_SW_CLR_INT; 18862306a36Sopenharmony_ci reset_gmbus_controller(vgpu); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * TODO: "This bit is cleared to zero when an event 19262306a36Sopenharmony_ci * causes the HW_RDY bit transition to occur " 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci } else { 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * per bspec setting this bit can cause: 19762306a36Sopenharmony_ci * 1) INT status bit cleared 19862306a36Sopenharmony_ci * 2) HW_RDY bit asserted 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci if (wvalue & GMBUS_SW_CLR_INT) { 20162306a36Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) &= ~GMBUS_INT; 20262306a36Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_HW_RDY; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* For virtualization, we suppose that HW is always ready, 20662306a36Sopenharmony_ci * so GMBUS_SW_RDY should always be cleared 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci if (wvalue & GMBUS_SW_RDY) 20962306a36Sopenharmony_ci wvalue &= ~GMBUS_SW_RDY; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci i2c_edid->gmbus.total_byte_count = 21262306a36Sopenharmony_ci gmbus1_total_byte_count(wvalue); 21362306a36Sopenharmony_ci slave_addr = gmbus1_slave_addr(wvalue); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* vgpu gmbus only support EDID */ 21662306a36Sopenharmony_ci if (slave_addr == EDID_ADDR) { 21762306a36Sopenharmony_ci i2c_edid->slave_selected = true; 21862306a36Sopenharmony_ci } else if (slave_addr != 0) { 21962306a36Sopenharmony_ci gvt_dbg_dpy( 22062306a36Sopenharmony_ci "vgpu%d: unsupported gmbus slave addr(0x%x)\n" 22162306a36Sopenharmony_ci " gmbus operations will be ignored.\n", 22262306a36Sopenharmony_ci vgpu->id, slave_addr); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (wvalue & GMBUS_CYCLE_INDEX) 22662306a36Sopenharmony_ci i2c_edid->current_edid_read = 22762306a36Sopenharmony_ci gmbus1_slave_index(wvalue); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci i2c_edid->gmbus.cycle_type = gmbus1_bus_cycle(wvalue); 23062306a36Sopenharmony_ci switch (gmbus1_bus_cycle(wvalue)) { 23162306a36Sopenharmony_ci case GMBUS_NOCYCLE: 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case GMBUS_STOP: 23462306a36Sopenharmony_ci /* From spec: 23562306a36Sopenharmony_ci * This can only cause a STOP to be generated 23662306a36Sopenharmony_ci * if a GMBUS cycle is generated, the GMBUS is 23762306a36Sopenharmony_ci * currently in a data/wait/idle phase, or it is in a 23862306a36Sopenharmony_ci * WAIT phase 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci if (gmbus1_bus_cycle(vgpu_vreg(vgpu, offset)) 24162306a36Sopenharmony_ci != GMBUS_NOCYCLE) { 24262306a36Sopenharmony_ci intel_vgpu_init_i2c_edid(vgpu); 24362306a36Sopenharmony_ci /* After the 'stop' cycle, hw state would become 24462306a36Sopenharmony_ci * 'stop phase' and then 'idle phase' after a 24562306a36Sopenharmony_ci * few milliseconds. In emulation, we just set 24662306a36Sopenharmony_ci * it as 'idle phase' ('stop phase' is not 24762306a36Sopenharmony_ci * visible in gmbus interface) 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci i2c_edid->gmbus.phase = GMBUS_IDLE_PHASE; 25062306a36Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) &= ~GMBUS_ACTIVE; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci case NIDX_NS_W: 25462306a36Sopenharmony_ci case IDX_NS_W: 25562306a36Sopenharmony_ci case NIDX_STOP: 25662306a36Sopenharmony_ci case IDX_STOP: 25762306a36Sopenharmony_ci /* From hw spec the GMBUS phase 25862306a36Sopenharmony_ci * transition like this: 25962306a36Sopenharmony_ci * START (-->INDEX) -->DATA 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci i2c_edid->gmbus.phase = GMBUS_DATA_PHASE; 26262306a36Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_ACTIVE; 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci default: 26562306a36Sopenharmony_ci gvt_vgpu_err("Unknown/reserved GMBUS cycle detected!\n"); 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * From hw spec the WAIT state will be 27062306a36Sopenharmony_ci * cleared: 27162306a36Sopenharmony_ci * (1) in a new GMBUS cycle 27262306a36Sopenharmony_ci * (2) by generating a stop 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci vgpu_vreg(vgpu, offset) = wvalue; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int gmbus3_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, 28062306a36Sopenharmony_ci void *p_data, unsigned int bytes) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct drm_i915_private *i915 = vgpu->gvt->gt->i915; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci drm_WARN_ON(&i915->drm, 1); 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int gmbus3_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, 28962306a36Sopenharmony_ci void *p_data, unsigned int bytes) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci int i; 29262306a36Sopenharmony_ci unsigned char byte_data; 29362306a36Sopenharmony_ci struct intel_vgpu_i2c_edid *i2c_edid = &vgpu->display.i2c_edid; 29462306a36Sopenharmony_ci int byte_left = i2c_edid->gmbus.total_byte_count - 29562306a36Sopenharmony_ci i2c_edid->current_edid_read; 29662306a36Sopenharmony_ci int byte_count = byte_left; 29762306a36Sopenharmony_ci u32 reg_data = 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Data can only be recevied if previous settings correct */ 30062306a36Sopenharmony_ci if (vgpu_vreg_t(vgpu, PCH_GMBUS1) & GMBUS_SLAVE_READ) { 30162306a36Sopenharmony_ci if (byte_left <= 0) { 30262306a36Sopenharmony_ci memcpy(p_data, &vgpu_vreg(vgpu, offset), bytes); 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (byte_count > 4) 30762306a36Sopenharmony_ci byte_count = 4; 30862306a36Sopenharmony_ci for (i = 0; i < byte_count; i++) { 30962306a36Sopenharmony_ci byte_data = edid_get_byte(vgpu); 31062306a36Sopenharmony_ci reg_data |= (byte_data << (i << 3)); 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci memcpy(&vgpu_vreg(vgpu, offset), ®_data, byte_count); 31462306a36Sopenharmony_ci memcpy(p_data, &vgpu_vreg(vgpu, offset), bytes); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (byte_left <= 4) { 31762306a36Sopenharmony_ci switch (i2c_edid->gmbus.cycle_type) { 31862306a36Sopenharmony_ci case NIDX_STOP: 31962306a36Sopenharmony_ci case IDX_STOP: 32062306a36Sopenharmony_ci i2c_edid->gmbus.phase = GMBUS_IDLE_PHASE; 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci case NIDX_NS_W: 32362306a36Sopenharmony_ci case IDX_NS_W: 32462306a36Sopenharmony_ci default: 32562306a36Sopenharmony_ci i2c_edid->gmbus.phase = GMBUS_WAIT_PHASE; 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci intel_vgpu_init_i2c_edid(vgpu); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * Read GMBUS3 during send operation, 33262306a36Sopenharmony_ci * return the latest written value 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci } else { 33562306a36Sopenharmony_ci memcpy(p_data, &vgpu_vreg(vgpu, offset), bytes); 33662306a36Sopenharmony_ci gvt_vgpu_err("warning: gmbus3 read with nothing returned\n"); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci return 0; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int gmbus2_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, 34262306a36Sopenharmony_ci void *p_data, unsigned int bytes) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci u32 value = vgpu_vreg(vgpu, offset); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (!(vgpu_vreg(vgpu, offset) & GMBUS_INUSE)) 34762306a36Sopenharmony_ci vgpu_vreg(vgpu, offset) |= GMBUS_INUSE; 34862306a36Sopenharmony_ci memcpy(p_data, (void *)&value, bytes); 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int gmbus2_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, 35362306a36Sopenharmony_ci void *p_data, unsigned int bytes) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci u32 wvalue = *(u32 *)p_data; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (wvalue & GMBUS_INUSE) 35862306a36Sopenharmony_ci vgpu_vreg(vgpu, offset) &= ~GMBUS_INUSE; 35962306a36Sopenharmony_ci /* All other bits are read-only */ 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/** 36462306a36Sopenharmony_ci * intel_gvt_i2c_handle_gmbus_read - emulate gmbus register mmio read 36562306a36Sopenharmony_ci * @vgpu: a vGPU 36662306a36Sopenharmony_ci * @offset: reg offset 36762306a36Sopenharmony_ci * @p_data: data return buffer 36862306a36Sopenharmony_ci * @bytes: access data length 36962306a36Sopenharmony_ci * 37062306a36Sopenharmony_ci * This function is used to emulate gmbus register mmio read 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * Returns: 37362306a36Sopenharmony_ci * Zero on success, negative error code if failed. 37462306a36Sopenharmony_ci * 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ciint intel_gvt_i2c_handle_gmbus_read(struct intel_vgpu *vgpu, 37762306a36Sopenharmony_ci unsigned int offset, void *p_data, unsigned int bytes) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct drm_i915_private *i915 = vgpu->gvt->gt->i915; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (drm_WARN_ON(&i915->drm, bytes > 8 && (offset & (bytes - 1)))) 38262306a36Sopenharmony_ci return -EINVAL; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (offset == i915_mmio_reg_offset(PCH_GMBUS2)) 38562306a36Sopenharmony_ci return gmbus2_mmio_read(vgpu, offset, p_data, bytes); 38662306a36Sopenharmony_ci else if (offset == i915_mmio_reg_offset(PCH_GMBUS3)) 38762306a36Sopenharmony_ci return gmbus3_mmio_read(vgpu, offset, p_data, bytes); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci memcpy(p_data, &vgpu_vreg(vgpu, offset), bytes); 39062306a36Sopenharmony_ci return 0; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci/** 39462306a36Sopenharmony_ci * intel_gvt_i2c_handle_gmbus_write - emulate gmbus register mmio write 39562306a36Sopenharmony_ci * @vgpu: a vGPU 39662306a36Sopenharmony_ci * @offset: reg offset 39762306a36Sopenharmony_ci * @p_data: data return buffer 39862306a36Sopenharmony_ci * @bytes: access data length 39962306a36Sopenharmony_ci * 40062306a36Sopenharmony_ci * This function is used to emulate gmbus register mmio write 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * Returns: 40362306a36Sopenharmony_ci * Zero on success, negative error code if failed. 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ciint intel_gvt_i2c_handle_gmbus_write(struct intel_vgpu *vgpu, 40762306a36Sopenharmony_ci unsigned int offset, void *p_data, unsigned int bytes) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct drm_i915_private *i915 = vgpu->gvt->gt->i915; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (drm_WARN_ON(&i915->drm, bytes > 8 && (offset & (bytes - 1)))) 41262306a36Sopenharmony_ci return -EINVAL; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (offset == i915_mmio_reg_offset(PCH_GMBUS0)) 41562306a36Sopenharmony_ci return gmbus0_mmio_write(vgpu, offset, p_data, bytes); 41662306a36Sopenharmony_ci else if (offset == i915_mmio_reg_offset(PCH_GMBUS1)) 41762306a36Sopenharmony_ci return gmbus1_mmio_write(vgpu, offset, p_data, bytes); 41862306a36Sopenharmony_ci else if (offset == i915_mmio_reg_offset(PCH_GMBUS2)) 41962306a36Sopenharmony_ci return gmbus2_mmio_write(vgpu, offset, p_data, bytes); 42062306a36Sopenharmony_ci else if (offset == i915_mmio_reg_offset(PCH_GMBUS3)) 42162306a36Sopenharmony_ci return gmbus3_mmio_write(vgpu, offset, p_data, bytes); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci memcpy(&vgpu_vreg(vgpu, offset), p_data, bytes); 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cienum { 42862306a36Sopenharmony_ci AUX_CH_CTL = 0, 42962306a36Sopenharmony_ci AUX_CH_DATA1, 43062306a36Sopenharmony_ci AUX_CH_DATA2, 43162306a36Sopenharmony_ci AUX_CH_DATA3, 43262306a36Sopenharmony_ci AUX_CH_DATA4, 43362306a36Sopenharmony_ci AUX_CH_DATA5 43462306a36Sopenharmony_ci}; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic inline int get_aux_ch_reg(unsigned int offset) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci int reg; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci switch (offset & 0xff) { 44162306a36Sopenharmony_ci case 0x10: 44262306a36Sopenharmony_ci reg = AUX_CH_CTL; 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci case 0x14: 44562306a36Sopenharmony_ci reg = AUX_CH_DATA1; 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci case 0x18: 44862306a36Sopenharmony_ci reg = AUX_CH_DATA2; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case 0x1c: 45162306a36Sopenharmony_ci reg = AUX_CH_DATA3; 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci case 0x20: 45462306a36Sopenharmony_ci reg = AUX_CH_DATA4; 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case 0x24: 45762306a36Sopenharmony_ci reg = AUX_CH_DATA5; 45862306a36Sopenharmony_ci break; 45962306a36Sopenharmony_ci default: 46062306a36Sopenharmony_ci reg = -1; 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci return reg; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/** 46762306a36Sopenharmony_ci * intel_gvt_i2c_handle_aux_ch_write - emulate AUX channel register write 46862306a36Sopenharmony_ci * @vgpu: a vGPU 46962306a36Sopenharmony_ci * @port_idx: port index 47062306a36Sopenharmony_ci * @offset: reg offset 47162306a36Sopenharmony_ci * @p_data: write ptr 47262306a36Sopenharmony_ci * 47362306a36Sopenharmony_ci * This function is used to emulate AUX channel register write 47462306a36Sopenharmony_ci * 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_civoid intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, 47762306a36Sopenharmony_ci int port_idx, 47862306a36Sopenharmony_ci unsigned int offset, 47962306a36Sopenharmony_ci void *p_data) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct drm_i915_private *i915 = vgpu->gvt->gt->i915; 48262306a36Sopenharmony_ci struct intel_vgpu_i2c_edid *i2c_edid = &vgpu->display.i2c_edid; 48362306a36Sopenharmony_ci int msg_length, ret_msg_size; 48462306a36Sopenharmony_ci int msg, addr, ctrl, op; 48562306a36Sopenharmony_ci u32 value = *(u32 *)p_data; 48662306a36Sopenharmony_ci int aux_data_for_write = 0; 48762306a36Sopenharmony_ci int reg = get_aux_ch_reg(offset); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (reg != AUX_CH_CTL) { 49062306a36Sopenharmony_ci vgpu_vreg(vgpu, offset) = value; 49162306a36Sopenharmony_ci return; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci msg_length = REG_FIELD_GET(DP_AUX_CH_CTL_MESSAGE_SIZE_MASK, value); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci // check the msg in DATA register. 49762306a36Sopenharmony_ci msg = vgpu_vreg(vgpu, offset + 4); 49862306a36Sopenharmony_ci addr = (msg >> 8) & 0xffff; 49962306a36Sopenharmony_ci ctrl = (msg >> 24) & 0xff; 50062306a36Sopenharmony_ci op = ctrl >> 4; 50162306a36Sopenharmony_ci if (!(value & DP_AUX_CH_CTL_SEND_BUSY)) { 50262306a36Sopenharmony_ci /* The ctl write to clear some states */ 50362306a36Sopenharmony_ci return; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* Always set the wanted value for vms. */ 50762306a36Sopenharmony_ci ret_msg_size = (((op & 0x1) == GVT_AUX_I2C_READ) ? 2 : 1); 50862306a36Sopenharmony_ci vgpu_vreg(vgpu, offset) = 50962306a36Sopenharmony_ci DP_AUX_CH_CTL_DONE | 51062306a36Sopenharmony_ci DP_AUX_CH_CTL_MESSAGE_SIZE(ret_msg_size); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (msg_length == 3) { 51362306a36Sopenharmony_ci if (!(op & GVT_AUX_I2C_MOT)) { 51462306a36Sopenharmony_ci /* stop */ 51562306a36Sopenharmony_ci intel_vgpu_init_i2c_edid(vgpu); 51662306a36Sopenharmony_ci } else { 51762306a36Sopenharmony_ci /* start or restart */ 51862306a36Sopenharmony_ci i2c_edid->aux_ch.i2c_over_aux_ch = true; 51962306a36Sopenharmony_ci i2c_edid->aux_ch.aux_ch_mot = true; 52062306a36Sopenharmony_ci if (addr == 0) { 52162306a36Sopenharmony_ci /* reset the address */ 52262306a36Sopenharmony_ci intel_vgpu_init_i2c_edid(vgpu); 52362306a36Sopenharmony_ci } else if (addr == EDID_ADDR) { 52462306a36Sopenharmony_ci i2c_edid->state = I2C_AUX_CH; 52562306a36Sopenharmony_ci i2c_edid->port = port_idx; 52662306a36Sopenharmony_ci i2c_edid->slave_selected = true; 52762306a36Sopenharmony_ci if (intel_vgpu_has_monitor_on_port(vgpu, 52862306a36Sopenharmony_ci port_idx) && 52962306a36Sopenharmony_ci intel_vgpu_port_is_dp(vgpu, port_idx)) 53062306a36Sopenharmony_ci i2c_edid->edid_available = true; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci } else if ((op & 0x1) == GVT_AUX_I2C_WRITE) { 53462306a36Sopenharmony_ci /* TODO 53562306a36Sopenharmony_ci * We only support EDID reading from I2C_over_AUX. And 53662306a36Sopenharmony_ci * we do not expect the index mode to be used. Right now 53762306a36Sopenharmony_ci * the WRITE operation is ignored. It is good enough to 53862306a36Sopenharmony_ci * support the gfx driver to do EDID access. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci } else { 54162306a36Sopenharmony_ci if (drm_WARN_ON(&i915->drm, (op & 0x1) != GVT_AUX_I2C_READ)) 54262306a36Sopenharmony_ci return; 54362306a36Sopenharmony_ci if (drm_WARN_ON(&i915->drm, msg_length != 4)) 54462306a36Sopenharmony_ci return; 54562306a36Sopenharmony_ci if (i2c_edid->edid_available && i2c_edid->slave_selected) { 54662306a36Sopenharmony_ci unsigned char val = edid_get_byte(vgpu); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci aux_data_for_write = (val << 16); 54962306a36Sopenharmony_ci } else 55062306a36Sopenharmony_ci aux_data_for_write = (0xff << 16); 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci /* write the return value in AUX_CH_DATA reg which includes: 55362306a36Sopenharmony_ci * ACK of I2C_WRITE 55462306a36Sopenharmony_ci * returned byte if it is READ 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci aux_data_for_write |= GVT_AUX_I2C_REPLY_ACK << 24; 55762306a36Sopenharmony_ci vgpu_vreg(vgpu, offset + 4) = aux_data_for_write; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/** 56162306a36Sopenharmony_ci * intel_vgpu_init_i2c_edid - initialize vGPU i2c edid emulation 56262306a36Sopenharmony_ci * @vgpu: a vGPU 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci * This function is used to initialize vGPU i2c edid emulation stuffs 56562306a36Sopenharmony_ci * 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_civoid intel_vgpu_init_i2c_edid(struct intel_vgpu *vgpu) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct intel_vgpu_i2c_edid *edid = &vgpu->display.i2c_edid; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci edid->state = I2C_NOT_SPECIFIED; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci edid->port = -1; 57462306a36Sopenharmony_ci edid->slave_selected = false; 57562306a36Sopenharmony_ci edid->edid_available = false; 57662306a36Sopenharmony_ci edid->current_edid_read = 0; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci memset(&edid->gmbus, 0, sizeof(struct intel_vgpu_i2c_gmbus)); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci edid->aux_ch.i2c_over_aux_ch = false; 58162306a36Sopenharmony_ci edid->aux_ch.aux_ch_mot = false; 58262306a36Sopenharmony_ci} 583