~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/intel/skylake/skl-ssp-clk.c

Version: ~ [ linux-5.9 ] ~ [ linux-5.8.14 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.70 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.150 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.200 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.238 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.238 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
 20 struct skl_clk_parent {
 21         struct clk_hw *hw;
 22         struct clk_lookup *lookup;
 23 };
 24 
 25 struct 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 
 33 struct 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 
 39 static 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 
 56 static 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 
 73 static 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 */
104 static 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 
162 static 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 
176 static 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 
199 static 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 
206 static 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 
213 static 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 
238 static 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 */
250 static 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  */
260 static 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 
268 static 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 
277 static void unregister_src_clk(struct skl_clk_data *dclk)
278 {
279         while (dclk->avail_clk_cnt--)
280                 clkdev_drop(dclk->clk[dclk->avail_clk_cnt]->lookup);
281 }
282 
283 static int skl_register_parent_clks(struct device *dev,
284                         struct skl_clk_parent *parent,
285                         struct skl_clk_parent_src *pclk)
286 {
287         int i, ret;
288 
289         for (i = 0; i < SKL_MAX_CLK_SRC; i++) {
290 
291                 /* Register Parent clock */
292                 parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name,
293                                 pclk[i].parent_name, 0, pclk[i].rate);
294                 if (IS_ERR(parent[i].hw)) {
295                         ret = PTR_ERR(parent[i].hw);
296                         goto err;
297                 }
298 
299                 parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name,
300                                                                         NULL);
301                 if (!parent[i].lookup) {
302                         clk_hw_unregister_fixed_rate(parent[i].hw);
303                         ret = -ENOMEM;
304                         goto err;
305                 }
306         }
307 
308         return 0;
309 err:
310         unregister_parent_src_clk(parent, i);
311         return ret;
312 }
313 
314 /* Assign fmt_config to clk_data */
315 static struct skl_clk *register_skl_clk(struct device *dev,
316                         struct skl_ssp_clk *clk,
317                         struct skl_clk_pdata *clk_pdata, int id)
318 {
319         struct clk_init_data init;
320         struct skl_clk *clkdev;
321         int ret;
322 
323         clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL);
324         if (!clkdev)
325                 return ERR_PTR(-ENOMEM);
326 
327         init.name = clk->name;
328         init.ops = &skl_clk_ops;
329         init.flags = CLK_SET_RATE_GATE;
330         init.parent_names = &clk->parent_name;
331         init.num_parents = 1;
332         clkdev->hw.init = &init;
333         clkdev->pdata = clk_pdata;
334 
335         clkdev->id = id;
336         ret = devm_clk_hw_register(dev, &clkdev->hw);
337         if (ret) {
338                 clkdev = ERR_PTR(ret);
339                 return clkdev;
340         }
341 
342         clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL);
343         if (!clkdev->lookup)
344                 clkdev = ERR_PTR(-ENOMEM);
345 
346         return clkdev;
347 }
348 
349 static int skl_clk_dev_probe(struct platform_device *pdev)
350 {
351         struct device *dev = &pdev->dev;
352         struct device *parent_dev = dev->parent;
353         struct skl_clk_parent_src *parent_clks;
354         struct skl_clk_pdata *clk_pdata;
355         struct skl_clk_data *data;
356         struct skl_ssp_clk *clks;
357         int ret, i;
358 
359         clk_pdata = dev_get_platdata(&pdev->dev);
360         parent_clks = clk_pdata->parent_clks;
361         clks = clk_pdata->ssp_clks;
362         if (!parent_clks || !clks)
363                 return -EIO;
364 
365         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
366         if (!data)
367                 return -ENOMEM;
368 
369         /* Register Parent clock */
370         ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks);
371         if (ret < 0)
372                 return ret;
373 
374         for (i = 0; i < clk_pdata->num_clks; i++) {
375                 /*
376                  * Only register valid clocks
377                  * i.e. for which nhlt entry is present.
378                  */
379                 if (clks[i].rate_cfg[0].rate == 0)
380                         continue;
381 
382                 data->clk[data->avail_clk_cnt] = register_skl_clk(dev,
383                                 &clks[i], clk_pdata, i);
384 
385                 if (IS_ERR(data->clk[data->avail_clk_cnt])) {
386                         ret = PTR_ERR(data->clk[data->avail_clk_cnt++]);
387                         goto err_unreg_skl_clk;
388                 }
389         }
390 
391         platform_set_drvdata(pdev, data);
392 
393         return 0;
394 
395 err_unreg_skl_clk:
396         unregister_src_clk(data);
397         unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
398 
399         return ret;
400 }
401 
402 static int skl_clk_dev_remove(struct platform_device *pdev)
403 {
404         struct skl_clk_data *data;
405 
406         data = platform_get_drvdata(pdev);
407         unregister_src_clk(data);
408         unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
409 
410         return 0;
411 }
412 
413 static struct platform_driver skl_clk_driver = {
414         .driver = {
415                 .name = "skl-ssp-clk",
416         },
417         .probe = skl_clk_dev_probe,
418         .remove = skl_clk_dev_remove,
419 };
420 
421 module_platform_driver(skl_clk_driver);
422 
423 MODULE_DESCRIPTION("Skylake clock driver");
424 MODULE_AUTHOR("Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>");
425 MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
426 MODULE_LICENSE("GPL v2");
427 MODULE_ALIAS("platform:skl-ssp-clk");
428 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp