[alsa-devel] [PATCH v3] ASoC: sgtl5000: add avc support
The sgtl5000 features a automatic volume control block (AVC), which reduces loud signals and amplifies low level signals for easier listening. This patch adds support for this AVC block to the driver.
Apart from the "AVC Switch" control which enables the block following controls for the configuration of AVC are added: + AVC Threshold Volume: threshold where audio is compressed when the measured level is above or expanded when below + AVC Max Gain Volume: maximum gain which can be applied when the measured audio level is below threshold + AVC Hard Limiter Switch: when enabled the signal is limited to the programmed threshold. + AVC Integrator Response: response time of the integrator
The AVC block is enabled and configured using the DAP_AVC_CTRL and DAP_AVC_THRESHOLD registers.
Following 2 checkpatch.pl strict checks are ignored because the indentation style is different for the struct snd_kcontrol_new definition: patch:145: CHECK: Alignment should match open parenthesis patch:148: CHECK: Alignment should match open parenthesis
Signed-off-by: Richard Leitner richard.leitner@skidata.com --- CHANGES v3: - rename "AVC Enable Switch" to "AVC Switch" - add AVC configuration controls CHANGES v2: - added trailing "Switch" as covered in ControlNames.txt --- sound/soc/codecs/sgtl5000.c | 87 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+)
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 5a2702e..5d4b69f 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -74,6 +74,20 @@ static const struct reg_default sgtl5000_reg_defaults[] = { { SGTL5000_DAP_AVC_DECAY, 0x0050 }, };
+/* AVC: Threshold dB -> register: pre-calculated values */ +static const u16 avc_thr_db2reg[97] = { + 0x5168, 0x488E, 0x40AA, 0x39A1, 0x335D, 0x2DC7, 0x28CC, 0x245D, 0x2068, + 0x1CE2, 0x19BE, 0x16F1, 0x1472, 0x1239, 0x103E, 0x0E7A, 0x0CE6, 0x0B7F, + 0x0A3F, 0x0922, 0x0824, 0x0741, 0x0677, 0x05C3, 0x0522, 0x0493, 0x0414, + 0x03A2, 0x033D, 0x02E3, 0x0293, 0x024B, 0x020B, 0x01D2, 0x019F, 0x0172, + 0x014A, 0x0126, 0x0106, 0x00E9, 0x00D0, 0x00B9, 0x00A5, 0x0093, 0x0083, + 0x0075, 0x0068, 0x005D, 0x0052, 0x0049, 0x0041, 0x003A, 0x0034, 0x002E, + 0x0029, 0x0025, 0x0021, 0x001D, 0x001A, 0x0017, 0x0014, 0x0012, 0x0010, + 0x000E, 0x000D, 0x000B, 0x000A, 0x0009, 0x0008, 0x0007, 0x0006, 0x0005, + 0x0005, 0x0004, 0x0004, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, 0x0002, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; + /* regulator supplies for sgtl5000, VDDD is an optional external supply */ enum sgtl5000_regulator_supplies { VDDA, @@ -382,6 +396,63 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol, return 0; }
+/* + * custom function to get AVC threshold + * + * The threshold dB is calculated by rearranging the calculation from the + * avc_put_threshold function: register_value = 10^(dB/20) * 0.636 * 2^15 ==> + * dB = ( fls(register_value) - 14.347 ) * 6.02 + * + * As this calculation is expensive and the threshold dB values may not exeed + * 0 to 96 we use pre-calculated values. + */ +static int avc_get_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int db, i; + u16 reg = snd_soc_read(codec, SGTL5000_DAP_AVC_THRESHOLD); + + /* register value 0 => -96dB */ + if (!reg) { + ucontrol->value.integer.value[0] = 96; + ucontrol->value.integer.value[1] = 96; + return 0; + } + + /* get dB from register value (rounded down) */ + for (i = 0; avc_thr_db2reg[i] > reg; i++) + ; + db = i; + + ucontrol->value.integer.value[0] = db; + ucontrol->value.integer.value[1] = db; + + return 0; +} + +/* + * custom function to put AVC threshold + * + * The register value is calculated by following formula: + * register_value = 10^(dB/20) * 0.636 * 2^15 + * As this calculation is expensive and the threshold dB values may not exeed + * 0 to 96 we use pre-calculated values. + */ +static int avc_put_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int db; + u16 reg; + + db = clamp((int)ucontrol->value.integer.value[0], 0, 96); + reg = avc_thr_db2reg[db]; + snd_soc_write(codec, SGTL5000_DAP_AVC_THRESHOLD, reg); + + return 0; +} + static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0);
/* tlv for mic gain, 0db 20db 30db 40db */ @@ -396,6 +467,12 @@ static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0); /* tlv for lineout volume, 31 steps of .5db each */ static const DECLARE_TLV_DB_SCALE(lineout_volume, -1550, 50, 0);
+/* tlv for dap avc max gain, 0db, 6db, 12db */ +static const DECLARE_TLV_DB_SCALE(avc_max_gain, 0, 600, 0); + +/* tlv for dap avc threshold, */ +static const DECLARE_TLV_DB_MINMAX(avc_threshold, 0, 9600); + static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { /* SOC_DOUBLE_S8_TLV with invert */ { @@ -434,6 +511,16 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { 0x1f, 1, lineout_volume), SOC_SINGLE("Lineout Playback Switch", SGTL5000_CHIP_ANA_CTRL, 8, 1, 1), + + /* Automatic Volume Control (DAP AVC) */ + SOC_SINGLE("AVC Switch", SGTL5000_DAP_AVC_CTRL, 0, 1, 0), + SOC_SINGLE("AVC Hard Limiter Switch", SGTL5000_DAP_AVC_CTRL, 5, 1, 0), + SOC_SINGLE_TLV("AVC Max Gain Volume", SGTL5000_DAP_AVC_CTRL, 12, 2, 0, + avc_max_gain), + SOC_SINGLE("AVC Integrator Response", SGTL5000_DAP_AVC_CTRL, 8, 3, 0), + SOC_SINGLE_EXT_TLV("AVC Threshold Volume", SGTL5000_DAP_AVC_THRESHOLD, + 0, 96, 0, avc_get_threshold, avc_put_threshold, + avc_threshold), };
/* mute the codec used by alsa core */
Hi Richard,
On Tue, Jun 13, 2017 at 3:57 AM, Richard Leitner richard.leitner@skidata.com wrote:
The sgtl5000 features a automatic volume control block (AVC), which reduces loud signals and amplifies low level signals for easier listening. This patch adds support for this AVC block to the driver.
Apart from the "AVC Switch" control which enables the block following controls for the configuration of AVC are added: + AVC Threshold Volume: threshold where audio is compressed when the measured level is above or expanded when below + AVC Max Gain Volume: maximum gain which can be applied when the measured audio level is below threshold + AVC Hard Limiter Switch: when enabled the signal is limited to the programmed threshold. + AVC Integrator Response: response time of the integrator
The AVC block is enabled and configured using the DAP_AVC_CTRL and DAP_AVC_THRESHOLD registers.
Following 2 checkpatch.pl strict checks are ignored because the indentation style is different for the struct snd_kcontrol_new definition: patch:145: CHECK: Alignment should match open parenthesis patch:148: CHECK: Alignment should match open parenthesis
Signed-off-by: Richard Leitner richard.leitner@skidata.com
Running linux-next-20170720 on a imx53-qsb I get the following issue:
root@imx53qsb:~# reboot
Broadcast message from root@imx53qsb (ttymxc0) (Mon May 15 18:40:54 2017): The system is going down for reboot NOW! INIT: Switching to runlevel: 6 INIT: Sending processes the TERM signal root@imx53qsb:~# * Stopping Avahi mDNS/DNS-SD Daemon: avahi-daemon ...done. Stopping bluetooth /usr/libexec/bluetooth/bluetoothd Stopping system message bus: dbus. hwclock: can't open '/dev/misc/rtc': No such file or directory Stopping syslogd/klogd: stopped syslogd (pid 278) stopped klogd (pid 282) done Stopping Telephony daemon ALSA: Storing mixer settings... [ 20.031604] Unable to handle kernel paging request at virtual address fffffffe [ 20.039268] pgd = de2a0000 [ 20.041999] [fffffffe] *pgd=8fffd861, *pte=00000000, *ppte=00000000 [ 20.048387] Internal error: Oops: 80000007 [#1] SMP ARM [ 20.053626] Modules linked in: [ 20.056704] CPU: 0 PID: 331 Comm: alsactl Not tainted 4.13.0-rc1-next-20170720 #28 [ 20.064280] Hardware name: Freescale i.MX53 (Device Tree Support) [ 20.070381] task: dfb12640 task.stack: ddcfa000 [ 20.074922] PC is at 0xfffffffe [ 20.078083] LR is at snd_soc_component_read+0x34/0x40 [ 20.083142] pc : [<fffffffe>] lr : [<c0759b14>] psr: a00f0033 [ 20.089416] sp : ddcfbe60 ip : ddcfbe70 fp : ddcfbe6c [ 20.094648] r10: 00000000 r9 : de1cebf4 r8 : de1cef68 [ 20.099880] r7 : becb9450 r6 : de2e4800 r5 : de1b9980 r4 : de2e4800 [ 20.106416] r3 : ffffffff r2 : ddcfbe74 r1 : 00000126 r0 : de1a9e40 [ 20.112952] Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA Thumb Segment none [ 20.120268] Control: 10c5387d Table: 8e2a0019 DAC: 00000051 [ 20.126022] Process alsactl (pid: 331, stack limit = 0xddcfa210) [ 20.132036] Stack: (0xddcfbe60 to 0xddcfc000) [ 20.136406] be60: ddcfbe84 ddcfbe70 c0759bc8 c0759aec 00000001 00000000 ddcfbe9c ddcfbe88 [ 20.144595] be80: c075e19c c0759bb8 0000000f de1b9980 ddcfbf0c ddcfbea0 c07349fc c075e18c [ 20.152784] bea0: ffffe000 de280080 de280000 de280080 de280034 00000001 de280080 de280000 [ 20.160973] bec0: ddcfbee4 ddcfbed0 c09c44a0 c0174d34 00000010 de280000 ddcfbf0c ddcfbee8 [ 20.169161] bee0: c024bc0c becb9450 de1baf98 de221b40 c023d2e4 00000004 ddcfa000 00000000 [ 20.177350] bf00: ddcfbf7c ddcfbf10 c023c8f0 c0734450 de221b40 de20d000 00000020 de221b48 [ 20.185538] bf20: de1baf98 00000000 ddcfbf4c ddcfbf38 de221b40 de221b40 00000002 00000001 [ 20.193727] bf40: 00000004 ddcfa000 ddcfbfa4 ddcfbf58 c023b500 de221b40 00000004 de221b40 [ 20.201915] bf60: c2c85512 becb9450 ddcfa000 00000000 ddcfbfa4 ddcfbf80 c023d2e4 c023c860 [ 20.210104] bf80: becb9338 00000000 becb9450 00000036 c0107f24 ddcfa000 00000000 ddcfbfa8 [ 20.218292] bfa0: c0107d60 c023d2b4 becb9338 00000000 00000004 c2c85512 becb9450 01d22568 [ 20.226481] bfc0: becb9338 00000000 becb9450 00000036 01d25328 becb9998 01d226c0 becb9f10 [ 20.234670] bfe0: b6f8c41c becb931c b6f0b1d5 b6dde1a6 200f0030 00000004 8fffd861 8fffdc61 [ 20.242852] Backtrace: [ 20.245318] [<c0759ae0>] (snd_soc_component_read) from [<c0759bc8>] (snd_soc_read+0x1c/0x30) [ 20.253774] [<c0759bac>] (snd_soc_read) from [<c075e19c>] (avc_get_threshold+0x1c/0x90) [ 20.261798] [<c075e180>] (avc_get_threshold) from [<c07349fc>] (snd_ctl_ioctl+0x5b8/0xb38) [ 20.270069] r5:de1b9980 r4:0000000f [ 20.273661] [<c0734444>] (snd_ctl_ioctl) from [<c023c8f0>] (do_vfs_ioctl+0x9c/0xa54) [ 20.281415] r10:00000000 r9:ddcfa000 r8:00000004 r7:c023d2e4 r6:de221b40 r5:de1baf98 [ 20.289250] r4:becb9450 [ 20.291794] [<c023c854>] (do_vfs_ioctl) from [<c023d2e4>] (SyS_ioctl+0x3c/0x64) [ 20.299114] r10:00000000 r9:ddcfa000 r8:becb9450 r7:c2c85512 r6:de221b40 r5:00000004 [ 20.306949] r4:de221b40 [ 20.309503] [<c023d2a8>] (SyS_ioctl) from [<c0107d60>] (ret_fast_syscall+0x0/0x1c) [ 20.317083] r9:ddcfa000 r8:c0107f24 r7:00000036 r6:becb9450 r5:00000000 r4:becb9338 [ 20.325226] Code: bad PC value [ 20.328330] ---[ end trace 7d153a40b8b802a3 ]---
If I revert a729526720059ae ("ASoC: sgtl5000: add avc support") the problem does not happen.
Could you please fix this issue?
Hi Fabio, thanks for the testing/reporting!
On 07/20/2017 07:11 PM, Fabio Estevam wrote:
Running linux-next-20170720 on a imx53-qsb I get the following issue:
root@imx53qsb:~# reboot
...
ALSA: Storing mixer settings... [ 20.031604] Unable to handle kernel paging request at virtual address fffffffe
...
[ 20.242852] Backtrace: [ 20.245318] [<c0759ae0>] (snd_soc_component_read) from [<c0759bc8>] (snd_soc_read+0x1c/0x30) [ 20.253774] [<c0759bac>] (snd_soc_read) from [<c075e19c>] (avc_get_threshold+0x1c/0x90) [ 20.261798] [<c075e180>] (avc_get_threshold) from [<c07349fc>] (snd_ctl_ioctl+0x5b8/0xb38)
...
If I revert a729526720059ae ("ASoC: sgtl5000: add avc support") the problem does not happen.
Could you please fix this issue?
Does this occur only on reboot or everytime the driver tries to read the AVC threshold (avc_get_threshold)? Are you able to set the AVC threshold (using amixer)?
Thanks & regards, Richard.L
Hi Richard,
On Thu, Jul 20, 2017 at 2:25 PM, Richard Leitner dev@g0hl1n.net wrote:
Does this occur only on reboot or everytime the driver tries to read the AVC threshold (avc_get_threshold)? Are you able to set the AVC threshold (using amixer)?
I am not even using the codec. I just run 'reboot' and the problem happens.
The following change fixes the 'reboot' issue for me. Could you please try it?
--- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -409,7 +409,7 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol, static int avc_get_threshold(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); int db, i; u16 reg = snd_soc_read(codec, SGTL5000_DAP_AVC_THRESHOLD);
@@ -442,7 +442,7 @@ static int avc_get_threshold(struct snd_kcontrol *kcontrol, static int avc_put_threshold(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); int db; u16 reg;
On 07/20/2017 07:27 PM, Fabio Estevam wrote:
Hi Richard,
On Thu, Jul 20, 2017 at 2:25 PM, Richard Leitner dev@g0hl1n.net wrote:
Does this occur only on reboot or everytime the driver tries to read the AVC threshold (avc_get_threshold)? Are you able to set the AVC threshold (using amixer)?
I am not even using the codec. I just run 'reboot' and the problem happens.
The following change fixes the 'reboot' issue for me. Could you please try it?
--- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -409,7 +409,7 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol, static int avc_get_threshold(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) {
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
Thank you for spotting this! That works of course.
I'm sorry, it seems I messed something up during copy/paste for my own backport version of this patch. Seems like I tested and sent a different version of the patch... That definitely shouldn't have happened :-(
Do you want to send that patch or should I?
Thank you again Fabio!
regards, Richard.L
On Thu, Jul 20, 2017 at 2:38 PM, Richard Leitner dev@g0hl1n.net wrote:
Thank you for spotting this! That works of course.
I'm sorry, it seems I messed something up during copy/paste for my own backport version of this patch. Seems like I tested and sent a different version of the patch... That definitely shouldn't have happened :-(
Do you want to send that patch or should I?
I have just sent the patch.
participants (3)
-
Fabio Estevam
-
Richard Leitner
-
Richard Leitner