hdmi5.c 18.5 KB
Newer Older
1 2 3
/*
 * HDMI driver for OMAP5
 *
4
 * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
 *
 * Authors:
 *	Yong Zhi
 *	Mythri pk
 *	Archit Taneja <archit@ti.com>
 *	Tomi Valkeinen <tomi.valkeinen@ti.com>
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#define DSS_SUBSYS_NAME "HDMI"

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
T
Tomi Valkeinen 已提交
40
#include <linux/component.h>
T
Tomi Valkeinen 已提交
41
#include <linux/of.h>
42
#include <linux/of_graph.h>
43
#include <sound/omap-hdmi-audio.h>
44

45
#include "omapdss.h"
46 47 48
#include "hdmi5_core.h"
#include "dss.h"

49
static int hdmi_runtime_get(struct omap_hdmi *hdmi)
50 51 52 53 54
{
	int r;

	DSSDBG("hdmi_runtime_get\n");

55
	r = pm_runtime_get_sync(&hdmi->pdev->dev);
56 57 58 59 60 61 62
	WARN_ON(r < 0);
	if (r < 0)
		return r;

	return 0;
}

63
static void hdmi_runtime_put(struct omap_hdmi *hdmi)
64 65 66 67 68
{
	int r;

	DSSDBG("hdmi_runtime_put\n");

69
	r = pm_runtime_put_sync(&hdmi->pdev->dev);
70 71 72 73 74
	WARN_ON(r < 0 && r != -ENOSYS);
}

static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
75 76
	struct omap_hdmi *hdmi = data;
	struct hdmi_wp_data *wp = &hdmi->wp;
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
	u32 irqstatus;

	irqstatus = hdmi_wp_get_irqstatus(wp);
	hdmi_wp_set_irqstatus(wp, irqstatus);

	if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
			irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
		u32 v;
		/*
		 * If we get both connect and disconnect interrupts at the same
		 * time, turn off the PHY, clear interrupts, and restart, which
		 * raises connect interrupt if a cable is connected, or nothing
		 * if cable is not connected.
		 */

		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);

		/*
		 * We always get bogus CONNECT & DISCONNECT interrupts when
		 * setting the PHY to LDOON. To ignore those, we force the RXDET
		 * line to 0 until the PHY power state has been changed.
		 */
99
		v = hdmi_read_reg(hdmi->phy.base, HDMI_TXPHY_PAD_CFG_CTRL);
100 101
		v = FLD_MOD(v, 1, 15, 15); /* FORCE_RXDET_HIGH */
		v = FLD_MOD(v, 0, 14, 7); /* RXDET_LINE */
102
		hdmi_write_reg(hdmi->phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v);
103 104 105 106 107 108

		hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
				HDMI_IRQ_LINK_DISCONNECT);

		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);

109
		REG_FLD_MOD(hdmi->phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15);
110 111 112 113 114 115 116 117 118 119

	} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
	} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
	}

	return IRQ_HANDLED;
}

120
static int hdmi_power_on_core(struct omap_hdmi *hdmi)
121 122 123
{
	int r;

124
	r = regulator_enable(hdmi->vdda_reg);
125 126 127
	if (r)
		return r;

128
	r = hdmi_runtime_get(hdmi);
129 130 131 132
	if (r)
		goto err_runtime_get;

	/* Make selection of HDMI in DSS */
133
	dss_select_hdmi_venc_clk_source(hdmi->dss, DSS_HDMI_M_PCLK);
134

135
	hdmi->core_enabled = true;
136 137 138 139

	return 0;

err_runtime_get:
140
	regulator_disable(hdmi->vdda_reg);
141 142 143 144

	return r;
}

145
static void hdmi_power_off_core(struct omap_hdmi *hdmi)
146
{
147
	hdmi->core_enabled = false;
148

149 150
	hdmi_runtime_put(hdmi);
	regulator_disable(hdmi->vdda_reg);
151 152
}

