ASoC: pcm5102a: replace codec to component
[linux-2.6-block.git] / sound / soc / intel / skylake / skl-ssp-clk.c
CommitLineData
01f50d69
SP
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 */
250long 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");