hdmi4.c 17.3 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 30
 * 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>
31
#include <linux/platform_device.h>
32 33
#include <linux/pm_runtime.h>
#include <linux/clk.h>
34
#include <linux/gpio.h>
35
#include <linux/regulator/consumer.h>
T
Tomi Valkeinen 已提交
36
#include <linux/component.h>
T
Tomi Valkeinen 已提交
37
#include <linux/of.h>
38
#include <linux/of_graph.h>
39
#include <sound/omap-hdmi-audio.h>
40
#include <media/cec.h>
41

42
#include "omapdss.h"
43
#include "hdmi4_core.h"
44
#include "hdmi4_cec.h"
45
#include "dss.h"
46
#include "hdmi.h"
47

48
static struct omap_hdmi hdmi;
49

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

	return 0;
62 63 64 65 66 67 68 69
}

static void hdmi_runtime_put(void)
{
	int r;

	DSSDBG("hdmi_runtime_put\n");

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

74 75
static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
76 77
	struct omap_hdmi *hdmi = data;
	struct hdmi_wp_data *wp = &hdmi->wp;
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
	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);
	}
102 103 104 105 106 107 108
	if (irqstatus & HDMI_IRQ_CORE) {
		u32 intr4 = hdmi_read_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4);

		hdmi_write_reg(hdmi->core.base, HDMI_CORE_SYS_INTR4, intr4);
		if (intr4 & 8)
			hdmi4_cec_irq(&hdmi->core);
	}
109 110 111 112

	return IRQ_HANDLED;
}

113 114 115 116
static int hdmi_init_regulator(void)
{
	struct regulator *reg;

117
	if (hdmi.vdda_reg != NULL)
118 119
		return 0;

120
	reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
121 122

	if (IS_ERR(reg)) {
123
		if (PTR_ERR(reg) != -EPROBE_DEFER)
124
			DSSERR("can't get VDDA regulator\n");
125 126 127
		return PTR_ERR(reg);
	}

128
	hdmi.vdda_reg = reg;
129 130 131 132

	return 0;
}

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

137 138 139
	if (hdmi.core.core_pwr_cnt++)
		return 0;

140
	r = regulator_enable(hdmi.vdda_reg);
141
	if (r)
142
		goto err_reg_enable;
143

144 145
	r = hdmi_runtime_get();
	if (r)
146
		goto err_runtime_get;
147

148 149
	hdmi4_core_powerdown_disable(&hdmi.core);

150 151 152
	/* Make selection of HDMI in DSS */
	dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);

T
Tomi Valkeinen 已提交
153 154
	hdmi.core_enabled = true;

155 156 157
	return 0;

err_runtime_get:
158
	regulator_disable(hdmi.vdda_reg);
159 160
err_reg_enable:
	hdmi.core.core_pwr_cnt--;
161

162 163 164 165 166
	return r;
}

static void hdmi_power_off_core(struct omap_dss_device *dssdev)
{
167 168 169
	if (--hdmi.core.core_pwr_cnt)
		return;

T
Tomi Valkeinen 已提交
170 171
	hdmi.core_enabled = false;

172
	hdmi_runtime_put();
173
	regulator_disable(hdmi.vdda_reg);
174 175 176 177 178
}

static int hdmi_power_on_full(struct omap_dss_device *dssdev)
{
	int r;
179
	struct videomode *vm;
180
	enum omap_channel channel = dssdev->dispc_channel;
181
	struct hdmi_wp_data *wp = &hdmi.wp;
182
	struct dss_pll_clock_info hdmi_cinfo = { 0 };
183
	unsigned int pc;
184 185 186 187 188

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

189
	/* disable and clear irqs */
190 191
	hdmi_wp_clear_irqenable(wp, ~HDMI_IRQ_CORE);
	hdmi_wp_set_irqstatus(wp, ~HDMI_IRQ_CORE);
192

193
	vm = &hdmi.cfg.vm;
194

195 196
	DSSDBG("hdmi_power_on hactive= %d vactive = %d\n", vm->hactive,
	       vm->vactive);
197

198 199
	pc = vm->pixelclock;
	if (vm->flags & DISPLAY_FLAGS_DOUBLECLK)
200 201
		pc *= 2;

202 203 204
	/* DSS_HDMI_TCLK is bitclk / 10 */
	pc *= 10;

205 206
	dss_pll_calc_b(&hdmi.pll.pll, clk_get_rate(hdmi.pll.pll.clkin),
		pc, &hdmi_cinfo);
207

208
	r = dss_pll_enable(&hdmi.pll.pll);
209
	if (r) {
210
		DSSERR("Failed to enable PLL\n");
211
		goto err_pll_enable;
212 213
	}

214
	r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo);