153
static int hdmi_power_on_full(struct omap_hdmi *hdmi)
154 155
{
	int r;
156
	struct videomode *vm;
157
	struct dss_pll_clock_info hdmi_cinfo = { 0 };
158
	unsigned int pc;
159

160
	r = hdmi_power_on_core(hdmi);
161 162 163
	if (r)
		return r;

164
	vm = &hdmi->cfg.vm;
165

166 167
	DSSDBG("hdmi_power_on hactive= %d vactive = %d\n", vm->hactive,
	       vm->vactive);
168

169 170
	pc = vm->pixelclock;
	if (vm->flags & DISPLAY_FLAGS_DOUBLECLK)
171 172
		pc *= 2;

173 174 175
	/* DSS_HDMI_TCLK is bitclk / 10 */
	pc *= 10;

176
	dss_pll_calc_b(&hdmi->pll.pll, clk_get_rate(hdmi->pll.pll.clkin),
177
		pc, &hdmi_cinfo);
178 179

	/* disable and clear irqs */
180 181 182
	hdmi_wp_clear_irqenable(&hdmi->wp, 0xffffffff);
	hdmi_wp_set_irqstatus(&hdmi->wp,
			hdmi_wp_get_irqstatus(&hdmi->wp));
183

184
	r = dss_pll_enable(&hdmi->pll.pll);
185
	if (r) {
186
		DSSERR("Failed to enable PLL\n");
187 188 189
		goto err_pll_enable;
	}

190
	r = dss_pll_set_config(&hdmi->pll.pll, &hdmi_cinfo);
191 192 193 194 195
	if (r) {
		DSSERR("Failed to configure PLL\n");
		goto err_pll_cfg;
	}

196
	r = hdmi_phy_configure(&hdmi->phy, hdmi_cinfo.clkdco,
197
		hdmi_cinfo.clkout[0]);
198 199 200 201 202
	if (r) {
		DSSDBG("Failed to start PHY\n");
		goto err_phy_cfg;
	}

203
	r = hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_LDOON);
204 205 206
	if (r)
		goto err_phy_pwr;

207
	hdmi5_configure(&hdmi->core, &hdmi->wp, &hdmi->cfg);
208 209

	/* tv size */
210
	dss_mgr_set_timings(&hdmi->output, vm);
211

212
	r = dss_mgr_enable(&hdmi->output);
213 214 215
	if (r)
		goto err_mgr_enable;

216
	r = hdmi_wp_video_start(&hdmi->wp);
217 218 219
	if (r)
		goto err_vid_enable;

220
	hdmi_wp_set_irqenable(&hdmi->wp,
221 222 223 224 225
			HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);

	return 0;

err_vid_enable:
226
	dss_mgr_disable(&hdmi->output);
227
err_mgr_enable:
228
	hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF);
229 230
err_phy_pwr:
err_phy_cfg:
231
err_pll_cfg:
232
	dss_pll_disable(&hdmi->pll.pll);
233
err_pll_enable:
234
	hdmi_power_off_core(hdmi);
235 236 237
	return -EIO;
}

238
static void hdmi_power_off_full(struct omap_hdmi *hdmi)
239
{
240
	hdmi_wp_clear_irqenable(&hdmi->wp, 0xffffffff);
241

242
	hdmi_wp_video_stop(&hdmi->wp);
243

244
	dss_mgr_disable(&hdmi->output);
245

246
	hdmi_wp_set_phy_pwr(&hdmi->wp, HDMI_PHYPWRCMD_OFF);
247

248
	dss_pll_disable(&hdmi->pll.pll);
249

250
	hdmi_power_off_core(hdmi);
251 252 253
}

static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
254
				     struct videomode *vm)
255
{
256 257 258
	struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);

	if (!dispc_mgr_timings_ok(hdmi->dss->dispc, dssdev->dispc_channel, vm))
259 260 261 262 263 264
		return -EINVAL;

	return 0;
}

static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
265
				    struct videomode *vm)
266
{
267 268 269
	struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);

	mutex_lock(&hdmi->lock);
270

271
	hdmi->cfg.vm = *vm;
272

273
	dispc_set_tv_pclk(hdmi->dss->dispc, vm->pixelclock);
274

275
	mutex_unlock(&hdmi->lock);
276 277
}

278
static int hdmi_dump_regs(struct seq_file *s, void *p)
279
{
280 281 282
	struct omap_hdmi *hdmi = s->private;

	mutex_lock(&hdmi->lock);
283

284 285
	if (hdmi_runtime_get(hdmi)) {
		mutex_unlock(&hdmi->lock);
286
		return 0;
287 288
	}

289 290 291 292
	hdmi_wp_dump(&hdmi->wp, s);
	hdmi_pll_dump(&hdmi->pll, s);
	hdmi_phy_dump(&hdmi->phy, s);
	hdmi5_core_dump(&hdmi->core, s);
293

294 295
	hdmi_runtime_put(hdmi);
	mutex_unlock(&hdmi->lock);
296
	return 0;
297 298
}

