hdmi4.c 16.8 KB
Newer Older
1
/*
2
 * HDMI interface DSS driver for TI's OMAP4 family of SoCs.
3 4 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
 * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
 * Authors: Yong Zhi
 *	Mythri pk <mythripk@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>
30
#include <linux/platform_device.h>
31 32
#include <linux/pm_runtime.h>
#include <linux/clk.h>
33
#include <linux/gpio.h>
34
#include <linux/regulator/consumer.h>
T
Tomi Valkeinen 已提交
35
#include <linux/component.h>
T
Tomi Valkeinen 已提交
36
#include <linux/of.h>
37
#include <video/omapdss.h>
38
#include <sound/omap-hdmi-audio.h>
39

40
#include "hdmi4_core.h"
41
#include "dss.h"
42
#include "dss_features.h"
43
#include "hdmi.h"
44

45
static struct omap_hdmi hdmi;
46

47 48 49 50 51 52 53 54
static int hdmi_runtime_get(void)
{
	int r;

	DSSDBG("hdmi_runtime_get\n");

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

	return 0;
59 60 61 62 63 64 65 66
}

static void hdmi_runtime_put(void)
{
	int r;

	DSSDBG("hdmi_runtime_put\n");

67
	r = pm_runtime_put_sync(&hdmi.pdev->dev);
68
	WARN_ON(r < 0 && r != -ENOSYS);
69 70
}

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
	struct hdmi_wp_data *wp = data;
	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) {
		/*
		 * 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);

		hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
				HDMI_IRQ_LINK_DISCONNECT);

		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
	} 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;
}

102 103
static int hdmi_init_regulator(void)
{
104
	int r;
105 106
	struct regulator *reg;

107
	if (hdmi.vdda_reg != NULL)
108 109
		return 0;

110
	reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
111 112

	if (IS_ERR(reg)) {
113
		if (PTR_ERR(reg) != -EPROBE_DEFER)
114
			DSSERR("can't get VDDA regulator\n");
115 116 117
		return PTR_ERR(reg);
	}

118 119 120 121 122 123 124 125 126
	if (regulator_can_change_voltage(reg)) {
		r = regulator_set_voltage(reg, 1800000, 1800000);
		if (r) {
			devm_regulator_put(reg);
			DSSWARN("can't set the regulator voltage\n");
			return r;
		}
	}

127
	hdmi.vdda_reg = reg;
128 129 130 131

	return 0;
}

132
static int hdmi_power_on_core(struct omap_dss_device *dssdev)
133
{
134
	int r;
135

136
	r = regulator_enable(hdmi.vdda_reg);
137
	if (r)
138
		return r;
139

140 141
	r = hdmi_runtime_get();
	if (r)
142
		goto err_runtime_get;
143

144 145 146
	/* Make selection of HDMI in DSS */
	dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);

T
Tomi Valkeinen 已提交
147 148
	hdmi.core_enabled = true;

149 150 151
	return 0;

err_runtime_get:
152
	regulator_disable(hdmi.vdda_reg);
153

154 155 156 157 158
	return r;
}

static void hdmi_power_off_core(struct omap_dss_device *dssdev)
{
T
Tomi Valkeinen 已提交
159 160
	hdmi.core_enabled = false;

161
	hdmi_runtime_put();
162
	regulator_disable(hdmi.vdda_reg);
163 164 165 166 167 168
}

static int hdmi_power_on_full(struct omap_dss_device *dssdev)
{
	int r;
	struct omap_video_timings *p;
169
	enum omap_channel channel = dssdev->dispc_channel;
170
	struct hdmi_wp_data *wp = &hdmi.wp;
171
	struct dss_pll_clock_info hdmi_cinfo = { 0 };
172
	unsigned pc;
173 174 175 176 177

	r = hdmi_power_on_core(dssdev);
	if (r)
		return r;

178 179 180 181
	/* disable and clear irqs */
	hdmi_wp_clear_irqenable(wp, 0xffffffff);
	hdmi_wp_set_irqstatus(wp, 0xffffffff);

182
	p = &hdmi.cfg.timings;
183

184
	DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
185

186 187 188 189 190
	pc = p->pixelclock;
	if (p->double_pixel)
		pc *= 2;

	hdmi_pll_compute(&hdmi.pll, pc, &hdmi_cinfo);
191

192
	r = dss_pll_enable(&hdmi.pll.pll);
193
	if (r) {
194
		DSSERR("Failed to enable PLL\n");
195
		goto err_pll_enable;
196 197
	}

198
	r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo);
