diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_fimd.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fimd.c | 143 |
1 files changed, 79 insertions, 64 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index ca83139cd309..360adf2bba04 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c | |||
@@ -89,7 +89,7 @@ struct fimd_context { | |||
89 | bool suspended; | 89 | bool suspended; |
90 | struct mutex lock; | 90 | struct mutex lock; |
91 | 91 | ||
92 | struct fb_videomode *timing; | 92 | struct exynos_drm_panel_info *panel; |
93 | }; | 93 | }; |
94 | 94 | ||
95 | static bool fimd_display_is_connected(struct device *dev) | 95 | static bool fimd_display_is_connected(struct device *dev) |
@@ -101,13 +101,13 @@ static bool fimd_display_is_connected(struct device *dev) | |||
101 | return true; | 101 | return true; |
102 | } | 102 | } |
103 | 103 | ||
104 | static void *fimd_get_timing(struct device *dev) | 104 | static void *fimd_get_panel(struct device *dev) |
105 | { | 105 | { |
106 | struct fimd_context *ctx = get_fimd_context(dev); | 106 | struct fimd_context *ctx = get_fimd_context(dev); |
107 | 107 | ||
108 | DRM_DEBUG_KMS("%s\n", __FILE__); | 108 | DRM_DEBUG_KMS("%s\n", __FILE__); |
109 | 109 | ||
110 | return ctx->timing; | 110 | return ctx->panel; |
111 | } | 111 | } |
112 | 112 | ||
113 | static int fimd_check_timing(struct device *dev, void *timing) | 113 | static int fimd_check_timing(struct device *dev, void *timing) |
@@ -131,7 +131,7 @@ static int fimd_display_power_on(struct device *dev, int mode) | |||
131 | static struct exynos_drm_display_ops fimd_display_ops = { | 131 | static struct exynos_drm_display_ops fimd_display_ops = { |
132 | .type = EXYNOS_DISPLAY_TYPE_LCD, | 132 | .type = EXYNOS_DISPLAY_TYPE_LCD, |
133 | .is_connected = fimd_display_is_connected, | 133 | .is_connected = fimd_display_is_connected, |
134 | .get_timing = fimd_get_timing, | 134 | .get_panel = fimd_get_panel, |
135 | .check_timing = fimd_check_timing, | 135 | .check_timing = fimd_check_timing, |
136 | .power_on = fimd_display_power_on, | 136 | .power_on = fimd_display_power_on, |
137 | }; | 137 | }; |
@@ -158,7 +158,8 @@ static void fimd_dpms(struct device *subdrv_dev, int mode) | |||
158 | case DRM_MODE_DPMS_STANDBY: | 158 | case DRM_MODE_DPMS_STANDBY: |
159 | case DRM_MODE_DPMS_SUSPEND: | 159 | case DRM_MODE_DPMS_SUSPEND: |
160 | case DRM_MODE_DPMS_OFF: | 160 | case DRM_MODE_DPMS_OFF: |
161 | pm_runtime_put_sync(subdrv_dev); | 161 | if (!ctx->suspended) |
162 | pm_runtime_put_sync(subdrv_dev); | ||
162 | break; | 163 | break; |
163 | default: | 164 | default: |
164 | DRM_DEBUG_KMS("unspecified mode %d\n", mode); | 165 | DRM_DEBUG_KMS("unspecified mode %d\n", mode); |
@@ -192,7 +193,8 @@ static void fimd_apply(struct device *subdrv_dev) | |||
192 | static void fimd_commit(struct device *dev) | 193 | static void fimd_commit(struct device *dev) |
193 | { | 194 | { |
194 | struct fimd_context *ctx = get_fimd_context(dev); | 195 | struct fimd_context *ctx = get_fimd_context(dev); |
195 | struct fb_videomode *timing = ctx->timing; | 196 | struct exynos_drm_panel_info *panel = ctx->panel; |
197 | struct fb_videomode *timing = &panel->timing; | ||
196 | u32 val; | 198 | u32 val; |
197 | 199 | ||
198 | if (ctx->suspended) | 200 | if (ctx->suspended) |
@@ -603,7 +605,12 @@ static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc) | |||
603 | } | 605 | } |
604 | 606 | ||
605 | if (is_checked) { | 607 | if (is_checked) { |
606 | drm_vblank_put(drm_dev, crtc); | 608 | /* |
609 | * call drm_vblank_put only in case that drm_vblank_get was | ||
610 | * called. | ||
611 | */ | ||
612 | if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0) | ||
613 | drm_vblank_put(drm_dev, crtc); | ||
607 | 614 | ||
608 | /* | 615 | /* |
609 | * don't off vblank if vblank_disable_allowed is 1, | 616 | * don't off vblank if vblank_disable_allowed is 1, |
@@ -734,13 +741,53 @@ static void fimd_clear_win(struct fimd_context *ctx, int win) | |||
734 | writel(val, ctx->regs + SHADOWCON); | 741 | writel(val, ctx->regs + SHADOWCON); |
735 | } | 742 | } |
736 | 743 | ||
744 | static int fimd_power_on(struct fimd_context *ctx, bool enable) | ||
745 | { | ||
746 | struct exynos_drm_subdrv *subdrv = &ctx->subdrv; | ||
747 | struct device *dev = subdrv->manager.dev; | ||
748 | |||
749 | DRM_DEBUG_KMS("%s\n", __FILE__); | ||
750 | |||
751 | if (enable != false && enable != true) | ||
752 | return -EINVAL; | ||
753 | |||
754 | if (enable) { | ||
755 | int ret; | ||
756 | |||
757 | ret = clk_enable(ctx->bus_clk); | ||
758 | if (ret < 0) | ||
759 | return ret; | ||
760 | |||
761 | ret = clk_enable(ctx->lcd_clk); | ||
762 | if (ret < 0) { | ||
763 | clk_disable(ctx->bus_clk); | ||
764 | return ret; | ||
765 | } | ||
766 | |||
767 | ctx->suspended = false; | ||
768 | |||
769 | /* if vblank was enabled status, enable it again. */ | ||
770 | if (test_and_clear_bit(0, &ctx->irq_flags)) | ||
771 | fimd_enable_vblank(dev); | ||
772 | |||
773 | fimd_apply(dev); | ||
774 | } else { | ||
775 | clk_disable(ctx->lcd_clk); | ||
776 | clk_disable(ctx->bus_clk); | ||
777 | |||
778 | ctx->suspended = true; | ||
779 | } | ||
780 | |||
781 | return 0; | ||
782 | } | ||
783 | |||
737 | static int __devinit fimd_probe(struct platform_device *pdev) | 784 | static int __devinit fimd_probe(struct platform_device *pdev) |
738 | { | 785 | { |
739 | struct device *dev = &pdev->dev; | 786 | struct device *dev = &pdev->dev; |
740 | struct fimd_context *ctx; | 787 | struct fimd_context *ctx; |
741 | struct exynos_drm_subdrv *subdrv; | 788 | struct exynos_drm_subdrv *subdrv; |
742 | struct exynos_drm_fimd_pdata *pdata; | 789 | struct exynos_drm_fimd_pdata *pdata; |
743 | struct fb_videomode *timing; | 790 | struct exynos_drm_panel_info *panel; |
744 | struct resource *res; | 791 | struct resource *res; |
745 | int win; | 792 | int win; |
746 | int ret = -EINVAL; | 793 | int ret = -EINVAL; |
@@ -753,9 +800,9 @@ static int __devinit fimd_probe(struct platform_device *pdev) | |||
753 | return -EINVAL; | 800 | return -EINVAL; |
754 | } | 801 | } |
755 | 802 | ||
756 | timing = &pdata->timing; | 803 | panel = &pdata->panel; |
757 | if (!timing) { | 804 | if (!panel) { |
758 | dev_err(dev, "timing is null.\n"); | 805 | dev_err(dev, "panel is null.\n"); |
759 | return -EINVAL; | 806 | return -EINVAL; |
760 | } | 807 | } |
761 | 808 | ||
@@ -817,16 +864,16 @@ static int __devinit fimd_probe(struct platform_device *pdev) | |||
817 | goto err_req_irq; | 864 | goto err_req_irq; |
818 | } | 865 | } |
819 | 866 | ||
820 | ctx->clkdiv = fimd_calc_clkdiv(ctx, timing); | 867 | ctx->clkdiv = fimd_calc_clkdiv(ctx, &panel->timing); |
821 | ctx->vidcon0 = pdata->vidcon0; | 868 | ctx->vidcon0 = pdata->vidcon0; |
822 | ctx->vidcon1 = pdata->vidcon1; | 869 | ctx->vidcon1 = pdata->vidcon1; |
823 | ctx->default_win = pdata->default_win; | 870 | ctx->default_win = pdata->default_win; |
824 | ctx->timing = timing; | 871 | ctx->panel = panel; |
825 | 872 | ||
826 | timing->pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv; | 873 | panel->timing.pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv; |
827 | 874 | ||
828 | DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n", | 875 | DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n", |
829 | timing->pixclock, ctx->clkdiv); | 876 | panel->timing.pixclock, ctx->clkdiv); |
830 | 877 | ||
831 | subdrv = &ctx->subdrv; | 878 | subdrv = &ctx->subdrv; |
832 | 879 | ||
@@ -911,39 +958,30 @@ out: | |||
911 | #ifdef CONFIG_PM_SLEEP | 958 | #ifdef CONFIG_PM_SLEEP |
912 | static int fimd_suspend(struct device *dev) | 959 | static int fimd_suspend(struct device *dev) |
913 | { | 960 | { |
914 | int ret; | 961 | struct fimd_context *ctx = get_fimd_context(dev); |
915 | 962 | ||
916 | if (pm_runtime_suspended(dev)) | 963 | if (pm_runtime_suspended(dev)) |
917 | return 0; | 964 | return 0; |
918 | 965 | ||
919 | ret = pm_runtime_suspend(dev); | 966 | /* |
920 | if (ret < 0) | 967 | * do not use pm_runtime_suspend(). if pm_runtime_suspend() is |
921 | return ret; | 968 | * called here, an error would be returned by that interface |
922 | 969 | * because the usage_count of pm runtime is more than 1. | |
923 | return 0; | 970 | */ |
971 | return fimd_power_on(ctx, false); | ||
924 | } | 972 | } |
925 | 973 | ||
926 | static int fimd_resume(struct device *dev) | 974 | static int fimd_resume(struct device *dev) |
927 | { | 975 | { |
928 | int ret; | 976 | struct fimd_context *ctx = get_fimd_context(dev); |
929 | |||
930 | ret = pm_runtime_resume(dev); | ||
931 | if (ret < 0) { | ||
932 | DRM_ERROR("failed to resume runtime pm.\n"); | ||
933 | return ret; | ||
934 | } | ||
935 | |||
936 | pm_runtime_disable(dev); | ||
937 | |||
938 | ret = pm_runtime_set_active(dev); | ||
939 | if (ret < 0) { | ||
940 | DRM_ERROR("failed to active runtime pm.\n"); | ||
941 | pm_runtime_enable(dev); | ||
942 | pm_runtime_suspend(dev); | ||
943 | return ret; | ||
944 | } | ||
945 | 977 | ||
946 | pm_runtime_enable(dev); | 978 | /* |
979 | * if entered to sleep when lcd panel was on, the usage_count | ||
980 | * of pm runtime would still be 1 so in this case, fimd driver | ||
981 | * should be on directly not drawing on pm runtime interface. | ||
982 | */ | ||
983 | if (!pm_runtime_suspended(dev)) | ||
984 | return fimd_power_on(ctx, true); | ||
947 | 985 | ||
948 | return 0; | 986 | return 0; |
949 | } | 987 | } |
@@ -956,39 +994,16 @@ static int fimd_runtime_suspend(struct device *dev) | |||
956 | 994 | ||
957 | DRM_DEBUG_KMS("%s\n", __FILE__); | 995 | DRM_DEBUG_KMS("%s\n", __FILE__); |
958 | 996 | ||
959 | clk_disable(ctx->lcd_clk); | 997 | return fimd_power_on(ctx, false); |
960 | clk_disable(ctx->bus_clk); | ||
961 | |||
962 | ctx->suspended = true; | ||
963 | return 0; | ||
964 | } | 998 | } |
965 | 999 | ||
966 | static int fimd_runtime_resume(struct device *dev) | 1000 | static int fimd_runtime_resume(struct device *dev) |
967 | { | 1001 | { |
968 | struct fimd_context *ctx = get_fimd_context(dev); | 1002 | struct fimd_context *ctx = get_fimd_context(dev); |
969 | int ret; | ||
970 | 1003 | ||
971 | DRM_DEBUG_KMS("%s\n", __FILE__); | 1004 | DRM_DEBUG_KMS("%s\n", __FILE__); |
972 | 1005 | ||
973 | ret = clk_enable(ctx->bus_clk); | 1006 | return fimd_power_on(ctx, true); |
974 | if (ret < 0) | ||
975 | return ret; | ||
976 | |||
977 | ret = clk_enable(ctx->lcd_clk); | ||
978 | if (ret < 0) { | ||
979 | clk_disable(ctx->bus_clk); | ||
980 | return ret; | ||
981 | } | ||
982 | |||
983 | ctx->suspended = false; | ||
984 | |||
985 | /* if vblank was enabled status, enable it again. */ | ||
986 | if (test_and_clear_bit(0, &ctx->irq_flags)) | ||
987 | fimd_enable_vblank(dev); | ||
988 | |||
989 | fimd_apply(dev); | ||
990 | |||
991 | return 0; | ||
992 | } | 1007 | } |
993 | #endif | 1008 | #endif |
994 | 1009 | ||