dpaux.c 14.4 KB
Newer Older
T
Thierry Reding 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Copyright (C) 2013 NVIDIA Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/regulator/consumer.h>
18
#include <linux/workqueue.h>
T
Thierry Reding 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

#include <drm/drm_dp_helper.h>
#include <drm/drm_panel.h>

#include "dpaux.h"
#include "drm.h"

static DEFINE_MUTEX(dpaux_lock);
static LIST_HEAD(dpaux_list);

struct tegra_dpaux {
	struct drm_dp_aux aux;
	struct device *dev;

	void __iomem *regs;
	int irq;

	struct tegra_output *output;

	struct reset_control *rst;
	struct clk *clk_parent;
	struct clk *clk;

	struct regulator *vdd;

	struct completion complete;
45
	struct work_struct work;
T
Thierry Reding 已提交
46 47 48 49 50 51 52 53
	struct list_head list;
};

static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux)
{
	return container_of(aux, struct tegra_dpaux, aux);
}

54 55 56 57 58
static inline struct tegra_dpaux *work_to_dpaux(struct work_struct *work)
{
	return container_of(work, struct tegra_dpaux, work);
}

59 60
static inline u32 tegra_dpaux_readl(struct tegra_dpaux *dpaux,
				    unsigned long offset)
T
Thierry Reding 已提交
61 62 63 64 65
{
	return readl(dpaux->regs + (offset << 2));
}

static inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux,
66
				      u32 value, unsigned long offset)
T
Thierry Reding 已提交
67 68 69 70 71 72 73 74 75
{
	writel(value, dpaux->regs + (offset << 2));
}

static void tegra_dpaux_write_fifo(struct tegra_dpaux *dpaux, const u8 *buffer,
				   size_t size)
{
	size_t i, j;

76 77
	for (i = 0; i < DIV_ROUND_UP(size, 4); i++) {
		size_t num = min_t(size_t, size - i * 4, 4);
78
		u32 value = 0;
T
Thierry Reding 已提交
79 80

		for (j = 0; j < num; j++)
81
			value |= buffer[i * 4 + j] << (j * 8);
T
Thierry Reding 已提交
82

83
		tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXDATA_WRITE(i));
T
Thierry Reding 已提交
84 85 86 87 88 89 90 91
	}
}

static void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer,
				  size_t size)
{
	size_t i, j;

92 93
	for (i = 0; i < DIV_ROUND_UP(size, 4); i++) {
		size_t num = min_t(size_t, size - i * 4, 4);
94
		u32 value;
T
Thierry Reding 已提交
95

96
		value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXDATA_READ(i));
T
Thierry Reding 已提交
97 98

		for (j = 0; j < num; j++)
99
			buffer[i * 4 + j] = value >> (j * 8);
T
Thierry Reding 已提交
100 101 102 103 104 105 106 107 108 109
	}
}

static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
				    struct drm_dp_aux_msg *msg)
{
	unsigned long timeout = msecs_to_jiffies(250);
	struct tegra_dpaux *dpaux = to_dpaux(aux);
	unsigned long status;
	ssize_t ret = 0;
110
	u32 value;
T
Thierry Reding 已提交
111

112 113
	/* Tegra has 4x4 byte DP AUX transmit and receive FIFOs. */
	if (msg->size > 16)
T
Thierry Reding 已提交
114 115
		return -EINVAL;

116 117 118 119 120 121
	/*
	 * Allow zero-sized messages only for I2C, in which case they specify
	 * address-only transactions.
	 */
	if (msg->size < 1) {
		switch (msg->request & ~DP_AUX_I2C_MOT) {
122
		case DP_AUX_I2C_WRITE_STATUS_UPDATE:
123 124 125 126 127 128 129 130 131 132 133 134
		case DP_AUX_I2C_WRITE:
		case DP_AUX_I2C_READ:
			value = DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY;
			break;

		default:
			return -EINVAL;
		}
	} else {
		/* For non-zero-sized messages, set the CMDLEN field. */
		value = DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1);
	}