299
static int read_edid(struct omap_hdmi *hdmi, u8 *buf, int len)
300 301 302 303
{
	int r;
	int idlemode;

304
	mutex_lock(&hdmi->lock);
305

306
	r = hdmi_runtime_get(hdmi);
307 308
	BUG_ON(r);

309
	idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2);
310
	/* No-idle mode */
311
	REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
312

313
	r = hdmi5_read_edid(&hdmi->core,  buf, len);
314

315
	REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
316

317 318
	hdmi_runtime_put(hdmi);
	mutex_unlock(&hdmi->lock);
319 320 321 322

	return r;
}

323 324
static void hdmi_start_audio_stream(struct omap_hdmi *hd)
{
325
	REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
326 327 328 329 330 331 332 333 334 335 336
	hdmi_wp_audio_enable(&hd->wp, true);
	hdmi_wp_audio_core_req_enable(&hd->wp, true);
}

static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
{
	hdmi_wp_audio_core_req_enable(&hd->wp, false);
	hdmi_wp_audio_enable(&hd->wp, false);
	REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2);
}

337 338
static int hdmi_display_enable(struct omap_dss_device *dssdev)
{
339
	struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
340
	unsigned long flags;
341 342 343 344
	int r = 0;

	DSSDBG("ENTER hdmi_display_enable\n");

345
	mutex_lock(&hdmi->lock);
346

347
	if (!dssdev->dispc_channel_connected) {
348 349 350 351 352
		DSSERR("failed to enable display: no output/manager\n");
		r = -ENODEV;
		goto err0;
	}

353
	r = hdmi_power_on_full(hdmi);
354 355 356 357 358
	if (r) {
		DSSERR("failed to power on device\n");
		goto err0;
	}

359 360 361 362
	if (hdmi->audio_configured) {
		r = hdmi5_audio_config(&hdmi->core, &hdmi->wp,
				       &hdmi->audio_config,
				       hdmi->cfg.vm.pixelclock);
363 364
		if (r) {
			DSSERR("Error restoring audio configuration: %d", r);
365 366
			hdmi->audio_abort_cb(&hdmi->pdev->dev);
			hdmi->audio_configured = false;
367 368 369
		}
	}

370 371 372 373 374
	spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
	if (hdmi->audio_configured && hdmi->audio_playing)
		hdmi_start_audio_stream(hdmi);
	hdmi->display_enabled = true;
	spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
375

376
	mutex_unlock(&hdmi->lock);
377 378 379
	return 0;

err0:
380
	mutex_unlock(&hdmi->lock);
381 382 383 384 385
	return r;
}

static void hdmi_display_disable(struct omap_dss_device *dssdev)
{
386
	struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
387 388
	unsigned long flags;

389 390
	DSSDBG("Enter hdmi_display_disable\n");

391
	mutex_lock(&hdmi->lock);
392

393 394 395 396
	spin_lock_irqsave(&hdmi->audio_playing_lock, flags);
	hdmi_stop_audio_stream(hdmi);
	hdmi->display_enabled = false;
	spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
397

398
	hdmi_power_off_full(hdmi);
399

400
	mutex_unlock(&hdmi->lock);
401 402
}

403
static int hdmi_core_enable(struct omap_hdmi *hdmi)
404 405 406 407 408
{
	int r = 0;

	DSSDBG("ENTER omapdss_hdmi_core_enable\n");

409
	mutex_lock(&hdmi->lock);
410

411
	r = hdmi_power_on_core(hdmi);
412 413 414 415 416
	if (r) {
		DSSERR("failed to power on device\n");
		goto err0;
	}

417
	mutex_unlock(&hdmi->lock);
418 419 420
	return 0;

err0:
421
	mutex_unlock(&hdmi->lock);
422 423 424
	return r;
}

425
static void hdmi_core_disable(struct omap_hdmi *hdmi)
426 427 428
{
	DSSDBG("Enter omapdss_hdmi_core_disable\n");

429
	mutex_lock(&hdmi->lock);
430

431
	hdmi_power_off_core(hdmi);
432

433
	mutex_unlock(&hdmi->lock);
434 435
}

436 437
static int hdmi_connect(struct omap_dss_device *src,
			struct omap_dss_device *dst)
438 439 440
{
	int r;

441
	r = omapdss_device_connect(dst->dss, dst, dst->next);
442
	if (r)
443
		return r;
444

445
	dst->dispc_channel_connected = true;
446 447 448
	return 0;
}

