ASoC: SOF: IPC4: update gain ipc msg definition to align with fw
[linux-block.git] / sound / soc / sof / ipc4-control.c
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2 //
3 // This file is provided under a dual BSD/GPLv2 license.  When using or
4 // redistributing this file, you may do so under either license.
5 //
6 // Copyright(c) 2022 Intel Corporation. All rights reserved.
7 //
8 //
9
10 #include "sof-priv.h"
11 #include "sof-audio.h"
12 #include "ipc4-priv.h"
13 #include "ipc4-topology.h"
14
15 static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol,
16                                           bool set, bool lock)
17 {
18         struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
19         struct snd_soc_component *scomp = scontrol->scomp;
20         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
21         const struct sof_ipc_ops *iops = sdev->ipc->ops;
22         struct sof_ipc4_msg *msg = &cdata->msg;
23         struct snd_sof_widget *swidget;
24         bool widget_found = false;
25         int ret = 0;
26
27         /* find widget associated with the control */
28         list_for_each_entry(swidget, &sdev->widget_list, list) {
29                 if (swidget->comp_id == scontrol->comp_id) {
30                         widget_found = true;
31                         break;
32                 }
33         }
34
35         if (!widget_found) {
36                 dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name);
37                 return -ENOENT;
38         }
39
40         if (lock)
41                 mutex_lock(&swidget->setup_mutex);
42         else
43                 lockdep_assert_held(&swidget->setup_mutex);
44
45         /*
46          * Volatile controls should always be part of static pipelines and the
47          * widget use_count would always be > 0 in this case. For the others,
48          * just return the cached value if the widget is not set up.
49          */
50         if (!swidget->use_count)
51                 goto unlock;
52
53         msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;
54         msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);
55
56         ret = iops->set_get_data(sdev, msg, msg->data_size, set);
57
58 unlock:
59         if (lock)
60                 mutex_unlock(&swidget->setup_mutex);
61
62         return ret;
63 }
64
65 static int
66 sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
67                          struct snd_sof_control *scontrol, bool lock)
68 {
69         struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
70         struct sof_ipc4_gain *gain = swidget->private;
71         struct sof_ipc4_msg *msg = &cdata->msg;
72         struct sof_ipc4_gain_data data;
73         bool all_channels_equal = true;
74         u32 value;
75         int ret, i;
76
77         /* check if all channel values are equal */
78         value = cdata->chanv[0].value;
79         for (i = 1; i < scontrol->num_channels; i++) {
80                 if (cdata->chanv[i].value != value) {
81                         all_channels_equal = false;
82                         break;
83                 }
84         }
85
86         /*
87          * notify DSP with a single IPC message if all channel values are equal. Otherwise send
88          * a separate IPC for each channel.
89          */
90         for (i = 0; i < scontrol->num_channels; i++) {
91                 if (all_channels_equal) {
92                         data.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK;
93                         data.init_val = cdata->chanv[0].value;
94                 } else {
95                         data.channels = cdata->chanv[i].channel;
96                         data.init_val = cdata->chanv[i].value;
97                 }
98
99                 /* set curve type and duration from topology */
100                 data.curve_duration_l = gain->data.curve_duration_l;
101                 data.curve_duration_h = gain->data.curve_duration_h;
102                 data.curve_type = gain->data.curve_type;
103
104                 msg->data_ptr = &data;
105                 msg->data_size = sizeof(data);
106
107                 ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
108                 msg->data_ptr = NULL;
109                 msg->data_size = 0;
110                 if (ret < 0) {
111                         dev_err(sdev->dev, "Failed to set volume update for %s\n",
112                                 scontrol->name);
113                         return ret;
114                 }
115
116                 if (all_channels_equal)
117                         break;
118         }
119
120         return 0;
121 }
122
123 static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol,
124                                 struct snd_ctl_elem_value *ucontrol)
125 {
126         struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
127         struct snd_soc_component *scomp = scontrol->scomp;
128         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
129         unsigned int channels = scontrol->num_channels;
130         struct snd_sof_widget *swidget;
131         bool widget_found = false;
132         bool change = false;
133         unsigned int i;
134         int ret;
135
136         /* update each channel */
137         for (i = 0; i < channels; i++) {
138                 u32 value = mixer_to_ipc(ucontrol->value.integer.value[i],
139                                          scontrol->volume_table, scontrol->max + 1);
140
141                 change = change || (value != cdata->chanv[i].value);
142                 cdata->chanv[i].channel = i;
143                 cdata->chanv[i].value = value;
144         }
145
146         if (!pm_runtime_active(scomp->dev))
147                 return change;
148
149         /* find widget associated with the control */
150         list_for_each_entry(swidget, &sdev->widget_list, list) {
151                 if (swidget->comp_id == scontrol->comp_id) {
152                         widget_found = true;
153                         break;
154                 }
155         }
156
157         if (!widget_found) {
158                 dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name);
159                 return false;
160         }
161
162         ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true);
163         if (ret < 0)
164                 return false;
165
166         return change;
167 }
168
169 static int sof_ipc4_volume_get(struct snd_sof_control *scontrol,
170                                struct snd_ctl_elem_value *ucontrol)
171 {
172         struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
173         unsigned int channels = scontrol->num_channels;
174         unsigned int i;
175
176         for (i = 0; i < channels; i++)
177                 ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value,
178                                                                 scontrol->volume_table,
179                                                                 scontrol->max + 1);
180
181         return 0;
182 }
183
184 /* set up all controls for the widget */
185 static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
186 {
187         struct snd_sof_control *scontrol;
188         int ret;
189
190         list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
191                 if (scontrol->comp_id == swidget->comp_id) {
192                         ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false);
193                         if (ret < 0) {
194                                 dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n",
195                                         __func__, scontrol->comp_id, swidget->widget->name);
196                                 return ret;
197                         }
198                 }
199
200         return 0;
201 }
202
203 static int
204 sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size)
205 {
206         int i;
207
208         /* init the volume table */
209         scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL);
210         if (!scontrol->volume_table)
211                 return -ENOMEM;
212
213         /* populate the volume table */
214         for (i = 0; i < size ; i++) {
215                 u32 val = vol_compute_gain(i, tlv);
216                 u64 q31val = ((u64)val) << 15; /* Can be over Q1.31, need to saturate */
217
218                 scontrol->volume_table[i] = q31val > SOF_IPC4_VOL_ZERO_DB ?
219                                                 SOF_IPC4_VOL_ZERO_DB : q31val;
220         }
221
222         return 0;
223 }
224
225 const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = {
226         .volume_put = sof_ipc4_volume_put,
227         .volume_get = sof_ipc4_volume_get,
228         .widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup,
229         .set_up_volume_table = sof_ipc4_set_up_volume_table,
230 };