T
Thierry Reding 已提交
135 136 137 138

	switch (msg->request & ~DP_AUX_I2C_MOT) {
	case DP_AUX_I2C_WRITE:
		if (msg->request & DP_AUX_I2C_MOT)
139
			value |= DPAUX_DP_AUXCTL_CMD_MOT_WR;
T
Thierry Reding 已提交
140
		else
141
			value |= DPAUX_DP_AUXCTL_CMD_I2C_WR;
T
Thierry Reding 已提交
142 143 144 145 146

		break;

	case DP_AUX_I2C_READ:
		if (msg->request & DP_AUX_I2C_MOT)
147
			value |= DPAUX_DP_AUXCTL_CMD_MOT_RD;
T
Thierry Reding 已提交
148
		else
149
			value |= DPAUX_DP_AUXCTL_CMD_I2C_RD;
T
Thierry Reding 已提交
150 151 152

		break;

153
	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
T
Thierry Reding 已提交
154
		if (msg->request & DP_AUX_I2C_MOT)
155
			value |= DPAUX_DP_AUXCTL_CMD_MOT_RQ;
T
Thierry Reding 已提交
156
		else
157
			value |= DPAUX_DP_AUXCTL_CMD_I2C_RQ;
T
Thierry Reding 已提交
158 159 160 161

		break;

	case DP_AUX_NATIVE_WRITE:
162
		value |= DPAUX_DP_AUXCTL_CMD_AUX_WR;
T
Thierry Reding 已提交
163 164 165
		break;

	case DP_AUX_NATIVE_READ:
166
		value |= DPAUX_DP_AUXCTL_CMD_AUX_RD;
T
Thierry Reding 已提交
167 168 169 170 171 172
		break;

	default:
		return -EINVAL;
	}

173
	tegra_dpaux_writel(dpaux, msg->address, DPAUX_DP_AUXADDR);
T
Thierry Reding 已提交
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
	tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL);

	if ((msg->request & DP_AUX_I2C_READ) == 0) {
		tegra_dpaux_write_fifo(dpaux, msg->buffer, msg->size);
		ret = msg->size;
	}

	/* start transaction */
	value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXCTL);
	value |= DPAUX_DP_AUXCTL_TRANSACTREQ;
	tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL);

	status = wait_for_completion_timeout(&dpaux->complete, timeout);
	if (!status)
		return -ETIMEDOUT;

	/* read status and clear errors */
	value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT);
	tegra_dpaux_writel(dpaux, 0xf00, DPAUX_DP_AUXSTAT);

	if (value & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR)
		return -ETIMEDOUT;

	if ((value & DPAUX_DP_AUXSTAT_RX_ERROR) ||
	    (value & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR) ||
	    (value & DPAUX_DP_AUXSTAT_NO_STOP_ERROR))
		return -EIO;

	switch ((value & DPAUX_DP_AUXSTAT_REPLY_TYPE_MASK) >> 16) {
	case 0x00:
		msg->reply = DP_AUX_NATIVE_REPLY_ACK;
		break;

	case 0x01:
		msg->reply = DP_AUX_NATIVE_REPLY_NACK;
		break;

	case 0x02:
		msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
		break;

	case 0x04:
		msg->reply = DP_AUX_I2C_REPLY_NACK;
		break;

	case 0x08:
		msg->reply = DP_AUX_I2C_REPLY_DEFER;
		break;
	}

224
	if ((msg->size > 0) && (msg->reply == DP_AUX_NATIVE_REPLY_ACK)) {
T
Thierry Reding 已提交
225 226 227 228 229 230 231 232 233 234 235 236 237 238
		if (msg->request & DP_AUX_I2C_READ) {
			size_t count = value & DPAUX_DP_AUXSTAT_REPLY_MASK;

			if (WARN_ON(count != msg->size))
				count = min_t(size_t, count, msg->size);

			tegra_dpaux_read_fifo(dpaux, msg->buffer, count);
			ret = count;
		}
	}

	return ret;
}

239 240 241 242 243 244 245 246
static void tegra_dpaux_hotplug(struct work_struct *work)
{
	struct tegra_dpaux *dpaux = work_to_dpaux(work);

	if (dpaux->output)
		drm_helper_hpd_irq_event(dpaux->output->connector.dev);
}

T
Thierry Reding 已提交
247 248 249 250
static irqreturn_t tegra_dpaux_irq(int irq, void *data)
{
	struct tegra_dpaux *dpaux = data;
	irqreturn_t ret = IRQ_HANDLED;
251
	u32 value;
T
Thierry Reding 已提交
252 253 254 255 256

	/* clear interrupts */
	value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX);
	tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX);

257 258
	if (value & (DPAUX_INTR_PLUG_EVENT | DPAUX_INTR_UNPLUG_EVENT))
		schedule_work(&dpaux->work);
T
Thierry Reding 已提交
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273

	if (value & DPAUX_INTR_IRQ_EVENT) {
		/* TODO: handle this */
	}

	if (value & DPAUX_INTR_AUX_DONE)
		complete(&dpaux->complete);

	return ret;
}

