1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // Copyright(c) 2021-2022 Intel Corporation. All rights reserved. |
4 | // |
5 | // Authors: Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> |
6 | // Cezary Rojewski <cezary.rojewski@intel.com> |
7 | // |
8 | |
9 | #include <sound/soc.h> |
10 | #include "avs.h" |
11 | #include "control.h" |
12 | #include "messages.h" |
13 | #include "path.h" |
14 | |
15 | static struct avs_dev *avs_get_kcontrol_adev(struct snd_kcontrol *kcontrol) |
16 | { |
17 | struct snd_soc_dapm_widget *w; |
18 | |
19 | w = snd_soc_dapm_kcontrol_widget(kcontrol); |
20 | |
21 | return to_avs_dev(w->dapm->component->dev); |
22 | } |
23 | |
24 | static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 id) |
25 | { |
26 | struct avs_path *path; |
27 | struct avs_path_pipeline *ppl; |
28 | struct avs_path_module *mod; |
29 | |
30 | spin_lock(lock: &adev->path_list_lock); |
31 | list_for_each_entry(path, &adev->path_list, node) { |
32 | list_for_each_entry(ppl, &path->ppl_list, node) { |
33 | list_for_each_entry(mod, &ppl->mod_list, node) { |
34 | if (guid_equal(u1: &mod->template->cfg_ext->type, u2: &AVS_PEAKVOL_MOD_UUID) |
35 | && mod->template->ctl_id == id) { |
36 | spin_unlock(lock: &adev->path_list_lock); |
37 | return mod; |
38 | } |
39 | } |
40 | } |
41 | } |
42 | spin_unlock(lock: &adev->path_list_lock); |
43 | |
44 | return NULL; |
45 | } |
46 | |
47 | int avs_control_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
48 | { |
49 | struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; |
50 | struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private; |
51 | struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol); |
52 | struct avs_volume_cfg *dspvols = NULL; |
53 | struct avs_path_module *active_module; |
54 | size_t num_dspvols; |
55 | int ret = 0; |
56 | |
57 | /* prevent access to modules while path is being constructed */ |
58 | mutex_lock(&adev->path_mutex); |
59 | |
60 | active_module = avs_get_volume_module(adev, id: ctl_data->id); |
61 | if (active_module) { |
62 | ret = avs_ipc_peakvol_get_volume(adev, module_id: active_module->module_id, |
63 | instance_id: active_module->instance_id, vols: &dspvols, |
64 | num_vols: &num_dspvols); |
65 | if (!ret) |
66 | ucontrol->value.integer.value[0] = dspvols[0].target_volume; |
67 | |
68 | ret = AVS_IPC_RET(ret); |
69 | kfree(objp: dspvols); |
70 | } else { |
71 | ucontrol->value.integer.value[0] = ctl_data->volume; |
72 | } |
73 | |
74 | mutex_unlock(lock: &adev->path_mutex); |
75 | return ret; |
76 | } |
77 | |
78 | int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
79 | { |
80 | struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; |
81 | struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private; |
82 | struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol); |
83 | long *volume = &ctl_data->volume; |
84 | struct avs_path_module *active_module; |
85 | struct avs_volume_cfg dspvol = {0}; |
86 | long ctlvol = ucontrol->value.integer.value[0]; |
87 | int ret = 0, changed = 0; |
88 | |
89 | if (ctlvol < 0 || ctlvol > mc->max) |
90 | return -EINVAL; |
91 | |
92 | /* prevent access to modules while path is being constructed */ |
93 | mutex_lock(&adev->path_mutex); |
94 | |
95 | if (*volume != ctlvol) { |
96 | *volume = ctlvol; |
97 | changed = 1; |
98 | } |
99 | |
100 | active_module = avs_get_volume_module(adev, id: ctl_data->id); |
101 | if (active_module) { |
102 | dspvol.channel_id = AVS_ALL_CHANNELS_MASK; |
103 | dspvol.target_volume = *volume; |
104 | |
105 | ret = avs_ipc_peakvol_set_volume(adev, module_id: active_module->module_id, |
106 | instance_id: active_module->instance_id, vol: &dspvol); |
107 | ret = AVS_IPC_RET(ret); |
108 | } |
109 | |
110 | mutex_unlock(lock: &adev->path_mutex); |
111 | |
112 | return ret ? ret : changed; |
113 | } |
114 | |