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

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

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ 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.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

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

kernel.org | git.kernel.org | LWN.net | Project Home | 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.

sflogo.php