199 200 201 202 203
	if (r) {
		DSSERR("Failed to configure PLL\n");
		goto err_pll_cfg;
	}

204 205
	r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco,
		hdmi_cinfo.clkout[0]);
206
	if (r) {
207 208
		DSSDBG("Failed to configure PHY\n");
		goto err_phy_cfg;
209 210
	}

211 212 213 214
	r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
	if (r)
		goto err_phy_pwr;

215
	hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
216 217 218 219 220

	/* bypass TV gamma table */
	dispc_enable_gamma_table(0);

	/* tv size */
221
	dss_mgr_set_timings(channel, p);
222

223
	r = dss_mgr_enable(channel);
224 225
	if (r)
		goto err_mgr_enable;
226

227 228 229 230
	r = hdmi_wp_video_start(&hdmi.wp);
	if (r)
		goto err_vid_enable;

231 232 233
	hdmi_wp_set_irqenable(wp,
		HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);

234
	return 0;
235

236
err_vid_enable:
237
	dss_mgr_disable(channel);
238
err_mgr_enable:
239 240
	hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
err_phy_pwr:
241
err_phy_cfg:
242
err_pll_cfg:
243
	dss_pll_disable(&hdmi.pll.pll);
244
err_pll_enable:
245
	hdmi_power_off_core(dssdev);
246 247 248
	return -EIO;
}

249
static void hdmi_power_off_full(struct omap_dss_device *dssdev)
250
{
251
	enum omap_channel channel = dssdev->dispc_channel;
252

253 254
	hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);

255
	hdmi_wp_video_stop(&hdmi.wp);
256

257
	dss_mgr_disable(channel);
258

259 260
	hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);

261
	dss_pll_disable(&hdmi.pll.pll);
262

263
	hdmi_power_off_core(dssdev);
264 265
}

266
static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
267 268
					struct omap_video_timings *timings)
{
269
	if (!dispc_mgr_timings_ok(dssdev->dispc_channel, timings))
270 271 272 273 274
		return -EINVAL;

	return 0;
}

275
static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
276
		struct omap_video_timings *timings)
277
{
278 279
	mutex_lock(&hdmi.lock);

280
	hdmi.cfg.timings = *timings;
281

282
	dispc_set_tv_pclk(timings->pixelclock);
283

284
	mutex_unlock(&hdmi.lock);
285 286
}

287
static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
T
Tomi Valkeinen 已提交
288 289
		struct omap_video_timings *timings)
{
290
	*timings = hdmi.cfg.timings;
T
Tomi Valkeinen 已提交
291 292
}

293
static void hdmi_dump_regs(struct seq_file *s)
294 295 296
{
	mutex_lock(&hdmi.lock);

297 298
	if (hdmi_runtime_get()) {
		mutex_unlock(&hdmi.lock);
299
		return;
300
	}
301

302 303 304 305
	hdmi_wp_dump(&hdmi.wp, s);
	hdmi_pll_dump(&hdmi.pll, s);
	hdmi_phy_dump(&hdmi.phy, s);
	hdmi4_core_dump(&hdmi.core, s);
306 307 308 309 310

	hdmi_runtime_put();
	mutex_unlock(&hdmi.lock);
}

311
static int read_edid(u8 *buf, int len)
312 313 314 315 316 317 318 319
{
	int r;

	mutex_lock(&hdmi.lock);

	r = hdmi_runtime_get();
	BUG_ON(r);

320
	r = hdmi4_read_edid(&hdmi.core,  buf, len);
321 322 323 324 325 326 327

	hdmi_runtime_put();
	mutex_unlock(&hdmi.lock);

	return r;
}

328 329 330 331 332 333 334 335 336 337 338 339
static void hdmi_start_audio_stream(struct omap_hdmi *hd)
{
	hdmi_wp_audio_enable(&hd->wp, true);
	hdmi4_audio_start(&hd->core, &hd->wp);
}

static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
{
	hdmi4_audio_stop(&hd->core, &hd->wp);
	hdmi_wp_audio_enable(&hd->wp, false);
}