static int tegra_dpaux_probe(struct platform_device *pdev)
{
	struct tegra_dpaux *dpaux;
	struct resource *regs;
274
	u32 value;
T
Thierry Reding 已提交
275 276 277 278 279 280
	int err;

	dpaux = devm_kzalloc(&pdev->dev, sizeof(*dpaux), GFP_KERNEL);
	if (!dpaux)
		return -ENOMEM;

281
	INIT_WORK(&dpaux->work, tegra_dpaux_hotplug);
T
Thierry Reding 已提交
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
	init_completion(&dpaux->complete);
	INIT_LIST_HEAD(&dpaux->list);
	dpaux->dev = &pdev->dev;

	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	dpaux->regs = devm_ioremap_resource(&pdev->dev, regs);
	if (IS_ERR(dpaux->regs))
		return PTR_ERR(dpaux->regs);

	dpaux->irq = platform_get_irq(pdev, 0);
	if (dpaux->irq < 0) {
		dev_err(&pdev->dev, "failed to get IRQ\n");
		return -ENXIO;
	}

	dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux");
298 299 300
	if (IS_ERR(dpaux->rst)) {
		dev_err(&pdev->dev, "failed to get reset control: %ld\n",
			PTR_ERR(dpaux->rst));
T
Thierry Reding 已提交
301
		return PTR_ERR(dpaux->rst);
302
	}
T
Thierry Reding 已提交
303 304

	dpaux->clk = devm_clk_get(&pdev->dev, NULL);
305 306 307
	if (IS_ERR(dpaux->clk)) {
		dev_err(&pdev->dev, "failed to get module clock: %ld\n",
			PTR_ERR(dpaux->clk));
T
Thierry Reding 已提交
308
		return PTR_ERR(dpaux->clk);
309
	}
T
Thierry Reding 已提交
310 311

	err = clk_prepare_enable(dpaux->clk);
312 313 314
	if (err < 0) {
		dev_err(&pdev->dev, "failed to enable module clock: %d\n",
			err);
T
Thierry Reding 已提交
315
		return err;
316
	}
T
Thierry Reding 已提交
317 318 319 320

	reset_control_deassert(dpaux->rst);

	dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent");
321 322 323
	if (IS_ERR(dpaux->clk_parent)) {
		dev_err(&pdev->dev, "failed to get parent clock: %ld\n",
			PTR_ERR(dpaux->clk_parent));
324 325
		err = PTR_ERR(dpaux->clk_parent);
		goto assert_reset;
326
	}
T
Thierry Reding 已提交
327 328

	err = clk_prepare_enable(dpaux->clk_parent);
329 330 331
	if (err < 0) {
		dev_err(&pdev->dev, "failed to enable parent clock: %d\n",
			err);
332
		goto assert_reset;
333
	}
T
Thierry Reding 已提交
334 335 336 337 338

	err = clk_set_rate(dpaux->clk_parent, 270000000);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n",
			err);
339
		goto disable_parent_clk;
T
Thierry Reding 已提交
340 341 342
	}

	dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd");
343 344 345
	if (IS_ERR(dpaux->vdd)) {
		dev_err(&pdev->dev, "failed to get VDD supply: %ld\n",
			PTR_ERR(dpaux->vdd));
346 347
		err = PTR_ERR(dpaux->vdd);
		goto disable_parent_clk;
348
	}
T
Thierry Reding 已提交
349 350 351 352 353 354

	err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0,
			       dev_name(dpaux->dev), dpaux);
	if (err < 0) {
		dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n",
			dpaux->irq, err);
355
		goto disable_parent_clk;
T
Thierry Reding 已提交
356 357
	}

358 359
	disable_irq(dpaux->irq);

T
Thierry Reding 已提交
360 361 362
	dpaux->aux.transfer = tegra_dpaux_transfer;
	dpaux->aux.dev = &pdev->dev;

363
	err = drm_dp_aux_register(&dpaux->aux);
T
Thierry Reding 已提交
364
	if (err < 0)
365
		goto disable_parent_clk;
T
Thierry Reding 已提交
366

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
	/*
	 * Assume that by default the DPAUX/I2C pads will be used for HDMI,
	 * so power them up and configure them in I2C mode.
	 *
	 * The DPAUX code paths reconfigure the pads in AUX mode, but there
	 * is no possibility to perform the I2C mode configuration in the
	 * HDMI path.
	 */
	value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
	value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);

	value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_PADCTL);
	value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV |
		DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV |
		DPAUX_HYBRID_PADCTL_MODE_I2C;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL);

