GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / media / platform / s5p-mfc / s5p_mfc_pm.c
1 /*
2  * linux/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
3  *
4  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
5  *              http://www.samsung.com/
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  */
12
13 #include <linux/clk.h>
14 #include <linux/err.h>
15 #include <linux/platform_device.h>
16 #include <linux/pm_runtime.h>
17 #include "s5p_mfc_common.h"
18 #include "s5p_mfc_debug.h"
19 #include "s5p_mfc_pm.h"
20
21 static struct s5p_mfc_pm *pm;
22 static struct s5p_mfc_dev *p_dev;
23 static atomic_t clk_ref;
24
25 int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
26 {
27         int i;
28
29         pm = &dev->pm;
30         p_dev = dev;
31
32         pm->num_clocks = dev->variant->num_clocks;
33         pm->clk_names = dev->variant->clk_names;
34         pm->device = &dev->plat_dev->dev;
35         pm->clock_gate = NULL;
36
37         /* clock control */
38         for (i = 0; i < pm->num_clocks; i++) {
39                 pm->clocks[i] = devm_clk_get(pm->device, pm->clk_names[i]);
40                 if (IS_ERR(pm->clocks[i])) {
41                         /* additional clocks are optional */
42                         if (i && PTR_ERR(pm->clocks[i]) == -ENOENT) {
43                                 pm->clocks[i] = NULL;
44                                 continue;
45                         }
46                         mfc_err("Failed to get clock: %s\n",
47                                 pm->clk_names[i]);
48                         return PTR_ERR(pm->clocks[i]);
49                 }
50         }
51
52         if (dev->variant->use_clock_gating)
53                 pm->clock_gate = pm->clocks[0];
54
55         pm_runtime_enable(pm->device);
56         atomic_set(&clk_ref, 0);
57         return 0;
58 }
59
60 void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
61 {
62         pm_runtime_disable(pm->device);
63 }
64
65 int s5p_mfc_clock_on(void)
66 {
67         atomic_inc(&clk_ref);
68         mfc_debug(3, "+ %d\n", atomic_read(&clk_ref));
69
70         return clk_enable(pm->clock_gate);
71 }
72
73 void s5p_mfc_clock_off(void)
74 {
75         atomic_dec(&clk_ref);
76         mfc_debug(3, "- %d\n", atomic_read(&clk_ref));
77
78         clk_disable(pm->clock_gate);
79 }
80
81 int s5p_mfc_power_on(void)
82 {
83         int i, ret = 0;
84
85         ret = pm_runtime_get_sync(pm->device);
86         if (ret < 0) {
87                 pm_runtime_put_noidle(pm->device);
88                 return ret;
89         }
90
91         /* clock control */
92         for (i = 0; i < pm->num_clocks; i++) {
93                 ret = clk_prepare_enable(pm->clocks[i]);
94                 if (ret < 0) {
95                         mfc_err("clock prepare failed for clock: %s\n",
96                                 pm->clk_names[i]);
97                         i++;
98                         goto err;
99                 }
100         }
101
102         /* prepare for software clock gating */
103         clk_disable(pm->clock_gate);
104
105         return 0;
106 err:
107         while (--i > 0)
108                 clk_disable_unprepare(pm->clocks[i]);
109         pm_runtime_put(pm->device);
110         return ret;
111 }
112
113 int s5p_mfc_power_off(void)
114 {
115         int i;
116
117         /* finish software clock gating */
118         clk_enable(pm->clock_gate);
119
120         for (i = 0; i < pm->num_clocks; i++)
121                 clk_disable_unprepare(pm->clocks[i]);
122
123         return pm_runtime_put_sync(pm->device);
124 }
125