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 <sound/omap-hdmi-audio.h>
38

39
#include "omapdss.h"
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 104 105
static int hdmi_init_regulator(void)
{
	struct regulator *reg;

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

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

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

117
	hdmi.vdda_reg = reg;
118 119 120 121

	return 0;
}

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

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

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

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

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

139 140 141
	return 0;

err_runtime_get:
142
	regulator_disable(hdmi.vdda_reg);
143

144 145 146 147 148
	return r;
}

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

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

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

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

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

172
	vm = &hdmi.cfg.vm;
173

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

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

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

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

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

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

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

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

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

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

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

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

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

226
	return 0;
227

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

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

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

247
	hdmi_wp_video_stop(&hdmi.wp);
248

249
	dss_mgr_disable(channel);
250

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

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

255
	hdmi_power_off_core(dssdev);
256 257
}

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

	return 0;
}

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

272
	hdmi.cfg.vm = *vm;
273

274
	dispc_set_tv_pclk(vm->pixelclock);
275

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

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

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

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

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

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

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

	mutex_lock(&hdmi.lock);

	r = hdmi_runtime_get();
	BUG_ON(r);

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

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

	return r;
}

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

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

	DSSDBG("ENTER hdmi_display_enable\n");

	mutex_lock(&hdmi.lock);

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

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

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

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

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

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

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

	mutex_lock(&hdmi.lock);

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

391
	hdmi_power_off_full(dssdev);
392 393 394 395

	mutex_unlock(&hdmi.lock);
}

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

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

	mutex_lock(&hdmi.lock);

	hdmi_power_off_core(dssdev);

	mutex_unlock(&hdmi.lock);
}

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

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

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

	return 0;
}

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

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

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

	omapdss_output_unset_device(dssdev);

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

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

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

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

	return r;
}

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

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

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

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

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

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

533
	omapdss_register_output(out);
534 535
}

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

540
	omapdss_unregister_output(out);
541 542
}

543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
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;
}

565 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
/* 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;
593 594
	hd->audio_configured = false;
	hd->audio_playing = false;
595 596 597 598 599 600 601 602
	mutex_unlock(&hd->lock);

	return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

735 736
	pm_runtime_enable(&pdev->dev);

737 738
	hdmi_init_output(pdev);

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

747 748
	dss_debugfs_create_file("hdmi", hdmi_dump_regs);

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

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

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

762 763
	hdmi_uninit_output(pdev);

764 765
	hdmi_pll_uninit(&hdmi.pll);

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

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

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

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

	return 0;
}

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

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

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

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

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