linux/drivers/gpu/drm/sun4i/sun6i_drc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2016 Free Electrons
   4 *
   5 * Maxime Ripard <maxime.ripard@free-electrons.com>
   6 */
   7
   8#include <linux/clk.h>
   9#include <linux/component.h>
  10#include <linux/module.h>
  11#include <linux/mod_devicetable.h>
  12#include <linux/platform_device.h>
  13#include <linux/regmap.h>
  14#include <linux/reset.h>
  15
  16struct sun6i_drc {
  17        struct clk              *bus_clk;
  18        struct clk              *mod_clk;
  19        struct reset_control    *reset;
  20};
  21
  22static int sun6i_drc_bind(struct device *dev, struct device *master,
  23                         void *data)
  24{
  25        struct sun6i_drc *drc;
  26        int ret;
  27
  28        drc = devm_kzalloc(dev, sizeof(*drc), GFP_KERNEL);
  29        if (!drc)
  30                return -ENOMEM;
  31        dev_set_drvdata(dev, drc);
  32
  33        drc->reset = devm_reset_control_get(dev, NULL);
  34        if (IS_ERR(drc->reset)) {
  35                dev_err(dev, "Couldn't get our reset line\n");
  36                return PTR_ERR(drc->reset);
  37        }
  38
  39        ret = reset_control_deassert(drc->reset);
  40        if (ret) {
  41                dev_err(dev, "Couldn't deassert our reset line\n");
  42                return ret;
  43        }
  44
  45        drc->bus_clk = devm_clk_get(dev, "ahb");
  46        if (IS_ERR(drc->bus_clk)) {
  47                dev_err(dev, "Couldn't get our bus clock\n");
  48                ret = PTR_ERR(drc->bus_clk);
  49                goto err_assert_reset;
  50        }
  51        clk_prepare_enable(drc->bus_clk);
  52
  53        drc->mod_clk = devm_clk_get(dev, "mod");
  54        if (IS_ERR(drc->mod_clk)) {
  55                dev_err(dev, "Couldn't get our mod clock\n");
  56                ret = PTR_ERR(drc->mod_clk);
  57                goto err_disable_bus_clk;
  58        }
  59
  60        ret = clk_set_rate_exclusive(drc->mod_clk, 300000000);
  61        if (ret) {
  62                dev_err(dev, "Couldn't set the module clock frequency\n");
  63                goto err_disable_bus_clk;
  64        }
  65
  66        clk_prepare_enable(drc->mod_clk);
  67
  68        return 0;
  69
  70err_disable_bus_clk:
  71        clk_disable_unprepare(drc->bus_clk);
  72err_assert_reset:
  73        reset_control_assert(drc->reset);
  74        return ret;
  75}
  76
  77static void sun6i_drc_unbind(struct device *dev, struct device *master,
  78                            void *data)
  79{
  80        struct sun6i_drc *drc = dev_get_drvdata(dev);
  81
  82        clk_rate_exclusive_put(drc->mod_clk);
  83        clk_disable_unprepare(drc->mod_clk);
  84        clk_disable_unprepare(drc->bus_clk);
  85        reset_control_assert(drc->reset);
  86}
  87
  88static const struct component_ops sun6i_drc_ops = {
  89        .bind   = sun6i_drc_bind,
  90        .unbind = sun6i_drc_unbind,
  91};
  92
  93static int sun6i_drc_probe(struct platform_device *pdev)
  94{
  95        return component_add(&pdev->dev, &sun6i_drc_ops);
  96}
  97
  98static int sun6i_drc_remove(struct platform_device *pdev)
  99{
 100        component_del(&pdev->dev, &sun6i_drc_ops);
 101
 102        return 0;
 103}
 104
 105static const struct of_device_id sun6i_drc_of_table[] = {
 106        { .compatible = "allwinner,sun6i-a31-drc" },
 107        { .compatible = "allwinner,sun6i-a31s-drc" },
 108        { .compatible = "allwinner,sun8i-a23-drc" },
 109        { .compatible = "allwinner,sun8i-a33-drc" },
 110        { .compatible = "allwinner,sun9i-a80-drc" },
 111        { }
 112};
 113MODULE_DEVICE_TABLE(of, sun6i_drc_of_table);
 114
 115static struct platform_driver sun6i_drc_platform_driver = {
 116        .probe          = sun6i_drc_probe,
 117        .remove         = sun6i_drc_remove,
 118        .driver         = {
 119                .name           = "sun6i-drc",
 120                .of_match_table = sun6i_drc_of_table,
 121        },
 122};
 123module_platform_driver(sun6i_drc_platform_driver);
 124
 125MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
 126MODULE_DESCRIPTION("Allwinner A31 Dynamic Range Control (DRC) Driver");
 127MODULE_LICENSE("GPL");
 128