| /* |
| * Copyright 2013 Google Inc. |
| * Copyright © 2008 Intel Corporation |
| * |
| * 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: |
| * Keith Packard <keithp@keithp.com> |
| * |
| */ |
| |
| #include <console/console.h> |
| #include <stdint.h> |
| #include <delay.h> |
| #include "i915io.h" |
| |
| u32 |
| pack_aux(u32 *src32, int src_bytes) |
| { |
| u8 *src = (u8 *)src32; |
| int i; |
| u32 v = 0; |
| |
| if (src_bytes > 4) |
| src_bytes = 4; |
| for (i = 0; i < src_bytes; i++) |
| v |= ((u32) src[i]) << ((3-i) * 8); |
| return v; |
| } |
| |
| void |
| unpack_aux(u32 src, u32 *dst32, int dst_bytes) |
| { |
| u8 *dst = (u8 *)dst32; |
| |
| int i; |
| if (dst_bytes > 4) |
| dst_bytes = 4; |
| for (i = 0; i < dst_bytes; i++) |
| dst[i] = src >> ((3-i) * 8); |
| } |
| |
| int |
| intel_dp_aux_ch(u32 ch_ctl, u32 ch_data, u32 *send, int send_bytes, |
| u32 *recv, int recv_size) |
| { |
| int i; |
| int recv_bytes; |
| u32 status; |
| u32 aux_clock_divider; |
| int try, precharge = 5; |
| |
| /* The clock divider is based off the hrawclk, |
| * and would like to run at 2MHz. So, take the |
| * hrawclk value and divide by 2 and use that |
| * |
| * Note that PCH attached eDP panels should use a 125MHz input |
| * clock divider. |
| */ |
| /* 200 on link */ |
| aux_clock_divider = 200; /* SNB & IVB eDP input clock at 400Mhz */ |
| |
| /* Try to wait for any previous AUX channel activity */ |
| for (try = 0; try < 3; try++) { |
| status = io_i915_READ32(ch_ctl); |
| if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) |
| break; |
| udelay(1000); |
| } |
| |
| if (try == 3) { |
| printk(BIOS_SPEW, "[000000.0] [drm:%s], ", __func__); |
| printk(BIOS_SPEW, "dp_aux_ch not started status 0x%08lx\n", |
| io_i915_READ32(ch_ctl)); |
| return -1; |
| } |
| |
| /* Must try at least 3 times according to DP spec */ |
| for (try = 0; try < 5; try++) { |
| /* Load the send data into the aux channel data registers */ |
| for (i = 0; i < send_bytes; i += 4) |
| io_i915_WRITE32(send[i], ch_data + i); |
| |
| /* Send the command and wait for it to complete */ |
| io_i915_WRITE32( |
| DP_AUX_CH_CTL_SEND_BUSY | |
| DP_AUX_CH_CTL_TIME_OUT_400us | |
| (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | |
| (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | |
| (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) | |
| DP_AUX_CH_CTL_DONE | |
| DP_AUX_CH_CTL_TIME_OUT_ERROR | |
| DP_AUX_CH_CTL_RECEIVE_ERROR, ch_ctl); |
| for (;;) { |
| status = io_i915_READ32(ch_ctl); |
| if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) |
| break; |
| udelay(100); |
| } |
| |
| /* Clear done status and any errors */ |
| io_i915_WRITE32( |
| status | |
| DP_AUX_CH_CTL_DONE | |
| DP_AUX_CH_CTL_TIME_OUT_ERROR | |
| DP_AUX_CH_CTL_RECEIVE_ERROR, ch_ctl); |
| |
| if (status & (DP_AUX_CH_CTL_TIME_OUT_ERROR | |
| DP_AUX_CH_CTL_RECEIVE_ERROR)) |
| continue; |
| if (status & DP_AUX_CH_CTL_DONE) |
| break; |
| } |
| |
| if ((status & DP_AUX_CH_CTL_DONE) == 0) { |
| printk(BIOS_SPEW, "[000000.0] [drm:%s], ", __func__); |
| printk(BIOS_SPEW, "dp_aux_ch not done status 0x%08x\n", status); |
| return -1; |
| } |
| |
| /* Check for timeout or receive error. |
| * Timeouts occur when the sink is not connected |
| */ |
| if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { |
| printk(BIOS_SPEW, "[000000.0] [drm:%s], ", __func__); |
| printk(BIOS_SPEW, "dp_aux_ch receive error status 0x%08x\n", status); |
| return -1; |
| } |
| |
| /* Timeouts occur when the device isn't connected, so they're |
| * "normal" -- don't fill the kernel log with these */ |
| if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) { |
| printk(BIOS_SPEW, "[000000.0] [drm:%s], ", __func__); |
| printk(BIOS_SPEW, "dp_aux_ch timeout status 0x%08x\n", status); |
| return -1; |
| } |
| |
| /* Unload any bytes sent back from the other side */ |
| recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >> |
| DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT); |
| if (recv_bytes > recv_size) |
| recv_bytes = recv_size; |
| |
| for (i = 0; i < recv_bytes; i += 4) |
| unpack_aux(io_i915_READ32(ch_data + i), |
| recv + i, recv_bytes - i); |
| |
| return recv_bytes; |
| } |