Alsa-devel
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
June 2019
- 138 participants
- 273 discussions
[alsa-devel] why error return in sound/soc/codecs/pcm3168a.c::pcm3168a_set_dai_fmt ?
by Jurgen Lambrecht 01 Jul '19
by Jurgen Lambrecht 01 Jul '19
01 Jul '19
Hi,
When following
https://bootlin.com/blog/eight-channels-audio-on-i-mx7-with-pcm3168/ I
get this error (from soc_core.c):
... kernel: [ 3.059634][ T15] pcm3168a 3-0039: ASoC: Failed to set
DAI format: -22
I could propose this patch (I get that printf when applying the patch)
(all other errors in that file have a printf):
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index 08d3fe192e65..75ccbb919902 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -353,6 +353,7 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
case SND_SOC_DAIFMT_NB_NF:
break;
default:
+ dev_err(component->dev, "wrong INV_MASK\n");
return -EINVAL;
}
But why does this code returns an error for code that has no effect?
OK, it could be because SND_SOC_DAIFMT_INV_MASK must be
SND_SOC_DAIFMT_NB_NF. But then Alexandre's blog contains an error,
because the dac TDM sets frame-inversion in its dts (and it should not).
--
Kind Regards,
*Jürgen Lambrecht*
R&D Associate
Tel: +32 51 303045
www.televic-rail.com <https://www.televic-rail.com>
Televic Rail NV - Leo Bekaertlaan 1 - 8870 Izegem - Belgium
Company number 0825 539 581 - RPR Kortrijk
2
1
[alsa-devel] [PATCH] ALSA: xen-front: fix unintention integer overflow on left shifts
by Colin King 01 Jul '19
by Colin King 01 Jul '19
01 Jul '19
From: Colin Ian King <colin.king(a)canonical.com>
Shifting the integer value 1 is evaluated using 32-bit
arithmetic and then used in an expression that expects a 64-bit
value, so there is potentially an integer overflow. Fix this
by using the BIT_ULL macro to perform the shift.
Addresses-Coverity: ("Unintentional integer overflow")
Signed-off-by: Colin Ian King <colin.king(a)canonical.com>
---
sound/xen/xen_snd_front_alsa.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c
index b14ab512c2ce..e01631959ed8 100644
--- a/sound/xen/xen_snd_front_alsa.c
+++ b/sound/xen/xen_snd_front_alsa.c
@@ -196,7 +196,7 @@ static u64 to_sndif_formats_mask(u64 alsa_formats)
mask = 0;
for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
if (pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa) & alsa_formats)
- mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif;
+ mask |= BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif);
return mask;
}
@@ -208,7 +208,7 @@ static u64 to_alsa_formats_mask(u64 sndif_formats)
mask = 0;
for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
- if (1 << ALSA_SNDIF_FORMATS[i].sndif & sndif_formats)
+ if (BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif) & sndif_formats)
mask |= pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa);
return mask;
--
2.20.1
3
2
[alsa-devel] [PATCH RFC V3] ALSA: usb-audio: Scarlett Gen 2 mixer interface
by Geoffrey D. Bennett 30 Jun '19
by Geoffrey D. Bennett 30 Jun '19
30 Jun '19
Add mixer quirk for the Focusrite Scarlett 6i6, 18i8, and 18i20 Gen 2
audio interfaces. Although the interfaces are USB compliant,
additional input/output level controls and hardware routing/mixing
functionality are available using proprietary USB requests.
Signed-off-by: Geoffrey D. Bennett <g(a)b4.vu>
---
Hi all,
This patch adds the following controls for the Scarlett 6i6, 18i8, and
18i20 Gen 2:
- Master volume knob indicator (18i20 only)
- Mute and dim switches (18i20 only)
- Volume controls for the analogue HW outputs
- HW/SW volume switches for the 10 analogue HW outputs (18i20 only)
- Line Level/Instrument Level and Pad controls (6i6 and 18i8 only)
- Output mux (where the sound for the HW outputs comes from; defaults
to PCM outputs)
- Capture mux (where the sound for PCM recording comes from; defaults
to HW inputs)
- Matrix mux (where the sound going into the mixer comes from; 18
inputs default off)
- Mixer matrix (18 inputs * 10 outputs = 180 controls)
- Level meters
Changes since v1:
- Add support for the Scarlett 18i8 Gen 2
- Save configuration parameters to NVRAM
- Implemented feedback from Takashi's email 24/Apr/2019
- Moved private field from struct snd_usb_audio to struct
usb_mixer_interface
- Added timer and buffer fields to struct usb_mixer_interface
- Other small code fixes/cleanups/improvements
Changes since v2:
- Add support for the Scarlett 6i6 Gen 2
- Add support for controlling the mute/dim switches (18i20 only)
- Implemented feedback from Takashi's email 29/Apr/2019: One pointer
field private_data and one function pointer private_free. Replaced
timer with delayed work (thanks for that suggestion; it makes the
code a *lot* simpler!).
- The new functionality is disabled by default as there were reports
of some Gen 2 devices not responding to the initialisation sequence
and hanging. Added module parameter "scarlett_gen2_mixer_enable".
If this parameter is not set, logs a message "Focusrite Scarlett Gen
2 Mixer Driver disabled; use options snd_usb_audio
scarlett_gen2_mixer_enable=1 to enable and report any issues to
g(a)b4.vu".
Outstanding issues/questions:
- Takashi, you wrote last time "Also, the delayed work needs to be
canceled or flushed at PM suspend callback as well as
disconnecting." I'm not sure why it should be cancelled; is it not
okay to do on resume? But I tested suspending while the delayed work
was waiting and it seemed like the delayed work was cancelled
anyway. Probably better to do a flush on suspend though; should this
be done through a hook like:
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 2444737a14b2..1572a89267c6 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -3547,6 +3547,8 @@ static int snd_usb_mixer_activate(struct usb_mixer_interface *mixer)
int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer)
{
snd_usb_mixer_inactivate(mixer);
+ if (mixer->private_suspend)
+ mixer->private_suspend(mixer);
return 0;
}
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index fa6e216a06f0..d94c688c65f7 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -28,6 +28,7 @@ struct usb_mixer_interface {
void *private_data;
void (*private_free)(struct usb_mixer_interface *private_data);
+ void (*private_suspend)(struct usb_mixer_interface *);
};
#define MAX_CHANNELS 16 /* max logical channels */
?
Thanks,
Geoffrey.
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index d330f74c90e6..8cf7081e17cb 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -11,6 +11,7 @@ snd-usb-audio-objs := card.o \
mixer.o \
mixer_quirks.o \
mixer_scarlett.o \
+ mixer_scarlett_gen2.o \
mixer_us16x08.o \
pcm.o \
power.o \
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 53dccbfe392b..2444737a14b2 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -3518,6 +3518,8 @@ void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer)
usb_kill_urb(mixer->urb);
if (mixer->rc_urb)
usb_kill_urb(mixer->rc_urb);
+ if (mixer->private_free)
+ mixer->private_free(mixer);
mixer->disconnected = true;
}
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 3d12af8bf191..fa6e216a06f0 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -25,6 +25,9 @@ struct usb_mixer_interface {
u8 rc_buffer[6];
bool disconnected;
+
+ void *private_data;
+ void (*private_free)(struct usb_mixer_interface *);
};
#define MAX_CHANNELS 16 /* max logical channels */
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index a751a18ca4c2..1ae00a1e6c47 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -45,6 +45,7 @@
#include "mixer.h"
#include "mixer_quirks.h"
#include "mixer_scarlett.h"
+#include "mixer_scarlett_gen2.h"
#include "mixer_us16x08.h"
#include "helper.h"
@@ -2271,6 +2272,12 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
err = snd_scarlett_controls_create(mixer);
break;
+ case USB_ID(0x1235, 0x8203): /* Focusrite Scarlett 6i6 2nd Gen */
+ case USB_ID(0x1235, 0x8204): /* Focusrite Scarlett 18i8 2nd Gen */
+ case USB_ID(0x1235, 0x8201): /* Focusrite Scarlett 18i20 2nd Gen */
+ err = snd_scarlett_gen2_controls_create(mixer);
+ break;
+
case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
err = snd_soundblaster_e1_switch_create(mixer);
break;
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c
new file mode 100644
index 000000000000..05a8e47a85f4
--- /dev/null
+++ b/sound/usb/mixer_scarlett_gen2.c
@@ -0,0 +1,2078 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Focusrite Scarlett 6i6/18i8/18i20 Gen 2 Driver for ALSA
+ *
+ * Copyright (c) 2018-2019 by Geoffrey D. Bennett <g at b4.vu>
+ *
+ * Based on the Scarlett (Gen 1) Driver for ALSA:
+ *
+ * Copyright (c) 2013 by Tobias Hoffmann
+ * Copyright (c) 2013 by Robin Gareus <robin at gareus.org>
+ * Copyright (c) 2002 by Takashi Iwai <tiwai at suse.de>
+ * Copyright (c) 2014 by Chris J Arges <chris.j.arges at canonical.com>
+ *
+ * Many codes borrowed from audio.c by
+ * Alan Cox (alan at lxorguk.ukuu.org.uk)
+ * Thomas Sailer (sailer at ife.ee.ethz.ch)
+ *
+ * Code cleanup:
+ * David Henningsson <david.henningsson at canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* Mixer Interface for the Focusrite Scarlett 6i6/18i8/18i20 Gen 2 audio
+ * interface. Based on the Gen 1 driver and rewritten.
+ */
+
+/* The protocol was reverse engineered by looking at the communication
+ * between Focusrite Control 2.3.4 and the Focusrite(R) Scarlett 18i20
+ * (firmware 1083) using usbmon in July-August 2018.
+ *
+ * Scarlett 18i8 support added in April 2019.
+ *
+ * Scarlett 6i6 support added in June 2019 (thanks to Martin Wittmann
+ * for providing usbmon output and testing).
+ *
+ * This ALSA mixer gives access to:
+ * - input, output, mixer-matrix muxes
+ * - 18x10 mixer-matrix gain stages
+ * - gain/volume controls
+ * - level meters
+ * - line/inst level and pad controls
+ *
+ * <ditaa>
+ * /--------------\ 18chn 20chn /--------------\
+ * | Hardware in +--+------\ /-------------+--+ ALSA PCM out |
+ * \--------------/ | | | | \--------------/
+ * | | | /-----\ |
+ * | | | | | |
+ * | v v v | |
+ * | +---------------+ | |
+ * | \ Matrix Mux / | |
+ * | +-----+-----+ | |
+ * | | | |
+ * | |18chn | |
+ * | | | |
+ * | | 10chn| |
+ * | v | |
+ * | +------------+ | |
+ * | | Mixer | | |
+ * | | Matrix | | |
+ * | | | | |
+ * | | 18x10 Gain | | |
+ * | | stages | | |
+ * | +-----+------+ | |
+ * | | | |
+ * |18chn |10chn | |20chn
+ * | | | |
+ * | +----------/ |
+ * | | |
+ * v v v
+ * ===========================
+ * +---------------+ +--—------------+
+ * \ Output Mux / \ Capture Mux /
+ * +---+---+---+ +-----+-----+
+ * | | |
+ * 10chn| | |18chn
+ * | | |
+ * /--------------\ | | | /--------------\
+ * | S/PDIF, ADAT |<--/ |10chn \-->| ALSA PCM in |
+ * | Hardware out | | \--------------/
+ * \--------------/ |
+ * v
+ * +-------------+ Software gain per channel.
+ * | Master Gain |<-- 18i20 only: Switch per channel
+ * +------+------+ to select HW or SW gain control.
+ * |
+ * |10chn
+ * /--------------\ |
+ * | Analogue |<------/
+ * | Hardware out |
+ * \--------------/
+ * </ditaa>
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/moduleparam.h>
+
+#include <sound/control.h>
+#include <sound/tlv.h>
+
+#include "usbaudio.h"
+#include "mixer.h"
+#include "helper.h"
+
+#include "mixer_scarlett_gen2.h"
+
+static int scarlett_gen2_mixer_enable = 0;
+module_param(scarlett_gen2_mixer_enable, int, 0444);
+MODULE_PARM_DESC(scarlett_gen2_mixer_enable,
+ "Focusrite Scarlett Gen 2 Mixer Driver Enable");
+
+/* some gui mixers can't handle negative ctl values */
+#define SCARLETT_VOLUME_BIAS 127
+
+/* mixer range from -80dB to +6dB in 0.5dB steps */
+#define SCARLETT_MIXER_MIN_DB -80
+#define SCARLETT_MIXER_BIAS (-SCARLETT_MIXER_MIN_DB * 2)
+#define SCARLETT_MIXER_MAX_DB 6
+#define SCARLETT_MIXER_MAX_VALUE \
+ ((SCARLETT_MIXER_MAX_DB - SCARLETT_MIXER_MIN_DB) * 2)
+
+/* map from (dB + 80) * 2 to mixer value
+ * for dB in 0 .. 172: int(8192 * pow(10, ((dB - 160) / 2 / 20)))
+ */
+static const u16 scarlett2_mixer_values[173] = {
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
+ 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8,
+ 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 23, 24, 25, 27, 29, 30, 32, 34, 36, 38, 41, 43, 46, 48, 51,
+ 54, 57, 61, 65, 68, 73, 77, 81, 86, 91, 97, 103, 109, 115,
+ 122, 129, 137, 145, 154, 163, 173, 183, 194, 205, 217, 230,
+ 244, 259, 274, 290, 307, 326, 345, 365, 387, 410, 434, 460,
+ 487, 516, 547, 579, 614, 650, 689, 730, 773, 819, 867, 919,
+ 973, 1031, 1092, 1157, 1225, 1298, 1375, 1456, 1543, 1634,
+ 1731, 1833, 1942, 2057, 2179, 2308, 2445, 2590, 2744, 2906,
+ 3078, 3261, 3454, 3659, 3876, 4105, 4349, 4606, 4879, 5168,
+ 5475, 5799, 6143, 6507, 6892, 7301, 7733, 8192, 8677, 9191,
+ 9736, 10313, 10924, 11571, 12257, 12983, 13752, 14567, 15430,
+ 16345
+};
+
+/* Maximum number of analogue outputs */
+#define SCARLETT2_ANALOGUE_MAX 10
+
+/* Maximum number of level and pad switches */
+#define SCARLETT2_LEVEL_SWITCH_MAX 2
+#define SCARLETT2_PAD_SWITCH_MAX 4
+
+/* Maximum number of inputs to the mixer */
+#define SCARLETT2_INPUT_MIX_MAX 18
+
+/* Maximum number of outputs from the mixer */
+#define SCARLETT2_OUTPUT_MIX_MAX 10
+
+/* Maximum size of the data in the USB mux assignment message:
+ * 18 inputs, 20 outputs, 18 matrix inputs, 8 spare
+ */
+#define SCARLETT2_MUX_MAX 64
+
+/* Number of meters:
+ * 18 inputs, 20 outputs, 18 matrix inputs
+ */
+#define SCARLETT2_NUM_METERS 56
+
+/* Hardware port types:
+ * - None (no input to mux)
+ * - Analogue I/O
+ * - S/PDIF I/O
+ * - ADAT I/O
+ * - Mixer I/O
+ * - PCM I/O
+ */
+enum {
+ SCARLETT2_PORT_TYPE_NONE = 0,
+ SCARLETT2_PORT_TYPE_ANALOGUE = 1,
+ SCARLETT2_PORT_TYPE_SPDIF = 2,
+ SCARLETT2_PORT_TYPE_ADAT = 3,
+ SCARLETT2_PORT_TYPE_MIX = 4,
+ SCARLETT2_PORT_TYPE_PCM = 5,
+ SCARLETT2_PORT_TYPE_COUNT = 6,
+};
+
+/* Count of total I/O and number available at each sample rate */
+enum {
+ SCARLETT2_PORT_IN = 0,
+ SCARLETT2_PORT_OUT = 1,
+ SCARLETT2_PORT_OUT_44 = 2,
+ SCARLETT2_PORT_OUT_88 = 3,
+ SCARLETT2_PORT_OUT_176 = 4,
+ SCARLETT2_PORT_DIRECTIONS = 5,
+};
+
+/* Hardware buttons on the 18i20 */
+#define SCARLETT2_BUTTON_MAX 2
+
+static const char *const scarlett2_button_names[SCARLETT2_BUTTON_MAX] = {
+ "Mute", "Dim"
+};
+
+/* Description of each hardware port type:
+ * - id: hardware ID for this port type
+ * - num: number of sources/destinations of this port type
+ * - src_descr: printf format string for mux input selections
+ * - src_num_offset: added to channel number for the fprintf
+ * - dst_descr: printf format string for mixer controls
+ */
+struct scarlett2_ports {
+ u16 id;
+ int num[SCARLETT2_PORT_DIRECTIONS];
+ const char * const src_descr;
+ int src_num_offset;
+ const char * const dst_descr;
+};
+
+struct scarlett2_device_info {
+ u8 line_out_hw_vol; /* line out hw volume is sw controlled */
+ u8 button_count; /* number of buttons */
+ u8 level_input_count; /* inputs with level selectable */
+ u8 pad_input_count; /* inputs with pad selectable */
+ const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX];
+ struct scarlett2_ports ports[SCARLETT2_PORT_TYPE_COUNT];
+};
+
+struct scarlett2_mixer_data {
+ struct usb_mixer_interface *mixer;
+ struct mutex usb_mutex; /* prevent sending concurrent USB requests */
+ struct mutex data_mutex; /* lock access to this data */
+ struct delayed_work work;
+ const struct scarlett2_device_info *info;
+ int num_mux_srcs;
+ u16 scarlett2_seq;
+ u8 vol_updated;
+ u8 master_vol;
+ u8 vol[SCARLETT2_ANALOGUE_MAX];
+ u8 vol_sw_hw_switch[SCARLETT2_ANALOGUE_MAX];
+ u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX];
+ u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX];
+ u8 buttons[SCARLETT2_BUTTON_MAX];
+ struct snd_kcontrol *master_vol_ctl;
+ struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX];
+ struct snd_kcontrol *button_ctls[SCARLETT2_BUTTON_MAX];
+ u8 mux[SCARLETT2_MUX_MAX];
+ u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX];
+};
+
+static void snd_scarlett_gen2_private_free(struct usb_mixer_interface *);
+
+/*** Model-specific data ***/
+
+static const struct scarlett2_device_info s6i6_gen2_info = {
+ /* The first two analogue inputs can be switched between line
+ * and instrument levels.
+ */
+ .level_input_count = 2,
+
+ /* The first two analogue inputs have an optional pad. */
+ .pad_input_count = 2,
+
+ .line_out_descrs = {
+ "Monitor L",
+ "Monitor R",
+ "Headphones L",
+ "Headphones R",
+ },
+
+ .ports = {
+ {
+ .id = 0x000,
+ .num = { 1, 0, 8, 8, 8 },
+ .src_descr = "Off",
+ .src_num_offset = 0,
+ },
+ {
+ .id = 0x080,
+ .num = { 4, 4, 4, 4, 4 },
+ .src_descr = "Analogue %d",
+ .src_num_offset = 1,
+ .dst_descr = "Analogue Output %02d Playback"
+ },
+ {
+ .id = 0x180,
+ .num = { 2, 2, 2, 2, 2 },
+ .src_descr = "S/PDIF %d",
+ .src_num_offset = 1,
+ .dst_descr = "S/PDIF Output %d Playback"
+ },
+ {
+ .id = 0x300,
+ .num = { 10, 18, 18, 18, 18 },
+ .src_descr = "Mix %c",
+ .src_num_offset = 65,
+ .dst_descr = "Mixer Input %02d Capture"
+ },
+ {
+ .id = 0x600,
+ .num = { 6, 6, 6, 6, 6 },
+ .src_descr = "PCM %d",
+ .src_num_offset = 1,
+ .dst_descr = "PCM %02d Capture"
+ },
+ },
+};
+
+static const struct scarlett2_device_info s18i8_gen2_info = {
+ /* The first two analogue inputs can be switched between line
+ * and instrument levels.
+ */
+ .level_input_count = 2,
+
+ /* The first four analogue inputs have an optional pad. */
+ .pad_input_count = 4,
+
+ .line_out_descrs = {
+ "Monitor L",
+ "Monitor R",
+ "Headphones 1 L",
+ "Headphones 1 R",
+ "Headphones 2 L",
+ "Headphones 2 R",
+ },
+
+ .ports = {
+ {
+ .id = 0x000,
+ .num = { 1, 0, 8, 8, 4 },
+ .src_descr = "Off",
+ .src_num_offset = 0,
+ },
+ {
+ .id = 0x080,
+ .num = { 8, 6, 6, 6, 6 },
+ .src_descr = "Analogue %d",
+ .src_num_offset = 1,
+ .dst_descr = "Analogue Output %02d Playback"
+ },
+ {
+ /* S/PDIF outputs aren't available at 192KHz
+ * but are included in the USB mux I/O
+ * assignment message anyway
+ */
+ .id = 0x180,
+ .num = { 2, 2, 2, 2, 2 },
+ .src_descr = "S/PDIF %d",
+ .src_num_offset = 1,
+ .dst_descr = "S/PDIF Output %d Playback"
+ },
+ {
+ .id = 0x200,
+ .num = { 8, 0, 0, 0, 0 },
+ .src_descr = "ADAT %d",
+ .src_num_offset = 1,
+ },
+ {
+ .id = 0x300,
+ .num = { 10, 18, 18, 18, 18 },
+ .src_descr = "Mix %c",
+ .src_num_offset = 65,
+ .dst_descr = "Mixer Input %02d Capture"
+ },
+ {
+ .id = 0x600,
+ .num = { 20, 18, 18, 14, 10 },
+ .src_descr = "PCM %d",
+ .src_num_offset = 1,
+ .dst_descr = "PCM %02d Capture"
+ },
+ },
+};
+
+static const struct scarlett2_device_info s18i20_gen2_info = {
+ /* The analogue line outputs on the 18i20 can be switched
+ * between software and hardware volume control
+ */
+ .line_out_hw_vol = 1,
+
+ /* Mute and dim buttons */
+ .button_count = 2,
+
+ .line_out_descrs = {
+ "Monitor L",
+ "Monitor R",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Headphones 1 L",
+ "Headphones 1 R",
+ "Headphones 2 L",
+ "Headphones 2 R",
+ },
+
+ .ports = {
+ {
+ .id = 0x000,
+ .num = { 1, 0, 8, 8, 6 },
+ .src_descr = "Off",
+ .src_num_offset = 0,
+ },
+ {
+ .id = 0x080,
+ .num = { 8, 10, 10, 10, 10 },
+ .src_descr = "Analogue %d",
+ .src_num_offset = 1,
+ .dst_descr = "Analogue Output %02d Playback"
+ },
+ {
+ /* S/PDIF outputs aren't available at 192KHz
+ * but are included in the USB mux I/O
+ * assignment message anyway
+ */
+ .id = 0x180,
+ .num = { 2, 2, 2, 2, 2 },
+ .src_descr = "S/PDIF %d",
+ .src_num_offset = 1,
+ .dst_descr = "S/PDIF Output %d Playback"
+ },
+ {
+ .id = 0x200,
+ .num = { 8, 8, 8, 4, 0 },
+ .src_descr = "ADAT %d",
+ .src_num_offset = 1,
+ .dst_descr = "ADAT Output %d Playback"
+ },
+ {
+ .id = 0x300,
+ .num = { 10, 18, 18, 18, 18 },
+ .src_descr = "Mix %c",
+ .src_num_offset = 65,
+ .dst_descr = "Mixer Input %02d Capture"
+ },
+ {
+ .id = 0x600,
+ .num = { 20, 18, 18, 14, 10 },
+ .src_descr = "PCM %d",
+ .src_num_offset = 1,
+ .dst_descr = "PCM %02d Capture"
+ },
+ },
+};
+
+/* get the starting port index number for a given port type/direction */
+static int scarlett2_get_port_start_num(const struct scarlett2_ports *ports,
+ int direction, int port_type)
+{
+ int i, num = 0;
+
+ for (i = 0; i < port_type; i++)
+ num += ports[i].num[direction];
+
+ return num;
+}
+
+/*** USB Interactions ***/
+
+/* Vendor-Specific Interface, Endpoint, MaxPacketSize, Interval */
+#define SCARLETT2_USB_VENDOR_SPECIFIC_INTERFACE 5
+#define SCARLETT2_USB_INTERRUPT_ENDPOINT 4
+#define SCARLETT2_USB_INTERRUPT_MAX_DATA 64
+#define SCARLETT2_USB_INTERRUPT_INTERVAL 3
+
+/* Interrupt flags for volume and mute/dim button changes */
+#define SCARLETT2_USB_INTERRUPT_VOL_CHANGE 0x400000
+#define SCARLETT2_USB_INTERRUPT_BUTTON_CHANGE 0x200000
+
+/* Commands for sending/receiving requests/responses */
+#define SCARLETT2_USB_VENDOR_SPECIFIC_CMD_REQ 2
+#define SCARLETT2_USB_VENDOR_SPECIFIC_CMD_RESP 3
+
+#define SCARLETT2_USB_INIT_SEQ 0x00000000
+#define SCARLETT2_USB_GET_METER_LEVELS 0x00001001
+#define SCARLETT2_USB_SET_MIX 0x00002002
+#define SCARLETT2_USB_SET_MUX 0x00003002
+#define SCARLETT2_USB_GET_DATA 0x00800000
+#define SCARLETT2_USB_SET_DATA 0x00800001
+#define SCARLETT2_USB_DATA_CMD 0x00800002
+#define SCARLETT2_USB_CONFIG_SAVE 6
+
+#define SCARLETT2_USB_VOLUME_STATUS_OFFSET 0x31
+#define SCARLETT2_USB_METER_LEVELS_GET_MAGIC 1
+
+/* volume status is read together (matches scarlett2_config_items[]) */
+struct scarlett2_usb_volume_status {
+ /* mute & dim buttons */
+ u8 buttons[SCARLETT2_BUTTON_MAX];
+
+ u8 pad1;
+
+ /* software volume setting */
+ s16 sw_vol[SCARLETT2_ANALOGUE_MAX];
+
+ /* actual volume of output inc. dim (-18dB) */
+ s16 hw_vol[SCARLETT2_ANALOGUE_MAX];
+
+ u8 pad2[SCARLETT2_ANALOGUE_MAX];
+
+ /* sw (0) or hw (1) controlled */
+ u8 sw_hw_switch[SCARLETT2_ANALOGUE_MAX];
+
+ u8 pad3[6];
+
+ /* front panel volume knob */
+ s16 master_vol;
+} __packed;
+
+/* Configuration parameters that can be read and written */
+enum {
+ SCARLETT2_CONFIG_BUTTONS = 0,
+ SCARLETT2_CONFIG_LINE_OUT_VOLUME = 1,
+ SCARLETT2_CONFIG_SW_HW_SWITCH = 2,
+ SCARLETT2_CONFIG_LEVEL_SWITCH = 3,
+ SCARLETT2_CONFIG_PAD_SWITCH = 4,
+ SCARLETT2_CONFIG_COUNT = 5
+};
+
+/* Location, size, and activation command number for the configuration
+ * parameters
+ */
+struct scarlett2_config {
+ u8 offset;
+ u8 size;
+ u8 activate;
+};
+
+static const struct scarlett2_config
+ scarlett2_config_items[SCARLETT2_CONFIG_COUNT] = {
+ /* Mute/Dim Buttons */
+ {
+ .offset = 0x31,
+ .size = 1,
+ .activate = 2
+ },
+
+ /* Line Out Volume */
+ {
+ .offset = 0x34,
+ .size = 2,
+ .activate = 1
+ },
+
+ /* SW/HW Volume Switch */
+ {
+ .offset = 0x66,
+ .size = 1,
+ .activate = 3
+ },
+
+ /* Level Switch */
+ {
+ .offset = 0x7c,
+ .size = 1,
+ .activate = 7
+ },
+
+ /* Pad Switch */
+ {
+ .offset = 0x84,
+ .size = 1,
+ .activate = 8
+ }
+};
+
+/* proprietary request/response format */
+struct scarlett2_usb_packet {
+ u32 cmd;
+ u16 size;
+ u16 seq;
+ u32 error;
+ u32 pad;
+ u8 data[];
+};
+
+#define SCARLETT2_USB_PACKET_LEN (sizeof(struct scarlett2_usb_packet))
+
+static void scarlett2_fill_request_header(struct scarlett2_mixer_data *private,
+ struct scarlett2_usb_packet *req,
+ u32 cmd, u16 req_size)
+{
+ /* sequence must go up by 1 for each request */
+ u16 seq = private->scarlett2_seq++;
+
+ req->cmd = cpu_to_le32(cmd);
+ req->size = cpu_to_le16(req_size);
+ req->seq = cpu_to_le16(seq);
+ req->error = 0;
+ req->pad = 0;
+}
+
+/* Send a proprietary format request to the Scarlett interface */
+static int scarlett2_usb(
+ struct usb_mixer_interface *mixer, u32 cmd,
+ void *req_data, u16 req_size, void *resp_data, u16 resp_size)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ u16 req_buf_size = sizeof(struct scarlett2_usb_packet) + req_size;
+ u16 resp_buf_size = sizeof(struct scarlett2_usb_packet) + resp_size;
+
+ struct scarlett2_usb_packet *req = 0, *resp = 0;
+
+ int err = 0;
+
+ mutex_lock(&private->usb_mutex);
+
+ /* build request message and send it */
+
+ req = kmalloc(req_buf_size, GFP_KERNEL);
+ if (!req) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ scarlett2_fill_request_header(private, req, cmd, req_size);
+
+ if (req_size)
+ memcpy(req->data, req_data, req_size);
+
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0),
+ SCARLETT2_USB_VENDOR_SPECIFIC_CMD_REQ,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ 0,
+ SCARLETT2_USB_VENDOR_SPECIFIC_INTERFACE,
+ req,
+ req_buf_size);
+
+ if (err != req_buf_size) {
+ usb_audio_err(
+ mixer->chip,
+ "Scarlett Gen 2 USB request result cmd %x was %d\n",
+ cmd, err);
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ /* send a second message to get the response */
+
+ resp = kmalloc(resp_buf_size, GFP_KERNEL);
+ if (!resp) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0),
+ SCARLETT2_USB_VENDOR_SPECIFIC_CMD_RESP,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ 0,
+ SCARLETT2_USB_VENDOR_SPECIFIC_INTERFACE,
+ resp,
+ resp_buf_size);
+
+ /* validate the response */
+
+ if (err != resp_buf_size) {
+ usb_audio_err(
+ mixer->chip,
+ "Scarlett Gen 2 USB response result cmd %x was %d\n",
+ cmd, err);
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ if (resp->cmd != req->cmd ||
+ resp->seq != req->seq ||
+ resp_size != le16_to_cpu(resp->size) ||
+ resp->error ||
+ resp->pad) {
+ usb_audio_err(
+ mixer->chip,
+ "Scarlett Gen 2 USB invalid response; "
+ "cmd tx/rx %d/%d seq %d/%d size %d/%d "
+ "error %d pad %d\n",
+ le16_to_cpu(req->cmd), le16_to_cpu(resp->cmd),
+ le16_to_cpu(req->seq), le16_to_cpu(resp->seq),
+ resp_size, le16_to_cpu(resp->size),
+ le16_to_cpu(resp->error),
+ le16_to_cpu(resp->pad));
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ if (resp_size > 0)
+ memcpy(resp_data, resp->data, resp_size);
+
+unlock:
+ kfree(req);
+ kfree(resp);
+ mutex_unlock(&private->usb_mutex);
+ return err;
+}
+
+/* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */
+static void scarlett2_config_save(struct work_struct *work)
+{
+ struct scarlett2_mixer_data *private =
+ container_of(work, struct scarlett2_mixer_data, work.work);
+ struct usb_mixer_interface *mixer = private->mixer;
+ u32 req = cpu_to_le32(SCARLETT2_USB_CONFIG_SAVE);
+
+ scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD,
+ &req, sizeof(u32),
+ NULL, 0);
+}
+
+/* Send a USB message to set a configuration parameter (volume level,
+ * sw/hw volume switch, line/inst level switch, or pad switch)
+ */
+static int scarlett2_usb_set_config(
+ struct usb_mixer_interface *mixer,
+ int config_item_num, int index, int value)
+{
+ const struct scarlett2_config config_item =
+ scarlett2_config_items[config_item_num];
+ struct {
+ u32 offset;
+ u32 bytes;
+ s32 value;
+ } __packed req;
+ u32 req2;
+ int err;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ /* Send the configuration parameter data */
+ req.offset = cpu_to_le32(config_item.offset + index * config_item.size);
+ req.bytes = cpu_to_le32(config_item.size);
+ req.value = cpu_to_le32(value);
+ err = scarlett2_usb(mixer, SCARLETT2_USB_SET_DATA,
+ &req, sizeof(u32) * 2 + config_item.size,
+ NULL, 0);
+ if (err < 0)
+ return err;
+
+ /* Activate the change */
+ req2 = cpu_to_le32(config_item.activate);
+ err = scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD,
+ &req2, sizeof(req2), NULL, 0);
+ if (err < 0)
+ return err;
+
+ /* Schedule the change to be written to NVRAM */
+ schedule_delayed_work(&private->work, msecs_to_jiffies(2000));
+
+ return 0;
+}
+
+/* Send a USB message to get data; result placed in *buf */
+static int scarlett2_usb_get(
+ struct usb_mixer_interface *mixer,
+ int offset, void *buf, int size)
+{
+ struct {
+ u32 offset;
+ u32 size;
+ } __packed req;
+
+ req.offset = cpu_to_le32(offset);
+ req.size = cpu_to_le32(size);
+ return scarlett2_usb(mixer, SCARLETT2_USB_GET_DATA,
+ &req, sizeof(req), buf, size);
+}
+
+/* Send a USB message to get configuration parameters; result placed in *buf */
+static int scarlett2_usb_get_config(
+ struct usb_mixer_interface *mixer,
+ int config_item_num, int count, void *buf)
+{
+ const struct scarlett2_config config_item =
+ scarlett2_config_items[config_item_num];
+ int size = config_item.size * count;
+
+ return scarlett2_usb_get(mixer, config_item.offset, buf, size);
+}
+
+/* Send a USB message to get volume status; result placed in *buf */
+static int scarlett2_usb_get_volume_status(
+ struct usb_mixer_interface *mixer,
+ struct scarlett2_usb_volume_status *buf)
+{
+ return scarlett2_usb_get(mixer, SCARLETT2_USB_VOLUME_STATUS_OFFSET,
+ buf, sizeof(*buf));
+}
+
+/* Send a USB message to set the volumes for all inputs of one mix
+ * (values obtained from private->mix[])
+ */
+static int scarlett2_usb_set_mix(struct usb_mixer_interface *mixer,
+ int mix_num)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ struct {
+ u16 mix_num;
+ u16 data[SCARLETT2_INPUT_MIX_MAX];
+ } __packed req;
+
+ int i, j;
+ int num_mixer_in =
+ info->ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT];
+
+ req.mix_num = cpu_to_le16(mix_num);
+
+ for (i = 0, j = mix_num * num_mixer_in; i < num_mixer_in; i++, j++)
+ req.data[i] = cpu_to_le16(
+ scarlett2_mixer_values[private->mix[j]]
+ );
+
+ return scarlett2_usb(mixer, SCARLETT2_USB_SET_MIX,
+ &req, (num_mixer_in + 1) * sizeof(u16),
+ NULL, 0);
+}
+
+/* Convert a port number index (per info->ports) to a hardware ID */
+static u32 scarlett2_mux_src_num_to_id(const struct scarlett2_ports *ports,
+ int num)
+{
+ int port_type;
+
+ for (port_type = 0;
+ port_type < SCARLETT2_PORT_TYPE_COUNT;
+ port_type++) {
+ if (num < ports[port_type].num[SCARLETT2_PORT_IN])
+ return ports[port_type].id | num;
+ num -= ports[port_type].num[SCARLETT2_PORT_IN];
+ }
+
+ /* Oops */
+ return 0;
+}
+
+/* Send USB messages to set mux inputs */
+static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ const struct scarlett2_ports *ports = info->ports;
+ int rate, port_dir_rate;
+
+ static const int assignment_order[SCARLETT2_PORT_TYPE_COUNT] = {
+ SCARLETT2_PORT_TYPE_PCM,
+ SCARLETT2_PORT_TYPE_ANALOGUE,
+ SCARLETT2_PORT_TYPE_SPDIF,
+ SCARLETT2_PORT_TYPE_ADAT,
+ SCARLETT2_PORT_TYPE_MIX,
+ SCARLETT2_PORT_TYPE_NONE,
+ };
+
+ struct {
+ u16 pad;
+ u16 num;
+ u32 data[SCARLETT2_MUX_MAX];
+ } __packed req;
+
+ req.pad = 0;
+
+ /* mux settings for each rate */
+ for (rate = 0, port_dir_rate = SCARLETT2_PORT_OUT_44;
+ port_dir_rate <= SCARLETT2_PORT_OUT_176;
+ rate++, port_dir_rate++) {
+ int order_num, i, err;
+
+ req.num = cpu_to_le16(rate);
+
+ for (order_num = 0, i = 0;
+ order_num < SCARLETT2_PORT_TYPE_COUNT;
+ order_num++) {
+ int port_type = assignment_order[order_num];
+ int j = scarlett2_get_port_start_num(ports,
+ SCARLETT2_PORT_OUT,
+ port_type);
+ int port_id = ports[port_type].id;
+ int channel;
+
+ for (channel = 0;
+ channel < ports[port_type].num[port_dir_rate];
+ channel++, i++, j++)
+ /* lower 12 bits for the destination and
+ * next 12 bits for the source
+ */
+ req.data[i] = !port_id
+ ? 0
+ : cpu_to_le32(
+ port_id |
+ channel |
+ scarlett2_mux_src_num_to_id(
+ ports, private->mux[j]
+ ) << 12
+ );
+
+ /* skip private->mux[j] entries not output */
+ j += ports[port_type].num[SCARLETT2_PORT_OUT] -
+ ports[port_type].num[port_dir_rate];
+ }
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_SET_MUX,
+ &req, (i + 1) * sizeof(u32),
+ NULL, 0);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Send USB message to get meter levels */
+static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer,
+ u16 *levels)
+{
+ struct {
+ u16 pad;
+ u16 num_meters;
+ u32 magic;
+ } __packed req;
+ u32 resp[SCARLETT2_NUM_METERS];
+ int i, err;
+
+ req.pad = 0;
+ req.num_meters = cpu_to_le16(SCARLETT2_NUM_METERS);
+ req.magic = cpu_to_le32(SCARLETT2_USB_METER_LEVELS_GET_MAGIC);
+ err = scarlett2_usb(mixer, SCARLETT2_USB_GET_METER_LEVELS,
+ &req, sizeof(req), resp, sizeof(resp));
+ if (err < 0)
+ return err;
+
+ /* copy, convert to u16 */
+ for (i = 0; i < SCARLETT2_NUM_METERS; i++)
+ levels[i] = resp[i];
+
+ return 0;
+}
+
+/*** Control Functions ***/
+
+/* helper function to create a new control */
+static int scarlett2_add_new_ctl(struct usb_mixer_interface *mixer,
+ const struct snd_kcontrol_new *ncontrol,
+ int index, int channels, const char *name,
+ struct snd_kcontrol **kctl_return)
+{
+ struct snd_kcontrol *kctl;
+ struct usb_mixer_elem_info *elem;
+ int err;
+
+ elem = kzalloc(sizeof(*elem), GFP_KERNEL);
+ if (!elem)
+ return -ENOMEM;
+
+ elem->head.mixer = mixer;
+ elem->control = index;
+ elem->head.id = index;
+ elem->channels = channels;
+
+ kctl = snd_ctl_new1(ncontrol, elem);
+ if (!kctl) {
+ kfree(elem);
+ return -ENOMEM;
+ }
+ kctl->private_free = snd_usb_mixer_elem_free;
+
+ strlcpy(kctl->id.name, name, sizeof(kctl->id.name));
+
+ err = snd_usb_mixer_add_control(&elem->head, kctl);
+ if (err < 0)
+ return err;
+
+ if (kctl_return)
+ *kctl_return = kctl;
+
+ return 0;
+}
+
+/*** Analogue Line Out Volume Controls ***/
+
+/* Update hardware volume controls after receiving notification that
+ * they have changed
+ */
+static int scarlett2_update_volumes(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_ports *ports = private->info->ports;
+ struct scarlett2_usb_volume_status volume_status;
+ int num_line_out =
+ ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];
+ int err, i;
+
+ private->vol_updated = 0;
+
+ err = scarlett2_usb_get_volume_status(mixer, &volume_status);
+ if (err < 0)
+ return err;
+
+ private->master_vol = clamp(
+ volume_status.master_vol + SCARLETT_VOLUME_BIAS,
+ 0, SCARLETT_VOLUME_BIAS);
+
+ for (i = 0; i < num_line_out; i++) {
+ if (private->vol_sw_hw_switch[i])
+ private->vol[i] = private->master_vol;
+ }
+
+ for (i = 0; i < private->info->button_count; i++)
+ private->buttons[i] = !!volume_status.buttons[i];
+
+ return 0;
+}
+
+static int scarlett2_volume_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = elem->channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SCARLETT_VOLUME_BIAS;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int scarlett2_master_volume_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ if (private->vol_updated) {
+ mutex_lock(&private->data_mutex);
+ scarlett2_update_volumes(mixer);
+ mutex_unlock(&private->data_mutex);
+ }
+
+ ucontrol->value.integer.value[0] = private->master_vol;
+ return 0;
+}
+
+static int scarlett2_volume_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ int index = elem->control;
+
+ if (private->vol_updated) {
+ mutex_lock(&private->data_mutex);
+ scarlett2_update_volumes(mixer);
+ mutex_unlock(&private->data_mutex);
+ }
+
+ ucontrol->value.integer.value[0] = private->vol[index];
+ return 0;
+}
+
+static int scarlett2_volume_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ cancel_delayed_work_sync(&private->work);
+ mutex_lock(&private->data_mutex);
+
+ oval = private->vol[index];
+ val = ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->vol[index] = val;
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME,
+ index, val - SCARLETT_VOLUME_BIAS);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const DECLARE_TLV_DB_MINMAX(
+ db_scale_scarlett_gain, -SCARLETT_VOLUME_BIAS * 100, 0
+);
+
+static const struct snd_kcontrol_new scarlett2_master_volume_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_volume_ctl_info,
+ .get = scarlett2_master_volume_ctl_get,
+ .private_value = 0, /* max value */
+ .tlv = { .p = db_scale_scarlett_gain }
+};
+
+static const struct snd_kcontrol_new scarlett2_line_out_volume_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_volume_ctl_info,
+ .get = scarlett2_volume_ctl_get,
+ .put = scarlett2_volume_ctl_put,
+ .private_value = 0, /* max value */
+ .tlv = { .p = db_scale_scarlett_gain }
+};
+
+/*** HW/SW Volume Switch Controls ***/
+
+static int scarlett2_sw_hw_enum_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[2] = {
+ "SW", "HW"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, values);
+}
+
+static int scarlett2_sw_hw_enum_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.enumerated.item[0] =
+ private->vol_sw_hw_switch[elem->control];
+ return 0;
+}
+
+static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ cancel_delayed_work_sync(&private->work);
+ mutex_lock(&private->data_mutex);
+
+ oval = private->vol_sw_hw_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->vol_sw_hw_switch[index] = val;
+
+ /* Change access mode to RO (hardware controlled volume)
+ * or RW (software controlled volume)
+ */
+ if (val)
+ private->vol_ctls[index]->vd[0].access &=
+ ~SNDRV_CTL_ELEM_ACCESS_WRITE;
+ else
+ private->vol_ctls[index]->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_WRITE;
+
+ /* Reset volume to master volume */
+ private->vol[index] = private->master_vol;
+
+ /* Set SW volume to current HW volume */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME,
+ index, private->master_vol - SCARLETT_VOLUME_BIAS);
+ if (err < 0)
+ goto unlock;
+
+ /* Notify of RO/RW change */
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->vol_ctls[index]->id);
+
+ /* Send SW/HW switch change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SW_HW_SWITCH,
+ index, val);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_sw_hw_enum_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_sw_hw_enum_ctl_info,
+ .get = scarlett2_sw_hw_enum_ctl_get,
+ .put = scarlett2_sw_hw_enum_ctl_put,
+};
+
+/*** Line Level/Instrument Level Switch Controls ***/
+
+static int scarlett2_level_enum_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[2] = {
+ "Line", "Inst"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, values);
+}
+
+static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.enumerated.item[0] =
+ private->level_switch[elem->control];
+ return 0;
+}
+
+static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ cancel_delayed_work_sync(&private->work);
+ mutex_lock(&private->data_mutex);
+
+ oval = private->level_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->level_switch[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LEVEL_SWITCH,
+ index, val);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_level_enum_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_level_enum_ctl_info,
+ .get = scarlett2_level_enum_ctl_get,
+ .put = scarlett2_level_enum_ctl_put,
+};
+
+/*** Pad Switch Controls ***/
+
+static int scarlett2_pad_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.enumerated.item[0] =
+ private->pad_switch[elem->control];
+ return 0;
+}
+
+static int scarlett2_pad_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ cancel_delayed_work_sync(&private->work);
+ mutex_lock(&private->data_mutex);
+
+ oval = private->pad_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->pad_switch[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PAD_SWITCH,
+ index, val);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_pad_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_pad_ctl_get,
+ .put = scarlett2_pad_ctl_put,
+};
+
+/*** Mute/Dim Controls ***/
+
+static int scarlett2_button_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ if (private->vol_updated) {
+ mutex_lock(&private->data_mutex);
+ scarlett2_update_volumes(mixer);
+ mutex_unlock(&private->data_mutex);
+ }
+
+ ucontrol->value.enumerated.item[0] = private->buttons[elem->control];
+ return 0;
+}
+
+static int scarlett2_button_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ cancel_delayed_work_sync(&private->work);
+ mutex_lock(&private->data_mutex);
+
+ oval = private->buttons[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->buttons[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_BUTTONS,
+ index, val);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_button_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_button_ctl_get,
+ .put = scarlett2_button_ctl_put
+};
+
+/*** Create the analogue output controls ***/
+
+static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ const struct scarlett2_ports *ports = info->ports;
+ int num_line_out =
+ ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];
+ int err, i;
+ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ /* Add R/O HW volume control */
+ if (info->line_out_hw_vol) {
+ snprintf(s, sizeof(s), "Master HW Playback Volume");
+ err = scarlett2_add_new_ctl(mixer,
+ &scarlett2_master_volume_ctl,
+ 0, 1, s, &private->master_vol_ctl);
+ if (err < 0)
+ return err;
+ }
+
+ /* Add volume controls */
+ for (i = 0; i < num_line_out; i++) {
+
+ /* Fader */
+ if (info->line_out_descrs[i])
+ snprintf(s, sizeof(s),
+ "Line %02d (%s) Playback Volume",
+ i + 1, info->line_out_descrs[i]);
+ else
+ snprintf(s, sizeof(s),
+ "Line %02d Playback Volume",
+ i + 1);
+ err = scarlett2_add_new_ctl(mixer,
+ &scarlett2_line_out_volume_ctl,
+ i, 1, s, &private->vol_ctls[i]);
+ if (err < 0)
+ return err;
+
+ /* Make the fader read-only if the SW/HW switch is set to HW */
+ if (private->vol_sw_hw_switch[i])
+ private->vol_ctls[i]->vd[0].access &=
+ ~SNDRV_CTL_ELEM_ACCESS_WRITE;
+
+ /* SW/HW Switch */
+ if (info->line_out_hw_vol) {
+ snprintf(s, sizeof(s),
+ "Line Out %02d Volume Control Playback Enum",
+ i + 1);
+ err = scarlett2_add_new_ctl(mixer,
+ &scarlett2_sw_hw_enum_ctl,
+ i, 1, s, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /* Add HW button controls */
+ for (i = 0; i < private->info->button_count; i++) {
+ err = scarlett2_add_new_ctl(mixer, &scarlett2_button_ctl,
+ i, 1, scarlett2_button_names[i],
+ &private->button_ctls[i]);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*** Create the analogue input controls ***/
+
+static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int err, i;
+ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ /* Add input level (line/inst) controls */
+ for (i = 0; i < info->level_input_count; i++) {
+ snprintf(s, sizeof(s), "Line In %d Level Capture Enum", i + 1);
+ err = scarlett2_add_new_ctl(mixer, &scarlett2_level_enum_ctl,
+ i, 1, s, NULL);
+ if (err < 0)
+ return err;
+ }
+
+ /* Add input pad controls */
+ for (i = 0; i < info->pad_input_count; i++) {
+ snprintf(s, sizeof(s), "Line In %d Pad Capture Switch", i + 1);
+ err = scarlett2_add_new_ctl(mixer, &scarlett2_pad_ctl,
+ i, 1, s, NULL);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*** Mixer Volume Controls ***/
+
+static int scarlett2_mixer_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = elem->channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SCARLETT_MIXER_MAX_VALUE;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int scarlett2_mixer_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.integer.value[0] = private->mix[elem->control];
+ return 0;
+}
+
+static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ const struct scarlett2_ports *ports = info->ports;
+ int oval, val, num_mixer_in, mix_num, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ oval = private->mix[elem->control];
+ val = ucontrol->value.integer.value[0];
+ num_mixer_in = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT];
+ mix_num = elem->control / num_mixer_in;
+
+ if (oval == val)
+ goto unlock;
+
+ private->mix[elem->control] = val;
+ err = scarlett2_usb_set_mix(mixer, mix_num);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const DECLARE_TLV_DB_MINMAX(
+ db_scale_scarlett_mixer,
+ SCARLETT_MIXER_MIN_DB * 100,
+ SCARLETT_MIXER_MAX_DB * 100
+);
+
+static const struct snd_kcontrol_new scarlett2_mixer_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_mixer_ctl_info,
+ .get = scarlett2_mixer_ctl_get,
+ .put = scarlett2_mixer_ctl_put,
+ .private_value = SCARLETT_MIXER_MAX_DB, /* max value */
+ .tlv = { .p = db_scale_scarlett_mixer }
+};
+
+static int scarlett2_add_mixer_ctls(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_ports *ports = private->info->ports;
+ int err, i, j;
+ int index;
+ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ int num_inputs = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT];
+ int num_outputs = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_IN];
+
+ for (i = 0, index = 0; i < num_outputs; i++) {
+ for (j = 0; j < num_inputs; j++, index++) {
+ snprintf(s, sizeof(s),
+ "Mix %c Input %02d Playback Volume",
+ 'A' + i, j + 1);
+ err = scarlett2_add_new_ctl(mixer, &scarlett2_mixer_ctl,
+ index, 1, s, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/*** Mux Source Selection Controls ***/
+
+static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+ const struct scarlett2_ports *ports = private->info->ports;
+ unsigned int item = uinfo->value.enumerated.item;
+ int items = private->num_mux_srcs;
+ int port_type;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = elem->channels;
+ uinfo->value.enumerated.items = items;
+
+ if (item >= items)
+ item = uinfo->value.enumerated.item = items - 1;
+
+ for (port_type = 0;
+ port_type < SCARLETT2_PORT_TYPE_COUNT;
+ port_type++) {
+ if (item < ports[port_type].num[SCARLETT2_PORT_IN]) {
+ sprintf(uinfo->value.enumerated.name,
+ ports[port_type].src_descr,
+ item + ports[port_type].src_num_offset);
+ return 0;
+ }
+ item -= ports[port_type].num[SCARLETT2_PORT_IN];
+ }
+
+ return -EINVAL;
+}
+
+static int scarlett2_mux_src_enum_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_mixer_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.enumerated.item[0] = private->mux[elem->control];
+ return 0;
+}
+
+static int scarlett2_mux_src_enum_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ oval = private->mux[index];
+ val = clamp(ucontrol->value.integer.value[0],
+ 0L, private->num_mux_srcs - 1L);
+
+ if (oval == val)
+ goto unlock;
+
+ private->mux[index] = val;
+ err = scarlett2_usb_set_mux(mixer);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_mux_src_enum_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_mux_src_enum_ctl_info,
+ .get = scarlett2_mux_src_enum_ctl_get,
+ .put = scarlett2_mux_src_enum_ctl_put,
+};
+
+static int scarlett2_add_mux_enums(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_ports *ports = private->info->ports;
+ int port_type, channel, i;
+
+ for (i = 0, port_type = 0;
+ port_type < SCARLETT2_PORT_TYPE_COUNT;
+ port_type++) {
+ for (channel = 0;
+ channel < ports[port_type].num[SCARLETT2_PORT_OUT];
+ channel++, i++) {
+ int err;
+ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ const char *const descr = ports[port_type].dst_descr;
+
+ snprintf(s, sizeof(s) - 5, descr, channel + 1);
+ strcat(s, " Enum");
+
+ err = scarlett2_add_new_ctl(mixer,
+ &scarlett2_mux_src_enum_ctl,
+ i, 1, s, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/*** Meter Controls ***/
+
+static int scarlett2_meter_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = elem->channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 4095;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ u16 meter_levels[SCARLETT2_NUM_METERS];
+ int i, err;
+
+ err = scarlett2_usb_get_meter_levels(elem->head.mixer, meter_levels);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < elem->channels; i++)
+ ucontrol->value.integer.value[i] = meter_levels[i];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new scarlett2_meter_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .name = "",
+ .info = scarlett2_meter_ctl_info,
+ .get = scarlett2_meter_ctl_get
+};
+
+static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer)
+{
+ return scarlett2_add_new_ctl(mixer, &scarlett2_meter_ctl,
+ 0, SCARLETT2_NUM_METERS,
+ "Level Meter", NULL);
+}
+
+/*** Initialisation ***/
+
+static int scarlett2_count_mux_srcs(const struct scarlett2_ports *ports)
+{
+ int port_type, count = 0;
+
+ for (port_type = 0;
+ port_type < SCARLETT2_PORT_TYPE_COUNT;
+ port_type++)
+ count += ports[port_type].num[SCARLETT2_PORT_IN];
+
+ return count;
+}
+
+/* Default routing connects PCM outputs and inputs to Analogue,
+ * S/PDIF, then ADAT
+ */
+static void scarlett2_init_routing(u8 *mux,
+ const struct scarlett2_ports *ports)
+{
+ int i, input_num, input_count, port_type;
+ int output_num, output_count, port_type_connect_num;
+
+ static const int connect_order[] = {
+ SCARLETT2_PORT_TYPE_ANALOGUE,
+ SCARLETT2_PORT_TYPE_SPDIF,
+ SCARLETT2_PORT_TYPE_ADAT,
+ -1
+ };
+
+ /* Assign PCM inputs (routing outputs) */
+ output_num = scarlett2_get_port_start_num(ports,
+ SCARLETT2_PORT_OUT,
+ SCARLETT2_PORT_TYPE_PCM);
+ output_count = ports[SCARLETT2_PORT_TYPE_PCM].num[SCARLETT2_PORT_OUT];
+
+ for (port_type = connect_order[port_type_connect_num = 0];
+ port_type >= 0;
+ port_type = connect_order[++port_type_connect_num]) {
+ input_num = scarlett2_get_port_start_num(
+ ports, SCARLETT2_PORT_IN, port_type);
+ input_count = ports[port_type].num[SCARLETT2_PORT_IN];
+ for (i = 0;
+ i < input_count && output_count;
+ i++, output_count--)
+ mux[output_num++] = input_num++;
+ }
+
+ /* Assign PCM outputs (routing inputs) */
+ input_num = scarlett2_get_port_start_num(ports,
+ SCARLETT2_PORT_IN,
+ SCARLETT2_PORT_TYPE_PCM);
+ input_count = ports[SCARLETT2_PORT_TYPE_PCM].num[SCARLETT2_PORT_IN];
+
+ for (port_type = connect_order[port_type_connect_num = 0];
+ port_type >= 0;
+ port_type = connect_order[++port_type_connect_num]) {
+ output_num = scarlett2_get_port_start_num(
+ ports, SCARLETT2_PORT_OUT, port_type);
+ output_count = ports[port_type].num[SCARLETT2_PORT_OUT];
+ for (i = 0;
+ i < output_count && input_count;
+ i++, input_count--)
+ mux[output_num++] = input_num++;
+ }
+}
+
+/* Initialise private data, routing, sequence number */
+static int scarlett2_init_private(struct usb_mixer_interface *mixer,
+ const struct scarlett2_device_info *info)
+{
+ struct scarlett2_mixer_data *private =
+ kzalloc(sizeof(struct scarlett2_mixer_data), GFP_KERNEL);
+
+ if (!private)
+ return -ENOMEM;
+
+ mutex_init(&private->usb_mutex);
+ mutex_init(&private->data_mutex);
+ INIT_DELAYED_WORK(&private->work, scarlett2_config_save);
+ private->info = info;
+ private->num_mux_srcs = scarlett2_count_mux_srcs(info->ports);
+ private->scarlett2_seq = 0;
+ private->mixer = mixer;
+ mixer->private_data = private;
+ mixer->private_free = snd_scarlett_gen2_private_free;
+
+ /* Setup default routing */
+ scarlett2_init_routing(private->mux, info->ports);
+
+ /* Initialise the sequence number used for the proprietary commands */
+ return scarlett2_usb(mixer, SCARLETT2_USB_INIT_SEQ,
+ NULL, 0, NULL, 0);
+}
+
+/* Read line-in config and line-out volume settings on start */
+static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ const struct scarlett2_ports *ports = info->ports;
+ int num_line_out =
+ ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];
+ u8 level_switches[SCARLETT2_LEVEL_SWITCH_MAX];
+ u8 pad_switches[SCARLETT2_PAD_SWITCH_MAX];
+ struct scarlett2_usb_volume_status volume_status;
+ int err, i;
+
+ if (info->level_input_count) {
+ err = scarlett2_usb_get_config(
+ mixer,
+ SCARLETT2_CONFIG_LEVEL_SWITCH,
+ info->level_input_count,
+ level_switches);
+ if (err < 0)
+ return err;
+ for (i = 0; i < info->level_input_count; i++)
+ private->level_switch[i] = level_switches[i];
+ }
+
+ if (info->pad_input_count) {
+ err = scarlett2_usb_get_config(
+ mixer,
+ SCARLETT2_CONFIG_PAD_SWITCH,
+ info->pad_input_count,
+ pad_switches);
+ if (err < 0)
+ return err;
+ for (i = 0; i < info->pad_input_count; i++)
+ private->pad_switch[i] = pad_switches[i];
+ }
+
+ err = scarlett2_usb_get_volume_status(mixer, &volume_status);
+ if (err < 0)
+ return err;
+
+ private->master_vol = clamp(
+ volume_status.master_vol + SCARLETT_VOLUME_BIAS,
+ 0, SCARLETT_VOLUME_BIAS);
+
+ for (i = 0; i < num_line_out; i++) {
+ int volume;
+
+ private->vol_sw_hw_switch[i] =
+ info->line_out_hw_vol
+ && volume_status.sw_hw_switch[i];
+
+ volume = private->vol_sw_hw_switch[i]
+ ? volume_status.master_vol
+ : volume_status.sw_vol[i];
+ volume = clamp(volume + SCARLETT_VOLUME_BIAS,
+ 0, SCARLETT_VOLUME_BIAS);
+ private->vol[i] = volume;
+ }
+
+ for (i = 0; i < info->button_count; i++)
+ private->buttons[i] = !!volume_status.buttons[i];
+
+ return 0;
+}
+
+/* Notify on volume change */
+static void scarlett2_mixer_interrupt_vol_change(
+ struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ const struct scarlett2_ports *ports = private->info->ports;
+ int num_line_out =
+ ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];
+ int i;
+
+ private->vol_updated = 1;
+
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->master_vol_ctl->id);
+
+ for (i = 0; i < num_line_out; i++) {
+ if (!private->vol_sw_hw_switch[i])
+ continue;
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->vol_ctls[i]->id);
+ }
+}
+
+/* Notify on button change */
+static void scarlett2_mixer_interrupt_button_change(
+ struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ int i;
+
+ private->vol_updated = 1;
+
+ for (i = 0; i < private->info->button_count; i++)
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->button_ctls[i]->id);
+}
+
+/* Interrupt callback */
+static void scarlett2_mixer_interrupt(struct urb *urb)
+{
+ struct usb_mixer_interface *mixer = urb->context;
+ int len = urb->actual_length;
+ int ustatus = urb->status;
+ u32 data;
+
+ if (ustatus != 0)
+ goto requeue;
+
+ if (len == 8) {
+ data = le32_to_cpu(*(u32 *)urb->transfer_buffer);
+ if (data & SCARLETT2_USB_INTERRUPT_VOL_CHANGE)
+ scarlett2_mixer_interrupt_vol_change(mixer);
+ if (data & SCARLETT2_USB_INTERRUPT_BUTTON_CHANGE)
+ scarlett2_mixer_interrupt_button_change(mixer);
+ } else {
+ usb_audio_err(mixer->chip,
+ "scarlett mixer interrupt length %d\n", len);
+ }
+
+requeue:
+ if (ustatus != -ENOENT &&
+ ustatus != -ECONNRESET &&
+ ustatus != -ESHUTDOWN) {
+ urb->dev = mixer->chip->dev;
+ usb_submit_urb(urb, GFP_ATOMIC);
+ }
+}
+
+static int scarlett2_mixer_status_create(struct usb_mixer_interface *mixer)
+{
+ void *transfer_buffer;
+
+ if (mixer->urb) {
+ usb_audio_err(mixer->chip,
+ "%s: mixer urb already in use!\n", __func__);
+ return 0;
+ }
+
+ mixer->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!mixer->urb)
+ return -ENOMEM;
+
+ transfer_buffer = kmalloc(SCARLETT2_USB_INTERRUPT_MAX_DATA, GFP_KERNEL);
+ if (!transfer_buffer)
+ return -ENOMEM;
+
+ usb_fill_int_urb(
+ mixer->urb,
+ mixer->chip->dev,
+ usb_rcvintpipe(mixer->chip->dev,
+ SCARLETT2_USB_INTERRUPT_ENDPOINT),
+ transfer_buffer,
+ SCARLETT2_USB_INTERRUPT_MAX_DATA,
+ scarlett2_mixer_interrupt,
+ mixer,
+ SCARLETT2_USB_INTERRUPT_INTERVAL);
+
+ return usb_submit_urb(mixer->urb, GFP_KERNEL);
+}
+
+/* Entry point */
+int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer)
+{
+ const struct scarlett2_device_info *info;
+ int err;
+
+ /* only use UAC_VERSION_2 */
+ if (!mixer->protocol)
+ return 0;
+
+ switch (mixer->chip->usb_id) {
+ case USB_ID(0x1235, 0x8203):
+ info = &s6i6_gen2_info;
+ break;
+ case USB_ID(0x1235, 0x8204):
+ info = &s18i8_gen2_info;
+ break;
+ case USB_ID(0x1235, 0x8201):
+ info = &s18i20_gen2_info;
+ break;
+ default: /* device not (yet) supported */
+ return -EINVAL;
+ }
+
+ if (!scarlett_gen2_mixer_enable) {
+ usb_audio_err(mixer->chip,
+ "Focusrite Scarlett Gen 2 Mixer Driver disabled; use "
+ "options snd_usb_audio scarlett_gen2_mixer_enable=1 "
+ "to enable and report any issues to g(a)b4.vu");
+ return 0;
+ }
+
+ /* Initialise private data, routing, sequence number */
+ err = scarlett2_init_private(mixer, info);
+ if (err < 0)
+ return err;
+
+ /* Read volume levels and controls from the interface */
+ err = scarlett2_read_configs(mixer);
+ if (err < 0)
+ return err;
+
+ /* Create the analogue output controls */
+ err = scarlett2_add_line_out_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ /* Create the analogue input controls */
+ err = scarlett2_add_line_in_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ /* Create the input, output, and mixer mux input selections */
+ err = scarlett2_add_mux_enums(mixer);
+ if (err < 0)
+ return err;
+
+ /* Create the matrix mixer controls */
+ err = scarlett2_add_mixer_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ /* Create the level meter controls */
+ err = scarlett2_add_meter_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ /* Set up the interrupt polling if there are hardware buttons */
+ if (info->button_count) {
+ err = scarlett2_mixer_status_create(mixer);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void snd_scarlett_gen2_private_free(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+
+ cancel_delayed_work_sync(&private->work);
+ kfree(private);
+ mixer->private_data = NULL;
+}
diff --git a/sound/usb/mixer_scarlett_gen2.h b/sound/usb/mixer_scarlett_gen2.h
new file mode 100644
index 000000000000..52e1dad77afd
--- /dev/null
+++ b/sound/usb/mixer_scarlett_gen2.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __USB_MIXER_SCARLETT_GEN2_H
+#define __USB_MIXER_SCARLETT_GEN2_H
+
+int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer);
+
+#endif /* __USB_MIXER_SCARLETT_GEN2_H */
1
0
[alsa-devel] focusrite scarlett 18i20 : Mixer controls with corrupted names for
by Stefan Sauer 29 Jun '19
by Stefan Sauer 29 Jun '19
29 Jun '19
Hi,
when I run
amixer -D hw:4 controls | sort -n -t = -k2
I get the output below
numid=1,iface=MIXER,name='KKKKKKKKKKKKKÃÃÃÃÃÃÃÃÃÃÃ Switch'
numid=2,iface=CARD,name='Internal Validity'
numid=3,iface=CARD,name='S/PDIF Validity'
numid=4,iface=CARD,name='ADAT Validity'
numid=5,iface=MIXER,name='Scarlett 18i20 USB-Sync Clock Source'
numid=6,iface=MIXER,name=' Switch'
numid=7,iface=CARD,name='Keep Interface'
numid=8,iface=MIXER,name='Master Playback Switch'
numid=9,iface=MIXER,name='Master Playback Volume'
numid=10,iface=MIXER,name='Master 1 (Monitor) Playback Switch'
numid=11,iface=MIXER,name='Master 1 (Monitor) Playback Volume'
... lots of extra lines ...
Please note the lines for numid=1 and numid=6. The first one contains some
garbage and the 2nd one look like it should start with another word.
I now added some debug printing here:
https://github.com/torvalds/linux/blob/master/sound/usb/mixer_scarlett.c#L5…
and get output in dmesg:
[ 2971.642137] usb 1-2: Product: Scarlett 18i20 USB
[ 2971.642141] usb 1-2: Manufacturer: Focusrite
[ 2971.709773] new ctrl: name='Master Playback Switch', index=10, offset=1,
num=0: numid=8
[ 2971.709781] new ctrl: name='Master Playback Volume', index=10, offset=2,
num=0: numid=9
[ 2971.709788] new ctrl: name='Master 1 (Monitor) Playback Switch', index=10,
offset=1, num=1: numid=10
So the first 8 controls are added somewhere else. Looks like this is from
mixer.c and after
echo -n 'file sound/usb/mixer.c +p' >/sys/kernel/debug/dynamic_debug/control
I get
[ 4405.855432] usb 1-2: [51] PU [KKKKKKKKKKKKKÃÃÃÃÃÃÃÃÃÃÃ Switch] ch = 1, val = 0/1
[ 4405.856423] usb 1-2: [52] PU [ Switch] ch = 1, val = 0/1
I now added more debug prints into
https://github.com/torvalds/linux/blob/master/sound/usb/mixer.c#L2431
and its the code that calls snd_usb_copy_string_desc()
[ 5750.124123] usb 1-2: nameid=90, len=35
[ 5750.124157] usb 1-2: [51] PU [KKKKKKKKKKKKKÃÃÃÃÃÃÃÃÃÃÃ Switch] ch = 1, val = 0/1
[ 5750.125241] usb 1-2: nameid=82, len=1
[ 5750.125260] usb 1-2: [52] PU [ Switch] ch = 1, val = 0/1
In both cases the returned len seems wrong and the function does not seems to
copy any useful string here. snd_usb_copy_string_desc() is just a wrapper around
usb_string().
Is my hardware returning bogus data in usb descriptors? Can this be address
through some quirks table? Any other ideas?
Thanks!
Stefan
2
8
[alsa-devel] [PATCH - use build platform compiler while cross compilation 1/1] use build compilers in cross compilation setup
by Ding Xiang Fei 29 Jun '19
by Ding Xiang Fei 29 Jun '19
29 Jun '19
This patch is to make the build script always use the build platform
compilers, especially during the cross compilation process, since the
Makefile will pick up the cross compilers instead of the compilers on
the build machine.
Signed-off-by: Ding Xiang Fei <dingxiangfei2009(a)gmail.com>
diff --git a/compile b/compile
new file mode 120000
index 0000000..349ac66
--- /dev/null
+++ b/compile
@@ -0,0 +1 @@
+/nix/store/hwcmvzv53jzws5vnaga16l5dmqvslwkz-automake-1.16.1/share/automake-1.16/compile
\ No newline at end of file
diff --git a/configure.ac b/configure.ac
index 368854e..ee0e242 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,8 @@
AC_PREREQ(2.59)
AC_INIT(alsa-firmware, 1.0.29)
+AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
+AC_PROG_CC_FOR_BUILD
AC_PROG_INSTALL
AC_PROG_LN_S
AC_HEADER_STDC
diff --git a/echoaudio/Makefile.am b/echoaudio/Makefile.am
index 815742f..5ef6086 100644
--- a/echoaudio/Makefile.am
+++ b/echoaudio/Makefile.am
@@ -74,33 +74,42 @@ hotplugfwdir =
hotplugfw_DATA =
endif
-$(firmware_files): fw_writer
- ./fw_writer DSP/LoaderDSP.c loader_dsp.fw
- ./fw_writer DSP/Darla20DSP.c darla20_dsp.fw
- ./fw_writer DSP/Gina20DSP.c gina20_dsp.fw
- ./fw_writer DSP/Layla20DSP.c layla20_dsp.fw
- ./fw_writer ASIC/LaylaASIC.c layla20_asic.fw
- ./fw_writer DSP/Darla24DSP.c darla24_dsp.fw
- ./fw_writer DSP/Gina24DSP.c gina24_301_dsp.fw
- ./fw_writer ASIC/Gina24ASIC.c gina24_301_asic.fw
- ./fw_writer DSP/Gina24_361DSP.c gina24_361_dsp.fw
- ./fw_writer ASIC/Gina24ASIC_361.c gina24_361_asic.fw
- ./fw_writer DSP/Layla24DSP.c layla24_dsp.fw
- ./fw_writer ASIC/Layla24_1ASIC.c layla24_1_asic.fw
- ./fw_writer ASIC/Layla24_2A_ASIC.c layla24_2A_asic.fw
- ./fw_writer ASIC/Layla24_2S_ASIC.c layla24_2S_asic.fw
- ./fw_writer DSP/MonaDSP.c mona_301_dsp.fw
- ./fw_writer ASIC/Mona1ASIC48.c mona_301_1_asic_48.fw
- ./fw_writer ASIC/Mona1ASIC96.c mona_301_1_asic_96.fw
- ./fw_writer DSP/Mona361DSP.c mona_361_dsp.fw
- ./fw_writer ASIC/Mona1ASIC48_361.c mona_361_1_asic_48.fw
- ./fw_writer ASIC/Mona1ASIC96_361.c mona_361_1_asic_96.fw
- ./fw_writer ASIC/Mona2ASIC.c mona_2_asic.fw
- ./fw_writer DSP/MiaDSP.c mia_dsp.fw
- ./fw_writer DSP/Echo3gDSP.c echo3g_dsp.fw
- ./fw_writer ASIC/3G_ASIC.c 3g_asic.fw
- ./fw_writer DSP/IndigoDSP.c indigo_dsp.fw
- ./fw_writer DSP/IndigoIODSP.c indigo_io_dsp.fw
- ./fw_writer DSP/IndigoIOxDSP.c indigo_iox_dsp.fw
- ./fw_writer DSP/IndigoDJDSP.c indigo_dj_dsp.fw
- ./fw_writer DSP/IndigoDJxDSP.c indigo_djx_dsp.fw
+LINK_FOR_BUILD.c = $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $(TARGET_ARCH_FOR_BUILD)
+
+$(fw_writer_OBJECTS) : CC=$(CC_FOR_BUILD)
+$(fw_writer_OBJECTS) : CFLAGS=$(CFLAGS_FOR_BUILD)
+$(fw_writer_OBJECTS) : CPPFLAGS=$(CPPFLAGS_FOR_BUILD)
+
+fw_writer$(BUILD_EXEEXT): $(tobin_OBJECTS)
+ $(LINK_FOR_BUILD.c) $^ $(LOADLIBES_FOR_BUILD) $(LDLIBS_FOR_BUILD) -o $@
+
+$(firmware_files): fw_writer$(BUILD_EXEEXT)
+ ./fw_writer$(BUILD_EXEEXT) DSP/LoaderDSP.c loader_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/Darla20DSP.c darla20_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/Gina20DSP.c gina20_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/Layla20DSP.c layla20_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/LaylaASIC.c layla20_asic.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/Darla24DSP.c darla24_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/Gina24DSP.c gina24_301_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/Gina24ASIC.c gina24_301_asic.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/Gina24_361DSP.c gina24_361_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/Gina24ASIC_361.c gina24_361_asic.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/Layla24DSP.c layla24_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/Layla24_1ASIC.c layla24_1_asic.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/Layla24_2A_ASIC.c layla24_2A_asic.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/Layla24_2S_ASIC.c layla24_2S_asic.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/MonaDSP.c mona_301_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/Mona1ASIC48.c mona_301_1_asic_48.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/Mona1ASIC96.c mona_301_1_asic_96.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/Mona361DSP.c mona_361_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/Mona1ASIC48_361.c mona_361_1_asic_48.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/Mona1ASIC96_361.c mona_361_1_asic_96.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/Mona2ASIC.c mona_2_asic.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/MiaDSP.c mia_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/Echo3gDSP.c echo3g_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) ASIC/3G_ASIC.c 3g_asic.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/IndigoDSP.c indigo_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/IndigoIODSP.c indigo_io_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/IndigoIOxDSP.c indigo_iox_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/IndigoDJDSP.c indigo_dj_dsp.fw
+ ./fw_writer$(BUILD_EXEEXT) DSP/IndigoDJxDSP.c indigo_djx_dsp.fw
diff --git a/emu/Makefile.am b/emu/Makefile.am
index 3a633c0..e5a3017 100644
--- a/emu/Makefile.am
+++ b/emu/Makefile.am
@@ -22,5 +22,14 @@ hotplugfwdir =
hotplugfw_DATA =
endif
-$(firmware_files): fw_writer
- ./fw_writer
+LINK_FOR_BUILD.c = $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $(TARGET_ARCH_FOR_BUILD)
+
+$(fw_writer_OBJECTS) : CC=$(CC_FOR_BUILD)
+$(fw_writer_OBJECTS) : CFLAGS=$(CFLAGS_FOR_BUILD)
+$(fw_writer_OBJECTS) : CPPFLAGS=$(CPPFLAGS_FOR_BUILD)
+
+fw_writer$(BUILD_EXEEXT): $(tobin_OBJECTS)
+ $(LINK_FOR_BUILD.c) $^ $(LOADLIBES_FOR_BUILD) $(LDLIBS_FOR_BUILD) -o $@
+
+$(firmware_files): fw_writer$(BUILD_EXEEXT)
+ ./fw_writer$(BUILD_EXEEXT)
diff --git a/hdsploader/Makefile.am b/hdsploader/Makefile.am
index 48802a6..b13245b 100644
--- a/hdsploader/Makefile.am
+++ b/hdsploader/Makefile.am
@@ -32,5 +32,14 @@ EXTRA_DIST = $(dsp_hex_files:%.bin=%.dat) \
tobin.c
CLEANFILES = $(dsp_hex_files)
-$(dsp_hex_files): tobin
- ./tobin
+LINK_FOR_BUILD.c = $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $(TARGET_ARCH_FOR_BUILD)
+
+$(tobin_OBJECTS) : CC=$(CC_FOR_BUILD)
+$(tobin_OBJECTS) : CFLAGS=$(CFLAGS_FOR_BUILD)
+$(tobin_OBJECTS) : CPPFLAGS=$(CPPFLAGS_FOR_BUILD)
+
+tobin$(BUILD_EXEEXT): $(tobin_OBJECTS)
+ $(LINK_FOR_BUILD.c) $^ $(LOADLIBES_FOR_BUILD) $(LDLIBS_FOR_BUILD) -o $@
+
+$(dsp_hex_files): tobin$(BUILD_EXEEXT)
+ ./$<
diff --git a/m4/ax_prog_cc_for_build.m4 b/m4/ax_prog_cc_for_build.m4
new file mode 100644
index 0000000..12cb005
--- /dev/null
+++ b/m4/ax_prog_cc_for_build.m4
@@ -0,0 +1,125 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_PROG_CC_FOR_BUILD
+#
+# DESCRIPTION
+#
+# This macro searches for a C compiler that generates native executables,
+# that is a C compiler that surely is not a cross-compiler. This can be
+# useful if you have to generate source code at compile-time like for
+# example GCC does.
+#
+# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything
+# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD).
+# The value of these variables can be overridden by the user by specifying
+# a compiler with an environment variable (like you do for standard CC).
+#
+# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object
+# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if
+# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are
+# substituted in the Makefile.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Paolo Bonzini <bonzini(a)gnu.org>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 9
+
+AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD])
+AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_CPP])dnl
+AC_REQUIRE([AC_EXEEXT])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+
+dnl Use the standard macros, but make them use other variable names
+dnl
+pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl
+pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl
+pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl
+pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl
+pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl
+pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl
+pushdef([ac_cv_objext], ac_cv_build_objext)dnl
+pushdef([ac_exeext], ac_build_exeext)dnl
+pushdef([ac_objext], ac_build_objext)dnl
+pushdef([CC], CC_FOR_BUILD)dnl
+pushdef([CPP], CPP_FOR_BUILD)dnl
+pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl
+pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl
+pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl
+pushdef([host], build)dnl
+pushdef([host_alias], build_alias)dnl
+pushdef([host_cpu], build_cpu)dnl
+pushdef([host_vendor], build_vendor)dnl
+pushdef([host_os], build_os)dnl
+pushdef([ac_cv_host], ac_cv_build)dnl
+pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl
+pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl
+pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl
+pushdef([ac_cv_host_os], ac_cv_build_os)dnl
+pushdef([ac_cpp], ac_build_cpp)dnl
+pushdef([ac_compile], ac_build_compile)dnl
+pushdef([ac_link], ac_build_link)dnl
+
+save_cross_compiling=$cross_compiling
+save_ac_tool_prefix=$ac_tool_prefix
+cross_compiling=no
+ac_tool_prefix=
+
+AC_PROG_CC
+AC_PROG_CPP
+AC_EXEEXT
+
+ac_tool_prefix=$save_ac_tool_prefix
+cross_compiling=$save_cross_compiling
+
+dnl Restore the old definitions
+dnl
+popdef([ac_link])dnl
+popdef([ac_compile])dnl
+popdef([ac_cpp])dnl
+popdef([ac_cv_host_os])dnl
+popdef([ac_cv_host_vendor])dnl
+popdef([ac_cv_host_cpu])dnl
+popdef([ac_cv_host_alias])dnl
+popdef([ac_cv_host])dnl
+popdef([host_os])dnl
+popdef([host_vendor])dnl
+popdef([host_cpu])dnl
+popdef([host_alias])dnl
+popdef([host])dnl
+popdef([LDFLAGS])dnl
+popdef([CPPFLAGS])dnl
+popdef([CFLAGS])dnl
+popdef([CPP])dnl
+popdef([CC])dnl
+popdef([ac_objext])dnl
+popdef([ac_exeext])dnl
+popdef([ac_cv_objext])dnl
+popdef([ac_cv_exeext])dnl
+popdef([ac_cv_prog_cc_g])dnl
+popdef([ac_cv_prog_cc_cross])dnl
+popdef([ac_cv_prog_cc_works])dnl
+popdef([ac_cv_prog_gcc])dnl
+popdef([ac_cv_prog_CPP])dnl
+
+dnl Finally, set Makefile variables
+dnl
+BUILD_EXEEXT=$ac_build_exeext
+BUILD_OBJEXT=$ac_build_objext
+AC_SUBST(BUILD_EXEEXT)dnl
+AC_SUBST(BUILD_OBJEXT)dnl
+AC_SUBST([CFLAGS_FOR_BUILD])dnl
+AC_SUBST([CPPFLAGS_FOR_BUILD])dnl
+AC_SUBST([LDFLAGS_FOR_BUILD])dnl
+])
diff --git a/maestro3/Makefile.am b/maestro3/Makefile.am
index 7544f12..0826570 100644
--- a/maestro3/Makefile.am
+++ b/maestro3/Makefile.am
@@ -17,5 +17,14 @@ hotplugfwdir =
hotplugfw_DATA =
endif
-$(firmware_files): fw_writer
- ./fw_writer
+LINK_FOR_BUILD.c = $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $(TARGET_ARCH_FOR_BUILD)
+
+$(fw_writer_OBJECTS) : CC=$(CC_FOR_BUILD)
+$(fw_writer_OBJECTS) : CFLAGS=$(CFLAGS_FOR_BUILD)
+$(fw_writer_OBJECTS) : CPPFLAGS=$(CPPFLAGS_FOR_BUILD)
+
+fw_writer$(BUILD_EXEEXT): $(tobin_OBJECTS)
+ $(LINK_FOR_BUILD.c) $^ $(LOADLIBES_FOR_BUILD) $(LDLIBS_FOR_BUILD) -o $@
+
+$(firmware_files): fw_writer$(BUILD_EXEEXT)
+ ./fw_writer$(BUILD_EXEEXT)
diff --git a/sb16/Makefile.am b/sb16/Makefile.am
index 17eead9..58d0032 100644
--- a/sb16/Makefile.am
+++ b/sb16/Makefile.am
@@ -18,5 +18,14 @@ hotplugfwdir =
hotplugfw_DATA =
endif
-$(firmware_files): fw_writer
- ./fw_writer
+LINK_FOR_BUILD.c = $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $(TARGET_ARCH_FOR_BUILD)
+
+$(fw_writer_OBJECTS) : CC=$(CC_FOR_BUILD)
+$(fw_writer_OBJECTS) : CFLAGS=$(CFLAGS_FOR_BUILD)
+$(fw_writer_OBJECTS) : CPPFLAGS=$(CPPFLAGS_FOR_BUILD)
+
+fw_writer$(BUILD_EXEEXT): $(tobin_OBJECTS)
+ $(LINK_FOR_BUILD.c) $^ $(LOADLIBES_FOR_BUILD) $(LDLIBS_FOR_BUILD) -o $@
+
+$(firmware_files): fw_writer$(BUILD_EXEEXT)
+ ./fw_writer$(BUILD_EXEEXT)
diff --git a/vxloader/Makefile.am b/vxloader/Makefile.am
index ca21494..adcc204 100644
--- a/vxloader/Makefile.am
+++ b/vxloader/Makefile.am
@@ -43,5 +43,14 @@ hotplugfwdir =
hotplugfw_DATA =
endif
-%.xlx: %.rbt toxlx
- ./toxlx < $< > $@
+LINK_FOR_BUILD.c = $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $(TARGET_ARCH_FOR_BUILD)
+
+$(toxlx_OBJECTS) : CC=$(CC_FOR_BUILD)
+$(toxlx_OBJECTS) : CFLAGS=$(CFLAGS_FOR_BUILD)
+$(toxlx_OBJECTS) : CPPFLAGS=$(CPPFLAGS_FOR_BUILD)
+
+toxlx$(BUILD_EXEEXT): $(toxlx_OBJECTS)
+ $(LINK_FOR_BUILD.c) $^ $(LOADLIBES_FOR_BUILD) $(LDLIBS_FOR_BUILD) -o $@
+
+%.xlx: %.rbt toxlx$(BUILD_EXEEXT)
+ ./toxlx$(BUILD_EXEEXT) < $< > $@
diff --git a/wavefront/Makefile.am b/wavefront/Makefile.am
index e8c9fe1..8f67b59 100644
--- a/wavefront/Makefile.am
+++ b/wavefront/Makefile.am
@@ -17,5 +17,14 @@ hotplugfwdir =
hotplugfw_DATA =
endif
-$(firmware_files): fw_writer
- ./fw_writer
+LINK_FOR_BUILD.c = $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $(TARGET_ARCH_FOR_BUILD)
+
+$(fw_writer_OBJECTS) : CC=$(CC_FOR_BUILD)
+$(fw_writer_OBJECTS) : CFLAGS=$(CFLAGS_FOR_BUILD)
+$(fw_writer_OBJECTS) : CPPFLAGS=$(CPPFLAGS_FOR_BUILD)
+
+fw_writer$(BUILD_EXEEXT): $(tobin_OBJECTS)
+ $(LINK_FOR_BUILD.c) $^ $(LOADLIBES_FOR_BUILD) $(LDLIBS_FOR_BUILD) -o $@
+
+$(firmware_files): fw_writer$(BUILD_EXEEXT)
+ ./fw_writer$(BUILD_EXEEXT)
diff --git a/ymfpci/Makefile.am b/ymfpci/Makefile.am
index 8795fbc..645b471 100644
--- a/ymfpci/Makefile.am
+++ b/ymfpci/Makefile.am
@@ -17,5 +17,14 @@ hotplugfwdir =
hotplugfw_DATA =
endif
-$(firmware_files): fw_writer
- ./fw_writer
+LINK_FOR_BUILD.c = $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $(TARGET_ARCH_FOR_BUILD)
+
+$(fw_writer_OBJECTS) : CC=$(CC_FOR_BUILD)
+$(fw_writer_OBJECTS) : CFLAGS=$(CFLAGS_FOR_BUILD)
+$(fw_writer_OBJECTS) : CPPFLAGS=$(CPPFLAGS_FOR_BUILD)
+
+fw_writer$(BUILD_EXEEXT): $(tobin_OBJECTS)
+ $(LINK_FOR_BUILD.c) $^ $(LOADLIBES_FOR_BUILD) $(LDLIBS_FOR_BUILD) -o $@
+
+$(firmware_files): fw_writer$(BUILD_EXEEXT)
+ ./fw_writer$(BUILD_EXEEXT)
--
2.22.0
1
0
[alsa-devel] [PATCH] ASoC: meson: axg-card: remove useless check on codec
by Jerome Brunet 28 Jun '19
by Jerome Brunet 28 Jun '19
28 Jun '19
While checking cpus before dereferencing the pointer is required, it is
not necessary for codecs. 'codec' can't possibly be NULL in the loop
Reported-by: Dan Carpenter <dan.carpenter(a)oracle.com>
Signed-off-by: Jerome Brunet <jbrunet(a)baylibre.com>
---
sound/soc/meson/axg-card.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c
index 70bb0cbad233..14a8321744da 100644
--- a/sound/soc/meson/axg-card.c
+++ b/sound/soc/meson/axg-card.c
@@ -118,8 +118,7 @@ static void axg_card_clean_references(struct axg_card *priv)
if (link->cpus)
of_node_put(link->cpus->of_node);
for_each_link_codecs(link, j, codec)
- if (codec)
- of_node_put(codec->of_node);
+ of_node_put(codec->of_node);
}
}
--
2.21.0
2
1
28 Jun '19
My initial goal with this patchset was to allow a dai_link to have no
no platform component, instead of having dummy by default.
However, when rebasing, I discovered that Kuninori Morimoto had recently
done that in a different way :)
I am still submitting my change since it should allow multiple platform
components on a dai_link, which is one of the FIXME note in soc-core.
I have also added a check on the codecs component availability to align
on what was done for platforms and cpus
Change since v1 [0]:
* Fix registartion typo
* Rename dlc variable to codec/platform
[0]: https://lkml.kernel.org/r/20190626133617.25959-1-jbrunet@baylibre.com
Jerome Brunet (2):
ASoC: soc-core: defer card registration if codec component is missing
ASoC: soc-core: support dai_link with platforms_num != 1
include/sound/soc.h | 6 ++++
sound/soc/soc-core.c | 67 +++++++++++++++++++++-----------------------
2 files changed, 38 insertions(+), 35 deletions(-)
--
2.21.0
2
4
[alsa-devel] [PATCH][next] ASoC: topology: fix memory leaks on sm, se and sbe
by Colin King 28 Jun '19
by Colin King 28 Jun '19
28 Jun '19
From: Colin Ian King <colin.king(a)canonical.com>
Currently when a kstrdup fails the error exit paths don't free
the allocations for sm, se and sbe. This can be fixed by assigning
kc[i].private_value to these before doing the ksrtdup so that the error
exit path will be able to free these objects.
Addresses-Coverity: ("Resource leak")
Fixes: 9f90af3a9952 ("ASoC: topology: Consolidate and fix asoc_tplg_dapm_widget_*_create flow")
Signed-off-by: Colin Ian King <colin.king(a)canonical.com>
---
sound/soc/soc-topology.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index fc1f1d6f9e92..dc463f1a9e24 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1326,10 +1326,10 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
dev_dbg(tplg->dev, " adding DAPM widget mixer control %s at %d\n",
mc->hdr.name, i);
+ kc[i].private_value = (long)sm;
kc[i].name = kstrdup(mc->hdr.name, GFP_KERNEL);
if (kc[i].name == NULL)
goto err_sm;
- kc[i].private_value = (long)sm;
kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kc[i].access = mc->hdr.access;
@@ -1412,10 +1412,10 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
ec->hdr.name);
+ kc[i].private_value = (long)se;
kc[i].name = kstrdup(ec->hdr.name, GFP_KERNEL);
if (kc[i].name == NULL)
goto err_se;
- kc[i].private_value = (long)se;
kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kc[i].access = ec->hdr.access;
@@ -1524,10 +1524,10 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create(
"ASoC: adding bytes kcontrol %s with access 0x%x\n",
be->hdr.name, be->hdr.access);
+ kc[i].private_value = (long)sbe;
kc[i].name = kstrdup(be->hdr.name, GFP_KERNEL);
if (kc[i].name == NULL)
goto err_sbe;
- kc[i].private_value = (long)sbe;
kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kc[i].access = be->hdr.access;
--
2.20.1
2
1
[alsa-devel] [PATCH v4] ASoC: rt1308: Add RT1308 amplifier driver
by derek.fang@realtek.com 28 Jun '19
by derek.fang@realtek.com 28 Jun '19
28 Jun '19
From: Derek Fang <derek.fang(a)realtek.com>
This is the initial amplifier driver for rt1308.
Signed-off-by: Derek Fang <derek.fang(a)realtek.com>
---
Documentation/devicetree/bindings/sound/rt1308.txt | 17 +
sound/soc/codecs/Kconfig | 6 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/rt1308.c | 899 +++++++++++++++++++++
sound/soc/codecs/rt1308.h | 291 +++++++
5 files changed, 1215 insertions(+)
create mode 100755 Documentation/devicetree/bindings/sound/rt1308.txt
create mode 100755 sound/soc/codecs/rt1308.c
create mode 100755 sound/soc/codecs/rt1308.h
diff --git a/Documentation/devicetree/bindings/sound/rt1308.txt b/Documentation/devicetree/bindings/sound/rt1308.txt
new file mode 100755
index 0000000..2d46084
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/rt1308.txt
@@ -0,0 +1,17 @@
+RT1308 audio Amplifier
+
+This device supports I2C only.
+
+Required properties:
+
+- compatible : "realtek,rt1308".
+
+- reg : The I2C address of the device.
+
+
+Example:
+
+rt1308: rt1308@10 {
+ compatible = "realtek,rt1308";
+ reg = <0x10>;
+};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index b2d445b..9f89a53 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -146,6 +146,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_RT298 if I2C
select SND_SOC_RT1011 if I2C
select SND_SOC_RT1305 if I2C
+ select SND_SOC_RT1308 if I2C
select SND_SOC_RT5514 if I2C
select SND_SOC_RT5616 if I2C
select SND_SOC_RT5631 if I2C
@@ -904,6 +905,7 @@ config SND_SOC_RL6231
default y if SND_SOC_RT5682=y
default y if SND_SOC_RT1011=y
default y if SND_SOC_RT1305=y
+ default y if SND_SOC_RT1308=y
default m if SND_SOC_RT5514=m
default m if SND_SOC_RT5616=m
default m if SND_SOC_RT5640=m
@@ -919,6 +921,7 @@ config SND_SOC_RL6231
default m if SND_SOC_RT5682=m
default m if SND_SOC_RT1011=m
default m if SND_SOC_RT1305=m
+ default m if SND_SOC_RT1308=m
config SND_SOC_RL6347A
tristate
@@ -947,6 +950,9 @@ config SND_SOC_RT1011
config SND_SOC_RT1305
tristate
+config SND_SOC_RT1308
+ tristate
+
config SND_SOC_RT5514
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index ed74f5b..5b4bb8c 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -148,6 +148,7 @@ snd-soc-rl6231-objs := rl6231.o
snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt1011-objs := rt1011.o
snd-soc-rt1305-objs := rt1305.o
+snd-soc-rt1308-objs := rt1308.o
snd-soc-rt274-objs := rt274.o
snd-soc-rt286-objs := rt286.o
snd-soc-rt298-objs := rt298.o
@@ -428,6 +429,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o
obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o
+obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o
obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c
new file mode 100755
index 0000000..619a630
--- /dev/null
+++ b/sound/soc/codecs/rt1308.c
@@ -0,0 +1,899 @@
+/*
+ * rt1308.c -- RT1308 ALSA SoC amplifier component driver
+ *
+ * Copyright 2019 Realtek Semiconductor Corp.
+ * Author: Derek Fang <derek.fang(a)realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of_gpio.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt1308.h"
+
+static const struct reg_sequence init_list[] = {
+
+ { RT1308_I2C_I2S_SDW_SET, 0x01014005 },
+ { RT1308_CLASS_D_SET_2, 0x227f5501 },
+ { RT1308_PADS_1, 0x50150505 },
+ { RT1308_VREF, 0x18100000 },
+ { RT1308_IV_SENSE, 0x87010000 },
+ { RT1308_DUMMY_REG, 0x00000200 },
+ { RT1308_SIL_DET, 0x61c30000 },
+ { RT1308_DC_CAL_2, 0x00ffff00 },
+ { RT1308_CLK_DET, 0x01000000 },
+ { RT1308_POWER_STATUS, 0x00800000 },
+ { RT1308_DAC_SET, 0xafaf0700 },
+
+};
+#define RT1308_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+struct rt1308_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int master;
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+static const struct reg_default rt1308_reg[] = {
+
+ { 0x01, 0x1f3f5f00 },
+ { 0x02, 0x07000000 },
+ { 0x03, 0x80003e00 },
+ { 0x04, 0x80800600 },
+ { 0x05, 0x0aaa1a0a },
+ { 0x06, 0x52000000 },
+ { 0x07, 0x00000000 },
+ { 0x08, 0x00600000 },
+ { 0x09, 0xe1030000 },
+ { 0x0a, 0x00000000 },
+ { 0x0b, 0x30000000 },
+ { 0x0c, 0x7fff7000 },
+ { 0x10, 0xffff0700 },
+ { 0x11, 0x0a000000 },
+ { 0x12, 0x60040000 },
+ { 0x13, 0x00000000 },
+ { 0x14, 0x0f300000 },
+ { 0x15, 0x00000022 },
+ { 0x16, 0x02000000 },
+ { 0x17, 0x01004045 },
+ { 0x18, 0x00000000 },
+ { 0x19, 0x00000000 },
+ { 0x1a, 0x80000000 },
+ { 0x1b, 0x10325476 },
+ { 0x1c, 0x1d1d0000 },
+ { 0x20, 0xd2101300 },
+ { 0x21, 0xf3ffff00 },
+ { 0x22, 0x00000000 },
+ { 0x23, 0x00000000 },
+ { 0x24, 0x00000000 },
+ { 0x25, 0x00000000 },
+ { 0x26, 0x00000000 },
+ { 0x27, 0x00000000 },
+ { 0x28, 0x00000000 },
+ { 0x29, 0x00000000 },
+ { 0x2a, 0x00000000 },
+ { 0x2b, 0x00000000 },
+ { 0x2c, 0x00000000 },
+ { 0x2d, 0x00000000 },
+ { 0x2e, 0x00000000 },
+ { 0x2f, 0x00000000 },
+ { 0x30, 0x01000000 },
+ { 0x31, 0x20025501 },
+ { 0x32, 0x00000000 },
+ { 0x33, 0x105a0000 },
+ { 0x34, 0x10100000 },
+ { 0x35, 0x2aaa52aa },
+ { 0x36, 0x00c00000 },
+ { 0x37, 0x20046100 },
+ { 0x50, 0x10022f00 },
+ { 0x51, 0x003c0000 },
+ { 0x54, 0x04000000 },
+ { 0x55, 0x01000000 },
+ { 0x56, 0x02000000 },
+ { 0x57, 0x02000000 },
+ { 0x58, 0x02000000 },
+ { 0x59, 0x02000000 },
+ { 0x5b, 0x02000000 },
+ { 0x5c, 0x00000000 },
+ { 0x5d, 0x00000000 },
+ { 0x5e, 0x00000000 },
+ { 0x5f, 0x00000000 },
+ { 0x60, 0x02000000 },
+ { 0x61, 0x00000000 },
+ { 0x62, 0x00000000 },
+ { 0x63, 0x00000000 },
+ { 0x64, 0x00000000 },
+ { 0x65, 0x02000000 },
+ { 0x66, 0x00000000 },
+ { 0x67, 0x00000000 },
+ { 0x68, 0x00000000 },
+ { 0x69, 0x00000000 },
+ { 0x6a, 0x02000000 },
+ { 0x6c, 0x00000000 },
+ { 0x6d, 0x00000000 },
+ { 0x6e, 0x00000000 },
+ { 0x70, 0x10EC1308 },
+ { 0x71, 0x00000000 },
+ { 0x72, 0x00000000 },
+ { 0x73, 0x00000000 },
+ { 0x74, 0x00000000 },
+ { 0x75, 0x00000000 },
+ { 0x76, 0x00000000 },
+ { 0x77, 0x00000000 },
+ { 0x78, 0x00000000 },
+ { 0x79, 0x00000000 },
+ { 0x7a, 0x00000000 },
+ { 0x7b, 0x00000000 },
+ { 0x7c, 0x00000000 },
+ { 0x7d, 0x00000000 },
+ { 0x7e, 0x00000000 },
+ { 0x7f, 0x00020f00 },
+ { 0x80, 0x00000000 },
+ { 0x81, 0x00000000 },
+ { 0x82, 0x00000000 },
+ { 0x83, 0x00000000 },
+ { 0x84, 0x00000000 },
+ { 0x85, 0x00000000 },
+ { 0x86, 0x00000000 },
+ { 0x87, 0x00000000 },
+ { 0x88, 0x00000000 },
+ { 0x89, 0x00000000 },
+ { 0x8a, 0x00000000 },
+ { 0x8b, 0x00000000 },
+ { 0x8c, 0x00000000 },
+ { 0x8d, 0x00000000 },
+ { 0x8e, 0x00000000 },
+ { 0x90, 0x50250905 },
+ { 0x91, 0x15050000 },
+ { 0xa0, 0x00000000 },
+ { 0xa1, 0x00000000 },
+ { 0xa2, 0x00000000 },
+ { 0xa3, 0x00000000 },
+ { 0xa4, 0x00000000 },
+ { 0xb0, 0x00000000 },
+ { 0xb1, 0x00000000 },
+ { 0xb2, 0x00000000 },
+ { 0xb3, 0x00000000 },
+ { 0xb4, 0x00000000 },
+ { 0xb5, 0x00000000 },
+ { 0xb6, 0x00000000 },
+ { 0xb7, 0x00000000 },
+ { 0xb8, 0x00000000 },
+ { 0xb9, 0x00000000 },
+ { 0xba, 0x00000000 },
+ { 0xbb, 0x00000000 },
+ { 0xc0, 0x01000000 },
+ { 0xc1, 0x00000000 },
+ { 0xf0, 0x00000000 },
+};
+
+static int rt1308_reg_init(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ return regmap_multi_reg_write(rt1308->regmap, init_list,
+ RT1308_INIT_REG_LEN);
+}
+
+static bool rt1308_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1308_RESET:
+ case RT1308_RESET_N:
+ case RT1308_CLK_2:
+ case RT1308_SIL_DET:
+ case RT1308_CLK_DET:
+ case RT1308_DC_DET:
+ case RT1308_DAC_SET:
+ case RT1308_DAC_BUF:
+ case RT1308_SDW_REG_RDATA:
+ case RT1308_DC_CAL_1:
+ case RT1308_PVDD_OFFSET_CTL:
+ case RT1308_CAL_OFFSET_DAC_PBTL:
+ case RT1308_CAL_OFFSET_DAC_L:
+ case RT1308_CAL_OFFSET_DAC_R:
+ case RT1308_CAL_OFFSET_PWM_L:
+ case RT1308_CAL_OFFSET_PWM_R:
+ case RT1308_CAL_PWM_VOS_ADC_L:
+ case RT1308_CAL_PWM_VOS_ADC_R:
+ case RT1308_MBIAS:
+ case RT1308_POWER_STATUS:
+ case RT1308_POWER_INT:
+ case RT1308_SINE_TONE_GEN_2:
+ case RT1308_BQ_SET:
+ case RT1308_BQ_PARA_UPDATE:
+ case RT1308_VEN_DEV_ID:
+ case RT1308_VERSION_ID:
+ case RT1308_EFUSE_1:
+ case RT1308_EFUSE_READ_PVDD_L:
+ case RT1308_EFUSE_READ_PVDD_R:
+ case RT1308_EFUSE_READ_PVDD_PTBL:
+ case RT1308_EFUSE_READ_DEV:
+ case RT1308_EFUSE_READ_R0:
+ case RT1308_EFUSE_READ_ADC_L:
+ case RT1308_EFUSE_READ_ADC_R:
+ case RT1308_EFUSE_READ_ADC_PBTL:
+ case RT1308_EFUSE_RESERVE:
+ case RT1308_EFUSE_DATA_0_MSB:
+ case RT1308_EFUSE_DATA_0_LSB:
+ case RT1308_EFUSE_DATA_1_MSB:
+ case RT1308_EFUSE_DATA_1_LSB:
+ case RT1308_EFUSE_DATA_2_MSB:
+ case RT1308_EFUSE_DATA_2_LSB:
+ case RT1308_EFUSE_DATA_3_MSB:
+ case RT1308_EFUSE_DATA_3_LSB:
+ case RT1308_EFUSE_STATUS_1:
+ case RT1308_EFUSE_STATUS_2:
+ case RT1308_DUMMY_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1308_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT1308_RESET:
+ case RT1308_RESET_N:
+ case RT1308_CLK_GATING ... RT1308_DC_DET_THRES:
+ case RT1308_DAC_SET ... RT1308_AD_FILTER_SET:
+ case RT1308_DC_CAL_1 ... RT1308_POWER_INT:
+ case RT1308_SINE_TONE_GEN_1:
+ case RT1308_SINE_TONE_GEN_2:
+ case RT1308_BQ_SET:
+ case RT1308_BQ_PARA_UPDATE:
+ case RT1308_BQ_PRE_VOL_L ... RT1308_BQ_POST_VOL_R:
+ case RT1308_BQ1_L_H0 ... RT1308_BQ2_R_A2:
+ case RT1308_VEN_DEV_ID:
+ case RT1308_VERSION_ID:
+ case RT1308_SPK_BOUND:
+ case RT1308_BQ1_EQ_L_1 ... RT1308_BQ2_EQ_R_3:
+ case RT1308_EFUSE_1 ... RT1308_EFUSE_RESERVE:
+ case RT1308_PADS_1:
+ case RT1308_PADS_2:
+ case RT1308_TEST_MODE:
+ case RT1308_TEST_1:
+ case RT1308_TEST_2:
+ case RT1308_TEST_3:
+ case RT1308_TEST_4:
+ case RT1308_EFUSE_DATA_0_MSB ... RT1308_EFUSE_STATUS_2:
+ case RT1308_TCON_1:
+ case RT1308_TCON_2:
+ case RT1308_DUMMY_REG:
+ case RT1308_MAX_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt1308_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(30);
+ snd_soc_component_update_bits(component, RT1308_POWER_STATUS,
+ RT1308_POW_PDB_REG_BIT, RT1308_POW_PDB_REG_BIT);
+ msleep(40);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, RT1308_POWER_STATUS,
+ RT1308_POW_PDB_REG_BIT, 0);
+ usleep_range(150000, 200000);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const rt1308_rx_data_ch_select[] = {
+ "LR",
+ "LL",
+ "RL",
+ "RR",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1308_rx_data_ch_enum, RT1308_DATA_PATH, 24,
+ rt1308_rx_data_ch_select);
+
+static const struct snd_kcontrol_new rt1308_snd_controls[] = {
+
+ /* I2S Data Channel Selection */
+ SOC_ENUM("RX Channel Select", rt1308_rx_data_ch_enum),
+};
+
+static const struct snd_kcontrol_new rt1308_sto_dac_l =
+ SOC_DAPM_SINGLE("Switch", RT1308_DAC_SET,
+ RT1308_DVOL_MUTE_L_EN_SFT, 1, 1);
+
+static const struct snd_kcontrol_new rt1308_sto_dac_r =
+ SOC_DAPM_SINGLE("Switch", RT1308_DAC_SET,
+ RT1308_DVOL_MUTE_R_EN_SFT, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Supply Widgets */
+ SND_SOC_DAPM_SUPPLY("MBIAS20U", RT1308_POWER,
+ RT1308_POW_MBIAS20U_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ALDO", RT1308_POWER,
+ RT1308_POW_ALDO_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DBG", RT1308_POWER,
+ RT1308_POW_DBG_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DACL", RT1308_POWER,
+ RT1308_POW_DACL_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK25M", RT1308_POWER,
+ RT1308_POW_CLK25M_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_R", RT1308_POWER,
+ RT1308_POW_ADC_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_L", RT1308_POWER,
+ RT1308_POW_ADC_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DLDO", RT1308_POWER,
+ RT1308_POW_DLDO_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF", RT1308_POWER,
+ RT1308_POW_VREF_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIXER_R", RT1308_POWER,
+ RT1308_POW_MIXER_R_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIXER_L", RT1308_POWER,
+ RT1308_POW_MIXER_L_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS4U", RT1308_POWER,
+ RT1308_POW_MBIAS4U_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2_LDO", RT1308_POWER,
+ RT1308_POW_PLL2_LDO_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B", RT1308_POWER,
+ RT1308_POW_PLL2B_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F", RT1308_POWER,
+ RT1308_POW_PLL2F_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2F2", RT1308_POWER,
+ RT1308_POW_PLL2F2_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL2B2", RT1308_POWER,
+ RT1308_POW_PLL2B2_EN_BIT, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("DAC Power", RT1308_POWER,
+ RT1308_POW_DAC1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_l),
+ SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_r),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1308_classd_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt1308_dapm_routes[] = {
+
+ { "DAC", NULL, "AIF1RX" },
+
+ { "DAC", NULL, "MBIAS20U" },
+ { "DAC", NULL, "ALDO" },
+ { "DAC", NULL, "DBG" },
+ { "DAC", NULL, "DACL" },
+ { "DAC", NULL, "CLK25M" },
+ { "DAC", NULL, "ADC_R" },
+ { "DAC", NULL, "ADC_L" },
+ { "DAC", NULL, "DLDO" },
+ { "DAC", NULL, "VREF" },
+ { "DAC", NULL, "MIXER_R" },
+ { "DAC", NULL, "MIXER_L" },
+ { "DAC", NULL, "MBIAS4U" },
+ { "DAC", NULL, "PLL2_LDO" },
+ { "DAC", NULL, "PLL2B" },
+ { "DAC", NULL, "PLL2F" },
+ { "DAC", NULL, "PLL2F2" },
+ { "DAC", NULL, "PLL2B2" },
+
+ { "DAC L", "Switch", "DAC" },
+ { "DAC R", "Switch", "DAC" },
+ { "DAC L", NULL, "DAC Power" },
+ { "DAC R", NULL, "DAC Power" },
+
+ { "CLASS D", NULL, "DAC L" },
+ { "CLASS D", NULL, "DAC R" },
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+};
+
+static int rt1308_get_clk_info(int sclk, int rate)
+{
+ int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+ if (sclk <= 0 || rate <= 0)
+ return -EINVAL;
+
+ rate = rate << 8;
+ for (i = 0; i < ARRAY_SIZE(pd); i++)
+ if (sclk == rate * pd[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int rt1308_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0, val_clk, mask_clk;
+ int pre_div, bclk_ms, frame_size;
+
+ rt1308->lrck = params_rate(params);
+ pre_div = rt1308_get_clk_info(rt1308->sysclk, rt1308->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev,
+ "Unsupported clock setting %d\n", rt1308->lrck);
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(component->dev, "Unsupported frame size: %d\n",
+ frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt1308->bclk = rt1308->lrck * (32 << bclk_ms);
+
+ dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n",
+ rt1308->lrck, pre_div, dai->id);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= RT1308_I2S_DL_SEL_16B;
+ break;
+ case 20:
+ val_len |= RT1308_I2S_DL_SEL_20B;
+ break;
+ case 24:
+ val_len |= RT1308_I2S_DL_SEL_24B;
+ break;
+ case 8:
+ val_len |= RT1308_I2S_DL_SEL_8B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1308_AIF1:
+ mask_clk = RT1308_DIV_FS_SYS_MASK;
+ val_clk = pre_div << RT1308_DIV_FS_SYS_SFT;
+ snd_soc_component_update_bits(component,
+ RT1308_I2S_SET_2, RT1308_I2S_DL_SEL_MASK,
+ val_len);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RT1308_CLK_1,
+ mask_clk, val_clk);
+
+ return 0;
+}
+
+static int rt1308_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, reg1_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ rt1308->master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT1308_I2S_DF_SEL_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1308_I2S_DF_SEL_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1308_I2S_DF_SEL_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg1_val |= RT1308_I2S_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT1308_AIF1:
+ snd_soc_component_update_bits(component,
+ RT1308_I2S_SET_1, RT1308_I2S_DF_SEL_MASK,
+ reg_val);
+ snd_soc_component_update_bits(component,
+ RT1308_I2S_SET_2, RT1308_I2S_BCLK_MASK,
+ reg1_val);
+ break;
+ default:
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rt1308_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0;
+
+ if (freq == rt1308->sysclk && clk_id == rt1308->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1308_FS_SYS_S_MCLK:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_MCLK;
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_DET, RT1308_MCLK_DET_EN_MASK,
+ RT1308_MCLK_DET_EN);
+ break;
+ case RT1308_FS_SYS_S_BCLK:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_BCLK;
+ break;
+ case RT1308_FS_SYS_S_PLL:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_PLL;
+ break;
+ case RT1308_FS_SYS_S_RCCLK:
+ reg_val |= RT1308_SEL_FS_SYS_SRC_RCCLK;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, RT1308_CLK_1,
+ RT1308_SEL_FS_SYS_MASK, reg_val);
+ rt1308->sysclk = freq;
+ rt1308->sysclk_src = clk_id;
+
+ dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
+}
+
+static int rt1308_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (source == rt1308->pll_src && freq_in == rt1308->pll_in &&
+ freq_out == rt1308->pll_out)
+ return 0;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+
+ rt1308->pll_in = 0;
+ rt1308->pll_out = 0;
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_1, RT1308_SEL_FS_SYS_MASK,
+ RT1308_SEL_FS_SYS_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT1308_PLL_S_MCLK:
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK,
+ RT1308_SEL_PLL_SRC_MCLK);
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_DET, RT1308_MCLK_DET_EN_MASK,
+ RT1308_MCLK_DET_EN);
+ break;
+ case RT1308_PLL_S_BCLK:
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK,
+ RT1308_SEL_PLL_SRC_BCLK);
+ break;
+ case RT1308_PLL_S_RCCLK:
+ snd_soc_component_update_bits(component,
+ RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK,
+ RT1308_SEL_PLL_SRC_RCCLK);
+ freq_in = 25000000;
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL Source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_component_write(component, RT1308_PLL_1,
+ pll_code.k_code << RT1308_PLL1_K_SFT |
+ pll_code.m_bp << RT1308_PLL1_M_BYPASS_SFT |
+ (pll_code.m_bp ? 0 : pll_code.m_code) << RT1308_PLL1_M_SFT |
+ pll_code.n_code << RT1308_PLL1_N_SFT);
+
+ rt1308->pll_in = freq_in;
+ rt1308->pll_out = freq_out;
+ rt1308->pll_src = source;
+
+ return 0;
+}
+
+static int rt1308_probe(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ rt1308->component = component;
+
+ return rt1308_reg_init(component);
+}
+
+static void rt1308_remove(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(rt1308->regmap, RT1308_RESET, 0);
+}
+
+#ifdef CONFIG_PM
+static int rt1308_suspend(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1308->regmap, true);
+ regcache_mark_dirty(rt1308->regmap);
+
+ return 0;
+}
+
+static int rt1308_resume(struct snd_soc_component *component)
+{
+ struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1308->regmap, false);
+ regcache_sync(rt1308->regmap);
+
+ return 0;
+}
+#else
+#define rt1308_suspend NULL
+#define rt1308_resume NULL
+#endif
+
+#define RT1308_STEREO_RATES SNDRV_PCM_RATE_48000
+#define RT1308_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt1308_aif_dai_ops = {
+ .hw_params = rt1308_hw_params,
+ .set_fmt = rt1308_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver rt1308_dai[] = {
+ {
+ .name = "rt1308-aif",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1308_STEREO_RATES,
+ .formats = RT1308_FORMATS,
+ },
+ .ops = &rt1308_aif_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1308 = {
+ .probe = rt1308_probe,
+ .remove = rt1308_remove,
+ .suspend = rt1308_suspend,
+ .resume = rt1308_resume,
+ .controls = rt1308_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1308_snd_controls),
+ .dapm_widgets = rt1308_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets),
+ .dapm_routes = rt1308_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes),
+ .set_sysclk = rt1308_set_component_sysclk,
+ .set_pll = rt1308_set_component_pll,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static const struct regmap_config rt1308_regmap = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = RT1308_MAX_REG,
+ .volatile_reg = rt1308_volatile_register,
+ .readable_reg = rt1308_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt1308_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1308_reg),
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt1308_of_match[] = {
+ { .compatible = "realtek,rt1308", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rt1308_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id rt1308_acpi_match[] = {
+ { "10EC1308", 0, },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, rt1308_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1308_i2c_id[] = {
+ { "rt1308", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1308_i2c_id);
+
+static void rt1308_efuse(struct rt1308_priv *rt1308)
+{
+ regmap_write(rt1308->regmap, RT1308_RESET, 0);
+
+ regmap_write(rt1308->regmap, RT1308_POWER, 0xff371600);
+ regmap_write(rt1308->regmap, RT1308_CLK_1, 0x52100000);
+ regmap_write(rt1308->regmap, RT1308_I2C_I2S_SDW_SET, 0x01014005);
+ regmap_write(rt1308->regmap, RT1308_CLASS_D_SET_2, 0x227f5501);
+ regmap_write(rt1308->regmap, RT1308_PADS_1, 0x50150505);
+ regmap_write(rt1308->regmap, RT1308_VREF, 0x18100000);
+ regmap_write(rt1308->regmap, RT1308_IV_SENSE, 0x87010000);
+ regmap_write(rt1308->regmap, RT1308_DUMMY_REG, 0x00000200);
+ regmap_write(rt1308->regmap, RT1308_SIL_DET, 0x61c30000);
+ regmap_write(rt1308->regmap, RT1308_CLK_DET, 0x03700000);
+ regmap_write(rt1308->regmap, RT1308_SINE_TONE_GEN_1, 0x50022f00);
+ regmap_write(rt1308->regmap, RT1308_POWER_STATUS, 0x01800000);
+ regmap_write(rt1308->regmap, RT1308_DC_CAL_2, 0x00ffff00);
+ regmap_write(rt1308->regmap, RT1308_CLASS_D_SET_2, 0x607e5501);
+
+ regmap_write(rt1308->regmap, RT1308_CLK_2, 0x0060e000);
+ regmap_write(rt1308->regmap, RT1308_EFUSE_1, 0x04fe0f00);
+ msleep(100);
+ regmap_write(rt1308->regmap, RT1308_EFUSE_1, 0x44fe0f00);
+ msleep(20);
+ regmap_write(rt1308->regmap, RT1308_PVDD_OFFSET_CTL, 0x10000000);
+
+ regmap_write(rt1308->regmap, RT1308_POWER_STATUS, 0x00800000);
+ regmap_write(rt1308->regmap, RT1308_POWER, 0x0);
+ regmap_write(rt1308->regmap, RT1308_CLK_1, 0x52000000);
+ regmap_write(rt1308->regmap, RT1308_CLASS_D_SET_2, 0x227f5501);
+ regmap_write(rt1308->regmap, RT1308_SINE_TONE_GEN_1, 0x10022f00);
+}
+
+static int rt1308_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt1308_priv *rt1308;
+ int ret;
+ unsigned int val;
+
+ rt1308 = devm_kzalloc(&i2c->dev, sizeof(struct rt1308_priv),
+ GFP_KERNEL);
+ if (rt1308 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1308);
+
+ rt1308->regmap = devm_regmap_init_i2c(i2c, &rt1308_regmap);
+ if (IS_ERR(rt1308->regmap)) {
+ ret = PTR_ERR(rt1308->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1308->regmap, RT1308_VEN_DEV_ID, &val);
+ /* ignore last byte difference */
+ if ((val & 0xFFFFFF00) != RT1308_DEVICE_ID_NUM) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt1308\n", val);
+ return -ENODEV;
+ }
+
+ rt1308_efuse(rt1308);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1308,
+ rt1308_dai, ARRAY_SIZE(rt1308_dai));
+}
+
+static void rt1308_i2c_shutdown(struct i2c_client *client)
+{
+ struct rt1308_priv *rt1308 = i2c_get_clientdata(client);
+
+ regmap_write(rt1308->regmap, RT1308_RESET, 0);
+}
+
+static struct i2c_driver rt1308_i2c_driver = {
+ .driver = {
+ .name = "rt1308",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(rt1308_of_match),
+ .acpi_match_table = ACPI_PTR(rt1308_acpi_match),
+ },
+ .probe = rt1308_i2c_probe,
+ .shutdown = rt1308_i2c_shutdown,
+ .id_table = rt1308_i2c_id,
+};
+module_i2c_driver(rt1308_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1308 amplifier driver");
+MODULE_AUTHOR("Derek Fang <derek.fang(a)realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1308.h b/sound/soc/codecs/rt1308.h
new file mode 100755
index 0000000..c330aae
--- /dev/null
+++ b/sound/soc/codecs/rt1308.h
@@ -0,0 +1,291 @@
+/*
+ * RT1308.h -- RT1308 ALSA SoC amplifier component driver
+ *
+ * Copyright 2019 Realtek Semiconductor Corp.
+ * Author: Derek Fang <derek.fang(a)realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _RT1308_H_
+#define _RT1308_H_
+
+#define RT1308_DEVICE_ID_NUM 0x10ec1300
+
+#define RT1308_RESET 0x00
+#define RT1308_RESET_N 0x01
+#define RT1308_CLK_GATING 0x02
+#define RT1308_PLL_1 0x03
+#define RT1308_PLL_2 0x04
+#define RT1308_PLL_INT 0x05
+#define RT1308_CLK_1 0x06
+#define RT1308_DATA_PATH 0x07
+#define RT1308_CLK_2 0x08
+#define RT1308_SIL_DET 0x09
+#define RT1308_CLK_DET 0x0a
+#define RT1308_DC_DET 0x0b
+#define RT1308_DC_DET_THRES 0x0c
+#define RT1308_DAC_SET 0x10
+#define RT1308_SRC_SET 0x11
+#define RT1308_DAC_BUF 0x12
+#define RT1308_ADC_SET 0x13
+#define RT1308_ADC_SET_INT 0x14
+#define RT1308_I2S_SET_1 0x15
+#define RT1308_I2S_SET_2 0x16
+#define RT1308_I2C_I2S_SDW_SET 0x17
+#define RT1308_SDW_REG_RW 0x18
+#define RT1308_SDW_REG_RDATA 0x19
+#define RT1308_IV_SENSE 0x1a
+#define RT1308_I2S_TX_DAC_SET 0x1b
+#define RT1308_AD_FILTER_SET 0x1c
+#define RT1308_DC_CAL_1 0x20
+#define RT1308_DC_CAL_2 0x21
+#define RT1308_DC_CAL_L_OFFSET 0x22
+#define RT1308_DC_CAL_R_OFFSET 0x23
+#define RT1308_PVDD_OFFSET_CTL 0x24
+#define RT1308_PVDD_OFFSET_L 0x25
+#define RT1308_PVDD_OFFSET_R 0x26
+#define RT1308_PVDD_OFFSET_PBTL 0x27
+#define RT1308_PVDD_OFFSET_PVDD 0x28
+#define RT1308_CAL_OFFSET_DAC_PBTL 0x29
+#define RT1308_CAL_OFFSET_DAC_L 0x2a
+#define RT1308_CAL_OFFSET_DAC_R 0x2b
+#define RT1308_CAL_OFFSET_PWM_L 0x2c
+#define RT1308_CAL_OFFSET_PWM_R 0x2d
+#define RT1308_CAL_PWM_VOS_ADC_L 0x2e
+#define RT1308_CAL_PWM_VOS_ADC_R 0x2f
+#define RT1308_CLASS_D_SET_1 0x30
+#define RT1308_CLASS_D_SET_2 0x31
+#define RT1308_POWER 0x32
+#define RT1308_LDO 0x33
+#define RT1308_VREF 0x34
+#define RT1308_MBIAS 0x35
+#define RT1308_POWER_STATUS 0x36
+#define RT1308_POWER_INT 0x37
+#define RT1308_SINE_TONE_GEN_1 0x50
+#define RT1308_SINE_TONE_GEN_2 0x51
+#define RT1308_BQ_SET 0x54
+#define RT1308_BQ_PARA_UPDATE 0x55
+#define RT1308_BQ_PRE_VOL_L 0x56
+#define RT1308_BQ_PRE_VOL_R 0x57
+#define RT1308_BQ_POST_VOL_L 0x58
+#define RT1308_BQ_POST_VOL_R 0x59
+#define RT1308_BQ1_L_H0 0x5b
+#define RT1308_BQ1_L_B1 0x5c
+#define RT1308_BQ1_L_B2 0x5d
+#define RT1308_BQ1_L_A1 0x5e
+#define RT1308_BQ1_L_A2 0x5f
+#define RT1308_BQ1_R_H0 0x60
+#define RT1308_BQ1_R_B1 0x61
+#define RT1308_BQ1_R_B2 0x62
+#define RT1308_BQ1_R_A1 0x63
+#define RT1308_BQ1_R_A2 0x64
+#define RT1308_BQ2_L_H0 0x65
+#define RT1308_BQ2_L_B1 0x66
+#define RT1308_BQ2_L_B2 0x67
+#define RT1308_BQ2_L_A1 0x68
+#define RT1308_BQ2_L_A2 0x69
+#define RT1308_BQ2_R_H0 0x6a
+#define RT1308_BQ2_R_B1 0x6b
+#define RT1308_BQ2_R_B2 0x6c
+#define RT1308_BQ2_R_A1 0x6d
+#define RT1308_BQ2_R_A2 0x6e
+#define RT1308_VEN_DEV_ID 0x70
+#define RT1308_VERSION_ID 0x71
+#define RT1308_SPK_BOUND 0x72
+#define RT1308_BQ1_EQ_L_1 0x73
+#define RT1308_BQ1_EQ_L_2 0x74
+#define RT1308_BQ1_EQ_L_3 0x75
+#define RT1308_BQ1_EQ_R_1 0x76
+#define RT1308_BQ1_EQ_R_2 0x77
+#define RT1308_BQ1_EQ_R_3 0x78
+#define RT1308_BQ2_EQ_L_1 0x79
+#define RT1308_BQ2_EQ_L_2 0x7a
+#define RT1308_BQ2_EQ_L_3 0x7b
+#define RT1308_BQ2_EQ_R_1 0x7c
+#define RT1308_BQ2_EQ_R_2 0x7d
+#define RT1308_BQ2_EQ_R_3 0x7e
+#define RT1308_EFUSE_1 0x7f
+#define RT1308_EFUSE_2 0x80
+#define RT1308_EFUSE_PROG_PVDD_L 0x81
+#define RT1308_EFUSE_PROG_PVDD_R 0x82
+#define RT1308_EFUSE_PROG_R0_L 0x83
+#define RT1308_EFUSE_PROG_R0_R 0x84
+#define RT1308_EFUSE_PROG_DEV 0x85
+#define RT1308_EFUSE_READ_PVDD_L 0x86
+#define RT1308_EFUSE_READ_PVDD_R 0x87
+#define RT1308_EFUSE_READ_PVDD_PTBL 0x88
+#define RT1308_EFUSE_READ_DEV 0x89
+#define RT1308_EFUSE_READ_R0 0x8a
+#define RT1308_EFUSE_READ_ADC_L 0x8b
+#define RT1308_EFUSE_READ_ADC_R 0x8c
+#define RT1308_EFUSE_READ_ADC_PBTL 0x8d
+#define RT1308_EFUSE_RESERVE 0x8e
+#define RT1308_PADS_1 0x90
+#define RT1308_PADS_2 0x91
+#define RT1308_TEST_MODE 0xa0
+#define RT1308_TEST_1 0xa1
+#define RT1308_TEST_2 0xa2
+#define RT1308_TEST_3 0xa3
+#define RT1308_TEST_4 0xa4
+#define RT1308_EFUSE_DATA_0_MSB 0xb0
+#define RT1308_EFUSE_DATA_0_LSB 0xb1
+#define RT1308_EFUSE_DATA_1_MSB 0xb2
+#define RT1308_EFUSE_DATA_1_LSB 0xb3
+#define RT1308_EFUSE_DATA_2_MSB 0xb4
+#define RT1308_EFUSE_DATA_2_LSB 0xb5
+#define RT1308_EFUSE_DATA_3_MSB 0xb6
+#define RT1308_EFUSE_DATA_3_LSB 0xb7
+#define RT1308_EFUSE_DATA_TEST_MSB 0xb8
+#define RT1308_EFUSE_DATA_TEST_LSB 0xb9
+#define RT1308_EFUSE_STATUS_1 0xba
+#define RT1308_EFUSE_STATUS_2 0xbb
+#define RT1308_TCON_1 0xc0
+#define RT1308_TCON_2 0xc1
+#define RT1308_DUMMY_REG 0xf0
+#define RT1308_MAX_REG 0xff
+
+/* PLL1 M/N/K Code-1 (0x03) */
+#define RT1308_PLL1_K_SFT 24
+#define RT1308_PLL1_K_MASK (0x1f << 24)
+#define RT1308_PLL1_M_BYPASS_MASK (0x1 << 23)
+#define RT1308_PLL1_M_BYPASS_SFT 23
+#define RT1308_PLL1_M_BYPASS (0x1 << 23)
+#define RT1308_PLL1_M_MASK (0x3f << 16)
+#define RT1308_PLL1_M_SFT 16
+#define RT1308_PLL1_N_MASK (0x7f << 8)
+#define RT1308_PLL1_N_SFT 8
+
+/* CLOCK-1 (0x06) */
+#define RT1308_DIV_FS_SYS_MASK (0xf << 28)
+#define RT1308_DIV_FS_SYS_SFT 28
+#define RT1308_SEL_FS_SYS_MASK (0x7 << 24)
+#define RT1308_SEL_FS_SYS_SFT 24
+#define RT1308_SEL_FS_SYS_SRC_MCLK (0x0 << 24)
+#define RT1308_SEL_FS_SYS_SRC_BCLK (0x1 << 24)
+#define RT1308_SEL_FS_SYS_SRC_PLL (0x2 << 24)
+#define RT1308_SEL_FS_SYS_SRC_RCCLK (0x4 << 24)
+
+/* CLOCK-2 (0x08) */
+#define RT1308_DIV_PRE_PLL_MASK (0xf << 28)
+#define RT1308_DIV_PRE_PLL_SFT 28
+#define RT1308_SEL_PLL_SRC_MASK (0x7 << 24)
+#define RT1308_SEL_PLL_SRC_SFT 24
+#define RT1308_SEL_PLL_SRC_MCLK (0x0 << 24)
+#define RT1308_SEL_PLL_SRC_BCLK (0x1 << 24)
+#define RT1308_SEL_PLL_SRC_RCCLK (0x4 << 24)
+
+/* Clock Detect (0x0a) */
+#define RT1308_MCLK_DET_EN_MASK (0x1 << 25)
+#define RT1308_MCLK_DET_EN_SFT 25
+#define RT1308_MCLK_DET_EN (0x1 << 25)
+#define RT1308_BCLK_DET_EN_MASK (0x1 << 24)
+#define RT1308_BCLK_DET_EN_SFT 24
+#define RT1308_BCLK_DET_EN (0x1 << 24)
+
+/* DAC Setting (0x10) */
+#define RT1308_DVOL_MUTE_R_EN_SFT 7
+#define RT1308_DVOL_MUTE_L_EN_SFT 6
+
+/* I2S Setting-1 (0x15) */
+#define RT1308_I2S_DF_SEL_MASK (0x3 << 12)
+#define RT1308_I2S_DF_SEL_SFT 12
+#define RT1308_I2S_DF_SEL_I2S (0x0 << 12)
+#define RT1308_I2S_DF_SEL_LEFT (0x1 << 12)
+#define RT1308_I2S_DF_SEL_PCM_A (0x2 << 12)
+#define RT1308_I2S_DF_SEL_PCM_B (0x3 << 12)
+#define RT1308_I2S_DL_RX_SEL_MASK (0x7 << 4)
+#define RT1308_I2S_DL_RX_SEL_SFT 4
+#define RT1308_I2S_DL_RX_SEL_16B (0x0 << 4)
+#define RT1308_I2S_DL_RX_SEL_20B (0x1 << 4)
+#define RT1308_I2S_DL_RX_SEL_24B (0x2 << 4)
+#define RT1308_I2S_DL_RX_SEL_32B (0x3 << 4)
+#define RT1308_I2S_DL_RX_SEL_8B (0x4 << 4)
+#define RT1308_I2S_DL_TX_SEL_MASK (0x7 << 0)
+#define RT1308_I2S_DL_TX_SEL_SFT 0
+#define RT1308_I2S_DL_TX_SEL_16B (0x0 << 0)
+#define RT1308_I2S_DL_TX_SEL_20B (0x1 << 0)
+#define RT1308_I2S_DL_TX_SEL_24B (0x2 << 0)
+#define RT1308_I2S_DL_TX_SEL_32B (0x3 << 0)
+#define RT1308_I2S_DL_TX_SEL_8B (0x4 << 0)
+
+/* I2S Setting-2 (0x16) */
+#define RT1308_I2S_DL_SEL_MASK (0x7 << 24)
+#define RT1308_I2S_DL_SEL_SFT 24
+#define RT1308_I2S_DL_SEL_16B (0x0 << 24)
+#define RT1308_I2S_DL_SEL_20B (0x1 << 24)
+#define RT1308_I2S_DL_SEL_24B (0x2 << 24)
+#define RT1308_I2S_DL_SEL_32B (0x3 << 24)
+#define RT1308_I2S_DL_SEL_8B (0x4 << 24)
+#define RT1308_I2S_BCLK_MASK (0x1 << 14)
+#define RT1308_I2S_BCLK_SFT 14
+#define RT1308_I2S_BCLK_NORMAL (0x0 << 14)
+#define RT1308_I2S_BCLK_INV (0x1 << 14)
+
+/* Power Control-1 (0x32) */
+#define RT1308_POW_MBIAS20U (0x1 << 31)
+#define RT1308_POW_MBIAS20U_BIT 31
+#define RT1308_POW_ALDO (0x1 << 30)
+#define RT1308_POW_ALDO_BIT 30
+#define RT1308_POW_DBG (0x1 << 29)
+#define RT1308_POW_DBG_BIT 29
+#define RT1308_POW_DACL (0x1 << 28)
+#define RT1308_POW_DACL_BIT 28
+#define RT1308_POW_DAC1 (0x1 << 27)
+#define RT1308_POW_DAC1_BIT 27
+#define RT1308_POW_CLK25M (0x1 << 26)
+#define RT1308_POW_CLK25M_BIT 26
+#define RT1308_POW_ADC_R (0x1 << 25)
+#define RT1308_POW_ADC_R_BIT 25
+#define RT1308_POW_ADC_L (0x1 << 24)
+#define RT1308_POW_ADC_L_BIT 24
+#define RT1308_POW_DLDO (0x1 << 21)
+#define RT1308_POW_DLDO_BIT 21
+#define RT1308_POW_VREF (0x1 << 20)
+#define RT1308_POW_VREF_BIT 20
+#define RT1308_POW_MIXER_R (0x1 << 18)
+#define RT1308_POW_MIXER_R_BIT 18
+#define RT1308_POW_MIXER_L (0x1 << 17)
+#define RT1308_POW_MIXER_L_BIT 17
+#define RT1308_POW_MBIAS4U (0x1 << 16)
+#define RT1308_POW_MBIAS4U_BIT 16
+#define RT1308_POW_PLL2_LDO_EN (0x1 << 12)
+#define RT1308_POW_PLL2_LDO_EN_BIT 12
+#define RT1308_POW_PLL2B_EN (0x1 << 11)
+#define RT1308_POW_PLL2B_EN_BIT 11
+#define RT1308_POW_PLL2F_EN (0x1 << 10)
+#define RT1308_POW_PLL2F_EN_BIT 10
+#define RT1308_POW_PLL2F2_EN (0x1 << 9)
+#define RT1308_POW_PLL2F2_EN_BIT 9
+#define RT1308_POW_PLL2B2_EN (0x1 << 8)
+#define RT1308_POW_PLL2B2_EN_BIT 8
+
+/* Power Control-2 (0x36) */
+#define RT1308_POW_PDB_SRC_BIT (0x1 << 27)
+#define RT1308_POW_PDB_MN_BIT (0x1 << 25)
+#define RT1308_POW_PDB_REG_BIT (0x1 << 24)
+
+
+/* System Clock Source */
+enum {
+ RT1308_FS_SYS_S_MCLK,
+ RT1308_FS_SYS_S_BCLK,
+ RT1308_FS_SYS_S_PLL,
+ RT1308_FS_SYS_S_RCCLK, /* 25.0 MHz */
+};
+
+/* PLL Source */
+enum {
+ RT1308_PLL_S_MCLK,
+ RT1308_PLL_S_BCLK,
+ RT1308_PLL_S_RCCLK,
+};
+
+enum {
+ RT1308_AIF1,
+ RT1308_AIFS
+};
+
+#endif /* end of _RT1308_H_ */
--
2.7.4
2
1
[alsa-devel] ca0132 audio in Ubuntu 19.04 only after Windows 10 started, missing ctefx-r3di.bin
by Kai-Heng Feng 28 Jun '19
by Kai-Heng Feng 28 Jun '19
28 Jun '19
Hi Connor,
The bug was filed at Launchpad [1], I think the most notable error is
[ 3.768667] snd_hda_intel 0000:00:1f.3: Direct firmware load for
ctefx-r3di.bin failed with error -2
The firmware is indeed listed in patch_ca0132.c, but looks like there’s no
corresponding file in linux-firmware.
Can you please take a look at the bug?
[1] https://bugs.launchpad.net/bugs/1833470
Kai-Heng
3
3