340
static int hdmi_display_enable(struct omap_dss_device *dssdev)
341
{
342
	struct omap_dss_device *out = &hdmi.output;
343
	unsigned long flags;
344 345 346 347 348 349
	int r = 0;

	DSSDBG("ENTER hdmi_display_enable\n");

	mutex_lock(&hdmi.lock);

350
	if (!out->dispc_channel_connected) {
351
		DSSERR("failed to enable display: no output/manager\n");
352 353 354 355
		r = -ENODEV;
		goto err0;
	}

356
	r = hdmi_power_on_full(dssdev);
357 358
	if (r) {
		DSSERR("failed to power on device\n");
359
		goto err0;
360 361
	}

362 363 364 365 366 367 368 369 370 371 372 373 374
	if (hdmi.audio_configured) {
		r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config,
				       hdmi.cfg.timings.pixelclock);
		if (r) {
			DSSERR("Error restoring audio configuration: %d", r);
			hdmi.audio_abort_cb(&hdmi.pdev->dev);
			hdmi.audio_configured = false;
		}
	}

	spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
	if (hdmi.audio_configured && hdmi.audio_playing)
		hdmi_start_audio_stream(&hdmi);
375
	hdmi.display_enabled = true;
376
	spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
377

378 379 380 381 382 383 384 385
	mutex_unlock(&hdmi.lock);
	return 0;

err0:
	mutex_unlock(&hdmi.lock);
	return r;
}

386
static void hdmi_display_disable(struct omap_dss_device *dssdev)
387
{
388 389
	unsigned long flags;

390 391 392 393
	DSSDBG("Enter hdmi_display_disable\n");

	mutex_lock(&hdmi.lock);

394 395 396 397
	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);
398

399
	hdmi_power_off_full(dssdev);
400 401 402 403

	mutex_unlock(&hdmi.lock);
}

404
static int hdmi_core_enable(struct omap_dss_device *dssdev)
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
{
	int r = 0;

	DSSDBG("ENTER omapdss_hdmi_core_enable\n");

	mutex_lock(&hdmi.lock);

	r = hdmi_power_on_core(dssdev);
	if (r) {
		DSSERR("failed to power on device\n");
		goto err0;
	}

	mutex_unlock(&hdmi.lock);
	return 0;

err0:
	mutex_unlock(&hdmi.lock);
	return r;
}

426
static void hdmi_core_disable(struct omap_dss_device *dssdev)
427 428 429 430 431 432 433 434 435 436
{
	DSSDBG("Enter omapdss_hdmi_core_disable\n");

	mutex_lock(&hdmi.lock);

	hdmi_power_off_core(dssdev);

	mutex_unlock(&hdmi.lock);
}

T
Tomi Valkeinen 已提交
437 438 439
static int hdmi_connect(struct omap_dss_device *dssdev,
		struct omap_dss_device *dst)
{
440
	enum omap_channel channel = dssdev->dispc_channel;
T
Tomi Valkeinen 已提交
441 442 443 444 445 446
	int r;

	r = hdmi_init_regulator();
	if (r)
		return r;

447
	r = dss_mgr_connect(channel, dssdev);
T
Tomi Valkeinen 已提交
448 449 450 451 452 453 454
	if (r)
		return r;

	r = omapdss_output_set_device(dssdev, dst);
	if (r) {
		DSSERR("failed to connect output to new device: %s\n",
				dst->name);
455
		dss_mgr_disconnect(channel, dssdev);
T
Tomi Valkeinen 已提交
456 457 458 459 460 461 462 463 464
		return r;
	}

	return 0;
}

static void hdmi_disconnect(struct omap_dss_device *dssdev,
		struct omap_dss_device *dst)
{
465 466
	enum omap_channel channel = dssdev->dispc_channel;

467
	WARN_ON(dst != dssdev->dst);
T
Tomi Valkeinen 已提交
468

469
	if (dst != dssdev->dst)
T
Tomi Valkeinen 已提交
470 471 472 473
		return;

	omapdss_output_unset_device(dssdev);

474
	dss_mgr_disconnect(channel, dssdev);
T
Tomi Valkeinen 已提交
475 476 477 478 479 480 481 482 483 484 485
}

static int hdmi_read_edid(struct omap_dss_device *dssdev,
		u8 *edid, int len)
{
	bool need_enable;
	int r;

	need_enable = hdmi.core_enabled == false;

	if (need_enable) {
486
		r = hdmi_core_enable(dssdev);
T
Tomi Valkeinen 已提交
487 488 489 490
		if (r)
			return r;
	}

491
	r = read_edid(edid, len);
T
Tomi Valkeinen 已提交
492 493

	if (need_enable)
494
		hdmi_core_disable(dssdev);
T
Tomi Valkeinen 已提交
495 496 497 498

	return r;
}

499 500 501 502 503 504 505 506 507 508 509 510 511 512
static int hdmi_set_infoframe(struct omap_dss_device *dssdev,
		const struct hdmi_avi_infoframe *avi)
{
	hdmi.cfg.infoframe = *avi;
	return 0;
}

static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev,
		bool hdmi_mode)
{
	hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI;
	return 0;
}

T
Tomi Valkeinen 已提交
513 514 515 516
static const struct omapdss_hdmi_ops hdmi_ops = {
	.connect		= hdmi_connect,
	.disconnect		= hdmi_disconnect,

517 518
	.enable			= hdmi_display_enable,
	.disable		= hdmi_display_disable,
T
Tomi Valkeinen 已提交
519

520 521 522
	.check_timings		= hdmi_display_check_timing,
	.set_timings		= hdmi_display_set_timing,
	.get_timings		= hdmi_display_get_timings,
T
Tomi Valkeinen 已提交
523 524

	.read_edid		= hdmi_read_edid,
525 526
	.set_infoframe		= hdmi_set_infoframe,
	.set_hdmi_mode		= hdmi_set_hdmi_mode,
T
Tomi Valkeinen 已提交
527 528
};

529
static void hdmi_init_output(struct platform_device *pdev)
530
{
531
	struct omap_dss_device *out = &hdmi.output;
532

533
	out->dev = &pdev->dev;
534
	out->id = OMAP_DSS_OUTPUT_HDMI;
535
	out->output_type = OMAP_DISPLAY_TYPE_HDMI;
T
Tomi Valkeinen 已提交
536
	out->name = "hdmi.0";
537
	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
T
Tomi Valkeinen 已提交
538
	out->ops.hdmi = &hdmi_ops;
539
	out->owner = THIS_MODULE;
540

541
	omapdss_register_output(out);
542 543
}

544
static void hdmi_uninit_output(struct platform_device *pdev)
545
{
546
	struct omap_dss_device *out = &hdmi.output;
547

548
	omapdss_unregister_output(out);
549 550
}

551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
static int hdmi_probe_of(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
	struct device_node *ep;
	int r;

	ep = omapdss_of_get_first_endpoint(node);
	if (!ep)
		return 0;

	r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy);
	if (r)
		goto err;

	of_node_put(ep);
	return 0;

err:
	of_node_put(ep);
	return r;
}

573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
/* Audio callbacks */
static int hdmi_audio_startup(struct device *dev,
			      void (*abort_cb)(struct device *dev))
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);
	int ret = 0;

	mutex_lock(&hd->lock);

	if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) {
		ret = -EPERM;
		goto out;
	}

	hd->audio_abort_cb = abort_cb;

out:
	mutex_unlock(&hd->lock);

	return ret;
}

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;
601 602
	hd->audio_configured = false;
	hd->audio_playing = false;
603 604 605 606 607 608 609 610
	mutex_unlock(&hd->lock);

	return 0;
}

static int hdmi_audio_start(struct device *dev)
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);
611
	unsigned long flags;
612 613 614

	WARN_ON(!hdmi_mode_has_audio(&hd->cfg));

615 616 617 618 619
	spin_lock_irqsave(&hd->audio_playing_lock, flags);

	if (hd->display_enabled)
		hdmi_start_audio_stream(hd);
	hd->audio_playing = true;
620

621
	spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
622 623 624 625 626 627
	return 0;
}

static void hdmi_audio_stop(struct device *dev)
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);
628
	unsigned long flags;
629 630 631

	WARN_ON(!hdmi_mode_has_audio(&hd->cfg));

632 633 634 635 636 637 638
	spin_lock_irqsave(&hd->audio_playing_lock, flags);

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

	spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
}

static int hdmi_audio_config(struct device *dev,
			     struct omap_dss_audio *dss_audio)
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);
	int ret;

	mutex_lock(&hd->lock);

	if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) {
		ret = -EPERM;
		goto out;
	}

	ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio,
				 hd->cfg.timings.pixelclock);
656 657 658 659
	if (!ret) {
		hd->audio_configured = true;
		hd->audio_config = *dss_audio;
	}
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
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,
};

static int hdmi_audio_register(struct device *dev)
{
	struct omap_hdmi_audio_pdata pdata = {
		.dev = dev,
		.dss_version = omapdss_get_version(),
		.audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp),
		.ops = &hdmi_audio_ops,
	};

	hdmi.audio_pdev = platform_device_register_data(
		dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
		&pdata, sizeof(pdata));

	if (IS_ERR(hdmi.audio_pdev))
		return PTR_ERR(hdmi.audio_pdev);

	return 0;
}

