diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_fimd.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 117 |
1 files changed, 92 insertions, 25 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 58d50e368a58..a32837951dd2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c | |||
@@ -57,6 +57,18 @@ | |||
57 | 57 | ||
58 | #define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev)) | 58 | #define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev)) |
59 | 59 | ||
60 | struct fimd_driver_data { | ||
61 | unsigned int timing_base; | ||
62 | }; | ||
63 | |||
64 | struct fimd_driver_data exynos4_fimd_driver_data = { | ||
65 | .timing_base = 0x0, | ||
66 | }; | ||
67 | |||
68 | struct fimd_driver_data exynos5_fimd_driver_data = { | ||
69 | .timing_base = 0x20000, | ||
70 | }; | ||
71 | |||
60 | struct fimd_win_data { | 72 | struct fimd_win_data { |
61 | unsigned int offset_x; | 73 | unsigned int offset_x; |
62 | unsigned int offset_y; | 74 | unsigned int offset_y; |
@@ -91,6 +103,13 @@ struct fimd_context { | |||
91 | struct exynos_drm_panel_info *panel; | 103 | struct exynos_drm_panel_info *panel; |
92 | }; | 104 | }; |
93 | 105 | ||
106 | static inline struct fimd_driver_data *drm_fimd_get_driver_data( | ||
107 | struct platform_device *pdev) | ||
108 | { | ||
109 | return (struct fimd_driver_data *) | ||
110 | platform_get_device_id(pdev)->driver_data; | ||
111 | } | ||
112 | |||
94 | static bool fimd_display_is_connected(struct device *dev) | 113 | static bool fimd_display_is_connected(struct device *dev) |
95 | { | 114 | { |
96 | DRM_DEBUG_KMS("%s\n", __FILE__); | 115 | DRM_DEBUG_KMS("%s\n", __FILE__); |
@@ -194,32 +213,35 @@ static void fimd_commit(struct device *dev) | |||
194 | struct fimd_context *ctx = get_fimd_context(dev); | 213 | struct fimd_context *ctx = get_fimd_context(dev); |
195 | struct exynos_drm_panel_info *panel = ctx->panel; | 214 | struct exynos_drm_panel_info *panel = ctx->panel; |
196 | struct fb_videomode *timing = &panel->timing; | 215 | struct fb_videomode *timing = &panel->timing; |
216 | struct fimd_driver_data *driver_data; | ||
217 | struct platform_device *pdev = to_platform_device(dev); | ||
197 | u32 val; | 218 | u32 val; |
198 | 219 | ||
220 | driver_data = drm_fimd_get_driver_data(pdev); | ||
199 | if (ctx->suspended) | 221 | if (ctx->suspended) |
200 | return; | 222 | return; |
201 | 223 | ||
202 | DRM_DEBUG_KMS("%s\n", __FILE__); | 224 | DRM_DEBUG_KMS("%s\n", __FILE__); |
203 | 225 | ||
204 | /* setup polarity values from machine code. */ | 226 | /* setup polarity values from machine code. */ |
205 | writel(ctx->vidcon1, ctx->regs + VIDCON1); | 227 | writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); |
206 | 228 | ||
207 | /* setup vertical timing values. */ | 229 | /* setup vertical timing values. */ |
208 | val = VIDTCON0_VBPD(timing->upper_margin - 1) | | 230 | val = VIDTCON0_VBPD(timing->upper_margin - 1) | |
209 | VIDTCON0_VFPD(timing->lower_margin - 1) | | 231 | VIDTCON0_VFPD(timing->lower_margin - 1) | |
210 | VIDTCON0_VSPW(timing->vsync_len - 1); | 232 | VIDTCON0_VSPW(timing->vsync_len - 1); |
211 | writel(val, ctx->regs + VIDTCON0); | 233 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); |
212 | 234 | ||
213 | /* setup horizontal timing values. */ | 235 | /* setup horizontal timing values. */ |
214 | val = VIDTCON1_HBPD(timing->left_margin - 1) | | 236 | val = VIDTCON1_HBPD(timing->left_margin - 1) | |
215 | VIDTCON1_HFPD(timing->right_margin - 1) | | 237 | VIDTCON1_HFPD(timing->right_margin - 1) | |
216 | VIDTCON1_HSPW(timing->hsync_len - 1); | 238 | VIDTCON1_HSPW(timing->hsync_len - 1); |
217 | writel(val, ctx->regs + VIDTCON1); | 239 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); |
218 | 240 | ||
219 | /* setup horizontal and vertical display size. */ | 241 | /* setup horizontal and vertical display size. */ |
220 | val = VIDTCON2_LINEVAL(timing->yres - 1) | | 242 | val = VIDTCON2_LINEVAL(timing->yres - 1) | |
221 | VIDTCON2_HOZVAL(timing->xres - 1); | 243 | VIDTCON2_HOZVAL(timing->xres - 1); |
222 | writel(val, ctx->regs + VIDTCON2); | 244 | writel(val, ctx->regs + driver_data->timing_base + VIDTCON2); |
223 | 245 | ||
224 | /* setup clock source, clock divider, enable dma. */ | 246 | /* setup clock source, clock divider, enable dma. */ |
225 | val = ctx->vidcon0; | 247 | val = ctx->vidcon0; |
@@ -570,10 +592,22 @@ static void fimd_win_disable(struct device *dev, int zpos) | |||
570 | win_data->enabled = false; | 592 | win_data->enabled = false; |
571 | } | 593 | } |
572 | 594 | ||
595 | static void fimd_wait_for_vblank(struct device *dev) | ||
596 | { | ||
597 | struct fimd_context *ctx = get_fimd_context(dev); | ||
598 | int ret; | ||
599 | |||
600 | ret = wait_for((__raw_readl(ctx->regs + VIDCON1) & | ||
601 | VIDCON1_VSTATUS_VSYNC), 50); | ||
602 | if (ret < 0) | ||
603 | DRM_DEBUG_KMS("vblank wait timed out.\n"); | ||
604 | } | ||
605 | |||
573 | static struct exynos_drm_overlay_ops fimd_overlay_ops = { | 606 | static struct exynos_drm_overlay_ops fimd_overlay_ops = { |
574 | .mode_set = fimd_win_mode_set, | 607 | .mode_set = fimd_win_mode_set, |
575 | .commit = fimd_win_commit, | 608 | .commit = fimd_win_commit, |
576 | .disable = fimd_win_disable, | 609 | .disable = fimd_win_disable, |
610 | .wait_for_vblank = fimd_wait_for_vblank, | ||
577 | }; | 611 | }; |
578 | 612 | ||
579 | static struct exynos_drm_manager fimd_manager = { | 613 | static struct exynos_drm_manager fimd_manager = { |
@@ -678,7 +712,7 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev) | |||
678 | return 0; | 712 | return 0; |
679 | } | 713 | } |
680 | 714 | ||
681 | static void fimd_subdrv_remove(struct drm_device *drm_dev) | 715 | static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev) |
682 | { | 716 | { |
683 | DRM_DEBUG_KMS("%s\n", __FILE__); | 717 | DRM_DEBUG_KMS("%s\n", __FILE__); |
684 | 718 | ||
@@ -747,16 +781,10 @@ static void fimd_clear_win(struct fimd_context *ctx, int win) | |||
747 | writel(val, ctx->regs + SHADOWCON); | 781 | writel(val, ctx->regs + SHADOWCON); |
748 | } | 782 | } |
749 | 783 | ||
750 | static int fimd_power_on(struct fimd_context *ctx, bool enable) | 784 | static int fimd_clock(struct fimd_context *ctx, bool enable) |
751 | { | 785 | { |
752 | struct exynos_drm_subdrv *subdrv = &ctx->subdrv; | ||
753 | struct device *dev = subdrv->dev; | ||
754 | |||
755 | DRM_DEBUG_KMS("%s\n", __FILE__); | 786 | DRM_DEBUG_KMS("%s\n", __FILE__); |
756 | 787 | ||
757 | if (enable != false && enable != true) | ||
758 | return -EINVAL; | ||
759 | |||
760 | if (enable) { | 788 | if (enable) { |
761 | int ret; | 789 | int ret; |
762 | 790 | ||
@@ -769,18 +797,31 @@ static int fimd_power_on(struct fimd_context *ctx, bool enable) | |||
769 | clk_disable(ctx->bus_clk); | 797 | clk_disable(ctx->bus_clk); |
770 | return ret; | 798 | return ret; |
771 | } | 799 | } |
800 | } else { | ||
801 | clk_disable(ctx->lcd_clk); | ||
802 | clk_disable(ctx->bus_clk); | ||
803 | } | ||
804 | |||
805 | return 0; | ||
806 | } | ||
807 | |||
808 | static int fimd_activate(struct fimd_context *ctx, bool enable) | ||
809 | { | ||
810 | if (enable) { | ||
811 | int ret; | ||
812 | struct device *dev = ctx->subdrv.dev; | ||
813 | |||
814 | ret = fimd_clock(ctx, true); | ||
815 | if (ret < 0) | ||
816 | return ret; | ||
772 | 817 | ||
773 | ctx->suspended = false; | 818 | ctx->suspended = false; |
774 | 819 | ||
775 | /* if vblank was enabled status, enable it again. */ | 820 | /* if vblank was enabled status, enable it again. */ |
776 | if (test_and_clear_bit(0, &ctx->irq_flags)) | 821 | if (test_and_clear_bit(0, &ctx->irq_flags)) |
777 | fimd_enable_vblank(dev); | 822 | fimd_enable_vblank(dev); |
778 | |||
779 | fimd_apply(dev); | ||
780 | } else { | 823 | } else { |
781 | clk_disable(ctx->lcd_clk); | 824 | fimd_clock(ctx, false); |
782 | clk_disable(ctx->bus_clk); | ||
783 | |||
784 | ctx->suspended = true; | 825 | ctx->suspended = true; |
785 | } | 826 | } |
786 | 827 | ||
@@ -930,15 +971,15 @@ static int fimd_suspend(struct device *dev) | |||
930 | { | 971 | { |
931 | struct fimd_context *ctx = get_fimd_context(dev); | 972 | struct fimd_context *ctx = get_fimd_context(dev); |
932 | 973 | ||
933 | if (pm_runtime_suspended(dev)) | ||
934 | return 0; | ||
935 | |||
936 | /* | 974 | /* |
937 | * do not use pm_runtime_suspend(). if pm_runtime_suspend() is | 975 | * do not use pm_runtime_suspend(). if pm_runtime_suspend() is |
938 | * called here, an error would be returned by that interface | 976 | * called here, an error would be returned by that interface |
939 | * because the usage_count of pm runtime is more than 1. | 977 | * because the usage_count of pm runtime is more than 1. |
940 | */ | 978 | */ |
941 | return fimd_power_on(ctx, false); | 979 | if (!pm_runtime_suspended(dev)) |
980 | return fimd_activate(ctx, false); | ||
981 | |||
982 | return 0; | ||
942 | } | 983 | } |
943 | 984 | ||
944 | static int fimd_resume(struct device *dev) | 985 | static int fimd_resume(struct device *dev) |
@@ -950,8 +991,21 @@ static int fimd_resume(struct device *dev) | |||
950 | * of pm runtime would still be 1 so in this case, fimd driver | 991 | * of pm runtime would still be 1 so in this case, fimd driver |
951 | * should be on directly not drawing on pm runtime interface. | 992 | * should be on directly not drawing on pm runtime interface. |
952 | */ | 993 | */ |
953 | if (!pm_runtime_suspended(dev)) | 994 | if (pm_runtime_suspended(dev)) { |
954 | return fimd_power_on(ctx, true); | 995 | int ret; |
996 | |||
997 | ret = fimd_activate(ctx, true); | ||
998 | if (ret < 0) | ||
999 | return ret; | ||
1000 | |||
1001 | /* | ||
1002 | * in case of dpms on(standby), fimd_apply function will | ||
1003 | * be called by encoder's dpms callback to update fimd's | ||
1004 | * registers but in case of sleep wakeup, it's not. | ||
1005 | * so fimd_apply function should be called at here. | ||
1006 | */ | ||
1007 | fimd_apply(dev); | ||
1008 | } | ||
955 | 1009 | ||
956 | return 0; | 1010 | return 0; |
957 | } | 1011 | } |
@@ -964,7 +1018,7 @@ static int fimd_runtime_suspend(struct device *dev) | |||
964 | 1018 | ||
965 | DRM_DEBUG_KMS("%s\n", __FILE__); | 1019 | DRM_DEBUG_KMS("%s\n", __FILE__); |
966 | 1020 | ||
967 | return fimd_power_on(ctx, false); | 1021 | return fimd_activate(ctx, false); |
968 | } | 1022 | } |
969 | 1023 | ||
970 | static int fimd_runtime_resume(struct device *dev) | 1024 | static int fimd_runtime_resume(struct device *dev) |
@@ -973,10 +1027,22 @@ static int fimd_runtime_resume(struct device *dev) | |||
973 | 1027 | ||
974 | DRM_DEBUG_KMS("%s\n", __FILE__); | 1028 | DRM_DEBUG_KMS("%s\n", __FILE__); |
975 | 1029 | ||
976 | return fimd_power_on(ctx, true); | 1030 | return fimd_activate(ctx, true); |
977 | } | 1031 | } |
978 | #endif | 1032 | #endif |
979 | 1033 | ||
1034 | static struct platform_device_id fimd_driver_ids[] = { | ||
1035 | { | ||
1036 | .name = "exynos4-fb", | ||
1037 | .driver_data = (unsigned long)&exynos4_fimd_driver_data, | ||
1038 | }, { | ||
1039 | .name = "exynos5-fb", | ||
1040 | .driver_data = (unsigned long)&exynos5_fimd_driver_data, | ||
1041 | }, | ||
1042 | {}, | ||
1043 | }; | ||
1044 | MODULE_DEVICE_TABLE(platform, fimd_driver_ids); | ||
1045 | |||
980 | static const struct dev_pm_ops fimd_pm_ops = { | 1046 | static const struct dev_pm_ops fimd_pm_ops = { |
981 | SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume) | 1047 | SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume) |
982 | SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL) | 1048 | SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL) |
@@ -985,6 +1051,7 @@ static const struct dev_pm_ops fimd_pm_ops = { | |||
985 | struct platform_driver fimd_driver = { | 1051 | struct platform_driver fimd_driver = { |
986 | .probe = fimd_probe, | 1052 | .probe = fimd_probe, |
987 | .remove = __devexit_p(fimd_remove), | 1053 | .remove = __devexit_p(fimd_remove), |
1054 | .id_table = fimd_driver_ids, | ||
988 | .driver = { | 1055 | .driver = { |
989 | .name = "exynos4-fb", | 1056 | .name = "exynos4-fb", |
990 | .owner = THIS_MODULE, | 1057 | .owner = THIS_MODULE, |