18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 138c2ecf20Sopenharmony_ci * Software. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 208c2ecf20Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 218c2ecf20Sopenharmony_ci * SOFTWARE. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Authors: 248c2ecf20Sopenharmony_ci * Ke Yu 258c2ecf20Sopenharmony_ci * Zhiyuan Lv <zhiyuan.lv@intel.com> 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Contributors: 288c2ecf20Sopenharmony_ci * Terrence Xu <terrence.xu@intel.com> 298c2ecf20Sopenharmony_ci * Changbin Du <changbin.du@intel.com> 308c2ecf20Sopenharmony_ci * Bing Niu <bing.niu@intel.com> 318c2ecf20Sopenharmony_ci * Zhi Wang <zhi.a.wang@intel.com> 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include "i915_drv.h" 368c2ecf20Sopenharmony_ci#include "gvt.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define GMBUS1_TOTAL_BYTES_SHIFT 16 398c2ecf20Sopenharmony_ci#define GMBUS1_TOTAL_BYTES_MASK 0x1ff 408c2ecf20Sopenharmony_ci#define gmbus1_total_byte_count(v) (((v) >> \ 418c2ecf20Sopenharmony_ci GMBUS1_TOTAL_BYTES_SHIFT) & GMBUS1_TOTAL_BYTES_MASK) 428c2ecf20Sopenharmony_ci#define gmbus1_slave_addr(v) (((v) & 0xff) >> 1) 438c2ecf20Sopenharmony_ci#define gmbus1_slave_index(v) (((v) >> 8) & 0xff) 448c2ecf20Sopenharmony_ci#define gmbus1_bus_cycle(v) (((v) >> 25) & 0x7) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* GMBUS0 bits definitions */ 478c2ecf20Sopenharmony_ci#define _GMBUS_PIN_SEL_MASK (0x7) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic unsigned char edid_get_byte(struct intel_vgpu *vgpu) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct intel_vgpu_i2c_edid *edid = &vgpu->display.i2c_edid; 528c2ecf20Sopenharmony_ci unsigned char chr = 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (edid->state == I2C_NOT_SPECIFIED || !edid->slave_selected) { 558c2ecf20Sopenharmony_ci gvt_vgpu_err("Driver tries to read EDID without proper sequence!\n"); 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci if (edid->current_edid_read >= EDID_SIZE) { 598c2ecf20Sopenharmony_ci gvt_vgpu_err("edid_get_byte() exceeds the size of EDID!\n"); 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (!edid->edid_available) { 648c2ecf20Sopenharmony_ci gvt_vgpu_err("Reading EDID but EDID is not available!\n"); 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (intel_vgpu_has_monitor_on_port(vgpu, edid->port)) { 698c2ecf20Sopenharmony_ci struct intel_vgpu_edid_data *edid_data = 708c2ecf20Sopenharmony_ci intel_vgpu_port(vgpu, edid->port)->edid; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci chr = edid_data->edid_block[edid->current_edid_read]; 738c2ecf20Sopenharmony_ci edid->current_edid_read++; 748c2ecf20Sopenharmony_ci } else { 758c2ecf20Sopenharmony_ci gvt_vgpu_err("No EDID available during the reading?\n"); 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci return chr; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic inline int cnp_get_port_from_gmbus0(u32 gmbus0) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; 838c2ecf20Sopenharmony_ci int port = -EINVAL; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (port_select == GMBUS_PIN_1_BXT) 868c2ecf20Sopenharmony_ci port = PORT_B; 878c2ecf20Sopenharmony_ci else if (port_select == GMBUS_PIN_2_BXT) 888c2ecf20Sopenharmony_ci port = PORT_C; 898c2ecf20Sopenharmony_ci else if (port_select == GMBUS_PIN_3_BXT) 908c2ecf20Sopenharmony_ci port = PORT_D; 918c2ecf20Sopenharmony_ci else if (port_select == GMBUS_PIN_4_CNP) 928c2ecf20Sopenharmony_ci port = PORT_E; 938c2ecf20Sopenharmony_ci return port; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic inline int bxt_get_port_from_gmbus0(u32 gmbus0) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; 998c2ecf20Sopenharmony_ci int port = -EINVAL; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (port_select == GMBUS_PIN_1_BXT) 1028c2ecf20Sopenharmony_ci port = PORT_B; 1038c2ecf20Sopenharmony_ci else if (port_select == GMBUS_PIN_2_BXT) 1048c2ecf20Sopenharmony_ci port = PORT_C; 1058c2ecf20Sopenharmony_ci else if (port_select == GMBUS_PIN_3_BXT) 1068c2ecf20Sopenharmony_ci port = PORT_D; 1078c2ecf20Sopenharmony_ci return port; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic inline int get_port_from_gmbus0(u32 gmbus0) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; 1138c2ecf20Sopenharmony_ci int port = -EINVAL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (port_select == GMBUS_PIN_VGADDC) 1168c2ecf20Sopenharmony_ci port = PORT_E; 1178c2ecf20Sopenharmony_ci else if (port_select == GMBUS_PIN_DPC) 1188c2ecf20Sopenharmony_ci port = PORT_C; 1198c2ecf20Sopenharmony_ci else if (port_select == GMBUS_PIN_DPB) 1208c2ecf20Sopenharmony_ci port = PORT_B; 1218c2ecf20Sopenharmony_ci else if (port_select == GMBUS_PIN_DPD) 1228c2ecf20Sopenharmony_ci port = PORT_D; 1238c2ecf20Sopenharmony_ci return port; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void reset_gmbus_controller(struct intel_vgpu *vgpu) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) = GMBUS_HW_RDY; 1298c2ecf20Sopenharmony_ci if (!vgpu->display.i2c_edid.edid_available) 1308c2ecf20Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_SATOER; 1318c2ecf20Sopenharmony_ci vgpu->display.i2c_edid.gmbus.phase = GMBUS_IDLE_PHASE; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* GMBUS0 */ 1358c2ecf20Sopenharmony_cistatic int gmbus0_mmio_write(struct intel_vgpu *vgpu, 1368c2ecf20Sopenharmony_ci unsigned int offset, void *p_data, unsigned int bytes) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = vgpu->gvt->gt->i915; 1398c2ecf20Sopenharmony_ci int port, pin_select; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci memcpy(&vgpu_vreg(vgpu, offset), p_data, bytes); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci pin_select = vgpu_vreg(vgpu, offset) & _GMBUS_PIN_SEL_MASK; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci intel_vgpu_init_i2c_edid(vgpu); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (pin_select == 0) 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (IS_BROXTON(i915)) 1518c2ecf20Sopenharmony_ci port = bxt_get_port_from_gmbus0(pin_select); 1528c2ecf20Sopenharmony_ci else if (IS_COFFEELAKE(i915) || IS_COMETLAKE(i915)) 1538c2ecf20Sopenharmony_ci port = cnp_get_port_from_gmbus0(pin_select); 1548c2ecf20Sopenharmony_ci else 1558c2ecf20Sopenharmony_ci port = get_port_from_gmbus0(pin_select); 1568c2ecf20Sopenharmony_ci if (drm_WARN_ON(&i915->drm, port < 0)) 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci vgpu->display.i2c_edid.state = I2C_GMBUS; 1608c2ecf20Sopenharmony_ci vgpu->display.i2c_edid.gmbus.phase = GMBUS_IDLE_PHASE; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) &= ~GMBUS_ACTIVE; 1638c2ecf20Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_HW_RDY | GMBUS_HW_WAIT_PHASE; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (intel_vgpu_has_monitor_on_port(vgpu, port) && 1668c2ecf20Sopenharmony_ci !intel_vgpu_port_is_dp(vgpu, port)) { 1678c2ecf20Sopenharmony_ci vgpu->display.i2c_edid.port = port; 1688c2ecf20Sopenharmony_ci vgpu->display.i2c_edid.edid_available = true; 1698c2ecf20Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) &= ~GMBUS_SATOER; 1708c2ecf20Sopenharmony_ci } else 1718c2ecf20Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_SATOER; 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int gmbus1_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, 1768c2ecf20Sopenharmony_ci void *p_data, unsigned int bytes) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct intel_vgpu_i2c_edid *i2c_edid = &vgpu->display.i2c_edid; 1798c2ecf20Sopenharmony_ci u32 slave_addr; 1808c2ecf20Sopenharmony_ci u32 wvalue = *(u32 *)p_data; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (vgpu_vreg(vgpu, offset) & GMBUS_SW_CLR_INT) { 1838c2ecf20Sopenharmony_ci if (!(wvalue & GMBUS_SW_CLR_INT)) { 1848c2ecf20Sopenharmony_ci vgpu_vreg(vgpu, offset) &= ~GMBUS_SW_CLR_INT; 1858c2ecf20Sopenharmony_ci reset_gmbus_controller(vgpu); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci /* 1888c2ecf20Sopenharmony_ci * TODO: "This bit is cleared to zero when an event 1898c2ecf20Sopenharmony_ci * causes the HW_RDY bit transition to occur " 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci } else { 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * per bspec setting this bit can cause: 1948c2ecf20Sopenharmony_ci * 1) INT status bit cleared 1958c2ecf20Sopenharmony_ci * 2) HW_RDY bit asserted 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci if (wvalue & GMBUS_SW_CLR_INT) { 1988c2ecf20Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) &= ~GMBUS_INT; 1998c2ecf20Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_HW_RDY; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* For virtualization, we suppose that HW is always ready, 2038c2ecf20Sopenharmony_ci * so GMBUS_SW_RDY should always be cleared 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci if (wvalue & GMBUS_SW_RDY) 2068c2ecf20Sopenharmony_ci wvalue &= ~GMBUS_SW_RDY; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci i2c_edid->gmbus.total_byte_count = 2098c2ecf20Sopenharmony_ci gmbus1_total_byte_count(wvalue); 2108c2ecf20Sopenharmony_ci slave_addr = gmbus1_slave_addr(wvalue); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* vgpu gmbus only support EDID */ 2138c2ecf20Sopenharmony_ci if (slave_addr == EDID_ADDR) { 2148c2ecf20Sopenharmony_ci i2c_edid->slave_selected = true; 2158c2ecf20Sopenharmony_ci } else if (slave_addr != 0) { 2168c2ecf20Sopenharmony_ci gvt_dbg_dpy( 2178c2ecf20Sopenharmony_ci "vgpu%d: unsupported gmbus slave addr(0x%x)\n" 2188c2ecf20Sopenharmony_ci " gmbus operations will be ignored.\n", 2198c2ecf20Sopenharmony_ci vgpu->id, slave_addr); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (wvalue & GMBUS_CYCLE_INDEX) 2238c2ecf20Sopenharmony_ci i2c_edid->current_edid_read = 2248c2ecf20Sopenharmony_ci gmbus1_slave_index(wvalue); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci i2c_edid->gmbus.cycle_type = gmbus1_bus_cycle(wvalue); 2278c2ecf20Sopenharmony_ci switch (gmbus1_bus_cycle(wvalue)) { 2288c2ecf20Sopenharmony_ci case GMBUS_NOCYCLE: 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci case GMBUS_STOP: 2318c2ecf20Sopenharmony_ci /* From spec: 2328c2ecf20Sopenharmony_ci * This can only cause a STOP to be generated 2338c2ecf20Sopenharmony_ci * if a GMBUS cycle is generated, the GMBUS is 2348c2ecf20Sopenharmony_ci * currently in a data/wait/idle phase, or it is in a 2358c2ecf20Sopenharmony_ci * WAIT phase 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_ci if (gmbus1_bus_cycle(vgpu_vreg(vgpu, offset)) 2388c2ecf20Sopenharmony_ci != GMBUS_NOCYCLE) { 2398c2ecf20Sopenharmony_ci intel_vgpu_init_i2c_edid(vgpu); 2408c2ecf20Sopenharmony_ci /* After the 'stop' cycle, hw state would become 2418c2ecf20Sopenharmony_ci * 'stop phase' and then 'idle phase' after a 2428c2ecf20Sopenharmony_ci * few milliseconds. In emulation, we just set 2438c2ecf20Sopenharmony_ci * it as 'idle phase' ('stop phase' is not 2448c2ecf20Sopenharmony_ci * visible in gmbus interface) 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci i2c_edid->gmbus.phase = GMBUS_IDLE_PHASE; 2478c2ecf20Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) &= ~GMBUS_ACTIVE; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci case NIDX_NS_W: 2518c2ecf20Sopenharmony_ci case IDX_NS_W: 2528c2ecf20Sopenharmony_ci case NIDX_STOP: 2538c2ecf20Sopenharmony_ci case IDX_STOP: 2548c2ecf20Sopenharmony_ci /* From hw spec the GMBUS phase 2558c2ecf20Sopenharmony_ci * transition like this: 2568c2ecf20Sopenharmony_ci * START (-->INDEX) -->DATA 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci i2c_edid->gmbus.phase = GMBUS_DATA_PHASE; 2598c2ecf20Sopenharmony_ci vgpu_vreg_t(vgpu, PCH_GMBUS2) |= GMBUS_ACTIVE; 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci default: 2628c2ecf20Sopenharmony_ci gvt_vgpu_err("Unknown/reserved GMBUS cycle detected!\n"); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci /* 2668c2ecf20Sopenharmony_ci * From hw spec the WAIT state will be 2678c2ecf20Sopenharmony_ci * cleared: 2688c2ecf20Sopenharmony_ci * (1) in a new GMBUS cycle 2698c2ecf20Sopenharmony_ci * (2) by generating a stop 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci vgpu_vreg(vgpu, offset) = wvalue; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int gmbus3_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, 2778c2ecf20Sopenharmony_ci void *p_data, unsigned int bytes) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = vgpu->gvt->gt->i915; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci drm_WARN_ON(&i915->drm, 1); 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int gmbus3_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, 2868c2ecf20Sopenharmony_ci void *p_data, unsigned int bytes) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci int i; 2898c2ecf20Sopenharmony_ci unsigned char byte_data; 2908c2ecf20Sopenharmony_ci struct intel_vgpu_i2c_edid *i2c_edid = &vgpu->display.i2c_edid; 2918c2ecf20Sopenharmony_ci int byte_left = i2c_edid->gmbus.total_byte_count - 2928c2ecf20Sopenharmony_ci i2c_edid->current_edid_read; 2938c2ecf20Sopenharmony_ci int byte_count = byte_left; 2948c2ecf20Sopenharmony_ci u32 reg_data = 0; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Data can only be recevied if previous settings correct */ 2978c2ecf20Sopenharmony_ci if (vgpu_vreg_t(vgpu, PCH_GMBUS1) & GMBUS_SLAVE_READ) { 2988c2ecf20Sopenharmony_ci if (byte_left <= 0) { 2998c2ecf20Sopenharmony_ci memcpy(p_data, &vgpu_vreg(vgpu, offset), bytes); 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (byte_count > 4) 3048c2ecf20Sopenharmony_ci byte_count = 4; 3058c2ecf20Sopenharmony_ci for (i = 0; i < byte_count; i++) { 3068c2ecf20Sopenharmony_ci byte_data = edid_get_byte(vgpu); 3078c2ecf20Sopenharmony_ci reg_data |= (byte_data << (i << 3)); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci memcpy(&vgpu_vreg(vgpu, offset), ®_data, byte_count); 3118c2ecf20Sopenharmony_ci memcpy(p_data, &vgpu_vreg(vgpu, offset), bytes); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (byte_left <= 4) { 3148c2ecf20Sopenharmony_ci switch (i2c_edid->gmbus.cycle_type) { 3158c2ecf20Sopenharmony_ci case NIDX_STOP: 3168c2ecf20Sopenharmony_ci case IDX_STOP: 3178c2ecf20Sopenharmony_ci i2c_edid->gmbus.phase = GMBUS_IDLE_PHASE; 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci case NIDX_NS_W: 3208c2ecf20Sopenharmony_ci case IDX_NS_W: 3218c2ecf20Sopenharmony_ci default: 3228c2ecf20Sopenharmony_ci i2c_edid->gmbus.phase = GMBUS_WAIT_PHASE; 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci intel_vgpu_init_i2c_edid(vgpu); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * Read GMBUS3 during send operation, 3298c2ecf20Sopenharmony_ci * return the latest written value 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci } else { 3328c2ecf20Sopenharmony_ci memcpy(p_data, &vgpu_vreg(vgpu, offset), bytes); 3338c2ecf20Sopenharmony_ci gvt_vgpu_err("warning: gmbus3 read with nothing returned\n"); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int gmbus2_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, 3398c2ecf20Sopenharmony_ci void *p_data, unsigned int bytes) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci u32 value = vgpu_vreg(vgpu, offset); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (!(vgpu_vreg(vgpu, offset) & GMBUS_INUSE)) 3448c2ecf20Sopenharmony_ci vgpu_vreg(vgpu, offset) |= GMBUS_INUSE; 3458c2ecf20Sopenharmony_ci memcpy(p_data, (void *)&value, bytes); 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int gmbus2_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, 3508c2ecf20Sopenharmony_ci void *p_data, unsigned int bytes) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci u32 wvalue = *(u32 *)p_data; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (wvalue & GMBUS_INUSE) 3558c2ecf20Sopenharmony_ci vgpu_vreg(vgpu, offset) &= ~GMBUS_INUSE; 3568c2ecf20Sopenharmony_ci /* All other bits are read-only */ 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci/** 3618c2ecf20Sopenharmony_ci * intel_gvt_i2c_handle_gmbus_read - emulate gmbus register mmio read 3628c2ecf20Sopenharmony_ci * @vgpu: a vGPU 3638c2ecf20Sopenharmony_ci * @offset: reg offset 3648c2ecf20Sopenharmony_ci * @p_data: data return buffer 3658c2ecf20Sopenharmony_ci * @bytes: access data length 3668c2ecf20Sopenharmony_ci * 3678c2ecf20Sopenharmony_ci * This function is used to emulate gmbus register mmio read 3688c2ecf20Sopenharmony_ci * 3698c2ecf20Sopenharmony_ci * Returns: 3708c2ecf20Sopenharmony_ci * Zero on success, negative error code if failed. 3718c2ecf20Sopenharmony_ci * 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_ciint intel_gvt_i2c_handle_gmbus_read(struct intel_vgpu *vgpu, 3748c2ecf20Sopenharmony_ci unsigned int offset, void *p_data, unsigned int bytes) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = vgpu->gvt->gt->i915; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (drm_WARN_ON(&i915->drm, bytes > 8 && (offset & (bytes - 1)))) 3798c2ecf20Sopenharmony_ci return -EINVAL; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (offset == i915_mmio_reg_offset(PCH_GMBUS2)) 3828c2ecf20Sopenharmony_ci return gmbus2_mmio_read(vgpu, offset, p_data, bytes); 3838c2ecf20Sopenharmony_ci else if (offset == i915_mmio_reg_offset(PCH_GMBUS3)) 3848c2ecf20Sopenharmony_ci return gmbus3_mmio_read(vgpu, offset, p_data, bytes); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci memcpy(p_data, &vgpu_vreg(vgpu, offset), bytes); 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci/** 3918c2ecf20Sopenharmony_ci * intel_gvt_i2c_handle_gmbus_write - emulate gmbus register mmio write 3928c2ecf20Sopenharmony_ci * @vgpu: a vGPU 3938c2ecf20Sopenharmony_ci * @offset: reg offset 3948c2ecf20Sopenharmony_ci * @p_data: data return buffer 3958c2ecf20Sopenharmony_ci * @bytes: access data length 3968c2ecf20Sopenharmony_ci * 3978c2ecf20Sopenharmony_ci * This function is used to emulate gmbus register mmio write 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * Returns: 4008c2ecf20Sopenharmony_ci * Zero on success, negative error code if failed. 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ciint intel_gvt_i2c_handle_gmbus_write(struct intel_vgpu *vgpu, 4048c2ecf20Sopenharmony_ci unsigned int offset, void *p_data, unsigned int bytes) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = vgpu->gvt->gt->i915; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (drm_WARN_ON(&i915->drm, bytes > 8 && (offset & (bytes - 1)))) 4098c2ecf20Sopenharmony_ci return -EINVAL; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (offset == i915_mmio_reg_offset(PCH_GMBUS0)) 4128c2ecf20Sopenharmony_ci return gmbus0_mmio_write(vgpu, offset, p_data, bytes); 4138c2ecf20Sopenharmony_ci else if (offset == i915_mmio_reg_offset(PCH_GMBUS1)) 4148c2ecf20Sopenharmony_ci return gmbus1_mmio_write(vgpu, offset, p_data, bytes); 4158c2ecf20Sopenharmony_ci else if (offset == i915_mmio_reg_offset(PCH_GMBUS2)) 4168c2ecf20Sopenharmony_ci return gmbus2_mmio_write(vgpu, offset, p_data, bytes); 4178c2ecf20Sopenharmony_ci else if (offset == i915_mmio_reg_offset(PCH_GMBUS3)) 4188c2ecf20Sopenharmony_ci return gmbus3_mmio_write(vgpu, offset, p_data, bytes); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci memcpy(&vgpu_vreg(vgpu, offset), p_data, bytes); 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cienum { 4258c2ecf20Sopenharmony_ci AUX_CH_CTL = 0, 4268c2ecf20Sopenharmony_ci AUX_CH_DATA1, 4278c2ecf20Sopenharmony_ci AUX_CH_DATA2, 4288c2ecf20Sopenharmony_ci AUX_CH_DATA3, 4298c2ecf20Sopenharmony_ci AUX_CH_DATA4, 4308c2ecf20Sopenharmony_ci AUX_CH_DATA5 4318c2ecf20Sopenharmony_ci}; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic inline int get_aux_ch_reg(unsigned int offset) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci int reg; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci switch (offset & 0xff) { 4388c2ecf20Sopenharmony_ci case 0x10: 4398c2ecf20Sopenharmony_ci reg = AUX_CH_CTL; 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case 0x14: 4428c2ecf20Sopenharmony_ci reg = AUX_CH_DATA1; 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci case 0x18: 4458c2ecf20Sopenharmony_ci reg = AUX_CH_DATA2; 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci case 0x1c: 4488c2ecf20Sopenharmony_ci reg = AUX_CH_DATA3; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case 0x20: 4518c2ecf20Sopenharmony_ci reg = AUX_CH_DATA4; 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci case 0x24: 4548c2ecf20Sopenharmony_ci reg = AUX_CH_DATA5; 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci default: 4578c2ecf20Sopenharmony_ci reg = -1; 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci return reg; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci#define AUX_CTL_MSG_LENGTH(reg) \ 4648c2ecf20Sopenharmony_ci ((reg & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >> \ 4658c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci/** 4688c2ecf20Sopenharmony_ci * intel_gvt_i2c_handle_aux_ch_write - emulate AUX channel register write 4698c2ecf20Sopenharmony_ci * @vgpu: a vGPU 4708c2ecf20Sopenharmony_ci * @port_idx: port index 4718c2ecf20Sopenharmony_ci * @offset: reg offset 4728c2ecf20Sopenharmony_ci * @p_data: write ptr 4738c2ecf20Sopenharmony_ci * 4748c2ecf20Sopenharmony_ci * This function is used to emulate AUX channel register write 4758c2ecf20Sopenharmony_ci * 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_civoid intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, 4788c2ecf20Sopenharmony_ci int port_idx, 4798c2ecf20Sopenharmony_ci unsigned int offset, 4808c2ecf20Sopenharmony_ci void *p_data) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = vgpu->gvt->gt->i915; 4838c2ecf20Sopenharmony_ci struct intel_vgpu_i2c_edid *i2c_edid = &vgpu->display.i2c_edid; 4848c2ecf20Sopenharmony_ci int msg_length, ret_msg_size; 4858c2ecf20Sopenharmony_ci int msg, addr, ctrl, op; 4868c2ecf20Sopenharmony_ci u32 value = *(u32 *)p_data; 4878c2ecf20Sopenharmony_ci int aux_data_for_write = 0; 4888c2ecf20Sopenharmony_ci int reg = get_aux_ch_reg(offset); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (reg != AUX_CH_CTL) { 4918c2ecf20Sopenharmony_ci vgpu_vreg(vgpu, offset) = value; 4928c2ecf20Sopenharmony_ci return; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci msg_length = AUX_CTL_MSG_LENGTH(value); 4968c2ecf20Sopenharmony_ci // check the msg in DATA register. 4978c2ecf20Sopenharmony_ci msg = vgpu_vreg(vgpu, offset + 4); 4988c2ecf20Sopenharmony_ci addr = (msg >> 8) & 0xffff; 4998c2ecf20Sopenharmony_ci ctrl = (msg >> 24) & 0xff; 5008c2ecf20Sopenharmony_ci op = ctrl >> 4; 5018c2ecf20Sopenharmony_ci if (!(value & DP_AUX_CH_CTL_SEND_BUSY)) { 5028c2ecf20Sopenharmony_ci /* The ctl write to clear some states */ 5038c2ecf20Sopenharmony_ci return; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* Always set the wanted value for vms. */ 5078c2ecf20Sopenharmony_ci ret_msg_size = (((op & 0x1) == GVT_AUX_I2C_READ) ? 2 : 1); 5088c2ecf20Sopenharmony_ci vgpu_vreg(vgpu, offset) = 5098c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_DONE | 5108c2ecf20Sopenharmony_ci ((ret_msg_size << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) & 5118c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_MESSAGE_SIZE_MASK); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (msg_length == 3) { 5148c2ecf20Sopenharmony_ci if (!(op & GVT_AUX_I2C_MOT)) { 5158c2ecf20Sopenharmony_ci /* stop */ 5168c2ecf20Sopenharmony_ci intel_vgpu_init_i2c_edid(vgpu); 5178c2ecf20Sopenharmony_ci } else { 5188c2ecf20Sopenharmony_ci /* start or restart */ 5198c2ecf20Sopenharmony_ci i2c_edid->aux_ch.i2c_over_aux_ch = true; 5208c2ecf20Sopenharmony_ci i2c_edid->aux_ch.aux_ch_mot = true; 5218c2ecf20Sopenharmony_ci if (addr == 0) { 5228c2ecf20Sopenharmony_ci /* reset the address */ 5238c2ecf20Sopenharmony_ci intel_vgpu_init_i2c_edid(vgpu); 5248c2ecf20Sopenharmony_ci } else if (addr == EDID_ADDR) { 5258c2ecf20Sopenharmony_ci i2c_edid->state = I2C_AUX_CH; 5268c2ecf20Sopenharmony_ci i2c_edid->port = port_idx; 5278c2ecf20Sopenharmony_ci i2c_edid->slave_selected = true; 5288c2ecf20Sopenharmony_ci if (intel_vgpu_has_monitor_on_port(vgpu, 5298c2ecf20Sopenharmony_ci port_idx) && 5308c2ecf20Sopenharmony_ci intel_vgpu_port_is_dp(vgpu, port_idx)) 5318c2ecf20Sopenharmony_ci i2c_edid->edid_available = true; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci } else if ((op & 0x1) == GVT_AUX_I2C_WRITE) { 5358c2ecf20Sopenharmony_ci /* TODO 5368c2ecf20Sopenharmony_ci * We only support EDID reading from I2C_over_AUX. And 5378c2ecf20Sopenharmony_ci * we do not expect the index mode to be used. Right now 5388c2ecf20Sopenharmony_ci * the WRITE operation is ignored. It is good enough to 5398c2ecf20Sopenharmony_ci * support the gfx driver to do EDID access. 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci } else { 5428c2ecf20Sopenharmony_ci if (drm_WARN_ON(&i915->drm, (op & 0x1) != GVT_AUX_I2C_READ)) 5438c2ecf20Sopenharmony_ci return; 5448c2ecf20Sopenharmony_ci if (drm_WARN_ON(&i915->drm, msg_length != 4)) 5458c2ecf20Sopenharmony_ci return; 5468c2ecf20Sopenharmony_ci if (i2c_edid->edid_available && i2c_edid->slave_selected) { 5478c2ecf20Sopenharmony_ci unsigned char val = edid_get_byte(vgpu); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci aux_data_for_write = (val << 16); 5508c2ecf20Sopenharmony_ci } else 5518c2ecf20Sopenharmony_ci aux_data_for_write = (0xff << 16); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci /* write the return value in AUX_CH_DATA reg which includes: 5548c2ecf20Sopenharmony_ci * ACK of I2C_WRITE 5558c2ecf20Sopenharmony_ci * returned byte if it is READ 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_ci aux_data_for_write |= GVT_AUX_I2C_REPLY_ACK << 24; 5588c2ecf20Sopenharmony_ci vgpu_vreg(vgpu, offset + 4) = aux_data_for_write; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/** 5628c2ecf20Sopenharmony_ci * intel_vgpu_init_i2c_edid - initialize vGPU i2c edid emulation 5638c2ecf20Sopenharmony_ci * @vgpu: a vGPU 5648c2ecf20Sopenharmony_ci * 5658c2ecf20Sopenharmony_ci * This function is used to initialize vGPU i2c edid emulation stuffs 5668c2ecf20Sopenharmony_ci * 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_civoid intel_vgpu_init_i2c_edid(struct intel_vgpu *vgpu) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct intel_vgpu_i2c_edid *edid = &vgpu->display.i2c_edid; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci edid->state = I2C_NOT_SPECIFIED; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci edid->port = -1; 5758c2ecf20Sopenharmony_ci edid->slave_selected = false; 5768c2ecf20Sopenharmony_ci edid->edid_available = false; 5778c2ecf20Sopenharmony_ci edid->current_edid_read = 0; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci memset(&edid->gmbus, 0, sizeof(struct intel_vgpu_i2c_gmbus)); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci edid->aux_ch.i2c_over_aux_ch = false; 5828c2ecf20Sopenharmony_ci edid->aux_ch.aux_ch_mot = false; 5838c2ecf20Sopenharmony_ci} 584