449 450
static void hdmi_disconnect(struct omap_dss_device *src,
			    struct omap_dss_device *dst)
451
{
452 453
	dst->dispc_channel_connected = false;

454
	omapdss_device_disconnect(dst, dst->next);
455 456 457 458 459
}

static int hdmi_read_edid(struct omap_dss_device *dssdev,
		u8 *edid, int len)
{
460
	struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
461 462 463
	bool need_enable;
	int r;

464
	need_enable = hdmi->core_enabled == false;
465 466

	if (need_enable) {
467
		r = hdmi_core_enable(hdmi);
468 469 470 471
		if (r)
			return r;
	}

472
	r = read_edid(hdmi, edid, len);
473 474

	if (need_enable)
475
		hdmi_core_disable(hdmi);
476 477 478 479

	return r;
}

480 481 482
static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
		const struct hdmi_avi_infoframe *avi)
{
483 484 485
	struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);

	hdmi->cfg.infoframe = *avi;
486 487 488 489 490 491
	return 0;
}

static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
		bool hdmi_mode)
{
492 493 494
	struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);

	hdmi->cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
495 496 497
	return 0;
}

498
static const struct omap_dss_device_ops hdmi_ops = {
499 500 501 502 503 504 505 506 507
	.connect		= hdmi_connect,
	.disconnect		= hdmi_disconnect,

	.enable			= hdmi_display_enable,
	.disable		= hdmi_display_disable,

	.check_timings		= hdmi_display_check_timing,
	.set_timings		= hdmi_display_set_timing,

508 509
	.read_edid		= hdmi_read_edid,

510 511 512 513
	.hdmi = {
		.set_infoframe		= hdmi_set_infoframe,
		.set_hdmi_mode		= hdmi_set_hdmi_mode,
	},
514 515
};

516 517 518
/* -----------------------------------------------------------------------------
 * Audio Callbacks
 */
519

520 521 522 523 524 525 526
static int hdmi_audio_startup(struct device *dev,
			      void (*abort_cb)(struct device *dev))
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);

	mutex_lock(&hd->lock);

527
	WARN_ON(hd->audio_abort_cb != NULL);
528 529 530 531 532

	hd->audio_abort_cb = abort_cb;

	mutex_unlock(&hd->lock);

533
	return 0;
534 535 536 537 538 539 540 541
}

static int hdmi_audio_shutdown(struct device *dev)
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);

	mutex_lock(&hd->lock);
	hd->audio_abort_cb = NULL;
542 543
	hd->audio_configured = false;
	hd->audio_playing = false;
544 545 546 547 548 549 550 551
	mutex_unlock(&hd->lock);

	return 0;
}

static int hdmi_audio_start(struct device *dev)
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);
552
	unsigned long flags;
553

554
	spin_lock_irqsave(&hd->audio_playing_lock, flags);
555

556 557 558 559
	if (hd->display_enabled) {
		if (!hdmi_mode_has_audio(&hd->cfg))
			DSSERR("%s: Video mode does not support audio\n",
			       __func__);
560
		hdmi_start_audio_stream(hd);
561
	}
562
	hd->audio_playing = true;
563

564
	spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
565 566 567 568 569 570
	return 0;
}

static void hdmi_audio_stop(struct device *dev)
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);
571
	unsigned long flags;
572

573 574
	if (!hdmi_mode_has_audio(&hd->cfg))
		DSSERR("%s: Video mode does not support audio\n", __func__);
575

576 577 578 579 580
	spin_lock_irqsave(&hd->audio_playing_lock, flags);

	if (hd->display_enabled)
		hdmi_stop_audio_stream(hd);
	hd->audio_playing = false;
581

582
	spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
583 584 585 586 587 588
}

static int hdmi_audio_config(struct device *dev,
			     struct omap_dss_audio *dss_audio)
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);
589
	int ret = 0;
590 591 592

	mutex_lock(&hd->lock);

593 594 595 596 597
	if (hd->display_enabled) {
		ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio,
					 hd->cfg.vm.pixelclock);
		if (ret)
			goto out;
598 599
	}

600 601
	hd->audio_configured = true;
	hd->audio_config = *dss_audio;
602 603 604 605 606 607 608 609 610 611 612 613 614 615
out:
	mutex_unlock(&hd->lock);

	return ret;
}

