hdmi4.c 16.6 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 <linux/of_graph.h>
38
#include <sound/omap-hdmi-audio.h>
39

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

46
static struct omap_hdmi hdmi;
47

48 49 50 51 52 53 54 55
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);
56
	if (r < 0)
57
		return r;
58 59

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

static void hdmi_runtime_put(void)
{
	int r;

	DSSDBG("hdmi_runtime_put\n");

68
	r = pm_runtime_put_sync(&hdmi.pdev->dev);
69
	WARN_ON(r < 0 && r != -ENOSYS);
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 102
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;
}

103 104 105 106
static int hdmi_init_regulator(void)
{
	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
	hdmi.vdda_reg = reg;
119 120 121 122

	return 0;
}

123
static int hdmi_power_on_core(struct omap_dss_device *dssdev)
124
{
125
	int r;
126

127
	r = regulator_enable(hdmi.vdda_reg);
128
	if (r)
129
		return r;
130

131 132
	r = hdmi_runtime_get();
	if (r)
133
		goto err_runtime_get;
134

135 136 137
	/* Make selection of HDMI in DSS */
	dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);

T
Tomi Valkeinen 已提交
138 139
	hdmi.core_enabled = true;

140 141 142
	return 0;

err_runtime_get:
143
	regulator_disable(hdmi.vdda_reg);
144

145 146 147 148 149
	return r;
}

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

152
	hdmi_runtime_put();
153
	regulator_disable(hdmi.vdda_reg);
154 155 156 157 158
}

static int hdmi_power_on_full(struct omap_dss_device *dssdev)
{
	int r;
159
	struct videomode *vm;
160
	enum omap_channel channel = dssdev->dispc_channel;
161
	struct hdmi_wp_data *wp = &hdmi.wp;
162
	struct dss_pll_clock_info hdmi_cinfo = { 0 };
163
	unsigned pc;
164 165 166 167 168

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

169 170 171 172
	/* disable and clear irqs */
	hdmi_wp_clear_irqenable(wp, 0xffffffff);
	hdmi_wp_set_irqstatus(wp, 0xffffffff);

173
	vm = &hdmi.cfg.vm;
174

175 176
	DSSDBG("hdmi_power_on hactive= %d vactive = %d\n", vm->hactive,
	       vm->vactive);
177

178 179
	pc = vm->pixelclock;
	if (vm->flags & DISPLAY_FLAGS_DOUBLECLK)
180 181
		pc *= 2;

182 183 184
	/* DSS_HDMI_TCLK is bitclk / 10 */
	pc *= 10;

185 186
	dss_pll_calc_b(&hdmi.pll.pll, clk_get_rate(hdmi.pll.pll.clkin),
		pc, &hdmi_cinfo);
187

188
	r = dss_pll_enable(&hdmi.pll.pll);
189
	if (r) {
190
		DSSERR("Failed to enable PLL\n");
191
		goto err_pll_enable;
192 193
	}

194
	r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo);
195 196 197 198 199
	if (r) {
		DSSERR("Failed to configure PLL\n");
		goto err_pll_cfg;
	}

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

207 208 209 210
	r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
	if (r)
		goto err_phy_pwr;

211
	hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
212 213

	/* tv size */
214
	dss_mgr_set_timings(channel, vm);
215

216
	r = dss_mgr_enable(channel);
217 218
	if (r)
		goto err_mgr_enable;
219

220 221 222 223
	r = hdmi_wp_video_start(&hdmi.wp);
	if (r)
		goto err_vid_enable;

224 225 226
	hdmi_wp_set_irqenable(wp,
		HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);

227
	return 0;
228

229
err_vid_enable:
230
	dss_mgr_disable(channel);
231
err_mgr_enable:
232 233
	hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
err_phy_pwr:
234
err_phy_cfg:
235
err_pll_cfg:
236
	dss_pll_disable(&hdmi.pll.pll);
237
err_pll_enable:
238
	hdmi_power_off_core(dssdev);
