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