static const struct omap_hdmi_audio_ops hdmi_audio_ops = {
	.audio_startup = hdmi_audio_startup,
	.audio_shutdown = hdmi_audio_shutdown,
	.audio_start = hdmi_audio_start,
	.audio_stop = hdmi_audio_stop,
	.audio_config = hdmi_audio_config,
};

616
static int hdmi_audio_register(struct omap_hdmi *hdmi)
617 618
{
	struct omap_hdmi_audio_pdata pdata = {
619
		.dev = &hdmi->pdev->dev,
620
		.version = 5,
621
		.audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi->wp),
622 623 624
		.ops = &hdmi_audio_ops,
	};

625 626
	hdmi->audio_pdev = platform_device_register_data(
		&hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
627 628
		&pdata, sizeof(pdata));

629 630
	if (IS_ERR(hdmi->audio_pdev))
		return PTR_ERR(hdmi->audio_pdev);
631

632 633 634 635
	hdmi_runtime_get(hdmi);
	hdmi->wp_idlemode =
		REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2);
	hdmi_runtime_put(hdmi);
636

637 638 639
	return 0;
}

640 641 642 643
/* -----------------------------------------------------------------------------
 * Component Bind & Unbind
 */

T
Tomi Valkeinen 已提交
644
static int hdmi5_bind(struct device *dev, struct device *master, void *data)
645
{
646
	struct dss_device *dss = dss_get_device(master);
647
	struct omap_hdmi *hdmi = dev_get_drvdata(dev);
648
	int r;
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692

	hdmi->dss = dss;

	r = hdmi_pll_init(dss, hdmi->pdev, &hdmi->pll, &hdmi->wp);
	if (r)
		return r;

	r = hdmi_audio_register(hdmi);
	if (r) {
		DSSERR("Registering HDMI audio failed %d\n", r);
		goto err_pll_uninit;
	}

	hdmi->debugfs = dss_debugfs_create_file(dss, "hdmi", hdmi_dump_regs,
						hdmi);

	return 0;

err_pll_uninit:
	hdmi_pll_uninit(&hdmi->pll);
	return r;
}

static void hdmi5_unbind(struct device *dev, struct device *master, void *data)
{
	struct omap_hdmi *hdmi = dev_get_drvdata(dev);

	dss_debugfs_remove_file(hdmi->debugfs);

	if (hdmi->audio_pdev)
		platform_device_unregister(hdmi->audio_pdev);

	hdmi_pll_uninit(&hdmi->pll);
}

static const struct component_ops hdmi5_component_ops = {
	.bind	= hdmi5_bind,
	.unbind	= hdmi5_unbind,
};

/* -----------------------------------------------------------------------------
 * Probe & Remove, Suspend & Resume
 */

693
static int hdmi5_init_output(struct omap_hdmi *hdmi)
694 695
{
	struct omap_dss_device *out = &hdmi->output;
696
	int r;
697 698 699 700 701 702 703 704 705 706

	out->dev = &hdmi->pdev->dev;
	out->id = OMAP_DSS_OUTPUT_HDMI;
	out->output_type = OMAP_DISPLAY_TYPE_HDMI;
	out->name = "hdmi.0";
	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
	out->ops = &hdmi_ops;
	out->owner = THIS_MODULE;
	out->of_ports = BIT(0);

707 708 709 710 711 712 713
	out->next = omapdss_of_find_connected_device(out->dev->of_node, 0);
	if (IS_ERR(out->next)) {
		if (PTR_ERR(out->next) != -EPROBE_DEFER)
			dev_err(out->dev, "failed to find video sink\n");
		return PTR_ERR(out->next);
	}

714 715 716 717 718 719 720
	r = omapdss_output_validate(out);
	if (r) {
		omapdss_device_put(out->next);
		out->next = NULL;
		return r;
	}

721
	omapdss_device_register(out);
722 723

	return 0;
724 725 726 727 728 729
}

static void hdmi5_uninit_output(struct omap_hdmi *hdmi)
{
	struct omap_dss_device *out = &hdmi->output;

730 731
	if (out->next)
		omapdss_device_put(out->next);
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
	omapdss_device_unregister(out);
}

static int hdmi5_probe_of(struct omap_hdmi *hdmi)
{
	struct platform_device *pdev = hdmi->pdev;
	struct device_node *node = pdev->dev.of_node;
	struct device_node *ep;
	int r;

	ep = of_graph_get_endpoint_by_regs(node, 0, 0);
	if (!ep)
		return 0;

	r = hdmi_parse_lanes_of(pdev, ep, &hdmi->phy);
	of_node_put(ep);
	return r;
}