239 240 241
	return -EIO;
}

242
static void hdmi_power_off_full(struct omap_dss_device *dssdev)
243
{
244
	enum omap_channel channel = dssdev->dispc_channel;
245

246 247
	hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);

248
	hdmi_wp_video_stop(&hdmi.wp);
249

250
	dss_mgr_disable(channel);
251

252 253
	hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);

254
	dss_pll_disable(&hdmi.pll.pll);
255

256
	hdmi_power_off_core(dssdev);
257 258
}

259
static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
260
				     struct videomode *vm)
261
{
262
	if (!dispc_mgr_timings_ok(dssdev->dispc_channel, vm))
263 264 265 266 267
		return -EINVAL;

	return 0;
}

268
static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
269
				    struct videomode *vm)
270
{
271 272
	mutex_lock(&hdmi.lock);

273
	hdmi.cfg.vm = *vm;
274

275
	dispc_set_tv_pclk(vm->pixelclock);
276

277
	mutex_unlock(&hdmi.lock);
278 279
}

280
static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
281
				     struct videomode *vm)
T
Tomi Valkeinen 已提交
282
{
283
	*vm = hdmi.cfg.vm;
T
Tomi Valkeinen 已提交
284 285
}

286
static void hdmi_dump_regs(struct seq_file *s)
287 288 289
{
	mutex_lock(&hdmi.lock);

290 291
	if (hdmi_runtime_get()) {
		mutex_unlock(&hdmi.lock);
292
		return;
293
	}
294

295 296 297 298
	hdmi_wp_dump(&hdmi.wp, s);
	hdmi_pll_dump(&hdmi.pll, s);
	hdmi_phy_dump(&hdmi.phy, s);
	hdmi4_core_dump(&hdmi.core, s);
299 300 301 302 303

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

304
static int read_edid(u8 *buf, int len)
305 306 307 308 309 310 311 312
{
	int r;

	mutex_lock(&hdmi.lock);

	r = hdmi_runtime_get();
	BUG_ON(r);

313
	r = hdmi4_read_edid(&hdmi.core,  buf, len);
314 315 316 317 318 319 320

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

	return r;
}

321 322 323 324 325 326 327 328 329 330 331 332
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);
}

333
static int hdmi_display_enable(struct omap_dss_device *dssdev)
334
{
335
	struct omap_dss_device *out = &hdmi.output;
336
	unsigned long flags;
337 338 339 340 341 342
	int r = 0;

	DSSDBG("ENTER hdmi_display_enable\n");

	mutex_lock(&hdmi.lock);

343
	if (!out->dispc_channel_connected) {
344
		DSSERR("failed to enable display: no output/manager\n");
345 346 347 348
		r = -ENODEV;
		goto err0;
	}

349
	r = hdmi_power_on_full(dssdev);
350 351
	if (r) {
		DSSERR("failed to power on device\n");
352
		goto err0;
353 354
	}

355 356
	if (hdmi.audio_configured) {
		r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config,
357
				       hdmi.cfg.vm.pixelclock);
358 359 360 361 362 363 364 365 366 367
		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);
368
	hdmi.display_enabled = true;
369
	spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
370

371 372 373 374 375 376 377 378
	mutex_unlock(&hdmi.lock);
	return 0;

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

379
static void hdmi_display_disable(struct omap_dss_device *dssdev)
380
{
381 382
	unsigned long flags;

383 384 385 386
	DSSDBG("Enter hdmi_display_disable\n");

	mutex_lock(&hdmi.lock);

387 388 389 390
	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);
391

392
	hdmi_power_off_full(dssdev);
393 394 395 396

	mutex_unlock(&hdmi.lock);
}

397
static int hdmi_core_enable(struct omap_dss_device *dssdev)
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
{
	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;
}

419
static void hdmi_core_disable(struct omap_dss_device *dssdev)
420 421 422 423 424 425 426 427 428 429
{
	DSSDBG("Enter omapdss_hdmi_core_disable\n");

	mutex_lock(&hdmi.lock);

	hdmi_power_off_core(dssdev);

	mutex_unlock(&hdmi.lock);
}

T
Tomi Valkeinen 已提交
430 431 432
static int hdmi_connect(struct omap_dss_device *dssdev,
		struct omap_dss_device *dst)
{
433
	enum omap_channel channel = dssdev->dispc_channel;
T
Tomi Valkeinen 已提交
434 435 436 437 438 439
	int r;

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

440
	r = dss_mgr_connect(channel, dssdev);
T
Tomi Valkeinen 已提交
441 442 443 444 445 446 447
	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);
448
		dss_mgr_disconnect(channel, dssdev);
T
Tomi Valkeinen 已提交
449 450 451 452 453 454 455 456 457
		return r;
	}

	return 0;
}

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

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

462
	if (dst != dssdev->dst)
T
Tomi Valkeinen 已提交
463 464 465 466
		return;

	omapdss_output_unset_device(dssdev);

467
	dss_mgr_disconnect(channel, dssdev);
T
Tomi Valkeinen 已提交
468 469 470 471 472 473 474 475 476 477 478
}

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) {
479
		r = hdmi_core_enable(dssdev);
T
Tomi Valkeinen 已提交
480 481 482 483
		if (r)
			return r;
	}

484
	r = read_edid(edid, len);
T
Tomi Valkeinen 已提交
485 486

	if (need_enable)
487
		hdmi_core_disable(dssdev);
T
Tomi Valkeinen 已提交
488 489 490 491

	return r;
}

492 493 494 495 496 497 498 499 500 501 502 503 504 505
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 已提交
506 507 508 509
static const struct omapdss_hdmi_ops hdmi_ops = {
	.connect		= hdmi_connect,
	.disconnect		= hdmi_disconnect,

510 511
	.enable			= hdmi_display_enable,
	.disable		= hdmi_display_disable,
T
Tomi Valkeinen 已提交
512

513 514 515
	.check_timings		= hdmi_display_check_timing,
	.set_timings		= hdmi_display_set_timing,
	.get_timings		= hdmi_display_get_timings,
T
Tomi Valkeinen 已提交
516 517

	.read_edid		= hdmi_read_edid,
518 519
	.set_infoframe		= hdmi_set_infoframe,
	.set_hdmi_mode		= hdmi_set_hdmi_mode,
T
Tomi Valkeinen 已提交
520 521
};

522
static void hdmi_init_output(struct platform_device *pdev)
523
{
524
	struct omap_dss_device *out = &hdmi.output;
525

526
	out->dev = &pdev->dev;
527
	out->id = OMAP_DSS_OUTPUT_HDMI;
528
	out->output_type = OMAP_DISPLAY_TYPE_HDMI;
T
Tomi Valkeinen 已提交
529
	out->name = "hdmi.0";
530
	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
T
Tomi Valkeinen 已提交
531
	out->ops.hdmi = &hdmi_ops;
532
	out->owner = THIS_MODULE;
533

534
	omapdss_register_output(out);
535 536
}

537
static void hdmi_uninit_output(struct platform_device *pdev)
538
{
539
	struct omap_dss_device *out = &hdmi.output;
540

541
	omapdss_unregister_output(out);
542 543
}

