Initial commit
[kernel/linux-3.0.git] / drivers / media / video / samsung / mfc5x / mfc_pm.c
1 /*
2  * linux/drivers/media/video/samsung/mfc5x/mfc_pm.c
3  *
4  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
5  *              http://www.samsung.com/
6  *
7  * Power management module for Samsung MFC (Multi Function Codec - FIMV) driver
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  */
13
14 #include <linux/err.h>
15 #include <linux/clk.h>
16 #include <linux/delay.h>
17 #ifdef CONFIG_PM_RUNTIME
18 #include <linux/pm_runtime.h>
19 #endif
20 #include <linux/interrupt.h>
21
22 #include <plat/clock.h>
23 #include <plat/s5p-mfc.h>
24 #include <plat/cpu.h>
25
26 #include <mach/regs-pmu.h>
27
28 #include <asm/io.h>
29
30 #include "mfc_dev.h"
31 #include "mfc_log.h"
32
33 #define MFC_PARENT_CLK_NAME     "mout_mfc0"
34 #define MFC_CLKNAME             "sclk_mfc"
35 #define MFC_GATE_CLK_NAME       "mfc"
36
37 #undef CLK_DEBUG
38
39 static struct mfc_pm *pm;
40
41 #ifdef CLK_DEBUG
42 atomic_t clk_ref;
43 #endif
44
45 int mfc_init_pm(struct mfc_dev *mfcdev)
46 {
47         struct clk *parent, *sclk;
48         int ret = 0;
49
50         pm = &mfcdev->pm;
51
52         parent = clk_get(mfcdev->device, MFC_PARENT_CLK_NAME);
53         if (IS_ERR(parent)) {
54                 printk(KERN_ERR "failed to get parent clock\n");
55                 ret = -ENOENT;
56                 goto err_gp_clk;
57         }
58
59         sclk = clk_get(mfcdev->device, MFC_CLKNAME);
60         if (IS_ERR(sclk)) {
61                 printk(KERN_ERR "failed to get source clock\n");
62                 ret = -ENOENT;
63                 goto err_gs_clk;
64         }
65
66         ret = clk_set_parent(sclk, parent);
67         if (ret) {
68                 printk(KERN_ERR "unable to set parent %s of clock %s\n",
69                                 parent->name, sclk->name);
70                 goto err_sp_clk;
71         }
72
73         /* FIXME: clock name & rate have to move to machine code */
74         ret = clk_set_rate(sclk, mfc_clk_rate);
75         if (ret) {
76                 printk(KERN_ERR "%s rate change failed: %u\n", sclk->name, 200 * 1000000);
77                 goto err_ss_clk;
78         }
79
80         /* clock for gating */
81         pm->clock = clk_get(mfcdev->device, MFC_GATE_CLK_NAME);
82         if (IS_ERR(pm->clock)) {
83                 printk(KERN_ERR "failed to get clock-gating control\n");
84                 ret = -ENOENT;
85                 goto err_gg_clk;
86         }
87
88         atomic_set(&pm->power, 0);
89
90 #ifdef CONFIG_PM_RUNTIME
91         pm->device = mfcdev->device;
92         pm_runtime_enable(pm->device);
93 #endif
94
95 #ifdef CLK_DEBUG
96         atomic_set(&clk_ref, 0);
97 #endif
98
99         return 0;
100
101 err_gg_clk:
102 err_ss_clk:
103 err_sp_clk:
104         clk_put(sclk);
105 err_gs_clk:
106         clk_put(parent);
107 err_gp_clk:
108         return ret;
109 }
110
111 void mfc_final_pm(struct mfc_dev *mfcdev)
112 {
113 #ifdef CONFIG_PM_RUNTIME
114         pm_runtime_disable(pm->device);
115 #endif
116 }
117
118 int mfc_clock_on(struct mfc_dev *mfcdev)
119 {
120         int ret;
121 #ifdef CLK_DEBUG
122         atomic_inc(&clk_ref);
123         mfc_dbg("+ %d", atomic_read(&clk_ref));
124 #endif
125
126         ret = clk_enable(pm->clock);
127         enable_irq(mfcdev->irq);
128         return  ret;
129 }
130
131 void mfc_clock_off(struct mfc_dev *mfcdev)
132 {
133 #ifdef CLK_DEBUG
134         atomic_dec(&clk_ref);
135         mfc_dbg("- %d", atomic_read(&clk_ref));
136 #endif
137         disable_irq(mfcdev->irq);
138         clk_disable(pm->clock);
139 }
140
141 int mfc_power_on(void)
142 {
143 #ifdef CONFIG_PM_RUNTIME
144         if ((soc_is_exynos4212() && (samsung_rev() < EXYNOS4212_REV_1_0)) ||
145                 (soc_is_exynos4412() && (samsung_rev() < EXYNOS4412_REV_1_1)))
146                 return 0;
147         else
148                 return pm_runtime_get_sync(pm->device);
149 #else
150         atomic_set(&pm->power, 1);
151
152         return 0;
153 #endif
154 }
155
156 int mfc_power_off(void)
157 {
158 #ifdef CONFIG_PM_RUNTIME
159         if ((soc_is_exynos4212() && (samsung_rev() < EXYNOS4212_REV_1_0)) ||
160                 (soc_is_exynos4412() && (samsung_rev() < EXYNOS4412_REV_1_1)))
161                 return 0;
162         else
163                 return pm_runtime_put_sync(pm->device);
164 #else
165         atomic_set(&pm->power, 0);
166
167         return 0;
168 #endif
169 }
170
171 bool mfc_power_chk(void)
172 {
173         mfc_dbg("%s", atomic_read(&pm->power) ? "on" : "off");
174
175         return atomic_read(&pm->power) ? true : false;
176 }
177
178 void mfc_pd_enable(void)
179 {
180         u32 timeout;
181
182         __raw_writel(S5P_INT_LOCAL_PWR_EN, S5P_PMU_MFC_CONF);
183
184         /* Wait max 1ms */
185         timeout = 10;
186         while ((__raw_readl(S5P_PMU_MFC_CONF + 0x4) & S5P_INT_LOCAL_PWR_EN)
187                 != S5P_INT_LOCAL_PWR_EN) {
188                 if (timeout == 0) {
189                         printk(KERN_ERR "Power domain MFC enable failed.\n");
190                         break;
191                 }
192
193                 timeout--;
194
195                 udelay(100);
196         }
197 }
198