aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVinod Koul <vinod.koul@intel.com>2014-10-15 10:42:56 -0400
committerMark Brown <broonie@kernel.org>2014-10-20 07:20:32 -0400
commit4fa805738e497c6f5bad53fcdc76b9759bc9dc80 (patch)
tree4176fa874eafaac9112c72c3bbf1d2361d106324
parentf114040e3ea6e07372334ade75d1ee0775c355e1 (diff)
ASoC: Intel: mrfld: add the gain controls
The DSP has various gain modules in the path, add these as ALSA gain controls 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/Kconfig2
-rw-r--r--sound/soc/intel/sst-atom-controls.c202
-rw-r--r--sound/soc/intel/sst-atom-controls.h121
3 files changed, 322 insertions, 3 deletions
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index f5b4a9c79cdf..726f7d891a7a 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -1,6 +1,6 @@
1config SND_MFLD_MACHINE 1config SND_MFLD_MACHINE
2 tristate "SOC Machine Audio driver for Intel Medfield MID platform" 2 tristate "SOC Machine Audio driver for Intel Medfield MID platform"
3 depends on INTEL_SCU_IPC 3 depends on INTEL_SCU_IPC || COMPILE_TEST
4 select SND_SOC_SN95031 4 select SND_SOC_SN95031
5 select SND_SST_MFLD_PLATFORM 5 select SND_SST_MFLD_PLATFORM
6 help 6 help
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c
index 7104a34181a9..a00d506af179 100644
--- a/sound/soc/intel/sst-atom-controls.c
+++ b/sound/soc/intel/sst-atom-controls.c
@@ -162,6 +162,190 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
162 return ret; 162 return ret;
163} 163}
164 164
165static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol,
166 struct snd_ctl_elem_info *uinfo)
167{
168 struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
169
170 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
171 uinfo->count = mc->stereo ? 2 : 1;
172 uinfo->value.integer.min = mc->min;
173 uinfo->value.integer.max = mc->max;
174
175 return 0;
176}
177
178/**
179 * sst_send_gain_cmd - send the gain algorithm IPC to the FW
180 * @gv: the stored value of gain (also contains rampduration)
181 * @mute: flag that indicates whether this was called from the
182 * digital_mute callback or directly. If called from the
183 * digital_mute callback, module will be muted/unmuted based on this
184 * flag. The flag is always 0 if called directly.
185 *
186 * Called with sst_data.lock held
187 *
188 * The user-set gain value is sent only if the user-controllable 'mute' control
189 * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is
190 * sent.
191 */
192static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv,
193 u16 task_id, u16 loc_id, u16 module_id, int mute)
194{
195 struct sst_cmd_set_gain_dual cmd;
196
197 dev_dbg(&drv->pdev->dev, "Enter\n");
198
199 cmd.header.command_id = MMX_SET_GAIN;
200 SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
201 cmd.gain_cell_num = 1;
202
203 if (mute || gv->mute) {
204 cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE;
205 cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE;
206 } else {
207 cmd.cell_gains[0].cell_gain_left = gv->l_gain;
208 cmd.cell_gains[0].cell_gain_right = gv->r_gain;
209 }
210
211 SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest,
212 loc_id, module_id);
213 cmd.cell_gains[0].gain_time_constant = gv->ramp_duration;
214
215 cmd.header.length = sizeof(struct sst_cmd_set_gain_dual)
216 - sizeof(struct sst_dsp_header);
217
218 /* we are with lock held, so call the unlocked api to send */
219 return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
220 SST_FLAG_BLOCKED, task_id, 0, &cmd,
221 sizeof(cmd.header) + cmd.header.length);
222}
223
224static int sst_gain_get(struct snd_kcontrol *kcontrol,
225 struct snd_ctl_elem_value *ucontrol)
226{
227 struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
228 struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
229 struct sst_gain_value *gv = mc->gain_val;
230
231 switch (mc->type) {
232 case SST_GAIN_TLV:
233 ucontrol->value.integer.value[0] = gv->l_gain;
234 ucontrol->value.integer.value[1] = gv->r_gain;
235 break;
236
237 case SST_GAIN_MUTE:
238 ucontrol->value.integer.value[0] = gv->mute ? 1 : 0;
239 break;
240
241 case SST_GAIN_RAMP_DURATION:
242 ucontrol->value.integer.value[0] = gv->ramp_duration;
243 break;
244
245 default:
246 dev_err(component->dev, "Invalid Input- gain type:%d\n",
247 mc->type);
248 return -EINVAL;
249 };
250
251 return 0;
252}
253
254static int sst_gain_put(struct snd_kcontrol *kcontrol,
255 struct snd_ctl_elem_value *ucontrol)
256{
257 int ret = 0;
258 struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
259 struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
260 struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
261 struct sst_gain_value *gv = mc->gain_val;
262
263 mutex_lock(&drv->lock);
264
265 switch (mc->type) {
266 case SST_GAIN_TLV:
267 gv->l_gain = ucontrol->value.integer.value[0];
268 gv->r_gain = ucontrol->value.integer.value[1];
269 dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n",
270 mc->pname, gv->l_gain, gv->r_gain);
271 break;
272
273 case SST_GAIN_MUTE:
274 gv->mute = !!ucontrol->value.integer.value[0];
275 dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);
276 break;
277
278 case SST_GAIN_RAMP_DURATION:
279 gv->ramp_duration = ucontrol->value.integer.value[0];
280 dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n",
281 mc->pname, gv->ramp_duration);
282 break;
283
284 default:
285 mutex_unlock(&drv->lock);
286 dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n",
287 mc->type);
288 return -EINVAL;
289 };
290
291 if (mc->w && mc->w->power)
292 ret = sst_send_gain_cmd(drv, gv, mc->task_id,
293 mc->pipe_id | mc->instance_id, mc->module_id, 0);
294 mutex_unlock(&drv->lock);
295
296 return ret;
297}
298
299static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);
300
301/* Gain helper with min/max set */
302#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, \
304 SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \
305 sst_gain_get, sst_gain_put, \
306 SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id, \
307 sst_gain_tlv_common, gain_var)
308
309#define SST_VOLUME(name, path_id, task_id, instance, gain_var) \
310 SST_GAIN_KCONTROLS(name, "Volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \
311 SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \
312 sst_gain_get, sst_gain_put, \
313 SST_MODULE_ID_VOLUME, path_id, instance, task_id, \
314 sst_gain_tlv_common, gain_var)
315
316static struct sst_gain_value sst_gains[];
317
318static const struct snd_kcontrol_new sst_gain_controls[] = {
319 SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]),
320 SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]),
321 SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]),
322 SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]),
323
324 SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]),
325 SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]),
326 SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[6]),
327 SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]),
328
329 SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]),
330 SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]),
331 SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]),
332 SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]),
333 SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]),
334 SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),
335 SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),
336 SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),
337};
338
339#define SST_GAIN_NUM_CONTROLS 3
340/* the SST_GAIN macro above will create three alsa controls for each
341 * instance invoked, gain, mute and ramp duration, which use the same gain
342 * cell sst_gain to keep track of data
343 * To calculate number of gain cell instances we need to device by 3 in
344 * below caulcation for gain cell memory.
345 * This gets rid of static number and issues while adding new controls
346 */
347static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS];
348
165static const struct snd_kcontrol_new sst_algo_controls[] = { 349static const struct snd_kcontrol_new sst_algo_controls[] = {
166 SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24, 350 SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,
167 SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), 351 SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
@@ -200,19 +384,33 @@ static int sst_algo_control_init(struct device *dev)
200 384
201int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) 385int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
202{ 386{
203 int ret = 0; 387 int i, ret = 0;
204 struct sst_data *drv = snd_soc_platform_get_drvdata(platform); 388 struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
389 unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;
205 390
206 drv->byte_stream = devm_kzalloc(platform->dev, 391 drv->byte_stream = devm_kzalloc(platform->dev,
207 SST_MAX_BIN_BYTES, GFP_KERNEL); 392 SST_MAX_BIN_BYTES, GFP_KERNEL);
208 if (!drv->byte_stream) 393 if (!drv->byte_stream)
209 return -ENOMEM; 394 return -ENOMEM;
210 395
211 /*Initialize algo control params*/ 396 for (i = 0; i < gains; i++) {
397 sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT;
398 sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT;
399 sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT;
400 sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT;
401 }
402
403 ret = snd_soc_add_platform_controls(platform, sst_gain_controls,
404 ARRAY_SIZE(sst_gain_controls));
405 if (ret)
406 return ret;
407
408 /* Initialize algo control params */
212 ret = sst_algo_control_init(platform->dev); 409 ret = sst_algo_control_init(platform->dev);
213 if (ret) 410 if (ret)
214 return ret; 411 return ret;
215 ret = snd_soc_add_platform_controls(platform, sst_algo_controls, 412 ret = snd_soc_add_platform_controls(platform, sst_algo_controls,
216 ARRAY_SIZE(sst_algo_controls)); 413 ARRAY_SIZE(sst_algo_controls));
414
217 return ret; 415 return ret;
218} 416}
diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h
index a73e894b175c..e530002cd763 100644
--- a/sound/soc/intel/sst-atom-controls.h
+++ b/sound/soc/intel/sst-atom-controls.h
@@ -23,6 +23,9 @@
23#ifndef __SST_ATOM_CONTROLS_H__ 23#ifndef __SST_ATOM_CONTROLS_H__
24#define __SST_ATOM_CONTROLS_H__ 24#define __SST_ATOM_CONTROLS_H__
25 25
26#include <sound/soc.h>
27#include <sound/tlv.h>
28
26enum { 29enum {
27 MERR_DPCM_AUDIO = 0, 30 MERR_DPCM_AUDIO = 0,
28 MERR_DPCM_COMPR, 31 MERR_DPCM_COMPR,
@@ -360,16 +363,134 @@ struct sst_dsp_header {
360struct sst_cmd_generic { 363struct sst_cmd_generic {
361 struct sst_dsp_header header; 364 struct sst_dsp_header header;
362} __packed; 365} __packed;
366
367struct gain_cell {
368 struct sst_destination_id dest;
369 s16 cell_gain_left;
370 s16 cell_gain_right;
371 u16 gain_time_constant;
372} __packed;
373
374#define NUM_GAIN_CELLS 1
375struct sst_cmd_set_gain_dual {
376 struct sst_dsp_header header;
377 u16 gain_cell_num;
378 struct gain_cell cell_gains[NUM_GAIN_CELLS];
379} __packed;
363struct sst_cmd_set_params { 380struct sst_cmd_set_params {
364 struct sst_destination_id dst; 381 struct sst_destination_id dst;
365 u16 command_id; 382 u16 command_id;
366 char params[0]; 383 char params[0];
367} __packed; 384} __packed;
385
386/**** widget defines *****/
387
388struct sst_ids {
389 u16 location_id;
390 u16 module_id;
391 u8 task_id;
392 u8 format;
393 u8 reg;
394 const char *parent_wname;
395 struct snd_soc_dapm_widget *parent_w;
396 struct list_head algo_list;
397 struct list_head gain_list;
398 const struct sst_pcm_format *pcm_fmt;
399};
400enum sst_gain_kcontrol_type {
401 SST_GAIN_TLV,
402 SST_GAIN_MUTE,
403 SST_GAIN_RAMP_DURATION,
404};
405
406struct sst_gain_mixer_control {
407 bool stereo;
408 enum sst_gain_kcontrol_type type;
409 struct sst_gain_value *gain_val;
410 int max;
411 int min;
412 u16 instance_id;
413 u16 module_id;
414 u16 pipe_id;
415 u16 task_id;
416 char pname[44];
417 struct snd_soc_dapm_widget *w;
418};
419
420struct sst_gain_value {
421 u16 ramp_duration;
422 s16 l_gain;
423 s16 r_gain;
424 bool mute;
425};
426#define SST_GAIN_VOLUME_DEFAULT (-1440)
427#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */
428#define SST_GAIN_MUTE_DEFAULT true
429
430#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \
431 xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \
432 xmin, xmax, xpname) \
433 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
434 .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
435 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
436 .tlv.p = (tlv_array), \
437 .info = sst_gain_ctl_info,\
438 .get = xhandler_get, .put = xhandler_put, \
439 .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
440 { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \
441 .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
442 .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
443
444#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \
445 xmod, xpipe, xinstance, xtask, xtype, xgain_val, \
446 xmin, xmax, xpname) \
447 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
448 .info = sst_gain_ctl_info, \
449 .get = xhandler_get, .put = xhandler_put, \
450 .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
451 { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \
452 .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
453 .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
454
455#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\
456 xmod, xpipe, xinstance, xtask, xgain_val, xpname) \
457 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
458 .info = snd_soc_info_bool_ext, \
459 .get = xhandler_get, .put = xhandler_put, \
460 .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
461 { .stereo = false, .type = SST_GAIN_MUTE, \
462 .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
463 .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
368#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \ 464#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \
369 xpname " " xmname " " #xinstance " " xtype 465 xpname " " xmname " " #xinstance " " xtype
370 466
371#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \ 467#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \
372 xpname " " xmname " " #xinstance " " xtype " " xsubmodule 468 xpname " " xmname " " #xinstance " " xtype " " xsubmodule
469
470/*
471 * 3 Controls for each Gain module
472 * e.g. - pcm0_in Gain 0 Volume
473 * - pcm0_in Gain 0 Ramp Delay
474 * - pcm0_in Gain 0 Switch
475 */
476#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \
477 xhandler_get, xhandler_put, \
478 xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \
479 { SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "Ramp Delay"), \
480 xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \
481 xgain_val, xmin_tc, xmax_tc, xpname) }, \
482 { SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "Switch"), \
483 xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \
484 xgain_val, xpname) } ,\
485 { SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "Volume"), \
486 xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \
487 xgain_val, xmin_gain, xmax_gain, xpname) }
488
489#define SST_GAIN_TC_MIN 5
490#define SST_GAIN_TC_MAX 5000
491#define SST_GAIN_MIN_VALUE -1440 /* in 0.1 DB units */
492#define SST_GAIN_MAX_VALUE 360
493
373enum sst_algo_kcontrol_type { 494enum sst_algo_kcontrol_type {
374 SST_ALGO_PARAMS, 495 SST_ALGO_PARAMS,
375 SST_ALGO_BYPASS, 496 SST_ALGO_BYPASS,