aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/tlv320aic326x_minidsp_config.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/tlv320aic326x_minidsp_config.c')
-rw-r--r--sound/soc/codecs/tlv320aic326x_minidsp_config.c495
1 files changed, 495 insertions, 0 deletions
diff --git a/sound/soc/codecs/tlv320aic326x_minidsp_config.c b/sound/soc/codecs/tlv320aic326x_minidsp_config.c
new file mode 100644
index 00000000000..e34ffbe2ca8
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic326x_minidsp_config.c
@@ -0,0 +1,495 @@
1/*
2 * linux/sound/soc/codecs/tlv320aic326x_minidsp_config.c
3 *
4 * Copyright (C) 2012 Texas Instruments, Inc.
5 *
6 * This package is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
11 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
12 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13 *
14 * The TLV320AIC3262 is a flexible, low-power, low-voltage stereo audio
15 * codec with digital microphone inputs and programmable outputs.
16 *
17 * History:
18 *
19 * Rev 0.1 Added the multiconfig support 17-08-2011
20 *
21 * Rev 0.2 Migrated for aic3262 nVidia
22 * 21-10-2011
23 */
24
25/*
26 *****************************************************************************
27 * INCLUDES
28 *****************************************************************************
29 */
30#include <linux/module.h>
31#include <linux/moduleparam.h>
32#include <linux/init.h>
33#include <linux/kernel.h>
34#include <linux/fs.h>
35#include <linux/types.h>
36#include <linux/kdev_t.h>
37#include <linux/cdev.h>
38#include <linux/device.h>
39#include <linux/io.h>
40#include <linux/delay.h>
41#include <linux/i2c.h>
42#include <linux/platform_device.h>
43#include <sound/soc.h>
44#include <sound/core.h>
45#include <sound/soc-dapm.h>
46#include <sound/control.h>
47#include <linux/time.h> /* For timing computations */
48#include "tlv320aic326x.h"
49#include "tlv320aic326x_mini-dsp.h"
50
51#if 0
52#include "Patch_base_jazz_Rate48_pps_driver.h"
53#include "Patch_base_main_Rate48_pps_driver.h"
54#include "Patch_base_pop_Rate48_pps_driver.h"
55#include "Patch_base_rock_Rate48_pps_driver.h"
56#endif
57
58#ifdef CONFIG_MINI_DSP
59
60#define MAX_CONFIG_D_ARRAYS 4
61#define MAX_CONFIG_A_ARRAYS 0
62#define MAX_CONFIG_ARRAYS 4
63#define MINIDSP_DMODE 0
64#define MINIDSP_AMODE 1
65
66/*
67 *****************************************************************************
68 * LOCAL STATIC DECLARATIONS
69 *****************************************************************************
70 */
71static int multibyte_coeff_change(struct snd_soc_codec *codec, int);
72
73static int m_control_get(struct snd_kcontrol *kcontrol,
74 struct snd_ctl_elem_value *ucontrol);
75static int m_control_put(struct snd_kcontrol *kcontrol,
76 struct snd_ctl_elem_value *ucontrol);
77
78/* k-control macros used for miniDSP related Kcontrols */
79#define SOC_SINGLE_VALUE_M(xmax, xinvert) \
80 ((unsigned long)&(struct soc_mixer_control) \
81 {.max = xmax, \
82 .invert = xinvert})
83#define SOC_SINGLE_M(xname, max, invert) \
84{\
85 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
86 .info = m_control_info, .get = m_control_get,\
87 .put = m_control_put, \
88 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
89 .private_value = SOC_SINGLE_VALUE_M(max, invert) }
90#define SOC_SINGLE_AIC3262_M(xname) \
91{\
92 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
93 .info = m_control_info, .get = m_control_get,\
94 .put = m_control_put, \
95 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
96}
97
98
99/* The Multi-Configurations generated through PPS GDE has been
100 * named as Rock, Pop, Jazz and Main. These were the Configurations
101 * that were used while testing this AUdio Driver. If the user
102 * creates Process-flow with different names, it is advised to
103 * modify the names present in the below array.
104 */
105static const char *multi_config_support_DAC[] = {
106 "ROCK",
107 "POP",
108 "JAZZ",
109 "MAIN",
110};
111
112/* SOC_ENUM Declaration and kControl for switching Configurations
113 * at run-time.
114 */
115static const struct soc_enum aic3262_enum =
116 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(multi_config_support_DAC),
117 multi_config_support_DAC);
118
119static const struct snd_kcontrol_new aic3262_minidsp_controls1[] = {
120
121 SOC_ENUM_EXT("Multiconfig support for DAC",
122 aic3262_enum, m_control_get, m_control_put),
123
124};
125
126
127/*
128 * multibyte_config
129 *
130 * This structure has been devised to maintain information about each
131 * configuration provided with the PPS GDE Processflow. For each
132 * configuration, the Driver needs to know where the starting offset
133 * of the Coefficient change, Total Size of Coefficients being affected,
134 * and the Instruction Sizes.
135 * This has to be replicated for both miniDSP_A and miniDSP_D
136 */
137
138#if 0
139struct multibyte_config {
140 reg_value *regs;
141 unsigned int d_coeff_start;
142 unsigned int d_coeff_size;
143 unsigned int d_inst_start;
144 unsigned int d_inst_size;
145 unsigned int a_coeff_start;
146 unsigned int a_coeff_size;
147 unsigned int a_inst_start;
148 unsigned int a_inst_size;
149} config_array[][2][MAX_CONFIG_ARRAYS] = {
150 /* Process flow 1 */
151 {
152 {
153 /* DAC */
154 {rock_D_reg_values, 0, 67, 67, 0, 0, 0, 0, 0},
155 {pop_D_reg_values, 0, 67, 67, 0, 0, 0, 0, 0},
156 {jazz_D_reg_values, 0, 67, 67, 0, 0, 0, 0, 0},
157 {main_D_reg_values, 0, 67, 67, 0, 0, 0, 0, 0},
158 },
159 /* ADC */
160 {},
161 },
162
163 /* Process flow 2 */
164 {
165#if 0
166 {
167 {main, 0, 0, 0, 0, 0, 0, 0, 0},
168 {pop, 0, 0, 0, 0, 0, 0, 0, 0},
169 {jazz, 0, 0, 0, 0, 0, 0, 0, 0},
170 {rock, 0, 0, 0, 0, 0, 0, 0, 0},
171 },
172 /* ADC */
173 {},
174#endif
175 },
176};
177
178#else
179struct multibyte_config {
180 reg_value *regs;
181 unsigned int d_coeff_start;
182 unsigned int d_coeff_size;
183 unsigned int d_inst_start;
184 unsigned int d_inst_size;
185 unsigned int a_coeff_start;
186 unsigned int a_coeff_size;
187 unsigned int a_inst_start;
188 unsigned int a_inst_size;
189} config_array[][2][MAX_CONFIG_ARRAYS] = {
190 /* Process flow 1 */
191 {
192 {
193 /* DAC */
194 {},
195 },
196 /* ADC */
197 {},
198 },
199
200 /* Process flow 2 */
201 {
202#if 0
203 {
204 {main, 0, 0, 0, 0, 0, 0, 0, 0},
205 {pop, 0, 0, 0, 0, 0, 0, 0, 0},
206 {jazz, 0, 0, 0, 0, 0, 0, 0, 0},
207 {rock, 0, 0, 0, 0, 0, 0, 0, 0},
208 },
209 /* ADC */
210 {},
211#endif
212 },
213};
214#endif
215
216/*
217 *----------------------------------------------------------------------------
218 * Function : m_control_get
219 * Purpose : This function is to read data of new control for
220 * program the AIC3262 registers.
221 *
222 *----------------------------------------------------------------------------
223 */
224static int m_control_get(struct snd_kcontrol *kcontrol,
225 struct snd_ctl_elem_value *ucontrol)
226{
227
228 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
229 struct aic3262_priv *aic326x = snd_soc_codec_get_drvdata(codec);
230 u32 val = 0;
231 u32 mode = aic326x->process_flow;
232
233
234 if (!strcmp(kcontrol->id.name, "Multiconfig support for DAC"))
235 val = aic326x->current_dac_config[mode];
236 else if (!strcmp(kcontrol->id.name, "Multiconfig support for ADC"))
237 val = aic326x->current_adc_config[mode];
238
239
240 ucontrol->value.integer.value[0] = val;
241 return 0;
242}
243
244/*
245 *----------------------------------------------------------------------------
246 * Function : m_new_control_put
247 * Purpose : new_control_put is called to pass data from user/application to
248 * the driver.
249 *
250 *----------------------------------------------------------------------------
251 */
252static int m_control_put(struct snd_kcontrol *kcontrol,
253 struct snd_ctl_elem_value *ucontrol)
254{
255 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
256 struct aic3262_priv *aic326x = snd_soc_codec_get_drvdata(codec);
257 u32 val;
258 u8 value;
259 int pf = aic326x->process_flow;
260 struct multibyte_config *array;
261
262 val = ucontrol->value.integer.value[0];
263 if (!strcmp(kcontrol->id.name, "Multiconfig support for DAC")) {
264 if (aic326x->process_flow == MINIDSP_DMODE) {
265 if (val > MAX_CONFIG_D_ARRAYS) {
266 dev_err(codec->dev, "Value not in range\n");
267 return -1;
268 }
269
270 value = aic3262_read(codec, ADC_CHANNEL_POW);
271 value = aic3262_read(codec, PASI_DAC_DP_SETUP);
272
273 array = &config_array[pf][MINIDSP_DMODE][val];
274 minidsp_i2c_multibyte_transfer(codec,
275 array->regs,
276 (array->d_inst_size + array->d_coeff_size));
277
278
279 /*coefficent buffer change*/
280 multibyte_coeff_change(codec, 0x50);
281
282 minidsp_i2c_multibyte_transfer(codec,
283 array->regs,
284 (array->d_inst_size + array->d_coeff_size));
285
286
287
288 value = aic3262_read(codec, ADC_CHANNEL_POW);
289 value = aic3262_read(codec, PASI_DAC_DP_SETUP);
290 }
291 aic326x->current_dac_config[pf] = val;
292
293 } else {
294 if (aic326x->process_flow == MINIDSP_AMODE) {
295 if (val > MAX_CONFIG_A_ARRAYS) {
296 dev_err(codec->dev, "Value not in range\n");
297 return -1;
298 }
299 array = &config_array[pf][MINIDSP_AMODE][val];
300 minidsp_i2c_multibyte_transfer(codec,
301 array->regs,
302 (array->a_inst_size + array->a_coeff_size));
303 }
304 aic326x->current_adc_config[pf] = val;
305 }
306 return val;
307}
308
309
310/*
311 *--------------------------------------------------------------------------
312 * Function : aic3262_add_multiconfig_controls
313 * Purpose : Configures the AMIXER Control Interfaces that can be exercised by
314 * the user at run-time. Utilizes the the snd_adaptive_controls[]
315 * array to specify two run-time controls.
316 *---------------------------------------------------------------------------
317 */
318int aic3262_add_multiconfig_controls(struct snd_soc_codec *codec)
319{
320 int i, err;
321
322 DBG(KERN_INFO
323 "#%s: Invoked to add controls for Multi-Configuration\n",
324 __func__);
325
326 /* add mode k control */
327 for (i = 0; i < ARRAY_SIZE(aic3262_minidsp_controls1); i++) {
328 err = snd_ctl_add(codec->card->snd_card,
329 snd_ctl_new1(&aic3262_minidsp_controls1[i],
330 codec));
331 if (err < 0) {
332 printk(KERN_ERR
333 "Cannot add controls for mulibyte configuration\n");
334 return err;
335 }
336 }
337 DBG(KERN_INFO "#%s: Completed control addition.\n", __func__);
338 return 0;
339}
340
341/*
342 *--------------------------------------------------------------------------
343 * Function : minidsp_multiconfig
344 * Purpose : Function which is invoked when user changes the configuration
345 * at run-time. Internally configures/switches both
346 * miniDSP_D and miniDSP_A Coefficient arrays.
347 *---------------------------------------------------------------------------
348 */
349void minidsp_multiconfig(struct snd_soc_codec *codec,
350 reg_value *a_patch, int a_size,
351 reg_value *d_patch, int d_size)
352{
353 struct aic3262_priv *aic326x = snd_soc_codec_get_drvdata(codec);
354 int val1,val2;
355 int adc_status,dac_status;
356 int (*ptransfer)(struct snd_soc_codec *codec,
357 reg_value *program_ptr,
358 int size);
359
360printk("======in the config_multiconfiguration function==== \n");
361#ifndef MULTIBYTE_I2C
362 ptransfer = byte_i2c_array_transfer;
363#else
364 ptransfer = minidsp_i2c_multibyte_transfer;
365#endif
366 //DBG(KERN_INFO "#%s: Invoked for miniDSP Mode %d\n", __func__, mode);
367
368 adc_status=aic3262_read(codec,ADC_FLAG_R1);
369 printk("ADC STATUS = %x", adc_status);
370
371 dac_status=aic3262_read(codec,DAC_FLAG_R1);
372 printk("DAC STATUS = %x", dac_status);
373
374 #if 0
375 /* Switching off the adaptive filtering for loading the patch file*/
376 aic3262_change_book(codec, 40);
377 val1 = i2c_smbus_read_byte_data(codec->control_data, 1);
378 aic3262_write(codec, 1, (val1&0xfb));
379
380 aic3262_change_book(codec, 80);
381 val2 = i2c_smbus_read_byte_data(codec->control_data, 1);
382 aic3262_write(codec, 1, (val2&0xfb));
383
384 #endif
385
386 /*apply patches to both pages (mirrored coeffs). this works when DSP are stopped*/
387 /* to apply patches when DSPs are runnig, the 'buffer swap' has to be executed, which is
388 done in the buffer swap section below*/
389
390 if (a_size)
391 ptransfer (codec, a_patch, a_size);
392 if (d_size)
393 ptransfer (codec, d_patch, d_size);
394
395
396 /*swap buffers for both a&d only if DSPs are running*/
397 if((dac_status & 0x80) || (dac_status & 0x8))
398 {
399 multibyte_coeff_change(codec, 0x50);/*swap DSP D Buffer*/
400 if (d_size)
401 ptransfer(codec, d_patch, d_size); /*apply patch after swapping buffer*/
402 }
403
404 if((adc_status & 0x40) || (adc_status & 0x4))
405 {
406 multibyte_coeff_change(codec, 0x28);/*swap DSP A Buffer*/
407 if (a_size)
408 ptransfer(codec, a_patch, a_size); /*apply patch after swapping buffer*/
409 }
410
411 #if 0
412 if (a_size)
413 ptransfer (codec, a_patch, a_size);
414 if (d_size)
415 ptransfer (codec, d_patch, d_size);
416 #endif
417
418
419 return ;
420}
421
422/*
423 *--------------------------------------------------------------------------
424 * Function : config_multibyte_for_mode
425 * Purpose : Function which is invoked when user changes the configuration
426 * at run-time. Internally configures/switches both
427 * miniDSP_D and miniDSP_A Coefficient arrays.
428 *---------------------------------------------------------------------------
429 */
430static int multibyte_coeff_change(struct snd_soc_codec *codec, int bk)
431{
432
433 u8 value[2], swap_reg_pre, swap_reg_post;
434 struct i2c_client *i2c;
435 i2c = codec->control_data;
436
437 aic3262_change_book(codec, bk);
438 aic3262_change_page(codec, 0);
439
440 value[0] = 1;
441
442 if (i2c_master_send(i2c, value, 1) != 1)
443 printk(KERN_ERR "Can not write register address\n");
444 else {
445 /* Read the Value of the Page 8 Register 1 which controls the
446 Adaptive Switching Mode */
447 if (i2c_master_recv(i2c, value, 1) != 1) {
448 printk(KERN_ERR "Can not read codec registers\n");
449 goto err;
450 }
451 swap_reg_pre = value[0];
452
453 /* Write the Register bit updates */
454 value[1] = value[0] | 1;
455 value[0] = 1;
456
457 if (i2c_master_send(i2c, value, 2) != 2) {
458 printk(KERN_ERR "Can not write register address\n");
459 goto err;
460 }
461 /*before verifying for buffer_swap, make sure we give one
462 frame delay, because the buffer swap happens at the end of the frame */
463 mdelay(5);
464 value[0] = 1;
465 /* verify buffer swap */
466 if (i2c_master_send(i2c, value, 1) != 1)
467 printk(KERN_ERR "Can not write register address\n");
468
469 /* Read the Value of the Page 8 Register 1 which controls the
470 Adaptive Switching Mode */
471 if (i2c_master_recv(i2c, &swap_reg_post, 1) != 1)
472 printk(KERN_ERR "Can not read codec registers\n");
473
474 if ((swap_reg_pre == 4 && swap_reg_post == 6)
475 || (swap_reg_pre == 6 && swap_reg_post == 4))
476 printk(KERN_INFO "Buffer swap success\n");
477 else
478 printk(KERN_ERR
479 "Buffer swap...FAILED\nswap_reg_pre=%x, swap_reg_post=%x\n",
480 swap_reg_pre, swap_reg_post);
481
482 aic3262_change_book(codec, 0x00);
483 }
484
485err:
486 return 0;
487}
488
489
490
491#endif
492
493MODULE_DESCRIPTION("ASoC AIC3262 miniDSP multi-configuration");
494MODULE_AUTHOR("Barani Prashanth <gvbarani@mistralsolutions.com>");
495MODULE_LICENSE("GPL");