diff options
author | Vinod Koul <vinod.koul@intel.com> | 2014-10-15 10:42:57 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2014-10-20 07:20:33 -0400 |
commit | 24c8d14192cc63661ca049b423d7baaa0bbafeb3 (patch) | |
tree | 026099f57f5b14e079f8d7c772e1b64d2ea40646 | |
parent | 4fa805738e497c6f5bad53fcdc76b9759bc9dc80 (diff) |
ASoC: Intel: mrfld: add DSP core controls
This patch adds core controls like interleavers, SSP BEs, and also
logic of sending pipeline and module commands to the DSP.
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/intel/sst-atom-controls.c | 754 | ||||
-rw-r--r-- | sound/soc/intel/sst-atom-controls.h | 307 |
2 files changed, 1061 insertions, 0 deletions
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index a00d506af179..9239eff26749 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c | |||
@@ -15,6 +15,9 @@ | |||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * General Public License for more details. | 16 | * General Public License for more details. |
17 | * | 17 | * |
18 | * In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active | ||
19 | * we forward the settings and parameters, rest we keep the values in | ||
20 | * driver and forward when DAPM enables them | ||
18 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 21 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
19 | */ | 22 | */ |
20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
@@ -81,6 +84,183 @@ static int sst_fill_and_send_cmd(struct sst_data *drv, | |||
81 | return ret; | 84 | return ret; |
82 | } | 85 | } |
83 | 86 | ||
87 | /** | ||
88 | * tx map value is a bitfield where each bit represents a FW channel | ||
89 | * | ||
90 | * 3 2 1 0 # 0 = codec0, 1 = codec1 | ||
91 | * RLRLRLRL # 3, 4 = reserved | ||
92 | * | ||
93 | * e.g. slot 0 rx map = 00001100b -> data from slot 0 goes into codec_in1 L,R | ||
94 | */ | ||
95 | static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = { | ||
96 | 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */ | ||
97 | }; | ||
98 | |||
99 | /** | ||
100 | * rx map value is a bitfield where each bit represents a slot | ||
101 | * | ||
102 | * 76543210 # 0 = slot 0, 1 = slot 1 | ||
103 | * | ||
104 | * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2 | ||
105 | */ | ||
106 | static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = { | ||
107 | 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */ | ||
108 | }; | ||
109 | |||
110 | /** | ||
111 | * NOTE: this is invoked with lock held | ||
112 | */ | ||
113 | static int sst_send_slot_map(struct sst_data *drv) | ||
114 | { | ||
115 | struct sst_param_sba_ssp_slot_map cmd; | ||
116 | |||
117 | SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); | ||
118 | cmd.header.command_id = SBA_SET_SSP_SLOT_MAP; | ||
119 | cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map) | ||
120 | - sizeof(struct sst_dsp_header); | ||
121 | |||
122 | cmd.param_id = SBA_SET_SSP_SLOT_MAP; | ||
123 | cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map) | ||
124 | + sizeof(cmd.ssp_index); | ||
125 | cmd.ssp_index = SSP_CODEC; | ||
126 | |||
127 | memcpy(cmd.rx_slot_map, &sst_ssp_tx_map[0], sizeof(cmd.rx_slot_map)); | ||
128 | memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map)); | ||
129 | |||
130 | return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS, | ||
131 | SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, | ||
132 | sizeof(cmd.header) + cmd.header.length); | ||
133 | } | ||
134 | |||
135 | int sst_slot_enum_info(struct snd_kcontrol *kcontrol, | ||
136 | struct snd_ctl_elem_info *uinfo) | ||
137 | { | ||
138 | struct sst_enum *e = (struct sst_enum *)kcontrol->private_value; | ||
139 | |||
140 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
141 | uinfo->count = 1; | ||
142 | uinfo->value.enumerated.items = e->max; | ||
143 | |||
144 | if (uinfo->value.enumerated.item > e->max - 1) | ||
145 | uinfo->value.enumerated.item = e->max - 1; | ||
146 | strcpy(uinfo->value.enumerated.name, | ||
147 | e->texts[uinfo->value.enumerated.item]); | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * sst_slot_get - get the status of the interleaver/deinterleaver control | ||
154 | * | ||
155 | * Searches the map where the control status is stored, and gets the | ||
156 | * channel/slot which is currently set for this enumerated control. Since it is | ||
157 | * an enumerated control, there is only one possible value. | ||
158 | */ | ||
159 | static int sst_slot_get(struct snd_kcontrol *kcontrol, | ||
160 | struct snd_ctl_elem_value *ucontrol) | ||
161 | { | ||
162 | struct sst_enum *e = (void *)kcontrol->private_value; | ||
163 | struct snd_soc_component *c = snd_kcontrol_chip(kcontrol); | ||
164 | struct sst_data *drv = snd_soc_component_get_drvdata(c); | ||
165 | unsigned int ctl_no = e->reg; | ||
166 | unsigned int is_tx = e->tx; | ||
167 | unsigned int val, mux; | ||
168 | u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map; | ||
169 | |||
170 | mutex_lock(&drv->lock); | ||
171 | val = 1 << ctl_no; | ||
172 | /* search which slot/channel has this bit set - there should be only one */ | ||
173 | for (mux = e->max; mux > 0; mux--) | ||
174 | if (map[mux - 1] & val) | ||
175 | break; | ||
176 | |||
177 | ucontrol->value.enumerated.item[0] = mux; | ||
178 | mutex_unlock(&drv->lock); | ||
179 | |||
180 | dev_dbg(c->dev, "%s - %s map = %#x\n", | ||
181 | is_tx ? "tx channel" : "rx slot", | ||
182 | e->texts[mux], mux ? map[mux - 1] : -1); | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | /* sst_check_and_send_slot_map - helper for checking power state and sending | ||
187 | * slot map cmd | ||
188 | * | ||
189 | * called with lock held | ||
190 | */ | ||
191 | static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol) | ||
192 | { | ||
193 | struct sst_enum *e = (void *)kcontrol->private_value; | ||
194 | int ret = 0; | ||
195 | |||
196 | if (e->w && e->w->power) | ||
197 | ret = sst_send_slot_map(drv); | ||
198 | else | ||
199 | dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n", | ||
200 | kcontrol->id.name); | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | /** | ||
205 | * sst_slot_put - set the status of interleaver/deinterleaver control | ||
206 | * | ||
207 | * (de)interleaver controls are defined in opposite sense to be user-friendly | ||
208 | * | ||
209 | * Instead of the enum value being the value written to the register, it is the | ||
210 | * register address; and the kcontrol number (register num) is the value written | ||
211 | * to the register. This is so that there can be only one value for each | ||
212 | * slot/channel since there is only one control for each slot/channel. | ||
213 | * | ||
214 | * This means that whenever an enum is set, we need to clear the bit | ||
215 | * for that kcontrol_no for all the interleaver OR deinterleaver registers | ||
216 | */ | ||
217 | static int sst_slot_put(struct snd_kcontrol *kcontrol, | ||
218 | struct snd_ctl_elem_value *ucontrol) | ||
219 | { | ||
220 | struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); | ||
221 | struct sst_data *drv = snd_soc_component_get_drvdata(c); | ||
222 | struct sst_enum *e = (void *)kcontrol->private_value; | ||
223 | int i, ret = 0; | ||
224 | unsigned int ctl_no = e->reg; | ||
225 | unsigned int is_tx = e->tx; | ||
226 | unsigned int slot_channel_no; | ||
227 | unsigned int val, mux; | ||
228 | u8 *map; | ||
229 | |||
230 | map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map; | ||
231 | |||
232 | val = 1 << ctl_no; | ||
233 | mux = ucontrol->value.enumerated.item[0]; | ||
234 | if (mux > e->max - 1) | ||
235 | return -EINVAL; | ||
236 | |||
237 | mutex_lock(&drv->lock); | ||
238 | /* first clear all registers of this bit */ | ||
239 | for (i = 0; i < e->max; i++) | ||
240 | map[i] &= ~val; | ||
241 | |||
242 | if (mux == 0) { | ||
243 | /* kctl set to 'none' and we reset the bits so send IPC */ | ||
244 | ret = sst_check_and_send_slot_map(drv, kcontrol); | ||
245 | |||
246 | mutex_unlock(&drv->lock); | ||
247 | return ret; | ||
248 | } | ||
249 | |||
250 | /* offset by one to take "None" into account */ | ||
251 | slot_channel_no = mux - 1; | ||
252 | map[slot_channel_no] |= val; | ||
253 | |||
254 | dev_dbg(c->dev, "%s %s map = %#x\n", | ||
255 | is_tx ? "tx channel" : "rx slot", | ||
256 | e->texts[mux], map[slot_channel_no]); | ||
257 | |||
258 | ret = sst_check_and_send_slot_map(drv, kcontrol); | ||
259 | |||
260 | mutex_unlock(&drv->lock); | ||
261 | return ret; | ||
262 | } | ||
263 | |||
84 | static int sst_send_algo_cmd(struct sst_data *drv, | 264 | static int sst_send_algo_cmd(struct sst_data *drv, |
85 | struct sst_algo_control *bc) | 265 | struct sst_algo_control *bc) |
86 | { | 266 | { |
@@ -104,6 +284,34 @@ static int sst_send_algo_cmd(struct sst_data *drv, | |||
104 | return ret; | 284 | return ret; |
105 | } | 285 | } |
106 | 286 | ||
287 | /** | ||
288 | * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe | ||
289 | * | ||
290 | * The algos which are in each pipeline are sent to the firmware one by one | ||
291 | * | ||
292 | * Called with lock held | ||
293 | */ | ||
294 | static int sst_find_and_send_pipe_algo(struct sst_data *drv, | ||
295 | const char *pipe, struct sst_ids *ids) | ||
296 | { | ||
297 | int ret = 0; | ||
298 | struct sst_algo_control *bc; | ||
299 | struct sst_module *algo = NULL; | ||
300 | |||
301 | dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe); | ||
302 | |||
303 | list_for_each_entry(algo, &ids->algo_list, node) { | ||
304 | bc = (void *)algo->kctl->private_value; | ||
305 | |||
306 | dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n", | ||
307 | algo->kctl->id.name, pipe); | ||
308 | ret = sst_send_algo_cmd(drv, bc); | ||
309 | if (ret) | ||
310 | return ret; | ||
311 | } | ||
312 | return ret; | ||
313 | } | ||
314 | |||
107 | static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol, | 315 | static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol, |
108 | struct snd_ctl_elem_info *uinfo) | 316 | struct snd_ctl_elem_info *uinfo) |
109 | { | 317 | { |
@@ -296,8 +504,317 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, | |||
296 | return ret; | 504 | return ret; |
297 | } | 505 | } |
298 | 506 | ||
507 | static int sst_set_pipe_gain(struct sst_ids *ids, | ||
508 | struct sst_data *drv, int mute); | ||
509 | |||
510 | static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w, | ||
511 | struct snd_kcontrol *kcontrol) | ||
512 | { | ||
513 | struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); | ||
514 | struct sst_data *drv = snd_soc_component_get_drvdata(c); | ||
515 | struct sst_ids *ids = w->priv; | ||
516 | |||
517 | mutex_lock(&drv->lock); | ||
518 | sst_find_and_send_pipe_algo(drv, w->name, ids); | ||
519 | sst_set_pipe_gain(ids, drv, 0); | ||
520 | mutex_unlock(&drv->lock); | ||
521 | |||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | static int sst_generic_modules_event(struct snd_soc_dapm_widget *w, | ||
526 | struct snd_kcontrol *k, int event) | ||
527 | { | ||
528 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
529 | return sst_send_pipe_module_params(w, k); | ||
530 | return 0; | ||
531 | } | ||
532 | |||
299 | static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0); | 533 | static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0); |
300 | 534 | ||
535 | /* Look up table to convert MIXER SW bit regs to SWM inputs */ | ||
536 | static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = { | ||
537 | [SST_IP_CODEC0] = SST_SWM_IN_CODEC0, | ||
538 | [SST_IP_CODEC1] = SST_SWM_IN_CODEC1, | ||
539 | [SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP, | ||
540 | [SST_IP_LOOP1] = SST_SWM_IN_MEDIA_LOOP1, | ||
541 | [SST_IP_LOOP2] = SST_SWM_IN_MEDIA_LOOP2, | ||
542 | [SST_IP_PCM0] = SST_SWM_IN_PCM0, | ||
543 | [SST_IP_PCM1] = SST_SWM_IN_PCM1, | ||
544 | [SST_IP_MEDIA0] = SST_SWM_IN_MEDIA0, | ||
545 | [SST_IP_MEDIA1] = SST_SWM_IN_MEDIA1, | ||
546 | [SST_IP_MEDIA2] = SST_SWM_IN_MEDIA2, | ||
547 | [SST_IP_MEDIA3] = SST_SWM_IN_MEDIA3, | ||
548 | }; | ||
549 | |||
550 | /** | ||
551 | * called with lock held | ||
552 | */ | ||
553 | static int sst_set_pipe_gain(struct sst_ids *ids, | ||
554 | struct sst_data *drv, int mute) | ||
555 | { | ||
556 | int ret = 0; | ||
557 | struct sst_gain_mixer_control *mc; | ||
558 | struct sst_gain_value *gv; | ||
559 | struct sst_module *gain = NULL; | ||
560 | |||
561 | list_for_each_entry(gain, &ids->gain_list, node) { | ||
562 | struct snd_kcontrol *kctl = gain->kctl; | ||
563 | |||
564 | dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name); | ||
565 | mc = (void *)kctl->private_value; | ||
566 | gv = mc->gain_val; | ||
567 | |||
568 | ret = sst_send_gain_cmd(drv, gv, mc->task_id, | ||
569 | mc->pipe_id | mc->instance_id, mc->module_id, mute); | ||
570 | if (ret) | ||
571 | return ret; | ||
572 | } | ||
573 | return ret; | ||
574 | } | ||
575 | |||
576 | /* | ||
577 | * sst_handle_vb_timer - Start/Stop the DSP scheduler | ||
578 | * | ||
579 | * The DSP expects first cmd to be SBA_VB_START, so at first startup send | ||
580 | * that. | ||
581 | * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that. | ||
582 | * | ||
583 | * Do refcount internally so that we send command only at first start | ||
584 | * and last end. Since SST driver does its own ref count, invoke sst's | ||
585 | * power ops always! | ||
586 | */ | ||
587 | int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) | ||
588 | { | ||
589 | int ret = 0; | ||
590 | struct sst_cmd_generic cmd; | ||
591 | struct sst_data *drv = snd_soc_dai_get_drvdata(dai); | ||
592 | static int timer_usage; | ||
593 | |||
594 | if (enable) | ||
595 | cmd.header.command_id = SBA_VB_START; | ||
596 | else | ||
597 | cmd.header.command_id = SBA_IDLE; | ||
598 | dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage); | ||
599 | |||
600 | SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); | ||
601 | cmd.header.length = 0; | ||
602 | |||
603 | if (enable) { | ||
604 | ret = sst->ops->power(sst->dev, true); | ||
605 | if (ret < 0) | ||
606 | return ret; | ||
607 | } | ||
608 | |||
609 | mutex_lock(&drv->lock); | ||
610 | if (enable) | ||
611 | timer_usage++; | ||
612 | else | ||
613 | timer_usage--; | ||
614 | |||
615 | /* | ||
616 | * Send the command only if this call is the first enable or last | ||
617 | * disable | ||
618 | */ | ||
619 | if ((enable && (timer_usage == 1)) || | ||
620 | (!enable && (timer_usage == 0))) { | ||
621 | ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, | ||
622 | SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, | ||
623 | sizeof(cmd.header) + cmd.header.length); | ||
624 | if (ret && enable) { | ||
625 | timer_usage--; | ||
626 | enable = false; | ||
627 | } | ||
628 | } | ||
629 | mutex_unlock(&drv->lock); | ||
630 | |||
631 | if (!enable) | ||
632 | sst->ops->power(sst->dev, false); | ||
633 | return ret; | ||
634 | } | ||
635 | |||
636 | /** | ||
637 | * sst_ssp_config - contains SSP configuration for media UC | ||
638 | */ | ||
639 | static const struct sst_ssp_config sst_ssp_configs = { | ||
640 | .ssp_id = SSP_CODEC, | ||
641 | .bits_per_slot = 24, | ||
642 | .slots = 4, | ||
643 | .ssp_mode = SSP_MODE_MASTER, | ||
644 | .pcm_mode = SSP_PCM_MODE_NETWORK, | ||
645 | .duplex = SSP_DUPLEX, | ||
646 | .ssp_protocol = SSP_MODE_PCM, | ||
647 | .fs_width = 1, | ||
648 | .fs_frequency = SSP_FS_48_KHZ, | ||
649 | .active_slot_map = 0xF, | ||
650 | .start_delay = 0, | ||
651 | }; | ||
652 | |||
653 | int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable) | ||
654 | { | ||
655 | struct sst_cmd_sba_hw_set_ssp cmd; | ||
656 | struct sst_data *drv = snd_soc_dai_get_drvdata(dai); | ||
657 | const struct sst_ssp_config *config; | ||
658 | |||
659 | dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id); | ||
660 | |||
661 | SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); | ||
662 | cmd.header.command_id = SBA_HW_SET_SSP; | ||
663 | cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) | ||
664 | - sizeof(struct sst_dsp_header); | ||
665 | |||
666 | config = &sst_ssp_configs; | ||
667 | dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id); | ||
668 | |||
669 | if (enable) | ||
670 | cmd.switch_state = SST_SWITCH_ON; | ||
671 | else | ||
672 | cmd.switch_state = SST_SWITCH_OFF; | ||
673 | |||
674 | cmd.selection = config->ssp_id; | ||
675 | cmd.nb_bits_per_slots = config->bits_per_slot; | ||
676 | cmd.nb_slots = config->slots; | ||
677 | cmd.mode = config->ssp_mode | (config->pcm_mode << 1); | ||
678 | cmd.duplex = config->duplex; | ||
679 | cmd.active_tx_slot_map = config->active_slot_map; | ||
680 | cmd.active_rx_slot_map = config->active_slot_map; | ||
681 | cmd.frame_sync_frequency = config->fs_frequency; | ||
682 | cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH; | ||
683 | cmd.data_polarity = 1; | ||
684 | cmd.frame_sync_width = config->fs_width; | ||
685 | cmd.ssp_protocol = config->ssp_protocol; | ||
686 | cmd.start_delay = config->start_delay; | ||
687 | cmd.reserved1 = cmd.reserved2 = 0xFF; | ||
688 | |||
689 | return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, | ||
690 | SST_TASK_SBA, 0, &cmd, | ||
691 | sizeof(cmd.header) + cmd.header.length); | ||
692 | } | ||
693 | |||
694 | static int sst_set_be_modules(struct snd_soc_dapm_widget *w, | ||
695 | struct snd_kcontrol *k, int event) | ||
696 | { | ||
697 | int ret = 0; | ||
698 | struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); | ||
699 | struct sst_data *drv = snd_soc_component_get_drvdata(c); | ||
700 | |||
701 | dev_dbg(c->dev, "Enter: widget=%s\n", w->name); | ||
702 | |||
703 | if (SND_SOC_DAPM_EVENT_ON(event)) { | ||
704 | ret = sst_send_slot_map(drv); | ||
705 | if (ret) | ||
706 | return ret; | ||
707 | ret = sst_send_pipe_module_params(w, k); | ||
708 | } | ||
709 | return ret; | ||
710 | } | ||
711 | |||
712 | static int sst_set_media_path(struct snd_soc_dapm_widget *w, | ||
713 | struct snd_kcontrol *k, int event) | ||
714 | { | ||
715 | int ret = 0; | ||
716 | struct sst_cmd_set_media_path cmd; | ||
717 | struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); | ||
718 | struct sst_data *drv = snd_soc_component_get_drvdata(c); | ||
719 | struct sst_ids *ids = w->priv; | ||
720 | |||
721 | dev_dbg(c->dev, "widget=%s\n", w->name); | ||
722 | dev_dbg(c->dev, "task=%u, location=%#x\n", | ||
723 | ids->task_id, ids->location_id); | ||
724 | |||
725 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
726 | cmd.switch_state = SST_PATH_ON; | ||
727 | else | ||
728 | cmd.switch_state = SST_PATH_OFF; | ||
729 | |||
730 | SST_FILL_DESTINATION(2, cmd.header.dst, | ||
731 | ids->location_id, SST_DEFAULT_MODULE_ID); | ||
732 | |||
733 | /* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */ | ||
734 | cmd.header.command_id = MMX_SET_MEDIA_PATH; | ||
735 | cmd.header.length = sizeof(struct sst_cmd_set_media_path) | ||
736 | - sizeof(struct sst_dsp_header); | ||
737 | |||
738 | ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, | ||
739 | ids->task_id, 0, &cmd, | ||
740 | sizeof(cmd.header) + cmd.header.length); | ||
741 | if (ret) | ||
742 | return ret; | ||
743 | |||
744 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
745 | ret = sst_send_pipe_module_params(w, k); | ||
746 | return ret; | ||
747 | } | ||
748 | |||
749 | static int sst_set_media_loop(struct snd_soc_dapm_widget *w, | ||
750 | struct snd_kcontrol *k, int event) | ||
751 | { | ||
752 | int ret = 0; | ||
753 | struct sst_cmd_sba_set_media_loop_map cmd; | ||
754 | struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); | ||
755 | struct sst_data *drv = snd_soc_component_get_drvdata(c); | ||
756 | struct sst_ids *ids = w->priv; | ||
757 | |||
758 | dev_dbg(c->dev, "Enter:widget=%s\n", w->name); | ||
759 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
760 | cmd.switch_state = SST_SWITCH_ON; | ||
761 | else | ||
762 | cmd.switch_state = SST_SWITCH_OFF; | ||
763 | |||
764 | SST_FILL_DESTINATION(2, cmd.header.dst, | ||
765 | ids->location_id, SST_DEFAULT_MODULE_ID); | ||
766 | |||
767 | cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP; | ||
768 | cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map) | ||
769 | - sizeof(struct sst_dsp_header); | ||
770 | cmd.param.part.cfg.rate = 2; /* 48khz */ | ||
771 | |||
772 | cmd.param.part.cfg.format = ids->format; /* stereo/Mono */ | ||
773 | cmd.param.part.cfg.s_length = 1; /* 24bit left justified */ | ||
774 | cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */ | ||
775 | |||
776 | ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, | ||
777 | SST_TASK_SBA, 0, &cmd, | ||
778 | sizeof(cmd.header) + cmd.header.length); | ||
779 | if (ret) | ||
780 | return ret; | ||
781 | |||
782 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
783 | ret = sst_send_pipe_module_params(w, k); | ||
784 | return ret; | ||
785 | } | ||
786 | |||
787 | static const char * const slot_names[] = { | ||
788 | "none", | ||
789 | "slot 0", "slot 1", "slot 2", "slot 3", | ||
790 | "slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */ | ||
791 | }; | ||
792 | |||
793 | static const char * const channel_names[] = { | ||
794 | "none", | ||
795 | "codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1", | ||
796 | "codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */ | ||
797 | }; | ||
798 | |||
799 | #define SST_INTERLEAVER(xpname, slot_name, slotno) \ | ||
800 | SST_SSP_SLOT_CTL(xpname, "tx interleaver", slot_name, slotno, true, \ | ||
801 | channel_names, sst_slot_get, sst_slot_put) | ||
802 | |||
803 | #define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \ | ||
804 | SST_SSP_SLOT_CTL(xpname, "rx deinterleaver", channel_name, channel_no, false, \ | ||
805 | slot_names, sst_slot_get, sst_slot_put) | ||
806 | |||
807 | static const struct snd_kcontrol_new sst_slot_controls[] = { | ||
808 | SST_INTERLEAVER("codec_out", "slot 0", 0), | ||
809 | SST_INTERLEAVER("codec_out", "slot 1", 1), | ||
810 | SST_INTERLEAVER("codec_out", "slot 2", 2), | ||
811 | SST_INTERLEAVER("codec_out", "slot 3", 3), | ||
812 | SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0), | ||
813 | SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1), | ||
814 | SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2), | ||
815 | SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3), | ||
816 | }; | ||
817 | |||
301 | /* Gain helper with min/max set */ | 818 | /* Gain helper with min/max set */ |
302 | #define SST_GAIN(name, path_id, task_id, instance, gain_var) \ | 819 | #define SST_GAIN(name, path_id, task_id, instance, gain_var) \ |
303 | SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ | 820 | SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ |
@@ -382,6 +899,234 @@ static int sst_algo_control_init(struct device *dev) | |||
382 | return 0; | 899 | return 0; |
383 | } | 900 | } |
384 | 901 | ||
902 | static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w) | ||
903 | { | ||
904 | switch (w->id) { | ||
905 | case snd_soc_dapm_pga: | ||
906 | case snd_soc_dapm_aif_in: | ||
907 | case snd_soc_dapm_aif_out: | ||
908 | case snd_soc_dapm_input: | ||
909 | case snd_soc_dapm_output: | ||
910 | case snd_soc_dapm_mixer: | ||
911 | return true; | ||
912 | default: | ||
913 | return false; | ||
914 | } | ||
915 | } | ||
916 | |||
917 | /** | ||
918 | * sst_send_pipe_gains - send gains for the front-end DAIs | ||
919 | * | ||
920 | * The gains in the pipes connected to the front-ends are muted/unmuted | ||
921 | * automatically via the digital_mute() DAPM callback. This function sends the | ||
922 | * gains for the front-end pipes. | ||
923 | */ | ||
924 | int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) | ||
925 | { | ||
926 | struct sst_data *drv = snd_soc_dai_get_drvdata(dai); | ||
927 | struct snd_soc_dapm_widget *w; | ||
928 | struct snd_soc_dapm_path *p = NULL; | ||
929 | |||
930 | dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream); | ||
931 | |||
932 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
933 | dev_dbg(dai->dev, "Stream name=%s\n", | ||
934 | dai->playback_widget->name); | ||
935 | w = dai->playback_widget; | ||
936 | list_for_each_entry(p, &w->sinks, list_source) { | ||
937 | if (p->connected && !p->connected(w, p->sink)) | ||
938 | continue; | ||
939 | |||
940 | if (p->connect && p->sink->power && | ||
941 | is_sst_dapm_widget(p->sink)) { | ||
942 | struct sst_ids *ids = p->sink->priv; | ||
943 | |||
944 | dev_dbg(dai->dev, "send gains for widget=%s\n", | ||
945 | p->sink->name); | ||
946 | mutex_lock(&drv->lock); | ||
947 | sst_set_pipe_gain(ids, drv, mute); | ||
948 | mutex_unlock(&drv->lock); | ||
949 | } | ||
950 | } | ||
951 | } else { | ||
952 | dev_dbg(dai->dev, "Stream name=%s\n", | ||
953 | dai->capture_widget->name); | ||
954 | w = dai->capture_widget; | ||
955 | list_for_each_entry(p, &w->sources, list_sink) { | ||
956 | if (p->connected && !p->connected(w, p->sink)) | ||
957 | continue; | ||
958 | |||
959 | if (p->connect && p->source->power && | ||
960 | is_sst_dapm_widget(p->source)) { | ||
961 | struct sst_ids *ids = p->source->priv; | ||
962 | |||
963 | dev_dbg(dai->dev, "send gain for widget=%s\n", | ||
964 | p->source->name); | ||
965 | mutex_lock(&drv->lock); | ||
966 | sst_set_pipe_gain(ids, drv, mute); | ||
967 | mutex_unlock(&drv->lock); | ||
968 | } | ||
969 | } | ||
970 | } | ||
971 | return 0; | ||
972 | } | ||
973 | |||
974 | /** | ||
975 | * sst_fill_module_list - populate the list of modules/gains for a pipe | ||
976 | * | ||
977 | * | ||
978 | * Fills the widget pointer in the kcontrol private data, and also fills the | ||
979 | * kcontrol pointer in the widget private data. | ||
980 | * | ||
981 | * Widget pointer is used to send the algo/gain in the .put() handler if the | ||
982 | * widget is powerd on. | ||
983 | * | ||
984 | * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF | ||
985 | * event handler. Each widget (pipe) has multiple algos stored in the algo_list. | ||
986 | */ | ||
987 | static int sst_fill_module_list(struct snd_kcontrol *kctl, | ||
988 | struct snd_soc_dapm_widget *w, int type) | ||
989 | { | ||
990 | struct sst_module *module = NULL; | ||
991 | struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); | ||
992 | struct sst_ids *ids = w->priv; | ||
993 | int ret = 0; | ||
994 | |||
995 | module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL); | ||
996 | if (!module) | ||
997 | return -ENOMEM; | ||
998 | |||
999 | if (type == SST_MODULE_GAIN) { | ||
1000 | struct sst_gain_mixer_control *mc = (void *)kctl->private_value; | ||
1001 | |||
1002 | mc->w = w; | ||
1003 | module->kctl = kctl; | ||
1004 | list_add_tail(&module->node, &ids->gain_list); | ||
1005 | } else if (type == SST_MODULE_ALGO) { | ||
1006 | struct sst_algo_control *bc = (void *)kctl->private_value; | ||
1007 | |||
1008 | bc->w = w; | ||
1009 | module->kctl = kctl; | ||
1010 | list_add_tail(&module->node, &ids->algo_list); | ||
1011 | } else { | ||
1012 | dev_err(c->dev, "invoked for unknown type %d module %s", | ||
1013 | type, kctl->id.name); | ||
1014 | ret = -EINVAL; | ||
1015 | } | ||
1016 | |||
1017 | return ret; | ||
1018 | } | ||
1019 | |||
1020 | /** | ||
1021 | * sst_fill_widget_module_info - fill list of gains/algos for the pipe | ||
1022 | * @widget: pipe modelled as a DAPM widget | ||
1023 | * | ||
1024 | * Fill the list of gains/algos for the widget by looking at all the card | ||
1025 | * controls and comparing the name of the widget with the first part of control | ||
1026 | * name. First part of control name contains the pipe name (widget name). | ||
1027 | */ | ||
1028 | static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w, | ||
1029 | struct snd_soc_platform *platform) | ||
1030 | { | ||
1031 | struct snd_kcontrol *kctl; | ||
1032 | int index, ret = 0; | ||
1033 | struct snd_card *card = platform->component.card->snd_card; | ||
1034 | char *idx; | ||
1035 | |||
1036 | down_read(&card->controls_rwsem); | ||
1037 | |||
1038 | list_for_each_entry(kctl, &card->controls, list) { | ||
1039 | idx = strstr(kctl->id.name, " "); | ||
1040 | if (idx == NULL) | ||
1041 | continue; | ||
1042 | index = strlen(kctl->id.name) - strlen(idx); | ||
1043 | |||
1044 | if (strstr(kctl->id.name, "Volume") && | ||
1045 | !strncmp(kctl->id.name, w->name, index)) | ||
1046 | ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN); | ||
1047 | |||
1048 | else if (strstr(kctl->id.name, "params") && | ||
1049 | !strncmp(kctl->id.name, w->name, index)) | ||
1050 | ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO); | ||
1051 | |||
1052 | else if (strstr(kctl->id.name, "Switch") && | ||
1053 | !strncmp(kctl->id.name, w->name, index) && | ||
1054 | strstr(kctl->id.name, "Gain")) { | ||
1055 | struct sst_gain_mixer_control *mc = | ||
1056 | (void *)kctl->private_value; | ||
1057 | |||
1058 | mc->w = w; | ||
1059 | |||
1060 | } else if (strstr(kctl->id.name, "interleaver") && | ||
1061 | !strncmp(kctl->id.name, w->name, index)) { | ||
1062 | struct sst_enum *e = (void *)kctl->private_value; | ||
1063 | |||
1064 | e->w = w; | ||
1065 | |||
1066 | } else if (strstr(kctl->id.name, "deinterleaver") && | ||
1067 | !strncmp(kctl->id.name, w->name, index)) { | ||
1068 | |||
1069 | struct sst_enum *e = (void *)kctl->private_value; | ||
1070 | |||
1071 | e->w = w; | ||
1072 | } | ||
1073 | |||
1074 | if (ret < 0) { | ||
1075 | up_read(&card->controls_rwsem); | ||
1076 | return ret; | ||
1077 | } | ||
1078 | } | ||
1079 | |||
1080 | up_read(&card->controls_rwsem); | ||
1081 | return 0; | ||
1082 | } | ||
1083 | |||
1084 | /** | ||
1085 | * sst_fill_linked_widgets - fill the parent pointer for the linked widget | ||
1086 | */ | ||
1087 | static void sst_fill_linked_widgets(struct snd_soc_platform *platform, | ||
1088 | struct sst_ids *ids) | ||
1089 | { | ||
1090 | struct snd_soc_dapm_widget *w; | ||
1091 | unsigned int len = strlen(ids->parent_wname); | ||
1092 | |||
1093 | list_for_each_entry(w, &platform->component.card->widgets, list) { | ||
1094 | if (!strncmp(ids->parent_wname, w->name, len)) { | ||
1095 | ids->parent_w = w; | ||
1096 | break; | ||
1097 | } | ||
1098 | } | ||
1099 | } | ||
1100 | |||
1101 | /** | ||
1102 | * sst_map_modules_to_pipe - fill algo/gains list for all pipes | ||
1103 | */ | ||
1104 | static int sst_map_modules_to_pipe(struct snd_soc_platform *platform) | ||
1105 | { | ||
1106 | struct snd_soc_dapm_widget *w; | ||
1107 | int ret = 0; | ||
1108 | |||
1109 | list_for_each_entry(w, &platform->component.card->widgets, list) { | ||
1110 | if (platform && is_sst_dapm_widget(w) && (w->priv)) { | ||
1111 | struct sst_ids *ids = w->priv; | ||
1112 | |||
1113 | dev_dbg(platform->dev, "widget type=%d name=%s\n", | ||
1114 | w->id, w->name); | ||
1115 | INIT_LIST_HEAD(&ids->algo_list); | ||
1116 | INIT_LIST_HEAD(&ids->gain_list); | ||
1117 | ret = sst_fill_widget_module_info(w, platform); | ||
1118 | |||
1119 | if (ret < 0) | ||
1120 | return ret; | ||
1121 | |||
1122 | /* fill linked widgets */ | ||
1123 | if (ids->parent_wname != NULL) | ||
1124 | sst_fill_linked_widgets(platform, ids); | ||
1125 | } | ||
1126 | } | ||
1127 | return 0; | ||
1128 | } | ||
1129 | |||
385 | int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) | 1130 | int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) |
386 | { | 1131 | { |
387 | int i, ret = 0; | 1132 | int i, ret = 0; |
@@ -411,6 +1156,15 @@ int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) | |||
411 | return ret; | 1156 | return ret; |
412 | ret = snd_soc_add_platform_controls(platform, sst_algo_controls, | 1157 | ret = snd_soc_add_platform_controls(platform, sst_algo_controls, |
413 | ARRAY_SIZE(sst_algo_controls)); | 1158 | ARRAY_SIZE(sst_algo_controls)); |
1159 | if (ret) | ||
1160 | return ret; | ||
1161 | |||
1162 | ret = snd_soc_add_platform_controls(platform, sst_slot_controls, | ||
1163 | ARRAY_SIZE(sst_slot_controls)); | ||
1164 | if (ret) | ||
1165 | return ret; | ||
1166 | |||
1167 | ret = sst_map_modules_to_pipe(platform); | ||
414 | 1168 | ||
415 | return ret; | 1169 | return ret; |
416 | } | 1170 | } |
diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h index e530002cd763..dfebfdd5eb2a 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/sst-atom-controls.h | |||
@@ -364,6 +364,38 @@ struct sst_cmd_generic { | |||
364 | struct sst_dsp_header header; | 364 | struct sst_dsp_header header; |
365 | } __packed; | 365 | } __packed; |
366 | 366 | ||
367 | struct swm_input_ids { | ||
368 | struct sst_destination_id input_id; | ||
369 | } __packed; | ||
370 | |||
371 | struct sst_cmd_set_swm { | ||
372 | struct sst_dsp_header header; | ||
373 | struct sst_destination_id output_id; | ||
374 | u16 switch_state; | ||
375 | u16 nb_inputs; | ||
376 | struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS]; | ||
377 | } __packed; | ||
378 | |||
379 | struct sst_cmd_set_media_path { | ||
380 | struct sst_dsp_header header; | ||
381 | u16 switch_state; | ||
382 | } __packed; | ||
383 | |||
384 | struct pcm_cfg { | ||
385 | u8 s_length:2; | ||
386 | u8 rate:3; | ||
387 | u8 format:3; | ||
388 | } __packed; | ||
389 | |||
390 | struct sst_cmd_set_speech_path { | ||
391 | struct sst_dsp_header header; | ||
392 | u16 switch_state; | ||
393 | struct { | ||
394 | u16 rsvd:8; | ||
395 | struct pcm_cfg cfg; | ||
396 | } config; | ||
397 | } __packed; | ||
398 | |||
367 | struct gain_cell { | 399 | struct gain_cell { |
368 | struct sst_destination_id dest; | 400 | struct sst_destination_id dest; |
369 | s16 cell_gain_left; | 401 | s16 cell_gain_left; |
@@ -383,8 +415,162 @@ struct sst_cmd_set_params { | |||
383 | char params[0]; | 415 | char params[0]; |
384 | } __packed; | 416 | } __packed; |
385 | 417 | ||
418 | |||
419 | struct sst_cmd_sba_vb_start { | ||
420 | struct sst_dsp_header header; | ||
421 | } __packed; | ||
422 | |||
423 | union sba_media_loop_params { | ||
424 | struct { | ||
425 | u16 rsvd:8; | ||
426 | struct pcm_cfg cfg; | ||
427 | } part; | ||
428 | u16 full; | ||
429 | } __packed; | ||
430 | |||
431 | struct sst_cmd_sba_set_media_loop_map { | ||
432 | struct sst_dsp_header header; | ||
433 | u16 switch_state; | ||
434 | union sba_media_loop_params param; | ||
435 | u16 map; | ||
436 | } __packed; | ||
437 | |||
438 | struct sst_cmd_tone_stop { | ||
439 | struct sst_dsp_header header; | ||
440 | u16 switch_state; | ||
441 | } __packed; | ||
442 | |||
443 | enum sst_ssp_mode { | ||
444 | SSP_MODE_MASTER = 0, | ||
445 | SSP_MODE_SLAVE = 1, | ||
446 | }; | ||
447 | |||
448 | enum sst_ssp_pcm_mode { | ||
449 | SSP_PCM_MODE_NORMAL = 0, | ||
450 | SSP_PCM_MODE_NETWORK = 1, | ||
451 | }; | ||
452 | |||
453 | enum sst_ssp_duplex { | ||
454 | SSP_DUPLEX = 0, | ||
455 | SSP_RX = 1, | ||
456 | SSP_TX = 2, | ||
457 | }; | ||
458 | |||
459 | enum sst_ssp_fs_frequency { | ||
460 | SSP_FS_8_KHZ = 0, | ||
461 | SSP_FS_16_KHZ = 1, | ||
462 | SSP_FS_44_1_KHZ = 2, | ||
463 | SSP_FS_48_KHZ = 3, | ||
464 | }; | ||
465 | |||
466 | enum sst_ssp_fs_polarity { | ||
467 | SSP_FS_ACTIVE_LOW = 0, | ||
468 | SSP_FS_ACTIVE_HIGH = 1, | ||
469 | }; | ||
470 | |||
471 | enum sst_ssp_protocol { | ||
472 | SSP_MODE_PCM = 0, | ||
473 | SSP_MODE_I2S = 1, | ||
474 | }; | ||
475 | |||
476 | enum sst_ssp_port_id { | ||
477 | SSP_MODEM = 0, | ||
478 | SSP_BT = 1, | ||
479 | SSP_FM = 2, | ||
480 | SSP_CODEC = 3, | ||
481 | }; | ||
482 | |||
483 | struct sst_cmd_sba_hw_set_ssp { | ||
484 | struct sst_dsp_header header; | ||
485 | u16 selection; /* 0:SSP0(def), 1:SSP1, 2:SSP2 */ | ||
486 | |||
487 | u16 switch_state; | ||
488 | |||
489 | u16 nb_bits_per_slots:6; /* 0-32 bits, 24 (def) */ | ||
490 | u16 nb_slots:4; /* 0-8: slots per frame */ | ||
491 | u16 mode:3; /* 0:Master, 1: Slave */ | ||
492 | u16 duplex:3; | ||
493 | |||
494 | u16 active_tx_slot_map:8; /* Bit map, 0:off, 1:on */ | ||
495 | u16 reserved1:8; | ||
496 | |||
497 | u16 active_rx_slot_map:8; /* Bit map 0: Off, 1:On */ | ||
498 | u16 reserved2:8; | ||
499 | |||
500 | u16 frame_sync_frequency; | ||
501 | |||
502 | u16 frame_sync_polarity:8; | ||
503 | u16 data_polarity:8; | ||
504 | |||
505 | u16 frame_sync_width; /* 1 to N clocks */ | ||
506 | u16 ssp_protocol:8; | ||
507 | u16 start_delay:8; /* Start delay in terms of clock ticks */ | ||
508 | } __packed; | ||
509 | |||
510 | #define SST_MAX_TDM_SLOTS 8 | ||
511 | |||
512 | struct sst_param_sba_ssp_slot_map { | ||
513 | struct sst_dsp_header header; | ||
514 | |||
515 | u16 param_id; | ||
516 | u16 param_len; | ||
517 | u16 ssp_index; | ||
518 | |||
519 | u8 rx_slot_map[SST_MAX_TDM_SLOTS]; | ||
520 | u8 tx_slot_map[SST_MAX_TDM_SLOTS]; | ||
521 | } __packed; | ||
522 | |||
523 | enum { | ||
524 | SST_PROBE_EXTRACTOR = 0, | ||
525 | SST_PROBE_INJECTOR = 1, | ||
526 | }; | ||
527 | |||
386 | /**** widget defines *****/ | 528 | /**** widget defines *****/ |
387 | 529 | ||
530 | #define SST_MODULE_GAIN 1 | ||
531 | #define SST_MODULE_ALGO 2 | ||
532 | |||
533 | #define SST_FMT_MONO 0 | ||
534 | #define SST_FMT_STEREO 3 | ||
535 | |||
536 | /* physical SSP numbers */ | ||
537 | enum { | ||
538 | SST_SSP0 = 0, | ||
539 | SST_SSP1, | ||
540 | SST_SSP2, | ||
541 | SST_SSP_LAST = SST_SSP2, | ||
542 | }; | ||
543 | |||
544 | #define SST_NUM_SSPS (SST_SSP_LAST + 1) /* physical SSPs */ | ||
545 | #define SST_MAX_SSP_MUX 2 /* single SSP muxed between pipes */ | ||
546 | #define SST_MAX_SSP_DOMAINS 2 /* domains present in each pipe */ | ||
547 | |||
548 | struct sst_module { | ||
549 | struct snd_kcontrol *kctl; | ||
550 | struct list_head node; | ||
551 | }; | ||
552 | |||
553 | struct sst_ssp_config { | ||
554 | u8 ssp_id; | ||
555 | u8 bits_per_slot; | ||
556 | u8 slots; | ||
557 | u8 ssp_mode; | ||
558 | u8 pcm_mode; | ||
559 | u8 duplex; | ||
560 | u8 ssp_protocol; | ||
561 | u8 fs_frequency; | ||
562 | u8 active_slot_map; | ||
563 | u8 start_delay; | ||
564 | u16 fs_width; | ||
565 | }; | ||
566 | |||
567 | struct sst_ssp_cfg { | ||
568 | const u8 ssp_number; | ||
569 | const int *mux_shift; | ||
570 | const int (*domain_shift)[SST_MAX_SSP_MUX]; | ||
571 | const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS]; | ||
572 | }; | ||
573 | |||
388 | struct sst_ids { | 574 | struct sst_ids { |
389 | u16 location_id; | 575 | u16 location_id; |
390 | u16 module_id; | 576 | u16 module_id; |
@@ -397,6 +583,102 @@ struct sst_ids { | |||
397 | struct list_head gain_list; | 583 | struct list_head gain_list; |
398 | const struct sst_pcm_format *pcm_fmt; | 584 | const struct sst_pcm_format *pcm_fmt; |
399 | }; | 585 | }; |
586 | |||
587 | |||
588 | #define SST_AIF_IN(wname, wevent) \ | ||
589 | { .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL, \ | ||
590 | .reg = SND_SOC_NOPM, .shift = 0, \ | ||
591 | .on_val = 1, .off_val = 0, \ | ||
592 | .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ | ||
593 | .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ | ||
594 | } | ||
595 | |||
596 | #define SST_AIF_OUT(wname, wevent) \ | ||
597 | { .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL, \ | ||
598 | .reg = SND_SOC_NOPM, .shift = 0, \ | ||
599 | .on_val = 1, .off_val = 0, \ | ||
600 | .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ | ||
601 | .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ | ||
602 | } | ||
603 | |||
604 | #define SST_INPUT(wname, wevent) \ | ||
605 | { .id = snd_soc_dapm_input, .name = wname, .sname = NULL, \ | ||
606 | .reg = SND_SOC_NOPM, .shift = 0, \ | ||
607 | .on_val = 1, .off_val = 0, \ | ||
608 | .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ | ||
609 | .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ | ||
610 | } | ||
611 | |||
612 | #define SST_OUTPUT(wname, wevent) \ | ||
613 | { .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \ | ||
614 | .reg = SND_SOC_NOPM, .shift = 0, \ | ||
615 | .on_val = 1, .off_val = 0, \ | ||
616 | .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ | ||
617 | .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \ | ||
618 | } | ||
619 | |||
620 | #define SST_DAPM_OUTPUT(wname, wloc_id, wtask_id, wformat, wevent) \ | ||
621 | { .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \ | ||
622 | .reg = SND_SOC_NOPM, .shift = 0, \ | ||
623 | .on_val = 1, .off_val = 0, \ | ||
624 | .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ | ||
625 | .priv = (void *)&(struct sst_ids) { .location_id = wloc_id, .task_id = wtask_id,\ | ||
626 | .pcm_fmt = wformat, } \ | ||
627 | } | ||
628 | |||
629 | #define SST_PATH(wname, wtask, wloc_id, wevent, wflags) \ | ||
630 | { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ | ||
631 | .kcontrol_news = NULL, .num_kcontrols = 0, \ | ||
632 | .on_val = 1, .off_val = 0, \ | ||
633 | .event = wevent, .event_flags = wflags, \ | ||
634 | .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \ | ||
635 | } | ||
636 | |||
637 | #define SST_LINKED_PATH(wname, wtask, wloc_id, linked_wname, wevent, wflags) \ | ||
638 | { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ | ||
639 | .kcontrol_news = NULL, .num_kcontrols = 0, \ | ||
640 | .on_val = 1, .off_val = 0, \ | ||
641 | .event = wevent, .event_flags = wflags, \ | ||
642 | .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ | ||
643 | .parent_wname = linked_wname} \ | ||
644 | } | ||
645 | |||
646 | #define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags) \ | ||
647 | { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ | ||
648 | .kcontrol_news = NULL, .num_kcontrols = 0, \ | ||
649 | .event = wevent, .event_flags = wflags, \ | ||
650 | .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ | ||
651 | .format = wformat,} \ | ||
652 | } | ||
653 | |||
654 | /* output is triggered before input */ | ||
655 | #define SST_PATH_INPUT(name, task_id, loc_id, event) \ | ||
656 | SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) | ||
657 | |||
658 | #define SST_PATH_LINKED_INPUT(name, task_id, loc_id, linked_wname, event) \ | ||
659 | SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \ | ||
660 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) | ||
661 | |||
662 | #define SST_PATH_OUTPUT(name, task_id, loc_id, event) \ | ||
663 | SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) | ||
664 | |||
665 | #define SST_PATH_LINKED_OUTPUT(name, task_id, loc_id, linked_wname, event) \ | ||
666 | SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \ | ||
667 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) | ||
668 | |||
669 | #define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event) \ | ||
670 | SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) | ||
671 | |||
672 | |||
673 | #define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent) \ | ||
674 | { .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ | ||
675 | .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\ | ||
676 | .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD | \ | ||
677 | SND_SOC_DAPM_POST_REG, \ | ||
678 | .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ | ||
679 | .reg = wreg } \ | ||
680 | } | ||
681 | |||
400 | enum sst_gain_kcontrol_type { | 682 | enum sst_gain_kcontrol_type { |
401 | SST_GAIN_TLV, | 683 | SST_GAIN_TLV, |
402 | SST_GAIN_MUTE, | 684 | SST_GAIN_MUTE, |
@@ -560,4 +842,29 @@ struct sst_enum { | |||
560 | struct snd_soc_dapm_widget *w; | 842 | struct snd_soc_dapm_widget *w; |
561 | }; | 843 | }; |
562 | 844 | ||
845 | /* only 4 slots/channels supported atm */ | ||
846 | #define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \ | ||
847 | (struct sst_enum){ .reg = s_ch_no, .tx = is_tx, .max = 4+1, .texts = xtexts, } | ||
848 | |||
849 | #define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \ | ||
850 | xpname " " xmname " " s_ch_name | ||
851 | |||
852 | #define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \ | ||
853 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | ||
854 | .name = SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \ | ||
855 | .info = sst_slot_enum_info, \ | ||
856 | .get = xget, .put = xput, \ | ||
857 | .private_value = (unsigned long)&SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \ | ||
858 | } | ||
859 | |||
860 | #define SST_MUX_CTL_NAME(xpname, xinstance) \ | ||
861 | xpname " " #xinstance | ||
862 | |||
863 | #define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \ | ||
864 | (struct soc_enum) SOC_ENUM_DOUBLE(xreg, xshift, xshift, ARRAY_SIZE(xtexts), xtexts) | ||
865 | |||
866 | #define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts) \ | ||
867 | SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \ | ||
868 | SST_SSP_MUX_ENUM(xreg, xshift, xtexts)) | ||
869 | |||
563 | #endif | 870 | #endif |