215 216 217 218 219
	if (r) {
		DSSERR("Failed to configure PLL\n");
		goto err_pll_cfg;
	}

220 221
	r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco,
		hdmi_cinfo.clkout[0]);
222
	if (r) {
223 224
		DSSDBG("Failed to configure PHY\n");
		goto err_phy_cfg;
225 226
	}

227 228 229 230
	r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
	if (r)
		goto err_phy_pwr;

231
	hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
232 233

	/* tv size */
234
	dss_mgr_set_timings(channel, vm);
235

236
	r = dss_mgr_enable(channel);
237 238
	if (r)
		goto err_mgr_enable;
239

240 241 242 243
	r = hdmi_wp_video_start(&hdmi.wp);
	if (r)
		goto err_vid_enable;

244 245 246
	hdmi_wp_set_irqenable(wp,
		HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);

247
	return 0;
248

249
err_vid_enable:
250
	dss_mgr_disable(channel);
251
err_mgr_enable:
252 253
	hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
err_phy_pwr:
254
err_phy_cfg:
255
err_pll_cfg:
256
	dss_pll_disable(&hdmi.pll.pll);
257
err_pll_enable:
258
	hdmi_power_off_core(dssdev);
259 260 261
	return -EIO;
}

262
static void hdmi_power_off_full(struct omap_dss_device *dssdev)
263
{
264
	enum omap_channel channel = dssdev->dispc_channel;
265

266
	hdmi_wp_clear_irqenable(&hdmi.wp, ~HDMI_IRQ_CORE);
267

268
	hdmi_wp_video_stop(&hdmi.wp);
269

270
	dss_mgr_disable(channel);
271

272 273
	hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);

274
	dss_pll_disable(&hdmi.pll.pll);
275

276
	hdmi_power_off_core(dssdev);
277 278
}

279
static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
280
				     struct videomode *vm)
281
{
282
	if (!dispc_mgr_timings_ok(dssdev->dispc_channel, vm))
283 284 285 286 287
		return -EINVAL;

	return 0;
}

288
static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
289
				    struct videomode *vm)
290
{
291 292
	mutex_lock(&hdmi.lock);

293
	hdmi.cfg.vm = *vm;
294

295
	dispc_set_tv_pclk(vm->pixelclock);
296

297
	mutex_unlock(&hdmi.lock);
298 299
}

300
static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
301
				     struct videomode *vm)
T
Tomi Valkeinen 已提交
302
{
303
	*vm = hdmi.cfg.vm;
T
Tomi Valkeinen 已提交
304 305
}

306
static void hdmi_dump_regs(struct seq_file *s)
307 308 309
{
	mutex_lock(&hdmi.lock);

310 311
	if (hdmi_runtime_get()) {
		mutex_unlock(&hdmi.lock);
312
		return;
313
	}
314

315 316 317 318
	hdmi_wp_dump(&hdmi.wp, s);
	hdmi_pll_dump(&hdmi.pll, s);
	hdmi_phy_dump(&hdmi.phy, s);
	hdmi4_core_dump(&hdmi.core, s);
319 320 321 322 323

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

324
static int read_edid(u8 *buf, int len)
325 326 327 328 329 330 331 332
{
	int r;

	mutex_lock(&hdmi.lock);

	r = hdmi_runtime_get();
	BUG_ON(r);

333
	r = hdmi4_read_edid(&hdmi.core,  buf, len);
334 335 336 337 338 339 340

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

	return r;
}

341 342 343 344 345 346 347 348 349 350 351 352
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);
}

353
static int hdmi_display_enable(struct omap_dss_device *dssdev)
354
{
355
	struct omap_dss_device *out = &hdmi.output;
356
	unsigned long flags;
357 358 359 360 361 362
	int r = 0;

	DSSDBG("ENTER hdmi_display_enable\n");

	mutex_lock(&hdmi.lock);

363
	if (!out->dispc_channel_connected) {
364
		DSSERR("failed to enable display: no output/manager\n");
365 366 367 368
		r = -ENODEV;
		goto err0;
	}

369
	r = hdmi_power_on_full(dssdev);
370 371
	if (r) {
		DSSERR("failed to power on device\n");
372
		goto err0;
373 374
	}

375 376
	if (hdmi.audio_configured) {
		r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config,
377
				       hdmi.cfg.vm.pixelclock);
378 379 380 381 382 383 384 385 386 387
		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);
388
	hdmi.display_enabled = true;
389
	spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
390

391 392 393 394 395 396 397 398
	mutex_unlock(&hdmi.lock);
	return 0;

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

399
static void hdmi_display_disable(struct omap_dss_device *dssdev)
400
{
401 402
	unsigned long flags;

403 404 405 406
	DSSDBG("Enter hdmi_display_disable\n");

	mutex_lock(&hdmi.lock);

407 408 409 410
	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);