static int hdmi5_probe(struct platform_device *pdev)
{
	struct omap_hdmi *hdmi;
754
	int irq;
755
	int r;
756

757 758 759
	hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL);
	if (!hdmi)
		return -ENOMEM;
760

761
	hdmi->pdev = pdev;
762

763
	dev_set_drvdata(&pdev->dev, hdmi);
764

765 766 767
	mutex_init(&hdmi->lock);
	spin_lock_init(&hdmi->audio_playing_lock);

768
	r = hdmi5_probe_of(hdmi);
769
	if (r)
770
		goto err_free;
771

772
	r = hdmi_wp_init(pdev, &hdmi->wp, 5);
773
	if (r)
774
		goto err_free;
775

776
	r = hdmi_phy_init(pdev, &hdmi->phy, 5);
777
	if (r)
778
		goto err_free;
779

780
	r = hdmi5_core_init(pdev, &hdmi->core);
781
	if (r)
782
		goto err_free;
783 784 785 786

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		DSSERR("platform_get_irq failed\n");
787
		r = -ENODEV;
788
		goto err_free;
789 790 791 792
	}

	r = devm_request_threaded_irq(&pdev->dev, irq,
			NULL, hdmi_irq_handler,
793
			IRQF_ONESHOT, "OMAP HDMI", hdmi);
794 795
	if (r) {
		DSSERR("HDMI IRQ request failed\n");
796
		goto err_free;
797 798
	}

799 800 801 802 803 804 805 806
	hdmi->vdda_reg = devm_regulator_get(&pdev->dev, "vdda");
	if (IS_ERR(hdmi->vdda_reg)) {
		r = PTR_ERR(hdmi->vdda_reg);
		if (r != -EPROBE_DEFER)
			DSSERR("can't get VDDA regulator\n");
		goto err_free;
	}

807 808
	pm_runtime_enable(&pdev->dev);

809 810 811
	r = hdmi5_init_output(hdmi);
	if (r)
		goto err_pm_disable;
812

813 814
	r = component_add(&pdev->dev, &hdmi5_component_ops);
	if (r)
815
		goto err_uninit_output;
816 817

	return 0;
818

819
err_uninit_output:
820
	hdmi5_uninit_output(hdmi);
821
err_pm_disable:
822
	pm_runtime_disable(&pdev->dev);
823 824
err_free:
	kfree(hdmi);
825
	return r;
826 827
}

828
static int hdmi5_remove(struct platform_device *pdev)
829
{
830
	struct omap_hdmi *hdmi = platform_get_drvdata(pdev);
T
Tomi Valkeinen 已提交
831

832
	component_del(&pdev->dev, &hdmi5_component_ops);
833

834
	hdmi5_uninit_output(hdmi);
835

836
	pm_runtime_disable(&pdev->dev);
837

838
	kfree(hdmi);
839 840 841 842 843
	return 0;
}

static int hdmi_runtime_suspend(struct device *dev)
{
844 845 846
	struct omap_hdmi *hdmi = dev_get_drvdata(dev);

	dispc_runtime_put(hdmi->dss->dispc);
847 848 849 850 851 852

	return 0;
}

static int hdmi_runtime_resume(struct device *dev)
{
853
	struct omap_hdmi *hdmi = dev_get_drvdata(dev);
854 855
	int r;

856
	r = dispc_runtime_get(hdmi->dss->dispc);
857 858 859 860 861 862 863 864 865 866 867 868 869
	if (r < 0)
		return r;

	return 0;
}

static const struct dev_pm_ops hdmi_pm_ops = {
	.runtime_suspend = hdmi_runtime_suspend,
	.runtime_resume = hdmi_runtime_resume,
};

static const struct of_device_id hdmi_of_match[] = {
	{ .compatible = "ti,omap5-hdmi", },
870
	{ .compatible = "ti,dra7-hdmi", },
871 872 873
	{},
};

874
struct platform_driver omapdss_hdmi5hw_driver = {
T
Tomi Valkeinen 已提交
875 876
	.probe		= hdmi5_probe,
	.remove		= hdmi5_remove,
877 878 879 880
	.driver         = {
		.name   = "omapdss_hdmi5",
		.pm	= &hdmi_pm_ops,
		.of_match_table = hdmi_of_match,
T
Tomi Valkeinen 已提交
881
		.suppress_bind_attrs = true,
882 883
	},
};