blob: 297e10e698986badb620f1edff98315f41ad4cee [file] [log] [blame]
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -03001/*
2 * V4L2 clock service
3 *
4 * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/atomic.h>
Guennadi Liakhovetski4f528af2015-02-01 08:12:33 -030012#include <linux/clk.h>
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -030013#include <linux/device.h>
14#include <linux/errno.h>
15#include <linux/list.h>
16#include <linux/module.h>
17#include <linux/mutex.h>
Josh Wudc51b7d2015-10-28 07:48:55 -020018#include <linux/of.h>
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -030019#include <linux/slab.h>
20#include <linux/string.h>
21
22#include <media/v4l2-clk.h>
23#include <media/v4l2-subdev.h>
24
25static DEFINE_MUTEX(clk_lock);
26static LIST_HEAD(clk_list);
27
Guennadi Liakhovetskia37462b2015-01-31 20:21:32 -030028static struct v4l2_clk *v4l2_clk_find(const char *dev_id)
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -030029{
30 struct v4l2_clk *clk;
31
Guennadi Liakhovetskia37462b2015-01-31 20:21:32 -030032 list_for_each_entry(clk, &clk_list, list)
33 if (!strcmp(dev_id, clk->dev_id))
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -030034 return clk;
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -030035
36 return ERR_PTR(-ENODEV);
37}
38
39struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id)
40{
41 struct v4l2_clk *clk;
Guennadi Liakhovetski4f528af2015-02-01 08:12:33 -030042 struct clk *ccf_clk = clk_get(dev, id);
Josh Wudc51b7d2015-10-28 07:48:55 -020043 char clk_name[V4L2_CLK_NAME_SIZE];
Guennadi Liakhovetski4f528af2015-02-01 08:12:33 -030044
45 if (PTR_ERR(ccf_clk) == -EPROBE_DEFER)
46 return ERR_PTR(-EPROBE_DEFER);
47
48 if (!IS_ERR_OR_NULL(ccf_clk)) {
49 clk = kzalloc(sizeof(*clk), GFP_KERNEL);
50 if (!clk) {
51 clk_put(ccf_clk);
52 return ERR_PTR(-ENOMEM);
53 }
54 clk->clk = ccf_clk;
55
56 return clk;
57 }
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -030058
59 mutex_lock(&clk_lock);
Guennadi Liakhovetskia37462b2015-01-31 20:21:32 -030060 clk = v4l2_clk_find(dev_name(dev));
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -030061
Josh Wudc51b7d2015-10-28 07:48:55 -020062 /* if dev_name is not found, try use the OF name to find again */
63 if (PTR_ERR(clk) == -ENODEV && dev->of_node) {
64 v4l2_clk_name_of(clk_name, sizeof(clk_name),
65 of_node_full_name(dev->of_node));
66 clk = v4l2_clk_find(clk_name);
67 }
68
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -030069 if (!IS_ERR(clk))
70 atomic_inc(&clk->use_count);
71 mutex_unlock(&clk_lock);
72
73 return clk;
74}
75EXPORT_SYMBOL(v4l2_clk_get);
76
77void v4l2_clk_put(struct v4l2_clk *clk)
78{
79 struct v4l2_clk *tmp;
80
81 if (IS_ERR(clk))
82 return;
83
Guennadi Liakhovetski4f528af2015-02-01 08:12:33 -030084 if (clk->clk) {
85 clk_put(clk->clk);
86 kfree(clk);
87 return;
88 }
89
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -030090 mutex_lock(&clk_lock);
91
92 list_for_each_entry(tmp, &clk_list, list)
93 if (tmp == clk)
94 atomic_dec(&clk->use_count);
95
96 mutex_unlock(&clk_lock);
97}
98EXPORT_SYMBOL(v4l2_clk_put);
99
100static int v4l2_clk_lock_driver(struct v4l2_clk *clk)
101{
102 struct v4l2_clk *tmp;
103 int ret = -ENODEV;
104
105 mutex_lock(&clk_lock);
106
107 list_for_each_entry(tmp, &clk_list, list)
108 if (tmp == clk) {
109 ret = !try_module_get(clk->ops->owner);
110 if (ret)
111 ret = -EFAULT;
112 break;
113 }
114
115 mutex_unlock(&clk_lock);
116
117 return ret;
118}
119
120static void v4l2_clk_unlock_driver(struct v4l2_clk *clk)
121{
122 module_put(clk->ops->owner);
123}
124
125int v4l2_clk_enable(struct v4l2_clk *clk)
126{
Guennadi Liakhovetski4f528af2015-02-01 08:12:33 -0300127 int ret;
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300128
Guennadi Liakhovetski4f528af2015-02-01 08:12:33 -0300129 if (clk->clk)
130 return clk_prepare_enable(clk->clk);
131
132 ret = v4l2_clk_lock_driver(clk);
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300133 if (ret < 0)
134 return ret;
135
136 mutex_lock(&clk->lock);
137
138 if (++clk->enable == 1 && clk->ops->enable) {
139 ret = clk->ops->enable(clk);
140 if (ret < 0)
141 clk->enable--;
142 }
143
144 mutex_unlock(&clk->lock);
145
146 return ret;
147}
148EXPORT_SYMBOL(v4l2_clk_enable);
149
150/*
151 * You might Oops if you try to disabled a disabled clock, because then the
152 * driver isn't locked and could have been unloaded by now, so, don't do that
153 */
154void v4l2_clk_disable(struct v4l2_clk *clk)
155{
156 int enable;
157
Guennadi Liakhovetski4f528af2015-02-01 08:12:33 -0300158 if (clk->clk)
159 return clk_disable_unprepare(clk->clk);
160
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300161 mutex_lock(&clk->lock);
162
163 enable = --clk->enable;
Guennadi Liakhovetskia37462b2015-01-31 20:21:32 -0300164 if (WARN(enable < 0, "Unbalanced %s() on %s!\n", __func__,
165 clk->dev_id))
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300166 clk->enable++;
167 else if (!enable && clk->ops->disable)
168 clk->ops->disable(clk);
169
170 mutex_unlock(&clk->lock);
171
172 v4l2_clk_unlock_driver(clk);
173}
174EXPORT_SYMBOL(v4l2_clk_disable);
175
176unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk)
177{
Guennadi Liakhovetski4f528af2015-02-01 08:12:33 -0300178 int ret;
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300179
Guennadi Liakhovetski4f528af2015-02-01 08:12:33 -0300180 if (clk->clk)
181 return clk_get_rate(clk->clk);
182
183 ret = v4l2_clk_lock_driver(clk);
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300184 if (ret < 0)
185 return ret;
186
187 mutex_lock(&clk->lock);
188 if (!clk->ops->get_rate)
189 ret = -ENOSYS;
190 else
191 ret = clk->ops->get_rate(clk);
192 mutex_unlock(&clk->lock);
193
194 v4l2_clk_unlock_driver(clk);
195
196 return ret;
197}
198EXPORT_SYMBOL(v4l2_clk_get_rate);
199
200int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate)
201{
Guennadi Liakhovetski4f528af2015-02-01 08:12:33 -0300202 int ret;
203
204 if (clk->clk) {
205 long r = clk_round_rate(clk->clk, rate);
206 if (r < 0)
207 return r;
208 return clk_set_rate(clk->clk, r);
209 }
210
211 ret = v4l2_clk_lock_driver(clk);
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300212
213 if (ret < 0)
214 return ret;
215
216 mutex_lock(&clk->lock);
217 if (!clk->ops->set_rate)
218 ret = -ENOSYS;
219 else
220 ret = clk->ops->set_rate(clk, rate);
221 mutex_unlock(&clk->lock);
222
223 v4l2_clk_unlock_driver(clk);
224
225 return ret;
226}
227EXPORT_SYMBOL(v4l2_clk_set_rate);
228
229struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops,
230 const char *dev_id,
Guennadi Liakhovetskia37462b2015-01-31 20:21:32 -0300231 void *priv)
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300232{
233 struct v4l2_clk *clk;
234 int ret;
235
236 if (!ops || !dev_id)
237 return ERR_PTR(-EINVAL);
238
239 clk = kzalloc(sizeof(struct v4l2_clk), GFP_KERNEL);
240 if (!clk)
241 return ERR_PTR(-ENOMEM);
242
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300243 clk->dev_id = kstrdup(dev_id, GFP_KERNEL);
Guennadi Liakhovetskia37462b2015-01-31 20:21:32 -0300244 if (!clk->dev_id) {
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300245 ret = -ENOMEM;
246 goto ealloc;
247 }
248 clk->ops = ops;
249 clk->priv = priv;
250 atomic_set(&clk->use_count, 0);
251 mutex_init(&clk->lock);
252
253 mutex_lock(&clk_lock);
Guennadi Liakhovetskia37462b2015-01-31 20:21:32 -0300254 if (!IS_ERR(v4l2_clk_find(dev_id))) {
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300255 mutex_unlock(&clk_lock);
256 ret = -EEXIST;
257 goto eexist;
258 }
259 list_add_tail(&clk->list, &clk_list);
260 mutex_unlock(&clk_lock);
261
262 return clk;
263
264eexist:
265ealloc:
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300266 kfree(clk->dev_id);
267 kfree(clk);
268 return ERR_PTR(ret);
269}
270EXPORT_SYMBOL(v4l2_clk_register);
271
272void v4l2_clk_unregister(struct v4l2_clk *clk)
273{
274 if (WARN(atomic_read(&clk->use_count),
Guennadi Liakhovetskia37462b2015-01-31 20:21:32 -0300275 "%s(): Refusing to unregister ref-counted %s clock!\n",
276 __func__, clk->dev_id))
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300277 return;
278
279 mutex_lock(&clk_lock);
280 list_del(&clk->list);
281 mutex_unlock(&clk_lock);
282
Guennadi Liakhovetskiff5430d2012-12-04 07:42:15 -0300283 kfree(clk->dev_id);
284 kfree(clk);
285}
286EXPORT_SYMBOL(v4l2_clk_unregister);
Guennadi Liakhovetskicf326df2013-08-28 10:28:26 -0300287
288struct v4l2_clk_fixed {
289 unsigned long rate;
290 struct v4l2_clk_ops ops;
291};
292
293static unsigned long fixed_get_rate(struct v4l2_clk *clk)
294{
295 struct v4l2_clk_fixed *priv = clk->priv;
296 return priv->rate;
297}
298
299struct v4l2_clk *__v4l2_clk_register_fixed(const char *dev_id,
Guennadi Liakhovetskia37462b2015-01-31 20:21:32 -0300300 unsigned long rate, struct module *owner)
Guennadi Liakhovetskicf326df2013-08-28 10:28:26 -0300301{
302 struct v4l2_clk *clk;
303 struct v4l2_clk_fixed *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
304
305 if (!priv)
306 return ERR_PTR(-ENOMEM);
307
308 priv->rate = rate;
309 priv->ops.get_rate = fixed_get_rate;
310 priv->ops.owner = owner;
311
Guennadi Liakhovetskia37462b2015-01-31 20:21:32 -0300312 clk = v4l2_clk_register(&priv->ops, dev_id, priv);
Guennadi Liakhovetskicf326df2013-08-28 10:28:26 -0300313 if (IS_ERR(clk))
314 kfree(priv);
315
316 return clk;
317}
318EXPORT_SYMBOL(__v4l2_clk_register_fixed);
319
320void v4l2_clk_unregister_fixed(struct v4l2_clk *clk)
321{
322 kfree(clk->priv);
323 v4l2_clk_unregister(clk);
324}
325EXPORT_SYMBOL(v4l2_clk_unregister_fixed);