diff options
Diffstat (limited to 'drivers/video/omap2/dss/dss.c')
-rw-r--r-- | drivers/video/omap2/dss/dss.c | 583 |
1 files changed, 135 insertions, 448 deletions
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index d9489d5c4f08..0f9c3a6457a5 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include <linux/delay.h> | 28 | #include <linux/delay.h> |
29 | #include <linux/seq_file.h> | 29 | #include <linux/seq_file.h> |
30 | #include <linux/clk.h> | 30 | #include <linux/clk.h> |
31 | #include <linux/platform_device.h> | ||
32 | #include <linux/pm_runtime.h> | ||
31 | 33 | ||
32 | #include <video/omapdss.h> | 34 | #include <video/omapdss.h> |
33 | #include <plat/clock.h> | 35 | #include <plat/clock.h> |
@@ -59,15 +61,9 @@ struct dss_reg { | |||
59 | static struct { | 61 | static struct { |
60 | struct platform_device *pdev; | 62 | struct platform_device *pdev; |
61 | void __iomem *base; | 63 | void __iomem *base; |
62 | int ctx_id; | ||
63 | 64 | ||
64 | struct clk *dpll4_m4_ck; | 65 | struct clk *dpll4_m4_ck; |
65 | struct clk *dss_ick; | 66 | struct clk *dss_clk; |
66 | struct clk *dss_fck; | ||
67 | struct clk *dss_sys_clk; | ||
68 | struct clk *dss_tv_fck; | ||
69 | struct clk *dss_video_fck; | ||
70 | unsigned num_clks_enabled; | ||
71 | 67 | ||
72 | unsigned long cache_req_pck; | 68 | unsigned long cache_req_pck; |
73 | unsigned long cache_prate; | 69 | unsigned long cache_prate; |
@@ -78,6 +74,7 @@ static struct { | |||
78 | enum omap_dss_clk_source dispc_clk_source; | 74 | enum omap_dss_clk_source dispc_clk_source; |
79 | enum omap_dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS]; | 75 | enum omap_dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS]; |
80 | 76 | ||
77 | bool ctx_valid; | ||
81 | u32 ctx[DSS_SZ_REGS / sizeof(u32)]; | 78 | u32 ctx[DSS_SZ_REGS / sizeof(u32)]; |
82 | } dss; | 79 | } dss; |
83 | 80 | ||
@@ -87,13 +84,6 @@ static const char * const dss_generic_clk_source_names[] = { | |||
87 | [OMAP_DSS_CLK_SRC_FCK] = "DSS_FCK", | 84 | [OMAP_DSS_CLK_SRC_FCK] = "DSS_FCK", |
88 | }; | 85 | }; |
89 | 86 | ||
90 | static void dss_clk_enable_all_no_ctx(void); | ||
91 | static void dss_clk_disable_all_no_ctx(void); | ||
92 | static void dss_clk_enable_no_ctx(enum dss_clock clks); | ||
93 | static void dss_clk_disable_no_ctx(enum dss_clock clks); | ||
94 | |||
95 | static int _omap_dss_wait_reset(void); | ||
96 | |||
97 | static inline void dss_write_reg(const struct dss_reg idx, u32 val) | 87 | static inline void dss_write_reg(const struct dss_reg idx, u32 val) |
98 | { | 88 | { |
99 | __raw_writel(val, dss.base + idx.idx); | 89 | __raw_writel(val, dss.base + idx.idx); |
@@ -109,12 +99,10 @@ static inline u32 dss_read_reg(const struct dss_reg idx) | |||
109 | #define RR(reg) \ | 99 | #define RR(reg) \ |
110 | dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)]) | 100 | dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)]) |
111 | 101 | ||
112 | void dss_save_context(void) | 102 | static void dss_save_context(void) |
113 | { | 103 | { |
114 | if (cpu_is_omap24xx()) | 104 | DSSDBG("dss_save_context\n"); |
115 | return; | ||
116 | 105 | ||
117 | SR(SYSCONFIG); | ||
118 | SR(CONTROL); | 106 | SR(CONTROL); |
119 | 107 | ||
120 | if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & | 108 | if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & |
@@ -122,14 +110,19 @@ void dss_save_context(void) | |||
122 | SR(SDI_CONTROL); | 110 | SR(SDI_CONTROL); |
123 | SR(PLL_CONTROL); | 111 | SR(PLL_CONTROL); |
124 | } | 112 | } |
113 | |||
114 | dss.ctx_valid = true; | ||
115 | |||
116 | DSSDBG("context saved\n"); | ||
125 | } | 117 | } |
126 | 118 | ||
127 | void dss_restore_context(void) | 119 | static void dss_restore_context(void) |
128 | { | 120 | { |
129 | if (_omap_dss_wait_reset()) | 121 | DSSDBG("dss_restore_context\n"); |
130 | DSSERR("DSS not coming out of reset after sleep\n"); | 122 | |
123 | if (!dss.ctx_valid) | ||
124 | return; | ||
131 | 125 | ||
132 | RR(SYSCONFIG); | ||
133 | RR(CONTROL); | 126 | RR(CONTROL); |
134 | 127 | ||
135 | if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & | 128 | if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & |
@@ -137,6 +130,8 @@ void dss_restore_context(void) | |||
137 | RR(SDI_CONTROL); | 130 | RR(SDI_CONTROL); |
138 | RR(PLL_CONTROL); | 131 | RR(PLL_CONTROL); |
139 | } | 132 | } |
133 | |||
134 | DSSDBG("context restored\n"); | ||
140 | } | 135 | } |
141 | 136 | ||
142 | #undef SR | 137 | #undef SR |
@@ -234,6 +229,7 @@ const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src) | |||
234 | return dss_generic_clk_source_names[clk_src]; | 229 | return dss_generic_clk_source_names[clk_src]; |
235 | } | 230 | } |
236 | 231 | ||
232 | |||
237 | void dss_dump_clocks(struct seq_file *s) | 233 | void dss_dump_clocks(struct seq_file *s) |
238 | { | 234 | { |
239 | unsigned long dpll4_ck_rate; | 235 | unsigned long dpll4_ck_rate; |
@@ -241,13 +237,14 @@ void dss_dump_clocks(struct seq_file *s) | |||
241 | const char *fclk_name, *fclk_real_name; | 237 | const char *fclk_name, *fclk_real_name; |
242 | unsigned long fclk_rate; | 238 | unsigned long fclk_rate; |
243 | 239 | ||
244 | dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); | 240 | if (dss_runtime_get()) |
241 | return; | ||
245 | 242 | ||
246 | seq_printf(s, "- DSS -\n"); | 243 | seq_printf(s, "- DSS -\n"); |
247 | 244 | ||
248 | fclk_name = dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_FCK); | 245 | fclk_name = dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_FCK); |
249 | fclk_real_name = dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_FCK); | 246 | fclk_real_name = dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_FCK); |
250 | fclk_rate = dss_clk_get_rate(DSS_CLK_FCK); | 247 | fclk_rate = clk_get_rate(dss.dss_clk); |
251 | 248 | ||
252 | if (dss.dpll4_m4_ck) { | 249 | if (dss.dpll4_m4_ck) { |
253 | dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); | 250 | dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); |
@@ -273,14 +270,15 @@ void dss_dump_clocks(struct seq_file *s) | |||
273 | fclk_rate); | 270 | fclk_rate); |
274 | } | 271 | } |
275 | 272 | ||
276 | dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); | 273 | dss_runtime_put(); |
277 | } | 274 | } |
278 | 275 | ||
279 | void dss_dump_regs(struct seq_file *s) | 276 | void dss_dump_regs(struct seq_file *s) |
280 | { | 277 | { |
281 | #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) | 278 | #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) |
282 | 279 | ||
283 | dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); | 280 | if (dss_runtime_get()) |
281 | return; | ||
284 | 282 | ||
285 | DUMPREG(DSS_REVISION); | 283 | DUMPREG(DSS_REVISION); |
286 | DUMPREG(DSS_SYSCONFIG); | 284 | DUMPREG(DSS_SYSCONFIG); |
@@ -294,7 +292,7 @@ void dss_dump_regs(struct seq_file *s) | |||
294 | DUMPREG(DSS_SDI_STATUS); | 292 | DUMPREG(DSS_SDI_STATUS); |
295 | } | 293 | } |
296 | 294 | ||
297 | dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); | 295 | dss_runtime_put(); |
298 | #undef DUMPREG | 296 | #undef DUMPREG |
299 | } | 297 | } |
300 | 298 | ||
@@ -437,7 +435,7 @@ int dss_calc_clock_rates(struct dss_clock_info *cinfo) | |||
437 | } else { | 435 | } else { |
438 | if (cinfo->fck_div != 0) | 436 | if (cinfo->fck_div != 0) |
439 | return -EINVAL; | 437 | return -EINVAL; |
440 | cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK); | 438 | cinfo->fck = clk_get_rate(dss.dss_clk); |
441 | } | 439 | } |
442 | 440 | ||
443 | return 0; | 441 | return 0; |
@@ -467,7 +465,7 @@ int dss_set_clock_div(struct dss_clock_info *cinfo) | |||
467 | 465 | ||
468 | int dss_get_clock_div(struct dss_clock_info *cinfo) | 466 | int dss_get_clock_div(struct dss_clock_info *cinfo) |
469 | { | 467 | { |
470 | cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK); | 468 | cinfo->fck = clk_get_rate(dss.dss_clk); |
471 | 469 | ||
472 | if (dss.dpll4_m4_ck) { | 470 | if (dss.dpll4_m4_ck) { |
473 | unsigned long prate; | 471 | unsigned long prate; |
@@ -512,7 +510,7 @@ int dss_calc_clock_div(bool is_tft, unsigned long req_pck, | |||
512 | 510 | ||
513 | max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); | 511 | max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); |
514 | 512 | ||
515 | fck = dss_clk_get_rate(DSS_CLK_FCK); | 513 | fck = clk_get_rate(dss.dss_clk); |
516 | if (req_pck == dss.cache_req_pck && | 514 | if (req_pck == dss.cache_req_pck && |
517 | ((cpu_is_omap34xx() && prate == dss.cache_prate) || | 515 | ((cpu_is_omap34xx() && prate == dss.cache_prate) || |
518 | dss.cache_dss_cinfo.fck == fck)) { | 516 | dss.cache_dss_cinfo.fck == fck)) { |
@@ -539,7 +537,7 @@ retry: | |||
539 | if (dss.dpll4_m4_ck == NULL) { | 537 | if (dss.dpll4_m4_ck == NULL) { |
540 | struct dispc_clock_info cur_dispc; | 538 | struct dispc_clock_info cur_dispc; |
541 | /* XXX can we change the clock on omap2? */ | 539 | /* XXX can we change the clock on omap2? */ |
542 | fck = dss_clk_get_rate(DSS_CLK_FCK); | 540 | fck = clk_get_rate(dss.dss_clk); |
543 | fck_div = 1; | 541 | fck_div = 1; |
544 | 542 | ||
545 | dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc); | 543 | dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc); |
@@ -616,28 +614,6 @@ found: | |||
616 | return 0; | 614 | return 0; |
617 | } | 615 | } |
618 | 616 | ||
619 | static int _omap_dss_wait_reset(void) | ||
620 | { | ||
621 | int t = 0; | ||
622 | |||
623 | while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) { | ||
624 | if (++t > 1000) { | ||
625 | DSSERR("soft reset failed\n"); | ||
626 | return -ENODEV; | ||
627 | } | ||
628 | udelay(1); | ||
629 | } | ||
630 | |||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static int _omap_dss_reset(void) | ||
635 | { | ||
636 | /* Soft reset */ | ||
637 | REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1); | ||
638 | return _omap_dss_wait_reset(); | ||
639 | } | ||
640 | |||
641 | void dss_set_venc_output(enum omap_dss_venc_type type) | 617 | void dss_set_venc_output(enum omap_dss_venc_type type) |
642 | { | 618 | { |
643 | int l = 0; | 619 | int l = 0; |
@@ -663,424 +639,88 @@ void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select hdmi) | |||
663 | REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15); /* VENC_HDMI_SWITCH */ | 639 | REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15); /* VENC_HDMI_SWITCH */ |
664 | } | 640 | } |
665 | 641 | ||
666 | static int dss_init(void) | 642 | static int dss_get_clocks(void) |
667 | { | 643 | { |
644 | struct clk *clk; | ||
668 | int r; | 645 | int r; |
669 | u32 rev; | ||
670 | struct resource *dss_mem; | ||
671 | struct clk *dpll4_m4_ck; | ||
672 | 646 | ||
673 | dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); | 647 | clk = clk_get(&dss.pdev->dev, "fck"); |
674 | if (!dss_mem) { | 648 | if (IS_ERR(clk)) { |
675 | DSSERR("can't get IORESOURCE_MEM DSS\n"); | 649 | DSSERR("can't get clock fck\n"); |
676 | r = -EINVAL; | 650 | r = PTR_ERR(clk); |
677 | goto fail0; | 651 | goto err; |
678 | } | ||
679 | dss.base = ioremap(dss_mem->start, resource_size(dss_mem)); | ||
680 | if (!dss.base) { | ||
681 | DSSERR("can't ioremap DSS\n"); | ||
682 | r = -ENOMEM; | ||
683 | goto fail0; | ||
684 | } | 652 | } |
685 | 653 | ||
686 | /* disable LCD and DIGIT output. This seems to fix the synclost | 654 | dss.dss_clk = clk; |
687 | * problem that we get, if the bootloader starts the DSS and | ||
688 | * the kernel resets it */ | ||
689 | omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440); | ||
690 | |||
691 | #ifdef CONFIG_OMAP2_DSS_SLEEP_BEFORE_RESET | ||
692 | /* We need to wait here a bit, otherwise we sometimes start to | ||
693 | * get synclost errors, and after that only power cycle will | ||
694 | * restore DSS functionality. I have no idea why this happens. | ||
695 | * And we have to wait _before_ resetting the DSS, but after | ||
696 | * enabling clocks. | ||
697 | * | ||
698 | * This bug was at least present on OMAP3430. It's unknown | ||
699 | * if it happens on OMAP2 or OMAP3630. | ||
700 | */ | ||
701 | msleep(50); | ||
702 | #endif | ||
703 | |||
704 | _omap_dss_reset(); | ||
705 | 655 | ||
706 | /* autoidle */ | ||
707 | REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0); | ||
708 | |||
709 | /* Select DPLL */ | ||
710 | REG_FLD_MOD(DSS_CONTROL, 0, 0, 0); | ||
711 | |||
712 | #ifdef CONFIG_OMAP2_DSS_VENC | ||
713 | REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */ | ||
714 | REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */ | ||
715 | REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */ | ||
716 | #endif | ||
717 | if (cpu_is_omap34xx()) { | 656 | if (cpu_is_omap34xx()) { |
718 | dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck"); | 657 | clk = clk_get(NULL, "dpll4_m4_ck"); |
719 | if (IS_ERR(dpll4_m4_ck)) { | 658 | if (IS_ERR(clk)) { |
720 | DSSERR("Failed to get dpll4_m4_ck\n"); | 659 | DSSERR("Failed to get dpll4_m4_ck\n"); |
721 | r = PTR_ERR(dpll4_m4_ck); | 660 | r = PTR_ERR(clk); |
722 | goto fail1; | 661 | goto err; |
723 | } | 662 | } |
724 | } else if (cpu_is_omap44xx()) { | 663 | } else if (cpu_is_omap44xx()) { |
725 | dpll4_m4_ck = clk_get(NULL, "dpll_per_m5x2_ck"); | 664 | clk = clk_get(NULL, "dpll_per_m5x2_ck"); |
726 | if (IS_ERR(dpll4_m4_ck)) { | 665 | if (IS_ERR(clk)) { |
727 | DSSERR("Failed to get dpll4_m4_ck\n"); | 666 | DSSERR("Failed to get dpll_per_m5x2_ck\n"); |
728 | r = PTR_ERR(dpll4_m4_ck); | 667 | r = PTR_ERR(clk); |
729 | goto fail1; | 668 | goto err; |
730 | } | 669 | } |
731 | } else { /* omap24xx */ | 670 | } else { /* omap24xx */ |
732 | dpll4_m4_ck = NULL; | 671 | clk = NULL; |
733 | } | 672 | } |
734 | 673 | ||
735 | dss.dpll4_m4_ck = dpll4_m4_ck; | 674 | dss.dpll4_m4_ck = clk; |
736 | |||
737 | dss.dsi_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; | ||
738 | dss.dsi_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; | ||
739 | dss.dispc_clk_source = OMAP_DSS_CLK_SRC_FCK; | ||
740 | dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; | ||
741 | dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; | ||
742 | |||
743 | dss_save_context(); | ||
744 | |||
745 | rev = dss_read_reg(DSS_REVISION); | ||
746 | printk(KERN_INFO "OMAP DSS rev %d.%d\n", | ||
747 | FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); | ||
748 | 675 | ||
749 | return 0; | 676 | return 0; |
750 | 677 | ||
751 | fail1: | 678 | err: |
752 | iounmap(dss.base); | 679 | if (dss.dss_clk) |
753 | fail0: | 680 | clk_put(dss.dss_clk); |
754 | return r; | ||
755 | } | ||
756 | |||
757 | static void dss_exit(void) | ||
758 | { | ||
759 | if (dss.dpll4_m4_ck) | 681 | if (dss.dpll4_m4_ck) |
760 | clk_put(dss.dpll4_m4_ck); | 682 | clk_put(dss.dpll4_m4_ck); |
761 | 683 | ||
762 | iounmap(dss.base); | ||
763 | } | ||
764 | |||
765 | /* CONTEXT */ | ||
766 | static int dss_get_ctx_id(void) | ||
767 | { | ||
768 | struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data; | ||
769 | int r; | ||
770 | |||
771 | if (!pdata->board_data->get_last_off_on_transaction_id) | ||
772 | return 0; | ||
773 | r = pdata->board_data->get_last_off_on_transaction_id(&dss.pdev->dev); | ||
774 | if (r < 0) { | ||
775 | dev_err(&dss.pdev->dev, "getting transaction ID failed, " | ||
776 | "will force context restore\n"); | ||
777 | r = -1; | ||
778 | } | ||
779 | return r; | ||
780 | } | ||
781 | |||
782 | int dss_need_ctx_restore(void) | ||
783 | { | ||
784 | int id = dss_get_ctx_id(); | ||
785 | |||
786 | if (id < 0 || id != dss.ctx_id) { | ||
787 | DSSDBG("ctx id %d -> id %d\n", | ||
788 | dss.ctx_id, id); | ||
789 | dss.ctx_id = id; | ||
790 | return 1; | ||
791 | } else { | ||
792 | return 0; | ||
793 | } | ||
794 | } | ||
795 | |||
796 | static void save_all_ctx(void) | ||
797 | { | ||
798 | DSSDBG("save context\n"); | ||
799 | |||
800 | dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK); | ||
801 | |||
802 | dss_save_context(); | ||
803 | dispc_save_context(); | ||
804 | #ifdef CONFIG_OMAP2_DSS_DSI | ||
805 | dsi_save_context(); | ||
806 | #endif | ||
807 | |||
808 | dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK); | ||
809 | } | ||
810 | |||
811 | static void restore_all_ctx(void) | ||
812 | { | ||
813 | DSSDBG("restore context\n"); | ||
814 | |||
815 | dss_clk_enable_all_no_ctx(); | ||
816 | |||
817 | dss_restore_context(); | ||
818 | dispc_restore_context(); | ||
819 | #ifdef CONFIG_OMAP2_DSS_DSI | ||
820 | dsi_restore_context(); | ||
821 | #endif | ||
822 | |||
823 | dss_clk_disable_all_no_ctx(); | ||
824 | } | ||
825 | |||
826 | static int dss_get_clock(struct clk **clock, const char *clk_name) | ||
827 | { | ||
828 | struct clk *clk; | ||
829 | |||
830 | clk = clk_get(&dss.pdev->dev, clk_name); | ||
831 | |||
832 | if (IS_ERR(clk)) { | ||
833 | DSSERR("can't get clock %s", clk_name); | ||
834 | return PTR_ERR(clk); | ||
835 | } | ||
836 | |||
837 | *clock = clk; | ||
838 | |||
839 | DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk)); | ||
840 | |||
841 | return 0; | ||
842 | } | ||
843 | |||
844 | static int dss_get_clocks(void) | ||
845 | { | ||
846 | int r; | ||
847 | struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data; | ||
848 | |||
849 | dss.dss_ick = NULL; | ||
850 | dss.dss_fck = NULL; | ||
851 | dss.dss_sys_clk = NULL; | ||
852 | dss.dss_tv_fck = NULL; | ||
853 | dss.dss_video_fck = NULL; | ||
854 | |||
855 | r = dss_get_clock(&dss.dss_ick, "ick"); | ||
856 | if (r) | ||
857 | goto err; | ||
858 | |||
859 | r = dss_get_clock(&dss.dss_fck, "fck"); | ||
860 | if (r) | ||
861 | goto err; | ||
862 | |||
863 | if (!pdata->opt_clock_available) { | ||
864 | r = -ENODEV; | ||
865 | goto err; | ||
866 | } | ||
867 | |||
868 | if (pdata->opt_clock_available("sys_clk")) { | ||
869 | r = dss_get_clock(&dss.dss_sys_clk, "sys_clk"); | ||
870 | if (r) | ||
871 | goto err; | ||
872 | } | ||
873 | |||
874 | if (pdata->opt_clock_available("tv_clk")) { | ||
875 | r = dss_get_clock(&dss.dss_tv_fck, "tv_clk"); | ||
876 | if (r) | ||
877 | goto err; | ||
878 | } | ||
879 | |||
880 | if (pdata->opt_clock_available("video_clk")) { | ||
881 | r = dss_get_clock(&dss.dss_video_fck, "video_clk"); | ||
882 | if (r) | ||
883 | goto err; | ||
884 | } | ||
885 | |||
886 | return 0; | ||
887 | |||
888 | err: | ||
889 | if (dss.dss_ick) | ||
890 | clk_put(dss.dss_ick); | ||
891 | if (dss.dss_fck) | ||
892 | clk_put(dss.dss_fck); | ||
893 | if (dss.dss_sys_clk) | ||
894 | clk_put(dss.dss_sys_clk); | ||
895 | if (dss.dss_tv_fck) | ||
896 | clk_put(dss.dss_tv_fck); | ||
897 | if (dss.dss_video_fck) | ||
898 | clk_put(dss.dss_video_fck); | ||
899 | |||
900 | return r; | 684 | return r; |
901 | } | 685 | } |
902 | 686 | ||
903 | static void dss_put_clocks(void) | 687 | static void dss_put_clocks(void) |
904 | { | 688 | { |
905 | if (dss.dss_video_fck) | 689 | if (dss.dpll4_m4_ck) |
906 | clk_put(dss.dss_video_fck); | 690 | clk_put(dss.dpll4_m4_ck); |
907 | if (dss.dss_tv_fck) | 691 | clk_put(dss.dss_clk); |
908 | clk_put(dss.dss_tv_fck); | ||
909 | if (dss.dss_sys_clk) | ||
910 | clk_put(dss.dss_sys_clk); | ||
911 | clk_put(dss.dss_fck); | ||
912 | clk_put(dss.dss_ick); | ||
913 | } | ||
914 | |||
915 | unsigned long dss_clk_get_rate(enum dss_clock clk) | ||
916 | { | ||
917 | switch (clk) { | ||
918 | case DSS_CLK_ICK: | ||
919 | return clk_get_rate(dss.dss_ick); | ||
920 | case DSS_CLK_FCK: | ||
921 | return clk_get_rate(dss.dss_fck); | ||
922 | case DSS_CLK_SYSCK: | ||
923 | return clk_get_rate(dss.dss_sys_clk); | ||
924 | case DSS_CLK_TVFCK: | ||
925 | return clk_get_rate(dss.dss_tv_fck); | ||
926 | case DSS_CLK_VIDFCK: | ||
927 | return clk_get_rate(dss.dss_video_fck); | ||
928 | } | ||
929 | |||
930 | BUG(); | ||
931 | return 0; | ||
932 | } | ||
933 | |||
934 | static unsigned count_clk_bits(enum dss_clock clks) | ||
935 | { | ||
936 | unsigned num_clks = 0; | ||
937 | |||
938 | if (clks & DSS_CLK_ICK) | ||
939 | ++num_clks; | ||
940 | if (clks & DSS_CLK_FCK) | ||
941 | ++num_clks; | ||
942 | if (clks & DSS_CLK_SYSCK) | ||
943 | ++num_clks; | ||
944 | if (clks & DSS_CLK_TVFCK) | ||
945 | ++num_clks; | ||
946 | if (clks & DSS_CLK_VIDFCK) | ||
947 | ++num_clks; | ||
948 | |||
949 | return num_clks; | ||
950 | } | ||
951 | |||
952 | static void dss_clk_enable_no_ctx(enum dss_clock clks) | ||
953 | { | ||
954 | unsigned num_clks = count_clk_bits(clks); | ||
955 | |||
956 | if (clks & DSS_CLK_ICK) | ||
957 | clk_enable(dss.dss_ick); | ||
958 | if (clks & DSS_CLK_FCK) | ||
959 | clk_enable(dss.dss_fck); | ||
960 | if ((clks & DSS_CLK_SYSCK) && dss.dss_sys_clk) | ||
961 | clk_enable(dss.dss_sys_clk); | ||
962 | if ((clks & DSS_CLK_TVFCK) && dss.dss_tv_fck) | ||
963 | clk_enable(dss.dss_tv_fck); | ||
964 | if ((clks & DSS_CLK_VIDFCK) && dss.dss_video_fck) | ||
965 | clk_enable(dss.dss_video_fck); | ||
966 | |||
967 | dss.num_clks_enabled += num_clks; | ||
968 | } | ||
969 | |||
970 | void dss_clk_enable(enum dss_clock clks) | ||
971 | { | ||
972 | bool check_ctx = dss.num_clks_enabled == 0; | ||
973 | |||
974 | dss_clk_enable_no_ctx(clks); | ||
975 | |||
976 | /* | ||
977 | * HACK: On omap4 the registers may not be accessible right after | ||
978 | * enabling the clocks. At some point this will be handled by | ||
979 | * pm_runtime, but for the time begin this should make things work. | ||
980 | */ | ||
981 | if (cpu_is_omap44xx() && check_ctx) | ||
982 | udelay(10); | ||
983 | |||
984 | if (check_ctx && cpu_is_omap34xx() && dss_need_ctx_restore()) | ||
985 | restore_all_ctx(); | ||
986 | } | 692 | } |
987 | 693 | ||
988 | static void dss_clk_disable_no_ctx(enum dss_clock clks) | 694 | struct clk *dss_get_ick(void) |
989 | { | 695 | { |
990 | unsigned num_clks = count_clk_bits(clks); | 696 | return clk_get(&dss.pdev->dev, "ick"); |
991 | |||
992 | if (clks & DSS_CLK_ICK) | ||
993 | clk_disable(dss.dss_ick); | ||
994 | if (clks & DSS_CLK_FCK) | ||
995 | clk_disable(dss.dss_fck); | ||
996 | if ((clks & DSS_CLK_SYSCK) && dss.dss_sys_clk) | ||
997 | clk_disable(dss.dss_sys_clk); | ||
998 | if ((clks & DSS_CLK_TVFCK) && dss.dss_tv_fck) | ||
999 | clk_disable(dss.dss_tv_fck); | ||
1000 | if ((clks & DSS_CLK_VIDFCK) && dss.dss_video_fck) | ||
1001 | clk_disable(dss.dss_video_fck); | ||
1002 | |||
1003 | dss.num_clks_enabled -= num_clks; | ||
1004 | } | 697 | } |
1005 | 698 | ||
1006 | void dss_clk_disable(enum dss_clock clks) | 699 | int dss_runtime_get(void) |
1007 | { | 700 | { |
1008 | if (cpu_is_omap34xx()) { | 701 | int r; |
1009 | unsigned num_clks = count_clk_bits(clks); | ||
1010 | |||
1011 | BUG_ON(dss.num_clks_enabled < num_clks); | ||
1012 | 702 | ||
1013 | if (dss.num_clks_enabled == num_clks) | 703 | DSSDBG("dss_runtime_get\n"); |
1014 | save_all_ctx(); | ||
1015 | } | ||
1016 | 704 | ||
1017 | dss_clk_disable_no_ctx(clks); | 705 | r = pm_runtime_get_sync(&dss.pdev->dev); |
706 | WARN_ON(r < 0); | ||
707 | return r < 0 ? r : 0; | ||
1018 | } | 708 | } |
1019 | 709 | ||
1020 | static void dss_clk_enable_all_no_ctx(void) | 710 | void dss_runtime_put(void) |
1021 | { | 711 | { |
1022 | enum dss_clock clks; | 712 | int r; |
1023 | |||
1024 | clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK; | ||
1025 | if (cpu_is_omap34xx()) | ||
1026 | clks |= DSS_CLK_VIDFCK; | ||
1027 | dss_clk_enable_no_ctx(clks); | ||
1028 | } | ||
1029 | |||
1030 | static void dss_clk_disable_all_no_ctx(void) | ||
1031 | { | ||
1032 | enum dss_clock clks; | ||
1033 | 713 | ||
1034 | clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK; | 714 | DSSDBG("dss_runtime_put\n"); |
1035 | if (cpu_is_omap34xx()) | ||
1036 | clks |= DSS_CLK_VIDFCK; | ||
1037 | dss_clk_disable_no_ctx(clks); | ||
1038 | } | ||
1039 | 715 | ||
1040 | #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) | 716 | r = pm_runtime_put(&dss.pdev->dev); |
1041 | /* CLOCKS */ | 717 | WARN_ON(r < 0); |
1042 | static void core_dump_clocks(struct seq_file *s) | ||
1043 | { | ||
1044 | int i; | ||
1045 | struct clk *clocks[5] = { | ||
1046 | dss.dss_ick, | ||
1047 | dss.dss_fck, | ||
1048 | dss.dss_sys_clk, | ||
1049 | dss.dss_tv_fck, | ||
1050 | dss.dss_video_fck | ||
1051 | }; | ||
1052 | |||
1053 | const char *names[5] = { | ||
1054 | "ick", | ||
1055 | "fck", | ||
1056 | "sys_clk", | ||
1057 | "tv_fck", | ||
1058 | "video_fck" | ||
1059 | }; | ||
1060 | |||
1061 | seq_printf(s, "- CORE -\n"); | ||
1062 | |||
1063 | seq_printf(s, "internal clk count\t\t%u\n", dss.num_clks_enabled); | ||
1064 | |||
1065 | for (i = 0; i < 5; i++) { | ||
1066 | if (!clocks[i]) | ||
1067 | continue; | ||
1068 | seq_printf(s, "%s (%s)%*s\t%lu\t%d\n", | ||
1069 | names[i], | ||
1070 | clocks[i]->name, | ||
1071 | 24 - strlen(names[i]) - strlen(clocks[i]->name), | ||
1072 | "", | ||
1073 | clk_get_rate(clocks[i]), | ||
1074 | clocks[i]->usecount); | ||
1075 | } | ||
1076 | } | 718 | } |
1077 | #endif /* defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) */ | ||
1078 | 719 | ||
1079 | /* DEBUGFS */ | 720 | /* DEBUGFS */ |
1080 | #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) | 721 | #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) |
1081 | void dss_debug_dump_clocks(struct seq_file *s) | 722 | void dss_debug_dump_clocks(struct seq_file *s) |
1082 | { | 723 | { |
1083 | core_dump_clocks(s); | ||
1084 | dss_dump_clocks(s); | 724 | dss_dump_clocks(s); |
1085 | dispc_dump_clocks(s); | 725 | dispc_dump_clocks(s); |
1086 | #ifdef CONFIG_OMAP2_DSS_DSI | 726 | #ifdef CONFIG_OMAP2_DSS_DSI |
@@ -1089,28 +729,51 @@ void dss_debug_dump_clocks(struct seq_file *s) | |||
1089 | } | 729 | } |
1090 | #endif | 730 | #endif |
1091 | 731 | ||
1092 | |||
1093 | /* DSS HW IP initialisation */ | 732 | /* DSS HW IP initialisation */ |
1094 | static int omap_dsshw_probe(struct platform_device *pdev) | 733 | static int omap_dsshw_probe(struct platform_device *pdev) |
1095 | { | 734 | { |
735 | struct resource *dss_mem; | ||
736 | u32 rev; | ||
1096 | int r; | 737 | int r; |
1097 | 738 | ||
1098 | dss.pdev = pdev; | 739 | dss.pdev = pdev; |
1099 | 740 | ||
741 | dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); | ||
742 | if (!dss_mem) { | ||
743 | DSSERR("can't get IORESOURCE_MEM DSS\n"); | ||
744 | r = -EINVAL; | ||
745 | goto err_ioremap; | ||
746 | } | ||
747 | dss.base = ioremap(dss_mem->start, resource_size(dss_mem)); | ||
748 | if (!dss.base) { | ||
749 | DSSERR("can't ioremap DSS\n"); | ||
750 | r = -ENOMEM; | ||
751 | goto err_ioremap; | ||
752 | } | ||
753 | |||
1100 | r = dss_get_clocks(); | 754 | r = dss_get_clocks(); |
1101 | if (r) | 755 | if (r) |
1102 | goto err_clocks; | 756 | goto err_clocks; |
1103 | 757 | ||
1104 | dss_clk_enable_all_no_ctx(); | 758 | pm_runtime_enable(&pdev->dev); |
1105 | 759 | ||
1106 | dss.ctx_id = dss_get_ctx_id(); | 760 | r = dss_runtime_get(); |
1107 | DSSDBG("initial ctx id %u\n", dss.ctx_id); | 761 | if (r) |
762 | goto err_runtime_get; | ||
1108 | 763 | ||
1109 | r = dss_init(); | 764 | /* Select DPLL */ |
1110 | if (r) { | 765 | REG_FLD_MOD(DSS_CONTROL, 0, 0, 0); |
1111 | DSSERR("Failed to initialize DSS\n"); | 766 | |
1112 | goto err_dss; | 767 | #ifdef CONFIG_OMAP2_DSS_VENC |
1113 | } | 768 | REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */ |
769 | REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */ | ||
770 | REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */ | ||
771 | #endif | ||
772 | dss.dsi_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; | ||
773 | dss.dsi_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; | ||
774 | dss.dispc_clk_source = OMAP_DSS_CLK_SRC_FCK; | ||
775 | dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; | ||
776 | dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; | ||
1114 | 777 | ||
1115 | r = dpi_init(); | 778 | r = dpi_init(); |
1116 | if (r) { | 779 | if (r) { |
@@ -1124,42 +787,66 @@ static int omap_dsshw_probe(struct platform_device *pdev) | |||
1124 | goto err_sdi; | 787 | goto err_sdi; |
1125 | } | 788 | } |
1126 | 789 | ||
1127 | dss_clk_disable_all_no_ctx(); | 790 | rev = dss_read_reg(DSS_REVISION); |
791 | printk(KERN_INFO "OMAP DSS rev %d.%d\n", | ||
792 | FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); | ||
793 | |||
794 | dss_runtime_put(); | ||
795 | |||
1128 | return 0; | 796 | return 0; |
1129 | err_sdi: | 797 | err_sdi: |
1130 | dpi_exit(); | 798 | dpi_exit(); |
1131 | err_dpi: | 799 | err_dpi: |
1132 | dss_exit(); | 800 | dss_runtime_put(); |
1133 | err_dss: | 801 | err_runtime_get: |
1134 | dss_clk_disable_all_no_ctx(); | 802 | pm_runtime_disable(&pdev->dev); |
1135 | dss_put_clocks(); | 803 | dss_put_clocks(); |
1136 | err_clocks: | 804 | err_clocks: |
805 | iounmap(dss.base); | ||
806 | err_ioremap: | ||
1137 | return r; | 807 | return r; |
1138 | } | 808 | } |
1139 | 809 | ||
1140 | static int omap_dsshw_remove(struct platform_device *pdev) | 810 | static int omap_dsshw_remove(struct platform_device *pdev) |
1141 | { | 811 | { |
812 | dpi_exit(); | ||
813 | sdi_exit(); | ||
1142 | 814 | ||
1143 | dss_exit(); | 815 | iounmap(dss.base); |
1144 | 816 | ||
1145 | /* | 817 | pm_runtime_disable(&pdev->dev); |
1146 | * As part of hwmod changes, DSS is not the only controller of dss | ||
1147 | * clocks; hwmod framework itself will also enable clocks during hwmod | ||
1148 | * init for dss, and autoidle is set in h/w for DSS. Hence, there's no | ||
1149 | * need to disable clocks if their usecounts > 1. | ||
1150 | */ | ||
1151 | WARN_ON(dss.num_clks_enabled > 0); | ||
1152 | 818 | ||
1153 | dss_put_clocks(); | 819 | dss_put_clocks(); |
820 | |||
821 | return 0; | ||
822 | } | ||
823 | |||
824 | static int dss_runtime_suspend(struct device *dev) | ||
825 | { | ||
826 | dss_save_context(); | ||
827 | clk_disable(dss.dss_clk); | ||
1154 | return 0; | 828 | return 0; |
1155 | } | 829 | } |
1156 | 830 | ||
831 | static int dss_runtime_resume(struct device *dev) | ||
832 | { | ||
833 | clk_enable(dss.dss_clk); | ||
834 | dss_restore_context(); | ||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | static const struct dev_pm_ops dss_pm_ops = { | ||
839 | .runtime_suspend = dss_runtime_suspend, | ||
840 | .runtime_resume = dss_runtime_resume, | ||
841 | }; | ||
842 | |||
1157 | static struct platform_driver omap_dsshw_driver = { | 843 | static struct platform_driver omap_dsshw_driver = { |
1158 | .probe = omap_dsshw_probe, | 844 | .probe = omap_dsshw_probe, |
1159 | .remove = omap_dsshw_remove, | 845 | .remove = omap_dsshw_remove, |
1160 | .driver = { | 846 | .driver = { |
1161 | .name = "omapdss_dss", | 847 | .name = "omapdss_dss", |
1162 | .owner = THIS_MODULE, | 848 | .owner = THIS_MODULE, |
849 | .pm = &dss_pm_ops, | ||
1163 | }, | 850 | }, |
1164 | }; | 851 | }; |
1165 | 852 | ||