Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.17-rc7 795 lines 16 kB view raw
1/* 2 * HDMI interface DSS driver for TI's OMAP4 family of SoCs. 3 * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ 4 * Authors: Yong Zhi 5 * Mythri pk <mythripk@ti.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License along with 17 * this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#define DSS_SUBSYS_NAME "HDMI" 21 22#include <linux/kernel.h> 23#include <linux/module.h> 24#include <linux/err.h> 25#include <linux/io.h> 26#include <linux/interrupt.h> 27#include <linux/mutex.h> 28#include <linux/delay.h> 29#include <linux/string.h> 30#include <linux/platform_device.h> 31#include <linux/pm_runtime.h> 32#include <linux/clk.h> 33#include <linux/gpio.h> 34#include <linux/regulator/consumer.h> 35#include <video/omapdss.h> 36 37#include "hdmi4_core.h" 38#include "dss.h" 39#include "dss_features.h" 40 41static struct { 42 struct mutex lock; 43 struct platform_device *pdev; 44 45 struct hdmi_wp_data wp; 46 struct hdmi_pll_data pll; 47 struct hdmi_phy_data phy; 48 struct hdmi_core_data core; 49 50 struct hdmi_config cfg; 51 52 struct clk *sys_clk; 53 struct regulator *vdda_hdmi_dac_reg; 54 55 bool core_enabled; 56 57 struct omap_dss_device output; 58} hdmi; 59 60static int hdmi_runtime_get(void) 61{ 62 int r; 63 64 DSSDBG("hdmi_runtime_get\n"); 65 66 r = pm_runtime_get_sync(&hdmi.pdev->dev); 67 WARN_ON(r < 0); 68 if (r < 0) 69 return r; 70 71 return 0; 72} 73 74static void hdmi_runtime_put(void) 75{ 76 int r; 77 78 DSSDBG("hdmi_runtime_put\n"); 79 80 r = pm_runtime_put_sync(&hdmi.pdev->dev); 81 WARN_ON(r < 0 && r != -ENOSYS); 82} 83 84static irqreturn_t hdmi_irq_handler(int irq, void *data) 85{ 86 struct hdmi_wp_data *wp = data; 87 u32 irqstatus; 88 89 irqstatus = hdmi_wp_get_irqstatus(wp); 90 hdmi_wp_set_irqstatus(wp, irqstatus); 91 92 if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && 93 irqstatus & HDMI_IRQ_LINK_DISCONNECT) { 94 /* 95 * If we get both connect and disconnect interrupts at the same 96 * time, turn off the PHY, clear interrupts, and restart, which 97 * raises connect interrupt if a cable is connected, or nothing 98 * if cable is not connected. 99 */ 100 hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); 101 102 hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | 103 HDMI_IRQ_LINK_DISCONNECT); 104 105 hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); 106 } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { 107 hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); 108 } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { 109 hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); 110 } 111 112 return IRQ_HANDLED; 113} 114 115static int hdmi_init_regulator(void) 116{ 117 int r; 118 struct regulator *reg; 119 120 if (hdmi.vdda_hdmi_dac_reg != NULL) 121 return 0; 122 123 reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); 124 125 if (IS_ERR(reg)) { 126 if (PTR_ERR(reg) != -EPROBE_DEFER) 127 DSSERR("can't get VDDA regulator\n"); 128 return PTR_ERR(reg); 129 } 130 131 if (regulator_can_change_voltage(reg)) { 132 r = regulator_set_voltage(reg, 1800000, 1800000); 133 if (r) { 134 devm_regulator_put(reg); 135 DSSWARN("can't set the regulator voltage\n"); 136 return r; 137 } 138 } 139 140 hdmi.vdda_hdmi_dac_reg = reg; 141 142 return 0; 143} 144 145static int hdmi_power_on_core(struct omap_dss_device *dssdev) 146{ 147 int r; 148 149 r = regulator_enable(hdmi.vdda_hdmi_dac_reg); 150 if (r) 151 return r; 152 153 r = hdmi_runtime_get(); 154 if (r) 155 goto err_runtime_get; 156 157 /* Make selection of HDMI in DSS */ 158 dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); 159 160 hdmi.core_enabled = true; 161 162 return 0; 163 164err_runtime_get: 165 regulator_disable(hdmi.vdda_hdmi_dac_reg); 166 167 return r; 168} 169 170static void hdmi_power_off_core(struct omap_dss_device *dssdev) 171{ 172 hdmi.core_enabled = false; 173 174 hdmi_runtime_put(); 175 regulator_disable(hdmi.vdda_hdmi_dac_reg); 176} 177 178static int hdmi_power_on_full(struct omap_dss_device *dssdev) 179{ 180 int r; 181 struct omap_video_timings *p; 182 struct omap_overlay_manager *mgr = hdmi.output.manager; 183 unsigned long phy; 184 struct hdmi_wp_data *wp = &hdmi.wp; 185 186 r = hdmi_power_on_core(dssdev); 187 if (r) 188 return r; 189 190 /* disable and clear irqs */ 191 hdmi_wp_clear_irqenable(wp, 0xffffffff); 192 hdmi_wp_set_irqstatus(wp, 0xffffffff); 193 194 p = &hdmi.cfg.timings; 195 196 DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); 197 198 /* the functions below use kHz pixel clock. TODO: change to Hz */ 199 phy = p->pixelclock / 1000; 200 201 hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy); 202 203 /* config the PLL and PHY hdmi_set_pll_pwrfirst */ 204 r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp); 205 if (r) { 206 DSSDBG("Failed to lock PLL\n"); 207 goto err_pll_enable; 208 } 209 210 r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); 211 if (r) { 212 DSSDBG("Failed to configure PHY\n"); 213 goto err_phy_cfg; 214 } 215 216 r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); 217 if (r) 218 goto err_phy_pwr; 219 220 hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); 221 222 /* bypass TV gamma table */ 223 dispc_enable_gamma_table(0); 224 225 /* tv size */ 226 dss_mgr_set_timings(mgr, p); 227 228 r = hdmi_wp_video_start(&hdmi.wp); 229 if (r) 230 goto err_vid_enable; 231 232 r = dss_mgr_enable(mgr); 233 if (r) 234 goto err_mgr_enable; 235 236 hdmi_wp_set_irqenable(wp, 237 HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); 238 239 return 0; 240 241err_mgr_enable: 242 hdmi_wp_video_stop(&hdmi.wp); 243err_vid_enable: 244err_phy_cfg: 245 hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); 246err_phy_pwr: 247 hdmi_pll_disable(&hdmi.pll, &hdmi.wp); 248err_pll_enable: 249 hdmi_power_off_core(dssdev); 250 return -EIO; 251} 252 253static void hdmi_power_off_full(struct omap_dss_device *dssdev) 254{ 255 struct omap_overlay_manager *mgr = hdmi.output.manager; 256 257 hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); 258 259 dss_mgr_disable(mgr); 260 261 hdmi_wp_video_stop(&hdmi.wp); 262 263 hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); 264 265 hdmi_pll_disable(&hdmi.pll, &hdmi.wp); 266 267 hdmi_power_off_core(dssdev); 268} 269 270static int hdmi_display_check_timing(struct omap_dss_device *dssdev, 271 struct omap_video_timings *timings) 272{ 273 struct omap_dss_device *out = &hdmi.output; 274 275 if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) 276 return -EINVAL; 277 278 return 0; 279} 280 281static void hdmi_display_set_timing(struct omap_dss_device *dssdev, 282 struct omap_video_timings *timings) 283{ 284 mutex_lock(&hdmi.lock); 285 286 hdmi.cfg.timings = *timings; 287 288 dispc_set_tv_pclk(timings->pixelclock); 289 290 mutex_unlock(&hdmi.lock); 291} 292 293static void hdmi_display_get_timings(struct omap_dss_device *dssdev, 294 struct omap_video_timings *timings) 295{ 296 *timings = hdmi.cfg.timings; 297} 298 299static void hdmi_dump_regs(struct seq_file *s) 300{ 301 mutex_lock(&hdmi.lock); 302 303 if (hdmi_runtime_get()) { 304 mutex_unlock(&hdmi.lock); 305 return; 306 } 307 308 hdmi_wp_dump(&hdmi.wp, s); 309 hdmi_pll_dump(&hdmi.pll, s); 310 hdmi_phy_dump(&hdmi.phy, s); 311 hdmi4_core_dump(&hdmi.core, s); 312 313 hdmi_runtime_put(); 314 mutex_unlock(&hdmi.lock); 315} 316 317static int read_edid(u8 *buf, int len) 318{ 319 int r; 320 321 mutex_lock(&hdmi.lock); 322 323 r = hdmi_runtime_get(); 324 BUG_ON(r); 325 326 r = hdmi4_read_edid(&hdmi.core, buf, len); 327 328 hdmi_runtime_put(); 329 mutex_unlock(&hdmi.lock); 330 331 return r; 332} 333 334static int hdmi_display_enable(struct omap_dss_device *dssdev) 335{ 336 struct omap_dss_device *out = &hdmi.output; 337 int r = 0; 338 339 DSSDBG("ENTER hdmi_display_enable\n"); 340 341 mutex_lock(&hdmi.lock); 342 343 if (out == NULL || out->manager == NULL) { 344 DSSERR("failed to enable display: no output/manager\n"); 345 r = -ENODEV; 346 goto err0; 347 } 348 349 r = hdmi_power_on_full(dssdev); 350 if (r) { 351 DSSERR("failed to power on device\n"); 352 goto err0; 353 } 354 355 mutex_unlock(&hdmi.lock); 356 return 0; 357 358err0: 359 mutex_unlock(&hdmi.lock); 360 return r; 361} 362 363static void hdmi_display_disable(struct omap_dss_device *dssdev) 364{ 365 DSSDBG("Enter hdmi_display_disable\n"); 366 367 mutex_lock(&hdmi.lock); 368 369 hdmi_power_off_full(dssdev); 370 371 mutex_unlock(&hdmi.lock); 372} 373 374static int hdmi_core_enable(struct omap_dss_device *dssdev) 375{ 376 int r = 0; 377 378 DSSDBG("ENTER omapdss_hdmi_core_enable\n"); 379 380 mutex_lock(&hdmi.lock); 381 382 r = hdmi_power_on_core(dssdev); 383 if (r) { 384 DSSERR("failed to power on device\n"); 385 goto err0; 386 } 387 388 mutex_unlock(&hdmi.lock); 389 return 0; 390 391err0: 392 mutex_unlock(&hdmi.lock); 393 return r; 394} 395 396static void hdmi_core_disable(struct omap_dss_device *dssdev) 397{ 398 DSSDBG("Enter omapdss_hdmi_core_disable\n"); 399 400 mutex_lock(&hdmi.lock); 401 402 hdmi_power_off_core(dssdev); 403 404 mutex_unlock(&hdmi.lock); 405} 406 407static int hdmi_get_clocks(struct platform_device *pdev) 408{ 409 struct clk *clk; 410 411 clk = devm_clk_get(&pdev->dev, "sys_clk"); 412 if (IS_ERR(clk)) { 413 DSSERR("can't get sys_clk\n"); 414 return PTR_ERR(clk); 415 } 416 417 hdmi.sys_clk = clk; 418 419 return 0; 420} 421 422static int hdmi_connect(struct omap_dss_device *dssdev, 423 struct omap_dss_device *dst) 424{ 425 struct omap_overlay_manager *mgr; 426 int r; 427 428 r = hdmi_init_regulator(); 429 if (r) 430 return r; 431 432 mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); 433 if (!mgr) 434 return -ENODEV; 435 436 r = dss_mgr_connect(mgr, dssdev); 437 if (r) 438 return r; 439 440 r = omapdss_output_set_device(dssdev, dst); 441 if (r) { 442 DSSERR("failed to connect output to new device: %s\n", 443 dst->name); 444 dss_mgr_disconnect(mgr, dssdev); 445 return r; 446 } 447 448 return 0; 449} 450 451static void hdmi_disconnect(struct omap_dss_device *dssdev, 452 struct omap_dss_device *dst) 453{ 454 WARN_ON(dst != dssdev->dst); 455 456 if (dst != dssdev->dst) 457 return; 458 459 omapdss_output_unset_device(dssdev); 460 461 if (dssdev->manager) 462 dss_mgr_disconnect(dssdev->manager, dssdev); 463} 464 465static int hdmi_read_edid(struct omap_dss_device *dssdev, 466 u8 *edid, int len) 467{ 468 bool need_enable; 469 int r; 470 471 need_enable = hdmi.core_enabled == false; 472 473 if (need_enable) { 474 r = hdmi_core_enable(dssdev); 475 if (r) 476 return r; 477 } 478 479 r = read_edid(edid, len); 480 481 if (need_enable) 482 hdmi_core_disable(dssdev); 483 484 return r; 485} 486 487#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) 488static int hdmi_audio_enable(struct omap_dss_device *dssdev) 489{ 490 int r; 491 492 mutex_lock(&hdmi.lock); 493 494 if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { 495 r = -EPERM; 496 goto err; 497 } 498 499 r = hdmi_wp_audio_enable(&hdmi.wp, true); 500 if (r) 501 goto err; 502 503 mutex_unlock(&hdmi.lock); 504 return 0; 505 506err: 507 mutex_unlock(&hdmi.lock); 508 return r; 509} 510 511static void hdmi_audio_disable(struct omap_dss_device *dssdev) 512{ 513 hdmi_wp_audio_enable(&hdmi.wp, false); 514} 515 516static int hdmi_audio_start(struct omap_dss_device *dssdev) 517{ 518 return hdmi4_audio_start(&hdmi.core, &hdmi.wp); 519} 520 521static void hdmi_audio_stop(struct omap_dss_device *dssdev) 522{ 523 hdmi4_audio_stop(&hdmi.core, &hdmi.wp); 524} 525 526static bool hdmi_audio_supported(struct omap_dss_device *dssdev) 527{ 528 bool r; 529 530 mutex_lock(&hdmi.lock); 531 532 r = hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode); 533 534 mutex_unlock(&hdmi.lock); 535 return r; 536} 537 538static int hdmi_audio_config(struct omap_dss_device *dssdev, 539 struct omap_dss_audio *audio) 540{ 541 int r; 542 u32 pclk = hdmi.cfg.timings.pixelclock; 543 544 mutex_lock(&hdmi.lock); 545 546 if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { 547 r = -EPERM; 548 goto err; 549 } 550 551 r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, audio, pclk); 552 if (r) 553 goto err; 554 555 mutex_unlock(&hdmi.lock); 556 return 0; 557 558err: 559 mutex_unlock(&hdmi.lock); 560 return r; 561} 562#else 563static int hdmi_audio_enable(struct omap_dss_device *dssdev) 564{ 565 return -EPERM; 566} 567 568static void hdmi_audio_disable(struct omap_dss_device *dssdev) 569{ 570} 571 572static int hdmi_audio_start(struct omap_dss_device *dssdev) 573{ 574 return -EPERM; 575} 576 577static void hdmi_audio_stop(struct omap_dss_device *dssdev) 578{ 579} 580 581static bool hdmi_audio_supported(struct omap_dss_device *dssdev) 582{ 583 return false; 584} 585 586static int hdmi_audio_config(struct omap_dss_device *dssdev, 587 struct omap_dss_audio *audio) 588{ 589 return -EPERM; 590} 591#endif 592 593static int hdmi_set_infoframe(struct omap_dss_device *dssdev, 594 const struct hdmi_avi_infoframe *avi) 595{ 596 hdmi.cfg.infoframe = *avi; 597 return 0; 598} 599 600static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, 601 bool hdmi_mode) 602{ 603 hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; 604 return 0; 605} 606 607static const struct omapdss_hdmi_ops hdmi_ops = { 608 .connect = hdmi_connect, 609 .disconnect = hdmi_disconnect, 610 611 .enable = hdmi_display_enable, 612 .disable = hdmi_display_disable, 613 614 .check_timings = hdmi_display_check_timing, 615 .set_timings = hdmi_display_set_timing, 616 .get_timings = hdmi_display_get_timings, 617 618 .read_edid = hdmi_read_edid, 619 .set_infoframe = hdmi_set_infoframe, 620 .set_hdmi_mode = hdmi_set_hdmi_mode, 621 622 .audio_enable = hdmi_audio_enable, 623 .audio_disable = hdmi_audio_disable, 624 .audio_start = hdmi_audio_start, 625 .audio_stop = hdmi_audio_stop, 626 .audio_supported = hdmi_audio_supported, 627 .audio_config = hdmi_audio_config, 628}; 629 630static void hdmi_init_output(struct platform_device *pdev) 631{ 632 struct omap_dss_device *out = &hdmi.output; 633 634 out->dev = &pdev->dev; 635 out->id = OMAP_DSS_OUTPUT_HDMI; 636 out->output_type = OMAP_DISPLAY_TYPE_HDMI; 637 out->name = "hdmi.0"; 638 out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; 639 out->ops.hdmi = &hdmi_ops; 640 out->owner = THIS_MODULE; 641 642 omapdss_register_output(out); 643} 644 645static void __exit hdmi_uninit_output(struct platform_device *pdev) 646{ 647 struct omap_dss_device *out = &hdmi.output; 648 649 omapdss_unregister_output(out); 650} 651 652static int hdmi_probe_of(struct platform_device *pdev) 653{ 654 struct device_node *node = pdev->dev.of_node; 655 struct device_node *ep; 656 int r; 657 658 ep = omapdss_of_get_first_endpoint(node); 659 if (!ep) 660 return 0; 661 662 r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); 663 if (r) 664 goto err; 665 666 of_node_put(ep); 667 return 0; 668 669err: 670 of_node_put(ep); 671 return r; 672} 673 674/* HDMI HW IP initialisation */ 675static int omapdss_hdmihw_probe(struct platform_device *pdev) 676{ 677 int r; 678 int irq; 679 680 hdmi.pdev = pdev; 681 682 mutex_init(&hdmi.lock); 683 684 if (pdev->dev.of_node) { 685 r = hdmi_probe_of(pdev); 686 if (r) 687 return r; 688 } 689 690 r = hdmi_wp_init(pdev, &hdmi.wp); 691 if (r) 692 return r; 693 694 r = hdmi_pll_init(pdev, &hdmi.pll); 695 if (r) 696 return r; 697 698 r = hdmi_phy_init(pdev, &hdmi.phy); 699 if (r) 700 return r; 701 702 r = hdmi4_core_init(pdev, &hdmi.core); 703 if (r) 704 return r; 705 706 r = hdmi_get_clocks(pdev); 707 if (r) { 708 DSSERR("can't get clocks\n"); 709 return r; 710 } 711 712 irq = platform_get_irq(pdev, 0); 713 if (irq < 0) { 714 DSSERR("platform_get_irq failed\n"); 715 return -ENODEV; 716 } 717 718 r = devm_request_threaded_irq(&pdev->dev, irq, 719 NULL, hdmi_irq_handler, 720 IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); 721 if (r) { 722 DSSERR("HDMI IRQ request failed\n"); 723 return r; 724 } 725 726 pm_runtime_enable(&pdev->dev); 727 728 hdmi_init_output(pdev); 729 730 dss_debugfs_create_file("hdmi", hdmi_dump_regs); 731 732 return 0; 733} 734 735static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) 736{ 737 hdmi_uninit_output(pdev); 738 739 pm_runtime_disable(&pdev->dev); 740 741 return 0; 742} 743 744static int hdmi_runtime_suspend(struct device *dev) 745{ 746 clk_disable_unprepare(hdmi.sys_clk); 747 748 dispc_runtime_put(); 749 750 return 0; 751} 752 753static int hdmi_runtime_resume(struct device *dev) 754{ 755 int r; 756 757 r = dispc_runtime_get(); 758 if (r < 0) 759 return r; 760 761 clk_prepare_enable(hdmi.sys_clk); 762 763 return 0; 764} 765 766static const struct dev_pm_ops hdmi_pm_ops = { 767 .runtime_suspend = hdmi_runtime_suspend, 768 .runtime_resume = hdmi_runtime_resume, 769}; 770 771static const struct of_device_id hdmi_of_match[] = { 772 { .compatible = "ti,omap4-hdmi", }, 773 {}, 774}; 775 776static struct platform_driver omapdss_hdmihw_driver = { 777 .probe = omapdss_hdmihw_probe, 778 .remove = __exit_p(omapdss_hdmihw_remove), 779 .driver = { 780 .name = "omapdss_hdmi", 781 .owner = THIS_MODULE, 782 .pm = &hdmi_pm_ops, 783 .of_match_table = hdmi_of_match, 784 }, 785}; 786 787int __init hdmi4_init_platform_driver(void) 788{ 789 return platform_driver_register(&omapdss_hdmihw_driver); 790} 791 792void __exit hdmi4_uninit_platform_driver(void) 793{ 794 platform_driver_unregister(&omapdss_hdmihw_driver); 795}