linux/sound/soc/intel/skylake/skl-ssp-clk.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright(c) 2015-17 Intel Corporation
   3
   4/*
   5 *  skl-ssp-clk.c - ASoC skylake ssp clock driver
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/err.h>
  11#include <linux/platform_device.h>
  12#include <linux/clk-provider.h>
  13#include <linux/clkdev.h>
  14#include "skl.h"
  15#include "skl-ssp-clk.h"
  16#include "skl-topology.h"
  17
  18#define to_skl_clk(_hw) container_of(_hw, struct skl_clk, hw)
  19
  20struct skl_clk_parent {
  21        struct clk_hw *hw;
  22        struct clk_lookup *lookup;
  23};
  24
  25struct skl_clk {
  26        struct clk_hw hw;
  27        struct clk_lookup *lookup;
  28        unsigned long rate;
  29        struct skl_clk_pdata *pdata;
  30        u32 id;
  31};
  32
  33struct skl_clk_data {
  34        struct skl_clk_parent parent[SKL_MAX_CLK_SRC];
  35        struct skl_clk *clk[SKL_MAX_CLK_CNT];
  36        u8 avail_clk_cnt;
  37};
  38
  39static int skl_get_clk_type(u32 index)
  40{
  41        switch (index) {
  42        case 0 ... (SKL_SCLK_OFS - 1):
  43                return SKL_MCLK;
  44
  45        case SKL_SCLK_OFS ... (SKL_SCLKFS_OFS - 1):
  46                return SKL_SCLK;
  47
  48        case SKL_SCLKFS_OFS ... (SKL_MAX_CLK_CNT - 1):
  49                return SKL_SCLK_FS;
  50
  51        default:
  52                return -EINVAL;
  53        }
  54}
  55
  56static int skl_get_vbus_id(u32 index, u8 clk_type)
  57{
  58        switch (clk_type) {
  59        case SKL_MCLK:
  60                return index;
  61
  62        case SKL_SCLK:
  63                return index - SKL_SCLK_OFS;
  64
  65        case SKL_SCLK_FS:
  66                return index - SKL_SCLKFS_OFS;
  67
  68        default:
  69                return -EINVAL;
  70        }
  71}
  72
  73static void skl_fill_clk_ipc(struct skl_clk_rate_cfg_table *rcfg, u8 clk_type)
  74{
  75        struct nhlt_fmt_cfg *fmt_cfg;
  76        union skl_clk_ctrl_ipc *ipc;
  77        struct wav_fmt *wfmt;
  78
  79        if (!rcfg)
  80                return;
  81
  82        ipc = &rcfg->dma_ctl_ipc;
  83        if (clk_type == SKL_SCLK_FS) {
  84                fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
  85                wfmt = &fmt_cfg->fmt_ext.fmt;
  86
  87                /* Remove TLV Header size */
  88                ipc->sclk_fs.hdr.size = sizeof(struct skl_dmactrl_sclkfs_cfg) -
  89                                                sizeof(struct skl_tlv_hdr);
  90                ipc->sclk_fs.sampling_frequency = wfmt->samples_per_sec;
  91                ipc->sclk_fs.bit_depth = wfmt->bits_per_sample;
  92                ipc->sclk_fs.valid_bit_depth =
  93                        fmt_cfg->fmt_ext.sample.valid_bits_per_sample;
  94                ipc->sclk_fs.number_of_channels = wfmt->channels;
  95        } else {
  96                ipc->mclk.hdr.type = DMA_CLK_CONTROLS;
  97                /* Remove TLV Header size */
  98                ipc->mclk.hdr.size = sizeof(struct skl_dmactrl_mclk_cfg) -
  99                                                sizeof(struct skl_tlv_hdr);
 100        }
 101}
 102
 103/* Sends dma control IPC to turn the clock ON/OFF */
 104static int skl_send_clk_dma_control(struct skl *skl,
 105                                struct skl_clk_rate_cfg_table *rcfg,
 106                                u32 vbus_id, u8 clk_type,
 107                                bool enable)
 108{
 109        struct nhlt_specific_cfg *sp_cfg;
 110        u32 i2s_config_size, node_id = 0;
 111        struct nhlt_fmt_cfg *fmt_cfg;
 112        union skl_clk_ctrl_ipc *ipc;
 113        void *i2s_config = NULL;
 114        u8 *data, size;
 115        int ret;
 116
 117        if (!rcfg)
 118                return -EIO;
 119
 120        ipc = &rcfg->dma_ctl_ipc;
 121        fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
 122        sp_cfg = &fmt_cfg->config;
 123
 124        if (clk_type == SKL_SCLK_FS) {
 125                ipc->sclk_fs.hdr.type =
 126                        enable ? DMA_TRANSMITION_START : DMA_TRANSMITION_STOP;
 127                data = (u8 *)&ipc->sclk_fs;
 128                size = sizeof(struct skl_dmactrl_sclkfs_cfg);
 129        } else {
 130                /* 1 to enable mclk, 0 to enable sclk */
 131                if (clk_type == SKL_SCLK)
 132                        ipc->mclk.mclk = 0;
 133                else
 134                        ipc->mclk.mclk = 1;
 135
 136                ipc->mclk.keep_running = enable;
 137                ipc->mclk.warm_up_over = enable;
 138                ipc->mclk.clk_stop_over = !enable;
 139                data = (u8 *)&ipc->mclk;
 140                size = sizeof(struct skl_dmactrl_mclk_cfg);
 141        }
 142
 143        i2s_config_size = sp_cfg->size + size;
 144        i2s_config = kzalloc(i2s_config_size, GFP_KERNEL);
 145        if (!i2s_config)
 146                return -ENOMEM;
 147
 148        /* copy blob */
 149        memcpy(i2s_config, sp_cfg->caps, sp_cfg->size);
 150
 151        /* copy additional dma controls information */
 152        memcpy(i2s_config + sp_cfg->size, data, size);
 153
 154        node_id = ((SKL_DMA_I2S_LINK_INPUT_CLASS << 8) | (vbus_id << 4));
 155        ret = skl_dsp_set_dma_control(skl->skl_sst, (u32 *)i2s_config,
 156                                        i2s_config_size, node_id);
 157        kfree(i2s_config);
 158
 159        return ret;
 160}
 161
 162static struct skl_clk_rate_cfg_table *skl_get_rate_cfg(
 163                struct skl_clk_rate_cfg_table *rcfg,
 164                                unsigned long rate)
 165{
 166        int i;
 167
 168        for (i = 0; (i < SKL_MAX_CLK_RATES) && rcfg[i].rate; i++) {
 169                if (rcfg[i].rate == rate)
 170                        return &rcfg[i];
 171        }
 172
 173        return NULL;
 174}
 175
 176static int skl_clk_change_status(struct skl_clk *clkdev,
 177                                bool enable)
 178{
 179        struct skl_clk_rate_cfg_table *rcfg;
 180        int vbus_id, clk_type;
 181
 182        clk_type = skl_get_clk_type(clkdev->id);
 183        if (clk_type < 0)
 184                return clk_type;
 185
 186        vbus_id = skl_get_vbus_id(clkdev->id, clk_type);
 187        if (vbus_id < 0)
 188                return vbus_id;
 189
 190        rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
 191                                                clkdev->rate);
 192        if (!rcfg)
 193                return -EINVAL;
 194
 195        return skl_send_clk_dma_control(clkdev->pdata->pvt_data, rcfg,
 196                                        vbus_id, clk_type, enable);
 197}
 198
 199static int skl_clk_prepare(struct clk_hw *hw)
 200{
 201        struct skl_clk *clkdev = to_skl_clk(hw);
 202
 203        return skl_clk_change_status(clkdev, true);
 204}
 205
 206static void skl_clk_unprepare(struct clk_hw *hw)
 207{
 208        struct skl_clk *clkdev = to_skl_clk(hw);
 209
 210        skl_clk_change_status(clkdev, false);
 211}
 212
 213static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
 214                                        unsigned long parent_rate)
 215{
 216        struct skl_clk *clkdev = to_skl_clk(hw);
 217        struct skl_clk_rate_cfg_table *rcfg;
 218        int clk_type;
 219
 220        if (!rate)
 221                return -EINVAL;
 222
 223        rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
 224                                                        rate);
 225        if (!rcfg)
 226                return -EINVAL;
 227
 228        clk_type = skl_get_clk_type(clkdev->id);
 229        if (clk_type < 0)
 230                return clk_type;
 231
 232        skl_fill_clk_ipc(rcfg, clk_type);
 233        clkdev->rate = rate;
 234
 235        return 0;
 236}
 237
 238static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
 239                                unsigned long parent_rate)
 240{
 241        struct skl_clk *clkdev = to_skl_clk(hw);
 242
 243        if (clkdev->rate)
 244                return clkdev->rate;
 245
 246        return 0;
 247}
 248
 249/* Not supported by clk driver. Implemented to satisfy clk fw */
 250static long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
 251                               unsigned long *parent_rate)
 252{
 253        return rate;
 254}
 255
 256/*
 257 * prepare/unprepare are used instead of enable/disable as IPC will be sent
 258 * in non-atomic context.
 259 */
 260static const struct clk_ops skl_clk_ops = {
 261        .prepare = skl_clk_prepare,
 262        .unprepare = skl_clk_unprepare,
 263        .set_rate = skl_clk_set_rate,
 264        .round_rate = skl_clk_round_rate,
 265        .recalc_rate = skl_clk_recalc_rate,
 266};
 267
 268static void unregister_parent_src_clk(struct skl_clk_parent *pclk,
 269                                        unsigned int id)
 270{
 271        while (id--) {
 272                clkdev_drop(pclk[id].lookup);
 273                clk_hw_unregister_fixed_rate(pclk[id].hw);
 274        }
 275}
 276
 277static void unregister_src_clk(struct skl_clk_data *dclk)
 278{
 279        u8 cnt = dclk->avail_clk_cnt;
 280
 281        while (cnt--)
 282                clkdev_drop(dclk->clk[cnt]->lookup);
 283}
 284
 285static int skl_register_parent_clks(struct device *dev,
 286                        struct skl_clk_parent *parent,
 287                        struct skl_clk_parent_src *pclk)
 288{
 289        int i, ret;
 290
 291        for (i = 0; i < SKL_MAX_CLK_SRC; i++) {
 292
 293                /* Register Parent clock */
 294                parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name,
 295                                pclk[i].parent_name, 0, pclk[i].rate);
 296                if (IS_ERR(parent[i].hw)) {
 297                        ret = PTR_ERR(parent[i].hw);
 298                        goto err;
 299                }
 300
 301                parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name,
 302                                                                        NULL);
 303                if (!parent[i].lookup) {
 304                        clk_hw_unregister_fixed_rate(parent[i].hw);
 305                        ret = -ENOMEM;
 306                        goto err;
 307                }
 308        }
 309
 310        return 0;
 311err:
 312        unregister_parent_src_clk(parent, i);
 313        return ret;
 314}
 315
 316/* Assign fmt_config to clk_data */
 317static struct skl_clk *register_skl_clk(struct device *dev,
 318                        struct skl_ssp_clk *clk,
 319                        struct skl_clk_pdata *clk_pdata, int id)
 320{
 321        struct clk_init_data init;
 322        struct skl_clk *clkdev;
 323        int ret;
 324
 325        clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL);
 326        if (!clkdev)
 327                return ERR_PTR(-ENOMEM);
 328
 329        init.name = clk->name;
 330        init.ops = &skl_clk_ops;
 331        init.flags = CLK_SET_RATE_GATE;
 332        init.parent_names = &clk->parent_name;
 333        init.num_parents = 1;
 334        clkdev->hw.init = &init;
 335        clkdev->pdata = clk_pdata;
 336
 337        clkdev->id = id;
 338        ret = devm_clk_hw_register(dev, &clkdev->hw);
 339        if (ret) {
 340                clkdev = ERR_PTR(ret);
 341                return clkdev;
 342        }
 343
 344        clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL);
 345        if (!clkdev->lookup)
 346                clkdev = ERR_PTR(-ENOMEM);
 347
 348        return clkdev;
 349}
 350
 351static int skl_clk_dev_probe(struct platform_device *pdev)
 352{
 353        struct device *dev = &pdev->dev;
 354        struct device *parent_dev = dev->parent;
 355        struct skl_clk_parent_src *parent_clks;
 356        struct skl_clk_pdata *clk_pdata;
 357        struct skl_clk_data *data;
 358        struct skl_ssp_clk *clks;
 359        int ret, i;
 360
 361        clk_pdata = dev_get_platdata(&pdev->dev);
 362        parent_clks = clk_pdata->parent_clks;
 363        clks = clk_pdata->ssp_clks;
 364        if (!parent_clks || !clks)
 365                return -EIO;
 366
 367        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 368        if (!data)
 369                return -ENOMEM;
 370
 371        /* Register Parent clock */
 372        ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks);
 373        if (ret < 0)
 374                return ret;
 375
 376        for (i = 0; i < clk_pdata->num_clks; i++) {
 377                /*
 378                 * Only register valid clocks
 379                 * i.e. for which nhlt entry is present.
 380                 */
 381                if (clks[i].rate_cfg[0].rate == 0)
 382                        continue;
 383
 384                data->clk[i] = register_skl_clk(dev, &clks[i], clk_pdata, i);
 385                if (IS_ERR(data->clk[i])) {
 386                        ret = PTR_ERR(data->clk[i]);
 387                        goto err_unreg_skl_clk;
 388                }
 389
 390                data->avail_clk_cnt++;
 391        }
 392
 393        platform_set_drvdata(pdev, data);
 394
 395        return 0;
 396
 397err_unreg_skl_clk:
 398        unregister_src_clk(data);
 399        unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
 400
 401        return ret;
 402}
 403
 404static int skl_clk_dev_remove(struct platform_device *pdev)
 405{
 406        struct skl_clk_data *data;
 407
 408        data = platform_get_drvdata(pdev);
 409        unregister_src_clk(data);
 410        unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
 411
 412        return 0;
 413}
 414
 415static struct platform_driver skl_clk_driver = {
 416        .driver = {
 417                .name = "skl-ssp-clk",
 418        },
 419        .probe = skl_clk_dev_probe,
 420        .remove = skl_clk_dev_remove,
 421};
 422
 423module_platform_driver(skl_clk_driver);
 424
 425MODULE_DESCRIPTION("Skylake clock driver");
 426MODULE_AUTHOR("Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>");
 427MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
 428MODULE_LICENSE("GPL v2");
 429MODULE_ALIAS("platform:skl-ssp-clk");
 430