T
Thierry Reding 已提交
385 386 387 388 389 390 391 392 393 394 395 396 397
	/* enable and clear all interrupts */
	value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT |
		DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT;
	tegra_dpaux_writel(dpaux, value, DPAUX_INTR_EN_AUX);
	tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX);

	mutex_lock(&dpaux_lock);
	list_add_tail(&dpaux->list, &dpaux_list);
	mutex_unlock(&dpaux_lock);

	platform_set_drvdata(pdev, dpaux);

	return 0;
398 399 400 401 402 403 404 405

disable_parent_clk:
	clk_disable_unprepare(dpaux->clk_parent);
assert_reset:
	reset_control_assert(dpaux->rst);
	clk_disable_unprepare(dpaux->clk);

	return err;
T
Thierry Reding 已提交
406 407 408 409 410
}

static int tegra_dpaux_remove(struct platform_device *pdev)
{
	struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
411 412 413 414 415 416
	u32 value;

	/* make sure pads are powered down when not in use */
	value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
	value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
T
Thierry Reding 已提交
417

418
	drm_dp_aux_unregister(&dpaux->aux);
T
Thierry Reding 已提交
419 420 421 422 423

	mutex_lock(&dpaux_lock);
	list_del(&dpaux->list);
	mutex_unlock(&dpaux_lock);

424 425
	cancel_work_sync(&dpaux->work);

T
Thierry Reding 已提交
426 427 428 429 430 431 432 433
	clk_disable_unprepare(dpaux->clk_parent);
	reset_control_assert(dpaux->rst);
	clk_disable_unprepare(dpaux->clk);

	return 0;
}

static const struct of_device_id tegra_dpaux_of_match[] = {
434
	{ .compatible = "nvidia,tegra210-dpaux", },
T
Thierry Reding 已提交
435 436 437
	{ .compatible = "nvidia,tegra124-dpaux", },
	{ },
};
438
MODULE_DEVICE_TABLE(of, tegra_dpaux_of_match);
T
Thierry Reding 已提交
439 440 441 442 443 444 445 446 447 448

struct platform_driver tegra_dpaux_driver = {
	.driver = {
		.name = "tegra-dpaux",
		.of_match_table = tegra_dpaux_of_match,
	},
	.probe = tegra_dpaux_probe,
	.remove = tegra_dpaux_remove,
};

449
struct drm_dp_aux *drm_dp_aux_find_by_of_node(struct device_node *np)
T
Thierry Reding 已提交
450 451 452 453 454 455 456 457
{
	struct tegra_dpaux *dpaux;

	mutex_lock(&dpaux_lock);

	list_for_each_entry(dpaux, &dpaux_list, list)
		if (np == dpaux->dev->of_node) {
			mutex_unlock(&dpaux_lock);
458
			return &dpaux->aux;
T
Thierry Reding 已提交
459 460 461 462 463 464 465
		}

	mutex_unlock(&dpaux_lock);

	return NULL;
}

466
int drm_dp_aux_attach(struct drm_dp_aux *aux, struct tegra_output *output)
T
Thierry Reding 已提交
467
{
468
	struct tegra_dpaux *dpaux = to_dpaux(aux);
T
Thierry Reding 已提交
469 470 471
	unsigned long timeout;
	int err;

472
	output->connector.polled = DRM_CONNECTOR_POLL_HPD;
T
Thierry Reding 已提交
473 474 475 476 477 478 479 480 481 482 483
	dpaux->output = output;

	err = regulator_enable(dpaux->vdd);
	if (err < 0)
		return err;

	timeout = jiffies + msecs_to_jiffies(250);

	while (time_before(jiffies, timeout)) {
		enum drm_connector_status status;

484
		status = drm_dp_aux_detect(aux);
485 486
		if (status == connector_status_connected) {
			enable_irq(dpaux->irq);
T
Thierry Reding 已提交
487
			return 0;
488
		}
T
Thierry Reding 已提交
489 490 491 492 493 494 495

		usleep_range(1000, 2000);
	}

	return -ETIMEDOUT;
}

