123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- // SPDX-License-Identifier: GPL-2.0-only
- // Copyright(c) 2015-17 Intel Corporation
- /*
- * skl-ssp-clk.c - ASoC skylake ssp clock driver
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/err.h>
- #include <linux/platform_device.h>
- #include <linux/clk-provider.h>
- #include <linux/clkdev.h>
- #include <sound/intel-nhlt.h>
- #include "skl.h"
- #include "skl-ssp-clk.h"
- #include "skl-topology.h"
- #define to_skl_clk(_hw) container_of(_hw, struct skl_clk, hw)
- struct skl_clk_parent {
- struct clk_hw *hw;
- struct clk_lookup *lookup;
- };
- struct skl_clk {
- struct clk_hw hw;
- struct clk_lookup *lookup;
- unsigned long rate;
- struct skl_clk_pdata *pdata;
- u32 id;
- };
- struct skl_clk_data {
- struct skl_clk_parent parent[SKL_MAX_CLK_SRC];
- struct skl_clk *clk[SKL_MAX_CLK_CNT];
- u8 avail_clk_cnt;
- };
- static int skl_get_clk_type(u32 index)
- {
- switch (index) {
- case 0 ... (SKL_SCLK_OFS - 1):
- return SKL_MCLK;
- case SKL_SCLK_OFS ... (SKL_SCLKFS_OFS - 1):
- return SKL_SCLK;
- case SKL_SCLKFS_OFS ... (SKL_MAX_CLK_CNT - 1):
- return SKL_SCLK_FS;
- default:
- return -EINVAL;
- }
- }
- static int skl_get_vbus_id(u32 index, u8 clk_type)
- {
- switch (clk_type) {
- case SKL_MCLK:
- return index;
- case SKL_SCLK:
- return index - SKL_SCLK_OFS;
- case SKL_SCLK_FS:
- return index - SKL_SCLKFS_OFS;
- default:
- return -EINVAL;
- }
- }
- static void skl_fill_clk_ipc(struct skl_clk_rate_cfg_table *rcfg, u8 clk_type)
- {
- struct nhlt_fmt_cfg *fmt_cfg;
- union skl_clk_ctrl_ipc *ipc;
- struct wav_fmt *wfmt;
- if (!rcfg)
- return;
- ipc = &rcfg->dma_ctl_ipc;
- if (clk_type == SKL_SCLK_FS) {
- fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
- wfmt = &fmt_cfg->fmt_ext.fmt;
- /* Remove TLV Header size */
- ipc->sclk_fs.hdr.size = sizeof(struct skl_dmactrl_sclkfs_cfg) -
- sizeof(struct skl_tlv_hdr);
- ipc->sclk_fs.sampling_frequency = wfmt->samples_per_sec;
- ipc->sclk_fs.bit_depth = wfmt->bits_per_sample;
- ipc->sclk_fs.valid_bit_depth =
- fmt_cfg->fmt_ext.sample.valid_bits_per_sample;
- ipc->sclk_fs.number_of_channels = wfmt->channels;
- } else {
- ipc->mclk.hdr.type = DMA_CLK_CONTROLS;
- /* Remove TLV Header size */
- ipc->mclk.hdr.size = sizeof(struct skl_dmactrl_mclk_cfg) -
- sizeof(struct skl_tlv_hdr);
- }
- }
- /* Sends dma control IPC to turn the clock ON/OFF */
- static int skl_send_clk_dma_control(struct skl_dev *skl,
- struct skl_clk_rate_cfg_table *rcfg,
- u32 vbus_id, u8 clk_type,
- bool enable)
- {
- struct nhlt_specific_cfg *sp_cfg;
- u32 i2s_config_size, node_id = 0;
- struct nhlt_fmt_cfg *fmt_cfg;
- union skl_clk_ctrl_ipc *ipc;
- void *i2s_config = NULL;
- u8 *data, size;
- int ret;
- if (!rcfg)
- return -EIO;
- ipc = &rcfg->dma_ctl_ipc;
- fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
- sp_cfg = &fmt_cfg->config;
- if (clk_type == SKL_SCLK_FS) {
- ipc->sclk_fs.hdr.type =
- enable ? DMA_TRANSMITION_START : DMA_TRANSMITION_STOP;
- data = (u8 *)&ipc->sclk_fs;
- size = sizeof(struct skl_dmactrl_sclkfs_cfg);
- } else {
- /* 1 to enable mclk, 0 to enable sclk */
- if (clk_type == SKL_SCLK)
- ipc->mclk.mclk = 0;
- else
- ipc->mclk.mclk = 1;
- ipc->mclk.keep_running = enable;
- ipc->mclk.warm_up_over = enable;
- ipc->mclk.clk_stop_over = !enable;
- data = (u8 *)&ipc->mclk;
- size = sizeof(struct skl_dmactrl_mclk_cfg);
- }
- i2s_config_size = sp_cfg->size + size;
- i2s_config = kzalloc(i2s_config_size, GFP_KERNEL);
- if (!i2s_config)
- return -ENOMEM;
- /* copy blob */
- memcpy(i2s_config, sp_cfg->caps, sp_cfg->size);
- /* copy additional dma controls information */
- memcpy(i2s_config + sp_cfg->size, data, size);
- node_id = ((SKL_DMA_I2S_LINK_INPUT_CLASS << 8) | (vbus_id << 4));
- ret = skl_dsp_set_dma_control(skl, (u32 *)i2s_config,
- i2s_config_size, node_id);
- kfree(i2s_config);
- return ret;
- }
- static struct skl_clk_rate_cfg_table *skl_get_rate_cfg(
- struct skl_clk_rate_cfg_table *rcfg,
- unsigned long rate)
- {
- int i;
- for (i = 0; (i < SKL_MAX_CLK_RATES) && rcfg[i].rate; i++) {
- if (rcfg[i].rate == rate)
- return &rcfg[i];
- }
- return NULL;
- }
- static int skl_clk_change_status(struct skl_clk *clkdev,
- bool enable)
- {
- struct skl_clk_rate_cfg_table *rcfg;
- int vbus_id, clk_type;
- clk_type = skl_get_clk_type(clkdev->id);
- if (clk_type < 0)
- return clk_type;
- vbus_id = skl_get_vbus_id(clkdev->id, clk_type);
- if (vbus_id < 0)
- return vbus_id;
- rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
- clkdev->rate);
- if (!rcfg)
- return -EINVAL;
- return skl_send_clk_dma_control(clkdev->pdata->pvt_data, rcfg,
- vbus_id, clk_type, enable);
- }
- static int skl_clk_prepare(struct clk_hw *hw)
- {
- struct skl_clk *clkdev = to_skl_clk(hw);
- return skl_clk_change_status(clkdev, true);
- }
- static void skl_clk_unprepare(struct clk_hw *hw)
- {
- struct skl_clk *clkdev = to_skl_clk(hw);
- skl_clk_change_status(clkdev, false);
- }
- static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
- {
- struct skl_clk *clkdev = to_skl_clk(hw);
- struct skl_clk_rate_cfg_table *rcfg;
- int clk_type;
- if (!rate)
- return -EINVAL;
- rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
- rate);
- if (!rcfg)
- return -EINVAL;
- clk_type = skl_get_clk_type(clkdev->id);
- if (clk_type < 0)
- return clk_type;
- skl_fill_clk_ipc(rcfg, clk_type);
- clkdev->rate = rate;
- return 0;
- }
- static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
- {
- struct skl_clk *clkdev = to_skl_clk(hw);
- if (clkdev->rate)
- return clkdev->rate;
- return 0;
- }
- /* Not supported by clk driver. Implemented to satisfy clk fw */
- static long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
- {
- return rate;
- }
- /*
- * prepare/unprepare are used instead of enable/disable as IPC will be sent
- * in non-atomic context.
- */
- static const struct clk_ops skl_clk_ops = {
- .prepare = skl_clk_prepare,
- .unprepare = skl_clk_unprepare,
- .set_rate = skl_clk_set_rate,
- .round_rate = skl_clk_round_rate,
- .recalc_rate = skl_clk_recalc_rate,
- };
- static void unregister_parent_src_clk(struct skl_clk_parent *pclk,
- unsigned int id)
- {
- while (id--) {
- clkdev_drop(pclk[id].lookup);
- clk_hw_unregister_fixed_rate(pclk[id].hw);
- }
- }
- static void unregister_src_clk(struct skl_clk_data *dclk)
- {
- while (dclk->avail_clk_cnt--)
- clkdev_drop(dclk->clk[dclk->avail_clk_cnt]->lookup);
- }
- static int skl_register_parent_clks(struct device *dev,
- struct skl_clk_parent *parent,
- struct skl_clk_parent_src *pclk)
- {
- int i, ret;
- for (i = 0; i < SKL_MAX_CLK_SRC; i++) {
- /* Register Parent clock */
- parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name,
- pclk[i].parent_name, 0, pclk[i].rate);
- if (IS_ERR(parent[i].hw)) {
- ret = PTR_ERR(parent[i].hw);
- goto err;
- }
- parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name,
- NULL);
- if (!parent[i].lookup) {
- clk_hw_unregister_fixed_rate(parent[i].hw);
- ret = -ENOMEM;
- goto err;
- }
- }
- return 0;
- err:
- unregister_parent_src_clk(parent, i);
- return ret;
- }
- /* Assign fmt_config to clk_data */
- static struct skl_clk *register_skl_clk(struct device *dev,
- struct skl_ssp_clk *clk,
- struct skl_clk_pdata *clk_pdata, int id)
- {
- struct clk_init_data init;
- struct skl_clk *clkdev;
- int ret;
- clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL);
- if (!clkdev)
- return ERR_PTR(-ENOMEM);
- init.name = clk->name;
- init.ops = &skl_clk_ops;
- init.flags = CLK_SET_RATE_GATE;
- init.parent_names = &clk->parent_name;
- init.num_parents = 1;
- clkdev->hw.init = &init;
- clkdev->pdata = clk_pdata;
- clkdev->id = id;
- ret = devm_clk_hw_register(dev, &clkdev->hw);
- if (ret) {
- clkdev = ERR_PTR(ret);
- return clkdev;
- }
- clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL);
- if (!clkdev->lookup)
- clkdev = ERR_PTR(-ENOMEM);
- return clkdev;
- }
- static int skl_clk_dev_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct device *parent_dev = dev->parent;
- struct skl_clk_parent_src *parent_clks;
- struct skl_clk_pdata *clk_pdata;
- struct skl_clk_data *data;
- struct skl_ssp_clk *clks;
- int ret, i;
- clk_pdata = dev_get_platdata(&pdev->dev);
- parent_clks = clk_pdata->parent_clks;
- clks = clk_pdata->ssp_clks;
- if (!parent_clks || !clks)
- return -EIO;
- data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- /* Register Parent clock */
- ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks);
- if (ret < 0)
- return ret;
- for (i = 0; i < clk_pdata->num_clks; i++) {
- /*
- * Only register valid clocks
- * i.e. for which nhlt entry is present.
- */
- if (clks[i].rate_cfg[0].rate == 0)
- continue;
- data->clk[data->avail_clk_cnt] = register_skl_clk(dev,
- &clks[i], clk_pdata, i);
- if (IS_ERR(data->clk[data->avail_clk_cnt])) {
- ret = PTR_ERR(data->clk[data->avail_clk_cnt]);
- goto err_unreg_skl_clk;
- }
- data->avail_clk_cnt++;
- }
- platform_set_drvdata(pdev, data);
- return 0;
- err_unreg_skl_clk:
- unregister_src_clk(data);
- unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
- return ret;
- }
- static int skl_clk_dev_remove(struct platform_device *pdev)
- {
- struct skl_clk_data *data;
- data = platform_get_drvdata(pdev);
- unregister_src_clk(data);
- unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
- return 0;
- }
- static struct platform_driver skl_clk_driver = {
- .driver = {
- .name = "skl-ssp-clk",
- },
- .probe = skl_clk_dev_probe,
- .remove = skl_clk_dev_remove,
- };
- module_platform_driver(skl_clk_driver);
- MODULE_DESCRIPTION("Skylake clock driver");
- MODULE_AUTHOR("Jaikrishna Nemallapudi <[email protected]>");
- MODULE_AUTHOR("Subhransu S. Prusty <[email protected]>");
- MODULE_LICENSE("GPL v2");
- MODULE_ALIAS("platform:skl-ssp-clk");
|