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 
s5p_mfc_init_pm(struct s5p_mfc_dev * dev)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 			mfc_err("Failed to get clock: %s\n",
42 				pm->clk_names[i]);
43 			return PTR_ERR(pm->clocks[i]);
44 		}
45 	}
46 
47 	if (dev->variant->use_clock_gating)
48 		pm->clock_gate = pm->clocks[0];
49 
50 	pm_runtime_enable(pm->device);
51 	atomic_set(&clk_ref, 0);
52 	return 0;
53 }
54 
s5p_mfc_final_pm(struct s5p_mfc_dev * dev)55 void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
56 {
57 	pm_runtime_disable(pm->device);
58 }
59 
s5p_mfc_clock_on(void)60 int s5p_mfc_clock_on(void)
61 {
62 	atomic_inc(&clk_ref);
63 	mfc_debug(3, "+ %d\n", atomic_read(&clk_ref));
64 
65 	return clk_enable(pm->clock_gate);
66 }
67 
s5p_mfc_clock_off(void)68 void s5p_mfc_clock_off(void)
69 {
70 	atomic_dec(&clk_ref);
71 	mfc_debug(3, "- %d\n", atomic_read(&clk_ref));
72 
73 	clk_disable(pm->clock_gate);
74 }
75 
s5p_mfc_power_on(void)76 int s5p_mfc_power_on(void)
77 {
78 	int i, ret = 0;
79 
80 	ret = pm_runtime_get_sync(pm->device);
81 	if (ret < 0)
82 		return ret;
83 
84 	/* clock control */
85 	for (i = 0; i < pm->num_clocks; i++) {
86 		ret = clk_prepare_enable(pm->clocks[i]);
87 		if (ret < 0) {
88 			mfc_err("clock prepare failed for clock: %s\n",
89 				pm->clk_names[i]);
90 			i++;
91 			goto err;
92 		}
93 	}
94 
95 	/* prepare for software clock gating */
96 	clk_disable(pm->clock_gate);
97 
98 	return 0;
99 err:
100 	while (--i > 0)
101 		clk_disable_unprepare(pm->clocks[i]);
102 	pm_runtime_put(pm->device);
103 	return ret;
104 }
105 
s5p_mfc_power_off(void)106 int s5p_mfc_power_off(void)
107 {
108 	int i;
109 
110 	/* finish software clock gating */
111 	clk_enable(pm->clock_gate);
112 
113 	for (i = 0; i < pm->num_clocks; i++)
114 		clk_disable_unprepare(pm->clocks[i]);
115 
116 	return pm_runtime_put_sync(pm->device);
117 }
118 
119