496
int drm_dp_aux_detach(struct drm_dp_aux *aux)
T
Thierry Reding 已提交
497
{
498
	struct tegra_dpaux *dpaux = to_dpaux(aux);
T
Thierry Reding 已提交
499 500 501
	unsigned long timeout;
	int err;

502 503
	disable_irq(dpaux->irq);

T
Thierry Reding 已提交
504 505 506 507 508 509 510 511 512
	err = regulator_disable(dpaux->vdd);
	if (err < 0)
		return err;

	timeout = jiffies + msecs_to_jiffies(250);

	while (time_before(jiffies, timeout)) {
		enum drm_connector_status status;

513
		status = drm_dp_aux_detect(aux);
T
Thierry Reding 已提交
514 515 516 517 518 519 520 521 522 523 524
		if (status == connector_status_disconnected) {
			dpaux->output = NULL;
			return 0;
		}

		usleep_range(1000, 2000);
	}

	return -ETIMEDOUT;
}

525
enum drm_connector_status drm_dp_aux_detect(struct drm_dp_aux *aux)
T
Thierry Reding 已提交
526
{
527
	struct tegra_dpaux *dpaux = to_dpaux(aux);
528
	u32 value;
T
Thierry Reding 已提交
529 530 531 532 533 534 535 536 537

	value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT);

	if (value & DPAUX_DP_AUXSTAT_HPD_STATUS)
		return connector_status_connected;

	return connector_status_disconnected;
}

538
int drm_dp_aux_enable(struct drm_dp_aux *aux)
T
Thierry Reding 已提交
539
{
540
	struct tegra_dpaux *dpaux = to_dpaux(aux);
541
	u32 value;
T
Thierry Reding 已提交
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556

	value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) |
		DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) |
		DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) |
		DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV |
		DPAUX_HYBRID_PADCTL_MODE_AUX;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL);

	value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
	value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);

	return 0;
}

557
int drm_dp_aux_disable(struct drm_dp_aux *aux)
T
Thierry Reding 已提交
558
{
559
	struct tegra_dpaux *dpaux = to_dpaux(aux);
560
	u32 value;
T
Thierry Reding 已提交
561 562 563 564 565 566 567 568

	value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
	value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);

	return 0;
}

569
int drm_dp_aux_prepare(struct drm_dp_aux *aux, u8 encoding)
T
Thierry Reding 已提交
570 571 572
{
	int err;

573
	err = drm_dp_dpcd_writeb(aux, DP_MAIN_LINK_CHANNEL_CODING_SET,
T
Thierry Reding 已提交
574 575 576 577 578 579 580
				 encoding);
	if (err < 0)
		return err;

	return 0;
}

581 582
int drm_dp_aux_train(struct drm_dp_aux *aux, struct drm_dp_link *link,
		     u8 pattern)
T
Thierry Reding 已提交
583 584 585 586 587 588
{
	u8 tp = pattern & DP_TRAINING_PATTERN_MASK;
	u8 status[DP_LINK_STATUS_SIZE], values[4];
	unsigned int i;
	int err;

589
	err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, pattern);
T
Thierry Reding 已提交
590 591 592 593 594 595 596 597
	if (err < 0)
		return err;

	if (tp == DP_TRAINING_PATTERN_DISABLE)
		return 0;

	for (i = 0; i < link->num_lanes; i++)
		values[i] = DP_TRAIN_MAX_PRE_EMPHASIS_REACHED |
598
			    DP_TRAIN_PRE_EMPH_LEVEL_0 |
T
Thierry Reding 已提交
599
			    DP_TRAIN_MAX_SWING_REACHED |
600
			    DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
T
Thierry Reding 已提交
601

602
	err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, values,
T
Thierry Reding 已提交
603 604 605 606 607 608
				link->num_lanes);
	if (err < 0)
		return err;

	usleep_range(500, 1000);

609
	err = drm_dp_dpcd_read_link_status(aux, status);
T
Thierry Reding 已提交
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
	if (err < 0)
		return err;

	switch (tp) {
	case DP_TRAINING_PATTERN_1:
		if (!drm_dp_clock_recovery_ok(status, link->num_lanes))
			return -EAGAIN;

		break;

	case DP_TRAINING_PATTERN_2:
		if (!drm_dp_channel_eq_ok(status, link->num_lanes))
			return -EAGAIN;

		break;

	default:
627
		dev_err(aux->dev, "unsupported training pattern %u\n", tp);
T
Thierry Reding 已提交
628 629 630
		return -EINVAL;
	}

631
	err = drm_dp_dpcd_writeb(aux, DP_EDP_CONFIGURATION_SET, 0);
T
Thierry Reding 已提交
632 633 634 635 636
	if (err < 0)
		return err;

	return 0;
}