linux/drivers/media/v4l2-core/v4l2-clk.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * V4L2 clock service
   4 *
   5 * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
   6 */
   7
   8#include <linux/atomic.h>
   9#include <linux/clk.h>
  10#include <linux/device.h>
  11#include <linux/errno.h>
  12#include <linux/list.h>
  13#include <linux/module.h>
  14#include <linux/mutex.h>
  15#include <linux/of.h>
  16#include <linux/slab.h>
  17#include <linux/string.h>
  18
  19#include <media/v4l2-clk.h>
  20#include <media/v4l2-subdev.h>
  21
  22static DEFINE_MUTEX(clk_lock);
  23static LIST_HEAD(clk_list);
  24
  25static struct v4l2_clk *v4l2_clk_find(const char *dev_id)
  26{
  27        struct v4l2_clk *clk;
  28
  29        list_for_each_entry(clk, &clk_list, list)
  30                if (!strcmp(dev_id, clk->dev_id))
  31                        return clk;
  32
  33        return ERR_PTR(-ENODEV);
  34}
  35
  36struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id)
  37{
  38        struct v4l2_clk *clk;
  39        struct clk *ccf_clk = clk_get(dev, id);
  40        char clk_name[V4L2_CLK_NAME_SIZE];
  41
  42        if (PTR_ERR(ccf_clk) == -EPROBE_DEFER)
  43                return ERR_PTR(-EPROBE_DEFER);
  44
  45        if (!IS_ERR_OR_NULL(ccf_clk)) {
  46                clk = kzalloc(sizeof(*clk), GFP_KERNEL);
  47                if (!clk) {
  48                        clk_put(ccf_clk);
  49                        return ERR_PTR(-ENOMEM);
  50                }
  51                clk->clk = ccf_clk;
  52
  53                return clk;
  54        }
  55
  56        mutex_lock(&clk_lock);
  57        clk = v4l2_clk_find(dev_name(dev));
  58
  59        /* if dev_name is not found, try use the OF name to find again  */
  60        if (PTR_ERR(clk) == -ENODEV && dev->of_node) {
  61                v4l2_clk_name_of(clk_name, sizeof(clk_name), dev->of_node);
  62                clk = v4l2_clk_find(clk_name);
  63        }
  64
  65        if (!IS_ERR(clk))
  66                atomic_inc(&clk->use_count);
  67        mutex_unlock(&clk_lock);
  68
  69        return clk;
  70}
  71EXPORT_SYMBOL(v4l2_clk_get);
  72
  73void v4l2_clk_put(struct v4l2_clk *clk)
  74{
  75        struct v4l2_clk *tmp;
  76
  77        if (IS_ERR(clk))
  78                return;
  79
  80        if (clk->clk) {
  81                clk_put(clk->clk);
  82                kfree(clk);
  83                return;
  84        }
  85
  86        mutex_lock(&clk_lock);
  87
  88        list_for_each_entry(tmp, &clk_list, list)
  89                if (tmp == clk)
  90                        atomic_dec(&clk->use_count);
  91
  92        mutex_unlock(&clk_lock);
  93}
  94EXPORT_SYMBOL(v4l2_clk_put);
  95
  96static int v4l2_clk_lock_driver(struct v4l2_clk *clk)
  97{
  98        struct v4l2_clk *tmp;
  99        int ret = -ENODEV;
 100
 101        mutex_lock(&clk_lock);
 102
 103        list_for_each_entry(tmp, &clk_list, list)
 104                if (tmp == clk) {
 105                        ret = !try_module_get(clk->ops->owner);
 106                        if (ret)
 107                                ret = -EFAULT;
 108                        break;
 109                }
 110
 111        mutex_unlock(&clk_lock);
 112
 113        return ret;
 114}
 115
 116static void v4l2_clk_unlock_driver(struct v4l2_clk *clk)
 117{
 118        module_put(clk->ops->owner);
 119}
 120
 121int v4l2_clk_enable(struct v4l2_clk *clk)
 122{
 123        int ret;
 124
 125        if (clk->clk)
 126                return clk_prepare_enable(clk->clk);
 127
 128        ret = v4l2_clk_lock_driver(clk);
 129        if (ret < 0)
 130                return ret;
 131
 132        mutex_lock(&clk->lock);
 133
 134        if (++clk->enable == 1 && clk->ops->enable) {
 135                ret = clk->ops->enable(clk);
 136                if (ret < 0)
 137                        clk->enable--;
 138        }
 139
 140        mutex_unlock(&clk->lock);
 141
 142        return ret;
 143}
 144EXPORT_SYMBOL(v4l2_clk_enable);
 145
 146/*
 147 * You might Oops if you try to disabled a disabled clock, because then the
 148 * driver isn't locked and could have been unloaded by now, so, don't do that
 149 */
 150void v4l2_clk_disable(struct v4l2_clk *clk)
 151{
 152        int enable;
 153
 154        if (clk->clk)
 155                return clk_disable_unprepare(clk->clk);
 156
 157        mutex_lock(&clk->lock);
 158
 159        enable = --clk->enable;
 160        if (WARN(enable < 0, "Unbalanced %s() on %s!\n", __func__,
 161                 clk->dev_id))
 162                clk->enable++;
 163        else if (!enable && clk->ops->disable)
 164                clk->ops->disable(clk);
 165
 166        mutex_unlock(&clk->lock);
 167
 168        v4l2_clk_unlock_driver(clk);
 169}
 170EXPORT_SYMBOL(v4l2_clk_disable);
 171
 172unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk)
 173{
 174        int ret;
 175
 176        if (clk->clk)
 177                return clk_get_rate(clk->clk);
 178
 179        ret = v4l2_clk_lock_driver(clk);
 180        if (ret < 0)
 181                return ret;
 182
 183        mutex_lock(&clk->lock);
 184        if (!clk->ops->get_rate)
 185                ret = -ENOSYS;
 186        else
 187                ret = clk->ops->get_rate(clk);
 188        mutex_unlock(&clk->lock);
 189
 190        v4l2_clk_unlock_driver(clk);
 191
 192        return ret;
 193}
 194EXPORT_SYMBOL(v4l2_clk_get_rate);
 195
 196int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate)
 197{
 198        int ret;
 199
 200        if (clk->clk) {
 201                long r = clk_round_rate(clk->clk, rate);
 202                if (r < 0)
 203                        return r;
 204                return clk_set_rate(clk->clk, r);
 205        }
 206
 207        ret = v4l2_clk_lock_driver(clk);
 208
 209        if (ret < 0)
 210                return ret;
 211
 212        mutex_lock(&clk->lock);
 213        if (!clk->ops->set_rate)
 214                ret = -ENOSYS;
 215        else
 216                ret = clk->ops->set_rate(clk, rate);
 217        mutex_unlock(&clk->lock);
 218
 219        v4l2_clk_unlock_driver(clk);
 220
 221        return ret;
 222}
 223EXPORT_SYMBOL(v4l2_clk_set_rate);
 224
 225struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops,
 226                                   const char *dev_id,
 227                                   void *priv)
 228{
 229        struct v4l2_clk *clk;
 230        int ret;
 231
 232        if (!ops || !dev_id)
 233                return ERR_PTR(-EINVAL);
 234
 235        clk = kzalloc(sizeof(struct v4l2_clk), GFP_KERNEL);
 236        if (!clk)
 237                return ERR_PTR(-ENOMEM);
 238
 239        clk->dev_id = kstrdup(dev_id, GFP_KERNEL);
 240        if (!clk->dev_id) {
 241                ret = -ENOMEM;
 242                goto ealloc;
 243        }
 244        clk->ops = ops;
 245        clk->priv = priv;
 246        atomic_set(&clk->use_count, 0);
 247        mutex_init(&clk->lock);
 248
 249        mutex_lock(&clk_lock);
 250        if (!IS_ERR(v4l2_clk_find(dev_id))) {
 251                mutex_unlock(&clk_lock);
 252                ret = -EEXIST;
 253                goto eexist;
 254        }
 255        list_add_tail(&clk->list, &clk_list);
 256        mutex_unlock(&clk_lock);
 257
 258        return clk;
 259
 260eexist:
 261ealloc:
 262        kfree(clk->dev_id);
 263        kfree(clk);
 264        return ERR_PTR(ret);
 265}
 266EXPORT_SYMBOL(v4l2_clk_register);
 267
 268void v4l2_clk_unregister(struct v4l2_clk *clk)
 269{
 270        if (WARN(atomic_read(&clk->use_count),
 271                 "%s(): Refusing to unregister ref-counted %s clock!\n",
 272                 __func__, clk->dev_id))
 273                return;
 274
 275        mutex_lock(&clk_lock);
 276        list_del(&clk->list);
 277        mutex_unlock(&clk_lock);
 278
 279        kfree(clk->dev_id);
 280        kfree(clk);
 281}
 282EXPORT_SYMBOL(v4l2_clk_unregister);
 283
 284struct v4l2_clk_fixed {
 285        unsigned long rate;
 286        struct v4l2_clk_ops ops;
 287};
 288
 289static unsigned long fixed_get_rate(struct v4l2_clk *clk)
 290{
 291        struct v4l2_clk_fixed *priv = clk->priv;
 292        return priv->rate;
 293}
 294
 295struct v4l2_clk *__v4l2_clk_register_fixed(const char *dev_id,
 296                                unsigned long rate, struct module *owner)
 297{
 298        struct v4l2_clk *clk;
 299        struct v4l2_clk_fixed *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 300
 301        if (!priv)
 302                return ERR_PTR(-ENOMEM);
 303
 304        priv->rate = rate;
 305        priv->ops.get_rate = fixed_get_rate;
 306        priv->ops.owner = owner;
 307
 308        clk = v4l2_clk_register(&priv->ops, dev_id, priv);
 309        if (IS_ERR(clk))
 310                kfree(priv);
 311
 312        return clk;
 313}
 314EXPORT_SYMBOL(__v4l2_clk_register_fixed);
 315
 316void v4l2_clk_unregister_fixed(struct v4l2_clk *clk)
 317{
 318        kfree(clk->priv);
 319        v4l2_clk_unregister(clk);
 320}
 321EXPORT_SYMBOL(v4l2_clk_unregister_fixed);
 322