544 545 546 547 548 549
static int hdmi_probe_of(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
	struct device_node *ep;
	int r;

550
	ep = of_graph_get_endpoint_by_regs(node, 0, 0);
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
	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;
}

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
/* 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;
594 595
	hd->audio_configured = false;
	hd->audio_playing = false;
596 597 598 599 600 601 602 603
	mutex_unlock(&hd->lock);

	return 0;
}

static int hdmi_audio_start(struct device *dev)
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);
604
	unsigned long flags;
605 606 607

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

608 609 610 611 612
	spin_lock_irqsave(&hd->audio_playing_lock, flags);

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

614
	spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
615 616 617 618 619 620
	return 0;
}

static void hdmi_audio_stop(struct device *dev)
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);
621
	unsigned long flags;
622 623 624

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

625 626 627 628 629 630 631
	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);
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
}

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,
648
				 hd->cfg.vm.pixelclock);
649 650 651 652
	if (!ret) {
		hd->audio_configured = true;
		hd->audio_config = *dss_audio;
	}
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
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;
}

686
/* HDMI HW IP initialisation */
T
Tomi Valkeinen 已提交
687
static int hdmi4_bind(struct device *dev, struct device *master, void *data)
688
{
T
Tomi Valkeinen 已提交
689
	struct platform_device *pdev = to_platform_device(dev);
690
	int r;
691
	int irq;
692 693

	hdmi.pdev = pdev;
694
	dev_set_drvdata(&pdev->dev, &hdmi);
695 696

	mutex_init(&hdmi.lock);
697
	spin_lock_init(&hdmi.audio_playing_lock);
698

699 700 701 702 703 704
	if (pdev->dev.of_node) {
		r = hdmi_probe_of(pdev);
		if (r)
			return r;
	}

705
	r = hdmi_wp_init(pdev, &hdmi.wp);
706 707
	if (r)
		return r;
708

709
	r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp);
710 711 712
	if (r)
		return r;

713
	r = hdmi_phy_init(pdev, &hdmi.phy);
714
	if (r)
715
		goto err;
716

717
	r = hdmi4_core_init(pdev, &hdmi.core);
718
	if (r)
719
		goto err;
720

721 722 723
	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		DSSERR("platform_get_irq failed\n");
724 725
		r = -ENODEV;
		goto err;
726 727 728 729 730 731 732
	}

	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");
733
		goto err;
734 735
	}

736 737
	pm_runtime_enable(&pdev->dev);

738 739
	hdmi_init_output(pdev);

740 741 742 743 744 745 746 747
	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;
	}

748 749
	dss_debugfs_create_file("hdmi", hdmi_dump_regs);

750
	return 0;
751 752 753
err:
	hdmi_pll_uninit(&hdmi.pll);
	return r;
754 755
}

T
Tomi Valkeinen 已提交
756
static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
757
{
T
Tomi Valkeinen 已提交
758 759
	struct platform_device *pdev = to_platform_device(dev);

760 761 762
	if (hdmi.audio_pdev)
		platform_device_unregister(hdmi.audio_pdev);

763 764
	hdmi_uninit_output(pdev);

765 766
	hdmi_pll_uninit(&hdmi.pll);

767
	pm_runtime_disable(&pdev->dev);
T
Tomi Valkeinen 已提交
768 769 770 771 772 773
}

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

T
Tomi Valkeinen 已提交
775 776 777 778 779 780 781 782
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);
783 784 785
	return 0;
}

786 787 788 789 790 791 792 793 794 795 796 797 798
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)
799
		return r;
800 801 802 803 804 805 806 807 808

	return 0;
}

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

T
Tomi Valkeinen 已提交
809 810 811 812 813
static const struct of_device_id hdmi_of_match[] = {
	{ .compatible = "ti,omap4-hdmi", },
	{},
};

814
static struct platform_driver omapdss_hdmihw_driver = {
T
Tomi Valkeinen 已提交
815 816
	.probe		= hdmi4_probe,
	.remove		= hdmi4_remove,
817 818
	.driver         = {
		.name   = "omapdss_hdmi",
819
		.pm	= &hdmi_pm_ops,
T
Tomi Valkeinen 已提交
820
		.of_match_table = hdmi_of_match,
T
Tomi Valkeinen 已提交
821
		.suppress_bind_attrs = true,
822 823 824
	},
};

825
int __init hdmi4_init_platform_driver(void)
826
{
827
	return platform_driver_register(&omapdss_hdmihw_driver);
828 829
}

830
void hdmi4_uninit_platform_driver(void)
831
{
832
	platform_driver_unregister(&omapdss_hdmihw_driver);
833
}