411

412
	hdmi_power_off_full(dssdev);
413 414 415 416

	mutex_unlock(&hdmi.lock);
}

417
int hdmi4_core_enable(struct omap_dss_device *dssdev)
418 419 420
{
	int r = 0;

421
	DSSDBG("ENTER omapdss_hdmi4_core_enable\n");
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438

	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;
}

439
void hdmi4_core_disable(struct omap_dss_device *dssdev)
440
{
441
	DSSDBG("Enter omapdss_hdmi4_core_disable\n");
442 443 444 445 446 447 448 449

	mutex_lock(&hdmi.lock);

	hdmi_power_off_core(dssdev);

	mutex_unlock(&hdmi.lock);
}

T
Tomi Valkeinen 已提交
450 451 452
static int hdmi_connect(struct omap_dss_device *dssdev,
		struct omap_dss_device *dst)
{
453
	enum omap_channel channel = dssdev->dispc_channel;
T
Tomi Valkeinen 已提交
454 455 456 457 458 459
	int r;

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

460
	r = dss_mgr_connect(channel, dssdev);
T
Tomi Valkeinen 已提交
461 462 463 464 465 466 467
	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);
468
		dss_mgr_disconnect(channel, dssdev);
T
Tomi Valkeinen 已提交
469 470 471 472 473 474 475 476 477
		return r;
	}

	return 0;
}

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

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

482
	if (dst != dssdev->dst)
T
Tomi Valkeinen 已提交
483 484 485 486
		return;

	omapdss_output_unset_device(dssdev);

487
	dss_mgr_disconnect(channel, dssdev);
T
Tomi Valkeinen 已提交
488 489 490 491 492 493 494 495 496 497 498
}

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) {
499
		r = hdmi4_core_enable(dssdev);
T
Tomi Valkeinen 已提交
500 501 502 503
		if (r)
			return r;
	}

504
	r = read_edid(edid, len);
505 506 507 508 509
	if (r >= 256)
		hdmi4_cec_set_phys_addr(&hdmi.core,
					cec_get_edid_phys_addr(edid, r, NULL));
	else
		hdmi4_cec_set_phys_addr(&hdmi.core, CEC_PHYS_ADDR_INVALID);
T
Tomi Valkeinen 已提交
510
	if (need_enable)
511
		hdmi4_core_disable(dssdev);
T
Tomi Valkeinen 已提交
512 513 514 515

	return r;
}

516 517 518 519 520
static void hdmi_lost_hotplug(struct omap_dss_device *dssdev)
{
	hdmi4_cec_set_phys_addr(&hdmi.core, CEC_PHYS_ADDR_INVALID);
}

521 522 523 524 525 526 527 528 529 530 531 532 533 534
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 已提交
535 536 537 538
static const struct omapdss_hdmi_ops hdmi_ops = {
	.connect		= hdmi_connect,
	.disconnect		= hdmi_disconnect,

539 540
	.enable			= hdmi_display_enable,
	.disable		= hdmi_display_disable,
T
Tomi Valkeinen 已提交
541

542 543 544
	.check_timings		= hdmi_display_check_timing,
	.set_timings		= hdmi_display_set_timing,
	.get_timings		= hdmi_display_get_timings,
T
Tomi Valkeinen 已提交
545 546

	.read_edid		= hdmi_read_edid,
547
	.lost_hotplug		= hdmi_lost_hotplug,
548 549
	.set_infoframe		= hdmi_set_infoframe,
	.set_hdmi_mode		= hdmi_set_hdmi_mode,
T
Tomi Valkeinen 已提交
550 551
};

552
static void hdmi_init_output(struct platform_device *pdev)
553
{
554
	struct omap_dss_device *out = &hdmi.output;
555

556
	out->dev = &pdev->dev;
557
	out->id = OMAP_DSS_OUTPUT_HDMI;
558
	out->output_type = OMAP_DISPLAY_TYPE_HDMI;
T
Tomi Valkeinen 已提交
559
	out->name = "hdmi.0";
560
	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
T
Tomi Valkeinen 已提交
561
	out->ops.hdmi = &hdmi_ops;
562
	out->owner = THIS_MODULE;
563

564
	omapdss_register_output(out);
565 566
}

567
static void hdmi_uninit_output(struct platform_device *pdev)
568
{
569
	struct omap_dss_device *out = &hdmi.output;
570

571
	omapdss_unregister_output(out);
572 573
}

574 575 576 577 578 579
static int hdmi_probe_of(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
	struct device_node *ep;
	int r;

580
	ep = of_graph_get_endpoint_by_regs(node, 0, 0);
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
	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;
}

