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 
skl_get_clk_type(u32 index)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 
skl_get_vbus_id(u32 index,u8 clk_type)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 
skl_fill_clk_ipc(struct skl_clk_rate_cfg_table * rcfg,u8 clk_type)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 */
skl_send_clk_dma_control(struct skl_dev * skl,struct skl_clk_rate_cfg_table * rcfg,u32 vbus_id,u8 clk_type,bool enable)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 
skl_get_rate_cfg(struct skl_clk_rate_cfg_table * rcfg,unsigned long rate)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 
skl_clk_change_status(struct skl_clk * clkdev,bool enable)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 
skl_clk_prepare(struct clk_hw * hw)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 
skl_clk_unprepare(struct clk_hw * hw)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 
skl_clk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)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 
skl_clk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)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 */
skl_clk_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)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 
unregister_parent_src_clk(struct skl_clk_parent * pclk,unsigned int id)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 
unregister_src_clk(struct skl_clk_data * dclk)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 
skl_register_parent_clks(struct device * dev,struct skl_clk_parent * parent,struct skl_clk_parent_src * pclk)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 */
register_skl_clk(struct device * dev,struct skl_ssp_clk * clk,struct skl_clk_pdata * clk_pdata,int id)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 
skl_clk_dev_probe(struct platform_device * pdev)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 
skl_clk_dev_remove(struct platform_device * pdev)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