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.13-rc7 696 lines 13 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 int hdmi_init_regulator(void) 85{ 86 struct regulator *reg; 87 88 if (hdmi.vdda_hdmi_dac_reg != NULL) 89 return 0; 90 91 reg = devm_regulator_get(&hdmi.pdev->dev, "vdda_hdmi_dac"); 92 93 /* DT HACK: try VDAC to make omapdss work for o4 sdp/panda */ 94 if (IS_ERR(reg)) 95 reg = devm_regulator_get(&hdmi.pdev->dev, "VDAC"); 96 97 if (IS_ERR(reg)) { 98 DSSERR("can't get VDDA_HDMI_DAC regulator\n"); 99 return PTR_ERR(reg); 100 } 101 102 hdmi.vdda_hdmi_dac_reg = reg; 103 104 return 0; 105} 106 107static int hdmi_power_on_core(struct omap_dss_device *dssdev) 108{ 109 int r; 110 111 r = regulator_enable(hdmi.vdda_hdmi_dac_reg); 112 if (r) 113 return r; 114 115 r = hdmi_runtime_get(); 116 if (r) 117 goto err_runtime_get; 118 119 /* Make selection of HDMI in DSS */ 120 dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); 121 122 hdmi.core_enabled = true; 123 124 return 0; 125 126err_runtime_get: 127 regulator_disable(hdmi.vdda_hdmi_dac_reg); 128 129 return r; 130} 131 132static void hdmi_power_off_core(struct omap_dss_device *dssdev) 133{ 134 hdmi.core_enabled = false; 135 136 hdmi_runtime_put(); 137 regulator_disable(hdmi.vdda_hdmi_dac_reg); 138} 139 140static int hdmi_power_on_full(struct omap_dss_device *dssdev) 141{ 142 int r; 143 struct omap_video_timings *p; 144 struct omap_overlay_manager *mgr = hdmi.output.manager; 145 unsigned long phy; 146 147 r = hdmi_power_on_core(dssdev); 148 if (r) 149 return r; 150 151 dss_mgr_disable(mgr); 152 153 p = &hdmi.cfg.timings; 154 155 DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); 156 157 phy = p->pixel_clock; 158 159 hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy); 160 161 hdmi_wp_video_stop(&hdmi.wp); 162 163 /* config the PLL and PHY hdmi_set_pll_pwrfirst */ 164 r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp); 165 if (r) { 166 DSSDBG("Failed to lock PLL\n"); 167 goto err_pll_enable; 168 } 169 170 r = hdmi_phy_enable(&hdmi.phy, &hdmi.wp, &hdmi.cfg); 171 if (r) { 172 DSSDBG("Failed to start PHY\n"); 173 goto err_phy_enable; 174 } 175 176 hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); 177 178 /* bypass TV gamma table */ 179 dispc_enable_gamma_table(0); 180 181 /* tv size */ 182 dss_mgr_set_timings(mgr, p); 183 184 r = hdmi_wp_video_start(&hdmi.wp); 185 if (r) 186 goto err_vid_enable; 187 188 r = dss_mgr_enable(mgr); 189 if (r) 190 goto err_mgr_enable; 191 192 return 0; 193 194err_mgr_enable: 195 hdmi_wp_video_stop(&hdmi.wp); 196err_vid_enable: 197 hdmi_phy_disable(&hdmi.phy, &hdmi.wp); 198err_phy_enable: 199 hdmi_pll_disable(&hdmi.pll, &hdmi.wp); 200err_pll_enable: 201 hdmi_power_off_core(dssdev); 202 return -EIO; 203} 204 205static void hdmi_power_off_full(struct omap_dss_device *dssdev) 206{ 207 struct omap_overlay_manager *mgr = hdmi.output.manager; 208 209 dss_mgr_disable(mgr); 210 211 hdmi_wp_video_stop(&hdmi.wp); 212 hdmi_phy_disable(&hdmi.phy, &hdmi.wp); 213 hdmi_pll_disable(&hdmi.pll, &hdmi.wp); 214 215 hdmi_power_off_core(dssdev); 216} 217 218static int hdmi_display_check_timing(struct omap_dss_device *dssdev, 219 struct omap_video_timings *timings) 220{ 221 struct hdmi_cm cm; 222 223 cm = hdmi_get_code(timings); 224 if (cm.code == -1) 225 return -EINVAL; 226 227 return 0; 228 229} 230 231static void hdmi_display_set_timing(struct omap_dss_device *dssdev, 232 struct omap_video_timings *timings) 233{ 234 struct hdmi_cm cm; 235 const struct hdmi_config *t; 236 237 mutex_lock(&hdmi.lock); 238 239 cm = hdmi_get_code(timings); 240 hdmi.cfg.cm = cm; 241 242 t = hdmi_get_timings(cm.mode, cm.code); 243 if (t != NULL) { 244 hdmi.cfg = *t; 245 246 dispc_set_tv_pclk(t->timings.pixel_clock * 1000); 247 } 248 249 mutex_unlock(&hdmi.lock); 250} 251 252static void hdmi_display_get_timings(struct omap_dss_device *dssdev, 253 struct omap_video_timings *timings) 254{ 255 const struct hdmi_config *cfg; 256 struct hdmi_cm cm = hdmi.cfg.cm; 257 258 cfg = hdmi_get_timings(cm.mode, cm.code); 259 if (cfg == NULL) 260 cfg = hdmi_default_timing(); 261 262 memcpy(timings, &cfg->timings, sizeof(cfg->timings)); 263} 264 265static void hdmi_dump_regs(struct seq_file *s) 266{ 267 mutex_lock(&hdmi.lock); 268 269 if (hdmi_runtime_get()) { 270 mutex_unlock(&hdmi.lock); 271 return; 272 } 273 274 hdmi_wp_dump(&hdmi.wp, s); 275 hdmi_pll_dump(&hdmi.pll, s); 276 hdmi_phy_dump(&hdmi.phy, s); 277 hdmi4_core_dump(&hdmi.core, s); 278 279 hdmi_runtime_put(); 280 mutex_unlock(&hdmi.lock); 281} 282 283static int read_edid(u8 *buf, int len) 284{ 285 int r; 286 287 mutex_lock(&hdmi.lock); 288 289 r = hdmi_runtime_get(); 290 BUG_ON(r); 291 292 r = hdmi4_read_edid(&hdmi.core, buf, len); 293 294 hdmi_runtime_put(); 295 mutex_unlock(&hdmi.lock); 296 297 return r; 298} 299 300static int hdmi_display_enable(struct omap_dss_device *dssdev) 301{ 302 struct omap_dss_device *out = &hdmi.output; 303 int r = 0; 304 305 DSSDBG("ENTER hdmi_display_enable\n"); 306 307 mutex_lock(&hdmi.lock); 308 309 if (out == NULL || out->manager == NULL) { 310 DSSERR("failed to enable display: no output/manager\n"); 311 r = -ENODEV; 312 goto err0; 313 } 314 315 r = hdmi_power_on_full(dssdev); 316 if (r) { 317 DSSERR("failed to power on device\n"); 318 goto err0; 319 } 320 321 mutex_unlock(&hdmi.lock); 322 return 0; 323 324err0: 325 mutex_unlock(&hdmi.lock); 326 return r; 327} 328 329static void hdmi_display_disable(struct omap_dss_device *dssdev) 330{ 331 DSSDBG("Enter hdmi_display_disable\n"); 332 333 mutex_lock(&hdmi.lock); 334 335 hdmi_power_off_full(dssdev); 336 337 mutex_unlock(&hdmi.lock); 338} 339 340static int hdmi_core_enable(struct omap_dss_device *dssdev) 341{ 342 int r = 0; 343 344 DSSDBG("ENTER omapdss_hdmi_core_enable\n"); 345 346 mutex_lock(&hdmi.lock); 347 348 r = hdmi_power_on_core(dssdev); 349 if (r) { 350 DSSERR("failed to power on device\n"); 351 goto err0; 352 } 353 354 mutex_unlock(&hdmi.lock); 355 return 0; 356 357err0: 358 mutex_unlock(&hdmi.lock); 359 return r; 360} 361 362static void hdmi_core_disable(struct omap_dss_device *dssdev) 363{ 364 DSSDBG("Enter omapdss_hdmi_core_disable\n"); 365 366 mutex_lock(&hdmi.lock); 367 368 hdmi_power_off_core(dssdev); 369 370 mutex_unlock(&hdmi.lock); 371} 372 373static int hdmi_get_clocks(struct platform_device *pdev) 374{ 375 struct clk *clk; 376 377 clk = devm_clk_get(&pdev->dev, "sys_clk"); 378 if (IS_ERR(clk)) { 379 DSSERR("can't get sys_clk\n"); 380 return PTR_ERR(clk); 381 } 382 383 hdmi.sys_clk = clk; 384 385 return 0; 386} 387 388static int hdmi_connect(struct omap_dss_device *dssdev, 389 struct omap_dss_device *dst) 390{ 391 struct omap_overlay_manager *mgr; 392 int r; 393 394 r = hdmi_init_regulator(); 395 if (r) 396 return r; 397 398 mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); 399 if (!mgr) 400 return -ENODEV; 401 402 r = dss_mgr_connect(mgr, dssdev); 403 if (r) 404 return r; 405 406 r = omapdss_output_set_device(dssdev, dst); 407 if (r) { 408 DSSERR("failed to connect output to new device: %s\n", 409 dst->name); 410 dss_mgr_disconnect(mgr, dssdev); 411 return r; 412 } 413 414 return 0; 415} 416 417static void hdmi_disconnect(struct omap_dss_device *dssdev, 418 struct omap_dss_device *dst) 419{ 420 WARN_ON(dst != dssdev->dst); 421 422 if (dst != dssdev->dst) 423 return; 424 425 omapdss_output_unset_device(dssdev); 426 427 if (dssdev->manager) 428 dss_mgr_disconnect(dssdev->manager, dssdev); 429} 430 431static int hdmi_read_edid(struct omap_dss_device *dssdev, 432 u8 *edid, int len) 433{ 434 bool need_enable; 435 int r; 436 437 need_enable = hdmi.core_enabled == false; 438 439 if (need_enable) { 440 r = hdmi_core_enable(dssdev); 441 if (r) 442 return r; 443 } 444 445 r = read_edid(edid, len); 446 447 if (need_enable) 448 hdmi_core_disable(dssdev); 449 450 return r; 451} 452 453#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) 454static int hdmi_audio_enable(struct omap_dss_device *dssdev) 455{ 456 int r; 457 458 mutex_lock(&hdmi.lock); 459 460 if (!hdmi_mode_has_audio(hdmi.cfg.cm.mode)) { 461 r = -EPERM; 462 goto err; 463 } 464 465 r = hdmi_wp_audio_enable(&hdmi.wp, true); 466 if (r) 467 goto err; 468 469 mutex_unlock(&hdmi.lock); 470 return 0; 471 472err: 473 mutex_unlock(&hdmi.lock); 474 return r; 475} 476 477static void hdmi_audio_disable(struct omap_dss_device *dssdev) 478{ 479 hdmi_wp_audio_enable(&hdmi.wp, false); 480} 481 482static int hdmi_audio_start(struct omap_dss_device *dssdev) 483{ 484 return hdmi4_audio_start(&hdmi.core, &hdmi.wp); 485} 486 487static void hdmi_audio_stop(struct omap_dss_device *dssdev) 488{ 489 hdmi4_audio_stop(&hdmi.core, &hdmi.wp); 490} 491 492static bool hdmi_audio_supported(struct omap_dss_device *dssdev) 493{ 494 bool r; 495 496 mutex_lock(&hdmi.lock); 497 498 r = hdmi_mode_has_audio(hdmi.cfg.cm.mode); 499 500 mutex_unlock(&hdmi.lock); 501 return r; 502} 503 504static int hdmi_audio_config(struct omap_dss_device *dssdev, 505 struct omap_dss_audio *audio) 506{ 507 int r; 508 u32 pclk = hdmi.cfg.timings.pixel_clock; 509 510 mutex_lock(&hdmi.lock); 511 512 if (!hdmi_mode_has_audio(hdmi.cfg.cm.mode)) { 513 r = -EPERM; 514 goto err; 515 } 516 517 r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, audio, pclk); 518 if (r) 519 goto err; 520 521 mutex_unlock(&hdmi.lock); 522 return 0; 523 524err: 525 mutex_unlock(&hdmi.lock); 526 return r; 527} 528#else 529static int hdmi_audio_enable(struct omap_dss_device *dssdev) 530{ 531 return -EPERM; 532} 533 534static void hdmi_audio_disable(struct omap_dss_device *dssdev) 535{ 536} 537 538static int hdmi_audio_start(struct omap_dss_device *dssdev) 539{ 540 return -EPERM; 541} 542 543static void hdmi_audio_stop(struct omap_dss_device *dssdev) 544{ 545} 546 547static bool hdmi_audio_supported(struct omap_dss_device *dssdev) 548{ 549 return false; 550} 551 552static int hdmi_audio_config(struct omap_dss_device *dssdev, 553 struct omap_dss_audio *audio) 554{ 555 return -EPERM; 556} 557#endif 558 559static const struct omapdss_hdmi_ops hdmi_ops = { 560 .connect = hdmi_connect, 561 .disconnect = hdmi_disconnect, 562 563 .enable = hdmi_display_enable, 564 .disable = hdmi_display_disable, 565 566 .check_timings = hdmi_display_check_timing, 567 .set_timings = hdmi_display_set_timing, 568 .get_timings = hdmi_display_get_timings, 569 570 .read_edid = hdmi_read_edid, 571 572 .audio_enable = hdmi_audio_enable, 573 .audio_disable = hdmi_audio_disable, 574 .audio_start = hdmi_audio_start, 575 .audio_stop = hdmi_audio_stop, 576 .audio_supported = hdmi_audio_supported, 577 .audio_config = hdmi_audio_config, 578}; 579 580static void hdmi_init_output(struct platform_device *pdev) 581{ 582 struct omap_dss_device *out = &hdmi.output; 583 584 out->dev = &pdev->dev; 585 out->id = OMAP_DSS_OUTPUT_HDMI; 586 out->output_type = OMAP_DISPLAY_TYPE_HDMI; 587 out->name = "hdmi.0"; 588 out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; 589 out->ops.hdmi = &hdmi_ops; 590 out->owner = THIS_MODULE; 591 592 omapdss_register_output(out); 593} 594 595static void __exit hdmi_uninit_output(struct platform_device *pdev) 596{ 597 struct omap_dss_device *out = &hdmi.output; 598 599 omapdss_unregister_output(out); 600} 601 602/* HDMI HW IP initialisation */ 603static int omapdss_hdmihw_probe(struct platform_device *pdev) 604{ 605 int r; 606 607 hdmi.pdev = pdev; 608 609 mutex_init(&hdmi.lock); 610 611 r = hdmi_wp_init(pdev, &hdmi.wp); 612 if (r) 613 return r; 614 615 r = hdmi_pll_init(pdev, &hdmi.pll); 616 if (r) 617 return r; 618 619 r = hdmi_phy_init(pdev, &hdmi.phy); 620 if (r) 621 return r; 622 623 r = hdmi4_core_init(pdev, &hdmi.core); 624 if (r) 625 return r; 626 627 r = hdmi_get_clocks(pdev); 628 if (r) { 629 DSSERR("can't get clocks\n"); 630 return r; 631 } 632 633 pm_runtime_enable(&pdev->dev); 634 635 hdmi_init_output(pdev); 636 637 dss_debugfs_create_file("hdmi", hdmi_dump_regs); 638 639 return 0; 640} 641 642static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) 643{ 644 hdmi_uninit_output(pdev); 645 646 pm_runtime_disable(&pdev->dev); 647 648 return 0; 649} 650 651static int hdmi_runtime_suspend(struct device *dev) 652{ 653 clk_disable_unprepare(hdmi.sys_clk); 654 655 dispc_runtime_put(); 656 657 return 0; 658} 659 660static int hdmi_runtime_resume(struct device *dev) 661{ 662 int r; 663 664 r = dispc_runtime_get(); 665 if (r < 0) 666 return r; 667 668 clk_prepare_enable(hdmi.sys_clk); 669 670 return 0; 671} 672 673static const struct dev_pm_ops hdmi_pm_ops = { 674 .runtime_suspend = hdmi_runtime_suspend, 675 .runtime_resume = hdmi_runtime_resume, 676}; 677 678static struct platform_driver omapdss_hdmihw_driver = { 679 .probe = omapdss_hdmihw_probe, 680 .remove = __exit_p(omapdss_hdmihw_remove), 681 .driver = { 682 .name = "omapdss_hdmi", 683 .owner = THIS_MODULE, 684 .pm = &hdmi_pm_ops, 685 }, 686}; 687 688int __init hdmi4_init_platform_driver(void) 689{ 690 return platform_driver_register(&omapdss_hdmihw_driver); 691} 692 693void __exit hdmi4_uninit_platform_driver(void) 694{ 695 platform_driver_unregister(&omapdss_hdmihw_driver); 696}