18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright © 2012 Intel Corporation 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 208c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 218c2ecf20Sopenharmony_ci * IN THE SOFTWARE. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Authors: 248c2ecf20Sopenharmony_ci * Keith Packard <keithp@keithp.com> 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/i2c.h> 298c2ecf20Sopenharmony_ci#include <linux/module.h> 308c2ecf20Sopenharmony_ci#include <linux/slab.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 338c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 348c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h> 358c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "gma_display.h" 388c2ecf20Sopenharmony_ci#include "psb_drv.h" 398c2ecf20Sopenharmony_ci#include "psb_intel_drv.h" 408c2ecf20Sopenharmony_ci#include "psb_intel_reg.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/** 438c2ecf20Sopenharmony_ci * struct i2c_algo_dp_aux_data - driver interface structure for i2c over dp 448c2ecf20Sopenharmony_ci * aux algorithm 458c2ecf20Sopenharmony_ci * @running: set by the algo indicating whether an i2c is ongoing or whether 468c2ecf20Sopenharmony_ci * the i2c bus is quiescent 478c2ecf20Sopenharmony_ci * @address: i2c target address for the currently ongoing transfer 488c2ecf20Sopenharmony_ci * @aux_ch: driver callback to transfer a single byte of the i2c payload 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistruct i2c_algo_dp_aux_data { 518c2ecf20Sopenharmony_ci bool running; 528c2ecf20Sopenharmony_ci u16 address; 538c2ecf20Sopenharmony_ci int (*aux_ch) (struct i2c_adapter *adapter, 548c2ecf20Sopenharmony_ci int mode, uint8_t write_byte, 558c2ecf20Sopenharmony_ci uint8_t *read_byte); 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ 598c2ecf20Sopenharmony_cistatic int 608c2ecf20Sopenharmony_cii2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, 618c2ecf20Sopenharmony_ci uint8_t write_byte, uint8_t *read_byte) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; 648c2ecf20Sopenharmony_ci int ret; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci ret = (*algo_data->aux_ch)(adapter, mode, 678c2ecf20Sopenharmony_ci write_byte, read_byte); 688c2ecf20Sopenharmony_ci return ret; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * I2C over AUX CH 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * Send the address. If the I2C link is running, this 'restarts' 778c2ecf20Sopenharmony_ci * the connection with the new address, this is used for doing 788c2ecf20Sopenharmony_ci * a write followed by a read (as needed for DDC) 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistatic int 818c2ecf20Sopenharmony_cii2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; 848c2ecf20Sopenharmony_ci int mode = MODE_I2C_START; 858c2ecf20Sopenharmony_ci int ret; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (reading) 888c2ecf20Sopenharmony_ci mode |= MODE_I2C_READ; 898c2ecf20Sopenharmony_ci else 908c2ecf20Sopenharmony_ci mode |= MODE_I2C_WRITE; 918c2ecf20Sopenharmony_ci algo_data->address = address; 928c2ecf20Sopenharmony_ci algo_data->running = true; 938c2ecf20Sopenharmony_ci ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); 948c2ecf20Sopenharmony_ci return ret; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* 988c2ecf20Sopenharmony_ci * Stop the I2C transaction. This closes out the link, sending 998c2ecf20Sopenharmony_ci * a bare address packet with the MOT bit turned off 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistatic void 1028c2ecf20Sopenharmony_cii2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; 1058c2ecf20Sopenharmony_ci int mode = MODE_I2C_STOP; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (reading) 1088c2ecf20Sopenharmony_ci mode |= MODE_I2C_READ; 1098c2ecf20Sopenharmony_ci else 1108c2ecf20Sopenharmony_ci mode |= MODE_I2C_WRITE; 1118c2ecf20Sopenharmony_ci if (algo_data->running) { 1128c2ecf20Sopenharmony_ci (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); 1138c2ecf20Sopenharmony_ci algo_data->running = false; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* 1188c2ecf20Sopenharmony_ci * Write a single byte to the current I2C address, the 1198c2ecf20Sopenharmony_ci * the I2C link must be running or this returns -EIO 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistatic int 1228c2ecf20Sopenharmony_cii2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; 1258c2ecf20Sopenharmony_ci int ret; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (!algo_data->running) 1288c2ecf20Sopenharmony_ci return -EIO; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL); 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* 1358c2ecf20Sopenharmony_ci * Read a single byte from the current I2C address, the 1368c2ecf20Sopenharmony_ci * I2C link must be running or this returns -EIO 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic int 1398c2ecf20Sopenharmony_cii2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; 1428c2ecf20Sopenharmony_ci int ret; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!algo_data->running) 1458c2ecf20Sopenharmony_ci return -EIO; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret); 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int 1528c2ecf20Sopenharmony_cii2c_algo_dp_aux_xfer(struct i2c_adapter *adapter, 1538c2ecf20Sopenharmony_ci struct i2c_msg *msgs, 1548c2ecf20Sopenharmony_ci int num) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci int ret = 0; 1578c2ecf20Sopenharmony_ci bool reading = false; 1588c2ecf20Sopenharmony_ci int m; 1598c2ecf20Sopenharmony_ci int b; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci for (m = 0; m < num; m++) { 1628c2ecf20Sopenharmony_ci u16 len = msgs[m].len; 1638c2ecf20Sopenharmony_ci u8 *buf = msgs[m].buf; 1648c2ecf20Sopenharmony_ci reading = (msgs[m].flags & I2C_M_RD) != 0; 1658c2ecf20Sopenharmony_ci ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading); 1668c2ecf20Sopenharmony_ci if (ret < 0) 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci if (reading) { 1698c2ecf20Sopenharmony_ci for (b = 0; b < len; b++) { 1708c2ecf20Sopenharmony_ci ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]); 1718c2ecf20Sopenharmony_ci if (ret < 0) 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci } else { 1758c2ecf20Sopenharmony_ci for (b = 0; b < len; b++) { 1768c2ecf20Sopenharmony_ci ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]); 1778c2ecf20Sopenharmony_ci if (ret < 0) 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci if (ret < 0) 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci if (ret >= 0) 1858c2ecf20Sopenharmony_ci ret = num; 1868c2ecf20Sopenharmony_ci i2c_algo_dp_aux_stop(adapter, reading); 1878c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret); 1888c2ecf20Sopenharmony_ci return ret; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic u32 1928c2ecf20Sopenharmony_cii2c_algo_dp_aux_functionality(struct i2c_adapter *adapter) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | 1958c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_BLOCK_DATA | 1968c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BLOCK_PROC_CALL | 1978c2ecf20Sopenharmony_ci I2C_FUNC_10BIT_ADDR; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic const struct i2c_algorithm i2c_dp_aux_algo = { 2018c2ecf20Sopenharmony_ci .master_xfer = i2c_algo_dp_aux_xfer, 2028c2ecf20Sopenharmony_ci .functionality = i2c_algo_dp_aux_functionality, 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void 2068c2ecf20Sopenharmony_cii2c_dp_aux_reset_bus(struct i2c_adapter *adapter) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci (void) i2c_algo_dp_aux_address(adapter, 0, false); 2098c2ecf20Sopenharmony_ci (void) i2c_algo_dp_aux_stop(adapter, false); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int 2138c2ecf20Sopenharmony_cii2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci adapter->algo = &i2c_dp_aux_algo; 2168c2ecf20Sopenharmony_ci adapter->retries = 3; 2178c2ecf20Sopenharmony_ci i2c_dp_aux_reset_bus(adapter); 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* 2228c2ecf20Sopenharmony_ci * FIXME: This is the old dp aux helper, gma500 is the last driver that needs to 2238c2ecf20Sopenharmony_ci * be ported over to the new helper code in drm_dp_helper.c like i915 or radeon. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_cistatic int 2268c2ecf20Sopenharmony_cii2c_dp_aux_add_bus(struct i2c_adapter *adapter) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int error; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci error = i2c_dp_aux_prepare_bus(adapter); 2318c2ecf20Sopenharmony_ci if (error) 2328c2ecf20Sopenharmony_ci return error; 2338c2ecf20Sopenharmony_ci error = i2c_add_adapter(adapter); 2348c2ecf20Sopenharmony_ci return error; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci#define _wait_for(COND, MS, W) ({ \ 2388c2ecf20Sopenharmony_ci unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ 2398c2ecf20Sopenharmony_ci int ret__ = 0; \ 2408c2ecf20Sopenharmony_ci while (! (COND)) { \ 2418c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout__)) { \ 2428c2ecf20Sopenharmony_ci ret__ = -ETIMEDOUT; \ 2438c2ecf20Sopenharmony_ci break; \ 2448c2ecf20Sopenharmony_ci } \ 2458c2ecf20Sopenharmony_ci if (W && !in_dbg_master()) msleep(W); \ 2468c2ecf20Sopenharmony_ci } \ 2478c2ecf20Sopenharmony_ci ret__; \ 2488c2ecf20Sopenharmony_ci}) 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci#define wait_for(COND, MS) _wait_for(COND, MS, 1) 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci#define DP_LINK_CHECK_TIMEOUT (10 * 1000) 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci#define DP_LINK_CONFIGURATION_SIZE 9 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci#define CDV_FAST_LINK_TRAIN 1 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistruct cdv_intel_dp { 2598c2ecf20Sopenharmony_ci uint32_t output_reg; 2608c2ecf20Sopenharmony_ci uint32_t DP; 2618c2ecf20Sopenharmony_ci uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]; 2628c2ecf20Sopenharmony_ci bool has_audio; 2638c2ecf20Sopenharmony_ci int force_audio; 2648c2ecf20Sopenharmony_ci uint32_t color_range; 2658c2ecf20Sopenharmony_ci uint8_t link_bw; 2668c2ecf20Sopenharmony_ci uint8_t lane_count; 2678c2ecf20Sopenharmony_ci uint8_t dpcd[4]; 2688c2ecf20Sopenharmony_ci struct gma_encoder *encoder; 2698c2ecf20Sopenharmony_ci struct i2c_adapter adapter; 2708c2ecf20Sopenharmony_ci struct i2c_algo_dp_aux_data algo; 2718c2ecf20Sopenharmony_ci uint8_t train_set[4]; 2728c2ecf20Sopenharmony_ci uint8_t link_status[DP_LINK_STATUS_SIZE]; 2738c2ecf20Sopenharmony_ci int panel_power_up_delay; 2748c2ecf20Sopenharmony_ci int panel_power_down_delay; 2758c2ecf20Sopenharmony_ci int panel_power_cycle_delay; 2768c2ecf20Sopenharmony_ci int backlight_on_delay; 2778c2ecf20Sopenharmony_ci int backlight_off_delay; 2788c2ecf20Sopenharmony_ci struct drm_display_mode *panel_fixed_mode; /* for eDP */ 2798c2ecf20Sopenharmony_ci bool panel_on; 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistruct ddi_regoff { 2838c2ecf20Sopenharmony_ci uint32_t PreEmph1; 2848c2ecf20Sopenharmony_ci uint32_t PreEmph2; 2858c2ecf20Sopenharmony_ci uint32_t VSwing1; 2868c2ecf20Sopenharmony_ci uint32_t VSwing2; 2878c2ecf20Sopenharmony_ci uint32_t VSwing3; 2888c2ecf20Sopenharmony_ci uint32_t VSwing4; 2898c2ecf20Sopenharmony_ci uint32_t VSwing5; 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic struct ddi_regoff ddi_DP_train_table[] = { 2938c2ecf20Sopenharmony_ci {.PreEmph1 = 0x812c, .PreEmph2 = 0x8124, .VSwing1 = 0x8154, 2948c2ecf20Sopenharmony_ci .VSwing2 = 0x8148, .VSwing3 = 0x814C, .VSwing4 = 0x8150, 2958c2ecf20Sopenharmony_ci .VSwing5 = 0x8158,}, 2968c2ecf20Sopenharmony_ci {.PreEmph1 = 0x822c, .PreEmph2 = 0x8224, .VSwing1 = 0x8254, 2978c2ecf20Sopenharmony_ci .VSwing2 = 0x8248, .VSwing3 = 0x824C, .VSwing4 = 0x8250, 2988c2ecf20Sopenharmony_ci .VSwing5 = 0x8258,}, 2998c2ecf20Sopenharmony_ci}; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic uint32_t dp_vswing_premph_table[] = { 3028c2ecf20Sopenharmony_ci 0x55338954, 0x4000, 3038c2ecf20Sopenharmony_ci 0x554d8954, 0x2000, 3048c2ecf20Sopenharmony_ci 0x55668954, 0, 3058c2ecf20Sopenharmony_ci 0x559ac0d4, 0x6000, 3068c2ecf20Sopenharmony_ci}; 3078c2ecf20Sopenharmony_ci/** 3088c2ecf20Sopenharmony_ci * is_edp - is the given port attached to an eDP panel (either CPU or PCH) 3098c2ecf20Sopenharmony_ci * @intel_dp: DP struct 3108c2ecf20Sopenharmony_ci * 3118c2ecf20Sopenharmony_ci * If a CPU or PCH DP output is attached to an eDP panel, this function 3128c2ecf20Sopenharmony_ci * will return true, and false otherwise. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_cistatic bool is_edp(struct gma_encoder *encoder) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci return encoder->type == INTEL_OUTPUT_EDP; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic void cdv_intel_dp_start_link_train(struct gma_encoder *encoder); 3218c2ecf20Sopenharmony_cistatic void cdv_intel_dp_complete_link_train(struct gma_encoder *encoder); 3228c2ecf20Sopenharmony_cistatic void cdv_intel_dp_link_down(struct gma_encoder *encoder); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int 3258c2ecf20Sopenharmony_cicdv_intel_dp_max_lane_count(struct gma_encoder *encoder) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 3288c2ecf20Sopenharmony_ci int max_lane_count = 4; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) { 3318c2ecf20Sopenharmony_ci max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f; 3328c2ecf20Sopenharmony_ci switch (max_lane_count) { 3338c2ecf20Sopenharmony_ci case 1: case 2: case 4: 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci default: 3368c2ecf20Sopenharmony_ci max_lane_count = 4; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci return max_lane_count; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int 3438c2ecf20Sopenharmony_cicdv_intel_dp_max_link_bw(struct gma_encoder *encoder) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 3468c2ecf20Sopenharmony_ci int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE]; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci switch (max_link_bw) { 3498c2ecf20Sopenharmony_ci case DP_LINK_BW_1_62: 3508c2ecf20Sopenharmony_ci case DP_LINK_BW_2_7: 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci default: 3538c2ecf20Sopenharmony_ci max_link_bw = DP_LINK_BW_1_62; 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci return max_link_bw; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int 3608c2ecf20Sopenharmony_cicdv_intel_dp_link_clock(uint8_t link_bw) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci if (link_bw == DP_LINK_BW_2_7) 3638c2ecf20Sopenharmony_ci return 270000; 3648c2ecf20Sopenharmony_ci else 3658c2ecf20Sopenharmony_ci return 162000; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int 3698c2ecf20Sopenharmony_cicdv_intel_dp_link_required(int pixel_clock, int bpp) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci return (pixel_clock * bpp + 7) / 8; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int 3758c2ecf20Sopenharmony_cicdv_intel_dp_max_data_rate(int max_link_clock, int max_lanes) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci return (max_link_clock * max_lanes * 19) / 20; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic void cdv_intel_edp_panel_vdd_on(struct gma_encoder *intel_encoder) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct drm_device *dev = intel_encoder->base.dev; 3838c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; 3848c2ecf20Sopenharmony_ci u32 pp; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (intel_dp->panel_on) { 3878c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Skip VDD on because of panel on\n"); 3888c2ecf20Sopenharmony_ci return; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci pp = REG_READ(PP_CONTROL); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci pp |= EDP_FORCE_VDD; 3958c2ecf20Sopenharmony_ci REG_WRITE(PP_CONTROL, pp); 3968c2ecf20Sopenharmony_ci REG_READ(PP_CONTROL); 3978c2ecf20Sopenharmony_ci msleep(intel_dp->panel_power_up_delay); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic void cdv_intel_edp_panel_vdd_off(struct gma_encoder *intel_encoder) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct drm_device *dev = intel_encoder->base.dev; 4038c2ecf20Sopenharmony_ci u32 pp; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 4068c2ecf20Sopenharmony_ci pp = REG_READ(PP_CONTROL); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci pp &= ~EDP_FORCE_VDD; 4098c2ecf20Sopenharmony_ci REG_WRITE(PP_CONTROL, pp); 4108c2ecf20Sopenharmony_ci REG_READ(PP_CONTROL); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/* Returns true if the panel was already on when called */ 4158c2ecf20Sopenharmony_cistatic bool cdv_intel_edp_panel_on(struct gma_encoder *intel_encoder) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct drm_device *dev = intel_encoder->base.dev; 4188c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; 4198c2ecf20Sopenharmony_ci u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_NONE; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (intel_dp->panel_on) 4228c2ecf20Sopenharmony_ci return true; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 4258c2ecf20Sopenharmony_ci pp = REG_READ(PP_CONTROL); 4268c2ecf20Sopenharmony_ci pp &= ~PANEL_UNLOCK_MASK; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci pp |= (PANEL_UNLOCK_REGS | POWER_TARGET_ON); 4298c2ecf20Sopenharmony_ci REG_WRITE(PP_CONTROL, pp); 4308c2ecf20Sopenharmony_ci REG_READ(PP_CONTROL); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (wait_for(((REG_READ(PP_STATUS) & idle_on_mask) == idle_on_mask), 1000)) { 4338c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Error in Powering up eDP panel, status %x\n", REG_READ(PP_STATUS)); 4348c2ecf20Sopenharmony_ci intel_dp->panel_on = false; 4358c2ecf20Sopenharmony_ci } else 4368c2ecf20Sopenharmony_ci intel_dp->panel_on = true; 4378c2ecf20Sopenharmony_ci msleep(intel_dp->panel_power_up_delay); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return false; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic void cdv_intel_edp_panel_off (struct gma_encoder *intel_encoder) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct drm_device *dev = intel_encoder->base.dev; 4458c2ecf20Sopenharmony_ci u32 pp, idle_off_mask = PP_ON ; 4468c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci pp = REG_READ(PP_CONTROL); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if ((pp & POWER_TARGET_ON) == 0) 4538c2ecf20Sopenharmony_ci return; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci intel_dp->panel_on = false; 4568c2ecf20Sopenharmony_ci pp &= ~PANEL_UNLOCK_MASK; 4578c2ecf20Sopenharmony_ci /* ILK workaround: disable reset around power sequence */ 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci pp &= ~POWER_TARGET_ON; 4608c2ecf20Sopenharmony_ci pp &= ~EDP_FORCE_VDD; 4618c2ecf20Sopenharmony_ci pp &= ~EDP_BLC_ENABLE; 4628c2ecf20Sopenharmony_ci REG_WRITE(PP_CONTROL, pp); 4638c2ecf20Sopenharmony_ci REG_READ(PP_CONTROL); 4648c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("PP_STATUS %x\n", REG_READ(PP_STATUS)); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (wait_for((REG_READ(PP_STATUS) & idle_off_mask) == 0, 1000)) { 4678c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Error in turning off Panel\n"); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci msleep(intel_dp->panel_power_cycle_delay); 4718c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Over\n"); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic void cdv_intel_edp_backlight_on (struct gma_encoder *intel_encoder) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct drm_device *dev = intel_encoder->base.dev; 4778c2ecf20Sopenharmony_ci u32 pp; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 4808c2ecf20Sopenharmony_ci /* 4818c2ecf20Sopenharmony_ci * If we enable the backlight right away following a panel power 4828c2ecf20Sopenharmony_ci * on, we may see slight flicker as the panel syncs with the eDP 4838c2ecf20Sopenharmony_ci * link. So delay a bit to make sure the image is solid before 4848c2ecf20Sopenharmony_ci * allowing it to appear. 4858c2ecf20Sopenharmony_ci */ 4868c2ecf20Sopenharmony_ci msleep(300); 4878c2ecf20Sopenharmony_ci pp = REG_READ(PP_CONTROL); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci pp |= EDP_BLC_ENABLE; 4908c2ecf20Sopenharmony_ci REG_WRITE(PP_CONTROL, pp); 4918c2ecf20Sopenharmony_ci gma_backlight_enable(dev); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void cdv_intel_edp_backlight_off (struct gma_encoder *intel_encoder) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct drm_device *dev = intel_encoder->base.dev; 4978c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; 4988c2ecf20Sopenharmony_ci u32 pp; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 5018c2ecf20Sopenharmony_ci gma_backlight_disable(dev); 5028c2ecf20Sopenharmony_ci msleep(10); 5038c2ecf20Sopenharmony_ci pp = REG_READ(PP_CONTROL); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci pp &= ~EDP_BLC_ENABLE; 5068c2ecf20Sopenharmony_ci REG_WRITE(PP_CONTROL, pp); 5078c2ecf20Sopenharmony_ci msleep(intel_dp->backlight_off_delay); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic enum drm_mode_status 5118c2ecf20Sopenharmony_cicdv_intel_dp_mode_valid(struct drm_connector *connector, 5128c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct gma_encoder *encoder = gma_attached_encoder(connector); 5158c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 5168c2ecf20Sopenharmony_ci int max_link_clock = cdv_intel_dp_link_clock(cdv_intel_dp_max_link_bw(encoder)); 5178c2ecf20Sopenharmony_ci int max_lanes = cdv_intel_dp_max_lane_count(encoder); 5188c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = connector->dev->dev_private; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (is_edp(encoder) && intel_dp->panel_fixed_mode) { 5218c2ecf20Sopenharmony_ci if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay) 5228c2ecf20Sopenharmony_ci return MODE_PANEL; 5238c2ecf20Sopenharmony_ci if (mode->vdisplay > intel_dp->panel_fixed_mode->vdisplay) 5248c2ecf20Sopenharmony_ci return MODE_PANEL; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* only refuse the mode on non eDP since we have seen some weird eDP panels 5288c2ecf20Sopenharmony_ci which are outside spec tolerances but somehow work by magic */ 5298c2ecf20Sopenharmony_ci if (!is_edp(encoder) && 5308c2ecf20Sopenharmony_ci (cdv_intel_dp_link_required(mode->clock, dev_priv->edp.bpp) 5318c2ecf20Sopenharmony_ci > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes))) 5328c2ecf20Sopenharmony_ci return MODE_CLOCK_HIGH; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (is_edp(encoder)) { 5358c2ecf20Sopenharmony_ci if (cdv_intel_dp_link_required(mode->clock, 24) 5368c2ecf20Sopenharmony_ci > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes)) 5378c2ecf20Sopenharmony_ci return MODE_CLOCK_HIGH; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci if (mode->clock < 10000) 5418c2ecf20Sopenharmony_ci return MODE_CLOCK_LOW; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return MODE_OK; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic uint32_t 5478c2ecf20Sopenharmony_cipack_aux(uint8_t *src, int src_bytes) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci int i; 5508c2ecf20Sopenharmony_ci uint32_t v = 0; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (src_bytes > 4) 5538c2ecf20Sopenharmony_ci src_bytes = 4; 5548c2ecf20Sopenharmony_ci for (i = 0; i < src_bytes; i++) 5558c2ecf20Sopenharmony_ci v |= ((uint32_t) src[i]) << ((3-i) * 8); 5568c2ecf20Sopenharmony_ci return v; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic void 5608c2ecf20Sopenharmony_ciunpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci int i; 5638c2ecf20Sopenharmony_ci if (dst_bytes > 4) 5648c2ecf20Sopenharmony_ci dst_bytes = 4; 5658c2ecf20Sopenharmony_ci for (i = 0; i < dst_bytes; i++) 5668c2ecf20Sopenharmony_ci dst[i] = src >> ((3-i) * 8); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic int 5708c2ecf20Sopenharmony_cicdv_intel_dp_aux_ch(struct gma_encoder *encoder, 5718c2ecf20Sopenharmony_ci uint8_t *send, int send_bytes, 5728c2ecf20Sopenharmony_ci uint8_t *recv, int recv_size) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 5758c2ecf20Sopenharmony_ci uint32_t output_reg = intel_dp->output_reg; 5768c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->base.dev; 5778c2ecf20Sopenharmony_ci uint32_t ch_ctl = output_reg + 0x10; 5788c2ecf20Sopenharmony_ci uint32_t ch_data = ch_ctl + 4; 5798c2ecf20Sopenharmony_ci int i; 5808c2ecf20Sopenharmony_ci int recv_bytes; 5818c2ecf20Sopenharmony_ci uint32_t status; 5828c2ecf20Sopenharmony_ci uint32_t aux_clock_divider; 5838c2ecf20Sopenharmony_ci int try, precharge; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* The clock divider is based off the hrawclk, 5868c2ecf20Sopenharmony_ci * and would like to run at 2MHz. So, take the 5878c2ecf20Sopenharmony_ci * hrawclk value and divide by 2 and use that 5888c2ecf20Sopenharmony_ci * On CDV platform it uses 200MHz as hrawclk. 5898c2ecf20Sopenharmony_ci * 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_ci aux_clock_divider = 200 / 2; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci precharge = 4; 5948c2ecf20Sopenharmony_ci if (is_edp(encoder)) 5958c2ecf20Sopenharmony_ci precharge = 10; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (REG_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) { 5988c2ecf20Sopenharmony_ci DRM_ERROR("dp_aux_ch not started status 0x%08x\n", 5998c2ecf20Sopenharmony_ci REG_READ(ch_ctl)); 6008c2ecf20Sopenharmony_ci return -EBUSY; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* Must try at least 3 times according to DP spec */ 6048c2ecf20Sopenharmony_ci for (try = 0; try < 5; try++) { 6058c2ecf20Sopenharmony_ci /* Load the send data into the aux channel data registers */ 6068c2ecf20Sopenharmony_ci for (i = 0; i < send_bytes; i += 4) 6078c2ecf20Sopenharmony_ci REG_WRITE(ch_data + i, 6088c2ecf20Sopenharmony_ci pack_aux(send + i, send_bytes - i)); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* Send the command and wait for it to complete */ 6118c2ecf20Sopenharmony_ci REG_WRITE(ch_ctl, 6128c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_SEND_BUSY | 6138c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_TIME_OUT_400us | 6148c2ecf20Sopenharmony_ci (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | 6158c2ecf20Sopenharmony_ci (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | 6168c2ecf20Sopenharmony_ci (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) | 6178c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_DONE | 6188c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_TIME_OUT_ERROR | 6198c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_RECEIVE_ERROR); 6208c2ecf20Sopenharmony_ci for (;;) { 6218c2ecf20Sopenharmony_ci status = REG_READ(ch_ctl); 6228c2ecf20Sopenharmony_ci if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) 6238c2ecf20Sopenharmony_ci break; 6248c2ecf20Sopenharmony_ci udelay(100); 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* Clear done status and any errors */ 6288c2ecf20Sopenharmony_ci REG_WRITE(ch_ctl, 6298c2ecf20Sopenharmony_ci status | 6308c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_DONE | 6318c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_TIME_OUT_ERROR | 6328c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_RECEIVE_ERROR); 6338c2ecf20Sopenharmony_ci if (status & DP_AUX_CH_CTL_DONE) 6348c2ecf20Sopenharmony_ci break; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if ((status & DP_AUX_CH_CTL_DONE) == 0) { 6388c2ecf20Sopenharmony_ci DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); 6398c2ecf20Sopenharmony_ci return -EBUSY; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* Check for timeout or receive error. 6438c2ecf20Sopenharmony_ci * Timeouts occur when the sink is not connected 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_ci if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { 6468c2ecf20Sopenharmony_ci DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); 6478c2ecf20Sopenharmony_ci return -EIO; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /* Timeouts occur when the device isn't connected, so they're 6518c2ecf20Sopenharmony_ci * "normal" -- don't fill the kernel log with these */ 6528c2ecf20Sopenharmony_ci if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) { 6538c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status); 6548c2ecf20Sopenharmony_ci return -ETIMEDOUT; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* Unload any bytes sent back from the other side */ 6588c2ecf20Sopenharmony_ci recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >> 6598c2ecf20Sopenharmony_ci DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT); 6608c2ecf20Sopenharmony_ci if (recv_bytes > recv_size) 6618c2ecf20Sopenharmony_ci recv_bytes = recv_size; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci for (i = 0; i < recv_bytes; i += 4) 6648c2ecf20Sopenharmony_ci unpack_aux(REG_READ(ch_data + i), 6658c2ecf20Sopenharmony_ci recv + i, recv_bytes - i); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci return recv_bytes; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci/* Write data to the aux channel in native mode */ 6718c2ecf20Sopenharmony_cistatic int 6728c2ecf20Sopenharmony_cicdv_intel_dp_aux_native_write(struct gma_encoder *encoder, 6738c2ecf20Sopenharmony_ci uint16_t address, uint8_t *send, int send_bytes) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci int ret; 6768c2ecf20Sopenharmony_ci uint8_t msg[20]; 6778c2ecf20Sopenharmony_ci int msg_bytes; 6788c2ecf20Sopenharmony_ci uint8_t ack; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (send_bytes > 16) 6818c2ecf20Sopenharmony_ci return -1; 6828c2ecf20Sopenharmony_ci msg[0] = DP_AUX_NATIVE_WRITE << 4; 6838c2ecf20Sopenharmony_ci msg[1] = address >> 8; 6848c2ecf20Sopenharmony_ci msg[2] = address & 0xff; 6858c2ecf20Sopenharmony_ci msg[3] = send_bytes - 1; 6868c2ecf20Sopenharmony_ci memcpy(&msg[4], send, send_bytes); 6878c2ecf20Sopenharmony_ci msg_bytes = send_bytes + 4; 6888c2ecf20Sopenharmony_ci for (;;) { 6898c2ecf20Sopenharmony_ci ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes, &ack, 1); 6908c2ecf20Sopenharmony_ci if (ret < 0) 6918c2ecf20Sopenharmony_ci return ret; 6928c2ecf20Sopenharmony_ci ack >>= 4; 6938c2ecf20Sopenharmony_ci if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_ACK) 6948c2ecf20Sopenharmony_ci break; 6958c2ecf20Sopenharmony_ci else if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_DEFER) 6968c2ecf20Sopenharmony_ci udelay(100); 6978c2ecf20Sopenharmony_ci else 6988c2ecf20Sopenharmony_ci return -EIO; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci return send_bytes; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci/* Write a single byte to the aux channel in native mode */ 7048c2ecf20Sopenharmony_cistatic int 7058c2ecf20Sopenharmony_cicdv_intel_dp_aux_native_write_1(struct gma_encoder *encoder, 7068c2ecf20Sopenharmony_ci uint16_t address, uint8_t byte) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci return cdv_intel_dp_aux_native_write(encoder, address, &byte, 1); 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci/* read bytes from a native aux channel */ 7128c2ecf20Sopenharmony_cistatic int 7138c2ecf20Sopenharmony_cicdv_intel_dp_aux_native_read(struct gma_encoder *encoder, 7148c2ecf20Sopenharmony_ci uint16_t address, uint8_t *recv, int recv_bytes) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci uint8_t msg[4]; 7178c2ecf20Sopenharmony_ci int msg_bytes; 7188c2ecf20Sopenharmony_ci uint8_t reply[20]; 7198c2ecf20Sopenharmony_ci int reply_bytes; 7208c2ecf20Sopenharmony_ci uint8_t ack; 7218c2ecf20Sopenharmony_ci int ret; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci msg[0] = DP_AUX_NATIVE_READ << 4; 7248c2ecf20Sopenharmony_ci msg[1] = address >> 8; 7258c2ecf20Sopenharmony_ci msg[2] = address & 0xff; 7268c2ecf20Sopenharmony_ci msg[3] = recv_bytes - 1; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci msg_bytes = 4; 7298c2ecf20Sopenharmony_ci reply_bytes = recv_bytes + 1; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci for (;;) { 7328c2ecf20Sopenharmony_ci ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes, 7338c2ecf20Sopenharmony_ci reply, reply_bytes); 7348c2ecf20Sopenharmony_ci if (ret == 0) 7358c2ecf20Sopenharmony_ci return -EPROTO; 7368c2ecf20Sopenharmony_ci if (ret < 0) 7378c2ecf20Sopenharmony_ci return ret; 7388c2ecf20Sopenharmony_ci ack = reply[0] >> 4; 7398c2ecf20Sopenharmony_ci if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_ACK) { 7408c2ecf20Sopenharmony_ci memcpy(recv, reply + 1, ret - 1); 7418c2ecf20Sopenharmony_ci return ret - 1; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci else if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_DEFER) 7448c2ecf20Sopenharmony_ci udelay(100); 7458c2ecf20Sopenharmony_ci else 7468c2ecf20Sopenharmony_ci return -EIO; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic int 7518c2ecf20Sopenharmony_cicdv_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, 7528c2ecf20Sopenharmony_ci uint8_t write_byte, uint8_t *read_byte) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; 7558c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = container_of(adapter, 7568c2ecf20Sopenharmony_ci struct cdv_intel_dp, 7578c2ecf20Sopenharmony_ci adapter); 7588c2ecf20Sopenharmony_ci struct gma_encoder *encoder = intel_dp->encoder; 7598c2ecf20Sopenharmony_ci uint16_t address = algo_data->address; 7608c2ecf20Sopenharmony_ci uint8_t msg[5]; 7618c2ecf20Sopenharmony_ci uint8_t reply[2]; 7628c2ecf20Sopenharmony_ci unsigned retry; 7638c2ecf20Sopenharmony_ci int msg_bytes; 7648c2ecf20Sopenharmony_ci int reply_bytes; 7658c2ecf20Sopenharmony_ci int ret; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* Set up the command byte */ 7688c2ecf20Sopenharmony_ci if (mode & MODE_I2C_READ) 7698c2ecf20Sopenharmony_ci msg[0] = DP_AUX_I2C_READ << 4; 7708c2ecf20Sopenharmony_ci else 7718c2ecf20Sopenharmony_ci msg[0] = DP_AUX_I2C_WRITE << 4; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (!(mode & MODE_I2C_STOP)) 7748c2ecf20Sopenharmony_ci msg[0] |= DP_AUX_I2C_MOT << 4; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci msg[1] = address >> 8; 7778c2ecf20Sopenharmony_ci msg[2] = address; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci switch (mode) { 7808c2ecf20Sopenharmony_ci case MODE_I2C_WRITE: 7818c2ecf20Sopenharmony_ci msg[3] = 0; 7828c2ecf20Sopenharmony_ci msg[4] = write_byte; 7838c2ecf20Sopenharmony_ci msg_bytes = 5; 7848c2ecf20Sopenharmony_ci reply_bytes = 1; 7858c2ecf20Sopenharmony_ci break; 7868c2ecf20Sopenharmony_ci case MODE_I2C_READ: 7878c2ecf20Sopenharmony_ci msg[3] = 0; 7888c2ecf20Sopenharmony_ci msg_bytes = 4; 7898c2ecf20Sopenharmony_ci reply_bytes = 2; 7908c2ecf20Sopenharmony_ci break; 7918c2ecf20Sopenharmony_ci default: 7928c2ecf20Sopenharmony_ci msg_bytes = 3; 7938c2ecf20Sopenharmony_ci reply_bytes = 1; 7948c2ecf20Sopenharmony_ci break; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci for (retry = 0; retry < 5; retry++) { 7988c2ecf20Sopenharmony_ci ret = cdv_intel_dp_aux_ch(encoder, 7998c2ecf20Sopenharmony_ci msg, msg_bytes, 8008c2ecf20Sopenharmony_ci reply, reply_bytes); 8018c2ecf20Sopenharmony_ci if (ret < 0) { 8028c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("aux_ch failed %d\n", ret); 8038c2ecf20Sopenharmony_ci return ret; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci switch ((reply[0] >> 4) & DP_AUX_NATIVE_REPLY_MASK) { 8078c2ecf20Sopenharmony_ci case DP_AUX_NATIVE_REPLY_ACK: 8088c2ecf20Sopenharmony_ci /* I2C-over-AUX Reply field is only valid 8098c2ecf20Sopenharmony_ci * when paired with AUX ACK. 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_ci break; 8128c2ecf20Sopenharmony_ci case DP_AUX_NATIVE_REPLY_NACK: 8138c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("aux_ch native nack\n"); 8148c2ecf20Sopenharmony_ci return -EREMOTEIO; 8158c2ecf20Sopenharmony_ci case DP_AUX_NATIVE_REPLY_DEFER: 8168c2ecf20Sopenharmony_ci udelay(100); 8178c2ecf20Sopenharmony_ci continue; 8188c2ecf20Sopenharmony_ci default: 8198c2ecf20Sopenharmony_ci DRM_ERROR("aux_ch invalid native reply 0x%02x\n", 8208c2ecf20Sopenharmony_ci reply[0]); 8218c2ecf20Sopenharmony_ci return -EREMOTEIO; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci switch ((reply[0] >> 4) & DP_AUX_I2C_REPLY_MASK) { 8258c2ecf20Sopenharmony_ci case DP_AUX_I2C_REPLY_ACK: 8268c2ecf20Sopenharmony_ci if (mode == MODE_I2C_READ) { 8278c2ecf20Sopenharmony_ci *read_byte = reply[1]; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci return reply_bytes - 1; 8308c2ecf20Sopenharmony_ci case DP_AUX_I2C_REPLY_NACK: 8318c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("aux_i2c nack\n"); 8328c2ecf20Sopenharmony_ci return -EREMOTEIO; 8338c2ecf20Sopenharmony_ci case DP_AUX_I2C_REPLY_DEFER: 8348c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("aux_i2c defer\n"); 8358c2ecf20Sopenharmony_ci udelay(100); 8368c2ecf20Sopenharmony_ci break; 8378c2ecf20Sopenharmony_ci default: 8388c2ecf20Sopenharmony_ci DRM_ERROR("aux_i2c invalid reply 0x%02x\n", reply[0]); 8398c2ecf20Sopenharmony_ci return -EREMOTEIO; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci DRM_ERROR("too many retries, giving up\n"); 8448c2ecf20Sopenharmony_ci return -EREMOTEIO; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic int 8488c2ecf20Sopenharmony_cicdv_intel_dp_i2c_init(struct gma_connector *connector, 8498c2ecf20Sopenharmony_ci struct gma_encoder *encoder, const char *name) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 8528c2ecf20Sopenharmony_ci int ret; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("i2c_init %s\n", name); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci intel_dp->algo.running = false; 8578c2ecf20Sopenharmony_ci intel_dp->algo.address = 0; 8588c2ecf20Sopenharmony_ci intel_dp->algo.aux_ch = cdv_intel_dp_i2c_aux_ch; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter)); 8618c2ecf20Sopenharmony_ci intel_dp->adapter.owner = THIS_MODULE; 8628c2ecf20Sopenharmony_ci intel_dp->adapter.class = I2C_CLASS_DDC; 8638c2ecf20Sopenharmony_ci strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1); 8648c2ecf20Sopenharmony_ci intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0'; 8658c2ecf20Sopenharmony_ci intel_dp->adapter.algo_data = &intel_dp->algo; 8668c2ecf20Sopenharmony_ci intel_dp->adapter.dev.parent = connector->base.kdev; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (is_edp(encoder)) 8698c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_on(encoder); 8708c2ecf20Sopenharmony_ci ret = i2c_dp_aux_add_bus(&intel_dp->adapter); 8718c2ecf20Sopenharmony_ci if (is_edp(encoder)) 8728c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_off(encoder); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return ret; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic void cdv_intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, 8788c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci adjusted_mode->hdisplay = fixed_mode->hdisplay; 8818c2ecf20Sopenharmony_ci adjusted_mode->hsync_start = fixed_mode->hsync_start; 8828c2ecf20Sopenharmony_ci adjusted_mode->hsync_end = fixed_mode->hsync_end; 8838c2ecf20Sopenharmony_ci adjusted_mode->htotal = fixed_mode->htotal; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci adjusted_mode->vdisplay = fixed_mode->vdisplay; 8868c2ecf20Sopenharmony_ci adjusted_mode->vsync_start = fixed_mode->vsync_start; 8878c2ecf20Sopenharmony_ci adjusted_mode->vsync_end = fixed_mode->vsync_end; 8888c2ecf20Sopenharmony_ci adjusted_mode->vtotal = fixed_mode->vtotal; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci adjusted_mode->clock = fixed_mode->clock; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic bool 8968c2ecf20Sopenharmony_cicdv_intel_dp_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, 8978c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = encoder->dev->dev_private; 9008c2ecf20Sopenharmony_ci struct gma_encoder *intel_encoder = to_gma_encoder(encoder); 9018c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; 9028c2ecf20Sopenharmony_ci int lane_count, clock; 9038c2ecf20Sopenharmony_ci int max_lane_count = cdv_intel_dp_max_lane_count(intel_encoder); 9048c2ecf20Sopenharmony_ci int max_clock = cdv_intel_dp_max_link_bw(intel_encoder) == DP_LINK_BW_2_7 ? 1 : 0; 9058c2ecf20Sopenharmony_ci static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; 9068c2ecf20Sopenharmony_ci int refclock = mode->clock; 9078c2ecf20Sopenharmony_ci int bpp = 24; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (is_edp(intel_encoder) && intel_dp->panel_fixed_mode) { 9108c2ecf20Sopenharmony_ci cdv_intel_fixed_panel_mode(intel_dp->panel_fixed_mode, adjusted_mode); 9118c2ecf20Sopenharmony_ci refclock = intel_dp->panel_fixed_mode->clock; 9128c2ecf20Sopenharmony_ci bpp = dev_priv->edp.bpp; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { 9168c2ecf20Sopenharmony_ci for (clock = max_clock; clock >= 0; clock--) { 9178c2ecf20Sopenharmony_ci int link_avail = cdv_intel_dp_max_data_rate(cdv_intel_dp_link_clock(bws[clock]), lane_count); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci if (cdv_intel_dp_link_required(refclock, bpp) <= link_avail) { 9208c2ecf20Sopenharmony_ci intel_dp->link_bw = bws[clock]; 9218c2ecf20Sopenharmony_ci intel_dp->lane_count = lane_count; 9228c2ecf20Sopenharmony_ci adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw); 9238c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Display port link bw %02x lane " 9248c2ecf20Sopenharmony_ci "count %d clock %d\n", 9258c2ecf20Sopenharmony_ci intel_dp->link_bw, intel_dp->lane_count, 9268c2ecf20Sopenharmony_ci adjusted_mode->clock); 9278c2ecf20Sopenharmony_ci return true; 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci if (is_edp(intel_encoder)) { 9328c2ecf20Sopenharmony_ci /* okay we failed just pick the highest */ 9338c2ecf20Sopenharmony_ci intel_dp->lane_count = max_lane_count; 9348c2ecf20Sopenharmony_ci intel_dp->link_bw = bws[max_clock]; 9358c2ecf20Sopenharmony_ci adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw); 9368c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Force picking display port link bw %02x lane " 9378c2ecf20Sopenharmony_ci "count %d clock %d\n", 9388c2ecf20Sopenharmony_ci intel_dp->link_bw, intel_dp->lane_count, 9398c2ecf20Sopenharmony_ci adjusted_mode->clock); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci return true; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci return false; 9448c2ecf20Sopenharmony_ci} 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistruct cdv_intel_dp_m_n { 9478c2ecf20Sopenharmony_ci uint32_t tu; 9488c2ecf20Sopenharmony_ci uint32_t gmch_m; 9498c2ecf20Sopenharmony_ci uint32_t gmch_n; 9508c2ecf20Sopenharmony_ci uint32_t link_m; 9518c2ecf20Sopenharmony_ci uint32_t link_n; 9528c2ecf20Sopenharmony_ci}; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic void 9558c2ecf20Sopenharmony_cicdv_intel_reduce_ratio(uint32_t *num, uint32_t *den) 9568c2ecf20Sopenharmony_ci{ 9578c2ecf20Sopenharmony_ci /* 9588c2ecf20Sopenharmony_ci while (*num > 0xffffff || *den > 0xffffff) { 9598c2ecf20Sopenharmony_ci *num >>= 1; 9608c2ecf20Sopenharmony_ci *den >>= 1; 9618c2ecf20Sopenharmony_ci }*/ 9628c2ecf20Sopenharmony_ci uint64_t value, m; 9638c2ecf20Sopenharmony_ci m = *num; 9648c2ecf20Sopenharmony_ci value = m * (0x800000); 9658c2ecf20Sopenharmony_ci m = do_div(value, *den); 9668c2ecf20Sopenharmony_ci *num = value; 9678c2ecf20Sopenharmony_ci *den = 0x800000; 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic void 9718c2ecf20Sopenharmony_cicdv_intel_dp_compute_m_n(int bpp, 9728c2ecf20Sopenharmony_ci int nlanes, 9738c2ecf20Sopenharmony_ci int pixel_clock, 9748c2ecf20Sopenharmony_ci int link_clock, 9758c2ecf20Sopenharmony_ci struct cdv_intel_dp_m_n *m_n) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci m_n->tu = 64; 9788c2ecf20Sopenharmony_ci m_n->gmch_m = (pixel_clock * bpp + 7) >> 3; 9798c2ecf20Sopenharmony_ci m_n->gmch_n = link_clock * nlanes; 9808c2ecf20Sopenharmony_ci cdv_intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); 9818c2ecf20Sopenharmony_ci m_n->link_m = pixel_clock; 9828c2ecf20Sopenharmony_ci m_n->link_n = link_clock; 9838c2ecf20Sopenharmony_ci cdv_intel_reduce_ratio(&m_n->link_m, &m_n->link_n); 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_civoid 9878c2ecf20Sopenharmony_cicdv_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, 9888c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 9918c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 9928c2ecf20Sopenharmony_ci struct drm_mode_config *mode_config = &dev->mode_config; 9938c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 9948c2ecf20Sopenharmony_ci struct gma_crtc *gma_crtc = to_gma_crtc(crtc); 9958c2ecf20Sopenharmony_ci int lane_count = 4, bpp = 24; 9968c2ecf20Sopenharmony_ci struct cdv_intel_dp_m_n m_n; 9978c2ecf20Sopenharmony_ci int pipe = gma_crtc->pipe; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* 10008c2ecf20Sopenharmony_ci * Find the lane count in the intel_encoder private 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &mode_config->encoder_list, head) { 10038c2ecf20Sopenharmony_ci struct gma_encoder *intel_encoder; 10048c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci if (encoder->crtc != crtc) 10078c2ecf20Sopenharmony_ci continue; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci intel_encoder = to_gma_encoder(encoder); 10108c2ecf20Sopenharmony_ci intel_dp = intel_encoder->dev_priv; 10118c2ecf20Sopenharmony_ci if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { 10128c2ecf20Sopenharmony_ci lane_count = intel_dp->lane_count; 10138c2ecf20Sopenharmony_ci break; 10148c2ecf20Sopenharmony_ci } else if (is_edp(intel_encoder)) { 10158c2ecf20Sopenharmony_ci lane_count = intel_dp->lane_count; 10168c2ecf20Sopenharmony_ci bpp = dev_priv->edp.bpp; 10178c2ecf20Sopenharmony_ci break; 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci /* 10228c2ecf20Sopenharmony_ci * Compute the GMCH and Link ratios. The '3' here is 10238c2ecf20Sopenharmony_ci * the number of bytes_per_pixel post-LUT, which we always 10248c2ecf20Sopenharmony_ci * set up for 8-bits of R/G/B, or 3 bytes total. 10258c2ecf20Sopenharmony_ci */ 10268c2ecf20Sopenharmony_ci cdv_intel_dp_compute_m_n(bpp, lane_count, 10278c2ecf20Sopenharmony_ci mode->clock, adjusted_mode->clock, &m_n); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci { 10308c2ecf20Sopenharmony_ci REG_WRITE(PIPE_GMCH_DATA_M(pipe), 10318c2ecf20Sopenharmony_ci ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) | 10328c2ecf20Sopenharmony_ci m_n.gmch_m); 10338c2ecf20Sopenharmony_ci REG_WRITE(PIPE_GMCH_DATA_N(pipe), m_n.gmch_n); 10348c2ecf20Sopenharmony_ci REG_WRITE(PIPE_DP_LINK_M(pipe), m_n.link_m); 10358c2ecf20Sopenharmony_ci REG_WRITE(PIPE_DP_LINK_N(pipe), m_n.link_n); 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic void 10408c2ecf20Sopenharmony_cicdv_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, 10418c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci struct gma_encoder *intel_encoder = to_gma_encoder(encoder); 10448c2ecf20Sopenharmony_ci struct drm_crtc *crtc = encoder->crtc; 10458c2ecf20Sopenharmony_ci struct gma_crtc *gma_crtc = to_gma_crtc(crtc); 10468c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; 10478c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; 10508c2ecf20Sopenharmony_ci intel_dp->DP |= intel_dp->color_range; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) 10538c2ecf20Sopenharmony_ci intel_dp->DP |= DP_SYNC_HS_HIGH; 10548c2ecf20Sopenharmony_ci if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) 10558c2ecf20Sopenharmony_ci intel_dp->DP |= DP_SYNC_VS_HIGH; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci intel_dp->DP |= DP_LINK_TRAIN_OFF; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci switch (intel_dp->lane_count) { 10608c2ecf20Sopenharmony_ci case 1: 10618c2ecf20Sopenharmony_ci intel_dp->DP |= DP_PORT_WIDTH_1; 10628c2ecf20Sopenharmony_ci break; 10638c2ecf20Sopenharmony_ci case 2: 10648c2ecf20Sopenharmony_ci intel_dp->DP |= DP_PORT_WIDTH_2; 10658c2ecf20Sopenharmony_ci break; 10668c2ecf20Sopenharmony_ci case 4: 10678c2ecf20Sopenharmony_ci intel_dp->DP |= DP_PORT_WIDTH_4; 10688c2ecf20Sopenharmony_ci break; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci if (intel_dp->has_audio) 10718c2ecf20Sopenharmony_ci intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE); 10748c2ecf20Sopenharmony_ci intel_dp->link_configuration[0] = intel_dp->link_bw; 10758c2ecf20Sopenharmony_ci intel_dp->link_configuration[1] = intel_dp->lane_count; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* 10788c2ecf20Sopenharmony_ci * Check for DPCD version > 1.1 and enhanced framing support 10798c2ecf20Sopenharmony_ci */ 10808c2ecf20Sopenharmony_ci if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && 10818c2ecf20Sopenharmony_ci (intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) { 10828c2ecf20Sopenharmony_ci intel_dp->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; 10838c2ecf20Sopenharmony_ci intel_dp->DP |= DP_ENHANCED_FRAMING; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci /* CPT DP's pipe select is decided in TRANS_DP_CTL */ 10878c2ecf20Sopenharmony_ci if (gma_crtc->pipe == 1) 10888c2ecf20Sopenharmony_ci intel_dp->DP |= DP_PIPEB_SELECT; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci REG_WRITE(intel_dp->output_reg, (intel_dp->DP | DP_PORT_EN)); 10918c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("DP expected reg is %x\n", intel_dp->DP); 10928c2ecf20Sopenharmony_ci if (is_edp(intel_encoder)) { 10938c2ecf20Sopenharmony_ci uint32_t pfit_control; 10948c2ecf20Sopenharmony_ci cdv_intel_edp_panel_on(intel_encoder); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if (mode->hdisplay != adjusted_mode->hdisplay || 10978c2ecf20Sopenharmony_ci mode->vdisplay != adjusted_mode->vdisplay) 10988c2ecf20Sopenharmony_ci pfit_control = PFIT_ENABLE; 10998c2ecf20Sopenharmony_ci else 11008c2ecf20Sopenharmony_ci pfit_control = 0; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci pfit_control |= gma_crtc->pipe << PFIT_PIPE_SHIFT; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci REG_WRITE(PFIT_CONTROL, pfit_control); 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci/* If the sink supports it, try to set the power state appropriately */ 11108c2ecf20Sopenharmony_cistatic void cdv_intel_dp_sink_dpms(struct gma_encoder *encoder, int mode) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 11138c2ecf20Sopenharmony_ci int ret, i; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci /* Should have a valid DPCD by this point */ 11168c2ecf20Sopenharmony_ci if (intel_dp->dpcd[DP_DPCD_REV] < 0x11) 11178c2ecf20Sopenharmony_ci return; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (mode != DRM_MODE_DPMS_ON) { 11208c2ecf20Sopenharmony_ci ret = cdv_intel_dp_aux_native_write_1(encoder, DP_SET_POWER, 11218c2ecf20Sopenharmony_ci DP_SET_POWER_D3); 11228c2ecf20Sopenharmony_ci if (ret != 1) 11238c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("failed to write sink power state\n"); 11248c2ecf20Sopenharmony_ci } else { 11258c2ecf20Sopenharmony_ci /* 11268c2ecf20Sopenharmony_ci * When turning on, we need to retry for 1ms to give the sink 11278c2ecf20Sopenharmony_ci * time to wake up. 11288c2ecf20Sopenharmony_ci */ 11298c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 11308c2ecf20Sopenharmony_ci ret = cdv_intel_dp_aux_native_write_1(encoder, 11318c2ecf20Sopenharmony_ci DP_SET_POWER, 11328c2ecf20Sopenharmony_ci DP_SET_POWER_D0); 11338c2ecf20Sopenharmony_ci if (ret == 1) 11348c2ecf20Sopenharmony_ci break; 11358c2ecf20Sopenharmony_ci udelay(1000); 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic void cdv_intel_dp_prepare(struct drm_encoder *encoder) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci struct gma_encoder *intel_encoder = to_gma_encoder(encoder); 11438c2ecf20Sopenharmony_ci int edp = is_edp(intel_encoder); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (edp) { 11468c2ecf20Sopenharmony_ci cdv_intel_edp_backlight_off(intel_encoder); 11478c2ecf20Sopenharmony_ci cdv_intel_edp_panel_off(intel_encoder); 11488c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_on(intel_encoder); 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci /* Wake up the sink first */ 11518c2ecf20Sopenharmony_ci cdv_intel_dp_sink_dpms(intel_encoder, DRM_MODE_DPMS_ON); 11528c2ecf20Sopenharmony_ci cdv_intel_dp_link_down(intel_encoder); 11538c2ecf20Sopenharmony_ci if (edp) 11548c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_off(intel_encoder); 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic void cdv_intel_dp_commit(struct drm_encoder *encoder) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct gma_encoder *intel_encoder = to_gma_encoder(encoder); 11608c2ecf20Sopenharmony_ci int edp = is_edp(intel_encoder); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci if (edp) 11638c2ecf20Sopenharmony_ci cdv_intel_edp_panel_on(intel_encoder); 11648c2ecf20Sopenharmony_ci cdv_intel_dp_start_link_train(intel_encoder); 11658c2ecf20Sopenharmony_ci cdv_intel_dp_complete_link_train(intel_encoder); 11668c2ecf20Sopenharmony_ci if (edp) 11678c2ecf20Sopenharmony_ci cdv_intel_edp_backlight_on(intel_encoder); 11688c2ecf20Sopenharmony_ci} 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_cistatic void 11718c2ecf20Sopenharmony_cicdv_intel_dp_dpms(struct drm_encoder *encoder, int mode) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci struct gma_encoder *intel_encoder = to_gma_encoder(encoder); 11748c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; 11758c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 11768c2ecf20Sopenharmony_ci uint32_t dp_reg = REG_READ(intel_dp->output_reg); 11778c2ecf20Sopenharmony_ci int edp = is_edp(intel_encoder); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci if (mode != DRM_MODE_DPMS_ON) { 11808c2ecf20Sopenharmony_ci if (edp) { 11818c2ecf20Sopenharmony_ci cdv_intel_edp_backlight_off(intel_encoder); 11828c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_on(intel_encoder); 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci cdv_intel_dp_sink_dpms(intel_encoder, mode); 11858c2ecf20Sopenharmony_ci cdv_intel_dp_link_down(intel_encoder); 11868c2ecf20Sopenharmony_ci if (edp) { 11878c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_off(intel_encoder); 11888c2ecf20Sopenharmony_ci cdv_intel_edp_panel_off(intel_encoder); 11898c2ecf20Sopenharmony_ci } 11908c2ecf20Sopenharmony_ci } else { 11918c2ecf20Sopenharmony_ci if (edp) 11928c2ecf20Sopenharmony_ci cdv_intel_edp_panel_on(intel_encoder); 11938c2ecf20Sopenharmony_ci cdv_intel_dp_sink_dpms(intel_encoder, mode); 11948c2ecf20Sopenharmony_ci if (!(dp_reg & DP_PORT_EN)) { 11958c2ecf20Sopenharmony_ci cdv_intel_dp_start_link_train(intel_encoder); 11968c2ecf20Sopenharmony_ci cdv_intel_dp_complete_link_train(intel_encoder); 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci if (edp) 11998c2ecf20Sopenharmony_ci cdv_intel_edp_backlight_on(intel_encoder); 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci} 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci/* 12048c2ecf20Sopenharmony_ci * Native read with retry for link status and receiver capability reads for 12058c2ecf20Sopenharmony_ci * cases where the sink may still be asleep. 12068c2ecf20Sopenharmony_ci */ 12078c2ecf20Sopenharmony_cistatic bool 12088c2ecf20Sopenharmony_cicdv_intel_dp_aux_native_read_retry(struct gma_encoder *encoder, uint16_t address, 12098c2ecf20Sopenharmony_ci uint8_t *recv, int recv_bytes) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci int ret, i; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* 12148c2ecf20Sopenharmony_ci * Sinks are *supposed* to come up within 1ms from an off state, 12158c2ecf20Sopenharmony_ci * but we're also supposed to retry 3 times per the spec. 12168c2ecf20Sopenharmony_ci */ 12178c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 12188c2ecf20Sopenharmony_ci ret = cdv_intel_dp_aux_native_read(encoder, address, recv, 12198c2ecf20Sopenharmony_ci recv_bytes); 12208c2ecf20Sopenharmony_ci if (ret == recv_bytes) 12218c2ecf20Sopenharmony_ci return true; 12228c2ecf20Sopenharmony_ci udelay(1000); 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci return false; 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci/* 12298c2ecf20Sopenharmony_ci * Fetch AUX CH registers 0x202 - 0x207 which contain 12308c2ecf20Sopenharmony_ci * link status information 12318c2ecf20Sopenharmony_ci */ 12328c2ecf20Sopenharmony_cistatic bool 12338c2ecf20Sopenharmony_cicdv_intel_dp_get_link_status(struct gma_encoder *encoder) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 12368c2ecf20Sopenharmony_ci return cdv_intel_dp_aux_native_read_retry(encoder, 12378c2ecf20Sopenharmony_ci DP_LANE0_1_STATUS, 12388c2ecf20Sopenharmony_ci intel_dp->link_status, 12398c2ecf20Sopenharmony_ci DP_LINK_STATUS_SIZE); 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cistatic uint8_t 12438c2ecf20Sopenharmony_cicdv_intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], 12448c2ecf20Sopenharmony_ci int r) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci return link_status[r - DP_LANE0_1_STATUS]; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic uint8_t 12508c2ecf20Sopenharmony_cicdv_intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE], 12518c2ecf20Sopenharmony_ci int lane) 12528c2ecf20Sopenharmony_ci{ 12538c2ecf20Sopenharmony_ci int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); 12548c2ecf20Sopenharmony_ci int s = ((lane & 1) ? 12558c2ecf20Sopenharmony_ci DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 12568c2ecf20Sopenharmony_ci DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); 12578c2ecf20Sopenharmony_ci uint8_t l = cdv_intel_dp_link_status(link_status, i); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cistatic uint8_t 12638c2ecf20Sopenharmony_cicdv_intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE], 12648c2ecf20Sopenharmony_ci int lane) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); 12678c2ecf20Sopenharmony_ci int s = ((lane & 1) ? 12688c2ecf20Sopenharmony_ci DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : 12698c2ecf20Sopenharmony_ci DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); 12708c2ecf20Sopenharmony_ci uint8_t l = cdv_intel_dp_link_status(link_status, i); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci#define CDV_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_LEVEL_3 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic void 12788c2ecf20Sopenharmony_cicdv_intel_get_adjust_train(struct gma_encoder *encoder) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 12818c2ecf20Sopenharmony_ci uint8_t v = 0; 12828c2ecf20Sopenharmony_ci uint8_t p = 0; 12838c2ecf20Sopenharmony_ci int lane; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci for (lane = 0; lane < intel_dp->lane_count; lane++) { 12868c2ecf20Sopenharmony_ci uint8_t this_v = cdv_intel_get_adjust_request_voltage(intel_dp->link_status, lane); 12878c2ecf20Sopenharmony_ci uint8_t this_p = cdv_intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (this_v > v) 12908c2ecf20Sopenharmony_ci v = this_v; 12918c2ecf20Sopenharmony_ci if (this_p > p) 12928c2ecf20Sopenharmony_ci p = this_p; 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci if (v >= CDV_DP_VOLTAGE_MAX) 12968c2ecf20Sopenharmony_ci v = CDV_DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (p == DP_TRAIN_PRE_EMPHASIS_MASK) 12998c2ecf20Sopenharmony_ci p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci for (lane = 0; lane < 4; lane++) 13028c2ecf20Sopenharmony_ci intel_dp->train_set[lane] = v | p; 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_cistatic uint8_t 13078c2ecf20Sopenharmony_cicdv_intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], 13088c2ecf20Sopenharmony_ci int lane) 13098c2ecf20Sopenharmony_ci{ 13108c2ecf20Sopenharmony_ci int i = DP_LANE0_1_STATUS + (lane >> 1); 13118c2ecf20Sopenharmony_ci int s = (lane & 1) * 4; 13128c2ecf20Sopenharmony_ci uint8_t l = cdv_intel_dp_link_status(link_status, i); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci return (l >> s) & 0xf; 13158c2ecf20Sopenharmony_ci} 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci/* Check for clock recovery is done on all channels */ 13188c2ecf20Sopenharmony_cistatic bool 13198c2ecf20Sopenharmony_cicdv_intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci int lane; 13228c2ecf20Sopenharmony_ci uint8_t lane_status; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci for (lane = 0; lane < lane_count; lane++) { 13258c2ecf20Sopenharmony_ci lane_status = cdv_intel_get_lane_status(link_status, lane); 13268c2ecf20Sopenharmony_ci if ((lane_status & DP_LANE_CR_DONE) == 0) 13278c2ecf20Sopenharmony_ci return false; 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci return true; 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci/* Check to see if channel eq is done on all channels */ 13338c2ecf20Sopenharmony_ci#define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\ 13348c2ecf20Sopenharmony_ci DP_LANE_CHANNEL_EQ_DONE|\ 13358c2ecf20Sopenharmony_ci DP_LANE_SYMBOL_LOCKED) 13368c2ecf20Sopenharmony_cistatic bool 13378c2ecf20Sopenharmony_cicdv_intel_channel_eq_ok(struct gma_encoder *encoder) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 13408c2ecf20Sopenharmony_ci uint8_t lane_align; 13418c2ecf20Sopenharmony_ci uint8_t lane_status; 13428c2ecf20Sopenharmony_ci int lane; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci lane_align = cdv_intel_dp_link_status(intel_dp->link_status, 13458c2ecf20Sopenharmony_ci DP_LANE_ALIGN_STATUS_UPDATED); 13468c2ecf20Sopenharmony_ci if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) 13478c2ecf20Sopenharmony_ci return false; 13488c2ecf20Sopenharmony_ci for (lane = 0; lane < intel_dp->lane_count; lane++) { 13498c2ecf20Sopenharmony_ci lane_status = cdv_intel_get_lane_status(intel_dp->link_status, lane); 13508c2ecf20Sopenharmony_ci if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) 13518c2ecf20Sopenharmony_ci return false; 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci return true; 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic bool 13578c2ecf20Sopenharmony_cicdv_intel_dp_set_link_train(struct gma_encoder *encoder, 13588c2ecf20Sopenharmony_ci uint32_t dp_reg_value, 13598c2ecf20Sopenharmony_ci uint8_t dp_train_pat) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->base.dev; 13638c2ecf20Sopenharmony_ci int ret; 13648c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci REG_WRITE(intel_dp->output_reg, dp_reg_value); 13678c2ecf20Sopenharmony_ci REG_READ(intel_dp->output_reg); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci ret = cdv_intel_dp_aux_native_write_1(encoder, 13708c2ecf20Sopenharmony_ci DP_TRAINING_PATTERN_SET, 13718c2ecf20Sopenharmony_ci dp_train_pat); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (ret != 1) { 13748c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failure in setting link pattern %x\n", 13758c2ecf20Sopenharmony_ci dp_train_pat); 13768c2ecf20Sopenharmony_ci return false; 13778c2ecf20Sopenharmony_ci } 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci return true; 13808c2ecf20Sopenharmony_ci} 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic bool 13848c2ecf20Sopenharmony_cicdv_intel_dplink_set_level(struct gma_encoder *encoder, 13858c2ecf20Sopenharmony_ci uint8_t dp_train_pat) 13868c2ecf20Sopenharmony_ci{ 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci int ret; 13898c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci ret = cdv_intel_dp_aux_native_write(encoder, 13928c2ecf20Sopenharmony_ci DP_TRAINING_LANE0_SET, 13938c2ecf20Sopenharmony_ci intel_dp->train_set, 13948c2ecf20Sopenharmony_ci intel_dp->lane_count); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if (ret != intel_dp->lane_count) { 13978c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failure in setting level %d, lane_cnt= %d\n", 13988c2ecf20Sopenharmony_ci intel_dp->train_set[0], intel_dp->lane_count); 13998c2ecf20Sopenharmony_ci return false; 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ci return true; 14028c2ecf20Sopenharmony_ci} 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_cistatic void 14058c2ecf20Sopenharmony_cicdv_intel_dp_set_vswing_premph(struct gma_encoder *encoder, uint8_t signal_level) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->base.dev; 14088c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 14098c2ecf20Sopenharmony_ci struct ddi_regoff *ddi_reg; 14108c2ecf20Sopenharmony_ci int vswing, premph, index; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci if (intel_dp->output_reg == DP_B) 14138c2ecf20Sopenharmony_ci ddi_reg = &ddi_DP_train_table[0]; 14148c2ecf20Sopenharmony_ci else 14158c2ecf20Sopenharmony_ci ddi_reg = &ddi_DP_train_table[1]; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci vswing = (signal_level & DP_TRAIN_VOLTAGE_SWING_MASK); 14188c2ecf20Sopenharmony_ci premph = ((signal_level & DP_TRAIN_PRE_EMPHASIS_MASK)) >> 14198c2ecf20Sopenharmony_ci DP_TRAIN_PRE_EMPHASIS_SHIFT; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci if (vswing + premph > 3) 14228c2ecf20Sopenharmony_ci return; 14238c2ecf20Sopenharmony_ci#ifdef CDV_FAST_LINK_TRAIN 14248c2ecf20Sopenharmony_ci return; 14258c2ecf20Sopenharmony_ci#endif 14268c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Test2\n"); 14278c2ecf20Sopenharmony_ci //return ; 14288c2ecf20Sopenharmony_ci cdv_sb_reset(dev); 14298c2ecf20Sopenharmony_ci /* ;Swing voltage programming 14308c2ecf20Sopenharmony_ci ;gfx_dpio_set_reg(0xc058, 0x0505313A) */ 14318c2ecf20Sopenharmony_ci cdv_sb_write(dev, ddi_reg->VSwing5, 0x0505313A); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci /* ;gfx_dpio_set_reg(0x8154, 0x43406055) */ 14348c2ecf20Sopenharmony_ci cdv_sb_write(dev, ddi_reg->VSwing1, 0x43406055); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci /* ;gfx_dpio_set_reg(0x8148, 0x55338954) 14378c2ecf20Sopenharmony_ci * The VSwing_PreEmph table is also considered based on the vswing/premp 14388c2ecf20Sopenharmony_ci */ 14398c2ecf20Sopenharmony_ci index = (vswing + premph) * 2; 14408c2ecf20Sopenharmony_ci if (premph == 1 && vswing == 1) { 14418c2ecf20Sopenharmony_ci cdv_sb_write(dev, ddi_reg->VSwing2, 0x055738954); 14428c2ecf20Sopenharmony_ci } else 14438c2ecf20Sopenharmony_ci cdv_sb_write(dev, ddi_reg->VSwing2, dp_vswing_premph_table[index]); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci /* ;gfx_dpio_set_reg(0x814c, 0x40802040) */ 14468c2ecf20Sopenharmony_ci if ((vswing + premph) == DP_TRAIN_VOLTAGE_SWING_LEVEL_3) 14478c2ecf20Sopenharmony_ci cdv_sb_write(dev, ddi_reg->VSwing3, 0x70802040); 14488c2ecf20Sopenharmony_ci else 14498c2ecf20Sopenharmony_ci cdv_sb_write(dev, ddi_reg->VSwing3, 0x40802040); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci /* ;gfx_dpio_set_reg(0x8150, 0x2b405555) */ 14528c2ecf20Sopenharmony_ci /* cdv_sb_write(dev, ddi_reg->VSwing4, 0x2b405555); */ 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci /* ;gfx_dpio_set_reg(0x8154, 0xc3406055) */ 14558c2ecf20Sopenharmony_ci cdv_sb_write(dev, ddi_reg->VSwing1, 0xc3406055); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci /* ;Pre emphasis programming 14588c2ecf20Sopenharmony_ci * ;gfx_dpio_set_reg(0xc02c, 0x1f030040) 14598c2ecf20Sopenharmony_ci */ 14608c2ecf20Sopenharmony_ci cdv_sb_write(dev, ddi_reg->PreEmph1, 0x1f030040); 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci /* ;gfx_dpio_set_reg(0x8124, 0x00004000) */ 14638c2ecf20Sopenharmony_ci index = 2 * premph + 1; 14648c2ecf20Sopenharmony_ci cdv_sb_write(dev, ddi_reg->PreEmph2, dp_vswing_premph_table[index]); 14658c2ecf20Sopenharmony_ci return; 14668c2ecf20Sopenharmony_ci} 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci/* Enable corresponding port and start training pattern 1 */ 14708c2ecf20Sopenharmony_cistatic void 14718c2ecf20Sopenharmony_cicdv_intel_dp_start_link_train(struct gma_encoder *encoder) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->base.dev; 14748c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 14758c2ecf20Sopenharmony_ci int i; 14768c2ecf20Sopenharmony_ci uint8_t voltage; 14778c2ecf20Sopenharmony_ci bool clock_recovery = false; 14788c2ecf20Sopenharmony_ci int tries; 14798c2ecf20Sopenharmony_ci u32 reg; 14808c2ecf20Sopenharmony_ci uint32_t DP = intel_dp->DP; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci DP |= DP_PORT_EN; 14838c2ecf20Sopenharmony_ci DP &= ~DP_LINK_TRAIN_MASK; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci reg = DP; 14868c2ecf20Sopenharmony_ci reg |= DP_LINK_TRAIN_PAT_1; 14878c2ecf20Sopenharmony_ci /* Enable output, wait for it to become active */ 14888c2ecf20Sopenharmony_ci REG_WRITE(intel_dp->output_reg, reg); 14898c2ecf20Sopenharmony_ci REG_READ(intel_dp->output_reg); 14908c2ecf20Sopenharmony_ci gma_wait_for_vblank(dev); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Link config\n"); 14938c2ecf20Sopenharmony_ci /* Write the link configuration data */ 14948c2ecf20Sopenharmony_ci cdv_intel_dp_aux_native_write(encoder, DP_LINK_BW_SET, 14958c2ecf20Sopenharmony_ci intel_dp->link_configuration, 14968c2ecf20Sopenharmony_ci 2); 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci memset(intel_dp->train_set, 0, 4); 14998c2ecf20Sopenharmony_ci voltage = 0; 15008c2ecf20Sopenharmony_ci tries = 0; 15018c2ecf20Sopenharmony_ci clock_recovery = false; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Start train\n"); 15048c2ecf20Sopenharmony_ci reg = DP | DP_LINK_TRAIN_PAT_1; 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci for (;;) { 15088c2ecf20Sopenharmony_ci /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ 15098c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n", 15108c2ecf20Sopenharmony_ci intel_dp->train_set[0], 15118c2ecf20Sopenharmony_ci intel_dp->link_configuration[0], 15128c2ecf20Sopenharmony_ci intel_dp->link_configuration[1]); 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_1)) { 15158c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 1\n"); 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci cdv_intel_dp_set_vswing_premph(encoder, intel_dp->train_set[0]); 15188c2ecf20Sopenharmony_ci /* Set training pattern 1 */ 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci cdv_intel_dplink_set_level(encoder, DP_TRAINING_PATTERN_1); 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci udelay(200); 15238c2ecf20Sopenharmony_ci if (!cdv_intel_dp_get_link_status(encoder)) 15248c2ecf20Sopenharmony_ci break; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n", 15278c2ecf20Sopenharmony_ci intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2], 15288c2ecf20Sopenharmony_ci intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci if (cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { 15318c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("PT1 train is done\n"); 15328c2ecf20Sopenharmony_ci clock_recovery = true; 15338c2ecf20Sopenharmony_ci break; 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* Check to see if we've tried the max voltage */ 15378c2ecf20Sopenharmony_ci for (i = 0; i < intel_dp->lane_count; i++) 15388c2ecf20Sopenharmony_ci if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) 15398c2ecf20Sopenharmony_ci break; 15408c2ecf20Sopenharmony_ci if (i == intel_dp->lane_count) 15418c2ecf20Sopenharmony_ci break; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci /* Check to see if we've tried the same voltage 5 times */ 15448c2ecf20Sopenharmony_ci if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { 15458c2ecf20Sopenharmony_ci ++tries; 15468c2ecf20Sopenharmony_ci if (tries == 5) 15478c2ecf20Sopenharmony_ci break; 15488c2ecf20Sopenharmony_ci } else 15498c2ecf20Sopenharmony_ci tries = 0; 15508c2ecf20Sopenharmony_ci voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci /* Compute new intel_dp->train_set as requested by target */ 15538c2ecf20Sopenharmony_ci cdv_intel_get_adjust_train(encoder); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci if (!clock_recovery) { 15588c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("failure in DP patter 1 training, train set %x\n", intel_dp->train_set[0]); 15598c2ecf20Sopenharmony_ci } 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci intel_dp->DP = DP; 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cistatic void 15658c2ecf20Sopenharmony_cicdv_intel_dp_complete_link_train(struct gma_encoder *encoder) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->base.dev; 15688c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 15698c2ecf20Sopenharmony_ci int tries, cr_tries; 15708c2ecf20Sopenharmony_ci u32 reg; 15718c2ecf20Sopenharmony_ci uint32_t DP = intel_dp->DP; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci /* channel equalization */ 15748c2ecf20Sopenharmony_ci tries = 0; 15758c2ecf20Sopenharmony_ci cr_tries = 0; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 15788c2ecf20Sopenharmony_ci reg = DP | DP_LINK_TRAIN_PAT_2; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci for (;;) { 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n", 15838c2ecf20Sopenharmony_ci intel_dp->train_set[0], 15848c2ecf20Sopenharmony_ci intel_dp->link_configuration[0], 15858c2ecf20Sopenharmony_ci intel_dp->link_configuration[1]); 15868c2ecf20Sopenharmony_ci /* channel eq pattern */ 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci if (!cdv_intel_dp_set_link_train(encoder, reg, 15898c2ecf20Sopenharmony_ci DP_TRAINING_PATTERN_2)) { 15908c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 2\n"); 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci if (cr_tries > 5) { 15958c2ecf20Sopenharmony_ci DRM_ERROR("failed to train DP, aborting\n"); 15968c2ecf20Sopenharmony_ci cdv_intel_dp_link_down(encoder); 15978c2ecf20Sopenharmony_ci break; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci cdv_intel_dp_set_vswing_premph(encoder, intel_dp->train_set[0]); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci cdv_intel_dplink_set_level(encoder, DP_TRAINING_PATTERN_2); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci udelay(1000); 16058c2ecf20Sopenharmony_ci if (!cdv_intel_dp_get_link_status(encoder)) 16068c2ecf20Sopenharmony_ci break; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n", 16098c2ecf20Sopenharmony_ci intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2], 16108c2ecf20Sopenharmony_ci intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci /* Make sure clock is still ok */ 16138c2ecf20Sopenharmony_ci if (!cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { 16148c2ecf20Sopenharmony_ci cdv_intel_dp_start_link_train(encoder); 16158c2ecf20Sopenharmony_ci cr_tries++; 16168c2ecf20Sopenharmony_ci continue; 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci if (cdv_intel_channel_eq_ok(encoder)) { 16208c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("PT2 train is done\n"); 16218c2ecf20Sopenharmony_ci break; 16228c2ecf20Sopenharmony_ci } 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci /* Try 5 times, then try clock recovery if that fails */ 16258c2ecf20Sopenharmony_ci if (tries > 5) { 16268c2ecf20Sopenharmony_ci cdv_intel_dp_link_down(encoder); 16278c2ecf20Sopenharmony_ci cdv_intel_dp_start_link_train(encoder); 16288c2ecf20Sopenharmony_ci tries = 0; 16298c2ecf20Sopenharmony_ci cr_tries++; 16308c2ecf20Sopenharmony_ci continue; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci /* Compute new intel_dp->train_set as requested by target */ 16348c2ecf20Sopenharmony_ci cdv_intel_get_adjust_train(encoder); 16358c2ecf20Sopenharmony_ci ++tries; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci } 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci reg = DP | DP_LINK_TRAIN_OFF; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci REG_WRITE(intel_dp->output_reg, reg); 16428c2ecf20Sopenharmony_ci REG_READ(intel_dp->output_reg); 16438c2ecf20Sopenharmony_ci cdv_intel_dp_aux_native_write_1(encoder, 16448c2ecf20Sopenharmony_ci DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); 16458c2ecf20Sopenharmony_ci} 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_cistatic void 16488c2ecf20Sopenharmony_cicdv_intel_dp_link_down(struct gma_encoder *encoder) 16498c2ecf20Sopenharmony_ci{ 16508c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->base.dev; 16518c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 16528c2ecf20Sopenharmony_ci uint32_t DP = intel_dp->DP; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci if ((REG_READ(intel_dp->output_reg) & DP_PORT_EN) == 0) 16558c2ecf20Sopenharmony_ci return; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci { 16618c2ecf20Sopenharmony_ci DP &= ~DP_LINK_TRAIN_MASK; 16628c2ecf20Sopenharmony_ci REG_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE); 16638c2ecf20Sopenharmony_ci } 16648c2ecf20Sopenharmony_ci REG_READ(intel_dp->output_reg); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci msleep(17); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci REG_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN); 16698c2ecf20Sopenharmony_ci REG_READ(intel_dp->output_reg); 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_cistatic enum drm_connector_status cdv_dp_detect(struct gma_encoder *encoder) 16738c2ecf20Sopenharmony_ci{ 16748c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 16758c2ecf20Sopenharmony_ci enum drm_connector_status status; 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci status = connector_status_disconnected; 16788c2ecf20Sopenharmony_ci if (cdv_intel_dp_aux_native_read(encoder, 0x000, intel_dp->dpcd, 16798c2ecf20Sopenharmony_ci sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd)) 16808c2ecf20Sopenharmony_ci { 16818c2ecf20Sopenharmony_ci if (intel_dp->dpcd[DP_DPCD_REV] != 0) 16828c2ecf20Sopenharmony_ci status = connector_status_connected; 16838c2ecf20Sopenharmony_ci } 16848c2ecf20Sopenharmony_ci if (status == connector_status_connected) 16858c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n", 16868c2ecf20Sopenharmony_ci intel_dp->dpcd[0], intel_dp->dpcd[1], 16878c2ecf20Sopenharmony_ci intel_dp->dpcd[2], intel_dp->dpcd[3]); 16888c2ecf20Sopenharmony_ci return status; 16898c2ecf20Sopenharmony_ci} 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci/** 16928c2ecf20Sopenharmony_ci * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection. 16938c2ecf20Sopenharmony_ci * 16948c2ecf20Sopenharmony_ci * \return true if DP port is connected. 16958c2ecf20Sopenharmony_ci * \return false if DP port is disconnected. 16968c2ecf20Sopenharmony_ci */ 16978c2ecf20Sopenharmony_cistatic enum drm_connector_status 16988c2ecf20Sopenharmony_cicdv_intel_dp_detect(struct drm_connector *connector, bool force) 16998c2ecf20Sopenharmony_ci{ 17008c2ecf20Sopenharmony_ci struct gma_encoder *encoder = gma_attached_encoder(connector); 17018c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 17028c2ecf20Sopenharmony_ci enum drm_connector_status status; 17038c2ecf20Sopenharmony_ci struct edid *edid = NULL; 17048c2ecf20Sopenharmony_ci int edp = is_edp(encoder); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci intel_dp->has_audio = false; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci if (edp) 17098c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_on(encoder); 17108c2ecf20Sopenharmony_ci status = cdv_dp_detect(encoder); 17118c2ecf20Sopenharmony_ci if (status != connector_status_connected) { 17128c2ecf20Sopenharmony_ci if (edp) 17138c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_off(encoder); 17148c2ecf20Sopenharmony_ci return status; 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci if (intel_dp->force_audio) { 17188c2ecf20Sopenharmony_ci intel_dp->has_audio = intel_dp->force_audio > 0; 17198c2ecf20Sopenharmony_ci } else { 17208c2ecf20Sopenharmony_ci edid = drm_get_edid(connector, &intel_dp->adapter); 17218c2ecf20Sopenharmony_ci if (edid) { 17228c2ecf20Sopenharmony_ci intel_dp->has_audio = drm_detect_monitor_audio(edid); 17238c2ecf20Sopenharmony_ci kfree(edid); 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_ci } 17268c2ecf20Sopenharmony_ci if (edp) 17278c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_off(encoder); 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci return connector_status_connected; 17308c2ecf20Sopenharmony_ci} 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_cistatic int cdv_intel_dp_get_modes(struct drm_connector *connector) 17338c2ecf20Sopenharmony_ci{ 17348c2ecf20Sopenharmony_ci struct gma_encoder *intel_encoder = gma_attached_encoder(connector); 17358c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; 17368c2ecf20Sopenharmony_ci struct edid *edid = NULL; 17378c2ecf20Sopenharmony_ci int ret = 0; 17388c2ecf20Sopenharmony_ci int edp = is_edp(intel_encoder); 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci edid = drm_get_edid(connector, &intel_dp->adapter); 17428c2ecf20Sopenharmony_ci if (edid) { 17438c2ecf20Sopenharmony_ci drm_connector_update_edid_property(connector, edid); 17448c2ecf20Sopenharmony_ci ret = drm_add_edid_modes(connector, edid); 17458c2ecf20Sopenharmony_ci kfree(edid); 17468c2ecf20Sopenharmony_ci } 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci if (is_edp(intel_encoder)) { 17498c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 17508c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_off(intel_encoder); 17538c2ecf20Sopenharmony_ci if (ret) { 17548c2ecf20Sopenharmony_ci if (edp && !intel_dp->panel_fixed_mode) { 17558c2ecf20Sopenharmony_ci struct drm_display_mode *newmode; 17568c2ecf20Sopenharmony_ci list_for_each_entry(newmode, &connector->probed_modes, 17578c2ecf20Sopenharmony_ci head) { 17588c2ecf20Sopenharmony_ci if (newmode->type & DRM_MODE_TYPE_PREFERRED) { 17598c2ecf20Sopenharmony_ci intel_dp->panel_fixed_mode = 17608c2ecf20Sopenharmony_ci drm_mode_duplicate(dev, newmode); 17618c2ecf20Sopenharmony_ci break; 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci } 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci return ret; 17678c2ecf20Sopenharmony_ci } 17688c2ecf20Sopenharmony_ci if (!intel_dp->panel_fixed_mode && dev_priv->lfp_lvds_vbt_mode) { 17698c2ecf20Sopenharmony_ci intel_dp->panel_fixed_mode = 17708c2ecf20Sopenharmony_ci drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); 17718c2ecf20Sopenharmony_ci if (intel_dp->panel_fixed_mode) { 17728c2ecf20Sopenharmony_ci intel_dp->panel_fixed_mode->type |= 17738c2ecf20Sopenharmony_ci DRM_MODE_TYPE_PREFERRED; 17748c2ecf20Sopenharmony_ci } 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci if (intel_dp->panel_fixed_mode != NULL) { 17778c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 17788c2ecf20Sopenharmony_ci mode = drm_mode_duplicate(dev, intel_dp->panel_fixed_mode); 17798c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 17808c2ecf20Sopenharmony_ci return 1; 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci return ret; 17858c2ecf20Sopenharmony_ci} 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cistatic bool 17888c2ecf20Sopenharmony_cicdv_intel_dp_detect_audio(struct drm_connector *connector) 17898c2ecf20Sopenharmony_ci{ 17908c2ecf20Sopenharmony_ci struct gma_encoder *encoder = gma_attached_encoder(connector); 17918c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 17928c2ecf20Sopenharmony_ci struct edid *edid; 17938c2ecf20Sopenharmony_ci bool has_audio = false; 17948c2ecf20Sopenharmony_ci int edp = is_edp(encoder); 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci if (edp) 17978c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_on(encoder); 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci edid = drm_get_edid(connector, &intel_dp->adapter); 18008c2ecf20Sopenharmony_ci if (edid) { 18018c2ecf20Sopenharmony_ci has_audio = drm_detect_monitor_audio(edid); 18028c2ecf20Sopenharmony_ci kfree(edid); 18038c2ecf20Sopenharmony_ci } 18048c2ecf20Sopenharmony_ci if (edp) 18058c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_off(encoder); 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci return has_audio; 18088c2ecf20Sopenharmony_ci} 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_cistatic int 18118c2ecf20Sopenharmony_cicdv_intel_dp_set_property(struct drm_connector *connector, 18128c2ecf20Sopenharmony_ci struct drm_property *property, 18138c2ecf20Sopenharmony_ci uint64_t val) 18148c2ecf20Sopenharmony_ci{ 18158c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = connector->dev->dev_private; 18168c2ecf20Sopenharmony_ci struct gma_encoder *encoder = gma_attached_encoder(connector); 18178c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = encoder->dev_priv; 18188c2ecf20Sopenharmony_ci int ret; 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci ret = drm_object_property_set_value(&connector->base, property, val); 18218c2ecf20Sopenharmony_ci if (ret) 18228c2ecf20Sopenharmony_ci return ret; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci if (property == dev_priv->force_audio_property) { 18258c2ecf20Sopenharmony_ci int i = val; 18268c2ecf20Sopenharmony_ci bool has_audio; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci if (i == intel_dp->force_audio) 18298c2ecf20Sopenharmony_ci return 0; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci intel_dp->force_audio = i; 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci if (i == 0) 18348c2ecf20Sopenharmony_ci has_audio = cdv_intel_dp_detect_audio(connector); 18358c2ecf20Sopenharmony_ci else 18368c2ecf20Sopenharmony_ci has_audio = i > 0; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci if (has_audio == intel_dp->has_audio) 18398c2ecf20Sopenharmony_ci return 0; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci intel_dp->has_audio = has_audio; 18428c2ecf20Sopenharmony_ci goto done; 18438c2ecf20Sopenharmony_ci } 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci if (property == dev_priv->broadcast_rgb_property) { 18468c2ecf20Sopenharmony_ci if (val == !!intel_dp->color_range) 18478c2ecf20Sopenharmony_ci return 0; 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci intel_dp->color_range = val ? DP_COLOR_RANGE_16_235 : 0; 18508c2ecf20Sopenharmony_ci goto done; 18518c2ecf20Sopenharmony_ci } 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci return -EINVAL; 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_cidone: 18568c2ecf20Sopenharmony_ci if (encoder->base.crtc) { 18578c2ecf20Sopenharmony_ci struct drm_crtc *crtc = encoder->base.crtc; 18588c2ecf20Sopenharmony_ci drm_crtc_helper_set_mode(crtc, &crtc->mode, 18598c2ecf20Sopenharmony_ci crtc->x, crtc->y, 18608c2ecf20Sopenharmony_ci crtc->primary->fb); 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci return 0; 18648c2ecf20Sopenharmony_ci} 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_cistatic void 18678c2ecf20Sopenharmony_cicdv_intel_dp_destroy(struct drm_connector *connector) 18688c2ecf20Sopenharmony_ci{ 18698c2ecf20Sopenharmony_ci struct gma_encoder *gma_encoder = gma_attached_encoder(connector); 18708c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp = gma_encoder->dev_priv; 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci if (is_edp(gma_encoder)) { 18738c2ecf20Sopenharmony_ci /* cdv_intel_panel_destroy_backlight(connector->dev); */ 18748c2ecf20Sopenharmony_ci kfree(intel_dp->panel_fixed_mode); 18758c2ecf20Sopenharmony_ci intel_dp->panel_fixed_mode = NULL; 18768c2ecf20Sopenharmony_ci } 18778c2ecf20Sopenharmony_ci i2c_del_adapter(&intel_dp->adapter); 18788c2ecf20Sopenharmony_ci drm_connector_unregister(connector); 18798c2ecf20Sopenharmony_ci drm_connector_cleanup(connector); 18808c2ecf20Sopenharmony_ci kfree(connector); 18818c2ecf20Sopenharmony_ci} 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs cdv_intel_dp_helper_funcs = { 18848c2ecf20Sopenharmony_ci .dpms = cdv_intel_dp_dpms, 18858c2ecf20Sopenharmony_ci .mode_fixup = cdv_intel_dp_mode_fixup, 18868c2ecf20Sopenharmony_ci .prepare = cdv_intel_dp_prepare, 18878c2ecf20Sopenharmony_ci .mode_set = cdv_intel_dp_mode_set, 18888c2ecf20Sopenharmony_ci .commit = cdv_intel_dp_commit, 18898c2ecf20Sopenharmony_ci}; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs cdv_intel_dp_connector_funcs = { 18928c2ecf20Sopenharmony_ci .dpms = drm_helper_connector_dpms, 18938c2ecf20Sopenharmony_ci .detect = cdv_intel_dp_detect, 18948c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 18958c2ecf20Sopenharmony_ci .set_property = cdv_intel_dp_set_property, 18968c2ecf20Sopenharmony_ci .destroy = cdv_intel_dp_destroy, 18978c2ecf20Sopenharmony_ci}; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs cdv_intel_dp_connector_helper_funcs = { 19008c2ecf20Sopenharmony_ci .get_modes = cdv_intel_dp_get_modes, 19018c2ecf20Sopenharmony_ci .mode_valid = cdv_intel_dp_mode_valid, 19028c2ecf20Sopenharmony_ci .best_encoder = gma_best_encoder, 19038c2ecf20Sopenharmony_ci}; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_cistatic void cdv_intel_dp_add_properties(struct drm_connector *connector) 19068c2ecf20Sopenharmony_ci{ 19078c2ecf20Sopenharmony_ci cdv_intel_attach_force_audio_property(connector); 19088c2ecf20Sopenharmony_ci cdv_intel_attach_broadcast_rgb_property(connector); 19098c2ecf20Sopenharmony_ci} 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci/* check the VBT to see whether the eDP is on DP-D port */ 19128c2ecf20Sopenharmony_cistatic bool cdv_intel_dpc_is_edp(struct drm_device *dev) 19138c2ecf20Sopenharmony_ci{ 19148c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 19158c2ecf20Sopenharmony_ci struct child_device_config *p_child; 19168c2ecf20Sopenharmony_ci int i; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci if (!dev_priv->child_dev_num) 19198c2ecf20Sopenharmony_ci return false; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci for (i = 0; i < dev_priv->child_dev_num; i++) { 19228c2ecf20Sopenharmony_ci p_child = dev_priv->child_dev + i; 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci if (p_child->dvo_port == PORT_IDPC && 19258c2ecf20Sopenharmony_ci p_child->device_type == DEVICE_TYPE_eDP) 19268c2ecf20Sopenharmony_ci return true; 19278c2ecf20Sopenharmony_ci } 19288c2ecf20Sopenharmony_ci return false; 19298c2ecf20Sopenharmony_ci} 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci/* Cedarview display clock gating 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci We need this disable dot get correct behaviour while enabling 19348c2ecf20Sopenharmony_ci DP/eDP. TODO - investigate if we can turn it back to normality 19358c2ecf20Sopenharmony_ci after enabling */ 19368c2ecf20Sopenharmony_cistatic void cdv_disable_intel_clock_gating(struct drm_device *dev) 19378c2ecf20Sopenharmony_ci{ 19388c2ecf20Sopenharmony_ci u32 reg_value; 19398c2ecf20Sopenharmony_ci reg_value = REG_READ(DSPCLK_GATE_D); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci reg_value |= (DPUNIT_PIPEB_GATE_DISABLE | 19428c2ecf20Sopenharmony_ci DPUNIT_PIPEA_GATE_DISABLE | 19438c2ecf20Sopenharmony_ci DPCUNIT_CLOCK_GATE_DISABLE | 19448c2ecf20Sopenharmony_ci DPLSUNIT_CLOCK_GATE_DISABLE | 19458c2ecf20Sopenharmony_ci DPOUNIT_CLOCK_GATE_DISABLE | 19468c2ecf20Sopenharmony_ci DPIOUNIT_CLOCK_GATE_DISABLE); 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci REG_WRITE(DSPCLK_GATE_D, reg_value); 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci udelay(500); 19518c2ecf20Sopenharmony_ci} 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_civoid 19548c2ecf20Sopenharmony_cicdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) 19558c2ecf20Sopenharmony_ci{ 19568c2ecf20Sopenharmony_ci struct gma_encoder *gma_encoder; 19578c2ecf20Sopenharmony_ci struct gma_connector *gma_connector; 19588c2ecf20Sopenharmony_ci struct drm_connector *connector; 19598c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 19608c2ecf20Sopenharmony_ci struct cdv_intel_dp *intel_dp; 19618c2ecf20Sopenharmony_ci const char *name = NULL; 19628c2ecf20Sopenharmony_ci int type = DRM_MODE_CONNECTOR_DisplayPort; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); 19658c2ecf20Sopenharmony_ci if (!gma_encoder) 19668c2ecf20Sopenharmony_ci return; 19678c2ecf20Sopenharmony_ci gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); 19688c2ecf20Sopenharmony_ci if (!gma_connector) 19698c2ecf20Sopenharmony_ci goto err_connector; 19708c2ecf20Sopenharmony_ci intel_dp = kzalloc(sizeof(struct cdv_intel_dp), GFP_KERNEL); 19718c2ecf20Sopenharmony_ci if (!intel_dp) 19728c2ecf20Sopenharmony_ci goto err_priv; 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci if ((output_reg == DP_C) && cdv_intel_dpc_is_edp(dev)) 19758c2ecf20Sopenharmony_ci type = DRM_MODE_CONNECTOR_eDP; 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci connector = &gma_connector->base; 19788c2ecf20Sopenharmony_ci encoder = &gma_encoder->base; 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, type); 19818c2ecf20Sopenharmony_ci drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci gma_connector_attach_encoder(gma_connector, gma_encoder); 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci if (type == DRM_MODE_CONNECTOR_DisplayPort) 19868c2ecf20Sopenharmony_ci gma_encoder->type = INTEL_OUTPUT_DISPLAYPORT; 19878c2ecf20Sopenharmony_ci else 19888c2ecf20Sopenharmony_ci gma_encoder->type = INTEL_OUTPUT_EDP; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci gma_encoder->dev_priv=intel_dp; 19928c2ecf20Sopenharmony_ci intel_dp->encoder = gma_encoder; 19938c2ecf20Sopenharmony_ci intel_dp->output_reg = output_reg; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci drm_encoder_helper_add(encoder, &cdv_intel_dp_helper_funcs); 19968c2ecf20Sopenharmony_ci drm_connector_helper_add(connector, &cdv_intel_dp_connector_helper_funcs); 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_HPD; 19998c2ecf20Sopenharmony_ci connector->interlace_allowed = false; 20008c2ecf20Sopenharmony_ci connector->doublescan_allowed = false; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci drm_connector_register(connector); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci /* Set up the DDC bus. */ 20058c2ecf20Sopenharmony_ci switch (output_reg) { 20068c2ecf20Sopenharmony_ci case DP_B: 20078c2ecf20Sopenharmony_ci name = "DPDDC-B"; 20088c2ecf20Sopenharmony_ci gma_encoder->ddi_select = (DP_MASK | DDI0_SELECT); 20098c2ecf20Sopenharmony_ci break; 20108c2ecf20Sopenharmony_ci case DP_C: 20118c2ecf20Sopenharmony_ci name = "DPDDC-C"; 20128c2ecf20Sopenharmony_ci gma_encoder->ddi_select = (DP_MASK | DDI1_SELECT); 20138c2ecf20Sopenharmony_ci break; 20148c2ecf20Sopenharmony_ci } 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci cdv_disable_intel_clock_gating(dev); 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci cdv_intel_dp_i2c_init(gma_connector, gma_encoder, name); 20198c2ecf20Sopenharmony_ci /* FIXME:fail check */ 20208c2ecf20Sopenharmony_ci cdv_intel_dp_add_properties(connector); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci if (is_edp(gma_encoder)) { 20238c2ecf20Sopenharmony_ci int ret; 20248c2ecf20Sopenharmony_ci struct edp_power_seq cur; 20258c2ecf20Sopenharmony_ci u32 pp_on, pp_off, pp_div; 20268c2ecf20Sopenharmony_ci u32 pwm_ctrl; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci pp_on = REG_READ(PP_CONTROL); 20298c2ecf20Sopenharmony_ci pp_on &= ~PANEL_UNLOCK_MASK; 20308c2ecf20Sopenharmony_ci pp_on |= PANEL_UNLOCK_REGS; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci REG_WRITE(PP_CONTROL, pp_on); 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci pwm_ctrl = REG_READ(BLC_PWM_CTL2); 20358c2ecf20Sopenharmony_ci pwm_ctrl |= PWM_PIPE_B; 20368c2ecf20Sopenharmony_ci REG_WRITE(BLC_PWM_CTL2, pwm_ctrl); 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci pp_on = REG_READ(PP_ON_DELAYS); 20398c2ecf20Sopenharmony_ci pp_off = REG_READ(PP_OFF_DELAYS); 20408c2ecf20Sopenharmony_ci pp_div = REG_READ(PP_DIVISOR); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci /* Pull timing values out of registers */ 20438c2ecf20Sopenharmony_ci cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >> 20448c2ecf20Sopenharmony_ci PANEL_POWER_UP_DELAY_SHIFT; 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >> 20478c2ecf20Sopenharmony_ci PANEL_LIGHT_ON_DELAY_SHIFT; 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >> 20508c2ecf20Sopenharmony_ci PANEL_LIGHT_OFF_DELAY_SHIFT; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >> 20538c2ecf20Sopenharmony_ci PANEL_POWER_DOWN_DELAY_SHIFT; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >> 20568c2ecf20Sopenharmony_ci PANEL_POWER_CYCLE_DELAY_SHIFT); 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", 20598c2ecf20Sopenharmony_ci cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12); 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci intel_dp->panel_power_up_delay = cur.t1_t3 / 10; 20638c2ecf20Sopenharmony_ci intel_dp->backlight_on_delay = cur.t8 / 10; 20648c2ecf20Sopenharmony_ci intel_dp->backlight_off_delay = cur.t9 / 10; 20658c2ecf20Sopenharmony_ci intel_dp->panel_power_down_delay = cur.t10 / 10; 20668c2ecf20Sopenharmony_ci intel_dp->panel_power_cycle_delay = (cur.t11_t12 - 1) * 100; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n", 20698c2ecf20Sopenharmony_ci intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay, 20708c2ecf20Sopenharmony_ci intel_dp->panel_power_cycle_delay); 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", 20738c2ecf20Sopenharmony_ci intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_on(gma_encoder); 20778c2ecf20Sopenharmony_ci ret = cdv_intel_dp_aux_native_read(gma_encoder, DP_DPCD_REV, 20788c2ecf20Sopenharmony_ci intel_dp->dpcd, 20798c2ecf20Sopenharmony_ci sizeof(intel_dp->dpcd)); 20808c2ecf20Sopenharmony_ci cdv_intel_edp_panel_vdd_off(gma_encoder); 20818c2ecf20Sopenharmony_ci if (ret <= 0) { 20828c2ecf20Sopenharmony_ci /* if this fails, presume the device is a ghost */ 20838c2ecf20Sopenharmony_ci DRM_INFO("failed to retrieve link info, disabling eDP\n"); 20848c2ecf20Sopenharmony_ci drm_encoder_cleanup(encoder); 20858c2ecf20Sopenharmony_ci cdv_intel_dp_destroy(connector); 20868c2ecf20Sopenharmony_ci goto err_connector; 20878c2ecf20Sopenharmony_ci } else { 20888c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n", 20898c2ecf20Sopenharmony_ci intel_dp->dpcd[0], intel_dp->dpcd[1], 20908c2ecf20Sopenharmony_ci intel_dp->dpcd[2], intel_dp->dpcd[3]); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci } 20938c2ecf20Sopenharmony_ci /* The CDV reference driver moves pnale backlight setup into the displays that 20948c2ecf20Sopenharmony_ci have a backlight: this is a good idea and one we should probably adopt, however 20958c2ecf20Sopenharmony_ci we need to migrate all the drivers before we can do that */ 20968c2ecf20Sopenharmony_ci /*cdv_intel_panel_setup_backlight(dev); */ 20978c2ecf20Sopenharmony_ci } 20988c2ecf20Sopenharmony_ci return; 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_cierr_priv: 21018c2ecf20Sopenharmony_ci kfree(gma_connector); 21028c2ecf20Sopenharmony_cierr_connector: 21038c2ecf20Sopenharmony_ci kfree(gma_encoder); 21048c2ecf20Sopenharmony_ci} 2105