]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - sound/soc/intel/skylake/skl-topology.c
ASoC: Intel: Skylake: Add pipe and modules handlers
[karo-tx-linux.git] / sound / soc / intel / skylake / skl-topology.c
1 /*
2  *  skl-topology.c - Implements Platform component ALSA controls/widget
3  *  handlers.
4  *
5  *  Copyright (C) 2014-2015 Intel Corp
6  *  Author: Jeeja KP <jeeja.kp@intel.com>
7  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as version 2, as
11  * published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  */
18
19 #include <linux/slab.h>
20 #include <linux/types.h>
21 #include <linux/firmware.h>
22 #include <sound/soc.h>
23 #include <sound/soc-topology.h>
24 #include "skl-sst-dsp.h"
25 #include "skl-sst-ipc.h"
26 #include "skl-topology.h"
27 #include "skl.h"
28 #include "skl-tplg-interface.h"
29
30 /*
31  * SKL DSP driver modelling uses only few DAPM widgets so for rest we will
32  * ignore. This helpers checks if the SKL driver handles this widget type
33  */
34 static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w)
35 {
36         switch (w->id) {
37         case snd_soc_dapm_dai_link:
38         case snd_soc_dapm_dai_in:
39         case snd_soc_dapm_aif_in:
40         case snd_soc_dapm_aif_out:
41         case snd_soc_dapm_dai_out:
42         case snd_soc_dapm_switch:
43                 return false;
44         default:
45                 return true;
46         }
47 }
48
49 /*
50  * Each pipelines needs memory to be allocated. Check if we have free memory
51  * from available pool. Then only add this to pool
52  * This is freed when pipe is deleted
53  * Note: DSP does actual memory management we only keep track for complete
54  * pool
55  */
56 static bool skl_tplg_alloc_pipe_mem(struct skl *skl,
57                                 struct skl_module_cfg *mconfig)
58 {
59         struct skl_sst *ctx = skl->skl_sst;
60
61         if (skl->resource.mem + mconfig->pipe->memory_pages >
62                                 skl->resource.max_mem) {
63                 dev_err(ctx->dev,
64                                 "%s: module_id %d instance %d\n", __func__,
65                                 mconfig->id.module_id,
66                                 mconfig->id.instance_id);
67                 dev_err(ctx->dev,
68                                 "exceeds ppl memory available %d mem %d\n",
69                                 skl->resource.max_mem, skl->resource.mem);
70                 return false;
71         }
72
73         skl->resource.mem += mconfig->pipe->memory_pages;
74         return true;
75 }
76
77 /*
78  * Pipeline needs needs DSP CPU resources for computation, this is
79  * quantified in MCPS (Million Clocks Per Second) required for module/pipe
80  *
81  * Each pipelines needs mcps to be allocated. Check if we have mcps for this
82  * pipe. This adds the mcps to driver counter
83  * This is removed on pipeline delete
84  */
85 static bool skl_tplg_alloc_pipe_mcps(struct skl *skl,
86                                 struct skl_module_cfg *mconfig)
87 {
88         struct skl_sst *ctx = skl->skl_sst;
89
90         if (skl->resource.mcps + mconfig->mcps > skl->resource.max_mcps) {
91                 dev_err(ctx->dev,
92                         "%s: module_id %d instance %d\n", __func__,
93                         mconfig->id.module_id, mconfig->id.instance_id);
94                 dev_err(ctx->dev,
95                         "exceeds ppl memory available %d > mem %d\n",
96                         skl->resource.max_mcps, skl->resource.mcps);
97                 return false;
98         }
99
100         skl->resource.mcps += mconfig->mcps;
101         return true;
102 }
103
104 /*
105  * Free the mcps when tearing down
106  */
107 static void
108 skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig)
109 {
110         skl->resource.mcps -= mconfig->mcps;
111 }
112
113 /*
114  * Free the memory when tearing down
115  */
116 static void
117 skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig)
118 {
119         skl->resource.mem -= mconfig->pipe->memory_pages;
120 }
121
122 /*
123  * A pipe can have multiple modules, each of them will be a DAPM widget as
124  * well. While managing a pipeline we need to get the list of all the
125  * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps
126  * to get the SKL type widgets in that pipeline
127  */
128 static int skl_tplg_alloc_pipe_widget(struct device *dev,
129         struct snd_soc_dapm_widget *w, struct skl_pipe *pipe)
130 {
131         struct skl_module_cfg *src_module = NULL;
132         struct snd_soc_dapm_path *p = NULL;
133         struct skl_pipe_module *p_module = NULL;
134
135         p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL);
136         if (!p_module)
137                 return -ENOMEM;
138
139         p_module->w = w;
140         list_add_tail(&p_module->node, &pipe->w_list);
141
142         snd_soc_dapm_widget_for_each_sink_path(w, p) {
143                 if ((p->sink->priv == NULL)
144                                 && (!is_skl_dsp_widget_type(w)))
145                         continue;
146
147                 if ((p->sink->priv != NULL) && p->connect
148                                 && is_skl_dsp_widget_type(p->sink)) {
149
150                         src_module = p->sink->priv;
151                         if (pipe->ppl_id == src_module->pipe->ppl_id)
152                                 skl_tplg_alloc_pipe_widget(dev,
153                                                         p->sink, pipe);
154                 }
155         }
156         return 0;
157 }
158
159 /*
160  * Inside a pipe instance, we can have various modules. These modules need
161  * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by
162  * skl_init_module() routine, so invoke that for all modules in a pipeline
163  */
164 static int
165 skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
166 {
167         struct skl_pipe_module *w_module;
168         struct snd_soc_dapm_widget *w;
169         struct skl_module_cfg *mconfig;
170         struct skl_sst *ctx = skl->skl_sst;
171         int ret = 0;
172
173         list_for_each_entry(w_module, &pipe->w_list, node) {
174                 w = w_module->w;
175                 mconfig = w->priv;
176
177                 /* check resource available */
178                 if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
179                         return -ENOMEM;
180
181                 ret = skl_init_module(ctx, mconfig, NULL);
182                 if (ret < 0)
183                         return ret;
184         }
185
186         return 0;
187 }