aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSriram Periyasamy <sriramx.periyasamy@intel.com>2018-01-04 06:25:14 -0500
committerMark Brown <broonie@kernel.org>2018-01-26 07:51:21 -0500
commit01f50d69bebe1bb0b30bba1eba3cdaf1f02dd7c4 (patch)
treeca17ceb027f460797027d3095c0a748578cac40c
parente29a22a86a20ea7651ff8c731ab034c31bd9764e (diff)
ASoC: Intel: Skylake: Add ssp clock driver
For certain platforms, it is required to start the clocks (mclk/sclk/fs) before the stream start. Example: for few chrome systems, codec needs the mclk/sclk to be enabled early for a successful clock synchronization and for few IVI platforms, clock need to be enabled at boot and should be ON always. Add the required structures and create set_dma_control ipc to enable or disable the clock. To enable sclk without fs, mclk ipc structure is used, else sclkfs ipc structure is used. Clock prepare/unprepare are used to enable/disable the clock as the IPC will be sent in non-atomic context. The clk set_dma_control IPC structures are populated during the set_rate callback and IPC is sent to enable the clock during prepare callback. This patch creates virtual clock driver, which allows the machine driver to use the clock interface to send IPCs to DSP to enable/disable the clocks. Signed-off-by: Sriram Periyasamy <sriramx.periyasamy@intel.com> Signed-off-by: Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com> Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com> Acked-by: Vinod Koul <vinod.koul@intel.com> Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/intel/Kconfig3
-rw-r--r--sound/soc/intel/skylake/Makefile5
-rw-r--r--sound/soc/intel/skylake/skl-messages.c1
-rw-r--r--sound/soc/intel/skylake/skl-ssp-clk.c429
-rw-r--r--sound/soc/intel/skylake/skl-ssp-clk.h38
-rw-r--r--sound/soc/intel/skylake/skl.h6
6 files changed, 482 insertions, 0 deletions
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index f2c9e8c5970a..ceb105cbd461 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -97,6 +97,9 @@ config SND_SST_ATOM_HIFI2_PLATFORM
97 codec, then enable this option by saying Y or m. This is a 97 codec, then enable this option by saying Y or m. This is a
98 recommended option 98 recommended option
99 99
100config SND_SOC_INTEL_SKYLAKE_SSP_CLK
101 tristate
102
100config SND_SOC_INTEL_SKYLAKE 103config SND_SOC_INTEL_SKYLAKE
101 tristate "SKL/BXT/KBL/GLK/CNL... Platforms" 104 tristate "SKL/BXT/KBL/GLK/CNL... Platforms"
102 depends on PCI && ACPI 105 depends on PCI && ACPI
diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile
index d1ccbecd141f..86f6e1d801af 100644
--- a/sound/soc/intel/skylake/Makefile
+++ b/sound/soc/intel/skylake/Makefile
@@ -14,3 +14,8 @@ snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \
14 skl-sst-utils.o 14 skl-sst-utils.o
15 15
16obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o 16obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
17
18#Skylake Clock device support
19snd-soc-skl-ssp-clk-objs := skl-ssp-clk.o
20
21obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE_SSP_CLK) += snd-soc-skl-ssp-clk.o
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index 8cbf080c38b3..60d76adade43 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -675,6 +675,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps,
675 kfree(dma_ctrl); 675 kfree(dma_ctrl);
676 return err; 676 return err;
677} 677}
678EXPORT_SYMBOL_GPL(skl_dsp_set_dma_control);
678 679
679static void skl_setup_out_format(struct skl_sst *ctx, 680static void skl_setup_out_format(struct skl_sst *ctx,
680 struct skl_module_cfg *mconfig, 681 struct skl_module_cfg *mconfig,
diff --git a/sound/soc/intel/skylake/skl-ssp-clk.c b/sound/soc/intel/skylake/skl-ssp-clk.c
new file mode 100644
index 000000000000..7fbddf5e3b00
--- /dev/null
+++ b/sound/soc/intel/skylake/skl-ssp-clk.c
@@ -0,0 +1,429 @@
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");
diff --git a/sound/soc/intel/skylake/skl-ssp-clk.h b/sound/soc/intel/skylake/skl-ssp-clk.h
index c9ea84004260..d1be50f96c05 100644
--- a/sound/soc/intel/skylake/skl-ssp-clk.h
+++ b/sound/soc/intel/skylake/skl-ssp-clk.h
@@ -54,8 +54,46 @@ struct skl_clk_parent_src {
54 const char *parent_name; 54 const char *parent_name;
55}; 55};
56 56
57struct skl_tlv_hdr {
58 u32 type;
59 u32 size;
60};
61
62struct skl_dmactrl_mclk_cfg {
63 struct skl_tlv_hdr hdr;
64 /* DMA Clk TLV params */
65 u32 clk_warm_up:16;
66 u32 mclk:1;
67 u32 warm_up_over:1;
68 u32 rsvd0:14;
69 u32 clk_stop_delay:16;
70 u32 keep_running:1;
71 u32 clk_stop_over:1;
72 u32 rsvd1:14;
73};
74
75struct skl_dmactrl_sclkfs_cfg {
76 struct skl_tlv_hdr hdr;
77 /* DMA SClk&FS TLV params */
78 u32 sampling_frequency;
79 u32 bit_depth;
80 u32 channel_map;
81 u32 channel_config;
82 u32 interleaving_style;
83 u32 number_of_channels : 8;
84 u32 valid_bit_depth : 8;
85 u32 sample_type : 8;
86 u32 reserved : 8;
87};
88
89union skl_clk_ctrl_ipc {
90 struct skl_dmactrl_mclk_cfg mclk;
91 struct skl_dmactrl_sclkfs_cfg sclk_fs;
92};
93
57struct skl_clk_rate_cfg_table { 94struct skl_clk_rate_cfg_table {
58 unsigned long rate; 95 unsigned long rate;
96 union skl_clk_ctrl_ipc dma_ctl_ipc;
59 void *config; 97 void *config;
60}; 98};
61 99
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index f411579bc713..2d13f3fd988a 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -38,6 +38,10 @@
38/* D0I3C Register fields */ 38/* D0I3C Register fields */
39#define AZX_REG_VS_D0I3C_CIP 0x1 /* Command in progress */ 39#define AZX_REG_VS_D0I3C_CIP 0x1 /* Command in progress */
40#define AZX_REG_VS_D0I3C_I3 0x4 /* D0i3 enable */ 40#define AZX_REG_VS_D0I3C_I3 0x4 /* D0i3 enable */
41#define SKL_MAX_DMACTRL_CFG 18
42#define DMA_CLK_CONTROLS 1
43#define DMA_TRANSMITION_START 2
44#define DMA_TRANSMITION_STOP 3
41 45
42struct skl_dsp_resource { 46struct skl_dsp_resource {
43 u32 max_mcps; 47 u32 max_mcps;
@@ -147,6 +151,8 @@ int skl_nhlt_create_sysfs(struct skl *skl);
147void skl_nhlt_remove_sysfs(struct skl *skl); 151void skl_nhlt_remove_sysfs(struct skl *skl);
148void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks); 152void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks);
149struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id); 153struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id);
154int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps,
155 u32 caps_size, u32 node_id);
150 156
151struct skl_module_cfg; 157struct skl_module_cfg;
152 158