693
/* HDMI HW IP initialisation */
T
Tomi Valkeinen 已提交
694
static int hdmi4_bind(struct device *dev, struct device *master, void *data)
695
{
T
Tomi Valkeinen 已提交
696
	struct platform_device *pdev = to_platform_device(dev);
697
	int r;
698
	int irq;
699 700

	hdmi.pdev = pdev;
701
	dev_set_drvdata(&pdev->dev, &hdmi);
702 703

	mutex_init(&hdmi.lock);
704
	spin_lock_init(&hdmi.audio_playing_lock);
705

706 707 708 709 710 711
	if (pdev->dev.of_node) {
		r = hdmi_probe_of(pdev);
		if (r)
			return r;
	}

712
	r = hdmi_wp_init(pdev, &hdmi.wp);
713 714
	if (r)
		return r;
715

716
	r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp);
717 718 719
	if (r)
		return r;

720
	r = hdmi_phy_init(pdev, &hdmi.phy);
721
	if (r)
722
		goto err;
723

724
	r = hdmi4_core_init(pdev, &hdmi.core);
725
	if (r)
726
		goto err;
727

728 729 730
	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		DSSERR("platform_get_irq failed\n");
731 732
		r = -ENODEV;
		goto err;
733 734 735 736 737 738 739
	}

	r = devm_request_threaded_irq(&pdev->dev, irq,
			NULL, hdmi_irq_handler,
			IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
	if (r) {
		DSSERR("HDMI IRQ request failed\n");
740
		goto err;
741 742
	}

743 744
	pm_runtime_enable(&pdev->dev);

745 746
	hdmi_init_output(pdev);

747 748 749 750 751 752 753 754
	r = hdmi_audio_register(&pdev->dev);
	if (r) {
		DSSERR("Registering HDMI audio failed\n");
		hdmi_uninit_output(pdev);
		pm_runtime_disable(&pdev->dev);
		return r;
	}

755 756
	dss_debugfs_create_file("hdmi", hdmi_dump_regs);

757
	return 0;
758 759 760
err:
	hdmi_pll_uninit(&hdmi.pll);
	return r;
761 762
}

T
Tomi Valkeinen 已提交
763
static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
764
{
T
Tomi Valkeinen 已提交
765 766
	struct platform_device *pdev = to_platform_device(dev);

767 768 769
	if (hdmi.audio_pdev)
		platform_device_unregister(hdmi.audio_pdev);

770 771
	hdmi_uninit_output(pdev);

772 773
	hdmi_pll_uninit(&hdmi.pll);

774
	pm_runtime_disable(&pdev->dev);
T
Tomi Valkeinen 已提交
775 776 777 778 779 780
}

static const struct component_ops hdmi4_component_ops = {
	.bind	= hdmi4_bind,
	.unbind	= hdmi4_unbind,
};
781

T
Tomi Valkeinen 已提交
782 783 784 785 786 787 788 789
static int hdmi4_probe(struct platform_device *pdev)
{
	return component_add(&pdev->dev, &hdmi4_component_ops);
}

static int hdmi4_remove(struct platform_device *pdev)
{
	component_del(&pdev->dev, &hdmi4_component_ops);
790 791 792
	return 0;
}

793 794 795 796 797 798 799 800 801 802 803 804 805
static int hdmi_runtime_suspend(struct device *dev)
{
	dispc_runtime_put();

	return 0;
}

static int hdmi_runtime_resume(struct device *dev)
{
	int r;

	r = dispc_runtime_get();
	if (r < 0)
806
		return r;
807 808 809 810 811 812 813 814 815

	return 0;
}

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

T
Tomi Valkeinen 已提交
816 817 818 819 820
static const struct of_device_id hdmi_of_match[] = {
	{ .compatible = "ti,omap4-hdmi", },
	{},
};

821
static struct platform_driver omapdss_hdmihw_driver = {
T
Tomi Valkeinen 已提交
822 823
	.probe		= hdmi4_probe,
	.remove		= hdmi4_remove,
824 825
	.driver         = {
		.name   = "omapdss_hdmi",
826
		.pm	= &hdmi_pm_ops,
T
Tomi Valkeinen 已提交
827
		.of_match_table = hdmi_of_match,
T
Tomi Valkeinen 已提交
828
		.suppress_bind_attrs = true,
829 830 831
	},
};

832
int __init hdmi4_init_platform_driver(void)
833
{
834
	return platform_driver_register(&omapdss_hdmihw_driver);
835 836
}

837
void hdmi4_uninit_platform_driver(void)
838
{
839
	platform_driver_unregister(&omapdss_hdmihw_driver);
840
}