diff options
Diffstat (limited to 'drivers/video/omap2/dss/dss.c')
-rw-r--r-- | drivers/video/omap2/dss/dss.c | 257 |
1 files changed, 167 insertions, 90 deletions
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 04b4586113e3..2ab1c3e96553 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c | |||
@@ -31,11 +31,11 @@ | |||
31 | #include <linux/clk.h> | 31 | #include <linux/clk.h> |
32 | #include <linux/platform_device.h> | 32 | #include <linux/platform_device.h> |
33 | #include <linux/pm_runtime.h> | 33 | #include <linux/pm_runtime.h> |
34 | #include <linux/gfp.h> | ||
34 | 35 | ||
35 | #include <video/omapdss.h> | 36 | #include <video/omapdss.h> |
36 | 37 | ||
37 | #include <plat/cpu.h> | 38 | #include <plat/cpu.h> |
38 | #include <plat/clock.h> | ||
39 | 39 | ||
40 | #include "dss.h" | 40 | #include "dss.h" |
41 | #include "dss_features.h" | 41 | #include "dss_features.h" |
@@ -65,6 +65,13 @@ struct dss_reg { | |||
65 | static int dss_runtime_get(void); | 65 | static int dss_runtime_get(void); |
66 | static void dss_runtime_put(void); | 66 | static void dss_runtime_put(void); |
67 | 67 | ||
68 | struct dss_features { | ||
69 | u8 fck_div_max; | ||
70 | u8 dss_fck_multiplier; | ||
71 | const char *clk_name; | ||
72 | int (*dpi_select_source)(enum omap_channel channel); | ||
73 | }; | ||
74 | |||
68 | static struct { | 75 | static struct { |
69 | struct platform_device *pdev; | 76 | struct platform_device *pdev; |
70 | void __iomem *base; | 77 | void __iomem *base; |
@@ -83,6 +90,8 @@ static struct { | |||
83 | 90 | ||
84 | bool ctx_valid; | 91 | bool ctx_valid; |
85 | u32 ctx[DSS_SZ_REGS / sizeof(u32)]; | 92 | u32 ctx[DSS_SZ_REGS / sizeof(u32)]; |
93 | |||
94 | const struct dss_features *feat; | ||
86 | } dss; | 95 | } dss; |
87 | 96 | ||
88 | static const char * const dss_generic_clk_source_names[] = { | 97 | static const char * const dss_generic_clk_source_names[] = { |
@@ -144,7 +153,7 @@ static void dss_restore_context(void) | |||
144 | #undef SR | 153 | #undef SR |
145 | #undef RR | 154 | #undef RR |
146 | 155 | ||
147 | void dss_sdi_init(u8 datapairs) | 156 | void dss_sdi_init(int datapairs) |
148 | { | 157 | { |
149 | u32 l; | 158 | u32 l; |
150 | 159 | ||
@@ -236,7 +245,6 @@ const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src) | |||
236 | return dss_generic_clk_source_names[clk_src]; | 245 | return dss_generic_clk_source_names[clk_src]; |
237 | } | 246 | } |
238 | 247 | ||
239 | |||
240 | void dss_dump_clocks(struct seq_file *s) | 248 | void dss_dump_clocks(struct seq_file *s) |
241 | { | 249 | { |
242 | unsigned long dpll4_ck_rate; | 250 | unsigned long dpll4_ck_rate; |
@@ -259,18 +267,10 @@ void dss_dump_clocks(struct seq_file *s) | |||
259 | 267 | ||
260 | seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate); | 268 | seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate); |
261 | 269 | ||
262 | if (cpu_is_omap3630() || cpu_is_omap44xx()) | 270 | seq_printf(s, "%s (%s) = %lu / %lu * %d = %lu\n", |
263 | seq_printf(s, "%s (%s) = %lu / %lu = %lu\n", | 271 | fclk_name, fclk_real_name, dpll4_ck_rate, |
264 | fclk_name, fclk_real_name, | 272 | dpll4_ck_rate / dpll4_m4_ck_rate, |
265 | dpll4_ck_rate, | 273 | dss.feat->dss_fck_multiplier, fclk_rate); |
266 | dpll4_ck_rate / dpll4_m4_ck_rate, | ||
267 | fclk_rate); | ||
268 | else | ||
269 | seq_printf(s, "%s (%s) = %lu / %lu * 2 = %lu\n", | ||
270 | fclk_name, fclk_real_name, | ||
271 | dpll4_ck_rate, | ||
272 | dpll4_ck_rate / dpll4_m4_ck_rate, | ||
273 | fclk_rate); | ||
274 | } else { | 274 | } else { |
275 | seq_printf(s, "%s (%s) = %lu\n", | 275 | seq_printf(s, "%s (%s) = %lu\n", |
276 | fclk_name, fclk_real_name, | 276 | fclk_name, fclk_real_name, |
@@ -431,31 +431,6 @@ enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel) | |||
431 | } | 431 | } |
432 | } | 432 | } |
433 | 433 | ||
434 | /* calculate clock rates using dividers in cinfo */ | ||
435 | int dss_calc_clock_rates(struct dss_clock_info *cinfo) | ||
436 | { | ||
437 | if (dss.dpll4_m4_ck) { | ||
438 | unsigned long prate; | ||
439 | u16 fck_div_max = 16; | ||
440 | |||
441 | if (cpu_is_omap3630() || cpu_is_omap44xx()) | ||
442 | fck_div_max = 32; | ||
443 | |||
444 | if (cinfo->fck_div > fck_div_max || cinfo->fck_div == 0) | ||
445 | return -EINVAL; | ||
446 | |||
447 | prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); | ||
448 | |||
449 | cinfo->fck = prate / cinfo->fck_div; | ||
450 | } else { | ||
451 | if (cinfo->fck_div != 0) | ||
452 | return -EINVAL; | ||
453 | cinfo->fck = clk_get_rate(dss.dss_clk); | ||
454 | } | ||
455 | |||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | int dss_set_clock_div(struct dss_clock_info *cinfo) | 434 | int dss_set_clock_div(struct dss_clock_info *cinfo) |
460 | { | 435 | { |
461 | if (dss.dpll4_m4_ck) { | 436 | if (dss.dpll4_m4_ck) { |
@@ -478,26 +453,6 @@ int dss_set_clock_div(struct dss_clock_info *cinfo) | |||
478 | return 0; | 453 | return 0; |
479 | } | 454 | } |
480 | 455 | ||
481 | int dss_get_clock_div(struct dss_clock_info *cinfo) | ||
482 | { | ||
483 | cinfo->fck = clk_get_rate(dss.dss_clk); | ||
484 | |||
485 | if (dss.dpll4_m4_ck) { | ||
486 | unsigned long prate; | ||
487 | |||
488 | prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); | ||
489 | |||
490 | if (cpu_is_omap3630() || cpu_is_omap44xx()) | ||
491 | cinfo->fck_div = prate / (cinfo->fck); | ||
492 | else | ||
493 | cinfo->fck_div = prate / (cinfo->fck / 2); | ||
494 | } else { | ||
495 | cinfo->fck_div = 0; | ||
496 | } | ||
497 | |||
498 | return 0; | ||
499 | } | ||
500 | |||
501 | unsigned long dss_get_dpll4_rate(void) | 456 | unsigned long dss_get_dpll4_rate(void) |
502 | { | 457 | { |
503 | if (dss.dpll4_m4_ck) | 458 | if (dss.dpll4_m4_ck) |
@@ -515,7 +470,7 @@ int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info *dss_cinfo, | |||
515 | 470 | ||
516 | unsigned long fck, max_dss_fck; | 471 | unsigned long fck, max_dss_fck; |
517 | 472 | ||
518 | u16 fck_div, fck_div_max = 16; | 473 | u16 fck_div; |
519 | 474 | ||
520 | int match = 0; | 475 | int match = 0; |
521 | int min_fck_per_pck; | 476 | int min_fck_per_pck; |
@@ -525,9 +480,8 @@ int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info *dss_cinfo, | |||
525 | max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); | 480 | max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); |
526 | 481 | ||
527 | fck = clk_get_rate(dss.dss_clk); | 482 | fck = clk_get_rate(dss.dss_clk); |
528 | if (req_pck == dss.cache_req_pck && | 483 | if (req_pck == dss.cache_req_pck && prate == dss.cache_prate && |
529 | ((cpu_is_omap34xx() && prate == dss.cache_prate) || | 484 | dss.cache_dss_cinfo.fck == fck) { |
530 | dss.cache_dss_cinfo.fck == fck)) { | ||
531 | DSSDBG("dispc clock info found from cache.\n"); | 485 | DSSDBG("dispc clock info found from cache.\n"); |
532 | *dss_cinfo = dss.cache_dss_cinfo; | 486 | *dss_cinfo = dss.cache_dss_cinfo; |
533 | *dispc_cinfo = dss.cache_dispc_cinfo; | 487 | *dispc_cinfo = dss.cache_dispc_cinfo; |
@@ -564,16 +518,10 @@ retry: | |||
564 | 518 | ||
565 | goto found; | 519 | goto found; |
566 | } else { | 520 | } else { |
567 | if (cpu_is_omap3630() || cpu_is_omap44xx()) | 521 | for (fck_div = dss.feat->fck_div_max; fck_div > 0; --fck_div) { |
568 | fck_div_max = 32; | ||
569 | |||
570 | for (fck_div = fck_div_max; fck_div > 0; --fck_div) { | ||
571 | struct dispc_clock_info cur_dispc; | 522 | struct dispc_clock_info cur_dispc; |
572 | 523 | ||
573 | if (fck_div_max == 32) | 524 | fck = prate / fck_div * dss.feat->dss_fck_multiplier; |
574 | fck = prate / fck_div; | ||
575 | else | ||
576 | fck = prate / fck_div * 2; | ||
577 | 525 | ||
578 | if (fck > max_dss_fck) | 526 | if (fck > max_dss_fck) |
579 | continue; | 527 | continue; |
@@ -648,9 +596,18 @@ void dss_set_dac_pwrdn_bgz(bool enable) | |||
648 | REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */ | 596 | REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */ |
649 | } | 597 | } |
650 | 598 | ||
651 | void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select hdmi) | 599 | void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select src) |
652 | { | 600 | { |
653 | REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15); /* VENC_HDMI_SWITCH */ | 601 | enum omap_display_type dp; |
602 | dp = dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_DIGIT); | ||
603 | |||
604 | /* Complain about invalid selections */ | ||
605 | WARN_ON((src == DSS_VENC_TV_CLK) && !(dp & OMAP_DISPLAY_TYPE_VENC)); | ||
606 | WARN_ON((src == DSS_HDMI_M_PCLK) && !(dp & OMAP_DISPLAY_TYPE_HDMI)); | ||
607 | |||
608 | /* Select only if we have options */ | ||
609 | if ((dp & OMAP_DISPLAY_TYPE_VENC) && (dp & OMAP_DISPLAY_TYPE_HDMI)) | ||
610 | REG_FLD_MOD(DSS_CONTROL, src, 15, 15); /* VENC_HDMI_SWITCH */ | ||
654 | } | 611 | } |
655 | 612 | ||
656 | enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void) | 613 | enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void) |
@@ -661,9 +618,71 @@ enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void) | |||
661 | if ((displays & OMAP_DISPLAY_TYPE_HDMI) == 0) | 618 | if ((displays & OMAP_DISPLAY_TYPE_HDMI) == 0) |
662 | return DSS_VENC_TV_CLK; | 619 | return DSS_VENC_TV_CLK; |
663 | 620 | ||
621 | if ((displays & OMAP_DISPLAY_TYPE_VENC) == 0) | ||
622 | return DSS_HDMI_M_PCLK; | ||
623 | |||
664 | return REG_GET(DSS_CONTROL, 15, 15); | 624 | return REG_GET(DSS_CONTROL, 15, 15); |
665 | } | 625 | } |
666 | 626 | ||
627 | static int dss_dpi_select_source_omap2_omap3(enum omap_channel channel) | ||
628 | { | ||
629 | if (channel != OMAP_DSS_CHANNEL_LCD) | ||
630 | return -EINVAL; | ||
631 | |||
632 | return 0; | ||
633 | } | ||
634 | |||
635 | static int dss_dpi_select_source_omap4(enum omap_channel channel) | ||
636 | { | ||
637 | int val; | ||
638 | |||
639 | switch (channel) { | ||
640 | case OMAP_DSS_CHANNEL_LCD2: | ||
641 | val = 0; | ||
642 | break; | ||
643 | case OMAP_DSS_CHANNEL_DIGIT: | ||
644 | val = 1; | ||
645 | break; | ||
646 | default: | ||
647 | return -EINVAL; | ||
648 | } | ||
649 | |||
650 | REG_FLD_MOD(DSS_CONTROL, val, 17, 17); | ||
651 | |||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | static int dss_dpi_select_source_omap5(enum omap_channel channel) | ||
656 | { | ||
657 | int val; | ||
658 | |||
659 | switch (channel) { | ||
660 | case OMAP_DSS_CHANNEL_LCD: | ||
661 | val = 1; | ||
662 | break; | ||
663 | case OMAP_DSS_CHANNEL_LCD2: | ||
664 | val = 2; | ||
665 | break; | ||
666 | case OMAP_DSS_CHANNEL_LCD3: | ||
667 | val = 3; | ||
668 | break; | ||
669 | case OMAP_DSS_CHANNEL_DIGIT: | ||
670 | val = 0; | ||
671 | break; | ||
672 | default: | ||
673 | return -EINVAL; | ||
674 | } | ||
675 | |||
676 | REG_FLD_MOD(DSS_CONTROL, val, 17, 16); | ||
677 | |||
678 | return 0; | ||
679 | } | ||
680 | |||
681 | int dss_dpi_select_source(enum omap_channel channel) | ||
682 | { | ||
683 | return dss.feat->dpi_select_source(channel); | ||
684 | } | ||
685 | |||
667 | static int dss_get_clocks(void) | 686 | static int dss_get_clocks(void) |
668 | { | 687 | { |
669 | struct clk *clk; | 688 | struct clk *clk; |
@@ -678,22 +697,11 @@ static int dss_get_clocks(void) | |||
678 | 697 | ||
679 | dss.dss_clk = clk; | 698 | dss.dss_clk = clk; |
680 | 699 | ||
681 | if (cpu_is_omap34xx()) { | 700 | clk = clk_get(NULL, dss.feat->clk_name); |
682 | clk = clk_get(NULL, "dpll4_m4_ck"); | 701 | if (IS_ERR(clk)) { |
683 | if (IS_ERR(clk)) { | 702 | DSSERR("Failed to get %s\n", dss.feat->clk_name); |
684 | DSSERR("Failed to get dpll4_m4_ck\n"); | 703 | r = PTR_ERR(clk); |
685 | r = PTR_ERR(clk); | 704 | goto err; |
686 | goto err; | ||
687 | } | ||
688 | } else if (cpu_is_omap44xx()) { | ||
689 | clk = clk_get(NULL, "dpll_per_m5x2_ck"); | ||
690 | if (IS_ERR(clk)) { | ||
691 | DSSERR("Failed to get dpll_per_m5x2_ck\n"); | ||
692 | r = PTR_ERR(clk); | ||
693 | goto err; | ||
694 | } | ||
695 | } else { /* omap24xx */ | ||
696 | clk = NULL; | ||
697 | } | 705 | } |
698 | 706 | ||
699 | dss.dpll4_m4_ck = clk; | 707 | dss.dpll4_m4_ck = clk; |
@@ -749,6 +757,71 @@ void dss_debug_dump_clocks(struct seq_file *s) | |||
749 | } | 757 | } |
750 | #endif | 758 | #endif |
751 | 759 | ||
760 | static const struct dss_features omap24xx_dss_feats __initconst = { | ||
761 | .fck_div_max = 16, | ||
762 | .dss_fck_multiplier = 2, | ||
763 | .clk_name = NULL, | ||
764 | .dpi_select_source = &dss_dpi_select_source_omap2_omap3, | ||
765 | }; | ||
766 | |||
767 | static const struct dss_features omap34xx_dss_feats __initconst = { | ||
768 | .fck_div_max = 16, | ||
769 | .dss_fck_multiplier = 2, | ||
770 | .clk_name = "dpll4_m4_ck", | ||
771 | .dpi_select_source = &dss_dpi_select_source_omap2_omap3, | ||
772 | }; | ||
773 | |||
774 | static const struct dss_features omap3630_dss_feats __initconst = { | ||
775 | .fck_div_max = 32, | ||
776 | .dss_fck_multiplier = 1, | ||
777 | .clk_name = "dpll4_m4_ck", | ||
778 | .dpi_select_source = &dss_dpi_select_source_omap2_omap3, | ||
779 | }; | ||
780 | |||
781 | static const struct dss_features omap44xx_dss_feats __initconst = { | ||
782 | .fck_div_max = 32, | ||
783 | .dss_fck_multiplier = 1, | ||
784 | .clk_name = "dpll_per_m5x2_ck", | ||
785 | .dpi_select_source = &dss_dpi_select_source_omap4, | ||
786 | }; | ||
787 | |||
788 | static const struct dss_features omap54xx_dss_feats __initconst = { | ||
789 | .fck_div_max = 64, | ||
790 | .dss_fck_multiplier = 1, | ||
791 | .clk_name = "dpll_per_h12x2_ck", | ||
792 | .dpi_select_source = &dss_dpi_select_source_omap5, | ||
793 | }; | ||
794 | |||
795 | static int __init dss_init_features(struct device *dev) | ||
796 | { | ||
797 | const struct dss_features *src; | ||
798 | struct dss_features *dst; | ||
799 | |||
800 | dst = devm_kzalloc(dev, sizeof(*dst), GFP_KERNEL); | ||
801 | if (!dst) { | ||
802 | dev_err(dev, "Failed to allocate local DSS Features\n"); | ||
803 | return -ENOMEM; | ||
804 | } | ||
805 | |||
806 | if (cpu_is_omap24xx()) | ||
807 | src = &omap24xx_dss_feats; | ||
808 | else if (cpu_is_omap34xx()) | ||
809 | src = &omap34xx_dss_feats; | ||
810 | else if (cpu_is_omap3630()) | ||
811 | src = &omap3630_dss_feats; | ||
812 | else if (cpu_is_omap44xx()) | ||
813 | src = &omap44xx_dss_feats; | ||
814 | else if (soc_is_omap54xx()) | ||
815 | src = &omap54xx_dss_feats; | ||
816 | else | ||
817 | return -ENODEV; | ||
818 | |||
819 | memcpy(dst, src, sizeof(*dst)); | ||
820 | dss.feat = dst; | ||
821 | |||
822 | return 0; | ||
823 | } | ||
824 | |||
752 | /* DSS HW IP initialisation */ | 825 | /* DSS HW IP initialisation */ |
753 | static int __init omap_dsshw_probe(struct platform_device *pdev) | 826 | static int __init omap_dsshw_probe(struct platform_device *pdev) |
754 | { | 827 | { |
@@ -758,6 +831,10 @@ static int __init omap_dsshw_probe(struct platform_device *pdev) | |||
758 | 831 | ||
759 | dss.pdev = pdev; | 832 | dss.pdev = pdev; |
760 | 833 | ||
834 | r = dss_init_features(&dss.pdev->dev); | ||
835 | if (r) | ||
836 | return r; | ||
837 | |||
761 | dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); | 838 | dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); |
762 | if (!dss_mem) { | 839 | if (!dss_mem) { |
763 | DSSERR("can't get IORESOURCE_MEM DSS\n"); | 840 | DSSERR("can't get IORESOURCE_MEM DSS\n"); |