diff mbox

[v3] ASoC: sgtl5000: add avc support

Message ID 1497337054-4911-1-git-send-email-richard.leitner@skidata.com (mailing list archive)
State Changes Requested
Headers show

Commit Message

Richard Leitner June 13, 2017, 6:57 a.m. UTC
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(+)

Comments

Fabio Estevam July 20, 2017, 5:11 p.m. UTC | #1
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?
Richard Leitner July 20, 2017, 5:25 p.m. UTC | #2
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
diff mbox

Patch

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 */