diff options
author | Marek Szyprowski <m.szyprowski@samsung.com> | 2016-11-16 04:04:57 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@s-opensource.com> | 2016-11-30 06:21:30 -0500 |
commit | 1bce6fb3edf17a0444226fbbd0f568559512f941 (patch) | |
tree | aa6f44c85fab5d77dad6bbc59d1ebaaf95015d3c | |
parent | 741f4331d07fd670b9ebb5eeda18208b6eb2ba28 (diff) |
[media] s5p-mfc: Rework clock handling
This patch changes the code for handling clocks. Now clocks are defined
per each device variant, what is a preparation for adding support for
Exynos 5433 MFC V8, which has more clocks than all previous versions.
Also use devm_clk_get() to simplify cleanup path.
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
-rw-r--r-- | drivers/media/platform/s5p-mfc/s5p_mfc.c | 8 | ||||
-rw-r--r-- | drivers/media/platform/s5p-mfc/s5p_mfc_common.h | 9 | ||||
-rw-r--r-- | drivers/media/platform/s5p-mfc/s5p_mfc_pm.c | 98 |
3 files changed, 56 insertions, 59 deletions
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index b34ccbefbb02..5fcf17008ded 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c | |||
@@ -1434,6 +1434,8 @@ static struct s5p_mfc_variant mfc_drvdata_v5 = { | |||
1434 | .buf_size = &buf_size_v5, | 1434 | .buf_size = &buf_size_v5, |
1435 | .buf_align = &mfc_buf_align_v5, | 1435 | .buf_align = &mfc_buf_align_v5, |
1436 | .fw_name[0] = "s5p-mfc.fw", | 1436 | .fw_name[0] = "s5p-mfc.fw", |
1437 | .clk_names = {"mfc", "sclk_mfc"}, | ||
1438 | .num_clocks = 2, | ||
1437 | .use_clock_gating = true, | 1439 | .use_clock_gating = true, |
1438 | }; | 1440 | }; |
1439 | 1441 | ||
@@ -1467,6 +1469,8 @@ static struct s5p_mfc_variant mfc_drvdata_v6 = { | |||
1467 | * for init buffer command | 1469 | * for init buffer command |
1468 | */ | 1470 | */ |
1469 | .fw_name[1] = "s5p-mfc-v6-v2.fw", | 1471 | .fw_name[1] = "s5p-mfc-v6-v2.fw", |
1472 | .clk_names = {"mfc"}, | ||
1473 | .num_clocks = 1, | ||
1470 | }; | 1474 | }; |
1471 | 1475 | ||
1472 | static struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = { | 1476 | static struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = { |
@@ -1494,6 +1498,8 @@ static struct s5p_mfc_variant mfc_drvdata_v7 = { | |||
1494 | .buf_size = &buf_size_v7, | 1498 | .buf_size = &buf_size_v7, |
1495 | .buf_align = &mfc_buf_align_v7, | 1499 | .buf_align = &mfc_buf_align_v7, |
1496 | .fw_name[0] = "s5p-mfc-v7.fw", | 1500 | .fw_name[0] = "s5p-mfc-v7.fw", |
1501 | .clk_names = {"mfc", "sclk_mfc"}, | ||
1502 | .num_clocks = 2, | ||
1497 | }; | 1503 | }; |
1498 | 1504 | ||
1499 | static struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = { | 1505 | static struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = { |
@@ -1521,6 +1527,8 @@ static struct s5p_mfc_variant mfc_drvdata_v8 = { | |||
1521 | .buf_size = &buf_size_v8, | 1527 | .buf_size = &buf_size_v8, |
1522 | .buf_align = &mfc_buf_align_v8, | 1528 | .buf_align = &mfc_buf_align_v8, |
1523 | .fw_name[0] = "s5p-mfc-v8.fw", | 1529 | .fw_name[0] = "s5p-mfc-v8.fw", |
1530 | .clk_names = {"mfc"}, | ||
1531 | .num_clocks = 1, | ||
1524 | }; | 1532 | }; |
1525 | 1533 | ||
1526 | static const struct of_device_id exynos_mfc_match[] = { | 1534 | static const struct of_device_id exynos_mfc_match[] = { |
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 58b15c212dd2..ab23236aa942 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h | |||
@@ -104,6 +104,8 @@ static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b) | |||
104 | #define S5P_MFC_R2H_CMD_ENC_BUFFER_FUL_RET 16 | 104 | #define S5P_MFC_R2H_CMD_ENC_BUFFER_FUL_RET 16 |
105 | #define S5P_MFC_R2H_CMD_ERR_RET 32 | 105 | #define S5P_MFC_R2H_CMD_ERR_RET 32 |
106 | 106 | ||
107 | #define MFC_MAX_CLOCKS 4 | ||
108 | |||
107 | #define mfc_read(dev, offset) readl(dev->regs_base + (offset)) | 109 | #define mfc_read(dev, offset) readl(dev->regs_base + (offset)) |
108 | #define mfc_write(dev, data, offset) writel((data), dev->regs_base + \ | 110 | #define mfc_write(dev, data, offset) writel((data), dev->regs_base + \ |
109 | (offset)) | 111 | (offset)) |
@@ -197,9 +199,12 @@ struct s5p_mfc_buf { | |||
197 | * struct s5p_mfc_pm - power management data structure | 199 | * struct s5p_mfc_pm - power management data structure |
198 | */ | 200 | */ |
199 | struct s5p_mfc_pm { | 201 | struct s5p_mfc_pm { |
200 | struct clk *clock; | ||
201 | struct clk *clock_gate; | 202 | struct clk *clock_gate; |
203 | const char **clk_names; | ||
204 | struct clk *clocks[MFC_MAX_CLOCKS]; | ||
205 | int num_clocks; | ||
202 | bool use_clock_gating; | 206 | bool use_clock_gating; |
207 | |||
203 | struct device *device; | 208 | struct device *device; |
204 | }; | 209 | }; |
205 | 210 | ||
@@ -235,6 +240,8 @@ struct s5p_mfc_variant { | |||
235 | struct s5p_mfc_buf_size *buf_size; | 240 | struct s5p_mfc_buf_size *buf_size; |
236 | struct s5p_mfc_buf_align *buf_align; | 241 | struct s5p_mfc_buf_align *buf_align; |
237 | char *fw_name[MFC_FW_MAX_VERSIONS]; | 242 | char *fw_name[MFC_FW_MAX_VERSIONS]; |
243 | const char *clk_names[MFC_MAX_CLOCKS]; | ||
244 | int num_clocks; | ||
238 | bool use_clock_gating; | 245 | bool use_clock_gating; |
239 | }; | 246 | }; |
240 | 247 | ||
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 796dac85746a..eb85cedc5ef3 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c | |||
@@ -18,56 +18,42 @@ | |||
18 | #include "s5p_mfc_debug.h" | 18 | #include "s5p_mfc_debug.h" |
19 | #include "s5p_mfc_pm.h" | 19 | #include "s5p_mfc_pm.h" |
20 | 20 | ||
21 | #define MFC_GATE_CLK_NAME "mfc" | ||
22 | #define MFC_SCLK_NAME "sclk_mfc" | ||
23 | |||
24 | static struct s5p_mfc_pm *pm; | 21 | static struct s5p_mfc_pm *pm; |
25 | static struct s5p_mfc_dev *p_dev; | 22 | static struct s5p_mfc_dev *p_dev; |
26 | static atomic_t clk_ref; | 23 | static atomic_t clk_ref; |
27 | 24 | ||
28 | int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) | 25 | int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) |
29 | { | 26 | { |
30 | int ret = 0; | 27 | int i; |
31 | 28 | ||
32 | pm = &dev->pm; | 29 | pm = &dev->pm; |
33 | p_dev = dev; | 30 | p_dev = dev; |
34 | pm->use_clock_gating = dev->variant->use_clock_gating; | ||
35 | pm->clock_gate = clk_get(&dev->plat_dev->dev, MFC_GATE_CLK_NAME); | ||
36 | if (IS_ERR(pm->clock_gate)) { | ||
37 | mfc_err("Failed to get clock-gating control\n"); | ||
38 | ret = PTR_ERR(pm->clock_gate); | ||
39 | goto err_g_ip_clk; | ||
40 | } | ||
41 | 31 | ||
42 | if (dev->variant->version != MFC_VERSION_V6) { | 32 | pm->num_clocks = dev->variant->num_clocks; |
43 | pm->clock = clk_get(&dev->plat_dev->dev, MFC_SCLK_NAME); | 33 | pm->clk_names = dev->variant->clk_names; |
44 | if (IS_ERR(pm->clock)) { | 34 | pm->device = &dev->plat_dev->dev; |
45 | mfc_info("Failed to get MFC special clock control\n"); | 35 | pm->clock_gate = NULL; |
46 | pm->clock = 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]); | ||
47 | } | 44 | } |
48 | } | 45 | } |
49 | 46 | ||
50 | pm->device = &dev->plat_dev->dev; | 47 | if (dev->variant->use_clock_gating) |
48 | pm->clock_gate = pm->clocks[0]; | ||
49 | |||
51 | pm_runtime_enable(pm->device); | 50 | pm_runtime_enable(pm->device); |
52 | atomic_set(&clk_ref, 0); | 51 | atomic_set(&clk_ref, 0); |
53 | |||
54 | return 0; | 52 | return 0; |
55 | |||
56 | clk_put(pm->clock_gate); | ||
57 | pm->clock_gate = NULL; | ||
58 | err_g_ip_clk: | ||
59 | return ret; | ||
60 | } | 53 | } |
61 | 54 | ||
62 | void s5p_mfc_final_pm(struct s5p_mfc_dev *dev) | 55 | void s5p_mfc_final_pm(struct s5p_mfc_dev *dev) |
63 | { | 56 | { |
64 | if (dev->variant->version != MFC_VERSION_V6 && | ||
65 | pm->clock) { | ||
66 | clk_put(pm->clock); | ||
67 | pm->clock = NULL; | ||
68 | } | ||
69 | clk_put(pm->clock_gate); | ||
70 | pm->clock_gate = NULL; | ||
71 | pm_runtime_disable(pm->device); | 57 | pm_runtime_disable(pm->device); |
72 | } | 58 | } |
73 | 59 | ||
@@ -76,8 +62,6 @@ int s5p_mfc_clock_on(void) | |||
76 | atomic_inc(&clk_ref); | 62 | atomic_inc(&clk_ref); |
77 | mfc_debug(3, "+ %d\n", atomic_read(&clk_ref)); | 63 | mfc_debug(3, "+ %d\n", atomic_read(&clk_ref)); |
78 | 64 | ||
79 | if (!pm->use_clock_gating) | ||
80 | return 0; | ||
81 | return clk_enable(pm->clock_gate); | 65 | return clk_enable(pm->clock_gate); |
82 | } | 66 | } |
83 | 67 | ||
@@ -86,50 +70,48 @@ void s5p_mfc_clock_off(void) | |||
86 | atomic_dec(&clk_ref); | 70 | atomic_dec(&clk_ref); |
87 | mfc_debug(3, "- %d\n", atomic_read(&clk_ref)); | 71 | mfc_debug(3, "- %d\n", atomic_read(&clk_ref)); |
88 | 72 | ||
89 | if (!pm->use_clock_gating) | ||
90 | return; | ||
91 | clk_disable(pm->clock_gate); | 73 | clk_disable(pm->clock_gate); |
92 | } | 74 | } |
93 | 75 | ||
94 | int s5p_mfc_power_on(void) | 76 | int s5p_mfc_power_on(void) |
95 | { | 77 | { |
96 | int ret; | 78 | int i, ret = 0; |
97 | 79 | ||
98 | ret = pm_runtime_get_sync(pm->device); | 80 | ret = pm_runtime_get_sync(pm->device); |
99 | if (ret) | 81 | if (ret < 0) |
100 | return ret; | 82 | return ret; |
101 | 83 | ||
102 | ret = clk_prepare_enable(pm->clock_gate); | 84 | /* clock control */ |
103 | if (ret) | 85 | for (i = 0; i < pm->num_clocks; i++) { |
104 | goto err_pm; | 86 | ret = clk_prepare_enable(pm->clocks[i]); |
105 | 87 | if (ret < 0) { | |
106 | if (pm->clock) { | 88 | mfc_err("clock prepare failed for clock: %s\n", |
107 | ret = clk_prepare_enable(pm->clock); | 89 | pm->clk_names[i]); |
108 | if (ret) | 90 | i++; |
109 | goto err_gate; | 91 | goto err; |
92 | } | ||
110 | } | 93 | } |
111 | 94 | ||
112 | if (pm->use_clock_gating) | 95 | /* prepare for software clock gating */ |
113 | clk_disable(pm->clock_gate); | 96 | clk_disable(pm->clock_gate); |
114 | return 0; | ||
115 | 97 | ||
116 | err_gate: | 98 | return 0; |
117 | clk_disable_unprepare(pm->clock_gate); | 99 | err: |
118 | err_pm: | 100 | while (--i > 0) |
119 | pm_runtime_put_sync(pm->device); | 101 | clk_disable_unprepare(pm->clocks[i]); |
102 | pm_runtime_put(pm->device); | ||
120 | return ret; | 103 | return ret; |
121 | |||
122 | } | 104 | } |
123 | 105 | ||
124 | int s5p_mfc_power_off(void) | 106 | int s5p_mfc_power_off(void) |
125 | { | 107 | { |
126 | if (pm->clock) | 108 | int i; |
127 | clk_disable_unprepare(pm->clock); | 109 | |
110 | /* finish software clock gating */ | ||
111 | clk_enable(pm->clock_gate); | ||
128 | 112 | ||
129 | if (pm->use_clock_gating) | 113 | for (i = 0; i < pm->num_clocks; i++) |
130 | clk_unprepare(pm->clock_gate); | 114 | clk_disable_unprepare(pm->clocks[i]); |
131 | else | ||
132 | clk_disable_unprepare(pm->clock_gate); | ||
133 | 115 | ||
134 | return pm_runtime_put_sync(pm->device); | 116 | return pm_runtime_put_sync(pm->device); |
135 | } | 117 | } |