diff options
Diffstat (limited to 'drivers/video/sh_mobile_hdmi.c')
-rw-r--r-- | drivers/video/sh_mobile_hdmi.c | 629 |
1 files changed, 405 insertions, 224 deletions
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index ef989d94511c..55b3077ff6ff 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include <video/sh_mobile_hdmi.h> | 28 | #include <video/sh_mobile_hdmi.h> |
29 | #include <video/sh_mobile_lcdc.h> | 29 | #include <video/sh_mobile_lcdc.h> |
30 | 30 | ||
31 | #include "sh_mobile_lcdcfb.h" | ||
32 | |||
31 | #define HDMI_SYSTEM_CTRL 0x00 /* System control */ | 33 | #define HDMI_SYSTEM_CTRL 0x00 /* System control */ |
32 | #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, | 34 | #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, |
33 | bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ | 35 | bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ |
@@ -206,12 +208,15 @@ enum hotplug_state { | |||
206 | 208 | ||
207 | struct sh_hdmi { | 209 | struct sh_hdmi { |
208 | void __iomem *base; | 210 | void __iomem *base; |
209 | enum hotplug_state hp_state; | 211 | enum hotplug_state hp_state; /* hot-plug status */ |
212 | bool preprogrammed_mode; /* use a pre-programmed VIC or the external mode */ | ||
210 | struct clk *hdmi_clk; | 213 | struct clk *hdmi_clk; |
211 | struct device *dev; | 214 | struct device *dev; |
212 | struct fb_info *info; | 215 | struct fb_info *info; |
216 | struct mutex mutex; /* Protect the info pointer */ | ||
213 | struct delayed_work edid_work; | 217 | struct delayed_work edid_work; |
214 | struct fb_var_screeninfo var; | 218 | struct fb_var_screeninfo var; |
219 | struct fb_monspecs monspec; | ||
215 | }; | 220 | }; |
216 | 221 | ||
217 | static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) | 222 | static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) |
@@ -277,7 +282,7 @@ static struct snd_soc_codec_driver soc_codec_dev_sh_hdmi = { | |||
277 | */ | 282 | */ |
278 | 283 | ||
279 | /* External video parameter settings */ | 284 | /* External video parameter settings */ |
280 | static void hdmi_external_video_param(struct sh_hdmi *hdmi) | 285 | static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) |
281 | { | 286 | { |
282 | struct fb_var_screeninfo *var = &hdmi->var; | 287 | struct fb_var_screeninfo *var = &hdmi->var; |
283 | u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; | 288 | u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; |
@@ -309,9 +314,9 @@ static void hdmi_external_video_param(struct sh_hdmi *hdmi) | |||
309 | if (var->sync & FB_SYNC_VERT_HIGH_ACT) | 314 | if (var->sync & FB_SYNC_VERT_HIGH_ACT) |
310 | sync |= 8; | 315 | sync |= 8; |
311 | 316 | ||
312 | pr_debug("H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", | 317 | dev_dbg(hdmi->dev, "H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", |
313 | htotal, hblank, hdelay, var->hsync_len, | 318 | htotal, hblank, hdelay, var->hsync_len, |
314 | vtotal, vblank, vdelay, var->vsync_len, sync); | 319 | vtotal, vblank, vdelay, var->vsync_len, sync); |
315 | 320 | ||
316 | hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); | 321 | hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); |
317 | 322 | ||
@@ -336,7 +341,10 @@ static void hdmi_external_video_param(struct sh_hdmi *hdmi) | |||
336 | 341 | ||
337 | hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); | 342 | hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); |
338 | 343 | ||
339 | /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for manual mode */ | 344 | /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ |
345 | if (!hdmi->preprogrammed_mode) | ||
346 | hdmi_write(hdmi, sync | 1 | (voffset << 4), | ||
347 | HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); | ||
340 | } | 348 | } |
341 | 349 | ||
342 | /** | 350 | /** |
@@ -454,21 +462,61 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) | |||
454 | } | 462 | } |
455 | 463 | ||
456 | /** | 464 | /** |
457 | * sh_hdmi_phy_config() | 465 | * sh_hdmi_phy_config() - configure the HDMI PHY for the used video mode |
458 | */ | 466 | */ |
459 | static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) | 467 | static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) |
460 | { | 468 | { |
461 | /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ | 469 | if (hdmi->var.yres > 480) { |
462 | hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); | 470 | /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ |
463 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | 471 | /* |
464 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); | 472 | * [1:0] Speed_A |
465 | /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */ | 473 | * [3:2] Speed_B |
466 | hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); | 474 | * [4] PLLA_Bypass |
467 | hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); | 475 | * [6] DRV_TEST_EN |
468 | hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); | 476 | * [7] DRV_TEST_IN |
469 | hdmi_write(hdmi, 0x0E, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | 477 | */ |
470 | hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | 478 | hdmi_write(hdmi, 0x0f, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); |
471 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | 479 | /* PLLB_CONFIG[17], PLLA_CONFIG[17] - not in PHY datasheet */ |
480 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | ||
481 | /* | ||
482 | * [2:0] BGR_I_OFFSET | ||
483 | * [6:4] BGR_V_OFFSET | ||
484 | */ | ||
485 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); | ||
486 | /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */ | ||
487 | hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); | ||
488 | /* | ||
489 | * PLLA_CONFIG[15:8]: regulator voltage[0], CP current, | ||
490 | * LPF capacitance, LPF resistance[1] | ||
491 | */ | ||
492 | hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); | ||
493 | /* PLLB_CONFIG[7:0]: LPF resistance[0], VCO offset, VCO gain */ | ||
494 | hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); | ||
495 | /* | ||
496 | * PLLB_CONFIG[15:8]: regulator voltage[0], CP current, | ||
497 | * LPF capacitance, LPF resistance[1] | ||
498 | */ | ||
499 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | ||
500 | /* DRV_CONFIG, PE_CONFIG */ | ||
501 | hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | ||
502 | /* | ||
503 | * [2:0] AMON_SEL (4 == LPF voltage) | ||
504 | * [4] PLLA_CONFIG[16] | ||
505 | * [5] PLLB_CONFIG[16] | ||
506 | */ | ||
507 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | ||
508 | } else { | ||
509 | /* for 480p8bit 27MHz */ | ||
510 | hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); | ||
511 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | ||
512 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); | ||
513 | hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); | ||
514 | hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); | ||
515 | hdmi_write(hdmi, 0x48, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); | ||
516 | hdmi_write(hdmi, 0x0F, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | ||
517 | hdmi_write(hdmi, 0x20, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | ||
518 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | ||
519 | } | ||
472 | } | 520 | } |
473 | 521 | ||
474 | /** | 522 | /** |
@@ -476,6 +524,8 @@ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) | |||
476 | */ | 524 | */ |
477 | static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) | 525 | static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) |
478 | { | 526 | { |
527 | u8 vic; | ||
528 | |||
479 | /* AVI InfoFrame */ | 529 | /* AVI InfoFrame */ |
480 | hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX); | 530 | hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX); |
481 | 531 | ||
@@ -500,9 +550,9 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) | |||
500 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); | 550 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); |
501 | 551 | ||
502 | /* | 552 | /* |
503 | * C = No Data | 553 | * [7:6] C = Colorimetry: no data |
504 | * M = 16:9 Picture Aspect Ratio | 554 | * [5:4] M = 2: 16:9, 1: 4:3 Picture Aspect Ratio |
505 | * R = Same as picture aspect ratio | 555 | * [3:0] R = 8: Active Frame Aspect Ratio: same as picture aspect ratio |
506 | */ | 556 | */ |
507 | hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2); | 557 | hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2); |
508 | 558 | ||
@@ -516,9 +566,15 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) | |||
516 | 566 | ||
517 | /* | 567 | /* |
518 | * VIC = 1280 x 720p: ignored if external config is used | 568 | * VIC = 1280 x 720p: ignored if external config is used |
519 | * Send 2 for 720 x 480p, 16 for 1080p | 569 | * Send 2 for 720 x 480p, 16 for 1080p, ignored in external mode |
520 | */ | 570 | */ |
521 | hdmi_write(hdmi, 4, HDMI_CTRL_PKT_BUF_ACCESS_PB4); | 571 | if (hdmi->var.yres == 1080 && hdmi->var.xres == 1920) |
572 | vic = 16; | ||
573 | else if (hdmi->var.yres == 480 && hdmi->var.xres == 720) | ||
574 | vic = 2; | ||
575 | else | ||
576 | vic = 4; | ||
577 | hdmi_write(hdmi, vic, HDMI_CTRL_PKT_BUF_ACCESS_PB4); | ||
522 | 578 | ||
523 | /* PR = No Repetition */ | 579 | /* PR = No Repetition */ |
524 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); | 580 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); |
@@ -592,100 +648,6 @@ static void sh_hdmi_audio_infoframe_setup(struct sh_hdmi *hdmi) | |||
592 | } | 648 | } |
593 | 649 | ||
594 | /** | 650 | /** |
595 | * sh_hdmi_gamut_metadata_setup() - Gamut Metadata Packet of CONTROL PACKET | ||
596 | */ | ||
597 | static void sh_hdmi_gamut_metadata_setup(struct sh_hdmi *hdmi) | ||
598 | { | ||
599 | int i; | ||
600 | |||
601 | /* Gamut Metadata Packet */ | ||
602 | hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_INDEX); | ||
603 | |||
604 | /* Packet Type = 0x0A */ | ||
605 | hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
606 | /* Gamut Packet is not used, so default value */ | ||
607 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
608 | /* Gamut Packet is not used, so default value */ | ||
609 | hdmi_write(hdmi, 0x10, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
610 | |||
611 | /* GBD bytes 0 through 27 */ | ||
612 | for (i = 0; i <= 27; i++) | ||
613 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0_63H - PB27_7EH */ | ||
614 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
615 | } | ||
616 | |||
617 | /** | ||
618 | * sh_hdmi_acp_setup() - Audio Content Protection Packet (ACP) | ||
619 | */ | ||
620 | static void sh_hdmi_acp_setup(struct sh_hdmi *hdmi) | ||
621 | { | ||
622 | int i; | ||
623 | |||
624 | /* Audio Content Protection Packet (ACP) */ | ||
625 | hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_INDEX); | ||
626 | |||
627 | /* Packet Type = 0x04 */ | ||
628 | hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
629 | /* ACP_Type */ | ||
630 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
631 | /* Reserved (0) */ | ||
632 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
633 | |||
634 | /* GBD bytes 0 through 27 */ | ||
635 | for (i = 0; i <= 27; i++) | ||
636 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
637 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
638 | } | ||
639 | |||
640 | /** | ||
641 | * sh_hdmi_isrc1_setup() - ISRC1 Packet | ||
642 | */ | ||
643 | static void sh_hdmi_isrc1_setup(struct sh_hdmi *hdmi) | ||
644 | { | ||
645 | int i; | ||
646 | |||
647 | /* ISRC1 Packet */ | ||
648 | hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_INDEX); | ||
649 | |||
650 | /* Packet Type = 0x05 */ | ||
651 | hdmi_write(hdmi, 0x05, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
652 | /* ISRC_Cont, ISRC_Valid, Reserved (0), ISRC_Status */ | ||
653 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
654 | /* Reserved (0) */ | ||
655 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
656 | |||
657 | /* PB0 UPC_EAN_ISRC_0-15 */ | ||
658 | /* Bytes PB16-PB27 shall be set to a value of 0. */ | ||
659 | for (i = 0; i <= 27; i++) | ||
660 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
661 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
662 | } | ||
663 | |||
664 | /** | ||
665 | * sh_hdmi_isrc2_setup() - ISRC2 Packet | ||
666 | */ | ||
667 | static void sh_hdmi_isrc2_setup(struct sh_hdmi *hdmi) | ||
668 | { | ||
669 | int i; | ||
670 | |||
671 | /* ISRC2 Packet */ | ||
672 | hdmi_write(hdmi, 0x03, HDMI_CTRL_PKT_BUF_INDEX); | ||
673 | |||
674 | /* HB0 Packet Type = 0x06 */ | ||
675 | hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
676 | /* Reserved (0) */ | ||
677 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
678 | /* Reserved (0) */ | ||
679 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
680 | |||
681 | /* PB0 UPC_EAN_ISRC_16-31 */ | ||
682 | /* Bytes PB16-PB27 shall be set to a value of 0. */ | ||
683 | for (i = 0; i <= 27; i++) | ||
684 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
685 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
686 | } | ||
687 | |||
688 | /** | ||
689 | * sh_hdmi_configure() - Initialise HDMI for output | 651 | * sh_hdmi_configure() - Initialise HDMI for output |
690 | */ | 652 | */ |
691 | static void sh_hdmi_configure(struct sh_hdmi *hdmi) | 653 | static void sh_hdmi_configure(struct sh_hdmi *hdmi) |
@@ -705,18 +667,6 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) | |||
705 | /* Audio InfoFrame */ | 667 | /* Audio InfoFrame */ |
706 | sh_hdmi_audio_infoframe_setup(hdmi); | 668 | sh_hdmi_audio_infoframe_setup(hdmi); |
707 | 669 | ||
708 | /* Gamut Metadata packet */ | ||
709 | sh_hdmi_gamut_metadata_setup(hdmi); | ||
710 | |||
711 | /* Audio Content Protection (ACP) Packet */ | ||
712 | sh_hdmi_acp_setup(hdmi); | ||
713 | |||
714 | /* ISRC1 Packet */ | ||
715 | sh_hdmi_isrc1_setup(hdmi); | ||
716 | |||
717 | /* ISRC2 Packet */ | ||
718 | sh_hdmi_isrc2_setup(hdmi); | ||
719 | |||
720 | /* | 670 | /* |
721 | * Control packet auto send with VSYNC control: auto send | 671 | * Control packet auto send with VSYNC control: auto send |
722 | * General control, Gamut metadata, ISRC, and ACP packets | 672 | * General control, Gamut metadata, ISRC, and ACP packets |
@@ -734,17 +684,42 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) | |||
734 | hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); | 684 | hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); |
735 | } | 685 | } |
736 | 686 | ||
737 | static void sh_hdmi_read_edid(struct sh_hdmi *hdmi) | 687 | static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, |
688 | const struct fb_videomode *mode) | ||
738 | { | 689 | { |
739 | struct fb_var_screeninfo *var = &hdmi->var; | 690 | long target = PICOS2KHZ(mode->pixclock) * 1000, |
740 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 691 | rate = clk_round_rate(hdmi->hdmi_clk, target); |
741 | struct fb_videomode *lcd_cfg = &pdata->lcd_chan->lcd_cfg; | 692 | unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX; |
742 | unsigned long height = var->height, width = var->width; | 693 | |
743 | int i; | 694 | dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n", |
695 | mode->left_margin, mode->xres, | ||
696 | mode->right_margin, mode->hsync_len, | ||
697 | mode->upper_margin, mode->yres, | ||
698 | mode->lower_margin, mode->vsync_len); | ||
699 | |||
700 | dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target, | ||
701 | rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, | ||
702 | mode->refresh); | ||
703 | |||
704 | return rate_error; | ||
705 | } | ||
706 | |||
707 | static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | ||
708 | { | ||
709 | struct fb_var_screeninfo tmpvar; | ||
710 | struct fb_var_screeninfo *var = &tmpvar; | ||
711 | const struct fb_videomode *mode, *found = NULL; | ||
712 | struct fb_info *info = hdmi->info; | ||
713 | struct fb_modelist *modelist = NULL; | ||
714 | unsigned int f_width = 0, f_height = 0, f_refresh = 0; | ||
715 | unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ | ||
716 | bool exact_match = false; | ||
744 | u8 edid[128]; | 717 | u8 edid[128]; |
718 | char *forced; | ||
719 | int i; | ||
745 | 720 | ||
746 | /* Read EDID */ | 721 | /* Read EDID */ |
747 | pr_debug("Read back EDID code:"); | 722 | dev_dbg(hdmi->dev, "Read back EDID code:"); |
748 | for (i = 0; i < 128; i++) { | 723 | for (i = 0; i < 128; i++) { |
749 | edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); | 724 | edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); |
750 | #ifdef DEBUG | 725 | #ifdef DEBUG |
@@ -759,29 +734,97 @@ static void sh_hdmi_read_edid(struct sh_hdmi *hdmi) | |||
759 | #ifdef DEBUG | 734 | #ifdef DEBUG |
760 | printk(KERN_CONT "\n"); | 735 | printk(KERN_CONT "\n"); |
761 | #endif | 736 | #endif |
762 | fb_parse_edid(edid, var); | 737 | |
763 | pr_debug("%u-%u-%u-%u x %u-%u-%u-%u @ %lu kHz monitor detected\n", | 738 | fb_edid_to_monspecs(edid, &hdmi->monspec); |
764 | var->left_margin, var->xres, var->right_margin, var->hsync_len, | 739 | |
765 | var->upper_margin, var->yres, var->lower_margin, var->vsync_len, | 740 | fb_get_options("sh_mobile_lcdc", &forced); |
766 | PICOS2KHZ(var->pixclock)); | 741 | if (forced && *forced) { |
767 | 742 | /* Only primitive parsing so far */ | |
768 | /* FIXME: Use user-provided configuration instead of EDID */ | 743 | i = sscanf(forced, "%ux%u@%u", |
769 | var->width = width; | 744 | &f_width, &f_height, &f_refresh); |
770 | var->xres = lcd_cfg->xres; | 745 | if (i < 2) { |
771 | var->xres_virtual = lcd_cfg->xres; | 746 | f_width = 0; |
772 | var->left_margin = lcd_cfg->left_margin; | 747 | f_height = 0; |
773 | var->right_margin = lcd_cfg->right_margin; | 748 | } |
774 | var->hsync_len = lcd_cfg->hsync_len; | 749 | dev_dbg(hdmi->dev, "Forced mode %ux%u@%uHz\n", |
775 | var->height = height; | 750 | f_width, f_height, f_refresh); |
776 | var->yres = lcd_cfg->yres; | 751 | } |
777 | var->yres_virtual = lcd_cfg->yres * 2; | 752 | |
778 | var->upper_margin = lcd_cfg->upper_margin; | 753 | /* Walk monitor modes to find the best or the exact match */ |
779 | var->lower_margin = lcd_cfg->lower_margin; | 754 | for (i = 0, mode = hdmi->monspec.modedb; |
780 | var->vsync_len = lcd_cfg->vsync_len; | 755 | f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; |
781 | var->sync = lcd_cfg->sync; | 756 | i++, mode++) { |
782 | var->pixclock = lcd_cfg->pixclock; | 757 | unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode); |
783 | 758 | ||
784 | hdmi_external_video_param(hdmi); | 759 | /* No interest in unmatching modes */ |
760 | if (f_width != mode->xres || f_height != mode->yres) | ||
761 | continue; | ||
762 | if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) | ||
763 | /* | ||
764 | * Exact match if either the refresh rate matches or it | ||
765 | * hasn't been specified and we've found a mode, for | ||
766 | * which we can configure the clock precisely | ||
767 | */ | ||
768 | exact_match = true; | ||
769 | else if (found && found_rate_error <= rate_error) | ||
770 | /* | ||
771 | * We otherwise search for the closest matching clock | ||
772 | * rate - either if no refresh rate has been specified | ||
773 | * or we cannot find an exactly matching one | ||
774 | */ | ||
775 | continue; | ||
776 | |||
777 | /* Check if supported: sufficient fb memory, supported clock-rate */ | ||
778 | fb_videomode_to_var(var, mode); | ||
779 | |||
780 | if (info && info->fbops->fb_check_var && | ||
781 | info->fbops->fb_check_var(var, info)) { | ||
782 | exact_match = false; | ||
783 | continue; | ||
784 | } | ||
785 | |||
786 | found = mode; | ||
787 | found_rate_error = rate_error; | ||
788 | } | ||
789 | |||
790 | /* | ||
791 | * TODO 1: if no ->info is present, postpone running the config until | ||
792 | * after ->info first gets registered. | ||
793 | * TODO 2: consider registering the HDMI platform device from the LCDC | ||
794 | * driver, and passing ->info with HDMI platform data. | ||
795 | */ | ||
796 | if (info && !found) { | ||
797 | modelist = hdmi->info->modelist.next && | ||
798 | !list_empty(&hdmi->info->modelist) ? | ||
799 | list_entry(hdmi->info->modelist.next, | ||
800 | struct fb_modelist, list) : | ||
801 | NULL; | ||
802 | |||
803 | if (modelist) { | ||
804 | found = &modelist->mode; | ||
805 | found_rate_error = sh_hdmi_rate_error(hdmi, found); | ||
806 | } | ||
807 | } | ||
808 | |||
809 | /* No cookie today */ | ||
810 | if (!found) | ||
811 | return -ENXIO; | ||
812 | |||
813 | dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", | ||
814 | modelist ? "default" : "EDID", found->xres, found->yres, | ||
815 | found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error); | ||
816 | |||
817 | if ((found->xres == 720 && found->yres == 480) || | ||
818 | (found->xres == 1280 && found->yres == 720) || | ||
819 | (found->xres == 1920 && found->yres == 1080)) | ||
820 | hdmi->preprogrammed_mode = true; | ||
821 | else | ||
822 | hdmi->preprogrammed_mode = false; | ||
823 | |||
824 | fb_videomode_to_var(&hdmi->var, found); | ||
825 | sh_hdmi_external_video_param(hdmi); | ||
826 | |||
827 | return 0; | ||
785 | } | 828 | } |
786 | 829 | ||
787 | static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | 830 | static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) |
@@ -809,8 +852,8 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
809 | hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2); | 852 | hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2); |
810 | 853 | ||
811 | if (printk_ratelimit()) | 854 | if (printk_ratelimit()) |
812 | pr_debug("IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n", | 855 | dev_dbg(hdmi->dev, "IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n", |
813 | irq, status1, mask1, status2, mask2); | 856 | irq, status1, mask1, status2, mask2); |
814 | 857 | ||
815 | if (!((status1 & mask1) | (status2 & mask2))) { | 858 | if (!((status1 & mask1) | (status2 & mask2))) { |
816 | return IRQ_NONE; | 859 | return IRQ_NONE; |
@@ -821,7 +864,7 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
821 | udelay(500); | 864 | udelay(500); |
822 | 865 | ||
823 | msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS); | 866 | msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS); |
824 | pr_debug("MSENS 0x%x\n", msens); | 867 | dev_dbg(hdmi->dev, "MSENS 0x%x\n", msens); |
825 | /* Check, if hot plug & MSENS pin status are both high */ | 868 | /* Check, if hot plug & MSENS pin status are both high */ |
826 | if ((msens & 0xC0) == 0xC0) { | 869 | if ((msens & 0xC0) == 0xC0) { |
827 | /* Display plug in */ | 870 | /* Display plug in */ |
@@ -857,83 +900,176 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
857 | return IRQ_HANDLED; | 900 | return IRQ_HANDLED; |
858 | } | 901 | } |
859 | 902 | ||
860 | static void hdmi_display_on(void *arg, struct fb_info *info) | 903 | /* locking: called with info->lock held, or before register_framebuffer() */ |
904 | static void sh_hdmi_display_on(void *arg, struct fb_info *info) | ||
861 | { | 905 | { |
906 | /* | ||
907 | * info is guaranteed to be valid, when we are called, because our | ||
908 | * FB_EVENT_FB_UNBIND notify is also called with info->lock held | ||
909 | */ | ||
862 | struct sh_hdmi *hdmi = arg; | 910 | struct sh_hdmi *hdmi = arg; |
863 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 911 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
912 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
864 | 913 | ||
865 | if (info->var.xres != 1280 || info->var.yres != 720) { | 914 | dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, |
866 | dev_warn(info->device, "Unsupported framebuffer geometry %ux%u\n", | 915 | pdata->lcd_dev, info->state); |
867 | info->var.xres, info->var.yres); | 916 | |
868 | return; | 917 | /* No need to lock */ |
869 | } | 918 | hdmi->info = info; |
870 | 919 | ||
871 | pr_debug("%s(%p): state %x\n", __func__, pdata->lcd_dev, info->state); | ||
872 | /* | 920 | /* |
873 | * FIXME: not a good place to store fb_info. And we cannot nullify it | 921 | * hp_state can be set to |
874 | * even on monitor disconnect. What should the lifecycle be? | 922 | * HDMI_HOTPLUG_DISCONNECTED: on monitor unplug |
923 | * HDMI_HOTPLUG_CONNECTED: on monitor plug-in | ||
924 | * HDMI_HOTPLUG_EDID_DONE: on EDID read completion | ||
875 | */ | 925 | */ |
876 | hdmi->info = info; | ||
877 | switch (hdmi->hp_state) { | 926 | switch (hdmi->hp_state) { |
878 | case HDMI_HOTPLUG_EDID_DONE: | 927 | case HDMI_HOTPLUG_EDID_DONE: |
879 | /* PS mode d->e. All functions are active */ | 928 | /* PS mode d->e. All functions are active */ |
880 | hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); | 929 | hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); |
881 | pr_debug("HDMI running\n"); | 930 | dev_dbg(hdmi->dev, "HDMI running\n"); |
882 | break; | 931 | break; |
883 | case HDMI_HOTPLUG_DISCONNECTED: | 932 | case HDMI_HOTPLUG_DISCONNECTED: |
884 | info->state = FBINFO_STATE_SUSPENDED; | 933 | info->state = FBINFO_STATE_SUSPENDED; |
885 | default: | 934 | default: |
886 | hdmi->var = info->var; | 935 | hdmi->var = ch->display_var; |
887 | } | 936 | } |
888 | } | 937 | } |
889 | 938 | ||
890 | static void hdmi_display_off(void *arg) | 939 | /* locking: called with info->lock held */ |
940 | static void sh_hdmi_display_off(void *arg) | ||
891 | { | 941 | { |
892 | struct sh_hdmi *hdmi = arg; | 942 | struct sh_hdmi *hdmi = arg; |
893 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 943 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
894 | 944 | ||
895 | pr_debug("%s(%p)\n", __func__, pdata->lcd_dev); | 945 | dev_dbg(hdmi->dev, "%s(%p)\n", __func__, pdata->lcd_dev); |
896 | /* PS mode e->a */ | 946 | /* PS mode e->a */ |
897 | hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); | 947 | hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); |
898 | } | 948 | } |
899 | 949 | ||
950 | static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) | ||
951 | { | ||
952 | struct fb_info *info = hdmi->info; | ||
953 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
954 | struct fb_var_screeninfo *new_var = &hdmi->var, *old_var = &ch->display_var; | ||
955 | struct fb_videomode mode1, mode2; | ||
956 | |||
957 | fb_var_to_videomode(&mode1, old_var); | ||
958 | fb_var_to_videomode(&mode2, new_var); | ||
959 | |||
960 | dev_dbg(info->dev, "Old %ux%u, new %ux%u\n", | ||
961 | mode1.xres, mode1.yres, mode2.xres, mode2.yres); | ||
962 | |||
963 | if (fb_mode_is_equal(&mode1, &mode2)) | ||
964 | return false; | ||
965 | |||
966 | dev_dbg(info->dev, "Switching %u -> %u lines\n", | ||
967 | mode1.yres, mode2.yres); | ||
968 | *old_var = *new_var; | ||
969 | |||
970 | return true; | ||
971 | } | ||
972 | |||
973 | /** | ||
974 | * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock | ||
975 | * @hdmi: driver context | ||
976 | * @pixclock: pixel clock period in picoseconds | ||
977 | * return: configured positive rate if successful | ||
978 | * 0 if couldn't set the rate, but managed to enable the clock | ||
979 | * negative error, if couldn't enable the clock | ||
980 | */ | ||
981 | static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock) | ||
982 | { | ||
983 | long rate; | ||
984 | int ret; | ||
985 | |||
986 | rate = PICOS2KHZ(pixclock) * 1000; | ||
987 | rate = clk_round_rate(hdmi->hdmi_clk, rate); | ||
988 | if (rate > 0) { | ||
989 | ret = clk_set_rate(hdmi->hdmi_clk, rate); | ||
990 | if (ret < 0) { | ||
991 | dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", rate, ret); | ||
992 | rate = 0; | ||
993 | } else { | ||
994 | dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", rate); | ||
995 | } | ||
996 | } else { | ||
997 | rate = 0; | ||
998 | dev_warn(hdmi->dev, "Cannot get suitable rate: %ld\n", rate); | ||
999 | } | ||
1000 | |||
1001 | ret = clk_enable(hdmi->hdmi_clk); | ||
1002 | if (ret < 0) { | ||
1003 | dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); | ||
1004 | return ret; | ||
1005 | } | ||
1006 | |||
1007 | return rate; | ||
1008 | } | ||
1009 | |||
900 | /* Hotplug interrupt occurred, read EDID */ | 1010 | /* Hotplug interrupt occurred, read EDID */ |
901 | static void edid_work_fn(struct work_struct *work) | 1011 | static void sh_hdmi_edid_work_fn(struct work_struct *work) |
902 | { | 1012 | { |
903 | struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); | 1013 | struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); |
904 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 1014 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
1015 | struct sh_mobile_lcdc_chan *ch; | ||
1016 | int ret; | ||
905 | 1017 | ||
906 | pr_debug("%s(%p): begin, hotplug status %d\n", __func__, | 1018 | dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, |
907 | pdata->lcd_dev, hdmi->hp_state); | 1019 | pdata->lcd_dev, hdmi->hp_state); |
908 | 1020 | ||
909 | if (!pdata->lcd_dev) | 1021 | if (!pdata->lcd_dev) |
910 | return; | 1022 | return; |
911 | 1023 | ||
1024 | mutex_lock(&hdmi->mutex); | ||
1025 | |||
912 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { | 1026 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { |
913 | pm_runtime_get_sync(hdmi->dev); | ||
914 | /* A device has been plugged in */ | 1027 | /* A device has been plugged in */ |
915 | sh_hdmi_read_edid(hdmi); | 1028 | pm_runtime_get_sync(hdmi->dev); |
1029 | |||
1030 | ret = sh_hdmi_read_edid(hdmi); | ||
1031 | if (ret < 0) | ||
1032 | goto out; | ||
1033 | |||
1034 | /* Reconfigure the clock */ | ||
1035 | clk_disable(hdmi->hdmi_clk); | ||
1036 | ret = sh_hdmi_clk_configure(hdmi, hdmi->var.pixclock); | ||
1037 | if (ret < 0) | ||
1038 | goto out; | ||
1039 | |||
916 | msleep(10); | 1040 | msleep(10); |
917 | sh_hdmi_configure(hdmi); | 1041 | sh_hdmi_configure(hdmi); |
918 | /* Switched to another (d) power-save mode */ | 1042 | /* Switched to another (d) power-save mode */ |
919 | msleep(10); | 1043 | msleep(10); |
920 | 1044 | ||
921 | if (!hdmi->info) | 1045 | if (!hdmi->info) |
922 | return; | 1046 | goto out; |
1047 | |||
1048 | ch = hdmi->info->par; | ||
923 | 1049 | ||
924 | acquire_console_sem(); | 1050 | acquire_console_sem(); |
925 | 1051 | ||
926 | /* HDMI plug in */ | 1052 | /* HDMI plug in */ |
927 | hdmi->info->var = hdmi->var; | 1053 | if (!sh_hdmi_must_reconfigure(hdmi) && |
928 | if (hdmi->info->state != FBINFO_STATE_RUNNING) | 1054 | hdmi->info->state == FBINFO_STATE_RUNNING) { |
1055 | /* | ||
1056 | * First activation with the default monitor - just turn | ||
1057 | * on, if we run a resume here, the logo disappears | ||
1058 | */ | ||
1059 | if (lock_fb_info(hdmi->info)) { | ||
1060 | sh_hdmi_display_on(hdmi, hdmi->info); | ||
1061 | unlock_fb_info(hdmi->info); | ||
1062 | } | ||
1063 | } else { | ||
1064 | /* New monitor or have to wake up */ | ||
929 | fb_set_suspend(hdmi->info, 0); | 1065 | fb_set_suspend(hdmi->info, 0); |
930 | else | 1066 | } |
931 | hdmi_display_on(hdmi, hdmi->info); | ||
932 | 1067 | ||
933 | release_console_sem(); | 1068 | release_console_sem(); |
934 | } else { | 1069 | } else { |
1070 | ret = 0; | ||
935 | if (!hdmi->info) | 1071 | if (!hdmi->info) |
936 | return; | 1072 | goto out; |
937 | 1073 | ||
938 | acquire_console_sem(); | 1074 | acquire_console_sem(); |
939 | 1075 | ||
@@ -942,15 +1078,67 @@ static void edid_work_fn(struct work_struct *work) | |||
942 | 1078 | ||
943 | release_console_sem(); | 1079 | release_console_sem(); |
944 | pm_runtime_put(hdmi->dev); | 1080 | pm_runtime_put(hdmi->dev); |
1081 | fb_destroy_modedb(hdmi->monspec.modedb); | ||
945 | } | 1082 | } |
946 | 1083 | ||
947 | pr_debug("%s(%p): end\n", __func__, pdata->lcd_dev); | 1084 | out: |
1085 | if (ret < 0) | ||
1086 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | ||
1087 | mutex_unlock(&hdmi->mutex); | ||
1088 | |||
1089 | dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, pdata->lcd_dev); | ||
1090 | } | ||
1091 | |||
1092 | static int sh_hdmi_notify(struct notifier_block *nb, | ||
1093 | unsigned long action, void *data); | ||
1094 | |||
1095 | static struct notifier_block sh_hdmi_notifier = { | ||
1096 | .notifier_call = sh_hdmi_notify, | ||
1097 | }; | ||
1098 | |||
1099 | static int sh_hdmi_notify(struct notifier_block *nb, | ||
1100 | unsigned long action, void *data) | ||
1101 | { | ||
1102 | struct fb_event *event = data; | ||
1103 | struct fb_info *info = event->info; | ||
1104 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
1105 | struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; | ||
1106 | struct sh_hdmi *hdmi = board_cfg->board_data; | ||
1107 | |||
1108 | if (nb != &sh_hdmi_notifier || !hdmi || hdmi->info != info) | ||
1109 | return NOTIFY_DONE; | ||
1110 | |||
1111 | switch(action) { | ||
1112 | case FB_EVENT_FB_REGISTERED: | ||
1113 | /* Unneeded, activation taken care by sh_hdmi_display_on() */ | ||
1114 | break; | ||
1115 | case FB_EVENT_FB_UNREGISTERED: | ||
1116 | /* | ||
1117 | * We are called from unregister_framebuffer() with the | ||
1118 | * info->lock held. This is bad for us, because we can race with | ||
1119 | * the scheduled work, which has to call fb_set_suspend(), which | ||
1120 | * takes info->lock internally, so, sh_hdmi_edid_work_fn() | ||
1121 | * cannot take and hold info->lock for the whole function | ||
1122 | * duration. Using an additional lock creates a classical AB-BA | ||
1123 | * lock up. Therefore, we have to release the info->lock | ||
1124 | * temporarily, synchronise with the work queue and re-acquire | ||
1125 | * the info->lock. | ||
1126 | */ | ||
1127 | unlock_fb_info(hdmi->info); | ||
1128 | mutex_lock(&hdmi->mutex); | ||
1129 | hdmi->info = NULL; | ||
1130 | mutex_unlock(&hdmi->mutex); | ||
1131 | lock_fb_info(hdmi->info); | ||
1132 | return NOTIFY_OK; | ||
1133 | } | ||
1134 | return NOTIFY_DONE; | ||
948 | } | 1135 | } |
949 | 1136 | ||
950 | static int __init sh_hdmi_probe(struct platform_device *pdev) | 1137 | static int __init sh_hdmi_probe(struct platform_device *pdev) |
951 | { | 1138 | { |
952 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | 1139 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; |
953 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1140 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1141 | struct sh_mobile_lcdc_board_cfg *board_cfg; | ||
954 | int irq = platform_get_irq(pdev, 0), ret; | 1142 | int irq = platform_get_irq(pdev, 0), ret; |
955 | struct sh_hdmi *hdmi; | 1143 | struct sh_hdmi *hdmi; |
956 | long rate; | 1144 | long rate; |
@@ -964,10 +1152,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
964 | return -ENOMEM; | 1152 | return -ENOMEM; |
965 | } | 1153 | } |
966 | 1154 | ||
967 | ret = snd_soc_register_codec(&pdev->dev, | 1155 | mutex_init(&hdmi->mutex); |
968 | &soc_codec_dev_sh_hdmi, &sh_hdmi_dai, 1); | ||
969 | if (ret < 0) | ||
970 | goto esndreg; | ||
971 | 1156 | ||
972 | hdmi->dev = &pdev->dev; | 1157 | hdmi->dev = &pdev->dev; |
973 | 1158 | ||
@@ -978,30 +1163,14 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
978 | goto egetclk; | 1163 | goto egetclk; |
979 | } | 1164 | } |
980 | 1165 | ||
981 | rate = PICOS2KHZ(pdata->lcd_chan->lcd_cfg.pixclock) * 1000; | 1166 | /* Some arbitrary relaxed pixclock just to get things started */ |
982 | 1167 | rate = sh_hdmi_clk_configure(hdmi, 37037); | |
983 | rate = clk_round_rate(hdmi->hdmi_clk, rate); | ||
984 | if (rate < 0) { | 1168 | if (rate < 0) { |
985 | ret = rate; | 1169 | ret = rate; |
986 | dev_err(&pdev->dev, "Cannot get suitable rate: %ld\n", rate); | ||
987 | goto erate; | 1170 | goto erate; |
988 | } | 1171 | } |
989 | 1172 | ||
990 | ret = clk_set_rate(hdmi->hdmi_clk, rate); | 1173 | dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); |
991 | if (ret < 0) { | ||
992 | dev_err(&pdev->dev, "Cannot set rate %ld: %d\n", rate, ret); | ||
993 | goto erate; | ||
994 | } | ||
995 | |||
996 | pr_debug("HDMI set frequency %lu\n", rate); | ||
997 | |||
998 | ret = clk_enable(hdmi->hdmi_clk); | ||
999 | if (ret < 0) { | ||
1000 | dev_err(&pdev->dev, "Cannot enable clock: %d\n", ret); | ||
1001 | goto eclkenable; | ||
1002 | } | ||
1003 | |||
1004 | dev_info(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); | ||
1005 | 1174 | ||
1006 | if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { | 1175 | if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { |
1007 | dev_err(&pdev->dev, "HDMI register region already claimed\n"); | 1176 | dev_err(&pdev->dev, "HDMI register region already claimed\n"); |
@@ -1018,18 +1187,18 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1018 | 1187 | ||
1019 | platform_set_drvdata(pdev, hdmi); | 1188 | platform_set_drvdata(pdev, hdmi); |
1020 | 1189 | ||
1021 | #if 1 | ||
1022 | /* Product and revision IDs are 0 in sh-mobile version */ | 1190 | /* Product and revision IDs are 0 in sh-mobile version */ |
1023 | dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", | 1191 | dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", |
1024 | hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); | 1192 | hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); |
1025 | #endif | ||
1026 | 1193 | ||
1027 | /* Set up LCDC callbacks */ | 1194 | /* Set up LCDC callbacks */ |
1028 | pdata->lcd_chan->board_cfg.board_data = hdmi; | 1195 | board_cfg = &pdata->lcd_chan->board_cfg; |
1029 | pdata->lcd_chan->board_cfg.display_on = hdmi_display_on; | 1196 | board_cfg->owner = THIS_MODULE; |
1030 | pdata->lcd_chan->board_cfg.display_off = hdmi_display_off; | 1197 | board_cfg->board_data = hdmi; |
1198 | board_cfg->display_on = sh_hdmi_display_on; | ||
1199 | board_cfg->display_off = sh_hdmi_display_off; | ||
1031 | 1200 | ||
1032 | INIT_DELAYED_WORK(&hdmi->edid_work, edid_work_fn); | 1201 | INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn); |
1033 | 1202 | ||
1034 | pm_runtime_enable(&pdev->dev); | 1203 | pm_runtime_enable(&pdev->dev); |
1035 | pm_runtime_resume(&pdev->dev); | 1204 | pm_runtime_resume(&pdev->dev); |
@@ -1041,8 +1210,17 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1041 | goto ereqirq; | 1210 | goto ereqirq; |
1042 | } | 1211 | } |
1043 | 1212 | ||
1213 | ret = snd_soc_register_codec(&pdev->dev, | ||
1214 | &soc_codec_dev_sh_hdmi, &sh_hdmi_dai, 1); | ||
1215 | if (ret < 0) { | ||
1216 | dev_err(&pdev->dev, "codec registration failed\n"); | ||
1217 | goto ecodec; | ||
1218 | } | ||
1219 | |||
1044 | return 0; | 1220 | return 0; |
1045 | 1221 | ||
1222 | ecodec: | ||
1223 | free_irq(irq, hdmi); | ||
1046 | ereqirq: | 1224 | ereqirq: |
1047 | pm_runtime_disable(&pdev->dev); | 1225 | pm_runtime_disable(&pdev->dev); |
1048 | iounmap(hdmi->base); | 1226 | iounmap(hdmi->base); |
@@ -1050,12 +1228,10 @@ emap: | |||
1050 | release_mem_region(res->start, resource_size(res)); | 1228 | release_mem_region(res->start, resource_size(res)); |
1051 | ereqreg: | 1229 | ereqreg: |
1052 | clk_disable(hdmi->hdmi_clk); | 1230 | clk_disable(hdmi->hdmi_clk); |
1053 | eclkenable: | ||
1054 | erate: | 1231 | erate: |
1055 | clk_put(hdmi->hdmi_clk); | 1232 | clk_put(hdmi->hdmi_clk); |
1056 | egetclk: | 1233 | egetclk: |
1057 | snd_soc_unregister_codec(&pdev->dev); | 1234 | mutex_destroy(&hdmi->mutex); |
1058 | esndreg: | ||
1059 | kfree(hdmi); | 1235 | kfree(hdmi); |
1060 | 1236 | ||
1061 | return ret; | 1237 | return ret; |
@@ -1066,21 +1242,26 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) | |||
1066 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | 1242 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; |
1067 | struct sh_hdmi *hdmi = platform_get_drvdata(pdev); | 1243 | struct sh_hdmi *hdmi = platform_get_drvdata(pdev); |
1068 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1244 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1245 | struct sh_mobile_lcdc_board_cfg *board_cfg = &pdata->lcd_chan->board_cfg; | ||
1069 | int irq = platform_get_irq(pdev, 0); | 1246 | int irq = platform_get_irq(pdev, 0); |
1070 | 1247 | ||
1071 | snd_soc_unregister_codec(&pdev->dev); | 1248 | snd_soc_unregister_codec(&pdev->dev); |
1072 | 1249 | ||
1073 | pdata->lcd_chan->board_cfg.display_on = NULL; | 1250 | board_cfg->display_on = NULL; |
1074 | pdata->lcd_chan->board_cfg.display_off = NULL; | 1251 | board_cfg->display_off = NULL; |
1075 | pdata->lcd_chan->board_cfg.board_data = NULL; | 1252 | board_cfg->board_data = NULL; |
1253 | board_cfg->owner = NULL; | ||
1076 | 1254 | ||
1255 | /* No new work will be scheduled, wait for running ISR */ | ||
1077 | free_irq(irq, hdmi); | 1256 | free_irq(irq, hdmi); |
1078 | pm_runtime_disable(&pdev->dev); | 1257 | /* Wait for already scheduled work */ |
1079 | cancel_delayed_work_sync(&hdmi->edid_work); | 1258 | cancel_delayed_work_sync(&hdmi->edid_work); |
1259 | pm_runtime_disable(&pdev->dev); | ||
1080 | clk_disable(hdmi->hdmi_clk); | 1260 | clk_disable(hdmi->hdmi_clk); |
1081 | clk_put(hdmi->hdmi_clk); | 1261 | clk_put(hdmi->hdmi_clk); |
1082 | iounmap(hdmi->base); | 1262 | iounmap(hdmi->base); |
1083 | release_mem_region(res->start, resource_size(res)); | 1263 | release_mem_region(res->start, resource_size(res)); |
1264 | mutex_destroy(&hdmi->mutex); | ||
1084 | kfree(hdmi); | 1265 | kfree(hdmi); |
1085 | 1266 | ||
1086 | return 0; | 1267 | return 0; |