596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
/* 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;
624 625
	hd->audio_configured = false;
	hd->audio_playing = false;
626 627 628 629 630 631 632 633
	mutex_unlock(&hd->lock);

	return 0;
}

static int hdmi_audio_start(struct device *dev)
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);
634
	unsigned long flags;
635 636 637

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

638 639 640 641 642
	spin_lock_irqsave(&hd->audio_playing_lock, flags);

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

644
	spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
645 646 647 648 649 650
	return 0;
}

static void hdmi_audio_stop(struct device *dev)
{
	struct omap_hdmi *hd = dev_get_drvdata(dev);
651
	unsigned long flags;
652 653 654

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

655 656 657 658 659 660 661
	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);
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
}

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,
678
				 hd->cfg.vm.pixelclock);
679 680 681 682
	if (!ret) {
		hd->audio_configured = true;
		hd->audio_config = *dss_audio;
	}
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
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,
701
		.version = 4,
702 703 704 705 706 707 708 709 710 711 712 713 714 715
		.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;
}

716
/* HDMI HW IP initialisation */
T
Tomi Valkeinen 已提交
717
static int hdmi4_bind(struct device *dev, struct device *master, void *data)
718
{
T
Tomi Valkeinen 已提交
719
	struct platform_device *pdev = to_platform_device(dev);
720
	struct dss_device *dss = dss_get_device(master);
721
	int r;
722
	int irq;
723 724

	hdmi.pdev = pdev;
725
	dev_set_drvdata(&pdev->dev, &hdmi);
726 727

	mutex_init(&hdmi.lock);
728
	spin_lock_init(&hdmi.audio_playing_lock);
729

730 731 732
	r = hdmi_probe_of(pdev);
	if (r)
		return r;
733

734
	r = hdmi_wp_init(pdev, &hdmi.wp, 4);
735 736
	if (r)
		return r;
737

738
	r = hdmi_pll_init(dss, pdev, &hdmi.pll, &hdmi.wp);
739 740 741
	if (r)
		return r;

742
	r = hdmi_phy_init(pdev, &hdmi.phy, 4);
743
	if (r)
744
		goto err;
745

746
	r = hdmi4_core_init(pdev, &hdmi.core);
747
	if (r)
748
		goto err;
749

750 751 752 753
	r = hdmi4_cec_init(pdev, &hdmi.core, &hdmi.wp);
	if (r)
		goto err;

754 755 756
	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		DSSERR("platform_get_irq failed\n");
757 758
		r = -ENODEV;
		goto err;
759 760 761 762
	}

	r = devm_request_threaded_irq(&pdev->dev, irq,
			NULL, hdmi_irq_handler,
763
			IRQF_ONESHOT, "OMAP HDMI", &hdmi);
764 765
	if (r) {
		DSSERR("HDMI IRQ request failed\n");
766
		goto err;
767 768
	}

769 770
	pm_runtime_enable(&pdev->dev);

771 772
	hdmi_init_output(pdev);

773 774 775 776 777 778 779 780
	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;
	}

781 782
	dss_debugfs_create_file("hdmi", hdmi_dump_regs);

783
	return 0;
784 785 786
err:
	hdmi_pll_uninit(&hdmi.pll);
	return r;
787 788
}

T
Tomi Valkeinen 已提交
789
static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
790
{
T
Tomi Valkeinen 已提交
791 792
	struct platform_device *pdev = to_platform_device(dev);

793 794 795
	if (hdmi.audio_pdev)
		platform_device_unregister(hdmi.audio_pdev);

796 797
	hdmi_uninit_output(pdev);

798 799
	hdmi4_cec_uninit(&hdmi.core);

800 801
	hdmi_pll_uninit(&hdmi.pll);

802
	pm_runtime_disable(&pdev->dev);
T
Tomi Valkeinen 已提交
803 804 805 806 807 808
}

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

T
Tomi Valkeinen 已提交
810 811 812 813 814 815 816 817
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);
818 819 820
	return 0;
}

821 822 823 824 825 826 827 828 829 830 831 832 833
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)
834
		return r;
835 836 837 838 839 840 841 842 843

	return 0;
}

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

T
Tomi Valkeinen 已提交
844 845 846 847 848
static const struct of_device_id hdmi_of_match[] = {
	{ .compatible = "ti,omap4-hdmi", },
	{},
};

849
struct platform_driver omapdss_hdmi4hw_driver = {
T
Tomi Valkeinen 已提交
850 851
	.probe		= hdmi4_probe,
	.remove		= hdmi4_remove,
852 853
	.driver         = {
		.name   = "omapdss_hdmi",
854
		.pm	= &hdmi_pm_ops,
T
Tomi Valkeinen 已提交
855
		.of_match_table = hdmi_of_match,
T
Tomi Valkeinen 已提交
856
		.suppress_bind_attrs = true,
857 858
	},
};