Alsa-devel
Threads by month
- ----- 2025 -----
- April
- March
- February
- January
- ----- 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
July 2024
- 85 participants
- 229 discussions

Fwd: [PATCH] ALSA: hda/realtek: Implement sound init sequence for Samsung Galaxy Book3 Pro 360
by Nick Weihs 22 Jul '24
by Nick Weihs 22 Jul '24
22 Jul '24
Samsung Galaxy Book3 Pro 360 sends a large amount of data to the codec
through hda processing coefficients. This data was captured using a
modified version of QEMU, but the actual content of the data remains
opaque to me. Elliding any part of the data seems to cause sound to
not work.
Signed-off-by: Nick Weihs <nick.weihs(a)gmail.com>
---
sound/pci/hda/patch_realtek.c | 305 ++++++++++++++++++++++++++++++++++
1 file changed, 305 insertions(+)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index aa76d1c88589..cbe6d9689fc3 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -4796,6 +4796,304 @@ static void alc298_fixup_samsung_amp(struct
hda_codec *codec,
}
}
+static inline void alc298_samsung_write_coef_pack2(struct hda_codec *codec,
+ const unsigned short coefs[4])
+{
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ if ((alc_read_coef_idx(codec, 0x26) & 0x0010) == 0)
+ break;
+ }
+
+ alc_write_coef_idx(codec, 0x23, coefs[0]);
+ alc_write_coef_idx(codec, 0x24, coefs[1]);
+ alc_write_coef_idx(codec, 0x25, coefs[2]);
+ alc_write_coef_idx(codec, 0x26, coefs[3]);
+}
+
+static inline void alc298_samsung_write_coef_pack_seq(
+ struct hda_codec *codec,
+ unsigned char target,
+ const unsigned short (*sequence)[4],
+ int count)
+{
+ alc_write_coef_idx(codec, 0x22, target);
+ for (int i = 0; i < count; i++)
+ alc298_samsung_write_coef_pack2(codec, sequence[i]);
+}
+
+struct alc298_samsung_coeff_fixup_desc {
+ unsigned char coeff_idx;
+ unsigned short coeff_value;
+};
+
+static void alc298_fixup_samsung_amp2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ int i;
+ static const struct alc298_samsung_coeff_fixup_desc fixups1[] = {
+ { 0x99, 0x8000 }, { 0x82, 0x4408 }, { 0x32, 0x3f00 }, { 0x0e, 0x6f80 },
+ { 0x10, 0x0e21 }, { 0x55, 0x8000 }, { 0x08, 0x2fcf }, { 0x08, 0x2fcf },
+ { 0x2d, 0xc020 }, { 0x19, 0x0017 }, { 0x50, 0x1000 }, { 0x0e, 0x6f80 },
+ { 0x08, 0x2fcf }, { 0x80, 0x0011 }, { 0x2b, 0x0c10 }, { 0x2d, 0xc020 },
+ { 0x03, 0x0042 }, { 0x0f, 0x0062 }, { 0x08, 0x2fcf },
+ };
+
+ static const unsigned short amp_0x38[][4] = {
+ { 0x2000, 0x0000, 0x0001, 0xB011 }, { 0x23FF, 0x0000, 0x0000, 0xB011 },
+ { 0x203A, 0x0000, 0x0080, 0xB011 }, { 0x23E1, 0x0000, 0x0000, 0xB011 },
+ { 0x2012, 0x0000, 0x006F, 0xB011 }, { 0x2014, 0x0000, 0x0000, 0xB011 },
+ { 0x201B, 0x0000, 0x0001, 0xB011 }, { 0x201D, 0x0000, 0x0001, 0xB011 },
+ { 0x201F, 0x0000, 0x00FE, 0xB011 }, { 0x2021, 0x0000, 0x0000, 0xB011 },
+ { 0x2022, 0x0000, 0x0010, 0xB011 }, { 0x203D, 0x0000, 0x0005, 0xB011 },
+ { 0x203F, 0x0000, 0x0003, 0xB011 }, { 0x2050, 0x0000, 0x002C, 0xB011 },
+ { 0x2076, 0x0000, 0x000E, 0xB011 }, { 0x207C, 0x0000, 0x004A, 0xB011 },
+ { 0x2081, 0x0000, 0x0003, 0xB011 }, { 0x2399, 0x0000, 0x0003, 0xB011 },
+ { 0x23A4, 0x0000, 0x00B5, 0xB011 }, { 0x23A5, 0x0000, 0x0001, 0xB011 },
+ { 0x23BA, 0x0000, 0x0094, 0xB011 }, { 0x2100, 0x00D0, 0x950E, 0xB017 },
+ { 0x2104, 0x0061, 0xD4E2, 0xB017 }, { 0x2108, 0x00D0, 0x950E, 0xB017 },
+ { 0x210C, 0x0075, 0xF4E2, 0xB017 }, { 0x2110, 0x00B4, 0x4B0D, 0xB017 },
+ { 0x2114, 0x000A, 0x1000, 0xB017 }, { 0x2118, 0x0015, 0x2000, 0xB017 },
+ { 0x211C, 0x000A, 0x1000, 0xB017 }, { 0x2120, 0x0075, 0xF4E2, 0xB017 },
+ { 0x2124, 0x00B4, 0x4B0D, 0xB017 }, { 0x2128, 0x0000, 0x0010, 0xB017 },
+ { 0x212C, 0x0000, 0x0000, 0xB017 }, { 0x2130, 0x0000, 0x0000, 0xB017 },
+ { 0x2134, 0x0000, 0x0000, 0xB017 }, { 0x2138, 0x0000, 0x0000, 0xB017 },
+ { 0x213C, 0x0000, 0x0010, 0xB017 }, { 0x2140, 0x0000, 0x0000, 0xB017 },
+ { 0x2144, 0x0000, 0x0000, 0xB017 }, { 0x2148, 0x0000, 0x0000, 0xB017 },
+ { 0x214C, 0x0000, 0x0000, 0xB017 }, { 0x2150, 0x0000, 0x0010, 0xB017 },
+ { 0x2154, 0x0000, 0x0000, 0xB017 }, { 0x2158, 0x0000, 0x0000, 0xB017 },
+ { 0x215C, 0x0000, 0x0000, 0xB017 }, { 0x2160, 0x0000, 0x0000, 0xB017 },
+ { 0x2164, 0x0000, 0x0010, 0xB017 }, { 0x2168, 0x0000, 0x0000, 0xB017 },
+ { 0x216C, 0x0000, 0x0000, 0xB017 }, { 0x2170, 0x0000, 0x0000, 0xB017 },
+ { 0x2174, 0x0000, 0x0000, 0xB017 }, { 0x2178, 0x0000, 0x0010, 0xB017 },
+ { 0x217C, 0x0000, 0x0000, 0xB017 }, { 0x2180, 0x0000, 0x0000, 0xB017 },
+ { 0x2184, 0x0000, 0x0000, 0xB017 }, { 0x2188, 0x0000, 0x0000, 0xB017 },
+ { 0x218C, 0x0064, 0x5800, 0xB017 }, { 0x2190, 0x00C8, 0xB000, 0xB017 },
+ { 0x2194, 0x0064, 0x5800, 0xB017 }, { 0x2198, 0x003D, 0x5BE7, 0xB017 },
+ { 0x219C, 0x0054, 0x060A, 0xB017 }, { 0x21A0, 0x00C8, 0xA310, 0xB017 },
+ { 0x21A4, 0x0029, 0x4DE5, 0xB017 }, { 0x21A8, 0x0032, 0x420C, 0xB017 },
+ { 0x21AC, 0x0029, 0x4DE5, 0xB017 }, { 0x21B0, 0x00FA, 0xE50C, 0xB017 },
+ { 0x21B4, 0x0000, 0x0010, 0xB017 }, { 0x21B8, 0x0000, 0x0000, 0xB017 },
+ { 0x21BC, 0x0000, 0x0000, 0xB017 }, { 0x21C0, 0x0000, 0x0000, 0xB017 },
+ { 0x21C4, 0x0000, 0x0000, 0xB017 }, { 0x21C8, 0x0056, 0xC50F, 0xB017 },
+ { 0x21CC, 0x007B, 0xD7E1, 0xB017 }, { 0x21D0, 0x0077, 0xA70E, 0xB017 },
+ { 0x21D4, 0x00E0, 0xBDE1, 0xB017 }, { 0x21D8, 0x0032, 0x530E, 0xB017 },
+ { 0x2204, 0x00FB, 0x7E0F, 0xB017 }, { 0x2208, 0x000B, 0x02E1, 0xB017 },
+ { 0x220C, 0x00FB, 0x7E0F, 0xB017 }, { 0x2210, 0x00D5, 0x17E1, 0xB017 },
+ { 0x2214, 0x00C0, 0x130F, 0xB017 }, { 0x2218, 0x00E5, 0x0A00, 0xB017 },
+ { 0x221C, 0x00CB, 0x1500, 0xB017 }, { 0x2220, 0x00E5, 0x0A00, 0xB017 },
+ { 0x2224, 0x00D5, 0x17E1, 0xB017 }, { 0x2228, 0x00C0, 0x130F, 0xB017 },
+ { 0x222C, 0x00F5, 0xDB0E, 0xB017 }, { 0x2230, 0x0017, 0x48E2, 0xB017 },
+ { 0x2234, 0x00F5, 0xDB0E, 0xB017 }, { 0x2238, 0x00EF, 0x5CE2, 0xB017 },
+ { 0x223C, 0x00C1, 0xCC0D, 0xB017 }, { 0x2240, 0x00F5, 0xDB0E, 0xB017 },
+ { 0x2244, 0x0017, 0x48E2, 0xB017 }, { 0x2248, 0x00F5, 0xDB0E, 0xB017 },
+ { 0x224C, 0x00EF, 0x5CE2, 0xB017 }, { 0x2250, 0x00C1, 0xCC0D, 0xB017 },
+ { 0x2254, 0x00F5, 0xDB0E, 0xB017 }, { 0x2258, 0x0017, 0x48E2, 0xB017 },
+ { 0x225C, 0x00F5, 0xDB0E, 0xB017 }, { 0x2260, 0x00EF, 0x5CE2, 0xB017 },
+ { 0x2264, 0x00C1, 0xCC0D, 0xB017 }, { 0x2268, 0x00F5, 0xDB0E, 0xB017 },
+ { 0x226C, 0x0017, 0x48E2, 0xB017 }, { 0x2270, 0x00F5, 0xDB0E, 0xB017 },
+ { 0x2274, 0x00EF, 0x5CE2, 0xB017 }, { 0x2278, 0x00C1, 0xCC0D, 0xB017 },
+ { 0x227C, 0x00F5, 0xDB0E, 0xB017 }, { 0x2280, 0x0017, 0x48E2, 0xB017 },
+ { 0x2284, 0x00F5, 0xDB0E, 0xB017 }, { 0x2288, 0x00EF, 0x5CE2, 0xB017 },
+ { 0x228C, 0x00C1, 0xCC0D, 0xB017 }, { 0x22CC, 0x00E8, 0x8D00, 0xB017 },
+ { 0x22D0, 0x0000, 0x0000, 0xB017 }, { 0x22D4, 0x0018, 0x72FF, 0xB017 },
+ { 0x22D8, 0x00CE, 0x25E1, 0xB017 }, { 0x22DC, 0x002F, 0xE40E, 0xB017 },
+ { 0x238E, 0x0000, 0x0099, 0xB011 }, { 0x238F, 0x0000, 0x0011, 0xB011 },
+ { 0x2390, 0x0000, 0x0056, 0xB011 }, { 0x2391, 0x0000, 0x0004, 0xB011 },
+ { 0x2392, 0x0000, 0x00BB, 0xB011 }, { 0x2393, 0x0000, 0x006D, 0xB011 },
+ { 0x2394, 0x0000, 0x0010, 0xB011 }, { 0x2395, 0x0000, 0x0064, 0xB011 },
+ { 0x2396, 0x0000, 0x00B6, 0xB011 }, { 0x2397, 0x0000, 0x0028, 0xB011 },
+ { 0x2398, 0x0000, 0x000B, 0xB011 }, { 0x239A, 0x0000, 0x0099, 0xB011 },
+ { 0x239B, 0x0000, 0x000D, 0xB011 }, { 0x23A6, 0x0000, 0x0064, 0xB011 },
+ { 0x23A7, 0x0000, 0x0078, 0xB011 }, { 0x23B9, 0x0000, 0x0000, 0xB011 },
+ { 0x23E0, 0x0000, 0x0021, 0xB011 }, { 0x23E1, 0x0000, 0x0001, 0xB011 },
+ };
+
+ static const unsigned short amp_0x39[][4] = {
+ { 0x2000, 0x0000, 0x0001, 0xB011 }, { 0x23FF, 0x0000, 0x0000, 0xB011 },
+ { 0x203A, 0x0000, 0x0080, 0xB011 }, { 0x23E1, 0x0000, 0x0000, 0xB011 },
+ { 0x2012, 0x0000, 0x006F, 0xB011 }, { 0x2014, 0x0000, 0x0000, 0xB011 },
+ { 0x201B, 0x0000, 0x0002, 0xB011 }, { 0x201D, 0x0000, 0x0002, 0xB011 },
+ { 0x201F, 0x0000, 0x00FD, 0xB011 }, { 0x2021, 0x0000, 0x0001, 0xB011 },
+ { 0x2022, 0x0000, 0x0010, 0xB011 }, { 0x203D, 0x0000, 0x0005, 0xB011 },
+ { 0x203F, 0x0000, 0x0003, 0xB011 }, { 0x2050, 0x0000, 0x002C, 0xB011 },
+ { 0x2076, 0x0000, 0x000E, 0xB011 }, { 0x207C, 0x0000, 0x004A, 0xB011 },
+ { 0x2081, 0x0000, 0x0003, 0xB011 }, { 0x2399, 0x0000, 0x0003, 0xB011 },
+ { 0x23A4, 0x0000, 0x00B5, 0xB011 }, { 0x23A5, 0x0000, 0x0001, 0xB011 },
+ { 0x23BA, 0x0000, 0x0094, 0xB011 }, { 0x2100, 0x00D0, 0x950E, 0xB017 },
+ { 0x2104, 0x0061, 0xD4E2, 0xB017 }, { 0x2108, 0x00D0, 0x950E, 0xB017 },
+ { 0x210C, 0x0075, 0xF4E2, 0xB017 }, { 0x2110, 0x00B4, 0x4B0D, 0xB017 },
+ { 0x2114, 0x000A, 0x1000, 0xB017 }, { 0x2118, 0x0015, 0x2000, 0xB017 },
+ { 0x211C, 0x000A, 0x1000, 0xB017 }, { 0x2120, 0x0075, 0xF4E2, 0xB017 },
+ { 0x2124, 0x00B4, 0x4B0D, 0xB017 }, { 0x2128, 0x0000, 0x0010, 0xB017 },
+ { 0x212C, 0x0000, 0x0000, 0xB017 }, { 0x2130, 0x0000, 0x0000, 0xB017 },
+ { 0x2134, 0x0000, 0x0000, 0xB017 }, { 0x2138, 0x0000, 0x0000, 0xB017 },
+ { 0x213C, 0x0000, 0x0010, 0xB017 }, { 0x2140, 0x0000, 0x0000, 0xB017 },
+ { 0x2144, 0x0000, 0x0000, 0xB017 }, { 0x2148, 0x0000, 0x0000, 0xB017 },
+ { 0x214C, 0x0000, 0x0000, 0xB017 }, { 0x2150, 0x0000, 0x0010, 0xB017 },
+ { 0x2154, 0x0000, 0x0000, 0xB017 }, { 0x2158, 0x0000, 0x0000, 0xB017 },
+ { 0x215C, 0x0000, 0x0000, 0xB017 }, { 0x2160, 0x0000, 0x0000, 0xB017 },
+ { 0x2164, 0x0000, 0x0010, 0xB017 }, { 0x2168, 0x0000, 0x0000, 0xB017 },
+ { 0x216C, 0x0000, 0x0000, 0xB017 }, { 0x2170, 0x0000, 0x0000, 0xB017 },
+ { 0x2174, 0x0000, 0x0000, 0xB017 }, { 0x2178, 0x0000, 0x0010, 0xB017 },
+ { 0x217C, 0x0000, 0x0000, 0xB017 }, { 0x2180, 0x0000, 0x0000, 0xB017 },
+ { 0x2184, 0x0000, 0x0000, 0xB017 }, { 0x2188, 0x0000, 0x0000, 0xB017 },
+ { 0x218C, 0x0064, 0x5800, 0xB017 }, { 0x2190, 0x00C8, 0xB000, 0xB017 },
+ { 0x2194, 0x0064, 0x5800, 0xB017 }, { 0x2198, 0x003D, 0x5BE7, 0xB017 },
+ { 0x219C, 0x0054, 0x060A, 0xB017 }, { 0x21A0, 0x00C8, 0xA310, 0xB017 },
+ { 0x21A4, 0x0029, 0x4DE5, 0xB017 }, { 0x21A8, 0x0032, 0x420C, 0xB017 },
+ { 0x21AC, 0x0029, 0x4DE5, 0xB017 }, { 0x21B0, 0x00FA, 0xE50C, 0xB017 },
+ { 0x21B4, 0x0000, 0x0010, 0xB017 }, { 0x21B8, 0x0000, 0x0000, 0xB017 },
+ { 0x21BC, 0x0000, 0x0000, 0xB017 }, { 0x21C0, 0x0000, 0x0000, 0xB017 },
+ { 0x21C4, 0x0000, 0x0000, 0xB017 }, { 0x21C8, 0x0056, 0xC50F, 0xB017 },
+ { 0x21CC, 0x007B, 0xD7E1, 0xB017 }, { 0x21D0, 0x0077, 0xA70E, 0xB017 },
+ { 0x21D4, 0x00E0, 0xBDE1, 0xB017 }, { 0x21D8, 0x0032, 0x530E, 0xB017 },
+ { 0x2204, 0x00FB, 0x7E0F, 0xB017 }, { 0x2208, 0x000B, 0x02E1, 0xB017 },
+ { 0x220C, 0x00FB, 0x7E0F, 0xB017 }, { 0x2210, 0x00D5, 0x17E1, 0xB017 },
+ { 0x2214, 0x00C0, 0x130F, 0xB017 }, { 0x2218, 0x00E5, 0x0A00, 0xB017 },
+ { 0x221C, 0x00CB, 0x1500, 0xB017 }, { 0x2220, 0x00E5, 0x0A00, 0xB017 },
+ { 0x2224, 0x00D5, 0x17E1, 0xB017 }, { 0x2228, 0x00C0, 0x130F, 0xB017 },
+ { 0x222C, 0x00F5, 0xDB0E, 0xB017 }, { 0x2230, 0x0017, 0x48E2, 0xB017 },
+ { 0x2234, 0x00F5, 0xDB0E, 0xB017 }, { 0x2238, 0x00EF, 0x5CE2, 0xB017 },
+ { 0x223C, 0x00C1, 0xCC0D, 0xB017 }, { 0x2240, 0x00F5, 0xDB0E, 0xB017 },
+ { 0x2244, 0x0017, 0x48E2, 0xB017 }, { 0x2248, 0x00F5, 0xDB0E, 0xB017 },
+ { 0x224C, 0x00EF, 0x5CE2, 0xB017 }, { 0x2250, 0x00C1, 0xCC0D, 0xB017 },
+ { 0x2254, 0x00F5, 0xDB0E, 0xB017 }, { 0x2258, 0x0017, 0x48E2, 0xB017 },
+ { 0x225C, 0x00F5, 0xDB0E, 0xB017 }, { 0x2260, 0x00EF, 0x5CE2, 0xB017 },
+ { 0x2264, 0x00C1, 0xCC0D, 0xB017 }, { 0x2268, 0x00F5, 0xDB0E, 0xB017 },
+ { 0x226C, 0x0017, 0x48E2, 0xB017 }, { 0x2270, 0x00F5, 0xDB0E, 0xB017 },
+ { 0x2274, 0x00EF, 0x5CE2, 0xB017 }, { 0x2278, 0x00C1, 0xCC0D, 0xB017 },
+ { 0x227C, 0x00F5, 0xDB0E, 0xB017 }, { 0x2280, 0x0017, 0x48E2, 0xB017 },
+ { 0x2284, 0x00F5, 0xDB0E, 0xB017 }, { 0x2288, 0x00EF, 0x5CE2, 0xB017 },
+ { 0x228C, 0x00C1, 0xCC0D, 0xB017 }, { 0x22CC, 0x00E8, 0x8D00, 0xB017 },
+ { 0x22D0, 0x0000, 0x0000, 0xB017 }, { 0x22D4, 0x0018, 0x72FF, 0xB017 },
+ { 0x22D8, 0x00CE, 0x25E1, 0xB017 }, { 0x22DC, 0x002F, 0xE40E, 0xB017 },
+ { 0x238E, 0x0000, 0x0099, 0xB011 }, { 0x238F, 0x0000, 0x0011, 0xB011 },
+ { 0x2390, 0x0000, 0x0056, 0xB011 }, { 0x2391, 0x0000, 0x0004, 0xB011 },
+ { 0x2392, 0x0000, 0x00BB, 0xB011 }, { 0x2393, 0x0000, 0x006D, 0xB011 },
+ { 0x2394, 0x0000, 0x0010, 0xB011 }, { 0x2395, 0x0000, 0x0064, 0xB011 },
+ { 0x2396, 0x0000, 0x00B6, 0xB011 }, { 0x2397, 0x0000, 0x0028, 0xB011 },
+ { 0x2398, 0x0000, 0x000B, 0xB011 }, { 0x239A, 0x0000, 0x0099, 0xB011 },
+ { 0x239B, 0x0000, 0x000D, 0xB011 }, { 0x23A6, 0x0000, 0x0064, 0xB011 },
+ { 0x23A7, 0x0000, 0x0078, 0xB011 }, { 0x23B9, 0x0000, 0x0000, 0xB011 },
+ { 0x23E0, 0x0000, 0x0021, 0xB011 }, { 0x23E1, 0x0000, 0x0001, 0xB011 },
+ };
+
+ static const unsigned short amp_0x3c[][4] = {
+ { 0x2000, 0x0000, 0x0001, 0xB011 }, { 0x23FF, 0x0000, 0x0000, 0xB011 },
+ { 0x203A, 0x0000, 0x0080, 0xB011 }, { 0x23E1, 0x0000, 0x0000, 0xB011 },
+ { 0x2012, 0x0000, 0x006F, 0xB011 }, { 0x2014, 0x0000, 0x0000, 0xB011 },
+ { 0x201B, 0x0000, 0x0001, 0xB011 }, { 0x201D, 0x0000, 0x0001, 0xB011 },
+ { 0x201F, 0x0000, 0x00FE, 0xB011 }, { 0x2021, 0x0000, 0x0000, 0xB011 },
+ { 0x2022, 0x0000, 0x0010, 0xB011 }, { 0x203D, 0x0000, 0x0005, 0xB011 },
+ { 0x203F, 0x0000, 0x0003, 0xB011 }, { 0x2050, 0x0000, 0x002C, 0xB011 },
+ { 0x2076, 0x0000, 0x000E, 0xB011 }, { 0x207C, 0x0000, 0x004A, 0xB011 },
+ { 0x2081, 0x0000, 0x0003, 0xB011 }, { 0x23BA, 0x0000, 0x008D, 0xB011 },
+ { 0x2128, 0x0005, 0x460D, 0xB017 }, { 0x212C, 0x00F6, 0x73E5, 0xB017 },
+ { 0x2130, 0x0005, 0x460D, 0xB017 }, { 0x2134, 0x00C0, 0xE9E5, 0xB017 },
+ { 0x2138, 0x00D5, 0x010B, 0xB017 }, { 0x213C, 0x009D, 0x7809, 0xB017 },
+ { 0x2140, 0x00C5, 0x0EED, 0xB017 }, { 0x2144, 0x009D, 0x7809, 0xB017 },
+ { 0x2148, 0x00C4, 0x4EF0, 0xB017 }, { 0x214C, 0x003A, 0x3106, 0xB017 },
+ { 0x2150, 0x00AF, 0x750E, 0xB017 }, { 0x2154, 0x008C, 0x1FF1, 0xB017 },
+ { 0x2158, 0x009E, 0x360C, 0xB017 }, { 0x215C, 0x008C, 0x1FF1, 0xB017 },
+ { 0x2160, 0x004D, 0xAC0A, 0xB017 }, { 0x2164, 0x007D, 0xA00F, 0xB017 },
+ { 0x2168, 0x00E1, 0x9CE3, 0xB017 }, { 0x216C, 0x00E8, 0x590E, 0xB017 },
+ { 0x2170, 0x00E1, 0x9CE3, 0xB017 }, { 0x2174, 0x0066, 0xFA0D, 0xB017 },
+ { 0x2178, 0x0000, 0x0010, 0xB017 }, { 0x217C, 0x0000, 0x0000, 0xB017 },
+ { 0x2180, 0x0000, 0x0000, 0xB017 }, { 0x2184, 0x0000, 0x0000, 0xB017 },
+ { 0x2188, 0x0000, 0x0000, 0xB017 }, { 0x218C, 0x0000, 0x0010, 0xB017 },
+ { 0x2190, 0x0000, 0x0000, 0xB017 }, { 0x2194, 0x0000, 0x0000, 0xB017 },
+ { 0x2198, 0x0000, 0x0000, 0xB017 }, { 0x219C, 0x0000, 0x0000, 0xB017 },
+ { 0x21A0, 0x0000, 0x0010, 0xB017 }, { 0x21A4, 0x0000, 0x0000, 0xB017 },
+ { 0x21A8, 0x0000, 0x0000, 0xB017 }, { 0x21AC, 0x0000, 0x0000, 0xB017 },
+ { 0x21B0, 0x0000, 0x0000, 0xB017 }, { 0x21B4, 0x0000, 0x0010, 0xB017 },
+ { 0x21B8, 0x0000, 0x0000, 0xB017 }, { 0x21BC, 0x0000, 0x0000, 0xB017 },
+ { 0x21C0, 0x0000, 0x0000, 0xB017 }, { 0x21C4, 0x0000, 0x0000, 0xB017 },
+ { 0x23B9, 0x0000, 0x0000, 0xB011 }, { 0x23E0, 0x0000, 0x0020, 0xB011 },
+ { 0x23E1, 0x0000, 0x0001, 0xB011 },
+ };
+
+ static const unsigned short amp_0x3d[][4] = {
+ { 0x2000, 0x0000, 0x0001, 0xB011 }, { 0x23FF, 0x0000, 0x0000, 0xB011 },
+ { 0x203A, 0x0000, 0x0080, 0xB011 }, { 0x23E1, 0x0000, 0x0000, 0xB011 },
+ { 0x2012, 0x0000, 0x006F, 0xB011 }, { 0x2014, 0x0000, 0x0000, 0xB011 },
+ { 0x201B, 0x0000, 0x0002, 0xB011 }, { 0x201D, 0x0000, 0x0002, 0xB011 },
+ { 0x201F, 0x0000, 0x00FD, 0xB011 }, { 0x2021, 0x0000, 0x0001, 0xB011 },
+ { 0x2022, 0x0000, 0x0010, 0xB011 }, { 0x203D, 0x0000, 0x0005, 0xB011 },
+ { 0x203F, 0x0000, 0x0003, 0xB011 }, { 0x2050, 0x0000, 0x002C, 0xB011 },
+ { 0x2076, 0x0000, 0x000E, 0xB011 }, { 0x207C, 0x0000, 0x004A, 0xB011 },
+ { 0x2081, 0x0000, 0x0003, 0xB011 }, { 0x23BA, 0x0000, 0x008D, 0xB011 },
+ { 0x2128, 0x0005, 0x460D, 0xB017 }, { 0x212C, 0x00F6, 0x73E5, 0xB017 },
+ { 0x2130, 0x0005, 0x460D, 0xB017 }, { 0x2134, 0x00C0, 0xE9E5, 0xB017 },
+ { 0x2138, 0x00D5, 0x010B, 0xB017 }, { 0x213C, 0x009D, 0x7809, 0xB017 },
+ { 0x2140, 0x00C5, 0x0EED, 0xB017 }, { 0x2144, 0x009D, 0x7809, 0xB017 },
+ { 0x2148, 0x00C4, 0x4EF0, 0xB017 }, { 0x214C, 0x003A, 0x3106, 0xB017 },
+ { 0x2150, 0x00AF, 0x750E, 0xB017 }, { 0x2154, 0x008C, 0x1FF1, 0xB017 },
+ { 0x2158, 0x009E, 0x360C, 0xB017 }, { 0x215C, 0x008C, 0x1FF1, 0xB017 },
+ { 0x2160, 0x004D, 0xAC0A, 0xB017 }, { 0x2164, 0x007D, 0xA00F, 0xB017 },
+ { 0x2168, 0x00E1, 0x9CE3, 0xB017 }, { 0x216C, 0x00E8, 0x590E, 0xB017 },
+ { 0x2170, 0x00E1, 0x9CE3, 0xB017 }, { 0x2174, 0x0066, 0xFA0D, 0xB017 },
+ { 0x2178, 0x0000, 0x0010, 0xB017 }, { 0x217C, 0x0000, 0x0000, 0xB017 },
+ { 0x2180, 0x0000, 0x0000, 0xB017 }, { 0x2184, 0x0000, 0x0000, 0xB017 },
+ { 0x2188, 0x0000, 0x0000, 0xB017 }, { 0x218C, 0x0000, 0x0010, 0xB017 },
+ { 0x2190, 0x0000, 0x0000, 0xB017 }, { 0x2194, 0x0000, 0x0000, 0xB017 },
+ { 0x2198, 0x0000, 0x0000, 0xB017 }, { 0x219C, 0x0000, 0x0000, 0xB017 },
+ { 0x21A0, 0x0000, 0x0010, 0xB017 }, { 0x21A4, 0x0000, 0x0000, 0xB017 },
+ { 0x21A8, 0x0000, 0x0000, 0xB017 }, { 0x21AC, 0x0000, 0x0000, 0xB017 },
+ { 0x21B0, 0x0000, 0x0000, 0xB017 }, { 0x21B4, 0x0000, 0x0010, 0xB017 },
+ { 0x21B8, 0x0000, 0x0000, 0xB017 }, { 0x21BC, 0x0000, 0x0000, 0xB017 },
+ { 0x21C0, 0x0000, 0x0000, 0xB017 }, { 0x21C4, 0x0000, 0x0000, 0xB017 },
+ { 0x23B9, 0x0000, 0x0000, 0xB011 }, { 0x23E0, 0x0000, 0x0020, 0xB011 },
+ { 0x23E1, 0x0000, 0x0001, 0xB011 },
+ };
+
+ static const unsigned short amp_seq1[][4] = {
+ { 0x23FF, 0x0000, 0x0000, 0xB011 }, { 0x203A, 0x0000, 0x0080, 0xB011 },
+ };
+
+ static const struct alc298_samsung_coeff_fixup_desc fixups2[] = {
+ { 0x4f, 0xb029 }, { 0x05, 0x2be0 }, { 0x30, 0x2421 },
+ };
+
+
+ static const unsigned short amp_seq2[][4] = {
+ { 0x203A, 0x0000, 0x0081, 0xB011 }, { 0x23FF, 0x0000, 0x0001, 0xB011 },
+ };
+
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+
+ ///// First set of fixups
+ for (i = 0; i < ARRAY_SIZE(fixups1); i++)
+ alc_write_coef_idx(codec, fixups1[i].coeff_idx,
fixups1[i].coeff_value);
+
+ ///// First set of writes
+ alc298_samsung_write_coef_pack_seq(codec, 0x38, amp_0x38,
ARRAY_SIZE(amp_0x38));
+ alc298_samsung_write_coef_pack_seq(codec, 0x39, amp_0x39,
ARRAY_SIZE(amp_0x39));
+ alc298_samsung_write_coef_pack_seq(codec, 0x3c, amp_0x3c,
ARRAY_SIZE(amp_0x3c));
+ alc298_samsung_write_coef_pack_seq(codec, 0x3d, amp_0x3d,
ARRAY_SIZE(amp_0x3d));
+
+ ///// Second set of writes
+ alc298_samsung_write_coef_pack_seq(codec, 0x38, amp_seq1,
ARRAY_SIZE(amp_seq1));
+ alc298_samsung_write_coef_pack_seq(codec, 0x39, amp_seq1,
ARRAY_SIZE(amp_seq1));
+ alc298_samsung_write_coef_pack_seq(codec, 0x3c, amp_seq1,
ARRAY_SIZE(amp_seq1));
+ alc298_samsung_write_coef_pack_seq(codec, 0x3d, amp_seq1,
ARRAY_SIZE(amp_seq1));
+
+ ////// Second set of fixups
+ for (i = 0; i < ARRAY_SIZE(fixups2); i++)
+ alc_write_coef_idx(codec, fixups2[i].coeff_idx,
fixups2[i].coeff_value);
+
+ ///// Third set of writes
+ alc298_samsung_write_coef_pack_seq(codec, 0x38, amp_seq2,
ARRAY_SIZE(amp_seq2));
+ alc298_samsung_write_coef_pack_seq(codec, 0x39, amp_seq2,
ARRAY_SIZE(amp_seq2));
+ alc298_samsung_write_coef_pack_seq(codec, 0x3c, amp_seq2,
ARRAY_SIZE(amp_seq2));
+ alc298_samsung_write_coef_pack_seq(codec, 0x3d, amp_seq2,
ARRAY_SIZE(amp_seq2));
+
+ ///// Final fixup
+ alc_write_coef_idx(codec, 0x10, 0x0F21);
+}
+
#if IS_REACHABLE(CONFIG_INPUT)
static void gpio2_mic_hotkey_event(struct hda_codec *codec,
struct hda_jack_callback *event)
@@ -7426,6 +7724,7 @@ enum {
ALC236_FIXUP_HP_MUTE_LED,
ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF,
ALC298_FIXUP_SAMSUNG_AMP,
+ ALC298_FIXUP_SAMSUNG_AMP2,
ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
@@ -9021,6 +9320,10 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET
},
+ [ALC298_FIXUP_SAMSUNG_AMP2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_samsung_amp2
+ },
[ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = {
.type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
@@ -10352,6 +10655,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x144d, 0xc832, "Samsung Galaxy Book Flex Alpha
(NP730QCJ)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET),
SND_PCI_QUIRK(0x144d, 0xca03, "Samsung Galaxy Book2 Pro 360
(NP930QED)", ALC298_FIXUP_SAMSUNG_AMP),
SND_PCI_QUIRK(0x144d, 0xc868, "Samsung Galaxy Book2 Pro
(NP930XED)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc1ca, "Samsung Galaxy Book3 Pro 360
(NP960QFG-KB1US)", ALC298_FIXUP_SAMSUNG_AMP2),
SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807",
ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120",
ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)",
ALC283_FIXUP_HEADSET_MIC),
@@ -10781,6 +11085,7 @@ static const struct hda_model_fixup
alc269_fixup_models[] = {
{.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"},
{.id = ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, .name =
"alc256-medion-headset"},
{.id = ALC298_FIXUP_SAMSUNG_AMP, .name = "alc298-samsung-amp"},
+ {.id = ALC298_FIXUP_SAMSUNG_AMP2, .name = "alc298-samsung-amp2"},
{.id = ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, .name =
"alc256-samsung-headphone"},
{.id = ALC255_FIXUP_XIAOMI_HEADSET_MIC, .name = "alc255-xiaomi-headset"},
{.id = ALC274_FIXUP_HP_MIC, .name = "alc274-hp-mic-detect"},
--
2.45.2
1
0
This patch was used to add TAS2781 devices on SPI support in sound/pci/hda.
It use ACPI node descript about parameters of TAS2781 on SPI, it like:
Scope (_SB.PC00.SPI0)
{
Device (GSPK)
{
Name (_HID, "TXNW2781") // _HID: Hardware ID
Method (_CRS, 0, NotSerialized)
{
Name (RBUF, ResourceTemplate ()
{
SpiSerialBusV2 (...)
SpiSerialBusV2 (...)
}
}
}
}
And in platform/x86/serial-multi-instantiate.c, those spi devices will be
added into system as a single SPI device, so TAS2781 SPI driver will
probe twice for every single SPI device. And driver will also parser
mono DSP firmware binary and RCA binary for itself.
The code support Realtek as the primary codec.
In patch version-10, add multi devices firmware binary support,
to compatble with windows driver, they can share same firmware binary.
Signed-off-by: Baojun Xu <baojun.xu(a)ti.com>
---
v10:
- Remove project id 103c8b93 in Patch_realtek.c, as it was used
for test only, offcial project id is 103c8daa (Gemtree).
- Add TAS2781_REG_CLK_CONFIG for check if AMP has been reset.
- Add table for pre-reset and post-reset.
- Perform pre-reset config before reset AMP.
- Perform post-reset config after reset AMP.
- Fix wrong offset in calibration data issue.
- Remove tuning switch after firmware download.
- Force stop play in runtime suspend.
- Apply patch for component change in bind & unbind.
- Remove program download and calibration data apply,
switch tuning only in runtime resume.
- Check if AMP has reseted in system resume,
and do firmware download and calibration data apply.
- Change flag 0xCx to 0x8x for coeff data force download.
- Add device index for multi device firmware binary file compatible.
- Check device index before registers write during firmware download.
- Force config data download after program download.
- Remove config download in tuning switch.
v9:
- Add parentheses for variables in micro define.
- Add define for calibration registers address identify.
- Change variable name from calibration_data to cali_data.
- Add variable for calibration registers address in struct tasdevice_priv.
- Change format for alignment to parentheses.
- Remove tas2781_reg_defaults as it will be over write in firmware download.
- Remove RESET for second device in tas2781_spi_reset(),
as it will cause no sound on second device.
- Add calibration registers address initial in tasdevice_spi_init().
- Remove unnecessary parentheses.
- Remove exception in tas2781_hda_playback_hook().
- Remove hard code for calibration registers address.
- Add support for calibration data format V3 for registers address identify,
as new firmware tool will define calibration registers address dynamic,
so can't hard code it, need read it from BIOS with calibration data.
- Remove some debug print.
- Change hard code value (3) to ARRAY_SIZE(tas2781_snd_controls)/2.
- New patch apply for change position of component_del().
- Change label from out, out1 to config_err and block_err.
- Remove unnecessary parentheses in tas2781_spi_fwlib.c.
- Remove tasdevice_spi_prmg_calibdata_load().
v8:
- Move *-y position in sound/pci/hda/Makefile.
- Change + to | for bit operation in macro define.
- Change TASDEVICE_Checksum to TASDEVICE_CHECKSUM.
v7:
- Add new project Gemtree (0x103c8daa) support for tas2781 spi driver.
v6:
- Remove client in tasdevice_priv struct as it was not used.
- Change format for tasdevice_spi_dev_write() in tas2781_hda_spi.c.
- Change update bits function to read & write mode, as tas2781 use last bit
to mask read & write, it cause regmap_update_bits work not as our expected.
- Add element in tas2781_snd_controls, tas2781_prof_ctrl,
tas2781_dsp_conf_ctrl, to support second device.
Previous version add sound card for first index only.
- Change calibration registers address declaire to TASDEVICE_REG() mode.
- Add snd_ctl_new1() for second device.
- Change format for tas2781_hda_unbind() to fit 79 chars in one line.
- Remove client for spi as it was not used.
- Remove unused variables.
- Remove cur_prog clean in runtime_suspend().
- Remove unused variables.
- Add tasdevice_spi_tuning_switch() in runtime_resume.
- Remove cur_prog clean in system_resume().
- Add tuning switch if playback started in system_resume().
- Change TASDEVICE_PAGE_REG to TASDEVICE_REG_ID in tas2781_spi_fwlib.c.
- Remove calibration binary load functions as data will be loaded from EFI.
v5:
- Combined all modification into one patch for pass the compile.
- Remove old hardware id "TIAS2781" as no production on market.
- Change max page size to 256 in tas2781-spi.h.
- Change book reg from 127 to GENMASK(7, 1), as one bit shift is needed.
- Change register address define as one bit shift needed for offset.
- Change block check sum comments to Firmware block from I2C.
- Change define of (page + reg) to TASDEVICE_PAGE_REG from TASDEVICE_PGRG.
- Change to lower case in defined value.
- Add registers default value table in tas2781_hda_spi.c
- Change rang_max to 256 * 256.
- Add zero_flag_mask in regmap_config.
- Change max_register to 256 * 256.
- Add registers default table into structure regmap_config.
- Change parameter from book number to whole register address.
- Remove page 0 switch as SPI driver will do it.
- Add bit(0) for read function as tas2781 will check last bit of address,
to determine operation mode is read (1) or write (0).
- Change switch case to if check in tas2781_hda_playback_hook(),
as it may cause compile problem in clang-19 building.
- Change variable declaration position.
- Change order in if check.
- Remove old hardware id ("TIAS2781") support.
- Remove debug message in runtime suspend and resume.
- Remove regmap.h include in tas2781_spi_fwlib.c.
- Remove MODULE_ in fwlib as this file will work with tas2781_hda_spi.c only.
v4:
- Add old hardware id "TIAS2781" for compatible with old production
- Add 2 devices in struct smi_node tas2781_hda, to compatible with 4 AMPs
v4:
- Add old hardware id "TIAS2781" for compatible with old production
- Add 2 devices in struct smi_node tas2781_hda, to compatible with 4 AMPs
- Move include of sound/tas2781-dsp.h to c source file tas2781_hda_spi.c,
tas2781_spi_fwlib.c, as it's not needed in header file tas2781-spi.h
- Use page and book number define in other micro define
- Change 4000000 to (4 * HZ_PER_MHZ)
- Add define for calibration variable name and size in UEFI.
- Remove structure tasdevice & tasdevice_irqinfo, and move variables in to
structure tasdevice_priv
- Remove some unused variables in sttructure tasdevice_priv
- Remove function declare of tascodec_init(), use tascodec_spi_init()
- Remove function declare of tasdevice_remove()
- Add array_size.h included in tas2781_hda_spi.c
- Add cleanup.h included for change mutex to guard
- Add units.h included
- Include tas2781-dsp.h
- Remove unused variables in tas2781_hda structure
- Change 0xff to GENMASK(7, 0)
- Change 128 to define of max page size
- Change code as all variables was moved to tasdevice_priv structure
- Change comments for error in book write, or page write
- Remove initial of ret, and return 0 directly
- Change comments for wrong typo, add space in front and end
- Add check for bulk read, max size should be a page size
- Change usleep_rang() to fsleep()
- Change mutex_lock to guard(mutex), and remove mutex_unlock()
- Change tasdevice_spi_init() to void from int as no value return required
- Change second parameter to sizeof
- Remove tasdevice_clamp(), use clamp()
- Add compatible with old hardware id "TIAS2781"
- Remove cs_gpio and spi device in tas2781_read_acpi() as use default CS
- Change dev-index check, return -EINVAL if it's <= 0
- Change 0xFF to U8_MAX
- Remove GPIO get for chip-select, use default from SPI device
- Perform RESET in first AMP only as all AMPs share same RESET pin
- return 0 directly in some simple routine to avoid variable initial
- Change comments for function which was used by system
- Change "ON" "OFF" to function str_on_off(...)
- Change format of GUID
- Add temp buffer for first efi.get_variable()
- Free data if it was allocated
- Change format of debug message of calibration date time print
- Remove total_sz = 0, as it's not needed
- Move save_calibration to after tuning_switch to avoid calibration data
overwrite
- Remove dev from structure tasdevice_hda, use dev in tasdevice_priv
- Remove 0 from { "tas2781-hda", 0}, & {"TXNW2781", 0},
- Add old hardware id "TIAS2781" for compatible with old production
- Add 2 devices in struct smi_node tas2781_hda, to compatible with 4 AMPs
v3:
- Move HID up to above /* Non-conforming _HID ... */ in scan.c,
for avoid misunderstanding.
- Move HID up to above /* Non-conforming _HID ... */ in
serial-multi-instantiate.c, for avoid misunderstanding.
- Change objs to y for snd-hda-scodec-tas2781-spi- in Makefile.
- Change format for some define in tas2781-spi.h.
- Add define for TASDEVICE_MAX_BOOK_NUM, TASDEVICE_MAX_PAGE.
- Move declare of "enum calib_data {" to header file from source file.
- Remove "enum device_catlog_id {" as only one platform was supported now.
- Remove "struct calidata", as we will save pure calibration data only.
- Remove "struct calidata", "enum device_catlog_id" in tasdevice_priv.
- Add calibration_data in tasdevice_priv to save pure calibration data.
- Remove declare of tasdevice_save_calibration() and
tasdevice_apply_calibration(), as it will be used within same c file.
- Add below header files included in tas2781_hda_spi.c:
bits.h, mutex.h, property.h, time.h, types.h.
- Move two micro define to header file tas2781-spi.h.
- Change format of some micro define.
- Change format of some structure.
- Remove irq in tas2781_hda, as have it already in tasdevice_priv.
- Remove some local functions declare as not necessarily.
- Return error if regmap_write() failed.
- Change fixed value 2 to sizeof(data).
- Remove all of EXPORT_SYMBOL_GPL,
as all of function was called within same module.
- Remove empty line after last return in some functions.
- Remove some variable initialyzation.
- Remove variable sub, store acpi_subsystem_id directly.
- Remove wrong comments for device index, it's must, can't be NULL.
- Remove some local variables, use elements in structure directly.
- Append commas in last element of an array.
- Change calibration data process, didn't save all of calibration data in EFI,
get in local, and save pure calibration data in tasdevice_priv.
- Call tasdevice_save_calibration() from function pointer in tasdevice_priv;
- Remove subid as one platform was supported only, needn't check.
- Add initialyzation for local variable.
- Move regmap initialyzation to before acpi read.
- Call tasdevice_apply_calibration() from function pointer in tasdevice_priv;
- Remove MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB), as all functions was
performed within same module.
- Update format and variables declare order.
- Change format of multi conditions for if.
- Remove casting which is not needed.
- Change variables type to avoid casting.
- Remove Unneeded parentheses.
- Change if check to avoid goto.
- Add {} for keep same style.
- Remove some local variables, use elements in structure directly.
v2:
- Change depends on SPI to SPI_MASTER in sound/pci/hda/Kconfig
- Add select SND_HDA_SCODEC_COMPONENT in sound/pci/hda/Kconfig
- Change comp_generic_fixup() 5th parameter from "-%s:00" to
"-%s:00-tas2781-hda.%d" in sound/pci/hda/patch_realtek.c
- Change chain_id from ALC285_FIXUP_THINKPAD_HEADSET_JACK to
ALC285_FIXUP_HP_GPIO_LED in sound/pci/hda/patch_realtek.c
- Remove project name "Gemtree"
- Update information in file header of tas2781-spi.h.
- Remove useless micro define.
- Change TASDEVICE_I2CChecksum to TASDEVICE_Checksum
- Remove enum control_bus as current code support SPI only.
- Remove device define as current code support TAS2781 only.
- Remove spi_device **spi_devs in structure tasdevice_priv.
- Remove cal_binaryname in structure tasdevice_priv.
- Remove ndev in structure tasdevice_priv.
- Change isacpi and isspi, replace by control_bus(I2C or SPI).
- Remove void tasdevice_spi_dsp_remove(void *context).
- Add acpi_device and irq in structure tas2781_hda in tas2781_hda_spi.c.
- Remove parameter chn in all of registers access functions.
- Add tascodec_spi_init().
- Add chip select gpio set for SPI device.
- Change tasdevice_tuning_switch() to tasdevice_spi_tuning_switch().
- Change device offset from tas_priv->ndev to tas_priv->index.
- Change tasdevice_dsp_remove() to tasdevice_spi_dsp_remove().
- tasdevice_prmg_load() to tasdevice_spi_prmg_load().
- Change tasdevice_config_info_remove() to
tasdevice_spi_config_info_remove().
- Add one dummy byte shift when read from page 2~127, or not book 0.
- Change tasdevice_spi_change_chn_book() to tasdevice_spi_switch_book()
- Perform put_device(physdev) before error return.
- Change calibration data id to 2781 from 2783 in EFI
- Change calibration data structure name in comments.
- Remove file name in tas2781_spi_fwlib.c.
- Remove #include <linux/i2c.h> as it not needed in SPI mode.
- Change TAB to speace in micro define in tas2781_spi_fwlib.c.
- Change to up-case for DSP.
- Change all of multi-line comments format, empty first line.
- Change all of sizeof(struct xx) to sizeof(*xx).
- Remove Explicit casting for variables compare to avoid possible fault.
- Return directly without goto.
- Change 1 << xx to BIT(xx).
- Remove deviceNumber[] as current SPI device will support one device only.
- Add memory free before return error.
- Change "buf[offset]; offset += 1" to buf[offset++].
- Remove some debug information print.
- Change firmware binary to single device mode.
- Change all bexx_to_cpup() to get_unaligned_bexx().
- Remove ndev process as current SPI device support single device only.
- Remove chn and chnend for single device support only.
- Change all of registers read/write function, remove parameter chn.
- Create new function for single write, burst write, delay, field write
in tasdevice_process_block().
---
drivers/acpi/scan.c | 1 +
.../platform/x86/serial-multi-instantiate.c | 12 +
sound/pci/hda/Kconfig | 14 +
sound/pci/hda/Makefile | 2 +
sound/pci/hda/patch_realtek.c | 13 +
sound/pci/hda/tas2781-spi.h | 158 ++
sound/pci/hda/tas2781_hda_spi.c | 1317 ++++++++++
sound/pci/hda/tas2781_spi_fwlib.c | 2115 +++++++++++++++++
8 files changed, 3632 insertions(+)
create mode 100644 sound/pci/hda/tas2781-spi.h
create mode 100644 sound/pci/hda/tas2781_hda_spi.c
create mode 100644 sound/pci/hda/tas2781_spi_fwlib.c
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 59771412686b..b1a8bf1043da 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1769,6 +1769,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
{"CSC3557", },
{"INT33FE", },
{"INT3515", },
+ {"TXNW2781", },
/* Non-conforming _HID for Cirrus Logic already released */
{"CLSA0100", },
{"CLSA0101", },
diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c
index 3be016cfe601..b15c819023d0 100644
--- a/drivers/platform/x86/serial-multi-instantiate.c
+++ b/drivers/platform/x86/serial-multi-instantiate.c
@@ -368,6 +368,17 @@ static const struct smi_node cs35l57_hda = {
.bus_type = SMI_AUTO_DETECT,
};
+static const struct smi_node tas2781_hda = {
+ .instances = {
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ {}
+ },
+ .bus_type = SMI_AUTO_DETECT,
+};
+
/*
* Note new device-ids must also be added to ignore_serial_bus_ids in
* drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
@@ -380,6 +391,7 @@ static const struct acpi_device_id smi_acpi_ids[] = {
{ "CSC3556", (unsigned long)&cs35l56_hda },
{ "CSC3557", (unsigned long)&cs35l57_hda },
{ "INT3515", (unsigned long)&int3515_data },
+ { "TXNW2781", (unsigned long)&tas2781_hda },
/* Non-conforming _HID for Cirrus Logic already released */
{ "CLSA0100", (unsigned long)&cs35l41_hda },
{ "CLSA0101", (unsigned long)&cs35l41_hda },
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index bb15a0248250..62cd9ba408e2 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -206,6 +206,20 @@ config SND_HDA_SCODEC_TAS2781_I2C
comment "Set to Y if you want auto-loading the side codec driver"
depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m
+config SND_HDA_SCODEC_TAS2781_SPI
+ tristate "Build TAS2781 HD-audio side codec support for SPI Bus"
+ depends on SPI_MASTER
+ depends on ACPI
+ depends on EFI
+ depends on SND_SOC
+ select CRC32_SARWATE
+ help
+ Say Y or M here to include TAS2781 SPI HD-audio side codec support
+ in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+ depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m
+
config SND_HDA_CODEC_REALTEK
tristate "Build Realtek HD-audio codec support"
select SND_HDA_GENERIC
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 80210f845df2..210c406dfbc5 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -40,6 +40,7 @@ snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o
snd-hda-cs-dsp-ctls-y := hda_cs_dsp_ctl.o
snd-hda-scodec-component-y := hda_component.o
snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o
+snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o tas2781_spi_fwlib.o
# common driver
obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
@@ -72,6 +73,7 @@ obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o
obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o
obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o
obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o
+obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o
# this must be the last entry after codec drivers;
# otherwise the codec patches won't be hooked before the PCI probe
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index c3a86a99f8c6..51707270943a 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -6924,6 +6924,11 @@ static void tas2781_fixup_i2c(struct hda_codec *cdc,
comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1);
}
+static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2);
+}
+
static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc,
const struct hda_fixup *fix, int action)
{
@@ -7501,6 +7506,7 @@ enum {
ALC236_FIXUP_DELL_DUAL_CODECS,
ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
ALC287_FIXUP_TAS2781_I2C,
+ ALC245_FIXUP_TAS2781_SPI_2,
ALC287_FIXUP_YOGA7_14ARB7_I2C,
ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
ALC245_FIXUP_HP_X360_MUTE_LEDS,
@@ -9726,6 +9732,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
},
+ [ALC245_FIXUP_TAS2781_SPI_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_spi,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_LED,
+ },
[ALC287_FIXUP_YOGA7_14ARB7_I2C] = {
.type = HDA_FIXUP_FUNC,
.v.func = yoga7_14arb7_fixup_i2c,
@@ -10263,6 +10275,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8cdf, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8ce0, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8daa, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2),
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
diff --git a/sound/pci/hda/tas2781-spi.h b/sound/pci/hda/tas2781-spi.h
new file mode 100644
index 000000000000..0cadfd18bde2
--- /dev/null
+++ b/sound/pci/hda/tas2781-spi.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+//
+// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2024 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2781 driver implements a flexible and configurable
+// algo coefficient setting for TAS2781 chips.
+//
+// Author: Baojun Xu <baojun.xu(a)ti.com>
+//
+
+#ifndef __TAS2781_SPI_H__
+#define __TAS2781_SPI_H__
+
+#define TASDEVICE_RATES \
+ (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200)
+
+#define TASDEVICE_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TASDEVICE_MAX_BOOK_NUM 256
+#define TASDEVICE_MAX_PAGE 256
+
+#define TASDEVICE_MAX_SIZE (TASDEVICE_MAX_BOOK_NUM * TASDEVICE_MAX_PAGE)
+
+/* PAGE Control Register (available in page0 of each book) */
+#define TASDEVICE_PAGE_SELECT 0x00
+#define TASDEVICE_BOOKCTL_PAGE 0x00
+#define TASDEVICE_BOOKCTL_REG GENMASK(7, 1)
+#define TASDEVICE_BOOK_ID(reg) (((reg) & GENMASK(24, 16)) >> 16)
+#define TASDEVICE_PAGE_ID(reg) (((reg) & GENMASK(15, 8)) >> 8)
+#define TASDEVICE_REG_ID(reg) (((reg) & GENMASK(7, 1)) >> 1)
+#define TASDEVICE_PAGE_REG(reg) ((reg) & GENMASK(15, 1))
+#define TASDEVICE_REG(book, page, reg) \
+ (((book) << 16) | ((page) << 8) | ((reg) << 1))
+
+/* Software Reset */
+#define TAS2781_REG_SWRESET TASDEVICE_REG(0x0, 0x0, 0x01)
+#define TAS2781_REG_SWRESET_RESET BIT(0)
+
+/* System Reset Check Register */
+#define TAS2781_REG_CLK_CONFIG TASDEVICE_REG(0x0, 0x0, 0x5c)
+#define TAS2781_REG_CLK_CONFIG_RESET (0x19)
+
+/* Block Checksum */
+#define TASDEVICE_CHECKSUM TASDEVICE_REG(0x0, 0x0, 0x7e)
+
+/* Volume control */
+#define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1a)
+#define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03)
+#define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1)
+
+#define TASDEVICE_CMD_SING_W 0x1
+#define TASDEVICE_CMD_BURST 0x2
+#define TASDEVICE_CMD_DELAY 0x3
+#define TASDEVICE_CMD_FIELD_W 0x4
+
+#define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ)
+
+#define TASDEVICE_CRC8_POLYNOMIAL 0x4d
+#define TASDEVICE_SPEAKER_CALIBRATION_SIZE 20
+
+/* Flag of calibration registers address. */
+#define TASDEVICE_CALIBRATION_REG_ADDRESS BIT(7)
+
+#define TASDEVICE_CALIBRATION_DATA_NAME L"CALI_DATA"
+#define TASDEVICE_CALIBRATION_DATA_SIZE 256
+
+enum calib_data {
+ R0_VAL = 0,
+ INV_R0,
+ R0LOW,
+ POWER,
+ TLIM,
+ CALIB_MAX
+};
+
+struct tasdevice_priv {
+ struct tasdevice_fw *cali_data_fmw;
+ struct tasdevice_rca rcabin;
+ struct tasdevice_fw *fmw;
+ struct gpio_desc *reset;
+ struct mutex codec_lock;
+ struct regmap *regmap;
+ struct device *dev;
+ struct tm tm;
+
+ const char *acpi_subsystem_id;
+ unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE];
+ unsigned char coef_binaryname[64];
+ unsigned char rca_binaryname[64];
+ unsigned char dev_name[32];
+
+ unsigned int err_code;
+ bool is_loading;
+ bool is_loaderr;
+ int cur_book;
+ int cur_prog;
+ int cur_conf;
+ int fw_state;
+ int index;
+ int irq;
+ void *codec;
+ bool force_fwload_status;
+ bool playback_started;
+ unsigned int cali_data[CALIB_MAX];
+ unsigned int cali_reg_array[CALIB_MAX];
+
+ int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv,
+ const struct firmware *fmw,
+ int offset);
+ int (*fw_parse_program_data)(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw,
+ int offset);
+ int (*tasdevice_load_block)(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block);
+
+ int (*save_calibration)(struct tasdevice_priv *tas_priv);
+ void (*apply_calibration)(struct tasdevice_priv *tas_priv);
+};
+
+int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
+ unsigned int reg, unsigned int *value);
+int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv,
+ unsigned int reg, unsigned int value);
+int tasdevice_spi_dev_bulk_write(struct tasdevice_priv *tas_priv,
+ unsigned int reg, unsigned char *p_data,
+ unsigned int n_length);
+int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
+ unsigned int reg, unsigned char *p_data,
+ unsigned int n_length);
+int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tasdevice,
+ unsigned int reg, unsigned int mask,
+ unsigned int value);
+
+void tasdevice_spi_select_cfg_blk(void *context, int conf_no,
+ unsigned char block_type);
+void tasdevice_spi_config_info_remove(void *context);
+int tasdevice_spi_dsp_parser(void *context);
+int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw);
+void tasdevice_spi_dsp_remove(void *context);
+void tasdevice_spi_calbin_remove(void *context);
+int tasdevice_spi_select_tuningprm_cfg(void *context, int prm, int cfg_no,
+ int rca_conf_no);
+int tasdevice_spi_prmg_load(void *context, int prm_no);
+int tasdevice_spi_prmg_calibdata_load(void *context, int prm_no);
+void tasdevice_spi_tuning_switch(void *context, int state);
+int tas2781_spi_load_calibration(void *context, char *file_name,
+ unsigned short i);
+#endif /* __TAS2781_SPI_H__ */
diff --git a/sound/pci/hda/tas2781_hda_spi.c b/sound/pci/hda/tas2781_hda_spi.c
new file mode 100644
index 000000000000..dd979cb46fce
--- /dev/null
+++ b/sound/pci/hda/tas2781_hda_spi.c
@@ -0,0 +1,1317 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA SPI driver
+//
+// Copyright 2024 Texas Instruments, Inc.
+//
+// Author: Baojun Xu <baojun.xu(a)ti.com>
+
+#include <linux/acpi.h>
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/firmware.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <sound/hda_codec.h>
+#include <sound/soc.h>
+#include <sound/tas2781-dsp.h>
+#include <sound/tlv.h>
+#include <sound/tas2781-tlv.h>
+
+#include "tas2781-spi.h"
+
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_component.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+
+/*
+ * No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD
+ * Define two controls, one is Volume control callbacks, the other is
+ * flag setting control callbacks.
+ */
+
+/* Volume control callbacks for tas2781 */
+#define ACARD_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \
+ xhandler_get, xhandler_put, tlv_array) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw_range, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) { \
+ .reg = xreg, .rreg = xreg, \
+ .shift = xshift, .rshift = xshift,\
+ .min = xmin, .max = xmax, .invert = xinvert, \
+ } \
+}
+
+/* Flag control callbacks for tas2781 */
+#define ACARD_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD, \
+ .name = xname, \
+ .info = snd_ctl_boolean_mono_info, \
+ .get = xhandler_get, \
+ .put = xhandler_put, \
+ .private_value = xdata, \
+}
+
+struct tas2781_hda {
+ struct tasdevice_priv *priv;
+ struct acpi_device *dacpi;
+ struct snd_kcontrol *dsp_prog_ctl;
+ struct snd_kcontrol *dsp_conf_ctl;
+ struct snd_kcontrol *snd_ctls[3];
+ struct snd_kcontrol *prof_ctl;
+};
+
+static const struct regmap_range_cfg tasdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = TASDEVICE_MAX_SIZE,
+ .selector_reg = TASDEVICE_PAGE_SELECT,
+ .selector_mask = GENMASK(7, 0),
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = TASDEVICE_MAX_PAGE,
+ },
+};
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .zero_flag_mask = true,
+ .cache_type = REGCACHE_NONE,
+ .ranges = tasdevice_ranges,
+ .num_ranges = ARRAY_SIZE(tasdevice_ranges),
+ .max_register = TASDEVICE_MAX_SIZE,
+};
+
+struct tasdevice_table {
+ int reg;
+ unsigned char data[8];
+};
+
+static struct tasdevice_table tas2781_pre_reset_table[] = {
+ /* Values setting for pre-reset. */
+ { TASDEVICE_REG(0x00, 0x00, 0x02), {0x01, 0x1a, },},
+ { TASDEVICE_REG(0x00, 0x01, 0x37), {0x01, 0x3a, },},
+ { TASDEVICE_REG(0x00, 0xfd, 0x0d), {0x01, 0x0d, },},
+ { TASDEVICE_REG(0x00, 0xfd, 0x06), {0x01, 0xc1, },},
+ { TASDEVICE_REG(0x00, 0x01, 0x19), {0x01, 0xe0, },},
+ { TASDEVICE_REG(0x00, 0xfd, 0x0d), {0x01, 0x0d, },},
+ { TASDEVICE_REG(0x00, 0xfd, 0x06), {0x01, 0xd5, },},
+ { TASDEVICE_REG(0x00, 0x00, 0x00), {0x01, 0x00, },},
+};
+
+static struct tasdevice_table tas2781_post_reset_table[] = {
+ /* Values setting for post-reset. */
+ { TASDEVICE_REG(0x00, 0x01, 0x37), {0x01, 0x3a, },},
+ { TASDEVICE_REG(0x00, 0xfd, 0x0d), {0x01, 0x0d, },},
+ { TASDEVICE_REG(0x00, 0xfd, 0x06), {0x01, 0xc1, },},
+ { TASDEVICE_REG(0x00, 0xfd, 0x06), {0x01, 0xd5, },},
+ { TASDEVICE_REG(0x00, 0xfd, 0x0a), {0x01, 0x01, },},
+ { TASDEVICE_REG(0x00, 0x00, 0x00), {0x01, 0x00, },},
+};
+
+static int tasdevice_spi_switch_book(struct tasdevice_priv *tas_priv, int reg)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ if (tas_priv->cur_book != TASDEVICE_BOOK_ID(reg)) {
+ ret = regmap_write(map, TASDEVICE_BOOKCTL_REG,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "Switch Book E=%d\n", ret);
+ return ret;
+ }
+ tas_priv->cur_book = TASDEVICE_BOOK_ID(reg);
+ }
+ return ret;
+}
+
+int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
+ unsigned int reg,
+ unsigned int *val)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ ret = tasdevice_spi_switch_book(tas_priv, reg);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * In our TAS2781 SPI mode, if read from other book (not book 0),
+ * or read from page number larger than 1 in book 0, one more byte
+ * read is needed, and first byte is a dummy byte, need to be ignored.
+ */
+ if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
+ unsigned char data[2];
+
+ ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1,
+ data, sizeof(data));
+ *val = data[1];
+ } else {
+ ret = regmap_read(map, TASDEVICE_PAGE_REG(reg) | 1, val);
+ }
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv,
+ unsigned int reg,
+ unsigned int value)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ ret = tasdevice_spi_switch_book(tas_priv, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(map, TASDEVICE_PAGE_REG(reg), value);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int tasdevice_spi_dev_bulk_write(struct tasdevice_priv *tas_priv,
+ unsigned int reg,
+ unsigned char *data,
+ unsigned int len)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ ret = tasdevice_spi_switch_book(tas_priv, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_bulk_write(map, TASDEVICE_PAGE_REG(reg), data, len);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
+ unsigned int reg,
+ unsigned char *data,
+ unsigned int len)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ ret = tasdevice_spi_switch_book(tas_priv, reg);
+ if (ret < 0)
+ return ret;
+
+ if (len > TASDEVICE_MAX_PAGE)
+ len = TASDEVICE_MAX_PAGE;
+ /*
+ * In our TAS2781 SPI mode, if read from other book (not book 0),
+ * or read from page number larger than 1 in book 0, one more byte
+ * read is needed, and first byte is a dummy byte, need to be ignored.
+ */
+ if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
+ unsigned char buf[TASDEVICE_MAX_PAGE+1];
+
+ ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1, buf,
+ len + 1);
+ memcpy(data, buf + 1, len);
+ } else {
+ ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1, data,
+ len);
+ }
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tas_priv,
+ unsigned int reg,
+ unsigned int mask,
+ unsigned int value)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret, val;
+
+ /*
+ * In our TAS2781 SPI mode, read/write was masked in last bit of
+ * address, it cause regmap_update_bits() not work as expected.
+ */
+ ret = tasdevice_spi_dev_read(tas_priv, reg, &val);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ return ret;
+ }
+ ret = regmap_write(map, TASDEVICE_PAGE_REG(reg),
+ (val & ~mask) | (mask & value));
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static void tas2781_spi_reset(struct tasdevice_priv *tas_dev)
+{
+ int ret;
+
+ if (tas_dev->reset) {
+ gpiod_set_value_cansleep(tas_dev->reset, 0);
+ fsleep(800);
+ gpiod_set_value_cansleep(tas_dev->reset, 1);
+ }
+ for (int j = 0; j >= 0; j++) {
+ if (tas2781_pre_reset_table[j].reg == 0)
+ break;
+ if (tas2781_pre_reset_table[j].data[0] > 1) {
+ tasdevice_spi_dev_bulk_write(tas_dev,
+ tas2781_pre_reset_table[j].reg,
+ &tas2781_pre_reset_table[j].data[1],
+ tas2781_pre_reset_table[j].data[0]);
+ } else {
+ tasdevice_spi_dev_write(tas_dev,
+ tas2781_pre_reset_table[j].reg,
+ tas2781_pre_reset_table[j].data[1]);
+ }
+ }
+ ret = tasdevice_spi_dev_write(tas_dev, TAS2781_REG_SWRESET,
+ TAS2781_REG_SWRESET_RESET);
+ if (ret < 0)
+ dev_err(tas_dev->dev, "dev sw-reset fail, %d\n", ret);
+ fsleep(1000);
+ for (int j = 0; j >= 0; j++) {
+ if (tas2781_post_reset_table[j].reg == 0)
+ break;
+ if (tas2781_post_reset_table[j].data[0] > 1) {
+ tasdevice_spi_dev_bulk_write(tas_dev,
+ tas2781_post_reset_table[j].reg,
+ &tas2781_post_reset_table[j].data[1],
+ tas2781_post_reset_table[j].data[0]);
+ } else {
+ tasdevice_spi_dev_write(tas_dev,
+ tas2781_post_reset_table[j].reg,
+ tas2781_post_reset_table[j].data[1]);
+ }
+ }
+}
+
+static int tascodec_spi_init(struct tasdevice_priv *tas_priv,
+ void *codec, struct module *module,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ int ret;
+
+ /*
+ * Codec Lock Hold to ensure that codec_probe and firmware parsing and
+ * loading do not simultaneously execute.
+ */
+ guard(mutex)(&tas_priv->codec_lock);
+
+ ret = scnprintf(tas_priv->rca_binaryname,
+ sizeof(tas_priv->rca_binaryname), "%sRCA%d.bin",
+ tas_priv->dev_name, tas_priv->index);
+ if (ret <= 0) {
+ dev_err(tas_priv->dev, "rca name err:0x%08x\n", ret);
+ return ret;
+ }
+ crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
+ tas_priv->codec = codec;
+ ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
+ tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
+ cont);
+ if (ret)
+ dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static void tasdevice_spi_init(struct tasdevice_priv *tas_priv)
+{
+ tas_priv->cur_prog = -1;
+ tas_priv->cur_conf = -1;
+
+ tas_priv->cur_book = -1;
+ tas_priv->cur_prog = -1;
+ tas_priv->cur_conf = -1;
+
+ /* Store default registers address for calibration data. */
+ tas_priv->cali_reg_array[0] = TASDEVICE_REG(0, 0x17, 0x74);
+ tas_priv->cali_reg_array[1] = TASDEVICE_REG(0, 0x18, 0x0c);
+ tas_priv->cali_reg_array[2] = TASDEVICE_REG(0, 0x18, 0x14);
+ tas_priv->cali_reg_array[3] = TASDEVICE_REG(0, 0x13, 0x70);
+ tas_priv->cali_reg_array[4] = TASDEVICE_REG(0, 0x18, 0x7c);
+
+ mutex_init(&tas_priv->codec_lock);
+}
+
+static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol,
+ struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask;
+ int max = mc->max;
+ int val, ret;
+
+ mask = rounddown_pow_of_two(max);
+ mask <<= mc->shift;
+ val = clamp(invert ? max - ucontrol->value.integer.value[0] :
+ ucontrol->value.integer.value[0], 0, max);
+ ret = tasdevice_spi_dev_update_bits(tas_priv,
+ mc->reg, mask, (unsigned int)(val << mc->shift));
+ if (ret)
+ dev_err(tas_priv->dev, "set AMP vol error in dev %d\n",
+ tas_priv->index);
+
+ return ret;
+}
+
+static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol,
+ struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask = 0;
+ int max = mc->max;
+ int ret, val;
+
+ /* Read the primary device */
+ ret = tasdevice_spi_dev_read(tas_priv, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
+ return ret;
+ }
+
+ mask = rounddown_pow_of_two(max);
+ mask <<= mc->shift;
+ val = (val & mask) >> mc->shift;
+ val = clamp(invert ? max - val : val, 0, max);
+ ucontrol->value.integer.value[0] = val;
+
+ return ret;
+}
+
+static int tasdevice_spi_digital_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol,
+ struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int val, ret;
+
+ val = clamp(invert ? max - ucontrol->value.integer.value[0] :
+ ucontrol->value.integer.value[0], 0, max);
+ ret = tasdevice_spi_dev_write(tas_priv, mc->reg, (unsigned int)val);
+ if (ret)
+ dev_err(tas_priv->dev, "set digital vol err in dev %d\n",
+ tas_priv->index);
+
+ return ret;
+}
+
+static int tasdevice_spi_digital_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol,
+ struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int ret, val;
+
+ /* Read the primary device as the whole */
+ ret = tasdevice_spi_dev_read(tas_priv, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get digital vol err\n", __func__);
+ return ret;
+ }
+
+ val = clamp(invert ? max - val : val, 0, max);
+ ucontrol->value.integer.value[0] = val;
+
+ return ret;
+}
+
+static int tas2781_read_acpi(struct tas2781_hda *tas_hda,
+ const char *hid,
+ int id)
+{
+ struct tasdevice_priv *p = tas_hda->priv;
+ struct acpi_device *adev;
+ struct device *physdev;
+ u32 values[HDA_MAX_COMPONENTS];
+ const char *property;
+ size_t nval;
+ int ret, i;
+
+ adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+ if (!adev) {
+ dev_err(p->dev, "Failed to find ACPI device: %s\n", hid);
+ return -ENODEV;
+ }
+
+ strscpy(p->dev_name, hid, sizeof(p->dev_name));
+ tas_hda->dacpi = adev;
+ physdev = get_device(acpi_get_first_physical_node(adev));
+ acpi_dev_put(adev);
+
+ /* No side-effect to the playback even if subsystem_id is NULL */
+ p->acpi_subsystem_id = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
+
+ property = "ti,dev-index";
+ ret = device_property_count_u32(physdev, property);
+ if (ret <= 0 || ret > ARRAY_SIZE(values)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ nval = ret;
+
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+
+ p->index = U8_MAX;
+ for (i = 0; i < nval; i++) {
+ if (values[i] == id) {
+ p->index = i;
+ break;
+ }
+ }
+ if (p->index == U8_MAX) {
+ dev_dbg(p->dev, "No index found in %s\n", property);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (p->index == 0) {
+ /* All of amps share same RESET pin. */
+ p->reset = devm_gpiod_get_index_optional(physdev, "reset",
+ p->index, GPIOD_OUT_LOW);
+ if (IS_ERR(p->reset)) {
+ dev_err_probe(p->dev, ret, "Failed on reset GPIO\n");
+ goto err;
+ }
+ }
+ put_device(physdev);
+
+ return 0;
+err:
+ dev_err(p->dev, "read acpi error, ret: %d\n", ret);
+ put_device(physdev);
+ acpi_dev_put(adev);
+
+ return ret;
+}
+
+static void tas2781_hda_playback_hook(struct device *dev, int action)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ if (action == HDA_GEN_PCM_ACT_OPEN) {
+ pm_runtime_get_sync(dev);
+ guard(mutex)(&tas_hda->priv->codec_lock);
+ tasdevice_spi_tuning_switch(tas_hda->priv, 0);
+ } else if (action == HDA_GEN_PCM_ACT_CLOSE) {
+ guard(mutex)(&tas_hda->priv->codec_lock);
+ tasdevice_spi_tuning_switch(tas_hda->priv, 1);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
+}
+
+static int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1;
+
+ return 0;
+}
+
+static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id;
+
+ return 0;
+}
+
+static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ int max = tas_priv->rcabin.ncfgs - 1;
+ int val;
+
+ val = clamp(ucontrol->value.integer.value[0], 0, max);
+ if (tas_priv->rcabin.profile_cfg_id != val) {
+ tas_priv->rcabin.profile_cfg_id = val;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->fmw->nr_programs - 1;
+
+ return 0;
+}
+
+static int tasdevice_info_config(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->fmw->nr_configurations - 1;
+
+ return 0;
+}
+
+static int tasdevice_program_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_prog;
+
+ return 0;
+}
+
+static int tasdevice_program_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ int nr_program = ucontrol->value.integer.value[0];
+ int max = tas_priv->fmw->nr_programs - 1;
+ int val;
+
+ val = clamp(nr_program, 0, max);
+
+ if (tas_priv->cur_prog != val) {
+ tas_priv->cur_prog = val;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int tasdevice_config_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_conf;
+
+ return 0;
+}
+
+static int tasdevice_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ int max = tas_priv->fmw->nr_configurations - 1;
+ int val;
+
+ val = clamp(ucontrol->value.integer.value[0], 0, max);
+
+ if (tas_priv->cur_conf != val) {
+ tas_priv->cur_conf = val;
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * tas2781_digital_getvol - get the volum control
+ * @kcontrol: control pointer
+ * @ucontrol: User data
+ *
+ * Customer Kcontrol for tas2781 is primarily for regmap booking, paging
+ * depends on internal regmap mechanism.
+ * tas2781 contains book and page two-level register map, especially
+ * book switching will set the register BXXP00R7F, after switching to the
+ * correct book, then leverage the mechanism for paging to access the
+ * register.
+ *
+ * Return 0 if succeeded.
+ */
+static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_spi_digital_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_spi_amp_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ /* The check of the given value is in tasdevice_digital_putvol. */
+ return tasdevice_spi_digital_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ /* The check of the given value is in tasdevice_amp_putvol. */
+ return tasdevice_spi_amp_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status;
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ str_on_off(tas_priv->force_fwload_status));
+
+ return 0;
+}
+
+static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ bool change, val = (bool)ucontrol->value.integer.value[0];
+
+ if (tas_priv->force_fwload_status == val) {
+ change = false;
+ } else {
+ change = true;
+ tas_priv->force_fwload_status = val;
+ }
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ str_on_off(tas_priv->force_fwload_status));
+
+ return change;
+}
+
+static const struct snd_kcontrol_new tas2781_snd_controls[] = {
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain 0", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, amp_vol_tlv),
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain 0", TAS2781_DVC_LVL,
+ 0, 0, 200, 1, tas2781_digital_getvol,
+ tas2781_digital_putvol, dvc_tlv),
+ ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load 0", 0,
+ tas2781_force_fwload_get, tas2781_force_fwload_put),
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain 1", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, amp_vol_tlv),
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain 1", TAS2781_DVC_LVL,
+ 0, 0, 200, 1, tas2781_digital_getvol,
+ tas2781_digital_putvol, dvc_tlv),
+ ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load 1", 0,
+ tas2781_force_fwload_get, tas2781_force_fwload_put),
+};
+
+static const struct snd_kcontrol_new tas2781_prof_ctrl[] = {
+{
+ .name = "Speaker Profile Id - 0",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_profile,
+ .get = tasdevice_get_profile_id,
+ .put = tasdevice_set_profile_id,
+},
+{
+ .name = "Speaker Profile Id - 1",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_profile,
+ .get = tasdevice_get_profile_id,
+ .put = tasdevice_set_profile_id,
+},
+};
+static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl[] = {
+{
+ .name = "Speaker Program Id 0",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_programs,
+ .get = tasdevice_program_get,
+ .put = tasdevice_program_put,
+},
+{
+ .name = "Speaker Program Id 1",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_programs,
+ .get = tasdevice_program_get,
+ .put = tasdevice_program_put,
+},
+};
+
+static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl[] = {
+{
+ .name = "Speaker Config Id 0",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_config,
+ .get = tasdevice_config_get,
+ .put = tasdevice_config_put,
+},
+{
+ .name = "Speaker Config Id 1",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_config,
+ .get = tasdevice_config_get,
+ .put = tasdevice_config_put,
+},
+};
+
+static void tas2781_apply_calib(struct tasdevice_priv *tas_priv)
+{
+ int j, rc;
+
+ /*
+ * If no calibration data exist in tasdevice_priv *tas_priv,
+ * calibration apply will be ignored, and use default values
+ * in firmware binary, which was loaded during firmware download.
+ */
+ if (tas_priv->cali_data[0] == 0)
+ return;
+ /*
+ * Calibration data was saved in tasdevice_priv *tas_priv as:
+ * unsigned int cali_data[CALIB_MAX];
+ * and every data (in 4 bytes) will be saved in register which in
+ * book 0, and page number in page_array[], offset was saved in
+ * rgno_array[].
+ */
+ for (j = 0; j < CALIB_MAX; j++) {
+ rc = tasdevice_spi_dev_bulk_write(tas_priv,
+ tas_priv->cali_reg_array[j],
+ (unsigned char *)&tas_priv->cali_data[j], 4);
+ if (rc < 0)
+ dev_err(tas_priv->dev,
+ "chn %d calib %d bulk_wr err = %d\n",
+ tas_priv->index, j, rc);
+ }
+}
+
+/*
+ * Update the calibration data, including speaker impedance, f0, etc,
+ * into algo. Calibrate data is done by manufacturer in the factory.
+ * These data are used by Algo for calculating the speaker temperature,
+ * speaker membrane excursion and f0 in real time during playback.
+ * Calibration data format in EFI is V2, since 2024.
+ */
+static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
+{
+ /*
+ * GUID was used for data access in BIOS, it was provided by board
+ * manufactory, like HP: "{02f9af02-7734-4233-b43d-93fe5aa35db3}"
+ */
+ efi_guid_t efi_guid =
+ EFI_GUID(0x02f9af02, 0x7734, 0x4233,
+ 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3);
+ static efi_char16_t efi_name[] = TASDEVICE_CALIBRATION_DATA_NAME;
+ unsigned char data[TASDEVICE_CALIBRATION_DATA_SIZE], *buf;
+ unsigned int attr, crc, offset, *tmp_val;
+ struct tm *tm = &tas_priv->tm;
+ unsigned long total_sz = 0;
+ efi_status_t status;
+
+ tas_priv->cali_data[0] = 0;
+ status = efi.get_variable(efi_name, &efi_guid, &attr, &total_sz, data);
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ if (total_sz > TASDEVICE_CALIBRATION_DATA_SIZE)
+ return -ENOMEM;
+ /* Get variable contents into buffer */
+ status = efi.get_variable(efi_name, &efi_guid, &attr,
+ &total_sz, data);
+ }
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ tmp_val = (unsigned int *)data;
+ if (tmp_val[0] == 2781) {
+ /*
+ * New features were added in calibrated Data V3:
+ * 1. Added calibration registers address define in
+ * a node, marked as Device id == 0x80.
+ * New features were added in calibrated Data V2:
+ * 1. Added some the fields to store the link_id and
+ * uniqie_id for multi-link solutions
+ * 2. Support flexible number of devices instead of
+ * fixed one in V1.
+ * Layout of calibrated data V2 in UEFI(total 256 bytes):
+ * ChipID (2781, 4 bytes)
+ * Device-Sum (4 bytes)
+ * TimeStamp of Calibration (4 bytes)
+ * for (i = 0; i < Device-Sum; i++) {
+ * Device #i index_info () {
+ * SDW link id (2bytes)
+ * SDW unique_id (2bytes)
+ * } // if Device number is 0x80, mean it's
+ * calibration registers address.
+ * Calibrated Data of Device #i (20 bytes)
+ * }
+ * CRC (4 bytes)
+ * Reserved (the rest)
+ */
+ crc = crc32(~0, data, (3 + tmp_val[1] * 6) * 4) ^ ~0;
+
+ if (crc != tmp_val[3 + tmp_val[1] * 6])
+ return 0;
+
+ time64_to_tm(tmp_val[2], 0, tm);
+ for (int j = 0; j < tmp_val[1]; j++) {
+ offset = j * 6 + 3;
+ if (tmp_val[offset] == tas_priv->index) {
+ for (int i = 0; i < CALIB_MAX; i++)
+ tas_priv->cali_data[i] =
+ tmp_val[offset + i + 1];
+ } else if (tmp_val[offset] ==
+ TASDEVICE_CALIBRATION_REG_ADDRESS) {
+ for (int i = 0; i < CALIB_MAX; i++) {
+ buf = &data[(offset + i + 1) * 4];
+ tas_priv->cali_reg_array[i] =
+ TASDEVICE_REG(buf[1], buf[2],
+ buf[3]);
+ }
+ }
+ tas_priv->apply_calibration(tas_priv);
+ }
+ } else {
+ /*
+ * Calibration data is in V1 format.
+ * struct cali_data {
+ * char cali_data[20];
+ * }
+ *
+ * struct {
+ * struct cali_data cali_data[4];
+ * int TimeStamp of Calibration (4 bytes)
+ * int CRC (4 bytes)
+ * } ueft;
+ */
+ crc = crc32(~0, data, 84) ^ ~0;
+ if (crc == tmp_val[21]) {
+ time64_to_tm(tmp_val[20], 0, tm);
+ for (int i = 0; i < CALIB_MAX; i++)
+ tas_priv->cali_data[i] =
+ tmp_val[tas_priv->index * 5 + i];
+ tas_priv->apply_calibration(tas_priv);
+ }
+ }
+
+ return 0;
+}
+
+static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda)
+{
+ struct hda_codec *codec = tas_hda->priv->codec;
+
+ snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl);
+
+ snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl);
+
+ for (int i = ARRAY_SIZE(tas_hda->snd_ctls) - 1; i >= 0; i--)
+ snd_ctl_remove(codec->card, tas_hda->snd_ctls[i]);
+
+ snd_ctl_remove(codec->card, tas_hda->prof_ctl);
+}
+
+static void tasdev_fw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
+ struct hda_codec *codec = tas_priv->codec;
+ int i, j, ret;
+
+ pm_runtime_get_sync(tas_priv->dev);
+ guard(mutex)(&tas_priv->codec_lock);
+
+ ret = tasdevice_spi_rca_parser(tas_priv, fmw);
+ if (ret)
+ goto out;
+
+ /* Add control one time only. */
+ tas_hda->prof_ctl = snd_ctl_new1(&tas2781_prof_ctrl[tas_priv->index],
+ tas_priv);
+ ret = snd_ctl_add(codec->card, tas_hda->prof_ctl);
+ if (ret) {
+ dev_err(tas_priv->dev, "Failed to add KControl %s = %d\n",
+ tas2781_prof_ctrl[tas_priv->index].name, ret);
+ goto out;
+ }
+ j = tas_priv->index * ARRAY_SIZE(tas2781_snd_controls) / 2;
+ for (i = 0; i < 3; i++) {
+ tas_hda->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_controls[i+j],
+ tas_priv);
+ ret = snd_ctl_add(codec->card, tas_hda->snd_ctls[i]);
+ if (ret) {
+ dev_err(tas_priv->dev,
+ "Failed to add KControl %s = %d\n",
+ tas2781_snd_controls[i+tas_priv->index*3].name,
+ ret);
+ goto out;
+ }
+ }
+
+ tasdevice_spi_dsp_remove(tas_priv);
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+ scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%08X-%01d.bin",
+ codec->core.subsystem_id, tas_priv->index);
+ ret = tasdevice_spi_dsp_parser(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "dspfw load %s error\n",
+ tas_priv->coef_binaryname);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+
+ /* Add control one time only. */
+ tas_hda->dsp_prog_ctl =
+ snd_ctl_new1(&tas2781_dsp_prog_ctrl[tas_priv->index],
+ tas_priv);
+ ret = snd_ctl_add(codec->card, tas_hda->dsp_prog_ctl);
+ if (ret) {
+ dev_err(tas_priv->dev,
+ "Failed to add KControl %s = %d\n",
+ tas2781_dsp_prog_ctrl[tas_priv->index].name, ret);
+ goto out;
+ }
+
+ tas_hda->dsp_conf_ctl =
+ snd_ctl_new1(&tas2781_dsp_conf_ctrl[tas_priv->index],
+ tas_priv);
+ ret = snd_ctl_add(codec->card, tas_hda->dsp_conf_ctl);
+ if (ret) {
+ dev_err(tas_priv->dev, "Failed to add KControl %s = %d\n",
+ tas2781_dsp_conf_ctrl[tas_priv->index].name, ret);
+ goto out;
+ }
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ tasdevice_spi_prmg_load(tas_priv, 0);
+ if (tas_priv->fmw->nr_programs > 0)
+ tas_priv->cur_prog = 0;
+ if (tas_priv->fmw->nr_configurations > 0)
+ tas_priv->cur_conf = 0;
+
+ /*
+ * If calibrated data occurs error, dsp will still works with default
+ * calibrated data inside algo.
+ */
+ tas_priv->save_calibration(tas_priv);
+
+out:
+ if (fmw)
+ release_firmware(fmw);
+ pm_runtime_mark_last_busy(tas_hda->priv->dev);
+ pm_runtime_put_autosuspend(tas_hda->priv->dev);
+}
+
+static int tas2781_hda_bind(struct device *dev, struct device *master,
+ void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+ struct hda_codec *codec;
+ int ret;
+
+ comp = hda_component_from_index(parent, tas_hda->priv->index);
+ if (!comp)
+ return -EINVAL;
+
+ if (comp->dev)
+ return -EBUSY;
+
+ codec = parent->codec;
+
+ pm_runtime_get_sync(dev);
+
+ comp->dev = dev;
+
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
+
+ ret = tascodec_spi_init(tas_hda->priv, codec, THIS_MODULE,
+ tasdev_fw_ready);
+ if (!ret)
+ comp->playback_hook = tas2781_hda_playback_hook;
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static void tas2781_hda_unbind(struct device *dev, struct device *master,
+ void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+
+ comp = hda_component_from_index(parent, tas_hda->priv->index);
+ if (comp && (comp->dev == dev)) {
+ comp->dev = NULL;
+ memset(comp->name, 0, sizeof(comp->name));
+ comp->playback_hook = NULL;
+ }
+
+ tas2781_hda_remove_controls(tas_hda);
+
+ tasdevice_spi_config_info_remove(tas_hda->priv);
+ tasdevice_spi_dsp_remove(tas_hda->priv);
+
+ tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static const struct component_ops tas2781_hda_comp_ops = {
+ .bind = tas2781_hda_bind,
+ .unbind = tas2781_hda_unbind,
+};
+
+static void tas2781_hda_remove(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ component_del(tas_hda->priv->dev, &tas2781_hda_comp_ops);
+
+ pm_runtime_get_sync(tas_hda->priv->dev);
+ pm_runtime_disable(tas_hda->priv->dev);
+
+ pm_runtime_put_noidle(tas_hda->priv->dev);
+
+ mutex_destroy(&tas_hda->priv->codec_lock);
+}
+
+static int tas2781_hda_spi_probe(struct spi_device *spi)
+{
+ struct tasdevice_priv *tas_priv;
+ struct tas2781_hda *tas_hda;
+ const char *device_name;
+ int ret = 0;
+
+ tas_hda = devm_kzalloc(&spi->dev, sizeof(*tas_hda), GFP_KERNEL);
+ if (!tas_hda)
+ return -ENOMEM;
+
+ spi->max_speed_hz = TAS2781_SPI_MAX_FREQ;
+
+ tas_priv = devm_kzalloc(&spi->dev, sizeof(*tas_priv), GFP_KERNEL);
+ if (!tas_priv)
+ goto err;
+ tas_priv->dev = &spi->dev;
+ tas_hda->priv = tas_priv;
+ tas_priv->regmap = devm_regmap_init_spi(spi, &tasdevice_regmap);
+ if (IS_ERR(tas_priv->regmap)) {
+ ret = PTR_ERR(tas_priv->regmap);
+ dev_err(tas_priv->dev, "Failed to allocate regmap: %d\n",
+ ret);
+ goto err;
+ }
+ if (strstr(dev_name(&spi->dev), "TXNW2781")) {
+ device_name = "TXNW2781";
+ tas_priv->save_calibration = tas2781_save_calibration;
+ tas_priv->apply_calibration = tas2781_apply_calib;
+ } else {
+ goto err;
+ }
+
+ tas_priv->irq = spi->irq;
+ dev_set_drvdata(&spi->dev, tas_hda);
+ ret = tas2781_read_acpi(tas_hda, device_name,
+ spi_get_chipselect(spi, 0));
+ if (ret)
+ return dev_err_probe(tas_priv->dev, ret,
+ "Platform not supported\n");
+
+ tasdevice_spi_init(tas_priv);
+
+ pm_runtime_set_autosuspend_delay(tas_priv->dev, 3000);
+ pm_runtime_use_autosuspend(tas_priv->dev);
+ pm_runtime_mark_last_busy(tas_priv->dev);
+ pm_runtime_set_active(tas_priv->dev);
+ pm_runtime_get_noresume(tas_priv->dev);
+ pm_runtime_enable(tas_priv->dev);
+
+ pm_runtime_put_autosuspend(tas_priv->dev);
+
+ tas2781_spi_reset(tas_priv);
+
+ ret = component_add(tas_priv->dev, &tas2781_hda_comp_ops);
+ if (ret) {
+ dev_err(tas_priv->dev, "Register component fail: %d\n", ret);
+ pm_runtime_disable(tas_priv->dev);
+ }
+
+err:
+ if (ret)
+ tas2781_hda_remove(&spi->dev);
+
+ return ret;
+}
+
+static void tas2781_hda_spi_remove(struct spi_device *spi)
+{
+ tas2781_hda_remove(&spi->dev);
+}
+
+static int tas2781_runtime_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ tasdevice_spi_tuning_switch(tas_hda->priv, 1);
+
+ tas_hda->priv->cur_book = -1;
+ tas_hda->priv->cur_conf = -1;
+
+ return 0;
+}
+
+static int tas2781_runtime_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ tasdevice_spi_tuning_switch(tas_hda->priv, 0);
+
+ return 0;
+}
+
+static int tas2781_system_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ /* Shutdown chip before system suspend */
+ tasdevice_spi_tuning_switch(tas_hda->priv, 1);
+
+ /*
+ * Reset GPIO may be shared, so cannot reset here.
+ * However beyond this point, amps may be powered down.
+ */
+ return 0;
+}
+
+static int tas2781_system_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ int ret, val;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ ret = tasdevice_spi_dev_read(tas_hda->priv, TAS2781_REG_CLK_CONFIG,
+ &val);
+ if (ret < 0)
+ return ret;
+
+ if (val == TAS2781_REG_CLK_CONFIG_RESET) {
+ tas_hda->priv->cur_book = -1;
+ tas_hda->priv->cur_conf = -1;
+ tas_hda->priv->cur_prog = -1;
+
+ tasdevice_spi_prmg_load(tas_hda->priv, 0);
+ tas_hda->priv->apply_calibration(tas_hda->priv);
+
+ if (tas_hda->priv->playback_started)
+ tasdevice_spi_tuning_switch(tas_hda->priv, 0);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops tas2781_hda_pm_ops = {
+ RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume)
+};
+
+static const struct spi_device_id tas2781_hda_spi_id[] = {
+ { "tas2781-hda", },
+ {}
+};
+
+static const struct acpi_device_id tas2781_acpi_hda_match[] = {
+ {"TXNW2781", },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match);
+
+static struct spi_driver tas2781_hda_spi_driver = {
+ .driver = {
+ .name = "tas2781-hda",
+ .acpi_match_table = tas2781_acpi_hda_match,
+ .pm = &tas2781_hda_pm_ops,
+ },
+ .id_table = tas2781_hda_spi_id,
+ .probe = tas2781_hda_spi_probe,
+ .remove = tas2781_hda_spi_remove,
+};
+module_spi_driver(tas2781_hda_spi_driver);
+
+MODULE_DESCRIPTION("TAS2781 HDA SPI Driver");
+MODULE_AUTHOR("Baojun, Xu, <baojun.xug(a)ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/tas2781_spi_fwlib.c b/sound/pci/hda/tas2781_spi_fwlib.c
new file mode 100644
index 000000000000..9e58e8d981e3
--- /dev/null
+++ b/sound/pci/hda/tas2781_spi_fwlib.c
@@ -0,0 +1,2114 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA SPI driver
+//
+// Copyright 2024 Texas Instruments, Inc.
+//
+// Author: Baojun Xu <baojun.xu(a)ti.com>
+
+#include <asm/unaligned.h>
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tas2781-dsp.h>
+#include <sound/tlv.h>
+
+#include "tas2781-spi.h"
+
+#define OFFSET_ERROR_BIT BIT(31)
+
+#define ERROR_PRAM_CRCCHK 0x0000000
+#define ERROR_YRAM_CRCCHK 0x0000001
+#define PPC_DRIVER_CRCCHK 0x00000200
+
+#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c)
+#define TAS2781_YRAM_BOOK1 140
+#define TAS2781_YRAM1_PAGE 42
+#define TAS2781_YRAM1_START_REG 88
+
+#define TAS2781_YRAM2_START_PAGE 43
+#define TAS2781_YRAM2_END_PAGE 49
+#define TAS2781_YRAM2_START_REG 8
+#define TAS2781_YRAM2_END_REG 127
+
+#define TAS2781_YRAM3_PAGE 50
+#define TAS2781_YRAM3_START_REG 8
+#define TAS2781_YRAM3_END_REG 27
+
+/* should not include B0_P53_R44-R47 */
+#define TAS2781_YRAM_BOOK2 0
+#define TAS2781_YRAM4_START_PAGE 50
+#define TAS2781_YRAM4_END_PAGE 60
+
+#define TAS2781_YRAM5_PAGE 61
+#define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG
+#define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG
+
+#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10
+#define MAIN_ALL_DEVICES_1X 0x01
+#define MAIN_DEVICE_A_1X 0x02
+#define MAIN_DEVICE_B_1X 0x03
+#define MAIN_DEVICE_C_1X 0x04
+#define MAIN_DEVICE_D_1X 0x05
+#define COEFF_DEVICE_A_1X 0x12
+#define COEFF_DEVICE_B_1X 0x13
+#define COEFF_DEVICE_C_1X 0x14
+#define COEFF_DEVICE_D_1X 0x15
+#define PRE_DEVICE_A_1X 0x22
+#define PRE_DEVICE_B_1X 0x23
+#define PRE_DEVICE_C_1X 0x24
+#define PRE_DEVICE_D_1X 0x25
+#define PRE_SOFTWARE_RESET_DEVICE_A 0x41
+#define PRE_SOFTWARE_RESET_DEVICE_B 0x42
+#define PRE_SOFTWARE_RESET_DEVICE_C 0x43
+#define PRE_SOFTWARE_RESET_DEVICE_D 0x44
+#define POST_SOFTWARE_RESET_DEVICE_A 0x45
+#define POST_SOFTWARE_RESET_DEVICE_B 0x46
+#define POST_SOFTWARE_RESET_DEVICE_C 0x47
+#define POST_SOFTWARE_RESET_DEVICE_D 0x48
+
+struct tas_crc {
+ unsigned char offset;
+ unsigned char len;
+};
+
+struct blktyp_devidx_map {
+ unsigned char blktyp;
+ unsigned char dev_idx;
+};
+
+/* fixed m68k compiling issue: mapping table can save code field */
+static const struct blktyp_devidx_map ppc3_tas2781_mapping_table[] = {
+ { MAIN_ALL_DEVICES_1X, 0x80 },
+ { MAIN_DEVICE_A_1X, 0x81 },
+ { COEFF_DEVICE_A_1X, 0x81 },
+ { PRE_DEVICE_A_1X, 0x81 },
+ { PRE_SOFTWARE_RESET_DEVICE_A, 0xC1 },
+ { POST_SOFTWARE_RESET_DEVICE_A, 0xC1 },
+ { MAIN_DEVICE_B_1X, 0x82 },
+ { COEFF_DEVICE_B_1X, 0x82 },
+ { PRE_DEVICE_B_1X, 0x82 },
+ { PRE_SOFTWARE_RESET_DEVICE_B, 0xC2 },
+ { POST_SOFTWARE_RESET_DEVICE_B, 0xC2 },
+ { MAIN_DEVICE_C_1X, 0x83 },
+ { COEFF_DEVICE_C_1X, 0x83 },
+ { PRE_DEVICE_C_1X, 0x83 },
+ { PRE_SOFTWARE_RESET_DEVICE_C, 0xC3 },
+ { POST_SOFTWARE_RESET_DEVICE_C, 0xC3 },
+ { MAIN_DEVICE_D_1X, 0x84 },
+ { COEFF_DEVICE_D_1X, 0x84 },
+ { PRE_DEVICE_D_1X, 0x84 },
+ { PRE_SOFTWARE_RESET_DEVICE_D, 0xC4 },
+ { POST_SOFTWARE_RESET_DEVICE_D, 0xC4 },
+};
+
+static const struct blktyp_devidx_map ppc3_mapping_table[] = {
+ { MAIN_ALL_DEVICES_1X, 0x80 },
+ { MAIN_DEVICE_A_1X, 0x81 },
+ { COEFF_DEVICE_A_1X, 0xC1 },
+ { PRE_DEVICE_A_1X, 0xC1 },
+ { MAIN_DEVICE_B_1X, 0x82 },
+ { COEFF_DEVICE_B_1X, 0xC2 },
+ { PRE_DEVICE_B_1X, 0xC2 },
+ { MAIN_DEVICE_C_1X, 0x83 },
+ { COEFF_DEVICE_C_1X, 0xC3 },
+ { PRE_DEVICE_C_1X, 0xC3 },
+ { MAIN_DEVICE_D_1X, 0x84 },
+ { COEFF_DEVICE_D_1X, 0xC4 },
+ { PRE_DEVICE_D_1X, 0xC4 },
+};
+
+static const struct blktyp_devidx_map non_ppc3_mapping_table[] = {
+ { MAIN_ALL_DEVICES, 0x80 },
+ { MAIN_DEVICE_A, 0x81 },
+ { COEFF_DEVICE_A, 0xC1 },
+ { PRE_DEVICE_A, 0xC1 },
+ { MAIN_DEVICE_B, 0x82 },
+ { COEFF_DEVICE_B, 0xC2 },
+ { PRE_DEVICE_B, 0xC2 },
+ { MAIN_DEVICE_C, 0x83 },
+ { COEFF_DEVICE_C, 0xC3 },
+ { PRE_DEVICE_C, 0xC3 },
+ { MAIN_DEVICE_D, 0x84 },
+ { COEFF_DEVICE_D, 0xC4 },
+ { PRE_DEVICE_D, 0xC4 },
+};
+/*
+ * Device support different configurations for different scene,
+ * like voice, music, calibration, was write in regbin file.
+ * Will be stored into tas_priv after regbin was loaded.
+ */
+static struct tasdevice_config_info *tasdevice_add_config(
+ struct tasdevice_priv *tas_priv, unsigned char *config_data,
+ unsigned int config_size, int *status)
+{
+ struct tasdevice_config_info *cfg_info;
+ struct tasdev_blk_data **bk_da;
+ unsigned int config_offset = 0;
+ unsigned int i;
+
+ /*
+ * In most projects are many audio cases, such as music, handfree,
+ * receiver, games, audio-to-haptics, PMIC record, bypass mode,
+ * portrait, landscape, etc. Even in multiple audios, one or
+ * two of the chips will work for the special case, such as
+ * ultrasonic application. In order to support these variable-numbers
+ * of audio cases, flexible configs have been introduced in the
+ * DSP firmware.
+ */
+ cfg_info = kzalloc(sizeof(*cfg_info), GFP_KERNEL);
+ if (!cfg_info) {
+ *status = -ENOMEM;
+ return NULL;
+ }
+
+ if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) {
+ if ((config_offset + 64) > config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev, "add conf: Out of boundary\n");
+ goto config_err;
+ }
+ config_offset += 64;
+ }
+
+ if ((config_offset + 4) > config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev, "add config: Out of boundary\n");
+ goto config_err;
+ }
+
+ /*
+ * convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host
+ */
+ cfg_info->nblocks = get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ /*
+ * Several kinds of dsp/algorithm firmwares can run on tas2781,
+ * the number and size of blk are not fixed and different among
+ * these firmwares.
+ */
+ bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks,
+ sizeof(*bk_da), GFP_KERNEL);
+ if (!bk_da) {
+ *status = -ENOMEM;
+ goto config_err;
+ }
+ cfg_info->real_nblocks = 0;
+ for (i = 0; i < cfg_info->nblocks; i++) {
+ if (config_offset + 12 > config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev,
+ "%s: Out of boundary: i = %d nblocks = %u!\n",
+ __func__, i, cfg_info->nblocks);
+ goto block_err;
+ }
+ bk_da[i] = kzalloc(sizeof(*bk_da[i]), GFP_KERNEL);
+ if (!bk_da[i]) {
+ *status = -ENOMEM;
+ goto block_err;
+ }
+
+ bk_da[i]->dev_idx = config_data[config_offset];
+ config_offset++;
+
+ bk_da[i]->block_type = config_data[config_offset];
+ config_offset++;
+
+ bk_da[i]->yram_checksum =
+ get_unaligned_be16(&config_data[config_offset]);
+ config_offset += 2;
+ bk_da[i]->block_size =
+ get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ bk_da[i]->n_subblks =
+ get_unaligned_be32(&config_data[config_offset]);
+
+ config_offset += 4;
+
+ if (config_offset + bk_da[i]->block_size > config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev,
+ "%s: Out of boundary: i = %d blks = %u!\n",
+ __func__, i, cfg_info->nblocks);
+ goto block_err;
+ }
+ /* instead of kzalloc+memcpy */
+ bk_da[i]->regdata = kmemdup(&config_data[config_offset],
+ bk_da[i]->block_size, GFP_KERNEL);
+ if (!bk_da[i]->regdata) {
+ *status = -ENOMEM;
+ i++;
+ goto block_err;
+ }
+
+ config_offset += bk_da[i]->block_size;
+ cfg_info->real_nblocks += 1;
+ }
+
+ return cfg_info;
+block_err:
+ for (int j = 0; j < i; j++)
+ kfree(bk_da[j]);
+ kfree(bk_da);
+config_err:
+ kfree(cfg_info);
+ return NULL;
+}
+
+/* Regbin file parser function. */
+int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_config_info **cfg_info;
+ struct tasdevice_rca_hdr *fw_hdr;
+ struct tasdevice_rca *rca;
+ unsigned int total_config_sz = 0;
+ int offset = 0, ret = 0, i;
+ unsigned char *buf;
+
+ rca = &tas_priv->rcabin;
+ fw_hdr = &rca->fw_hdr;
+ if (!fmw || !fmw->data) {
+ dev_err(tas_priv->dev, "Failed to read %s\n",
+ tas_priv->rca_binaryname);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -EINVAL;
+ }
+ buf = (unsigned char *)fmw->data;
+ fw_hdr->img_sz = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ if (fw_hdr->img_sz != fmw->size) {
+ dev_err(tas_priv->dev,
+ "File size not match, %d %u", (int)fmw->size,
+ fw_hdr->img_sz);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -EINVAL;
+ }
+
+ fw_hdr->checksum = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->binary_version_num < 0x103) {
+ dev_err(tas_priv->dev, "File version 0x%04x is too low",
+ fw_hdr->binary_version_num);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -EINVAL;
+ }
+ offset += 4;
+ fw_hdr->drv_fw_version = get_unaligned_be32(&buf[offset]);
+ offset += 8;
+ fw_hdr->plat_type = buf[offset++];
+ fw_hdr->dev_family = buf[offset++];
+ fw_hdr->reserve = buf[offset++];
+ fw_hdr->ndev = buf[offset++];
+ if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) {
+ dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n");
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -EINVAL;
+ }
+
+ for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++)
+ fw_hdr->devs[i] = buf[offset];
+
+ fw_hdr->nconfig = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) {
+ fw_hdr->config_size[i] = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ total_config_sz += fw_hdr->config_size[i];
+ }
+
+ if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) {
+ dev_err(tas_priv->dev, "Bin file err %d - %d != %d!\n",
+ fw_hdr->img_sz, total_config_sz, (int)offset);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -EINVAL;
+ }
+
+ cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL);
+ if (!cfg_info) {
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -ENOMEM;
+ }
+ rca->cfg_info = cfg_info;
+ rca->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ rca->ncfgs += 1;
+ cfg_info[i] = tasdevice_add_config(tas_priv, &buf[offset],
+ fw_hdr->config_size[i], &ret);
+ if (ret) {
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return ret;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ }
+
+ return ret;
+}
+
+/* fixed m68k compiling issue: mapping table can save code field */
+static unsigned char map_dev_idx(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block)
+{
+ struct blktyp_devidx_map *p =
+ (struct blktyp_devidx_map *)non_ppc3_mapping_table;
+ struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr;
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &fw_hdr->fixed_hdr;
+ int i, n = ARRAY_SIZE(non_ppc3_mapping_table);
+ unsigned char dev_idx = 0;
+
+ if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781) {
+ p = (struct blktyp_devidx_map *)ppc3_tas2781_mapping_table;
+ n = ARRAY_SIZE(ppc3_tas2781_mapping_table);
+ } else if (fw_fixed_hdr->ppcver >= PPC3_VERSION) {
+ p = (struct blktyp_devidx_map *)ppc3_mapping_table;
+ n = ARRAY_SIZE(ppc3_mapping_table);
+ }
+
+ for (i = 0; i < n; i++) {
+ if (block->type == p[i].blktyp) {
+ dev_idx = p[i].dev_idx;
+ break;
+ }
+ }
+
+ return dev_idx;
+}
+
+/* Block parser function. */
+static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+
+ if (offset + 16 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host.
+ */
+ block->type = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ block->is_pchksum_present = data[offset++];
+ block->pchksum = data[offset++];
+ block->is_ychksum_present = data[offset++];
+ block->ychksum = data[offset++];
+ block->blk_size = get_unaligned_be32(&data[offset]);
+ offset += 4;
+ block->nr_subblocks = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ /*
+ * Fixed m68k compiling issue:
+ * 1. mapping table can save code field.
+ * 2. storing the dev_idx as a member of block can reduce unnecessary
+ * time and system resource comsumption of dev_idx mapping every
+ * time the block data writing to the dsp.
+ */
+ block->dev_idx = map_dev_idx(tas_fmw, block);
+
+ if (offset + block->blk_size > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__);
+ return -EINVAL;
+ }
+ /* instead of kzalloc+memcpy */
+ block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL);
+ if (!block->data)
+ return -ENOMEM;
+
+ offset += block->blk_size;
+
+ return offset;
+}
+
+/* Data of block parser function. */
+static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = fmw->data;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+ img_data->nr_blk = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ img_data->dev_blks = kcalloc(img_data->nr_blk,
+ sizeof(struct tasdev_blk), GFP_KERNEL);
+ if (!img_data->dev_blks)
+ return -ENOMEM;
+
+ for (i = 0; i < img_data->nr_blk; i++) {
+ blk = &img_data->dev_blks[i];
+ offset = fw_parse_block_data_kernel(
+ tas_fmw, blk, fmw, offset);
+ if (offset < 0) {
+ kfree(img_data->dev_blks);
+ return -EINVAL;
+ }
+ }
+
+ return offset;
+}
+
+/* Data of DSP program parser function. */
+static int fw_parse_program_data_kernel(
+ struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ struct tasdevice_prog *program;
+ unsigned int i;
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ program = &tas_fmw->programs[i];
+ if (offset + 72 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ return -EINVAL;
+ }
+ /* skip 72 unused byts */
+ offset += 72;
+
+ offset = fw_parse_data_kernel(tas_fmw, &program->dev_data,
+ fmw, offset);
+ if (offset < 0)
+ break;
+ }
+
+ return offset;
+}
+
+/* Data of DSP configurations parser function. */
+static int fw_parse_configuration_data_kernel(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+ struct tasdevice_config *config;
+ unsigned int i;
+
+ for (i = 0; i < tas_fmw->nr_configurations; i++) {
+ config = &tas_fmw->configs[i];
+ if (offset + 80 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ return -EINVAL;
+ }
+ memcpy(config->name, &data[offset], 64);
+ /* skip extra 16 bytes */
+ offset += 80;
+
+ offset = fw_parse_data_kernel(tas_fmw, &config->dev_data,
+ fmw, offset);
+ if (offset < 0)
+ break;
+ }
+
+ return offset;
+}
+
+/* DSP firmware file header parser function for early PPC3 firmware binary. */
+static int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_priv,
+ const struct firmware *fmw, int offset)
+{
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr;
+ struct tasdevice_config *config;
+ struct tasdevice_prog *program;
+ const unsigned char *buf = fmw->data;
+ unsigned short max_confs;
+ unsigned int i;
+
+ if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+ fw_hdr->device_family = get_unaligned_be16(&buf[offset]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_priv->dev, "%s:not TAS device\n", __func__);
+ return -EINVAL;
+ }
+ offset += 2;
+ fw_hdr->device = get_unaligned_be16(&buf[offset]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device);
+ return -EINVAL;
+ }
+ offset += 2;
+
+ tas_fmw->nr_programs = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ if (tas_fmw->nr_programs == 0 ||
+ tas_fmw->nr_programs > TASDEVICE_MAXPROGRAM_NUM_KERNEL) {
+ dev_err(tas_priv->dev, "mnPrograms is invalid\n");
+ return -EINVAL;
+ }
+
+ tas_fmw->programs = kcalloc(tas_fmw->nr_programs,
+ sizeof(*tas_fmw->programs), GFP_KERNEL);
+ if (!tas_fmw->programs)
+ return -ENOMEM;
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ program = &tas_fmw->programs[i];
+ program->prog_size = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ }
+
+ /* Skip the unused prog_size */
+ offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs);
+
+ tas_fmw->nr_configurations = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ /*
+ * The max number of config in firmware greater than 4 pieces of
+ * tas2781s is different from the one lower than 4 pieces of
+ * tas2781s.
+ */
+ max_confs = TASDEVICE_MAXCONFIG_NUM_KERNEL;
+ if (tas_fmw->nr_configurations == 0 ||
+ tas_fmw->nr_configurations > max_confs) {
+ dev_err(tas_priv->dev, "%s: Conf is invalid\n", __func__);
+ kfree(tas_fmw->programs);
+ return -EINVAL;
+ }
+
+ if (offset + 4 * max_confs > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__);
+ kfree(tas_fmw->programs);
+ return -EINVAL;
+ }
+
+ tas_fmw->configs = kcalloc(tas_fmw->nr_configurations,
+ sizeof(*tas_fmw->configs), GFP_KERNEL);
+ if (!tas_fmw->configs) {
+ kfree(tas_fmw->programs);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ config = &tas_fmw->configs[i];
+ config->cfg_size = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ }
+
+ /* Skip the unused configs */
+ offset += 4 * (max_confs - tas_fmw->nr_programs);
+
+ return offset;
+}
+
+/*
+ * In sub-block data, have three type sub-block:
+ * 1. Single byte write.
+ * 2. Multi-byte write.
+ * 3. Delay.
+ * 4. Bits update.
+ * This function perform single byte write to device.
+ */
+static int tasdevice_single_byte_wr(void *context, int dev_idx,
+ unsigned char *data, int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = context;
+ unsigned short len = get_unaligned_be16(&data[2]);
+ int i, subblk_offset, rc;
+
+ subblk_offset = 4;
+ if (subblk_offset + 4 * len > sublocksize) {
+ dev_err(tas_priv->dev, "process_block: Out of boundary\n");
+ return 0;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) {
+ rc = tasdevice_spi_dev_write(tas_priv,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ data[subblk_offset + 3]);
+ if (rc < 0) {
+ dev_err(tas_priv->dev,
+ "process_block: single write error\n");
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ }
+ subblk_offset += 4;
+ }
+
+ return subblk_offset;
+}
+/*
+ * In sub-block data, have three type sub-block:
+ * 1. Single byte write.
+ * 2. Multi-byte write.
+ * 3. Delay.
+ * 4. Bits update.
+ * This function perform multi-write to device.
+ */
+static int tasdevice_burst_wr(void *context, int dev_idx, unsigned char *data,
+ int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = context;
+ unsigned short len = get_unaligned_be16(&data[2]);
+ int subblk_offset, rc;
+
+ subblk_offset = 4;
+ if (subblk_offset + 4 + len > sublocksize) {
+ dev_err(tas_priv->dev, "%s: BST Out of boundary\n", __func__);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ if (len % 4) {
+ dev_err(tas_priv->dev, "%s:Bst-len(%u)not div by 4\n",
+ __func__, len);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+
+ if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) {
+ rc = tasdevice_spi_dev_bulk_write(tas_priv,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ &data[subblk_offset + 4], len);
+ if (rc < 0) {
+ dev_err(tas_priv->dev, "%s: bulk_write error = %d\n",
+ __func__, rc);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ }
+ subblk_offset += (len + 4);
+
+ return subblk_offset;
+}
+/* Just delay for ms.*/
+static int tasdevice_delay(void *context, int dev_idx, unsigned char *data,
+ int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = context;
+ unsigned int sleep_time, subblk_offset = 2;
+
+ if (subblk_offset + 2 > sublocksize) {
+ dev_err(tas_priv->dev, "%s: delay Out of boundary\n",
+ __func__);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) {
+ sleep_time = get_unaligned_be16(&data[2]) * 1000;
+ fsleep(sleep_time);
+ }
+ subblk_offset += 2;
+
+ return subblk_offset;
+}
+/*
+ * In sub-block data, have three type sub-block:
+ * 1. Single byte write.
+ * 2. Multi-byte write.
+ * 3. Delay.
+ * 4. Bits update.
+ * This function perform bits update.
+ */
+static int tasdevice_field_wr(void *context, int dev_idx, unsigned char *data,
+ int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = context;
+ int rc, subblk_offset = 2;
+
+ if (subblk_offset + 6 > sublocksize) {
+ dev_err(tas_priv->dev, "%s: bit write Out of boundary\n",
+ __func__);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) {
+ rc = tasdevice_spi_dev_update_bits(tas_priv,
+ TASDEVICE_REG(data[subblk_offset + 2],
+ data[subblk_offset + 3],
+ data[subblk_offset + 4]),
+ data[subblk_offset + 1],
+ data[subblk_offset + 5]);
+ if (rc < 0) {
+ dev_err(tas_priv->dev, "%s: update_bits error = %d\n",
+ __func__, rc);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ }
+ subblk_offset += 6;
+
+ return subblk_offset;
+}
+/* Data block process function. */
+static int tasdevice_process_block(void *context, unsigned char *data,
+ unsigned char dev_idx, int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = context;
+ int blktyp = dev_idx & 0xC0, subblk_offset;
+ unsigned char subblk_typ = data[1];
+
+ switch (subblk_typ) {
+ case TASDEVICE_CMD_SING_W:
+ subblk_offset = tasdevice_single_byte_wr(tas_priv,
+ dev_idx & 0x4f, data, sublocksize);
+ break;
+ case TASDEVICE_CMD_BURST:
+ subblk_offset = tasdevice_burst_wr(tas_priv,
+ dev_idx & 0x4f, data, sublocksize);
+ break;
+ case TASDEVICE_CMD_DELAY:
+ subblk_offset = tasdevice_delay(tas_priv,
+ dev_idx & 0x4f, data, sublocksize);
+ break;
+ case TASDEVICE_CMD_FIELD_W:
+ subblk_offset = tasdevice_field_wr(tas_priv,
+ dev_idx & 0x4f, data, sublocksize);
+ break;
+ default:
+ subblk_offset = 2;
+ break;
+ }
+ if (((subblk_offset & OFFSET_ERROR_BIT) != 0) && blktyp != 0) {
+ if (blktyp == 0x80) {
+ tas_priv->cur_prog = -1;
+ tas_priv->cur_conf = -1;
+ } else
+ tas_priv->cur_conf = -1;
+ }
+ subblk_offset &= ~OFFSET_ERROR_BIT;
+
+ return subblk_offset;
+}
+/*
+ * Device support different configurations for different scene,
+ * this function was used for choose different config.
+ */
+void tasdevice_spi_select_cfg_blk(void *pContext, int conf_no,
+ unsigned char block_type)
+{
+ struct tasdevice_priv *tas_priv = pContext;
+ struct tasdevice_rca *rca = &tas_priv->rcabin;
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ struct tasdev_blk_data **blk_data;
+ unsigned int j, k;
+
+ if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) {
+ dev_err(tas_priv->dev, "conf_no should be not more than %u\n",
+ rca->ncfgs);
+ return;
+ }
+ blk_data = cfg_info[conf_no]->blk_data;
+
+ for (j = 0; j < cfg_info[conf_no]->real_nblocks; j++) {
+ unsigned int length = 0, rc = 0;
+
+ if (block_type > 5 || block_type < 2) {
+ dev_err(tas_priv->dev,
+ "block_type should be in range from 2 to 5\n");
+ break;
+ }
+ if (block_type != blk_data[j]->block_type)
+ continue;
+
+ for (k = 0; k < blk_data[j]->n_subblks; k++) {
+ tas_priv->is_loading = true;
+
+ rc = tasdevice_process_block(tas_priv,
+ blk_data[j]->regdata + length,
+ blk_data[j]->dev_idx,
+ blk_data[j]->block_size - length);
+ length += rc;
+ if (blk_data[j]->block_size < length) {
+ dev_err(tas_priv->dev,
+ "%s: %u %u out of boundary\n",
+ __func__, length,
+ blk_data[j]->block_size);
+ break;
+ }
+ }
+ if (length != blk_data[j]->block_size)
+ dev_err(tas_priv->dev, "%s: %u %u size is not same\n",
+ __func__, length, blk_data[j]->block_size);
+ }
+}
+/* Block process function. */
+static int tasdevice_load_block_kernel(
+ struct tasdevice_priv *tasdevice, struct tasdev_blk *block)
+{
+ const unsigned int blk_size = block->blk_size;
+ unsigned char *data = block->data;
+ unsigned int i, length;
+
+ for (i = 0, length = 0; i < block->nr_subblocks; i++) {
+ int rc = tasdevice_process_block(tasdevice, data + length,
+ block->dev_idx, blk_size - length);
+ if (rc < 0) {
+ dev_err(tasdevice->dev,
+ "%s: %u %u sublock write error\n",
+ __func__, length, blk_size);
+ break;
+ }
+ length += rc;
+ if (blk_size < length) {
+ dev_err(tasdevice->dev, "%s: %u %u out of boundary\n",
+ __func__, length, blk_size);
+ break;
+ }
+ }
+
+ return 0;
+}
+/* DSP firmware file header parser function. */
+static int fw_parse_variable_hdr(struct tasdevice_priv *tas_priv,
+ struct tasdevice_dspfw_hdr *fw_hdr,
+ const struct firmware *fmw, int offset)
+{
+ const unsigned char *buf = fmw->data;
+ int len = strlen((char *)&buf[offset]);
+
+ len++;
+
+ if (offset + len + 8 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+
+ offset += len;
+
+ fw_hdr->device_family = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_priv->dev, "%s: not TAS device\n", __func__);
+ return -EINVAL;
+ }
+ offset += 4;
+
+ fw_hdr->device = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device);
+ return -EINVAL;
+ }
+ offset += 4;
+ fw_hdr->ndev = 1;
+
+ return offset;
+}
+
+/* DSP firmware file header parser function for size variabled header. */
+static int fw_parse_variable_header_git(struct tasdevice_priv
+ *tas_priv, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr;
+
+ offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset);
+
+ return offset;
+}
+
+/* DSP firmware file block parser function. */
+static int fw_parse_block_data(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ int n;
+
+ if (offset + 8 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Type error\n", __func__);
+ return -EINVAL;
+ }
+ block->type = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) {
+ if (offset + 8 > fmw->size) {
+ dev_err(tas_fmw->dev, "PChkSumPresent error\n");
+ return -EINVAL;
+ }
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ block->pchksum = data[offset];
+ offset++;
+
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ block->ychksum = data[offset];
+ offset++;
+ } else {
+ block->is_pchksum_present = 0;
+ block->is_ychksum_present = 0;
+ }
+
+ block->nr_cmds = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ n = block->nr_cmds * 4;
+ if (offset + n > fmw->size) {
+ dev_err(tas_fmw->dev,
+ "%s: File Size(%lu) error offset = %d n = %d\n",
+ __func__, (unsigned long)fmw->size, offset, n);
+ return -EINVAL;
+ }
+ /* instead of kzalloc+memcpy */
+ block->data = kmemdup(&data[offset], n, GFP_KERNEL);
+ if (!block->data)
+ return -ENOMEM;
+
+ offset += n;
+
+ return offset;
+}
+
+/*
+ * When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_data(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = (unsigned char *)fmw->data;
+ struct tasdev_blk *blk;
+ unsigned int i, n;
+
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Name error\n", __func__);
+ return -EINVAL;
+ }
+ memcpy(img_data->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n + 2 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Description error\n", __func__);
+ return -EINVAL;
+ }
+ offset += n;
+ img_data->nr_blk = get_unaligned_be16(&data[offset]);
+ offset += 2;
+
+ img_data->dev_blks = kcalloc(img_data->nr_blk,
+ sizeof(*img_data->dev_blks), GFP_KERNEL);
+ if (!img_data->dev_blks)
+ return -ENOMEM;
+
+ for (i = 0; i < img_data->nr_blk; i++) {
+ blk = &img_data->dev_blks[i];
+ offset = fw_parse_block_data(tas_fmw, blk, fmw, offset);
+ if (offset < 0)
+ return -EINVAL;
+ }
+
+ return offset;
+}
+
+/*
+ * When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_program_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ unsigned char *buf = (unsigned char *)fmw->data;
+ struct tasdevice_prog *program;
+ int i;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+ tas_fmw->nr_programs = get_unaligned_be16(&buf[offset]);
+ offset += 2;
+
+ if (tas_fmw->nr_programs == 0) {
+ /* Not error in calibration Data file, return directly */
+ dev_dbg(tas_priv->dev, "%s: No Programs data, maybe calbin\n",
+ __func__);
+ return offset;
+ }
+
+ tas_fmw->programs =
+ kcalloc(tas_fmw->nr_programs, sizeof(*tas_fmw->programs),
+ GFP_KERNEL);
+ if (!tas_fmw->programs)
+ return -ENOMEM;
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ int n = 0;
+
+ program = &tas_fmw->programs[i];
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ return -EINVAL;
+ }
+ offset += 64;
+
+ n = strlen((char *)&buf[offset]);
+ /* skip '\0' and 5 unused bytes */
+ n += 6;
+ if (offset + n > fmw->size) {
+ dev_err(tas_priv->dev, "Description err\n");
+ return -EINVAL;
+ }
+
+ offset += n;
+
+ offset = fw_parse_data(tas_fmw, &program->dev_data, fmw,
+ offset);
+ if (offset < 0)
+ return offset;
+ }
+
+ return offset;
+}
+
+/*
+ * When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_configuration_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ struct tasdevice_config *config;
+ unsigned int i, n;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+ tas_fmw->nr_configurations = get_unaligned_be16(&data[offset]);
+ offset += 2;
+
+ if (tas_fmw->nr_configurations == 0) {
+ dev_err(tas_priv->dev, "%s: Conf is zero\n", __func__);
+ /* Not error for calibration Data file, return directly */
+ return offset;
+ }
+ tas_fmw->configs = kcalloc(tas_fmw->nr_configurations,
+ sizeof(*tas_fmw->configs), GFP_KERNEL);
+ if (!tas_fmw->configs)
+ return -ENOMEM;
+ for (i = 0; i < tas_fmw->nr_configurations; i++) {
+ config = &tas_fmw->configs[i];
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_priv->dev, "File Size err\n");
+ return -EINVAL;
+ }
+ memcpy(config->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n += 15;
+ if (offset + n > fmw->size) {
+ dev_err(tas_priv->dev, "Description err\n");
+ return -EINVAL;
+ }
+ offset += n;
+ offset = fw_parse_data(tas_fmw, &config->dev_data,
+ fmw, offset);
+ if (offset < 0)
+ break;
+ }
+
+ return offset;
+}
+
+/* yram5 page check. */
+static bool check_inpage_yram_rg(struct tas_crc *cd,
+ unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (reg <= TAS2781_YRAM5_END_REG &&
+ reg >= TAS2781_YRAM5_START_REG) {
+ if (reg + len > TAS2781_YRAM5_END_REG)
+ cd->len = TAS2781_YRAM5_END_REG - reg + 1;
+ else
+ cd->len = len;
+ cd->offset = reg;
+ in = true;
+ } else if (reg < TAS2781_YRAM5_START_REG) {
+ if (reg + len > TAS2781_YRAM5_START_REG) {
+ cd->offset = TAS2781_YRAM5_START_REG;
+ cd->len = len - TAS2781_YRAM5_START_REG + reg;
+ in = true;
+ }
+ }
+
+ return in;
+}
+
+/* DSP firmware yram block check. */
+static bool check_inpage_yram_bk1(struct tas_crc *cd,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (page == TAS2781_YRAM1_PAGE) {
+ if (reg >= TAS2781_YRAM1_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ in = true;
+ } else if (reg + len > TAS2781_YRAM1_START_REG) {
+ cd->offset = TAS2781_YRAM1_START_REG;
+ cd->len = len - TAS2781_YRAM1_START_REG + reg;
+ in = true;
+ }
+ } else if (page == TAS2781_YRAM3_PAGE) {
+ in = check_inpage_yram_rg(cd, reg, len);
+ }
+
+ return in;
+}
+
+/*
+ * Return Code:
+ * true -- the registers are in the inpage yram
+ * false -- the registers are NOT in the inpage yram
+ */
+static bool check_inpage_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (book == TAS2781_YRAM_BOOK1)
+ in = check_inpage_yram_bk1(cd, page, reg, len);
+ else if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE)
+ in = check_inpage_yram_rg(cd, reg, len);
+
+ return in;
+}
+/* yram4 page check. */
+static bool check_inblock_yram_bk(struct tas_crc *cd,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if ((page >= TAS2781_YRAM4_START_PAGE &&
+ page <= TAS2781_YRAM4_END_PAGE) ||
+ (page >= TAS2781_YRAM2_START_PAGE &&
+ page <= TAS2781_YRAM2_END_PAGE)) {
+ if (reg <= TAS2781_YRAM2_END_REG &&
+ reg >= TAS2781_YRAM2_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ in = true;
+ } else if (reg < TAS2781_YRAM2_START_REG) {
+ if (reg + len - 1 >= TAS2781_YRAM2_START_REG) {
+ cd->offset = TAS2781_YRAM2_START_REG;
+ cd->len = reg + len - TAS2781_YRAM2_START_REG;
+ in = true;
+ }
+ }
+ }
+
+ return in;
+}
+
+/*
+ * Return Code:
+ * true -- the registers are in the inblock yram
+ * false -- the registers are NOT in the inblock yram
+ */
+static bool check_inblock_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2)
+ in = check_inblock_yram_bk(cd, page, reg, len);
+
+ return in;
+}
+/* yram page check. */
+static bool check_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in;
+
+ in = check_inpage_yram(cd, book, page, reg, len);
+ if (!in)
+ in = check_inblock_yram(cd, book, page, reg, len);
+
+ return in;
+}
+
+/* Checksum for data block. */
+static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice,
+ unsigned char book, unsigned char page,
+ unsigned char reg, unsigned int len)
+{
+ struct tas_crc crc_data;
+ unsigned char crc_chksum = 0;
+ unsigned char nBuf1[128];
+ int ret = 0, i;
+ bool in;
+
+ if ((reg + len - 1) > 127) {
+ ret = -EINVAL;
+ dev_err(tasdevice->dev, "firmware error\n");
+ goto end;
+ }
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (reg == TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (len == 4)) {
+ /* DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ in = check_yram(&crc_data, book, page, reg, len);
+ if (!in)
+ goto end;
+
+ if (len == 1) {
+ dev_err(tasdevice->dev, "firmware error\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ret = tasdevice_spi_dev_bulk_read(tasdevice,
+ TASDEVICE_REG(book, page, crc_data.offset),
+ nBuf1, crc_data.len);
+ if (ret < 0)
+ goto end;
+
+ for (i = 0; i < crc_data.len; i++) {
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ ((i + crc_data.offset) >=
+ TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ ((i + crc_data.offset) <=
+ (TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG) + 4)))
+ /* DSP swap command, bypass */
+ continue;
+ else
+ crc_chksum += crc8(tasdevice->crc8_lkp_tbl, &nBuf1[i],
+ 1, 0);
+ }
+
+ ret = crc_chksum;
+
+end:
+ return ret;
+}
+
+/* Checksum for single register. */
+static int do_singlereg_checksum(struct tasdevice_priv *tasdevice,
+ unsigned char book, unsigned char page,
+ unsigned char reg, unsigned char val)
+{
+ struct tas_crc crc_data;
+ unsigned int nData1;
+ int ret = 0;
+ bool in;
+
+ /* DSP swap command, pass */
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (reg >= TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (reg <= (TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG) + 4)))
+ return 0;
+
+ in = check_yram(&crc_data, book, page, reg, 1);
+ if (!in)
+ return 0;
+ ret = tasdevice_spi_dev_read(tasdevice,
+ TASDEVICE_REG(book, page, reg), &nData1);
+ if (ret < 0)
+ return ret;
+
+ if (nData1 != val) {
+ dev_err(tasdevice->dev,
+ "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+ book, page, reg, val, nData1);
+ tasdevice->err_code |= ERROR_YRAM_CRCCHK;
+ return -EAGAIN;
+ }
+
+ ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0);
+
+ return ret;
+}
+
+/* Block type check. */
+static void set_err_prg_cfg(unsigned int type, struct tasdevice_priv *p)
+{
+ if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A) ||
+ (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C) ||
+ (type == MAIN_DEVICE_D))
+ p->cur_prog = -1;
+ else
+ p->cur_conf = -1;
+}
+/* Checksum for data bytes. */
+static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned int len,
+ unsigned char val, unsigned char *crc_chksum)
+{
+ int ret;
+
+ if (len > 1)
+ ret = tasdev_multibytes_chksum(tas_priv, book, page, reg,
+ len);
+ else
+ ret = do_singlereg_checksum(tas_priv, book, page, reg, val);
+
+ if (ret > 0) {
+ *crc_chksum += ret;
+ goto end;
+ }
+
+ if (ret != -EAGAIN)
+ goto end;
+
+ block->nr_retry--;
+ if (block->nr_retry > 0)
+ goto end;
+
+ set_err_prg_cfg(block->type, tas_priv);
+
+end:
+ return ret;
+}
+/* Multi-data byte write. */
+static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char *data,
+ unsigned int len, unsigned int *nr_cmds,
+ unsigned char *crc_chksum)
+{
+ int ret;
+
+ if (len > 1) {
+ ret = tasdevice_spi_dev_bulk_write(tas_priv,
+ TASDEVICE_REG(book, page, reg), data + 3, len);
+ if (ret < 0)
+ return ret;
+ if (block->is_ychksum_present)
+ ret = tasdev_bytes_chksum(tas_priv, block,
+ book, page, reg, len, 0, crc_chksum);
+ } else {
+ ret = tasdevice_spi_dev_write(tas_priv,
+ TASDEVICE_REG(book, page, reg), data[3]);
+ if (ret < 0)
+ return ret;
+ if (block->is_ychksum_present)
+ ret = tasdev_bytes_chksum(tas_priv, block, book,
+ page, reg, 1, data[3], crc_chksum);
+ }
+
+ if (!block->is_ychksum_present || ret >= 0) {
+ *nr_cmds += 1;
+ if (len >= 2)
+ *nr_cmds += ((len - 2) / 4) + 1;
+ }
+
+ return ret;
+}
+/* Checksum for block. */
+static int tasdev_block_chksum(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block)
+{
+ unsigned int nr_value;
+ int ret;
+
+ ret = tasdevice_spi_dev_read(tas_priv, TASDEVICE_CHECKSUM, &nr_value);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s: read error %d.\n", __func__, ret);
+ set_err_prg_cfg(block->type, tas_priv);
+ return ret;
+ }
+
+ if ((nr_value & 0xff) != block->pchksum) {
+ dev_err(tas_priv->dev, "%s: PChkSum err %d ", __func__, ret);
+ dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n",
+ block->pchksum, (nr_value & 0xff));
+ tas_priv->err_code |= ERROR_PRAM_CRCCHK;
+ ret = -EAGAIN;
+ block->nr_retry--;
+
+ if (block->nr_retry <= 0)
+ set_err_prg_cfg(block->type, tas_priv);
+ } else {
+ tas_priv->err_code &= ~ERROR_PRAM_CRCCHK;
+ }
+
+ return ret;
+}
+/* Firmware block load function. */
+static int tasdev_load_blk(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block)
+{
+ unsigned int sleep_time, len, nr_cmds;
+ unsigned char offset, book, page, val;
+ unsigned char *data = block->data;
+ unsigned char crc_chksum = 0;
+ int ret = 0;
+
+ while (block->nr_retry > 0) {
+ if (block->is_pchksum_present) {
+ ret = tasdevice_spi_dev_write(tas_priv,
+ TASDEVICE_CHECKSUM, 0);
+ if (ret < 0)
+ break;
+ }
+
+ if (block->is_ychksum_present)
+ crc_chksum = 0;
+
+ nr_cmds = 0;
+
+ while (nr_cmds < block->nr_cmds) {
+ data = block->data + nr_cmds * 4;
+
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ val = data[3];
+
+ nr_cmds++;
+ /* Single byte write */
+ if (offset <= 0x7F) {
+ ret = tasdevice_spi_dev_write(tas_priv,
+ TASDEVICE_REG(book, page, offset),
+ val);
+ if (ret < 0)
+ break;
+ if (block->is_ychksum_present) {
+ ret = tasdev_bytes_chksum(tas_priv,
+ block, book, page, offset,
+ 1, val, &crc_chksum);
+ if (ret < 0)
+ break;
+ }
+ continue;
+ }
+ /* sleep command */
+ if (offset == 0x81) {
+ /* book -- data[0] page -- data[1] */
+ sleep_time = ((book << 8) + page)*1000;
+ fsleep(sleep_time);
+ continue;
+ }
+ /* Multiple bytes write */
+ if (offset == 0x85) {
+ data += 4;
+ len = (book << 8) + page;
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ ret = tasdev_multibytes_wr(tas_priv,
+ block, book, page, offset, data,
+ len, &nr_cmds, &crc_chksum);
+ if (ret < 0)
+ break;
+ }
+ }
+ if (ret == -EAGAIN) {
+ if (block->nr_retry > 0)
+ continue;
+ } else if (ret < 0) {
+ /* err in current device, skip it */
+ break;
+ }
+
+ if (block->is_pchksum_present) {
+ ret = tasdev_block_chksum(tas_priv, block);
+ if (ret == -EAGAIN) {
+ if (block->nr_retry > 0)
+ continue;
+ } else if (ret < 0) {
+ /* err in current device, skip it */
+ break;
+ }
+ }
+
+ if (block->is_ychksum_present) {
+ /* TBD, open it when FW ready */
+ dev_err(tas_priv->dev,
+ "Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n",
+ block->ychksum, crc_chksum);
+
+ tas_priv->err_code &=
+ ~ERROR_YRAM_CRCCHK;
+ ret = 0;
+ }
+ /* skip current blk */
+ break;
+ }
+
+ return ret;
+}
+/* Firmware block load function. */
+static int tasdevice_load_block(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block)
+{
+ int ret = 0;
+
+ block->nr_retry = 6;
+ if (tas_priv->is_loading == false)
+ return 0;
+ ret = tasdev_load_blk(tas_priv, block);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "Blk (%d) load error\n", block->type);
+
+ return ret;
+}
+
+/*
+ * Select firmware binary parser & load callback functions by ppc3 version
+ * and firmware binary version.
+ */
+static int dspfw_default_callback(struct tasdevice_priv *tas_priv,
+ unsigned int drv_ver, unsigned int ppcver)
+{
+ int rc = 0;
+
+ if (drv_ver == 0x100) {
+ if (ppcver >= PPC3_VERSION) {
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ } else {
+ switch (ppcver) {
+ case 0x00:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ dev_err(tas_priv->dev,
+ "%s: PPCVer must be 0x0 or 0x%02x",
+ __func__, PPC3_VERSION);
+ dev_err(tas_priv->dev, " Current:0x%02x\n",
+ ppcver);
+ rc = -EINVAL;
+ break;
+ }
+ }
+ } else {
+ dev_err(tas_priv->dev,
+ "DrvVer must be 0x0, 0x230 or above 0x230 ");
+ dev_err(tas_priv->dev, "current is 0x%02x\n", drv_ver);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+/* Calibration block load function. */
+static int load_calib_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_data *dev_data)
+{
+ struct tasdev_blk *block;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < dev_data->nr_blk; i++) {
+ block = &dev_data->dev_blks[i];
+ ret = tasdevice_load_block(tas_priv, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+/* DSP firmware binary file header parser function. */
+static int fw_parse_header(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr;
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &fw_hdr->fixed_hdr;
+ static const unsigned char magic_number[] = {0x35, 0x35, 0x35, 0x32, };
+ const unsigned char *buf = (unsigned char *)fmw->data;
+
+ if (offset + 92 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ if (memcmp(&buf[offset], magic_number, 4)) {
+ dev_err(tas_priv->dev, "%s: Magic num NOT match\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+
+ /*
+ * Convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host
+ */
+ fw_fixed_hdr->fwsize = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ if (fw_fixed_hdr->fwsize != fmw->size) {
+ dev_err(tas_priv->dev, "File size not match, %lu %u",
+ (unsigned long)fmw->size, fw_fixed_hdr->fwsize);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ fw_fixed_hdr->ppcver = get_unaligned_be32(&buf[offset]);
+ offset += 8;
+ fw_fixed_hdr->drv_ver = get_unaligned_be32(&buf[offset]);
+ offset += 72;
+
+out:
+ return offset;
+}
+
+/* DSP firmware binary file parser function. */
+static int tasdevice_dspfw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr;
+ struct tasdevice_fw *tas_fmw;
+ int offset = 0, ret = 0;
+
+ if (!fmw || !fmw->data) {
+ dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n",
+ __func__, tas_priv->coef_binaryname);
+ return -EINVAL;
+ }
+
+ tas_priv->fmw = kzalloc(sizeof(*tas_priv->fmw), GFP_KERNEL);
+ if (!tas_priv->fmw)
+ return -ENOMEM;
+ tas_fmw = tas_priv->fmw;
+ tas_fmw->dev = tas_priv->dev;
+ offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset);
+
+ if (offset == -EINVAL)
+ return -EINVAL;
+
+ fw_fixed_hdr = &tas_fmw->fw_hdr.fixed_hdr;
+ /* Support different versions of firmware */
+ switch (fw_fixed_hdr->drv_ver) {
+ case 0x301:
+ case 0x302:
+ case 0x502:
+ case 0x503:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ break;
+ case 0x202:
+ case 0x400:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ ret = dspfw_default_callback(tas_priv,
+ fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset);
+ if (offset < 0)
+ return offset;
+
+ offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw,
+ offset);
+ if (offset < 0)
+ return offset;
+
+ offset = tas_priv->fw_parse_configuration_data(tas_priv,
+ tas_fmw, fmw, offset);
+ if (offset < 0)
+ ret = offset;
+
+ return ret;
+}
+/* DSP firmware binary file parser function. */
+int tasdevice_spi_dsp_parser(void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ const struct firmware *fw_entry;
+ int ret;
+
+ ret = request_firmware(&fw_entry, tas_priv->coef_binaryname,
+ tas_priv->dev);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s: load %s error\n", __func__,
+ tas_priv->coef_binaryname);
+ return ret;
+ }
+
+ ret = tasdevice_dspfw_ready(fw_entry, tas_priv);
+ release_firmware(fw_entry);
+ fw_entry = NULL;
+
+ return ret;
+}
+
+/* DSP firmware program block data remove function. */
+static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog)
+{
+ struct tasdevice_data *tas_dt;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (!prog)
+ return;
+
+ tas_dt = &prog->dev_data;
+
+ if (!tas_dt->dev_blks)
+ return;
+
+ for (i = 0; i < tas_dt->nr_blk; i++) {
+ blk = &tas_dt->dev_blks[i];
+ kfree(blk->data);
+ }
+ kfree(tas_dt->dev_blks);
+}
+
+/* DSP firmware program block data remove function. */
+static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog,
+ unsigned short nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ tasdev_dsp_prog_blk_remove(&prog[i]);
+ kfree(prog);
+}
+
+/* DSP firmware config block data remove function. */
+static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg)
+{
+ struct tasdevice_data *tas_dt;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (cfg) {
+ tas_dt = &cfg->dev_data;
+
+ if (!tas_dt->dev_blks)
+ return;
+
+ for (i = 0; i < tas_dt->nr_blk; i++) {
+ blk = &tas_dt->dev_blks[i];
+ kfree(blk->data);
+ }
+ kfree(tas_dt->dev_blks);
+ }
+}
+
+/* DSP firmware config remove function. */
+static void tasdev_dsp_cfg_remove(struct tasdevice_config *config,
+ unsigned short nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ tasdev_dsp_cfg_blk_remove(&config[i]);
+ kfree(config);
+}
+
+/* DSP firmware remove function. */
+void tasdevice_spi_dsp_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev = context;
+
+ if (!tas_dev->fmw)
+ return;
+
+ if (tas_dev->fmw->programs)
+ tasdev_dsp_prog_remove(tas_dev->fmw->programs,
+ tas_dev->fmw->nr_programs);
+ if (tas_dev->fmw->configs)
+ tasdev_dsp_cfg_remove(tas_dev->fmw->configs,
+ tas_dev->fmw->nr_configurations);
+ kfree(tas_dev->fmw);
+ tas_dev->fmw = NULL;
+}
+
+/* DSP firmware calibration data remove function. */
+static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw)
+{
+ struct tasdevice_calibration *calibration;
+ struct tasdev_blk *block;
+ unsigned int blks;
+ int i;
+
+ if (!tas_fmw->calibrations)
+ goto out;
+
+ for (i = 0; i < tas_fmw->nr_calibrations; i++) {
+ calibration = &tas_fmw->calibrations[i];
+ if (!calibration)
+ continue;
+
+ if (!calibration->dev_data.dev_blks)
+ continue;
+
+ for (blks = 0; blks < calibration->dev_data.nr_blk; blks++) {
+ block = &calibration->dev_data.dev_blks[blks];
+ if (!block)
+ continue;
+ kfree(block->data);
+ }
+ kfree(calibration->dev_data.dev_blks);
+ }
+ kfree(tas_fmw->calibrations);
+out:
+ kfree(tas_fmw);
+}
+
+/* Calibration data from firmware remove function. */
+void tasdevice_spi_calbin_remove(void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+
+ if (tas_priv->cali_data_fmw) {
+ tas2781_clear_calfirmware(tas_priv->cali_data_fmw);
+ tas_priv->cali_data_fmw = NULL;
+ }
+}
+
+/* Configuration remove function. */
+void tasdevice_spi_config_info_remove(void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_rca *rca = &tas_priv->rcabin;
+ struct tasdevice_config_info **ci = rca->cfg_info;
+ unsigned int i, j;
+
+ if (!ci)
+ return;
+ for (i = 0; i < rca->ncfgs; i++) {
+ if (!ci[i])
+ continue;
+ if (ci[i]->blk_data) {
+ for (j = 0; j < ci[i]->real_nblocks; j++) {
+ if (!ci[i]->blk_data[j])
+ continue;
+ kfree(ci[i]->blk_data[j]->regdata);
+ kfree(ci[i]->blk_data[j]);
+ }
+ kfree(ci[i]->blk_data);
+ }
+ kfree(ci[i]);
+ }
+ kfree(ci);
+}
+
+/* DSP firmware program block data load function. */
+static int tasdevice_load_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_data *dev_data)
+{
+ struct tasdev_blk *block;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < dev_data->nr_blk; i++) {
+ block = &dev_data->dev_blks[i];
+ ret = tas_priv->tasdevice_load_block(tas_priv, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+/* DSP firmware program load function. */
+int tasdevice_spi_select_tuningprm_cfg(void *context, int prm_no,
+ int cfg_no, int rca_conf_no)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_rca *rca = &tas_priv->rcabin;
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_prog *program;
+ struct tasdevice_config *conf;
+ int prog_status = 0;
+ int status;
+
+ if (!tas_fmw) {
+ dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+ return 0;
+ }
+
+ if (cfg_no >= tas_fmw->nr_configurations) {
+ dev_err(tas_priv->dev,
+ "%s: cfg(%d) is not in range of conf %u\n",
+ __func__, cfg_no, tas_fmw->nr_configurations);
+ return 0;
+ }
+
+ if (prm_no >= tas_fmw->nr_programs) {
+ dev_err(tas_priv->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->nr_programs);
+ return 0;
+ }
+
+ if (rca_conf_no >= rca->ncfgs ||
+ rca_conf_no < 0 ||
+ !cfg_info) {
+ dev_err(tas_priv->dev,
+ "conf_no:%d should be in range from 0 to %u\n",
+ rca_conf_no, rca->ncfgs-1);
+ return 0;
+ }
+
+ if (prm_no >= 0 &&
+ (tas_priv->cur_prog != prm_no ||
+ tas_priv->force_fwload_status)) {
+ tas_priv->cur_conf = -1;
+ tas_priv->is_loading = true;
+ prog_status++;
+ }
+
+ if (prog_status) {
+ program = &tas_fmw->programs[prm_no];
+ tasdevice_load_data(tas_priv, &program->dev_data);
+ if (tas_priv->is_loaderr == false &&
+ tas_priv->is_loading == true) {
+ struct tasdevice_fw *cal_fmw =
+ tas_priv->cali_data_fmw;
+
+ if (cal_fmw) {
+ struct tasdevice_calibration
+ *cal = cal_fmw->calibrations;
+
+ if (cal)
+ load_calib_data(tas_priv,
+ &cal->dev_data);
+ }
+ tas_priv->cur_prog = prm_no;
+ }
+
+ }
+
+ if (cfg_no &&
+ (cfg_no != tas_priv->cur_conf) &&
+ (tas_priv->is_loaderr == false)) {
+ status++;
+ tas_priv->is_loading = true;
+ } else {
+ tas_priv->is_loading = false;
+ }
+ if (status) {
+ conf = &tas_fmw->configs[cfg_no];
+ status = 0;
+ tasdevice_load_data(tas_priv, &conf->dev_data);
+ if (tas_priv->is_loaderr == true) {
+ status |= 1 << 4;
+ } else if (tas_priv->is_loaderr == false &&
+ tas_priv->is_loading == true) {
+ tas_priv->cur_conf = cfg_no;
+ }
+ } else {
+ dev_dbg(tas_priv->dev, "%s: Unneeded loading dsp conf %d\n",
+ __func__, cfg_no);
+ }
+
+ return prog_status;
+}
+
+/* DSP firmware program load interface function. */
+int tasdevice_spi_prmg_load(void *context, int prm_no)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_prog *program;
+ struct tasdevice_config *conf;
+ int prog_status = 0;
+
+ if (!tas_fmw) {
+ dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+ return 0;
+ }
+
+ if (prm_no >= tas_fmw->nr_programs) {
+ dev_err(tas_priv->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->nr_programs);
+ return 0;
+ }
+
+ if (prm_no >= 0) {
+ tas_priv->cur_conf = 0;
+ tas_priv->is_loading = true;
+ prog_status++;
+ }
+ if (prog_status) {
+ program = &tas_fmw->programs[prm_no];
+ tasdevice_load_data(tas_priv, &program->dev_data);
+ tas_priv->cur_prog = prm_no;
+ }
+ if (prog_status) {
+ conf = &tas_fmw->configs[tas_priv->cur_conf];
+ tasdevice_load_data(tas_priv, &conf->dev_data);
+ }
+
+ return prog_status;
+}
+
+/* DSP firmware program switch interface function. */
+void tasdevice_spi_tuning_switch(void *context, int state)
+{
+ struct tasdevice_priv *tas_priv = context;
+ int profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
+
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) {
+ dev_err(tas_priv->dev, "DSP bin file not loaded\n");
+ return;
+ }
+
+ if (state == 0) {
+ tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+ } else {
+ tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+ }
+}
--
2.40.1
1
0

19 Jul '24
From: Arnd Bergmann <arnd(a)arndb.de>
An earlier patch changed the DECLARE_TLV_DB_SCALE declaration, but
now there are additional static const variables that cause
the same build warnings:
In file included from sound/pci/hda/tas2781_hda_i2c.c:23:
include/sound/tas2781-tlv.h:23:28: error: 'tas2563_dvc_table' defined but not used [-Werror=unused-const-variable=]
23 | static const unsigned char tas2563_dvc_table[][4] = {
| ^~~~~~~~~~~~~~~~~
In file included from include/sound/tlv.h:10,
from sound/pci/hda/tas2781_hda_i2c.c:22:
include/sound/tas2781-tlv.h:20:35: error: 'tas2563_dvc_tlv' defined but not used [-Werror=unused-const-variable=]
20 | static const DECLARE_TLV_DB_SCALE(tas2563_dvc_tlv, -12150, 50, 1);
| ^~~~~~~~~~~~~~~
Mark them all as unused as well.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
include/sound/tas2781-tlv.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/include/sound/tas2781-tlv.h b/include/sound/tas2781-tlv.h
index 99c41bfc7827..00fd4d449ff3 100644
--- a/include/sound/tas2781-tlv.h
+++ b/include/sound/tas2781-tlv.h
@@ -16,11 +16,11 @@
#define __TAS2781_TLV_H__
static const __maybe_unused DECLARE_TLV_DB_SCALE(dvc_tlv, -10000, 100, 0);
-static const DECLARE_TLV_DB_SCALE(amp_vol_tlv, 1100, 50, 0);
-static const DECLARE_TLV_DB_SCALE(tas2563_dvc_tlv, -12150, 50, 1);
+static const __maybe_unused DECLARE_TLV_DB_SCALE(amp_vol_tlv, 1100, 50, 0);
+static const __maybe_unused DECLARE_TLV_DB_SCALE(tas2563_dvc_tlv, -12150, 50, 1);
/* pow(10, db/20) * pow(2,30) */
-static const unsigned char tas2563_dvc_table[][4] = {
+static const __maybe_unused unsigned char tas2563_dvc_table[][4] = {
{ 0X00, 0X00, 0X00, 0X00 }, /* -121.5db */
{ 0X00, 0X00, 0X03, 0XBC }, /* -121.0db */
{ 0X00, 0X00, 0X03, 0XF5 }, /* -120.5db */
--
2.39.2
2
1
From: wangdicheng <wangdicheng(a)kylinos.cn>
Sonix HD USB Camera does not support reading the sample rate which leads
to many lines of "cannot get freq at ep 0x84".
This patch adds the USB ID to quirks.c and avoids those error messages.
(snip)
[1.789698] usb 3-3: new high-speed USB device number 2 using xhci_hcd
[1.984121] usb 3-3: New USB device found, idVendor=0c45, idProduct=6340, bcdDevice= 0.00
[1.984124] usb 3-3: New USB device strings: Mfr=2, Product=1, SerialNumber=0
[1.984127] usb 3-3: Product: USB 2.0 Camera
[1.984128] usb 3-3: Manufacturer: Sonix Technology Co., Ltd.
[5.440957] usb 3-3: 3:1: cannot get freq at ep 0x84
[12.130679] usb 3-3: 3:1: cannot get freq at ep 0x84
[12.175065] usb 3-3: 3:1: cannot get freq at ep 0x84
Signed-off-by: wangdicheng <wangdicheng(a)kylinos.cn>
---
sound/usb/quirks.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 09712e61c606..c1fd690cbac0 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2033,6 +2033,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x0499, 0x3108, /* Yamaha YIT-W12TX */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x0c45, 0x6340, /* Sonix HD USB Camera */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x04d8, 0xfeea, /* Benchmark DAC1 Pre */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x04e8, 0xa051, /* Samsung USBC Headset (AKG) */
--
2.25.1
1
0
From: wangdicheng <wangdicheng(a)kylinos.cn>
I own an external usb Webcam, HD webcam, which had low mic volume and
inconsistent sound quality. Video works as expected.
(snip)
[ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
[ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
[ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
[ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
[ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
[ 96.092610][ 2] [ T3680] usb 5-2.2: Warning! Unlikely big volume range (=4096), cval->res is probably wrong.
[ 96.102436][ 2] [ T3680] usb 5-2.2: [5] FU [Mic Capture Volume] ch = 1, val = 0/4096/1
Set up quirk cval->res to 16 for 256 levels,
Set GET_SAMPLE_RATE quirk flag to stop trying to get the sample rate.
Confirmed that happened anyway later due to the backoff mechanism,
After 3 failures.
All audio stream on device interfaces share the same values,
apart from wMaxPacketSize and tSamFreq :
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 4
bNumEndpoints 1
bInterfaceClass 1 Audio
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 4
bNumEndpoints 1
bInterfaceClass 1 Audio
bInterfaceSubClass 2 Streaming
bInterfaceProtocol 0
iInterface 0
AudioStreaming Interface Descriptor:
bLength 7
bDescriptorType 36
bDescriptorSubtype 1 (AS_GENERAL)
bTerminalLink 3
bDelay 1 frames
wFormatTag 0x0001 PCM
AudioStreaming Interface Descriptor:
bLength 11
bDescriptorType 36
bDescriptorSubtype 2 (FORMAT_TYPE)
bFormatType 1 (FORMAT_TYPE_I)
bNrChannels 1
bSubframeSize 2
bBitResolution 16
bSamFreqType 1 Discrete
tSamFreq[ 0] 48000
Endpoint Descriptor:
bLength 9
bDescriptorType 5
bEndpointAddress 0x86 EP 6 IN
bmAttributes 5
Transfer Type Isochronous
Synch Type Asynchronous
Usage Type Data
wMaxPacketSize 0x0064 1x 100 bytes
bInterval 4
bRefresh 0
bSynchAddress 0
AudioStreaming Endpoint Descriptor:
bLength 7
bDescriptorType 37
bDescriptorSubtype 1 (EP_GENERAL)
bmAttributes 0x01
Sampling Frequency
bLockDelayUnits 0 Undefined
wLockDelay 0x0000
(snip)
Testing patch provides consistent good sound recording quality and volume range.
(snip)
[ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
[ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
[ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
[ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
[ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
[ 96.110630][ 3] [ T3680] usbcore: registered new interface driver snd-usb-audio
[ 96.114329][ 7] [ T3677] usb 5-2.2: Found UVC 1.00 device HD webcam (1bcf:2281)
[ 96.167555][ 7] [ T3677] usbcore: registered new interface driver uvcvideo
Signed-off-by: wangdicheng <wangdicheng(a)kylinos.cn>
---
v2 -> v3: make the mail address of Signed-off-by consistent with the From address
V1 -> V2: align the space,Update code to v6.8-rc3,and make modifications based on it
---
sound/usb/mixer.c | 7 +++++++
sound/usb/quirks.c | 2 ++
2 files changed, 9 insertions(+)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 409fc1164694..d1bdb0b93bda 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1211,6 +1211,13 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
cval->res = 16;
}
break;
+ case USB_ID(0x1bcf, 0x2281): /* HD Webcam */
+ if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ usb_audio_info(chip,
+ "set resolution quirk: cval->res = 16\n");
+ cval->res = 16;
+ }
+ break;
}
}
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 09712e61c606..f222532d4d43 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2185,6 +2185,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_ALIGN_TRANSFER),
DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
QUIRK_FLAG_ALIGN_TRANSFER),
+ DEVICE_FLG(0x1bcf, 0x2281, /* HD Webcam */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
/* Vendor matches */
VENDOR_FLG(0x045e, /* MS Lifecam */
--
2.25.1
1
0
Dear all,
I am having a question regarding how to specify 'champ' for a
plugin. I do not see any examples of this. I tried many ways to
specify but failed.
As indicated by the link below, I can specify the MAP as a string
array. Can someone provide one example on how exactly this [chmap
MAP] can be specified?
"
pcm.name {
type cras
[chmap MAP] # Provide channel maps; MAP is a string array
}
"
https://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html
I can something like these, but none of these work for me.
chmap LFE
chmap "LFE"
chmap FR,FL,LFE
champ "FR,FL,LFE"
Thanks!
Xinhui,
3
3

19 Jul '24
So far, aplaymidi2 passes the MIDI1/MIDI2 channel voice UMP messages
to the target while processing other UMP messages internally. But
sometimes we'd like to pass all UMP messages as is and let the
receiver processes.
This patch adds a new option -a (or --passall) to pass the all UMP
packets included in the given MIDI Clip file to the target as-is.
Signed-off-by: Takashi Iwai <tiwai(a)suse.de>
---
seq/aplaymidi2/aplaymidi2.1 | 8 ++++++++
seq/aplaymidi2/aplaymidi2.c | 20 +++++++++++++++-----
2 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/seq/aplaymidi2/aplaymidi2.1 b/seq/aplaymidi2/aplaymidi2.1
index fc85943c0d38..681796222914 100644
--- a/seq/aplaymidi2/aplaymidi2.1
+++ b/seq/aplaymidi2/aplaymidi2.1
@@ -67,6 +67,14 @@ Default is 2 seconds.
.I \-s, \-\-silent
Don't show message texts.
+.TP
+.I \-a, \-\-passall
+Pass all UMP packets as is.
+
+As default, \fBaplaymidi2\fP passes only MIDI1 and MIDI2 channel voice
+messages and process other UMP packets internally.
+With this option, it passes all UMP packets to the target.
+
.SH SEE ALSO
pmidi(1)
.br
diff --git a/seq/aplaymidi2/aplaymidi2.c b/seq/aplaymidi2/aplaymidi2.c
index 6a1f21e92444..f5dfdbd91f30 100644
--- a/seq/aplaymidi2/aplaymidi2.c
+++ b/seq/aplaymidi2/aplaymidi2.c
@@ -21,6 +21,7 @@ static snd_seq_addr_t ports[16];
static int queue;
static int end_delay = 2;
static int silent;
+static int passall;
static unsigned int _current_tempo = 50000000; /* default 120 bpm */
static unsigned int tempo_base = 10;
@@ -411,6 +412,9 @@ static void play_midi(FILE *file)
while ((len = read_ump_packet(file, ump)) > 0) {
const snd_ump_msg_hdr_t *h = (snd_ump_msg_hdr_t *)ump;
+ if (passall)
+ send_ump(ump, len);
+
if (h->type == SND_UMP_MSG_TYPE_UTILITY) {
const snd_ump_msg_utility_t *uh =
(const snd_ump_msg_utility_t *)ump;
@@ -448,9 +452,10 @@ static void play_midi(FILE *file)
end_clip();
continue;
}
- } else if (h->type == SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE ||
- h->type == SND_UMP_MSG_TYPE_DATA ||
- h->type == SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE) {
+ } else if (!passall &&
+ (h->type == SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE ||
+ h->type == SND_UMP_MSG_TYPE_DATA ||
+ h->type == SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE)) {
send_ump(ump, len);
}
}
@@ -496,7 +501,8 @@ static void usage(const char *argv0)
"-V, --version print current version\n"
"-p, --port=client:port,... set port(s) to play to\n"
"-d, --delay=seconds delay after song ends\n"
- "-s, --silent don't show texts\n",
+ "-s, --silent don't show texts\n"
+ "-a, --passall pass all UMP packets as-is\n",
argv0);
}
@@ -513,13 +519,14 @@ int main(int argc, char *argv[])
{"port", 1, NULL, 'p'},
{"delay", 1, NULL, 'd'},
{"silent", 0, NULL, 's'},
+ {"passall", 0, NULL, 'a'},
{0}
};
int c;
init_seq();
- while ((c = getopt_long(argc, argv, "hVp:d:s",
+ while ((c = getopt_long(argc, argv, "hVp:d:sa",
long_options, NULL)) != -1) {
switch (c) {
case 'h':
@@ -537,6 +544,9 @@ int main(int argc, char *argv[])
case 's':
silent = 1;
break;
+ case 'a':
+ passall = 1;
+ break;
default:
usage(argv[0]);
return 1;
--
2.43.0
1
0

[PATCH] ASoC: sof: amd: fix for firmware reload failure in Vangogh platform
by Venkata Prasad Potturu 18 Jul '24
by Venkata Prasad Potturu 18 Jul '24
18 Jul '24
Setting ACP ACLK as clock source when ACP enters D0 state causing
firmware load failure, as per design clock source should be internal
clock.
Remove acp_clkmux_sel field so that ACP will use internal clock
source when ACP enters into D0 state.
Fixes: d0dab6b76a9f ("ASoC: SOF: amd: Add sof support for vangogh platform")
Signed-off-by: Venkata Prasad Potturu <venkataprasad.potturu(a)amd.com>
---
sound/soc/sof/amd/pci-vangogh.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/sound/soc/sof/amd/pci-vangogh.c b/sound/soc/sof/amd/pci-vangogh.c
index 16eb2994fbab..eba580840100 100644
--- a/sound/soc/sof/amd/pci-vangogh.c
+++ b/sound/soc/sof/amd/pci-vangogh.c
@@ -34,7 +34,6 @@ static const struct sof_amd_acp_desc vangogh_chip_info = {
.dsp_intr_base = ACP5X_DSP_SW_INTR_BASE,
.sram_pte_offset = ACP5X_SRAM_PTE_OFFSET,
.hw_semaphore_offset = ACP5X_AXI2DAGB_SEM_0,
- .acp_clkmux_sel = ACP5X_CLKMUX_SEL,
.probe_reg_offset = ACP5X_FUTURE_REG_ACLK_0,
};
--
2.39.2
2
1
I own an external usb Webcam, HD webcam, which had low mic volume and
inconsistent sound quality. Video works as expected.
(snip)
[ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
[ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
[ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
[ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
[ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
[ 96.092610][ 2] [ T3680] usb 5-2.2: Warning! Unlikely big volume range (=4096), cval->res is probably wrong.
[ 96.102436][ 2] [ T3680] usb 5-2.2: [5] FU [Mic Capture Volume] ch = 1, val = 0/4096/1
Set up quirk cval->res to 16 for 256 levels,
Set GET_SAMPLE_RATE quirk flag to stop trying to get the sample rate.
Confirmed that happened anyway later due to the backoff mechanism,
After 3 failures.
All audio stream on device interfaces share the same values,
apart from wMaxPacketSize and tSamFreq :
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 4
bNumEndpoints 1
bInterfaceClass 1 Audio
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 4
bNumEndpoints 1
bInterfaceClass 1 Audio
bInterfaceSubClass 2 Streaming
bInterfaceProtocol 0
iInterface 0
AudioStreaming Interface Descriptor:
bLength 7
bDescriptorType 36
bDescriptorSubtype 1 (AS_GENERAL)
bTerminalLink 3
bDelay 1 frames
wFormatTag 0x0001 PCM
AudioStreaming Interface Descriptor:
bLength 11
bDescriptorType 36
bDescriptorSubtype 2 (FORMAT_TYPE)
bFormatType 1 (FORMAT_TYPE_I)
bNrChannels 1
bSubframeSize 2
bBitResolution 16
bSamFreqType 1 Discrete
tSamFreq[ 0] 48000
Endpoint Descriptor:
bLength 9
bDescriptorType 5
bEndpointAddress 0x86 EP 6 IN
bmAttributes 5
Transfer Type Isochronous
Synch Type Asynchronous
Usage Type Data
wMaxPacketSize 0x0064 1x 100 bytes
bInterval 4
bRefresh 0
bSynchAddress 0
AudioStreaming Endpoint Descriptor:
bLength 7
bDescriptorType 37
bDescriptorSubtype 1 (EP_GENERAL)
bmAttributes 0x01
Sampling Frequency
bLockDelayUnits 0 Undefined
wLockDelay 0x0000
(snip)
Testing patch provides consistent good sound recording quality and volume range.
(snip)
[ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
[ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
[ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
[ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
[ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
[ 96.110630][ 3] [ T3680] usbcore: registered new interface driver snd-usb-audio
[ 96.114329][ 7] [ T3677] usb 5-2.2: Found UVC 1.00 device HD webcam (1bcf:2281)
[ 96.167555][ 7] [ T3677] usbcore: registered new interface driver uvcvideo
Signed-off-by: wangdicheng <wangdicheng(a)kylinos.cn>
---
V1 -> V2: align the space,Update code to v6.8-rc3,and make modifications based on it
---
sound/usb/mixer.c | 7 +++++++
sound/usb/quirks.c | 2 ++
2 files changed, 9 insertions(+)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 409fc1164694..d1bdb0b93bda 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1211,6 +1211,13 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
cval->res = 16;
}
break;
+ case USB_ID(0x1bcf, 0x2281): /* HD Webcam */
+ if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ usb_audio_info(chip,
+ "set resolution quirk: cval->res = 16\n");
+ cval->res = 16;
+ }
+ break;
}
}
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 09712e61c606..f222532d4d43 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2185,6 +2185,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_ALIGN_TRANSFER),
DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
QUIRK_FLAG_ALIGN_TRANSFER),
+ DEVICE_FLG(0x1bcf, 0x2281, /* HD Webcam */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
/* Vendor matches */
VENDOR_FLG(0x045e, /* MS Lifecam */
--
2.25.1
1
0

18 Jul '24
Add new vendor_id and subsystem_id in quirk for Lenovo Hera2 Laptop.
Signed-off-by: Shenghao Ding <shenghao-ding(a)ti.com>
---
sound/pci/hda/patch_realtek.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 766f0b1d3e9d..bac68a84fd50 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -10539,6 +10539,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x231e, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318),
SND_PCI_QUIRK(0x17aa, 0x231f, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318),
+ SND_PCI_QUIRK(0x17aa, 0x2326, "Hera2", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
--
2.34.1
2
1

[REGRESSION] ALSA: firewire-lib: snd_pcm_period_elapsed deadlock with Fireface 800
by edmund.raile 18 Jul '24
by edmund.raile 18 Jul '24
18 Jul '24
On kernels since 5.14.0, ALSA playback to the FireWire RME Fireface 800
audio interface results in a deadlock involving snd_pcm_period_elapsed(),
freezing the system.
On kernels 5.0.0 to 5.13.19 the interface works just fine, as it does with
the RME driver.
Distributions tested:
Ubuntu
Manjaro
Arch
Fedora
FireWire chipsets tested:
LSI FW643
TI XIO2213B
Platforms tested:
Intel i5 4570 on AsRock H97 Pro4
Intel i5 12600K on MSI MS-7D25
The behavior was also observed on the RME forum:
https://forum.rme-audio.de/viewtopic.php?pid=190472#p190472
Shortened traces of 6.10.0-rc7 (Arch linux-mainline):
RIP: 0010:tasklet_unlock_spin_wait (./arch/x86/include/asm/bitops.h:206 ./arch/x86/include/asm/bitops.h:238 ./include/asm-generic/bitops/instrumented-non-atomic.h:142 kernel/softirq.c:851)
<NMI>
? watchdog_hardlockup_check.cold (kernel/watchdog.c:200)
? __perf_event_overflow (kernel/events/core.c:9737 (discriminator 2))
? handle_pmi_common (arch/x86/events/intel/core.c:3061 (discriminator 1))
? intel_pmu_handle_irq (./arch/x86/include/asm/paravirt.h:192 arch/x86/events/intel/core.c:2428 arch/x86/events/intel/core.c:3127)
? perf_event_nmi_handler (arch/x86/events/core.c:1744 arch/x86/events/core.c:1730)
? nmi_handle (arch/x86/kernel/nmi.c:151)
? default_do_nmi (arch/x86/kernel/nmi.c:352 (discriminator 61))
? exc_nmi (arch/x86/kernel/nmi.c:546)
? end_repeat_nmi (arch/x86/entry/entry_64.S:1408)
? tasklet_unlock_spin_wait (./arch/x86/include/asm/bitops.h:206 ./arch/x86/include/asm/bitops.h:238 ./include/asm-generic/bitops/instrumented-non-atomic.h:142 kernel/softirq.c:851)
? tasklet_unlock_spin_wait (./arch/x86/include/asm/bitops.h:206 ./arch/x86/include/asm/bitops.h:238 ./include/asm-generic/bitops/instrumented-non-atomic.h:142 kernel/softirq.c:851)
? tasklet_unlock_spin_wait (./arch/x86/include/asm/bitops.h:206 ./arch/x86/include/asm/bitops.h:238 ./include/asm-generic/bitops/instrumented-non-atomic.h:142 kernel/softirq.c:851)
</NMI>
<TASK>
ohci_flush_iso_completions (./include/linux/interrupt.h:740 drivers/firewire/ohci.c:3530) firewire_ohci
amdtp_domain_stream_pcm_pointer (sound/firewire/amdtp-stream.c:1858) snd_firewire_lib
snd_pcm_update_hw_ptr0 (sound/core/pcm_lib.c:304) snd_pcm
snd_pcm_status64 (sound/core/pcm_native.c:1034) snd_pcm
snd_pcm_status_user64 (./include/linux/uaccess.h:191 sound/core/pcm_native.c:1096) snd_pcm
snd_pcm_ioctl (sound/core/pcm_native.c:3401 (discriminator 1)) snd_pcm
__x64_sys_ioctl (fs/ioctl.c:51 fs/ioctl.c:907 fs/ioctl.c:893 fs/ioctl.c:893)
do_syscall_64 (arch/x86/entry/common.c:52 (discriminator 1) arch/x86/entry/common.c:83 (discriminator 1))
? snd_pcm_status_user64 (sound/core/pcm_native.c:1096 (discriminator 1)) snd_pcm
? futex_wake (kernel/futex/waitwake.c:173)
? do_futex (kernel/futex/syscalls.c:107 (discriminator 1))
? __x64_sys_futex (kernel/futex/syscalls.c:179 kernel/futex/syscalls.c:160 kernel/futex/syscalls.c:160)
? syscall_exit_to_user_mode (kernel/entry/common.c:221)
? do_syscall_64 (./arch/x86/include/asm/cpufeature.h:178 arch/x86/entry/common.c:98)
? do_futex (kernel/futex/syscalls.c:107 (discriminator 1))
? __x64_sys_futex (kernel/futex/syscalls.c:179 kernel/futex/syscalls.c:160 kernel/futex/syscalls.c:160)
? syscall_exit_to_user_mode (kernel/entry/common.c:221)
? do_syscall_64 (./arch/x86/include/asm/cpufeature.h:178 arch/x86/entry/common.c:98)
? syscall_exit_to_user_mode (kernel/entry/common.c:221)
? do_syscall_64 (./arch/x86/include/asm/cpufeature.h:178 arch/x86/entry/common.c:98)
? do_syscall_64 (./arch/x86/include/asm/cpufeature.h:178 arch/x86/entry/common.c:98)
? __irq_exit_rcu (kernel/softirq.c:620 (discriminator 1) kernel/softirq.c:639 (discriminator 1))
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130)
RIP: 0010:native_queued_spin_lock_slowpath (kernel/locking/qspinlock.c:380 (discriminator 3))
<NMI>
? watchdog_hardlockup_check.cold (kernel/watchdog.c:200)
? __perf_event_overflow (kernel/events/core.c:9737 (discriminator 2))
? handle_pmi_common (arch/x86/events/intel/core.c:3061 (discriminator 1))
? intel_pmu_handle_irq (./arch/x86/include/asm/paravirt.h:192 arch/x86/events/intel/core.c:2428 arch/x86/events/intel/core.c:3127)
? perf_event_nmi_handler (arch/x86/events/core.c:1744 arch/x86/events/core.c:1730)
? nmi_handle (arch/x86/kernel/nmi.c:151)
? default_do_nmi (arch/x86/kernel/nmi.c:352 (discriminator 61))
? exc_nmi (arch/x86/kernel/nmi.c:546)
? end_repeat_nmi (arch/x86/entry/entry_64.S:1408)
? native_queued_spin_lock_slowpath (kernel/locking/qspinlock.c:380 (discriminator 3))
? native_queued_spin_lock_slowpath (kernel/locking/qspinlock.c:380 (discriminator 3))
? native_queued_spin_lock_slowpath (kernel/locking/qspinlock.c:380 (discriminator 3))
</NMI>
<IRQ>
_raw_spin_lock_irqsave (./arch/x86/include/asm/paravirt.h:584 ./arch/x86/include/asm/qspinlock.h:51 ./include/asm-generic/qspinlock.h:114 ./include/linux/spinlock.h:187 ./include/linux/spinlock_api_smp.h:111 kernel/locking/spinlock.c:162)
snd_pcm_period_elapsed (sound/core/pcm_lib.c:1905) snd_pcm
process_rx_packets (sound/firewire/amdtp-stream.c:1164) snd_firewire_lib
irq_target_callback (sound/firewire/amdtp-stream.c:1549) snd_firewire_lib
handle_it_packet (drivers/firewire/ohci.c:2786 drivers/firewire/ohci.c:2974) firewire_ohci
context_tasklet (drivers/firewire/ohci.c:1127) firewire_ohci
tasklet_action_common.isra.0 (kernel/softirq.c:789)
handle_softirqs (kernel/softirq.c:554)
__irq_exit_rcu (kernel/softirq.c:589 kernel/softirq.c:428 kernel/softirq.c:637)
common_interrupt (arch/x86/kernel/irq.c:278 (discriminator 35))
</IRQ>
<TASK>
asm_common_interrupt (./arch/x86/include/asm/idtentry.h:693)
It can be induced by direct ALSA playback to the device:
mpv --audio-device=alsa/sysdefault:CARD=Fireface800 Spor-Ignition.flac
Time to occurrence ranges from two seconds to 30 minutes.
Loading the CPU appears to increase the likelihood:
stress --cpu $(nproc)
So does switching between applications via workspaces (only tested Xfce).
The regression has been traced to these two commits:
7ba5ca32fe6e8d2e153fb5602997336517b34743
b5b519965c4c364ae65c49fe9f11d222dd70a9c2
I am currently testing a simple patch, in essence reverting both commits.
Behaves well so far (stable), will likely send it in tomorrow.
#regzbot introduced: 7ba5ca32fe6e
2
2

18 Jul '24
alsa-project/alsa-ucm-conf issue #433 was opened from pma1:
The mic and sound output works fine.
The mute LED works fine.
But when press Fn+F4, the mic-mute led is not working sometimes after bootup.
The failrate is like 1/10.
Tested with latest alsa-ucm from git, it's reproduced.
/sys/class/sound/ctl-led/mic/card1# cat list
7
# amixer -c 1 controls
numid=12,iface=CARD,name='Headphone Jack'
numid=11,iface=CARD,name='Mic Jack'
numid=13,iface=CARD,name='Speaker Phantom Jack'
numid=10,iface=MIXER,name='Master Playback Switch'
numid=9,iface=MIXER,name='Master Playback Volume'
numid=2,iface=MIXER,name='Headphone Playback Switch'
numid=1,iface=MIXER,name='Headphone Playback Volume'
numid=16,iface=MIXER,name='PCM Playback Volume'
numid=17,iface=MIXER,name='Mic ACP LED Capture Switch'
numid=8,iface=MIXER,name='Mic Boost Volume'
numid=7,iface=MIXER,name='Capture Switch'
numid=6,iface=MIXER,name='Capture Volume'
numid=5,iface=MIXER,name='Auto-Mute Mode'
numid=4,iface=MIXER,name='Speaker Playback Switch'
numid=3,iface=MIXER,name='Speaker Playback Volume'
numid=15,iface=PCM,name='Capture Channel Map'
numid=14,iface=PCM,name='Playback Channel Map'
# echo 'Capture Switch' > detach
# echo 'Mic ACP LED Capture Switch' > attach
/sys/class/sound/ctl-led/mic/card1# cat list
17
Then the micmute LED will work.
[alsa-info-amd-micmutled.txt](https://github.com/user-attachments/files/1627…
Issue URL : https://github.com/alsa-project/alsa-ucm-conf/issues/433
Repository URL: https://github.com/alsa-project/alsa-ucm-conf
1
0

Inconsistent order of channels on C-Media 7.1 channel device
by victorï¼ allumeenergy.com.au 18 Jul '24
by victorï¼ allumeenergy.com.au 18 Jul '24
18 Jul '24
I have a USB sound device (Startech ICUSBAUDIO7D, which comes up as `ID 0d8c:0102 C-Media Electronics, Inc. CM106 Like Sound Device` through lsusb) which I'm trying to program the output of for 8 channels. The python code (outputs a 50Hz sine wave) I'm using is below (each channel gets a different amplitude so I can identify them with an oscilloscope).
The sound card has 4 stereo jacks, to which I have a scope connected.
The problem is that on different runs of the program, the output mappings are not consistent - each run of the program might have the order of the channels different to the previous. So running it one time might have amplitudes (0, 0.125) on the first jack, but the next run it might have those (0.75, 0.25) on the first jack (it doesn't appear to be 'paired' per jack)
I initially discovered this when using portaudio, and decided to try using alsa-only and the issue persists.
Is this likely to be a problem with the driver, alsa-lib, or is there something else going on here?
```
import numpy as np
import alsaaudio
def sine_wave(amplitude=1, freq=50, sample_rate=48000):
wave_len = sample_rate/freq
samples = (amplitude * 32767 * np.sin(2 * np.pi * np.arange(wave_len) * freq / sample_rate)).astype(np.int16)
return samples
def eightchan(freq=50, sample_rate=48000):
wave_len = sample_rate//freq
frames = np.zeros((wave_len, 8), dtype=np.int16)
samples = sine_wave()
for c in range(8):
frames[:wave_len,c] = samples[:wave_len]*(c/8)
return frames.tobytes()
if __name__ == '__main__':
pcm = alsaaudio.PCM(device='surround71:CARD=ICUSBAUDIO7D,DEV=0', format=alsaaudio.PCM_FORMAT_S16_LE, channels=8, rate=48000, type=alsaaudio.PCM_PLAYBACK, periodsize=960, periods=1)
frames = eightchan()
while True:
pcm.write(frames)
```
alsa-info output is attached
[alsa-info.txt](https://github.com/user-attachments/files/16277691/alsa-info…
1
1

18 Jul '24
alsa-project/alsa-lib issue #402 was opened from victorallume:
I have a USB sound device (Startech ICUSBAUDIO7D, which comes up as `ID 0d8c:0102 C-Media Electronics, Inc. CM106 Like Sound Device` through lsusb) which I'm trying to program the output of for 8 channels. The python code (outputs a 50Hz sine wave) I'm using is below (each channel gets a different amplitude so I can identify them with an oscilloscope).
The sound card has 4 stereo jacks, to which I have a scope connected.
The problem is that on different runs of the program, the output mappings are not consistent - each run of the program might have the order of the channels different to the previous. So running it one time might have amplitudes (0, 0.125) on the first jack, but the next run it might have those (0.75, 0.25) on the first jack (it doesn't appear to be 'paired' per jack)
I initially discovered this when using portaudio, and decided to try using alsa-only and the issue persists.
Is this likely to be a problem with the driver, alsa-lib, or is there something else going on here?
```
import numpy as np
import alsaaudio
def sine_wave(amplitude=1, freq=50, sample_rate=48000):
wave_len = sample_rate/freq
samples = (amplitude * 32767 * np.sin(2 * np.pi * np.arange(wave_len) * freq / sample_rate)).astype(np.int16)
return samples
def eightchan(freq=50, sample_rate=48000):
wave_len = sample_rate//freq
frames = np.zeros((wave_len, 8), dtype=np.int16)
samples = sine_wave()
for c in range(8):
frames[:wave_len,c] = samples[:wave_len]*(c/8)
return frames.tobytes()
if __name__ == '__main__':
pcm = alsaaudio.PCM(device='surround71:CARD=ICUSBAUDIO7D,DEV=0', format=alsaaudio.PCM_FORMAT_S16_LE, channels=8, rate=48000, type=alsaaudio.PCM_PLAYBACK, periodsize=960, periods=1)
frames = eightchan()
while True:
pcm.write(frames)
```
alsa-info output is attached
[alsa-info.txt](https://github.com/user-attachments/files/16277691/alsa-info…
Issue URL : https://github.com/alsa-project/alsa-lib/issues/402
Repository URL: https://github.com/alsa-project/alsa-lib
1
0
I own an external usb Webcam, HD webcam, which had low mic volume and
inconsistent sound quality. Video works as expected.
(snip)
[ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
[ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
[ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
[ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
[ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
[ 96.092610][ 2] [ T3680] usb 5-2.2: Warning! Unlikely big volume range (=4096), cval->res is probably wrong.
[ 96.102436][ 2] [ T3680] usb 5-2.2: [5] FU [Mic Capture Volume] ch = 1, val = 0/4096/1
Set up quirk cval->res to 16 for 256 levels,
Set GET_SAMPLE_RATE quirk flag to stop trying to get the sample rate.
Confirmed that happened anyway later due to the backoff mechanism,
After 3 failures.
All audio stream on device interfaces share the same values,
apart from wMaxPacketSize and tSamFreq :
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 4
bNumEndpoints 1
bInterfaceClass 1 Audio
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 4
bNumEndpoints 1
bInterfaceClass 1 Audio
bInterfaceSubClass 2 Streaming
bInterfaceProtocol 0
iInterface 0
AudioStreaming Interface Descriptor:
bLength 7
bDescriptorType 36
bDescriptorSubtype 1 (AS_GENERAL)
bTerminalLink 3
bDelay 1 frames
wFormatTag 0x0001 PCM
AudioStreaming Interface Descriptor:
bLength 11
bDescriptorType 36
bDescriptorSubtype 2 (FORMAT_TYPE)
bFormatType 1 (FORMAT_TYPE_I)
bNrChannels 1
bSubframeSize 2
bBitResolution 16
bSamFreqType 1 Discrete
tSamFreq[ 0] 48000
Endpoint Descriptor:
bLength 9
bDescriptorType 5
bEndpointAddress 0x86 EP 6 IN
bmAttributes 5
Transfer Type Isochronous
Synch Type Asynchronous
Usage Type Data
wMaxPacketSize 0x0064 1x 100 bytes
bInterval 4
bRefresh 0
bSynchAddress 0
AudioStreaming Endpoint Descriptor:
bLength 7
bDescriptorType 37
bDescriptorSubtype 1 (EP_GENERAL)
bmAttributes 0x01
Sampling Frequency
bLockDelayUnits 0 Undefined
wLockDelay 0x0000
(snip)
Testing patch provides consistent good sound recording quality and volume range.
(snip)
[ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
[ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
[ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
[ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
[ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
[ 96.110630][ 3] [ T3680] usbcore: registered new interface driver snd-usb-audio
[ 96.114329][ 7] [ T3677] usb 5-2.2: Found UVC 1.00 device HD webcam (1bcf:2281)
[ 96.167555][ 7] [ T3677] usbcore: registered new interface driver uvcvideo
Signed-off-by: wangdicheng <wangdicheng(a)kylinos.cn>
---
v2 -> v3: Update code to v6.8-rc3,and make modifications based on it
V1 -> V2: align the space
---
sound/usb/mixer.c | 7 +++++++
sound/usb/quirks.c | 2 ++
2 files changed, 9 insertions(+)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 409fc1164694..d1bdb0b93bda 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1211,6 +1211,13 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
cval->res = 16;
}
break;
+ case USB_ID(0x1bcf, 0x2281): /* HD Webcam */
+ if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ usb_audio_info(chip,
+ "set resolution quirk: cval->res = 16\n");
+ cval->res = 16;
+ }
+ break;
}
}
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 09712e61c606..f222532d4d43 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2185,6 +2185,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_ALIGN_TRANSFER),
DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
QUIRK_FLAG_ALIGN_TRANSFER),
+ DEVICE_FLG(0x1bcf, 0x2281, /* HD Webcam */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
/* Vendor matches */
VENDOR_FLG(0x045e, /* MS Lifecam */
--
2.25.1
1
0
From: wangdicheng <wangdicheng(a)kylinos.cn>
I own an external usb Webcam, HD webcam, which had low mic volume and
inconsistent sound quality. Video works as expected.
(snip)
[ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
[ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
[ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
[ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
[ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
[ 96.092610][ 2] [ T3680] usb 5-2.2: Warning! Unlikely big volume range (=4096), cval->res is probably wrong.
[ 96.102436][ 2] [ T3680] usb 5-2.2: [5] FU [Mic Capture Volume] ch = 1, val = 0/4096/1
Set up quirk cval->res to 16 for 256 levels,
Set GET_SAMPLE_RATE quirk flag to stop trying to get the sample rate.
Confirmed that happened anyway later due to the backoff mechanism,
After 3 failures.
All audio stream on device interfaces share the same values,
apart from wMaxPacketSize and tSamFreq :
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 4
bNumEndpoints 1
bInterfaceClass 1 Audio
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 4
bNumEndpoints 1
bInterfaceClass 1 Audio
bInterfaceSubClass 2 Streaming
bInterfaceProtocol 0
iInterface 0
AudioStreaming Interface Descriptor:
bLength 7
bDescriptorType 36
bDescriptorSubtype 1 (AS_GENERAL)
bTerminalLink 3
bDelay 1 frames
wFormatTag 0x0001 PCM
AudioStreaming Interface Descriptor:
bLength 11
bDescriptorType 36
bDescriptorSubtype 2 (FORMAT_TYPE)
bFormatType 1 (FORMAT_TYPE_I)
bNrChannels 1
bSubframeSize 2
bBitResolution 16
bSamFreqType 1 Discrete
tSamFreq[ 0] 48000
Endpoint Descriptor:
bLength 9
bDescriptorType 5
bEndpointAddress 0x86 EP 6 IN
bmAttributes 5
Transfer Type Isochronous
Synch Type Asynchronous
Usage Type Data
wMaxPacketSize 0x0064 1x 100 bytes
bInterval 4
bRefresh 0
bSynchAddress 0
AudioStreaming Endpoint Descriptor:
bLength 7
bDescriptorType 37
bDescriptorSubtype 1 (EP_GENERAL)
bmAttributes 0x01
Sampling Frequency
bLockDelayUnits 0 Undefined
wLockDelay 0x0000
(snip)
Testing patch provides consistent good sound recording quality and volume range.
(snip)
[ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
[ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
[ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
[ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
[ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
[ 96.110630][ 3] [ T3680] usbcore: registered new interface driver snd-usb-audio
[ 96.114329][ 7] [ T3677] usb 5-2.2: Found UVC 1.00 device HD webcam (1bcf:2281)
[ 96.167555][ 7] [ T3677] usbcore: registered new interface driver uvcvideo
Signed-off-by: wangdicheng <wangdicheng(a)kylinos.cn>
---
V1 -> V2: align the space
---
sound/usb/mixer.c | 7 +++++++
sound/usb/quirks.c | 2 ++
2 files changed, 9 insertions(+)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 409fc1164694..d1bdb0b93bda 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1211,6 +1211,13 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
cval->res = 16;
}
break;
+ case USB_ID(0x1bcf, 0x2281): /* HD Webcam */
+ if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ usb_audio_info(chip,
+ "set resolution quirk: cval->res = 16\n");
+ cval->res = 16;
+ }
+ break;
}
}
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 4e64842245e1..7cd13e4fda4f 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2179,6 +2179,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_FIXED_RATE),
DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x1bcf, 0x2281, /* HD Webcam */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
/* Vendor matches */
VENDOR_FLG(0x045e, /* MS Lifecam */
--
2.25.1
1
0
From: wangdicheng <wangdicheng(a)kylinos.cn>
I own an external usb Webcam, HD webcam, which had low mic volume and
inconsistent sound quality. Video works as expected.
(snip)
[ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
[ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
[ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
[ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
[ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
[ 96.092610][ 2] [ T3680] usb 5-2.2: Warning! Unlikely big volume range (=4096), cval->res is probably wrong.
[ 96.102436][ 2] [ T3680] usb 5-2.2: [5] FU [Mic Capture Volume] ch = 1, val = 0/4096/1
Set up quirk cval->res to 16 for 256 levels,
Set GET_SAMPLE_RATE quirk flag to stop trying to get the sample rate.
Confirmed that happened anyway later due to the backoff mechanism,
After 3 failures.
All audio stream on device interfaces share the same values,
apart from wMaxPacketSize and tSamFreq :
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 4
bNumEndpoints 1
bInterfaceClass 1 Audio
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 4
bNumEndpoints 1
bInterfaceClass 1 Audio
bInterfaceSubClass 2 Streaming
bInterfaceProtocol 0
iInterface 0
AudioStreaming Interface Descriptor:
bLength 7
bDescriptorType 36
bDescriptorSubtype 1 (AS_GENERAL)
bTerminalLink 3
bDelay 1 frames
wFormatTag 0x0001 PCM
AudioStreaming Interface Descriptor:
bLength 11
bDescriptorType 36
bDescriptorSubtype 2 (FORMAT_TYPE)
bFormatType 1 (FORMAT_TYPE_I)
bNrChannels 1
bSubframeSize 2
bBitResolution 16
bSamFreqType 1 Discrete
tSamFreq[ 0] 48000
Endpoint Descriptor:
bLength 9
bDescriptorType 5
bEndpointAddress 0x86 EP 6 IN
bmAttributes 5
Transfer Type Isochronous
Synch Type Asynchronous
Usage Type Data
wMaxPacketSize 0x0064 1x 100 bytes
bInterval 4
bRefresh 0
bSynchAddress 0
AudioStreaming Endpoint Descriptor:
bLength 7
bDescriptorType 37
bDescriptorSubtype 1 (EP_GENERAL)
bmAttributes 0x01
Sampling Frequency
bLockDelayUnits 0 Undefined
wLockDelay 0x0000
(snip)
Testing patch provides consistent good sound recording quality and volume range.
(snip)
[ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
[ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
[ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
[ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
[ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
[ 96.110630][ 3] [ T3680] usbcore: registered new interface driver snd-usb-audio
[ 96.114329][ 7] [ T3677] usb 5-2.2: Found UVC 1.00 device HD webcam (1bcf:2281)
[ 96.167555][ 7] [ T3677] usbcore: registered new interface driver uvcvideo
Signed-off-by: wangdicheng <wangdicheng(a)kylinos.cn>
---
V1 -> V2: align the space
---
sound/usb/mixer.c | 7 +++++++
sound/usb/quirks.c | 2 ++
2 files changed, 9 insertions(+)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 409fc1164694..d1bdb0b93bda 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1211,6 +1211,13 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
cval->res = 16;
}
break;
+ case USB_ID(0x1bcf, 0x2281): /* HD Webcam */
+ if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ usb_audio_info(chip,
+ "set resolution quirk: cval->res = 16\n");
+ cval->res = 16;
+ }
+ break;
}
}
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 4e64842245e1..7cd13e4fda4f 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2179,6 +2179,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_FIXED_RATE),
DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x1bcf, 0x2281, /* HD Webcam */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
/* Vendor matches */
VENDOR_FLG(0x045e, /* MS Lifecam */
--
2.25.1
1
0

[PATCH] ALSA: pcm_dmaengine: Don't synchronize DMA channel when DMA is paused
by Shengjiu Wang 17 Jul '24
by Shengjiu Wang 17 Jul '24
17 Jul '24
When suspended, the DMA channel may enter PAUSE state if dmaengine_pause()
is supported by DMA.
At this state, dmaengine_synchronize() should not be called, otherwise
the DMA channel can't be resumed successfully.
Fixes: e8343410ddf0 ("ALSA: dmaengine: Synchronize dma channel after drop()")
Signed-off-by: Shengjiu Wang <shengjiu.wang(a)nxp.com>
---
sound/core/pcm_dmaengine.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index b54336d4bffc..b134a51b3fd5 100644
--- a/sound/core/pcm_dmaengine.c
+++ b/sound/core/pcm_dmaengine.c
@@ -352,8 +352,12 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
int snd_dmaengine_pcm_sync_stop(struct snd_pcm_substream *substream)
{
struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct dma_tx_state state;
+ enum dma_status status;
- dmaengine_synchronize(prtd->dma_chan);
+ status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+ if (status != DMA_PAUSED)
+ dmaengine_synchronize(prtd->dma_chan);
return 0;
}
--
2.34.1
2
1

17 Jul '24
On Wed, 17 Jul 2024 09:51:49 +0200,
wangdicheng wrote:
>
> From: wangdicheng <wangdicheng(a)kylinos.cn>
>
> I own an external usb Webcam, HD webcam, which had low mic volume and
> inconsistent sound quality. Video works as expected.
>
> (snip)
> [ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
> [ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
> [ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
> [ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
> [ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
> [ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
> [ 96.092610][ 2] [ T3680] usb 5-2.2: Warning! Unlikely big volume range (=4096), cval->res is probably wrong.
> [ 96.102436][ 2] [ T3680] usb 5-2.2: [5] FU [Mic Capture Volume] ch = 1, val = 0/4096/1
>
> Set up quirk cval->res to 16 for 256 levels,
> Set GET_SAMPLE_RATE quirk flag to stop trying to get the sample rate.
> Confirmed that happened anyway later due to the backoff mechanism,
> After 3 failures.
>
> All audio stream on device interfaces share the same values,
> apart from wMaxPacketSize and tSamFreq :
>
> bLength 9
> bDescriptorType 4
> bInterfaceNumber 3
> bAlternateSetting 4
> bNumEndpoints 1
> bInterfaceClass 1 Audio
>
> Interface Descriptor:
> bLength 9
> bDescriptorType 4
> bInterfaceNumber 3
> bAlternateSetting 4
> bNumEndpoints 1
> bInterfaceClass 1 Audio
> bInterfaceSubClass 2 Streaming
> bInterfaceProtocol 0
> iInterface 0
> AudioStreaming Interface Descriptor:
> bLength 7
> bDescriptorType 36
> bDescriptorSubtype 1 (AS_GENERAL)
> bTerminalLink 3
> bDelay 1 frames
> wFormatTag 0x0001 PCM
> AudioStreaming Interface Descriptor:
> bLength 11
> bDescriptorType 36
> bDescriptorSubtype 2 (FORMAT_TYPE)
> bFormatType 1 (FORMAT_TYPE_I)
> bNrChannels 1
> bSubframeSize 2
> bBitResolution 16
> bSamFreqType 1 Discrete
> tSamFreq[ 0] 48000
> Endpoint Descriptor:
> bLength 9
> bDescriptorType 5
> bEndpointAddress 0x86 EP 6 IN
> bmAttributes 5
> Transfer Type Isochronous
> Synch Type Asynchronous
> Usage Type Data
> wMaxPacketSize 0x0064 1x 100 bytes
> bInterval 4
> bRefresh 0
> bSynchAddress 0
> AudioStreaming Endpoint Descriptor:
> bLength 7
> bDescriptorType 37
> bDescriptorSubtype 1 (EP_GENERAL)
> bmAttributes 0x01
> Sampling Frequency
> bLockDelayUnits 0 Undefined
> wLockDelay 0x0000
> (snip)
>
> Testing patch provides consistent good sound recording quality and volume range.
>
> (snip)
> [ 95.473820][ 1] [ T73] usb 5-2.2: new high-speed USB device number 7 using xhci_hcd
> [ 95.773974][ 1] [ T73] usb 5-2.2: New USB device found, idVendor=1bcf, idProduct=2281, bcdDevice= 0.05
> [ 95.783445][ 1] [ T73] usb 5-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
> [ 95.791872][ 1] [ T73] usb 5-2.2: Product: HD webcam
> [ 95.797001][ 1] [ T73] usb 5-2.2: Manufacturer: Sunplus IT Co
> [ 95.802996][ 1] [ T73] usb 5-2.2: SerialNumber: 20200513
> [ 96.110630][ 3] [ T3680] usbcore: registered new interface driver snd-usb-audio
> [ 96.114329][ 7] [ T3677] usb 5-2.2: Found UVC 1.00 device HD webcam (1bcf:2281)
> [ 96.167555][ 7] [ T3677] usbcore: registered new interface driver uvcvideo
>
> Signed-off-by: wangdicheng <wangdicheng(a)kylinos.cn>
The patch can't be applied cleanly to the latest tree, as the table
has been sorted. Could you rebase and resubmit? Also a nitpicking:
> --- a/sound/usb/quirks.c
> +++ b/sound/usb/quirks.c
> @@ -2179,6 +2179,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
> QUIRK_FLAG_FIXED_RATE),
> DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */
> QUIRK_FLAG_GET_SAMPLE_RATE),
> + DEVICE_FLG(0x1bcf, 0x2281, /* FHD Webcam */
> + QUIRK_FLAG_GET_SAMPLE_RATE),
Please try to align the space.
thanks,
Takashi
1
0
From: Curtis Malainey <cujomalainey(a)chromium.org>
Commit 8efcd4864652 ("ASoC: Intel: sof_rt5682: use common module for
sof_card_private initialization") migrated the pin assignment in the
context struct up to soc-acpi-intel-ssp-common.c. This uses a lookup
table to see if a device has a amp/codec before assigning the pin. The
issue here arises when combination parts that serve both (with 2 ports)
are used.
sysfs: cannot create duplicate filename '/devices/pci0000:00/0000:00:1f.3/adl_rt5682_def/SSP0-Codec'
CPU: 1 PID: 2079 Comm: udevd Tainted: G U 6.6.36-03391-g744739e00023 #1 3be1a2880a0970f65545a957db7d08ef4b3e2c0d
Hardware name: Google Anraggar/Anraggar, BIOS Google_Anraggar.15217.552.0 05/07/2024
Call Trace:
<TASK>
dump_stack_lvl+0x69/0xa0
sysfs_warn_dup+0x5b/0x70
sysfs_create_dir_ns+0xb0/0x100
kobject_add_internal+0x133/0x3c0
kobject_add+0x66/0xb0
? device_add+0x65/0x780
device_add+0x164/0x780
snd_soc_add_pcm_runtimes+0x2fa/0x800
snd_soc_bind_card+0x35e/0xc20
devm_snd_soc_register_card+0x48/0x90
platform_probe+0x7b/0xb0
really_probe+0xf7/0x2a0
...
kobject: kobject_add_internal failed for SSP0-Codec with -EEXIST, don't try to register things with the same name in the same directory.
The issue is that the ALC5650 was only defined in the codec table and
not the amp table which left the pin unassigned but the dai link was
still created by the machine driver.
Also patch the suffix filename code for the topology to prevent double
suffix names as a result of this change.
Fixes: 8efcd4864652 ("ASoC: Intel: sof_rt5682: use common module for sof_card_private initialization")
Reviewed-by: Bard Liao <yung-chuan.liao(a)linux.intel.com>
Signed-off-by: Curtis Malainey <cujomalainey(a)chromium.org>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart(a)linux.intel.com>
---
.../intel/common/soc-acpi-intel-ssp-common.c | 9 ++++
sound/soc/sof/intel/hda.c | 49 ++++++++++---------
2 files changed, 34 insertions(+), 24 deletions(-)
diff --git a/sound/soc/intel/common/soc-acpi-intel-ssp-common.c b/sound/soc/intel/common/soc-acpi-intel-ssp-common.c
index 75d0b931d895..de7a3f7f47f1 100644
--- a/sound/soc/intel/common/soc-acpi-intel-ssp-common.c
+++ b/sound/soc/intel/common/soc-acpi-intel-ssp-common.c
@@ -64,6 +64,15 @@ static const struct codec_map amps[] = {
CODEC_MAP_ENTRY("RT1015P", "rt1015", RT1015P_ACPI_HID, CODEC_RT1015P),
CODEC_MAP_ENTRY("RT1019P", "rt1019", RT1019P_ACPI_HID, CODEC_RT1019P),
CODEC_MAP_ENTRY("RT1308", "rt1308", RT1308_ACPI_HID, CODEC_RT1308),
+
+ /*
+ * Monolithic components
+ *
+ * Only put components that can serve as both the amp and the codec below this line.
+ * This will ensure that if the part is used just as a codec and there is an amp as well
+ * then the amp will be selected properly.
+ */
+ CODEC_MAP_ENTRY("RT5650", "rt5650", RT5650_ACPI_HID, CODEC_RT5650),
};
enum snd_soc_acpi_intel_codec
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index daf364f773dd..5a40b8fbbbd3 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -1307,9 +1307,10 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
const struct sof_dev_desc *desc = sof_pdata->desc;
struct hdac_bus *bus = sof_to_bus(sdev);
struct snd_soc_acpi_mach *mach = NULL;
- enum snd_soc_acpi_intel_codec codec_type;
+ enum snd_soc_acpi_intel_codec codec_type, amp_type;
const char *tplg_filename;
const char *tplg_suffix;
+ bool amp_name_valid;
/* Try I2S or DMIC if it is supported */
if (interface_mask & (BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC)))
@@ -1413,30 +1414,30 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
}
}
- codec_type = snd_soc_acpi_intel_detect_amp_type(sdev->dev);
-
- if (tplg_fixup &&
- mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_AMP_NAME &&
- codec_type != CODEC_NONE) {
- tplg_suffix = snd_soc_acpi_intel_get_amp_tplg_suffix(codec_type);
- if (!tplg_suffix) {
- dev_err(sdev->dev, "no tplg suffix found, amp %d\n",
- codec_type);
- return NULL;
- }
-
- tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
- "%s-%s",
- sof_pdata->tplg_filename,
- tplg_suffix);
- if (!tplg_filename)
- return NULL;
-
- sof_pdata->tplg_filename = tplg_filename;
- add_extension = true;
- }
-
+ amp_type = snd_soc_acpi_intel_detect_amp_type(sdev->dev);
codec_type = snd_soc_acpi_intel_detect_codec_type(sdev->dev);
+ amp_name_valid = amp_type != CODEC_NONE && amp_type != codec_type;
+
+ if (tplg_fixup && amp_name_valid &&
+ mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_AMP_NAME) {
+ tplg_suffix = snd_soc_acpi_intel_get_amp_tplg_suffix(amp_type);
+ if (!tplg_suffix) {
+ dev_err(sdev->dev, "no tplg suffix found, amp %d\n",
+ amp_type);
+ return NULL;
+ }
+
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s-%s",
+ sof_pdata->tplg_filename,
+ tplg_suffix);
+ if (!tplg_filename)
+ return NULL;
+
+ sof_pdata->tplg_filename = tplg_filename;
+ add_extension = true;
+ }
+
if (tplg_fixup &&
mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME &&
--
2.43.0
2
1

[PATCH] ASOC: SOF: Intel: hda-loader: only wait for HDaudio IOC for IPC4 devices
by Pierre-Louis Bossart 16 Jul '24
by Pierre-Louis Bossart 16 Jul '24
16 Jul '24
Multiple users report a regression bisected to commit d5263dbbd8af
("ASoC: SOF: Intel: don't ignore IOC interrupts for non-audio
transfers"). The firmware version is the likely suspect, as these
users relied on SOF 2.0 while Intel only tested with the 2.2 release.
Rather than completely disable the wait_for_completion(), which can
help us gather timing information on the different stages of the boot
process, the simplest course of action is to just disable it for older
IPC versions which are no longer under active development.
Closes: https://github.com/thesofproject/linux/issues/5072
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218961
Fixes: d5263dbbd8af ("ASoC: SOF: Intel: don't ignore IOC interrupts for non-audio transfers")
Tested-by: Mike Krinkin <krinkin.m.u(a)gmail.com>
Tested-by: Todd Brandt <todd.e.brandt(a)linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart(a)linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao(a)linux.intel.com>
---
sound/soc/sof/intel/hda-loader.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index b8b914eaf7e0..75f6240cf3e1 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -310,15 +310,19 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
return ret;
}
- /* Wait for completion of transfer */
- time_left = wait_for_completion_timeout(&hda_stream->ioc,
- msecs_to_jiffies(HDA_CL_DMA_IOC_TIMEOUT_MS));
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ /* Wait for completion of transfer */
+ time_left = wait_for_completion_timeout(&hda_stream->ioc,
+ msecs_to_jiffies(HDA_CL_DMA_IOC_TIMEOUT_MS));
- if (!time_left) {
- dev_err(sdev->dev, "Code loader DMA did not complete\n");
- return -ETIMEDOUT;
+ if (!time_left) {
+ dev_err(sdev->dev, "Code loader DMA did not complete\n");
+ return -ETIMEDOUT;
+ }
+ dev_dbg(sdev->dev, "Code loader DMA done\n");
}
- dev_dbg(sdev->dev, "Code loader DMA done, waiting for FW_ENTERED status\n");
+
+ dev_dbg(sdev->dev, "waiting for FW_ENTERED status\n");
status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
chip->rom_status_reg, reg,
--
2.43.0
2
1
Add TAS2563 into the Header in case of misunderstanding.
Signed-off-by: Shenghao Ding <shenghao-ding(a)ti.com>
---
v1:
- Add TAS2563 into the Header
- Add channel no into the log for error debug
---
sound/soc/codecs/tas2781-comlib.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c
index 1fbf4560f5cc..58abbc098a91 100644
--- a/sound/soc/codecs/tas2781-comlib.c
+++ b/sound/soc/codecs/tas2781-comlib.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
//
-// TAS2781 Common functions for HDA and ASoC Audio drivers
+// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers
//
// Copyright 2023 - 2024 Texas Instruments, Inc.
//
@@ -64,8 +64,8 @@ static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv,
*/
ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
if (ret < 0) {
- dev_err(tas_priv->dev, "%s, E=%d\n",
- __func__, ret);
+ dev_err(tas_priv->dev, "%s, E=%d channel:%d\n",
+ __func__, ret, chn);
goto out;
}
}
--
2.34.1
2
3
The following changes since commit 680e126ec0400f6daecf0510c5bb97a55779ff03:
firmware: cs_dsp: Use strnlen() on name fields in V1 wmfw files (2024-07-08 15:55:11 +0100)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git tags/asoc-v6.11
for you to fetch changes up to c51cba4755609ad97ba97713210c16f043c73224:
Fix the unbalanced pm_runtime_enable in wcd937x-sdw (2024-07-12 17:38:18 +0100)
----------------------------------------------------------------
ASoC: Updates for for v6.11
There are a lot of changes in here, though the big bulk of things is
cleanups and simplifications of various kinds which are internally
rather than externally visible. A good chunk of those are DT schema
conversions, but there's also a lot of changes in the code.
Highlights:
- Syncing of features between simple-audio-card and the two
audio-graph cards so there is no reason to stick with an older
driver.
- Support for specifying the order of operations for components within
cards to allow quirking for unusual systems.
- New support for Asahi Kasei AK4619, Cirrus Logic CS530x, Everest
Semiconductors ES8311, NXP i.MX95 and LPC32xx, Qualcomm LPASS v2.5
and WCD937x, Realtek RT1318 and RT1320 and Texas Instruments PCM5242.
----------------------------------------------------------------
Abdulrasaq Lawani (1):
dt-bindings: sound: Convert max98088 to dtschema
Aleksandr Mishin (2):
ASoC: qcom: Adjust issues in case of DT error in asoc_qcom_lpass_cpu_platform_probe()
ASoC: amd: Adjust error handling in case of absent codec device
Amadeusz Sławiński (13):
ASoC: topology: Simplify code
ASoC: topology: Do not do unnecessary dobj management
ASoC: topology: Properly initialize soc_enum values
ASoC: topology: Cleanup soc_tplg_dapm_widget_dbytes_create
ASoC: topology: Cleanup soc_tplg_dapm_widget_dmixer_create
ASoC: topology: Cleanup soc_tplg_dapm_widget_denum_create
ASoC: topology: Correctly set shift_r in soc_tplg_denum_create()
ASoC: topology: Align dynamic object initialization for controls
ASoC: topology: Rename function creating widget kcontrol
ASoC: topology: Reshuffle function placement
ASoC: topology: Unify code for creating standalone and widget bytes control
ASoC: topology: Unify code for creating standalone and widget mixer control
ASoC: topology: Unify code for creating standalone and widget enum control
Andrejs Cainikovs (3):
ASoC: nau8822: move nau8822_set_dai_sysclk()
ASoC: nau8822: set NAU8822_REFIMP_80K only once
ASoC: nau8822: add MCLK support
Andy Shevchenko (6):
ASoC: codecs: Remove unused of_gpio.h
ASoC: fsl: Remove unused of_gpio.h
ASoC: rockchip: Remove unused of_gpio.h
ASoC: codecs: Replace of_gpio.h by proper one
ASoC: generic: Replace of_gpio.h by proper one
ASoC: samsung: Replace of_gpio.h by proper one
Animesh Agarwal (11):
ASoC: dt-bindings: ak4554: Convert to dtschema
ASoC: dt-bindings: linux,spdif: Convert spdif-reciever.txt to dtschema
ASoC: dt-bindings: wlf,wm8782: Convert to dtschema
ASoC: dt-bindings: wlf,wm8804: Convert to dtschema
ASoC: dt-bindings: realtek,rt5631: Convert to dtschema
ASoC: dt-bindings: realtek,rt5514: Convert to dtschema
ASoC: dt-bindings: realtek,rt5659: Convert to dtschema
ASoC: dt-bindings: realtek,rt5677: Convert to dtschema
ASoC: dt-bindings: realtek,rt5645: Convert to dtschema
ASoC: dt-bindings: cirrus,cs4270: Convert to dtschema
ASoC: dt-bindings: cirrus,cs42xx8: Convert to dtschema
Artur Weber (6):
ASoC: dt-bindings: samsung,midas-audio: Add headset mic bias supply
ASoC: dt-bindings: samsung,midas-audio: Add GPIO-based headset jack detection
ASoC: samsung: midas_wm1811: Use SND_SOC_DAPM_REGULATOR_SUPPLY for bias regulators
ASoC: samsung: midas_wm1811: Add headset mic bias supply support
ASoC: samsung: midas_wm1811: Add GPIO-based headset jack detection
ASoC: samsung: midas_wm1811: Use dev_err_probe where appropriate
Bard Liao (8):
ASoC: Intel: sof_sdw_cs42l42: use dai parameter
ASoC: Intel: sof_sdw_rt711: use dai parameter
ASoC: Intel: sof_sdw_rt5682: use dai parameter
ASoC: Intel: sof_sdw_rt700: use dai parameter
ASoC: Intel: sof_sdw_rt_dmic: use from dai parameter
ASoC: Intel: sof_sdw_rt_sdca_jack_common: use dai parameter
ASoC: Intel: sof_sdw: remove get_codec_dai_by_name
ASoC: Intel: sof_sdw: select PINCTRL_CS42L43 and SPI_CS42L43
Brent Lu (8):
ASoC: SOF: sof-audio: rename dai clock setting query function
ASoC: SOF: sof-audio: add sof_dai_get_tdm_slots function
ASoC: SOF: ipc3-topology: support tdm slot number query
ASoC: SOF: ipc4-topology: support tdm slot number query
ASoC: Intel: maxim-common: rewrite max_98373_hw_params function
ASoC: Intel: sof_da7219: remove local max98373 ops
ASoC: Intel: sof_da7219: disable max98373 speaker pins in late_probe
ASoC: Intel: maxim-common: add max_98373_get_tx_mask function
Chancel Liu (3):
ASoC: dt-bindings: fsl_rpmsg: Add compatible string for i.MX95
ASoC: fsl_rpmsg: Add support for i.MX95 platform
ASoC: fsl_xcvr: Improve suspend/resume flow in fsl_xcvr_trigger()
Charles Keepax (3):
ASoC: Intel: sof_sdw: Add missing controls for cs42l43/cs35l56
ASoC: Intel: soc-acpi: Add match entries for some cs42l43 laptops
ASoC: Intel: sof_sdw: Add quirks for some new Dell laptops
Chen Ni (2):
ASoC: max98088: Check for clk_prepare_enable() error
ASoC: Intel: sof_sdw: Convert comma to semicolon
Christian Hewitt (2):
ASoC: Add support for ti,pcm5242 to the pcm512x driver
ASoC: dt-bindings: add ti,pcm5242 to pcm512x
Christophe JAILLET (8):
ASoC: topology: Constify an argument of snd_soc_tplg_component_load()
ASoC: Intel: avs: Constify struct snd_soc_tplg_ops
ASoC: qdsp6: audioreach: Constify struct snd_soc_tplg_ops
ASoC: Intel: Skylake: Constify struct snd_soc_tplg_ops
ASoC: SOF: topology: Constify struct snd_soc_tplg_ops
const_structs.checkpatch: add snd_soc_tplg_ops
ASoC: intel: Constify struct snd_soc_ops
ASoC: SOF: mediatek: Constify struct mtk_adsp_ipc_ops
Dan Carpenter (1):
ASoC: samsung: midas_wm1811: Fix error code in probe()
Dr. David Alan Gilbert (3):
ASoC: codecs: lpass-rx-macro: remove unused struct 'rx_macro_reg_mask_val'
ASoC: codecs: wm0010: remove unused struct 'wm0010_spi_msg'
ASoC: codecs: cx2072x: remove unused struct 'cx2072x_eq_ctrl'
Elinor Montmasson (5):
ASoC: fsl-asoc-card: add support for dai links with multiple codecs
ASoC: fsl-asoc-card: add second dai link component for codecs
ASoC: fsl-asoc-card: add compatibility to use 2 codecs in dai-links
ASoC: fsl-asoc-card: merge spdif support from imx-spdif.c
ASoC: dt-bindings: update fsl-asoc-card bindings after imx-spdif merge
Fabio Estevam (3):
ASoC: fsl: fsl_aud2htx: Switch to RUNTIME_PM_OPS()
ASoC: fsl: fsl_easrc: Switch to RUNTIME_PM_OPS()
ASoC: fsl: fsl_xcvr: Switch to RUNTIME_PM_OPS()
Hao Ge (1):
ASoc: PCM6240: Return directly after a failed devm_kzalloc() in pcmdevice_i2c_probe()
Herve Codina (10):
ASoC: fsl: fsl_qmc_audio: Check devm_kasprintf() returned value
ASoC: fsl: fsl_qmc_audio: Fix issues detected by checkpatch
ASoC: fsl: fsl_qmc_audio: Split channel buffer and PCM pointer handling
ASoC: fsl: fsl_qmc_audio: Identify the QMC channel involved in completion routines
ASoC: fsl: fsl_qmc_audio: Introduce qmc_audio_pcm_{read,write}_submit()
ASoC: fsl: fsl_qmc_audio: Introduce qmc_dai_constraints_interleaved()
soc: fsl: cpm1: qmc: Introduce functions to get a channel from a phandle list
soc: fsl: cpm1: qmc: Introduce qmc_chan_count_phandles()
dt-bindings: sound: fsl,qmc-audio: Add support for multiple QMC channels per DAI
ASoC: fsl: fsl_qmc_audio: Add support for non-interleaved mode.
Jack Yu (1):
ASoC: rt1318: Add RT1318 audio amplifier driver
Javier Carrasco (9):
ASoC: cs35l34: Constify struct regmap_config
ASoC: cs35l35: Constify struct regmap_config
ASoC: cs35l36: Constify struct regmap_config
ASoC: cs53l30: Constify struct regmap_config
ASoC: jz4760: Constify struct regmap_config
ASoC: jz4770: Constify struct regmap_config
ASoC: wsa881x: Constify struct regmap_config
ASoC: wsa883x: Constify struct regmap_config
ASoC: wsa884x: Constify struct regmap_config
Jeff Johnson (2):
ASoC: amd: add missing MODULE_DESCRIPTION() macros
ASoC: fsl: imx-pcm-fiq: add missing MODULE_DESCRIPTION() macro
Jerome Brunet (2):
ASoC: soc-utils: allow sample rate up to 768kHz for the dummy dai
ASoC: meson: tdm: add sample rate support up to 768kHz
Jiaxin Yu (1):
ASoC: mediatek: mt6358: Add "Dmic Mode Switch" kcontrol for switch DMIC mode.
Khanh Le (1):
ASoC: Add ak4619 codec support
Krzysztof Kozlowski (70):
ASoC: Constify channel mapping array arguments in set_channel_map()
ASoC: qcom: q6apm-lpass-dais: Implement proper channel mapping
ASoC: qcom: qdsp6: Set channel mapping instead of fixed defaults
ASoC: qcom: x1e80100: Correct channel mapping
ASoC: codecs: wcd938x: Drop unused duplicated MIC2 bias register defines
ASoC: codecs: wcd938x: Unify define used for MIC2 bias register
ASoC: codecs: wcd939x: Unify define used for MIC bias registers
ASoC: codecs: wcd939x: Minor white-space and define cleanup
ASoC: codecs: wcd939x: Unify define used for MIC bias VOUT registers
ASoC: codecs: wcd938x: Drop no-op ADC2_BCS Disable Switch
ASoC: soc-dai.h: Constify DAI ops auto_selectable_formats
ASoC: Constify DAI ops auto_selectable_formats
ASoC: Constify of_phandle_args in snd_soc_dai_driver
ASoC: Constify of_phandle_args in snd_soc_dai_link_component
ASoC: Constify passed data to core function
ASoC: Constify DAI passed to get_channel_map
ASoC: Constify return of snd_soc_dai_get_pcm_stream()
ASoC: qcom: x1e80100: Add USB DisplayPort plug support
ASoC: codecs: wcd-mbhc: Constify passed MBHC reg fields
ASoC: codecs: wcd9335: Drop unused state container fields
ASoC: codecs: wcd9335: Constify static data
ASoC: codecs: wcd9335: Handle nicer probe deferral and simplify with dev_err_probe()
ASoC: codecs: wcd9335: Drop unneeded error message
ASoC: codecs: wcd9335: Drop unused dmic rate handling
ASoC: codecs: wcd934x: Drop unused interp path enum
ASoC: codecs: wcd934x: Constify static data
ASoC: codecs: wcd934x: Drop unused mic bias voltage fields
ASoC: codecs: wcd934x: Handle nicer probe deferral and simplify with dev_err_probe()
ASoC: codecs: wcd937x: Constify static data
ASoC: codecs: wcd937x: Constify wcd937x_sdw_ch_info
ASoC: codecs: wcd937x: Drop unused enums, defines and types
ASoC: codecs: wcd937x: Drop unused state container fields
ASoC: codecs: wcd937x: Drop unused chipid member
ASoC: codecs: wcd938x: Constify static data
ASoC: codecs: wcd938x: Constify wcd938x_sdw_ch_info
ASoC: codecs: wcd938x: Drop unused RX/TX direction enum
ASoC: codecs: wcd938x: Drop unused num_ports field
ASoC: codecs: wcd939x: Constify static data
ASoC: codecs: wcd939x: Constify wcd939x_sdw_ch_info
ASoC: codecs: wcd939x: Drop unused RX/TX direction enum
ASoC: codecs: wcd939x: Drop unused num_ports field
ASoC: codecs: lpass-rx-macro: add missing handling of v2.1 codec
ASoC: codecs: lpass-wsa-macro: Drop unused define
ASoC: codecs: lpass-wsa-macro: Prepare to accommodate new codec versions
ASoC: codecs: lpass-wsa-macro: Correct support for newer v2.5 version
ASoC: codecs: lpass-macro: Gracefully handle unknown version
ASoC: codecs: lpass-macro: Use enum for handling codec version
ASoC: codecs: lpass-wsa-macro: add missing select of common code
ASoC: codecs: lpass-rx-macro: Fix using NULL pointer in probe() dev_err
ASoC: codecs: lpass-wsa-macro: Fix using NULL pointer in probe() dev_err
ASoC: codecs: lpass-rx-macro: Simplify PDS cleanup with devm
ASoC: codecs: lpass-rx-macro: Simplify with cleanup.h
ASoC: codecs: lpass-rx-macro: Keep static regmap_config as const
ASoC: dapm: Use unsigned for number of widgets in snd_soc_dapm_new_controls()
ASoC: codecs: lpass-rx-macro: Use unsigned for number of widgets
ASoC: codecs: lpass-wsa-macro: Simplify with cleanup.h
ASoC: codecs: wcd939x: Fix typec mux and switch leak during device removal
ASoC: codecs: audio-iio-aux: Simplify audio_iio_aux_add_dapms() with cleanup.h
ASoC: codecs: audio-iio-aux: Simplify audio_iio_aux_probe() with cleanup.h
ASoC: codecs: wcd9335: Simplify with cleanup.h
ASoC: codecs: wcd934x: Simplify with cleanup.h
ASoC: simple-card-utils: Simplify with cleanup.h
ASoC: audio-graph-card: Use cleanup.h instead of devm_kfree()
ASoC: audio-graph-card2: Use cleanup.h instead of devm_kfree()
ASoC: simple-card: Use cleanup.h instead of devm_kfree()
ASoC: ops: Simplify with cleanup.h
ASoC: dapm: Simplify dapm_cnew_widget() with cleanup.h
ASoC: dapm: Simplify snd_soc_dai_link_event_pre_pmu() with cleanup.h
ASoC: codecs: aw88395: Simplify with cleanup.h
ASoC: qcom: topology: Simplify with cleanup.h
Kuninori Morimoto (22):
ASoC: simple-card-utils: remove both playback/capture_only check
ASoC: audio-graph-card2: add ep_to_port() / port_to_ports()
ASoC: audio-graph-card2: remove ports node name check
ASoC: audio-graph-card2: expand dai_link property part
ASoC: audio-graph-card2: merge graph_parse_mclk_fs() into graph_link_init()
ASoC: audio-graph-card: add ep_to_port() / port_to_ports()
ASoC: audio-graph-card: remove ports node name check
ASoC: audio-graph-card: enable playback/capture_only property
ASoC: audio-graph-card: merge graph_parse_mclk_fs() into graph_link_init()
ASoC: simple-audio-card: enable playback/capture_only property
ASoC: simple-audio-card: merge simple_parse_mclk_fs() into simple_link_init()
ASoC: audio-graph-card2: add support for aux devices
ASoC: dt-bindings: audio-graph-card2: add support for aux devices
ASoC: dt-bindings: ak4619: Add initial DT binding
ASoC: dt-bindings: add missing vender prefix on filename
ASoC: audio-graph-port: add link-trigger-order
ASoC: simple-card-utils: add link-trigger-order support
ASoC: simple-audio-card: add link-trigger-order support
ASoC: audio-graph-card: add link-trigger-order support
ASoC: audio-graph-card2: add link-trigger-order support
ASoC: simple-audio-mux: enable to select MUX names
ASoC: dt-bindings: simple-audio-mux: add state-labels property
Linus Walleij (1):
ASoC: tas5086: Convert to GPIO descriptors
Lukas Bulwahn (1):
MAINTAINERS: adjust file entries after adding vendor prefix in sound dtbs
Manikantan R (1):
ASoC: dt-bindings: wsa883x: Document port mapping property
Mark Brown (42):
Support Tegra I2S client format conversion
ASoC: Intel: boards: updates for 6.11
ASoC: qcom: x1e80100: Correct channel mapping
ASoC: fsl_mqs: Add i.MX95 platform support
ASoC: codecs: add support for everest-semi es8311
ASoC: samsung: midas-audio: Add GPIO-based headset
ASoC: Merge up fixes
Dead structs in sound/soc/codecs
ASoC: fsl_xcvr: Support i.MX95 platform
ASoC: simple-card: sync support
ASoC: codecs: wcd937x: add wcd937x audio codec
ACPI/ALSA/soundwire: add acpi_get_local_u64_address()
ASoC: Drop or replace of_gpio.h
ASoC: codecs: lpass: add support for v2.5 rx macro
ASoC: Merge up fixes
Cirrus Logic Family of ADCs
tlv320adc3xxx: Allow MICBIAS pins to be used as
ASoC: dt-bindings: convert everest,es7134.txt &
ASoC: codecs: wcd family: cleanups
ASoC: add compatible for ti,pcm5242
ASoC: Add ak4619 codec support
ASoC: Few constifications (mostly arguments)
ASoC: qcom: display port changes
ASoC: nau8822: add MCLK support
ASoC: codecs: ES8326: Solving headphone detection and
ASoC: Intel: boards: updates for 6.11 - part2
ASoC: simple-card / audio-graph:
ASoC: imx-audmix: Split capture device to be a new
Add audio support for LPC32XX CPUs
ASoC: codecs: wsa88xx: add support for static port
ASoC: cs35l56: Remove obsolete and redundant code
ASoC: simple-audio-mux: add state-labels
Add master clock handling for nau8824
ASoC: topology: kcontrol registration cleanup
ASoC: cs35l56: Set correct upper volume limit
ASoC: codecs: lpass-rx-macro: Few code cleanups
Add support for non-interleaved mode in qmc_audio
ASoC: Simplify code with cleanup.h
ASoC: fsl-asoc-card: add S/PDIF controller support
firmware: cs_dsp: Some small coding improvements
ASoC: dt-bindings: convert qcom sound bindings to
Fix the unbalanced pm_runtime_enable in wcd937x-sdw
Matteo Martelli (2):
ASoC: es8311: dt-bindings: add everest es8311 codec
ASoC: codecs: es8311: add everest es8311 codec support
Maxim Kochetkov (2):
ASoC: dt-bindings: nau8824: Add master clock handling
ASoC: codecs: nau8824: Add master clock handling
Mithil Bavishi (1):
ASoC: dt-bindings: omap-mcpdm: Convert to DT schema
Mohammad Rafi Shaik (3):
ASoC: codecs: wcd937x-sdw: Fix Unbalanced pm_runtime_enable
ASoC: codecs: wcd937x: Remove the string compare in MIC BIAS widget settings
ASoC: codecs: wcd937x: Remove separate handling for vdd-buck supply
Mohan Kumar (2):
ASoC: simple-card-utils: Split simple_fixup_sample_fmt func
ASoC: tegra: I2S client convert formats handling
Nathan Chancellor (2):
ASoC: fsl: lpc3xxx-i2s: Avoid using ret uninitialized in lpc32xx_i2s_probe()
ASoC: fsl: lpc3xxx-i2s: Include bitfield.h for FIELD_PREP
Neil Armstrong (5):
ASoC: dt-bindings: convert amlogic,g12a-tohdmitx to dt-schema
ASoC: dt-bindings: convert tas571x.txt to dt-schema
ASoC: dt-bindings: convert everest,es7241.txt to dt-schema
ASoC: dt-bindings: convert everest,es7134.txt to dt-schema
ASoC: dt-bindings: amlogic,gx-sound-card: drop minItems for audio-widgets
Paul Handrigan (4):
ASoC: dt-bindings: cirrus,cs530x: Add initial DT binding
ASoC: cs530x: Support for cs530x ADCs
ASoC: cs530x: Calculate proper bclk rate using TDM
ASoC: cs530x: Remove bclk from private structure
Peter Ujfalusi (4):
ASoC: SOF: ipc4-topology: Add support for NHLT with 16-bit only DMIC blob
ASoC: SOF: Intel: pci-tgl: Align ADL-N sof_dev_desc struct name to convention
ASoC: SOF: ipc4-topology: Use correct queue_id for requesting input pin format
ASoC: SOF: ipc4-topology: Use single token list for the copiers
Pierre-Louis Bossart (6):
ASoC: SOF: Intel: hda: print PCI class info only once
ACPI: utils: introduce acpi_get_local_u64_address()
soundwire: slave: simplify code with acpi_get_local_u64_address()
ALSA: hda: intel-sdw-acpi: use acpi_get_local_u64_address()
ASoC: Intel: sof_sdw: fix jack detection on ADL-N variant RVP
ASoC: Intel: sof_sdw: add quirk for Dell SKU 0B8C
Piotr Wojtaszczyk (2):
ASoC: dt-bindings: lpc32xx: Add lpc32xx i2s DT binding
ASoC: fsl: Add i2s and pcm drivers for LPC32xx CPUs
Prasad Kumpatla (7):
ASoC: dt-bindings: document wcd937x Audio Codec
ASoC: codecs: wcd937x-sdw: add SoundWire driver
ASoC: codecs: wcd937x: add wcd937x codec driver
ASoC: codecs: wcd937x: add basic controls
ASoC: codecs: wcd937x: add playback dapm widgets
ASoC: codecs: wcd937x: add capture dapm widgets
ASoC: codecs: wcd937x: add audio routing and Kconfig
Rayyan Ansari (2):
ASoC: dt-bindings: qcom,msm8916-wcd-digital-codec: convert to dtschema
ASoC: dt-bindings: qcom,apq8096-sndcard: use dtschema
Ricard Wanderlof (3):
ASoC: dt-bindings: tlv320adc3xxx: Fix incorrect GPIO description
ASoC: dt-bindings: tlv320adc3xxx: Add MICBIAS-as-GPO properties
ASoC: tlv320adc3xxx: Add support for using MICBIAS pins as GPO
Richard Fitzgerald (10):
ASoC: cs35l56: Revert support for dual-ownership of ASP registers
ASoC: cs35l56: Remove support for A1 silicon
ASoC: cs35l56: Remove redundant clearing of clk_stop_mode1
firmware: cs_dsp: Don't allow writes to read-only controls
ASoC: cs35l56: Use header defines for Speaker Volume control definition
ASoC: cs35l56: Limit Speaker Volume to +12dB maximum
firmware: cs_dsp: Don't allocate temporary buffer for info text
firmware: cs_dsp: Make wmfw and bin filename arguments const char *
firmware: cs_dsp: Clarify wmfw format version log message
firmware: cs_dsp: Rename fw_ver to wmfw_ver
Rob Herring (Arm) (2):
ASoC: PCM6240: Use of_property_read_reg()
ASoC: tas2781: Use of_property_read_reg()
Shenghao Ding (8):
ASoc: tas2781: Enable RCA-based playback without DSP firmware download
ASoc: tas2781: Add name_prefix as the prefix name of firmwares and kcontrol to support corresponding TAS2563/TAS2781s
ASoc: tas2781: Add name_prefix as the prefix name of DSP firmwares and calibrated data files
ASoc: tas2781: Set "Speaker Force Firmware Load" as the common kcontrol for both tas27871 and tas2563
ASoc: pcm6240: Remove unnecessary name-prefix for all the controls
ASoc: TAS2781: replace beXX_to_cpup with get_unaligned_beXX for potentially broken alignment
ASoc: TAS2781: rename the tas2781_reset as tasdevice_reset
ASoC: tas2781: Add new Kontrol to set tas2563 digital Volume
Shengjiu Wang (9):
ASoC: dt-bindings: fsl,mqs: Add i.MX95 platform support
ASoC: fsl_mqs: Add i.MX95 platform support
ASoC: dt-bindings: fsl,xcvr: Add compatible string for i.MX95
ASoC: fsl_xcvr: Add support for i.MX95 platform
ASoC: fsl_sai: Add separate DAI for transmitter and receiver
ASoC: fsl_audmix: Split playback and capture stream to different DAI
ASoC: imx-audmix: Split capture device for audmix
ASoC: dt-bindings: fsl,xcvr: Adjust the number of interrupts
ASoC: ak4458: remove "reset-gpios" property handler
Shuming Fan (4):
ASoC: rt1320: Add RT1320 SDCA vendor-specific driver
ASoC: rt711-sdca: add GE selected mode control
ASoC: rt712-sdca: change the definition name of SDCA channel number
ASoC: rt712-sdca: add the function for version B
Simon Trimmer (2):
ASoC: cs35l56: Attempt to read from cirrus,speaker-id device property first
ASoC: cs35l56: Accept values greater than 0 as IRQ numbers
Srinivas Kandagatla (9):
ASoC: codecs: lpass-macro: add helpers to get codec version
ASoC: codec: lpass-rx-macro: prepare driver to accomdate new codec versions
ASoC: codec: lpass-rx-macro: add support for 2.5 codec version
ASoC: qcom: q6dsp: parse Display port tokens
ASoC: qcom: common: add Display port Jack function
ASoC: qcom: sc8280xp: add Display port Jack
ASoC: codecs: wsa883x: parse port-mapping information
ASoC: dt-bindings: wsa8840: Document port mapping property
ASoC: codecs: wsa884x: parse port-mapping information
Tony Luck (1):
ASoC: Intel: avs: es8336: Switch to new Intel CPU model defines
Uwe Kleine-König (1):
ASoC: codecs: Drop explicit initialization of struct i2c_device_id::driver_data to 0
Vijendar Mukunda (5):
ASoC: amd: acp: remove acp_i2s_probe function
ASoC: amd: acp: remove unused variables from acp_resource structure
ASoC: amd: acp: modify conditional check for programming i2s mclk
ASoC: amd: acp: move i2s clock generation sequence
ASoC: amd: acp: add pcm constraints for buffer size and period size
Xiaxi Shen (1):
ASoC: dt-bindings: ak4104: convert to dt schema
Yang Li (1):
ASoC: Remove unneeded semicolon
Zhang Yi (3):
ASoC: codecs: ES8326: Slove headphone detection issue
ASoC: codecs: ES8326: Minimize the pop noise
ASoC: codecs: ES8326: regcache_sync error issue
Documentation/admin-guide/LSM/tomoyo.rst | 35 +-
Documentation/admin-guide/mm/transhuge.rst | 4 +-
Documentation/cdrom/cdrom-standard.rst | 4 +-
.../devicetree/bindings/arm/stm32/st,mlahb.yaml | 3 +-
Documentation/devicetree/bindings/arm/sunxi.yaml | 6 +-
.../devicetree/bindings/input/elan,ekth6915.yaml | 19 +-
.../devicetree/bindings/input/ilitek,ili2901.yaml | 66 +
Documentation/devicetree/bindings/sound/ak4104.txt | 25 -
Documentation/devicetree/bindings/sound/ak4554.txt | 11 -
.../bindings/sound/amlogic,g12a-tohdmitx.txt | 58 -
.../bindings/sound/amlogic,g12a-tohdmitx.yaml | 54 +
.../bindings/sound/amlogic,gx-sound-card.yaml | 1 -
.../bindings/sound/asahi-kasei,ak4104.yaml | 49 +
.../sound/{ak4375.yaml => asahi-kasei,ak4375.yaml} | 2 +-
.../bindings/sound/asahi-kasei,ak4554.yaml | 27 +
.../sound/{ak4613.yaml => asahi-kasei,ak4613.yaml} | 2 +-
.../bindings/sound/asahi-kasei,ak4619.yaml | 62 +
.../sound/{ak4642.yaml => asahi-kasei,ak4642.yaml} | 2 +-
.../bindings/sound/audio-graph-card2.yaml | 5 +
.../bindings/sound/audio-graph-port.yaml | 9 +
.../devicetree/bindings/sound/cirrus,cs4270.yaml | 59 +
.../devicetree/bindings/sound/cirrus,cs42xx8.yaml | 81 +
.../devicetree/bindings/sound/cirrus,cs530x.yaml | 85 +
Documentation/devicetree/bindings/sound/cs4270.txt | 21 -
.../devicetree/bindings/sound/cs42xx8.txt | 34 -
.../devicetree/bindings/sound/everest,es7134.txt | 15 -
.../devicetree/bindings/sound/everest,es71x4.yaml | 62 +
.../devicetree/bindings/sound/everest,es7241.txt | 28 -
.../devicetree/bindings/sound/everest,es7241.yaml | 67 +
.../devicetree/bindings/sound/everest,es8316.yaml | 7 +-
.../bindings/sound/fsl,imx-audio-spdif.yaml | 66 -
.../devicetree/bindings/sound/fsl,mqs.yaml | 2 +
.../devicetree/bindings/sound/fsl,qmc-audio.yaml | 41 +-
.../devicetree/bindings/sound/fsl,rpmsg.yaml | 1 +
.../sound/{sgtl5000.yaml => fsl,sgtl5000.yaml} | 2 +-
.../devicetree/bindings/sound/fsl,xcvr.yaml | 43 +-
.../devicetree/bindings/sound/fsl-asoc-card.yaml | 53 +-
.../{linux,spdif-dit.yaml => linux,spdif.yaml} | 8 +-
.../devicetree/bindings/sound/maxim,max98088.txt | 23 -
.../devicetree/bindings/sound/maxim,max98088.yaml | 47 +
.../sound/{zl38060.yaml => mscc,zl38060.yaml} | 2 +-
.../devicetree/bindings/sound/nuvoton,nau8824.yaml | 8 +
.../devicetree/bindings/sound/nxp,lpc3220-i2s.yaml | 73 +
.../devicetree/bindings/sound/omap-mcpdm.txt | 30 -
.../devicetree/bindings/sound/pcm512x.txt | 2 +-
.../devicetree/bindings/sound/qcom,apq8096.txt | 128 -
.../sound/qcom,msm8916-wcd-digital-codec.yaml | 55 +
.../bindings/sound/qcom,msm8916-wcd-digital.txt | 20 -
.../devicetree/bindings/sound/qcom,sm8250.yaml | 1 +
.../bindings/sound/qcom,wcd937x-sdw.yaml | 91 +
.../devicetree/bindings/sound/qcom,wcd937x.yaml | 82 +
.../devicetree/bindings/sound/qcom,wsa883x.yaml | 8 +
.../devicetree/bindings/sound/qcom,wsa8840.yaml | 8 +
.../sound/{rt1019.yaml => realtek,rt1019.yaml} | 2 +-
.../devicetree/bindings/sound/realtek,rt5514.yaml | 70 +
.../devicetree/bindings/sound/realtek,rt5631.yaml | 67 +
.../devicetree/bindings/sound/realtek,rt5645.yaml | 131 +
.../devicetree/bindings/sound/realtek,rt5659.yaml | 129 +
.../devicetree/bindings/sound/realtek,rt5677.yaml | 135 +
Documentation/devicetree/bindings/sound/rt5514.txt | 37 -
Documentation/devicetree/bindings/sound/rt5631.txt | 48 -
Documentation/devicetree/bindings/sound/rt5645.txt | 82 -
Documentation/devicetree/bindings/sound/rt5659.txt | 89 -
Documentation/devicetree/bindings/sound/rt5677.txt | 78 -
.../bindings/sound/samsung,midas-audio.yaml | 33 +
.../bindings/sound/simple-audio-mux.yaml | 6 +
.../devicetree/bindings/sound/spdif-receiver.txt | 10 -
.../devicetree/bindings/sound/tas571x.txt | 49 -
.../devicetree/bindings/sound/ti,omap4-mcpdm.yaml | 73 +
.../sound/{tas2562.yaml => ti,tas2562.yaml} | 2 +-
.../sound/{tas2770.yaml => ti,tas2770.yaml} | 2 +-
.../sound/{tas27xx.yaml => ti,tas27xx.yaml} | 2 +-
.../devicetree/bindings/sound/ti,tas57xx.yaml | 133 +
.../sound/{tas5805m.yaml => ti,tas5805m.yaml} | 2 +-
.../bindings/sound/ti,tlv320adc3xxx.yaml | 32 +-
.../{tlv320adcx140.yaml => ti,tlv320adcx140.yaml} | 2 +-
.../sound/{wm8750.yaml => wlf,wm8750.yaml} | 2 +-
.../devicetree/bindings/sound/wlf,wm8782.yaml | 47 +
.../devicetree/bindings/sound/wlf,wm8804.yaml | 58 +
Documentation/devicetree/bindings/sound/wm8782.txt | 24 -
Documentation/devicetree/bindings/sound/wm8804.txt | 25 -
Documentation/kbuild/kconfig-language.rst | 12 +-
Documentation/networking/af_xdp.rst | 33 +-
.../userspace-api/media/v4l/dev-subdev.rst | 2 +-
MAINTAINERS | 24 +-
Makefile | 2 +-
arch/arm/configs/imx_v6_v7_defconfig | 1 -
arch/arm64/configs/defconfig | 1 -
arch/arm64/include/asm/el2_setup.h | 6 +-
arch/arm64/include/asm/io.h | 36 +-
arch/arm64/include/asm/kvm_arm.h | 6 +
arch/arm64/include/asm/kvm_emulate.h | 71 +-
arch/arm64/include/asm/kvm_host.h | 25 +-
arch/arm64/include/asm/kvm_hyp.h | 4 +-
arch/arm64/include/asm/kvm_pkvm.h | 9 +
arch/arm64/kernel/armv8_deprecated.c | 3 +
arch/arm64/kvm/arm.c | 76 +
arch/arm64/kvm/emulate-nested.c | 21 +-
arch/arm64/kvm/fpsimd.c | 11 +-
arch/arm64/kvm/guest.c | 3 +-
arch/arm64/kvm/hyp/aarch32.c | 18 +-
arch/arm64/kvm/hyp/fpsimd.S | 6 +
arch/arm64/kvm/hyp/include/hyp/switch.h | 36 +-
arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 1 -
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 84 +-
arch/arm64/kvm/hyp/nvhe/pkvm.c | 17 +-
arch/arm64/kvm/hyp/nvhe/setup.c | 25 +-
arch/arm64/kvm/hyp/nvhe/switch.c | 24 +-
arch/arm64/kvm/hyp/vhe/switch.c | 12 +-
arch/arm64/kvm/nested.c | 6 +-
arch/arm64/kvm/reset.c | 3 +
arch/arm64/mm/contpte.c | 4 +-
arch/loongarch/boot/dts/loongson-2k0500-ref.dts | 4 +-
arch/loongarch/boot/dts/loongson-2k1000-ref.dts | 4 +-
arch/loongarch/boot/dts/loongson-2k2000-ref.dts | 2 +-
arch/loongarch/include/asm/numa.h | 1 +
arch/loongarch/include/asm/stackframe.h | 2 +-
arch/loongarch/kernel/head.S | 2 +-
arch/loongarch/kernel/setup.c | 6 +-
arch/loongarch/kernel/smp.c | 5 +-
arch/loongarch/kernel/vmlinux.lds.S | 10 +-
arch/riscv/kvm/aia_device.c | 7 +-
arch/riscv/kvm/vcpu_onereg.c | 4 +-
arch/riscv/mm/fault.c | 4 +-
arch/riscv/mm/init.c | 21 +-
arch/s390/kernel/crash_dump.c | 54 +-
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/include/asm/vmxfeatures.h | 2 +-
arch/x86/kernel/amd_nb.c | 9 +-
arch/x86/kernel/machine_kexec_64.c | 11 +-
arch/x86/kvm/Kconfig | 11 +-
arch/x86/kvm/lapic.c | 39 +-
arch/x86/kvm/lapic.h | 2 +-
arch/x86/kvm/mmu/mmu.c | 46 +-
arch/x86/kvm/mmu/spte.h | 9 +
arch/x86/kvm/mmu/tdp_iter.h | 2 +
arch/x86/kvm/mmu/tdp_mmu.c | 2 +-
arch/x86/kvm/svm/sev.c | 19 +-
arch/x86/kvm/svm/svm.c | 69 +-
arch/x86/kvm/svm/svm.h | 4 +-
arch/x86/kvm/vmx/nested.c | 5 +
arch/x86/kvm/vmx/vmx.c | 11 +-
arch/x86/kvm/x86.c | 11 +-
drivers/acpi/ac.c | 4 +-
drivers/acpi/apei/einj-core.c | 2 +-
drivers/acpi/ec.c | 9 +-
drivers/acpi/sbs.c | 4 +-
drivers/acpi/utils.c | 16 +-
drivers/ata/pata_macio.c | 9 +-
drivers/block/null_blk/main.c | 4 +-
drivers/char/tpm/tpm.h | 2 +-
drivers/char/tpm/tpm_tis_core.c | 3 +-
drivers/char/tpm/tpm_tis_core.h | 2 +-
drivers/clk/sifive/sifive-prci.c | 8 -
drivers/cpufreq/amd-pstate-ut.c | 3 +-
drivers/cpufreq/amd-pstate.c | 36 +-
{include/linux => drivers/cpufreq}/amd-pstate.h | 33 -
drivers/cpufreq/intel_pstate.c | 3 +-
drivers/cxl/core/region.c | 18 +-
drivers/edac/amd64_edac.c | 8 +-
drivers/edac/igen6_edac.c | 4 +-
drivers/firmware/cirrus/cs_dsp.c | 71 +-
drivers/firmware/efi/efi-pstore.c | 8 +-
drivers/firmware/efi/libstub/loongarch.c | 2 +-
drivers/firmware/efi/libstub/zboot.lds | 1 +
drivers/firmware/efi/runtime-wrappers.c | 13 +-
drivers/gpio/Kconfig | 2 +-
drivers/gpio/gpio-gw-pld.c | 1 +
drivers/gpio/gpio-mc33880.c | 1 +
drivers/gpio/gpio-pcf857x.c | 1 +
drivers/gpio/gpio-pl061.c | 1 +
drivers/gpio/gpio-tqmx86.c | 110 +-
drivers/gpu/drm/amd/include/pptable.h | 91 +-
.../gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c | 20 +-
.../gpu/drm/arm/display/komeda/komeda_color_mgmt.c | 5 -
drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 4 +-
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 19 +-
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 3 -
drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c | 4 +-
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 28 +-
drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 60 +-
drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c | 1 +
drivers/hid/hid-asus.c | 4 +-
drivers/hid/hid-core.c | 1 -
drivers/hid/hid-debug.c | 2 +
drivers/hid/hid-ids.h | 2 +
drivers/hid/hid-input.c | 13 +
drivers/hid/hid-logitech-dj.c | 4 +-
drivers/hid/hid-logitech-hidpp.c | 1 +
drivers/hid/hid-nintendo.c | 6 +-
drivers/hid/hid-nvidia-shield.c | 4 +-
drivers/hid/i2c-hid/i2c-hid-of-elan.c | 59 +-
drivers/hid/intel-ish-hid/ishtp/loader.c | 79 +-
drivers/hid/intel-ish-hid/ishtp/loader.h | 33 +-
drivers/i2c/busses/i2c-synquacer.c | 11 +-
drivers/input/touchscreen/silead.c | 19 +-
drivers/iommu/amd/amd_iommu.h | 3 +-
drivers/iommu/amd/init.c | 9 +
drivers/iommu/amd/iommu.c | 48 +-
drivers/iommu/amd/ppr.c | 25 +-
drivers/iommu/dma-iommu.c | 8 +-
drivers/irqchip/irq-gic-v3-its.c | 44 +-
drivers/irqchip/irq-riscv-intc.c | 9 +-
drivers/irqchip/irq-sifive-plic.c | 34 +-
drivers/media/pci/intel/ipu6/ipu6-isys-queue.c | 6 +-
drivers/media/pci/intel/ipu6/ipu6-isys.c | 71 +-
drivers/media/pci/intel/ipu6/ipu6.c | 5 +-
drivers/media/pci/intel/ivsc/mei_csi.c | 5 +-
drivers/media/pci/mgb4/mgb4_core.c | 7 +-
drivers/net/ethernet/intel/ice/ice.h | 44 +-
drivers/net/ethernet/intel/ice/ice_base.c | 3 +
drivers/net/ethernet/intel/ice/ice_lib.c | 27 +-
drivers/net/ethernet/intel/ice/ice_main.c | 118 +-
drivers/net/ethernet/intel/ice/ice_nvm.c | 116 +-
drivers/net/ethernet/intel/ice/ice_type.h | 14 +-
drivers/net/ethernet/intel/ice/ice_xsk.c | 13 +-
drivers/net/ethernet/intel/igc/igc_ethtool.c | 9 +-
drivers/net/ethernet/intel/igc/igc_main.c | 4 +
.../net/ethernet/marvell/octeontx2/af/rvu_npc.c | 33 +-
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 104 +-
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 9 +-
drivers/net/ethernet/mellanox/mlx5/core/fw.c | 4 +
drivers/net/ethernet/mellanox/mlx5/core/health.c | 8 +
.../net/ethernet/mellanox/mlx5/core/lag/port_sel.c | 8 +-
.../net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c | 4 +
drivers/net/ethernet/mellanox/mlx5/core/main.c | 3 +
drivers/net/ethernet/pensando/ionic/ionic_txrx.c | 1 +
drivers/net/phy/micrel.c | 104 +-
drivers/net/virtio_net.c | 42 +-
drivers/net/vmxnet3/vmxnet3_drv.c | 2 +-
drivers/net/vxlan/vxlan_core.c | 8 +-
drivers/net/wireless/ath/ath10k/Kconfig | 1 +
drivers/net/wireless/ath/ath11k/core.c | 2 +-
drivers/net/wireless/ath/ath11k/mac.c | 38 +-
drivers/net/wireless/ath/ath11k/pcic.c | 25 +-
drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 2 +-
drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 16 +-
drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 9 +
drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 14 +-
drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 2 +-
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 39 +-
.../net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 2 -
drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c | 13 +-
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 +
drivers/net/wireless/intel/iwlwifi/mvm/rs.h | 9 +-
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 5 +-
drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 12 +-
drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 12 +-
drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 5 +
drivers/net/wireless/mediatek/mt76/mt7615/main.c | 4 +
drivers/net/wireless/microchip/wilc1000/cfg80211.c | 41 +-
drivers/net/wireless/microchip/wilc1000/hif.c | 17 +-
drivers/net/wireless/microchip/wilc1000/netdev.c | 43 +-
drivers/net/wireless/microchip/wilc1000/netdev.h | 12 +-
drivers/net/wireless/microchip/wilc1000/wlan.c | 5 +-
drivers/net/wireless/realtek/rtlwifi/core.c | 15 -
drivers/net/wwan/iosm/iosm_ipc_devlink.c | 2 +-
drivers/nvme/host/fabrics.c | 6 +-
drivers/nvme/host/pr.c | 2 +-
drivers/of/irq.c | 125 +-
drivers/of/of_private.h | 3 +
drivers/of/of_test.c | 1 +
drivers/of/property.c | 30 +-
drivers/pci/access.c | 4 -
drivers/pci/pci.c | 1 -
drivers/pci/probe.c | 3 -
drivers/platform/x86/Kconfig | 1 +
drivers/platform/x86/amd/hsmp.c | 50 +-
drivers/platform/x86/dell/dell-smbios-base.c | 103 +-
drivers/platform/x86/touchscreen_dmi.c | 59 +-
drivers/pnp/base.h | 1 +
drivers/pnp/driver.c | 6 +
drivers/ptp/ptp_chardev.c | 3 +-
drivers/scsi/device_handler/scsi_dh_alua.c | 31 +-
drivers/scsi/mpi3mr/mpi3mr_transport.c | 2 +-
drivers/scsi/mpt3sas/mpt3sas_scsih.c | 4 +-
drivers/scsi/qedf/qedf.h | 1 +
drivers/scsi/qedf/qedf_main.c | 47 +-
drivers/scsi/scsi.c | 7 +
drivers/scsi/sr.h | 2 +-
drivers/scsi/sr_ioctl.c | 5 +-
drivers/soc/fsl/qe/qmc.c | 32 +-
drivers/soundwire/slave.c | 13 +-
drivers/thermal/thermal_core.c | 35 +-
drivers/thermal/thermal_core.h | 2 +
drivers/thermal/thermal_debugfs.c | 18 +-
drivers/thermal/thermal_trip.c | 20 +-
drivers/ufs/core/ufs-mcq.c | 17 +-
fs/bcachefs/btree_locking.c | 1 +
fs/bcachefs/move.c | 16 +-
fs/btrfs/btrfs_inode.h | 10 +
fs/btrfs/disk-io.c | 10 +-
fs/btrfs/extent_io.c | 60 +-
fs/btrfs/file.c | 16 +
fs/btrfs/ordered-data.c | 31 +
fs/btrfs/tree-log.c | 17 +-
fs/nilfs2/dir.c | 2 +-
fs/nilfs2/segment.c | 3 +
fs/proc/base.c | 2 +-
fs/smb/client/smb2pdu.c | 3 -
fs/smb/client/smb2transport.c | 2 +-
include/dt-bindings/sound/audio-graph.h | 26 +
include/linux/acpi.h | 1 +
include/linux/atomic/atomic-arch-fallback.h | 6 +-
include/linux/atomic/atomic-instrumented.h | 8 +-
include/linux/atomic/atomic-long.h | 4 +-
include/linux/cdrom.h | 2 +-
include/linux/firmware/cirrus/cs_dsp.h | 10 +-
include/linux/firmware/mediatek/mtk-adsp-ipc.h | 2 +-
include/linux/huge_mm.h | 10 +-
include/linux/i2c.h | 1 -
include/linux/iommu.h | 2 +-
include/linux/ksm.h | 17 +-
include/linux/lockdep.h | 5 -
include/linux/mm_types.h | 2 +-
include/linux/pci.h | 2 -
include/linux/pnp.h | 6 +-
include/net/rtnetlink.h | 1 +
include/net/tcp_ao.h | 7 +-
include/soc/fsl/qe/qmc.h | 27 +-
include/sound/cs35l41.h | 4 +-
include/sound/cs35l56.h | 13 +-
include/sound/rt1318.h | 16 +
include/sound/simple_card_utils.h | 8 +-
include/sound/soc-dai.h | 36 +-
include/sound/soc-dapm.h | 2 +-
include/sound/soc-topology.h | 2 +-
include/sound/soc.h | 19 +-
include/sound/sof.h | 1 +
include/sound/tas2781-dsp.h | 11 +-
include/sound/tas2781-tlv.h | 262 +-
include/sound/tas2781.h | 10 +-
include/uapi/linux/input-event-codes.h | 2 +
io_uring/io-wq.c | 10 +-
io_uring/io_uring.h | 2 +-
io_uring/napi.c | 24 +-
io_uring/register.c | 4 +
kernel/bpf/devmap.c | 3 -
kernel/bpf/syscall.c | 11 +-
kernel/bpf/verifier.c | 4 +
kernel/events/core.c | 13 +
kernel/trace/bpf_trace.c | 2 -
lib/test_rhashtable.c | 1 +
mm/filemap.c | 2 +-
mm/huge_memory.c | 8 +-
mm/hugetlb.c | 16 +-
mm/kmsan/core.c | 15 +-
mm/ksm.c | 17 +-
mm/memcontrol.c | 2 -
mm/mempool.c | 2 +-
mm/page_alloc.c | 50 +-
mm/page_io.c | 2 +-
mm/slub.c | 5 +-
mm/util.c | 10 +-
mm/vmalloc.c | 2 +-
mm/vmscan.c | 2 +-
net/ax25/af_ax25.c | 6 +
net/ax25/ax25_dev.c | 2 +-
net/bpf/test_run.c | 6 +
net/core/dev.c | 3 +-
net/core/dst_cache.c | 2 +
net/core/rtnetlink.c | 44 +-
net/ethtool/ioctl.c | 2 +-
net/ethtool/tsinfo.c | 6 +-
net/ipv4/devinet.c | 2 +-
net/ipv4/fib_frontend.c | 7 +-
net/ipv4/tcp.c | 9 +-
net/ipv4/tcp_ao.c | 13 +-
net/ipv6/ila/ila_lwt.c | 7 +-
net/ipv6/ioam6_iptunnel.c | 8 +-
net/ipv6/ip6_fib.c | 6 +-
net/ipv6/route.c | 1 +
net/ipv6/rpl_iptunnel.c | 14 +-
net/ipv6/seg6_iptunnel.c | 14 +-
net/mac80211/cfg.c | 9 +-
net/mac80211/he.c | 10 +-
net/mac80211/ieee80211_i.h | 2 +
net/mac80211/main.c | 10 +-
net/mac80211/mesh.c | 1 +
net/mac80211/mesh_pathtbl.c | 13 +
net/mac80211/parse.c | 2 +-
net/mac80211/scan.c | 14 +-
net/mac80211/sta_info.c | 4 +-
net/mac80211/util.c | 2 +
net/mptcp/protocol.c | 9 +-
net/ncsi/internal.h | 2 +
net/ncsi/ncsi-manage.c | 73 +-
net/ncsi/ncsi-rsp.c | 4 +-
net/sched/sch_multiq.c | 2 +-
net/sched/sch_taprio.c | 15 +-
net/smc/af_smc.c | 22 +-
net/sunrpc/auth_gss/svcauth_gss.c | 2 +-
net/unix/af_unix.c | 90 +-
net/unix/diag.c | 12 +-
net/wireless/core.c | 2 +-
net/wireless/pmsr.c | 8 +-
net/wireless/rdev-ops.h | 6 +-
net/wireless/scan.c | 50 +-
net/wireless/sysfs.c | 4 +-
net/wireless/util.c | 7 +-
net/xdp/xsk.c | 5 +-
scripts/atomic/kerneldoc/sub_and_test | 2 +-
scripts/const_structs.checkpatch | 1 +
scripts/kconfig/confdata.c | 13 -
scripts/kconfig/expr.c | 29 -
scripts/kconfig/expr.h | 1 -
scripts/kconfig/gconf.c | 3 +-
scripts/kconfig/menu.c | 2 -
scripts/link-vmlinux.sh | 2 +-
scripts/mod/modpost.c | 5 +-
security/tomoyo/Kconfig | 2 +-
security/tomoyo/common.c | 2 +-
sound/hda/intel-sdw-acpi.c | 6 +-
sound/pci/hda/tas2781_hda_i2c.c | 4 +-
sound/soc/amd/acp-es8336.c | 4 +-
sound/soc/amd/acp/acp-i2s.c | 19 +-
sound/soc/amd/acp/acp-legacy-common.c | 1 +
sound/soc/amd/acp/acp-pci.c | 1 +
sound/soc/amd/acp/acp-pdm.c | 1 +
sound/soc/amd/acp/acp-platform.c | 14 +
sound/soc/amd/acp/acp-rembrandt.c | 7 +-
sound/soc/amd/acp/acp-renoir.c | 2 -
sound/soc/amd/acp/acp63.c | 7 +-
sound/soc/amd/acp/acp70.c | 2 -
sound/soc/amd/acp/amd.h | 3 +-
sound/soc/amd/ps/ps-mach.c | 1 +
sound/soc/amd/renoir/acp3x-rn.c | 1 +
sound/soc/amd/yc/acp6x-mach.c | 1 +
sound/soc/codecs/Kconfig | 56 +
sound/soc/codecs/Makefile | 19 +
sound/soc/codecs/adau7118.c | 6 +-
sound/soc/codecs/ak4118.c | 1 -
sound/soc/codecs/ak4458.c | 12 +-
sound/soc/codecs/ak4613.c | 2 +-
sound/soc/codecs/ak4619.c | 912 ++++++
sound/soc/codecs/audio-iio-aux.c | 83 +-
sound/soc/codecs/aw87390.c | 2 +-
sound/soc/codecs/aw88261.c | 2 +-
sound/soc/codecs/aw88395/aw88395.c | 4 +-
sound/soc/codecs/aw88395/aw88395_lib.c | 51 +-
sound/soc/codecs/aw88399.c | 4 +-
sound/soc/codecs/cs35l34.c | 2 +-
sound/soc/codecs/cs35l35.c | 2 +-
sound/soc/codecs/cs35l36.c | 2 +-
sound/soc/codecs/cs35l41-lib.c | 4 +-
sound/soc/codecs/cs35l41.c | 3 +-
sound/soc/codecs/cs35l56-sdw.c | 75 -
sound/soc/codecs/cs35l56-shared.c | 133 +-
sound/soc/codecs/cs35l56.c | 211 +-
sound/soc/codecs/cs35l56.h | 2 -
sound/soc/codecs/cs530x-i2c.c | 72 +
sound/soc/codecs/cs530x.c | 971 +++++++
sound/soc/codecs/cs530x.h | 223 ++
sound/soc/codecs/cs53l30.c | 3 +-
sound/soc/codecs/cx2072x.c | 5 -
sound/soc/codecs/da7213.c | 2 +-
sound/soc/codecs/es8311.c | 973 +++++++
sound/soc/codecs/es8311.h | 162 ++
sound/soc/codecs/es8326.c | 58 +-
sound/soc/codecs/framer-codec.c | 2 +-
sound/soc/codecs/hdmi-codec.c | 2 +-
sound/soc/codecs/idt821034.c | 2 +-
sound/soc/codecs/jz4760.c | 2 +-
sound/soc/codecs/jz4770.c | 2 +-
sound/soc/codecs/lpass-macro-common.c | 23 +
sound/soc/codecs/lpass-macro-common.h | 41 +
sound/soc/codecs/lpass-rx-macro.c | 623 ++--
sound/soc/codecs/lpass-tx-macro.c | 2 +-
sound/soc/codecs/lpass-va-macro.c | 31 +-
sound/soc/codecs/lpass-wsa-macro.c | 644 ++++-
sound/soc/codecs/max98088.c | 10 +-
sound/soc/codecs/max98390.c | 1 -
sound/soc/codecs/max98504.c | 6 +-
sound/soc/codecs/mt6358.c | 38 +-
sound/soc/codecs/nau8822.c | 76 +-
sound/soc/codecs/nau8822.h | 1 +
sound/soc/codecs/nau8824.c | 21 +-
sound/soc/codecs/nau8824.h | 1 +
sound/soc/codecs/pcm3168a.c | 3 +-
sound/soc/codecs/pcm512x-i2c.c | 2 +
sound/soc/codecs/pcm512x-spi.c | 2 +
sound/soc/codecs/pcm6240.c | 67 +-
sound/soc/codecs/peb2466.c | 2 +-
sound/soc/codecs/rk817_codec.c | 1 -
sound/soc/codecs/rt1318.c | 1354 +++++++++
sound/soc/codecs/rt1318.h | 342 +++
sound/soc/codecs/rt1320-sdw.c | 2260 +++++++++++++++
sound/soc/codecs/rt1320-sdw.h | 94 +
sound/soc/codecs/rt711-sdca.c | 72 +
sound/soc/codecs/rt711-sdca.h | 1 +
sound/soc/codecs/rt712-sdca-sdw.c | 36 +-
sound/soc/codecs/rt712-sdca-sdw.h | 95 +-
sound/soc/codecs/rt712-sdca.c | 665 ++++-
sound/soc/codecs/rt712-sdca.h | 48 +-
sound/soc/codecs/simple-mux.c | 55 +-
sound/soc/codecs/tas2552.c | 1 -
sound/soc/codecs/tas2764.c | 1 -
sound/soc/codecs/tas2770.c | 1 -
sound/soc/codecs/tas2780.c | 1 -
sound/soc/codecs/tas2781-comlib.c | 21 +-
sound/soc/codecs/tas2781-fmwlib.c | 89 +-
sound/soc/codecs/tas2781-i2c.c | 235 +-
sound/soc/codecs/tas5086.c | 27 +-
sound/soc/codecs/tlv320adc3xxx.c | 106 +-
sound/soc/codecs/tlv320adcx140.c | 1 -
sound/soc/codecs/tlv320aic31xx.c | 1 -
sound/soc/codecs/ts3a227e.c | 1 -
sound/soc/codecs/wcd-mbhc-v2.c | 4 +-
sound/soc/codecs/wcd-mbhc-v2.h | 4 +-
sound/soc/codecs/wcd9335.c | 128 +-
sound/soc/codecs/wcd934x.c | 72 +-
sound/soc/codecs/wcd937x-sdw.c | 1137 ++++++++
sound/soc/codecs/wcd937x.c | 2971 ++++++++++++++++++++
sound/soc/codecs/wcd937x.h | 624 ++++
sound/soc/codecs/wcd938x-sdw.c | 4 +-
sound/soc/codecs/wcd938x.c | 36 +-
sound/soc/codecs/wcd938x.h | 10 +-
sound/soc/codecs/wcd939x-sdw.c | 4 +-
sound/soc/codecs/wcd939x.c | 176 +-
sound/soc/codecs/wcd939x.h | 18 +-
sound/soc/codecs/wm0010.c | 8 -
sound/soc/codecs/wm_adsp.c | 2 +-
sound/soc/codecs/wsa881x.c | 2 +-
sound/soc/codecs/wsa883x.c | 11 +-
sound/soc/codecs/wsa884x.c | 10 +-
sound/soc/fsl/Kconfig | 18 +-
sound/soc/fsl/Makefile | 4 +-
sound/soc/fsl/fsl-asoc-card.c | 383 ++-
sound/soc/fsl/fsl_aud2htx.c | 11 +-
sound/soc/fsl/fsl_audmix.c | 16 +-
sound/soc/fsl/fsl_easrc.c | 10 +-
sound/soc/fsl/fsl_mqs.c | 46 +-
sound/soc/fsl/fsl_qmc_audio.c | 591 ++--
sound/soc/fsl/fsl_rpmsg.c | 9 +
sound/soc/fsl/fsl_sai.c | 141 +-
sound/soc/fsl/fsl_sai.h | 4 +-
sound/soc/fsl/fsl_xcvr.c | 181 +-
sound/soc/fsl/fsl_xcvr.h | 91 +
sound/soc/fsl/imx-audmix.c | 79 +-
sound/soc/fsl/imx-es8328.c | 1 -
sound/soc/fsl/imx-pcm-fiq.c | 1 +
sound/soc/fsl/imx-rpmsg.c | 2 -
sound/soc/fsl/imx-spdif.c | 103 -
sound/soc/fsl/lpc3xxx-i2s.c | 375 +++
sound/soc/fsl/lpc3xxx-i2s.h | 80 +
sound/soc/fsl/lpc3xxx-pcm.c | 72 +
sound/soc/generic/audio-graph-card.c | 113 +-
.../soc/generic/audio-graph-card2-custom-sample.c | 3 +-
sound/soc/generic/audio-graph-card2.c | 191 +-
sound/soc/generic/simple-card-utils.c | 129 +-
sound/soc/generic/simple-card.c | 71 +-
sound/soc/generic/test-component.c | 2 +-
sound/soc/intel/avs/boards/es8336.c | 8 +-
sound/soc/intel/avs/pcm.c | 4 +-
sound/soc/intel/avs/topology.c | 2 +-
sound/soc/intel/boards/Kconfig | 2 +
sound/soc/intel/boards/bdw-rt5650.c | 2 +-
sound/soc/intel/boards/ehl_rt5660.c | 2 +-
sound/soc/intel/boards/kbl_da7219_max98357a.c | 4 +-
sound/soc/intel/boards/kbl_da7219_max98927.c | 6 +-
sound/soc/intel/boards/kbl_rt5660.c | 2 +-
sound/soc/intel/boards/kbl_rt5663_max98927.c | 8 +-
.../soc/intel/boards/kbl_rt5663_rt5514_max98927.c | 6 +-
sound/soc/intel/boards/sof_board_helpers.h | 2 -
sound/soc/intel/boards/sof_da7219.c | 55 +-
sound/soc/intel/boards/sof_es8336.c | 2 +-
sound/soc/intel/boards/sof_maxim_common.c | 114 +-
sound/soc/intel/boards/sof_nau8825.c | 2 +-
sound/soc/intel/boards/sof_realtek_common.c | 2 +-
sound/soc/intel/boards/sof_rt5682.c | 2 +-
sound/soc/intel/boards/sof_sdw.c | 64 +-
sound/soc/intel/boards/sof_sdw_common.h | 6 +-
sound/soc/intel/boards/sof_sdw_cs42l42.c | 11 +-
sound/soc/intel/boards/sof_sdw_rt5682.c | 11 +-
sound/soc/intel/boards/sof_sdw_rt700.c | 11 +-
sound/soc/intel/boards/sof_sdw_rt711.c | 11 +-
sound/soc/intel/boards/sof_sdw_rt_amp.c | 2 +-
sound/soc/intel/boards/sof_sdw_rt_dmic.c | 14 +-
.../soc/intel/boards/sof_sdw_rt_sdca_jack_common.c | 11 +-
sound/soc/intel/boards/sof_wm8804.c | 2 +-
sound/soc/intel/common/soc-acpi-intel-arl-match.c | 50 +
sound/soc/intel/common/soc-acpi-intel-rpl-match.c | 50 +
sound/soc/intel/skylake/skl-topology.c | 2 +-
sound/soc/meson/axg-fifo.c | 2 +-
sound/soc/meson/axg-frddr.c | 4 +-
sound/soc/meson/axg-tdm.h | 2 +-
sound/soc/meson/axg-toddr.c | 4 +-
sound/soc/qcom/common.c | 35 +
sound/soc/qcom/common.h | 3 +
sound/soc/qcom/lpass-cpu.c | 4 +
sound/soc/qcom/qdsp6/audioreach.c | 30 +-
sound/soc/qcom/qdsp6/audioreach.h | 2 +-
sound/soc/qcom/qdsp6/q6afe-dai.c | 16 +-
sound/soc/qcom/qdsp6/q6apm-dai.c | 3 +
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 21 +-
sound/soc/qcom/qdsp6/topology.c | 38 +-
sound/soc/qcom/sc8280xp.c | 15 +
sound/soc/qcom/x1e80100.c | 38 +
sound/soc/rockchip/rockchip_i2s.c | 1 -
sound/soc/rockchip/rockchip_spdif.c | 1 -
sound/soc/samsung/Kconfig | 2 +-
sound/soc/samsung/aries_wm8994.c | 2 +-
sound/soc/samsung/midas_wm1811.c | 348 ++-
sound/soc/sh/fsi.c | 2 +-
sound/soc/sh/rcar/core.c | 2 +-
sound/soc/soc-core.c | 4 +-
sound/soc/soc-dai.c | 18 +-
sound/soc/soc-dapm.c | 49 +-
sound/soc/soc-ops.c | 26 +-
sound/soc/soc-pcm.c | 26 +-
sound/soc/soc-topology.c | 572 ++--
sound/soc/soc-utils.c | 19 +-
sound/soc/sof/intel/hda.c | 4 +-
sound/soc/sof/intel/pci-tgl.c | 4 +-
sound/soc/sof/ipc3-topology.c | 14 +-
sound/soc/sof/ipc4-topology.c | 71 +-
sound/soc/sof/mediatek/mt8186/mt8186.c | 2 +-
sound/soc/sof/mediatek/mt8195/mt8195.c | 2 +-
sound/soc/sof/sof-audio.c | 20 +-
sound/soc/sof/sof-audio.h | 9 +-
sound/soc/sof/topology.c | 4 +-
sound/soc/tegra/tegra210_i2s.c | 71 +-
sound/soc/tegra/tegra210_i2s.h | 2 +
tools/arch/arm64/include/asm/cputype.h | 6 +
tools/arch/x86/include/asm/msr-index.h | 9 +-
tools/arch/x86/include/uapi/asm/kvm.h | 22 +-
tools/include/uapi/asm-generic/unistd.h | 5 +-
tools/include/uapi/drm/i915_drm.h | 31 +-
tools/include/uapi/linux/kvm.h | 4 +-
tools/include/uapi/linux/stat.h | 4 +-
tools/lib/bpf/features.c | 3 +-
tools/perf/Makefile.perf | 1 +
.../perf/arch/mips/entry/syscalls/syscall_n64.tbl | 1 +
tools/perf/arch/powerpc/entry/syscalls/syscall.tbl | 1 +
tools/perf/arch/s390/entry/syscalls/syscall.tbl | 1 +
tools/perf/arch/x86/entry/syscalls/syscall_64.tbl | 3 +-
tools/perf/builtin-record.c | 6 +-
tools/perf/builtin-trace.c | 2 +-
.../beauty/arch/x86/include/asm/irq_vectors.h | 8 +-
tools/perf/trace/beauty/include/linux/socket.h | 3 +-
tools/perf/trace/beauty/include/uapi/linux/fcntl.h | 14 +-
tools/perf/trace/beauty/include/uapi/linux/prctl.h | 22 +
tools/perf/trace/beauty/include/uapi/linux/stat.h | 4 +-
tools/power/cpupower/utils/helpers/amd.c | 26 +-
tools/testing/cxl/test/mem.c | 1 +
tools/testing/selftests/alsa/Makefile | 2 +-
.../selftests/bpf/progs/test_sk_storage_tracing.c | 2 +-
tools/testing/selftests/cachestat/test_cachestat.c | 1 +
.../selftests/filesystems/overlayfs/dev_in_maps.c | 1 +
tools/testing/selftests/ftrace/config | 26 +-
.../ftrace/test.d/dynevent/test_duplicates.tc | 2 +-
.../ftrace/test.d/filter/event-filter-function.tc | 20 +-
.../ftrace/test.d/kprobe/kprobe_eventname.tc | 3 +-
tools/testing/selftests/futex/Makefile | 2 -
tools/testing/selftests/futex/functional/Makefile | 2 +-
.../selftests/futex/functional/futex_requeue_pi.c | 2 +-
tools/testing/selftests/kvm/Makefile | 1 +
.../selftests/kvm/s390x/shared_zeropage_test.c | 111 +
tools/testing/selftests/net/hsr/config | 1 +
tools/testing/selftests/net/lib.sh | 18 +-
tools/testing/selftests/openat2/openat2_test.c | 1 +
661 files changed, 23899 insertions(+), 5937 deletions(-)
create mode 100644 Documentation/devicetree/bindings/input/ilitek,ili2901.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/ak4104.txt
delete mode 100644 Documentation/devicetree/bindings/sound/ak4554.txt
delete mode 100644 Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.txt
create mode 100644 Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.yaml
create mode 100644 Documentation/devicetree/bindings/sound/asahi-kasei,ak4104.yaml
rename Documentation/devicetree/bindings/sound/{ak4375.yaml => asahi-kasei,ak4375.yaml} (94%)
create mode 100644 Documentation/devicetree/bindings/sound/asahi-kasei,ak4554.yaml
rename Documentation/devicetree/bindings/sound/{ak4613.yaml => asahi-kasei,ak4613.yaml} (94%)
create mode 100644 Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml
rename Documentation/devicetree/bindings/sound/{ak4642.yaml => asahi-kasei,ak4642.yaml} (94%)
create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml
create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml
create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/cs4270.txt
delete mode 100644 Documentation/devicetree/bindings/sound/cs42xx8.txt
delete mode 100644 Documentation/devicetree/bindings/sound/everest,es7134.txt
create mode 100644 Documentation/devicetree/bindings/sound/everest,es71x4.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/everest,es7241.txt
create mode 100644 Documentation/devicetree/bindings/sound/everest,es7241.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/fsl,imx-audio-spdif.yaml
rename Documentation/devicetree/bindings/sound/{sgtl5000.yaml => fsl,sgtl5000.yaml} (97%)
rename Documentation/devicetree/bindings/sound/{linux,spdif-dit.yaml => linux,spdif.yaml} (75%)
delete mode 100644 Documentation/devicetree/bindings/sound/maxim,max98088.txt
create mode 100644 Documentation/devicetree/bindings/sound/maxim,max98088.yaml
rename Documentation/devicetree/bindings/sound/{zl38060.yaml => mscc,zl38060.yaml} (96%)
create mode 100644 Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/omap-mcpdm.txt
delete mode 100644 Documentation/devicetree/bindings/sound/qcom,apq8096.txt
create mode 100644 Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt
create mode 100644 Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml
create mode 100644 Documentation/devicetree/bindings/sound/qcom,wcd937x.yaml
rename Documentation/devicetree/bindings/sound/{rt1019.yaml => realtek,rt1019.yaml} (90%)
create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5514.yaml
create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5631.yaml
create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5645.yaml
create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5659.yaml
create mode 100644 Documentation/devicetree/bindings/sound/realtek,rt5677.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/rt5514.txt
delete mode 100644 Documentation/devicetree/bindings/sound/rt5631.txt
delete mode 100644 Documentation/devicetree/bindings/sound/rt5645.txt
delete mode 100644 Documentation/devicetree/bindings/sound/rt5659.txt
delete mode 100644 Documentation/devicetree/bindings/sound/rt5677.txt
delete mode 100644 Documentation/devicetree/bindings/sound/spdif-receiver.txt
delete mode 100644 Documentation/devicetree/bindings/sound/tas571x.txt
create mode 100644 Documentation/devicetree/bindings/sound/ti,omap4-mcpdm.yaml
rename Documentation/devicetree/bindings/sound/{tas2562.yaml => ti,tas2562.yaml} (97%)
rename Documentation/devicetree/bindings/sound/{tas2770.yaml => ti,tas2770.yaml} (97%)
rename Documentation/devicetree/bindings/sound/{tas27xx.yaml => ti,tas27xx.yaml} (97%)
create mode 100644 Documentation/devicetree/bindings/sound/ti,tas57xx.yaml
rename Documentation/devicetree/bindings/sound/{tas5805m.yaml => ti,tas5805m.yaml} (95%)
rename Documentation/devicetree/bindings/sound/{tlv320adcx140.yaml => ti,tlv320adcx140.yaml} (99%)
rename Documentation/devicetree/bindings/sound/{wm8750.yaml => wlf,wm8750.yaml} (92%)
create mode 100644 Documentation/devicetree/bindings/sound/wlf,wm8782.yaml
create mode 100644 Documentation/devicetree/bindings/sound/wlf,wm8804.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/wm8782.txt
delete mode 100644 Documentation/devicetree/bindings/sound/wm8804.txt
rename {include/linux => drivers/cpufreq}/amd-pstate.h (82%)
create mode 100644 include/dt-bindings/sound/audio-graph.h
create mode 100644 include/sound/rt1318.h
create mode 100644 sound/soc/codecs/ak4619.c
create mode 100644 sound/soc/codecs/cs530x-i2c.c
create mode 100644 sound/soc/codecs/cs530x.c
create mode 100644 sound/soc/codecs/cs530x.h
create mode 100644 sound/soc/codecs/es8311.c
create mode 100644 sound/soc/codecs/es8311.h
create mode 100644 sound/soc/codecs/rt1318.c
create mode 100644 sound/soc/codecs/rt1318.h
create mode 100644 sound/soc/codecs/rt1320-sdw.c
create mode 100644 sound/soc/codecs/rt1320-sdw.h
create mode 100644 sound/soc/codecs/wcd937x-sdw.c
create mode 100644 sound/soc/codecs/wcd937x.c
create mode 100644 sound/soc/codecs/wcd937x.h
delete mode 100644 sound/soc/fsl/imx-spdif.c
create mode 100644 sound/soc/fsl/lpc3xxx-i2s.c
create mode 100644 sound/soc/fsl/lpc3xxx-i2s.h
create mode 100644 sound/soc/fsl/lpc3xxx-pcm.c
create mode 100644 tools/testing/selftests/kvm/s390x/shared_zeropage_test.c
2
1
The soundwire bus code is incorrectly logging probe deferrals as errors.
This series addresses that, cleans up the soundwire bus logging and
drops the unused soudwire driver name field.
Vinod, I booted linux-next yesterday and realised that these bogus error
messages are still spamming the logs when booting x1e80100 and making it
harder to see the valid warnings (which there were too many of). Would
be nice to have this fixed in 6.11:
[ 18.815950] wsa884x-codec sdw:4:0:0217:0204:00:0: Probe of wsa884x-codec failed: -517
[ 18.825093] wsa884x-codec sdw:1:0:0217:0204:00:0: Probe of wsa884x-codec failed: -517
[ 18.832335] wsa884x-codec sdw:4:0:0217:0204:00:1: Probe of wsa884x-codec failed: -517
[ 18.840870] wsa884x-codec sdw:1:0:0217:0204:00:1: Probe of wsa884x-codec failed: -517
[ 18.848463] wsa884x-codec sdw:1:0:0217:0204:00:0: Probe of wsa884x-codec failed: -517
Johan
Changes in v3
- amend the update status error message with "at probe" to make it more
descriptive (it's already unique)
- drop the patch dropping a probe debug message
Changes in v2
- drop probe error message completely
- drop soundwire driver name field
- cleanup probe warning messages
- drop probe debug message
Johan Hovold (3):
soundwire: bus: suppress probe deferral errors
soundwire: bus: drop unused driver name field
soundwire: bus: clean up probe warnings
drivers/soundwire/bus_type.c | 19 ++++---------------
include/linux/soundwire/sdw.h | 2 --
2 files changed, 4 insertions(+), 17 deletions(-)
--
2.44.2
2
5

[PATCH v2] ASoC: codecs: wsa884x: Implement temperature reading and hwmon
by Krzysztof Kozlowski 14 Jul '24
by Krzysztof Kozlowski 14 Jul '24
14 Jul '24
Read temperature of the speaker and expose it via hwmon interface, which
will be later used during calibration of speaker protection algorithms.
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski(a)linaro.org>
---
Changes in v2:
1. Add missing dependency on HWMON for y!=m builds (kernel test robot
report: undefined reference to
`devm_hwmon_device_register_with_info').
---
sound/soc/codecs/Kconfig | 1 +
sound/soc/codecs/wsa884x.c | 197 +++++++++++++++++++++++++++++++++++++
2 files changed, 198 insertions(+)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 97bb69c9848d..09a0b209bc2f 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -2447,6 +2447,7 @@ config SND_SOC_WSA883X
config SND_SOC_WSA884X
tristate "WSA884X Codec"
depends on SOUNDWIRE
+ depends on HWMON || !HWMON
select REGMAP_SOUNDWIRE
help
This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D
diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c
index 7b19df9c1728..5f4acd66f942 100644
--- a/sound/soc/codecs/wsa884x.c
+++ b/sound/soc/codecs/wsa884x.c
@@ -5,11 +5,14 @@
*/
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
+#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -301,8 +304,28 @@
#define WSA884X_PA_FSM_MSK1 (WSA884X_DIG_CTRL0_BASE + 0x3b)
#define WSA884X_PA_FSM_BYP_CTL (WSA884X_DIG_CTRL0_BASE + 0x3c)
#define WSA884X_PA_FSM_BYP0 (WSA884X_DIG_CTRL0_BASE + 0x3d)
+#define WSA884X_PA_FSM_BYP0_DC_CAL_EN_MASK 0x01
+#define WSA884X_PA_FSM_BYP0_DC_CAL_EN_SHIFT 0
+#define WSA884X_PA_FSM_BYP0_CLK_WD_EN_MASK 0x02
+#define WSA884X_PA_FSM_BYP0_CLK_WD_EN_SHIFT 1
+#define WSA884X_PA_FSM_BYP0_BG_EN_MASK 0x04
+#define WSA884X_PA_FSM_BYP0_BG_EN_SHIFT 2
+#define WSA884X_PA_FSM_BYP0_BOOST_EN_MASK 0x08
+#define WSA884X_PA_FSM_BYP0_BOOST_EN_SHIFT 3
+#define WSA884X_PA_FSM_BYP0_PA_EN_MASK 0x10
+#define WSA884X_PA_FSM_BYP0_PA_EN_SHIFT 4
+#define WSA884X_PA_FSM_BYP0_D_UNMUTE_MASK 0x20
+#define WSA884X_PA_FSM_BYP0_D_UNMUTE_SHIFT 5
+#define WSA884X_PA_FSM_BYP0_SPKR_PROT_EN_MASK 0x40
+#define WSA884X_PA_FSM_BYP0_SPKR_PROT_EN_SHIFT 6
+#define WSA884X_PA_FSM_BYP0_TSADC_EN_MASK 0x80
+#define WSA884X_PA_FSM_BYP0_TSADC_EN_SHIFT 7
#define WSA884X_PA_FSM_BYP1 (WSA884X_DIG_CTRL0_BASE + 0x3e)
#define WSA884X_TADC_VALUE_CTL (WSA884X_DIG_CTRL0_BASE + 0x50)
+#define WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK 0x01
+#define WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_SHIFT 0
+#define WSA884X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_MASK 0x02
+#define WSA884X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_SHIFT 1
#define WSA884X_TEMP_DETECT_CTL (WSA884X_DIG_CTRL0_BASE + 0x51)
#define WSA884X_TEMP_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x52)
#define WSA884X_TEMP_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x53)
@@ -691,6 +714,17 @@
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+/* Two-point trimming for temperature calibration */
+#define WSA884X_T1_TEMP -10L
+#define WSA884X_T2_TEMP 150L
+
+/*
+ * Device will report senseless data in many cases, so discard any measurements
+ * outside of valid range.
+ */
+#define WSA884X_LOW_TEMP_THRESHOLD 5
+#define WSA884X_HIGH_TEMP_THRESHOLD 45
+
struct wsa884x_priv {
struct regmap *regmap;
struct device *dev;
@@ -706,6 +740,13 @@ struct wsa884x_priv {
int active_ports;
int dev_mode;
bool hw_init;
+ /*
+ * Protects temperature reading code (related to speaker protection) and
+ * fields: temperature and pa_on.
+ */
+ struct mutex sp_lock;
+ unsigned int temperature;
+ bool pa_on;
};
enum {
@@ -1650,6 +1691,10 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ mutex_lock(&wsa884x->sp_lock);
+ wsa884x->pa_on = true;
+ mutex_unlock(&wsa884x->sp_lock);
+
wsa884x_spkr_post_pmu(component, wsa884x);
snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL,
@@ -1661,6 +1706,10 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w,
snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL,
WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK,
0x0);
+
+ mutex_lock(&wsa884x->sp_lock);
+ wsa884x->pa_on = false;
+ mutex_unlock(&wsa884x->sp_lock);
break;
}
@@ -1800,6 +1849,144 @@ static struct snd_soc_dai_driver wsa884x_dais[] = {
},
};
+static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp)
+{
+ unsigned int d1_msb = 0, d1_lsb = 0, d2_msb = 0, d2_lsb = 0;
+ unsigned int dmeas_msb = 0, dmeas_lsb = 0;
+ int d1, d2, dmeas;
+ unsigned int mask;
+ long val;
+ int ret;
+
+ guard(mutex)(&wsa884x->sp_lock);
+
+ if (wsa884x->pa_on) {
+ /*
+ * Reading temperature is possible only when Power Amplifier is
+ * off. Report last cached data.
+ */
+ *temp = wsa884x->temperature;
+ return 0;
+ }
+
+ ret = pm_runtime_resume_and_get(wsa884x->dev);
+ if (ret < 0)
+ return ret;
+
+ mask = WSA884X_PA_FSM_BYP0_DC_CAL_EN_MASK |
+ WSA884X_PA_FSM_BYP0_CLK_WD_EN_MASK |
+ WSA884X_PA_FSM_BYP0_BG_EN_MASK |
+ WSA884X_PA_FSM_BYP0_D_UNMUTE_MASK |
+ WSA884X_PA_FSM_BYP0_SPKR_PROT_EN_MASK |
+ WSA884X_PA_FSM_BYP0_TSADC_EN_MASK;
+ /*
+ * Here and further do not care about read or update failures.
+ * For example, before turning on Power Amplifier for the first
+ * time, reading WSA884X_TEMP_DIN_MSB will always return 0.
+ * Instead, check if returned value is within reasonable
+ * thresholds.
+ */
+ regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_BYP0, mask, mask);
+
+ regmap_update_bits(wsa884x->regmap, WSA884X_TADC_VALUE_CTL,
+ WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK,
+ FIELD_PREP(WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x0));
+
+ regmap_read(wsa884x->regmap, WSA884X_TEMP_DIN_MSB, &dmeas_msb);
+ regmap_read(wsa884x->regmap, WSA884X_TEMP_DIN_LSB, &dmeas_lsb);
+
+ regmap_update_bits(wsa884x->regmap, WSA884X_TADC_VALUE_CTL,
+ WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK,
+ FIELD_PREP(WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x1));
+
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_1, &d1_msb);
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_2, &d1_lsb);
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_3, &d2_msb);
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_4, &d2_lsb);
+
+ regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_BYP0, mask, 0x0);
+
+ dmeas = (((dmeas_msb & 0xff) << 0x8) | (dmeas_lsb & 0xff)) >> 0x6;
+ d1 = (((d1_msb & 0xff) << 0x8) | (d1_lsb & 0xff)) >> 0x6;
+ d2 = (((d2_msb & 0xff) << 0x8) | (d2_lsb & 0xff)) >> 0x6;
+
+ if (d1 == d2) {
+ /* Incorrect data in OTP? */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ val = WSA884X_T1_TEMP + (((dmeas - d1) * (WSA884X_T2_TEMP - WSA884X_T1_TEMP))/(d2 - d1));
+
+ dev_dbg(wsa884x->dev, "Measured temp %ld (dmeas=%d, d1=%d, d2=%d)\n",
+ val, dmeas, d1, d2);
+
+ if ((val > WSA884X_LOW_TEMP_THRESHOLD) &&
+ (val < WSA884X_HIGH_TEMP_THRESHOLD)) {
+ wsa884x->temperature = val;
+ *temp = val;
+ ret = 0;
+ } else {
+ ret = -EAGAIN;
+ }
+
+out:
+ pm_runtime_mark_last_busy(wsa884x->dev);
+ pm_runtime_put_autosuspend(wsa884x->dev);
+
+ return ret;
+}
+
+static umode_t wsa884x_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa884x_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = wsa884x_get_temp(dev_get_drvdata(dev), temp);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct hwmon_channel_info *const wsa884x_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops wsa884x_hwmon_ops = {
+ .is_visible = wsa884x_hwmon_is_visible,
+ .read = wsa884x_hwmon_read,
+};
+
+static const struct hwmon_chip_info wsa884x_hwmon_chip_info = {
+ .ops = &wsa884x_hwmon_ops,
+ .info = wsa884x_hwmon_info,
+};
+
static void wsa884x_reset_powerdown(void *data)
{
struct wsa884x_priv *wsa884x = data;
@@ -1849,6 +2036,7 @@ static int wsa884x_probe(struct sdw_slave *pdev,
{
struct device *dev = &pdev->dev;
struct wsa884x_priv *wsa884x;
+ struct device *hwmon;
unsigned int i;
int ret;
@@ -1856,6 +2044,8 @@ static int wsa884x_probe(struct sdw_slave *pdev,
if (!wsa884x)
return -ENOMEM;
+ mutex_init(&wsa884x->sp_lock);
+
for (i = 0; i < WSA884X_SUPPLIES_NUM; i++)
wsa884x->supplies[i].supply = wsa884x_supply_name[i];
@@ -1913,6 +2103,13 @@ static int wsa884x_probe(struct sdw_slave *pdev,
regcache_cache_only(wsa884x->regmap, true);
wsa884x->hw_init = true;
+ hwmon = devm_hwmon_device_register_with_info(dev, "wsa884x", wsa884x,
+ &wsa884x_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon))
+ return dev_err_probe(dev, PTR_ERR(hwmon),
+ "Failed to register hwmon sensor\n");
+
pm_runtime_set_autosuspend_delay(dev, 3000);
pm_runtime_use_autosuspend(dev);
pm_runtime_mark_last_busy(dev);
--
2.43.0
1
2
Hi,
I'm reverse engineering and implementing support for the RME Digiface
USB, which is an ADAT interface with a non-class-compliant interface
(probably similar to other RME interfaces like the MADIface, but I don't
have any others to test). The basic audio streaming works fine with an
entry in quirks-table.h and a format quirk to set the system sample rate
in quirks.c. Now I need to figure out how to implement the mixer controls.
Currently I have the snd-usb-audio driver claiming only interface #0
(streaming) and I use a Python script to control the mixer/settings
directly with libusb (control transfers and interface #1). This works
fine and there's some prior art for this in the firewire world (for
example, snd-dice doesn't do any mixer control stuff and you have to use
ffado-mixer to control everything from userspace) but I assume it's not
really the best way to go?
The problem is that the device has a 66x34 matrix mixer, with up to 2048
cross points enabled at once. Exposing each cross point as an ALSA mixer
control (similar to how mixer_scarlett2.c does it) would mean 2244
controls just for the mixer... which seems like a bit too much.
On top of that there is also VU meter feedback for all the
inputs/outputs, as well as general fader controls for each output and
global output configs and status. I'm not sure about the VU meters, but
everything else sounds like it would map fine to normal mixer controls.
Is there some recommended way to expose this kind of matrix mixer
interface to userspace? I think for something like this you pretty much
have to rely on device-specific tools to make the UX manageable, so
maybe hwdep... but at least exposing everything as an ALSA control would
have the advantage of supporting save/restore with something like
alsactl. So I don't really know what's the way to go here.
System settings/general status/output faders go via control transfers,
while interface #1 has an interrupt IN endpoint (streaming state
feedback, not very useful) and two bulk endpoints (matrix mixer control
out, VU meter data in). There's another pair of bulk endpoints in
interface #2 which I'm guessing are for firmware updates (I haven't
looked at that part). So in principle it's not crazy to expose all the
system controls/output faders as mixer controls in ALSA and leave
interface #1 entirely unclaimed so a userspace program can directly
configure the matrix mixer and access VU meter levels. There is a global
mixer enable bit (controlled via ctl transfer), so if that is exposed as
an ALSA control and disabled by default the interface will operate as a
1:1 in/out interface without needing any custom userspace to configure
the mixer.
There's one other quirky thing: it also needs a way to set the sample
rate as a mixer control, because you need to be able to configure the
rate even when the PCM device is not open (since that affects
stand-alone mixer operation). I imagine the right logic here would be to
have a selector control for the system sample rate, and automatically
change it and lock it when the PCM is opened with a given rate?
Any thoughts welcome ^^
~~ Lina
6
8
This is the initial i2s-based amplifier driver for rt1318.
Signed-off-by: Jack Yu <jack.yu(a)realtek.com>
---
include/sound/rt1318.h | 16 +
sound/soc/codecs/Kconfig | 5 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/rt1318.c | 1354 +++++++++++++++++++++++++++++++++++++
sound/soc/codecs/rt1318.h | 342 ++++++++++
5 files changed, 1719 insertions(+)
create mode 100644 include/sound/rt1318.h
create mode 100644 sound/soc/codecs/rt1318.c
create mode 100644 sound/soc/codecs/rt1318.h
diff --git a/include/sound/rt1318.h b/include/sound/rt1318.h
new file mode 100644
index 000000000000..fe6bff06036c
--- /dev/null
+++ b/include/sound/rt1318.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * linux/sound/rt1318.h -- Platform data for RT1318
+ *
+ * Copyright 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __LINUX_SND_RT1318_H
+#define __LINUX_SND_RT1318_H
+
+struct rt1318_platform_data {
+ unsigned int init_r0_l;
+ unsigned int init_r0_r;
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 7b99556f24d3..a99cb26eb280 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -221,6 +221,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT722_SDCA_SDW
imply SND_SOC_RT1308_SDW
imply SND_SOC_RT1316_SDW
+ imply SND_SOC_RT1318
imply SND_SOC_RT1318_SDW
imply SND_SOC_RT1320_SDW
imply SND_SOC_RT9120
@@ -1576,6 +1577,10 @@ config SND_SOC_RT1316_SDW
depends on SOUNDWIRE
select REGMAP_SOUNDWIRE
+config SND_SOC_RT1318
+ tristate
+ depends on I2C
+
config SND_SOC_RT1318_SDW
tristate "Realtek RT1318 Codec - SDW"
depends on SOUNDWIRE
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index ca69f35cc0f7..ad0f77224c74 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -222,6 +222,7 @@ snd-soc-rt1305-y := rt1305.o
snd-soc-rt1308-y := rt1308.o
snd-soc-rt1308-sdw-y := rt1308-sdw.o
snd-soc-rt1316-sdw-y := rt1316-sdw.o
+snd-soc-rt1318-y := rt1318.o
snd-soc-rt1318-sdw-y := rt1318-sdw.o
snd-soc-rt1320-sdw-y := rt1320-sdw.o
snd-soc-rt274-y := rt274.o
@@ -618,6 +619,7 @@ obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o
obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o
obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o
obj-$(CONFIG_SND_SOC_RT1316_SDW) += snd-soc-rt1316-sdw.o
+obj-$(CONFIG_SND_SOC_RT1318) += snd-soc-rt1318.o
obj-$(CONFIG_SND_SOC_RT1318_SDW) += snd-soc-rt1318-sdw.o
obj-$(CONFIG_SND_SOC_RT1320_SDW) += snd-soc-rt1320-sdw.o
obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o
diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c
new file mode 100644
index 000000000000..4e47db4fc7fb
--- /dev/null
+++ b/sound/soc/codecs/rt1318.c
@@ -0,0 +1,1354 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1318.c -- RT1318 ALSA SoC audio amplifier driver
+// Author: Jack Yu <jack.yu(a)realtek.com>
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/acpi.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/gpio.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 <sound/rt1318.h>
+
+#include "rt1318.h"
+
+static struct reg_sequence init_list[] = {
+ { 0x0000C000, 0x01},
+ { 0x0000F20D, 0x00},
+ { 0x0000F212, 0x3E},
+ { 0x0000C001, 0x02},
+ { 0x0000C003, 0x22},
+ { 0x0000C004, 0x44},
+ { 0x0000C005, 0x44},
+ { 0x0000C007, 0x64},
+ { 0x0000C00E, 0xE7},
+ { 0x0000F223, 0x7F},
+ { 0x0000F224, 0xDB},
+ { 0x0000F225, 0xEE},
+ { 0x0000F226, 0x3F},
+ { 0x0000F227, 0x0F},
+ { 0x0000F21A, 0x78},
+ { 0x0000F242, 0x3C},
+ { 0x0000C120, 0x40},
+ { 0x0000C125, 0x03},
+ { 0x0000C321, 0x0A},
+ { 0x0000C200, 0xD8},
+ { 0x0000C201, 0x27},
+ { 0x0000C202, 0x0F},
+ { 0x0000C400, 0x0E},
+ { 0x0000C401, 0x43},
+ { 0x0000C402, 0xE0},
+ { 0x0000C403, 0x00},
+ { 0x0000C404, 0x4C},
+ { 0x0000C406, 0x40},
+ { 0x0000C407, 0x02},
+ { 0x0000C408, 0x3F},
+ { 0x0000C300, 0x01},
+ { 0x0000C125, 0x03},
+ { 0x0000DF00, 0x10},
+ { 0x0000F20B, 0x2A},
+ { 0x0000DF5F, 0x01},
+ { 0x0000DF60, 0xA7},
+ { 0x0000C203, 0x84},
+ { 0x0000C206, 0x78},
+ { 0x0000F10A, 0x09},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x03},
+ { 0x0000F109, 0xE0},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x04},
+ { 0x0000F109, 0x65},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x02},
+ { 0x0000F109, 0x30},
+ { 0x0000F10B, 0x5C},
+ { 0x0000E706, 0x0F},
+ { 0x0000E707, 0x30},
+ { 0x0000E806, 0x0F},
+ { 0x0000E807, 0x30},
+ { 0x0000CE04, 0x03},
+ { 0x0000CE05, 0x5F},
+ { 0x0000CE06, 0xA2},
+ { 0x0000CE07, 0x6B},
+ { 0x0000CF04, 0x03},
+ { 0x0000CF05, 0x5F},
+ { 0x0000CF06, 0xA2},
+ { 0x0000CF07, 0x6B},
+ { 0x0000CE60, 0xE3},
+ { 0x0000C130, 0x51},
+ { 0x0000E000, 0xA8},
+ { 0x0000F102, 0x00},
+ { 0x0000F103, 0x00},
+ { 0x0000F104, 0xF5},
+ { 0x0000F105, 0x23},
+ { 0x0000F109, 0x04},
+ { 0x0000F10A, 0x0B},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F10B, 0x5C},
+ { 0x41001888, 0x00},
+ { 0x0000C121, 0x0B},
+ { 0x0000F102, 0x00},
+ { 0x0000F103, 0x00},
+ { 0x0000F104, 0xF5},
+ { 0x0000F105, 0x23},
+ { 0x0000F109, 0x00},
+ { 0x0000F10A, 0x0B},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F800, 0x20},
+ { 0x0000CA00, 0x80},
+ { 0x0000CA10, 0x00},
+ { 0x0000CA02, 0x78},
+ { 0x0000CA12, 0x78},
+ { 0x0000ED00, 0x90},
+ { 0x0000E604, 0x00},
+ { 0x0000DB00, 0x0C},
+ { 0x0000DD00, 0x0C},
+ { 0x0000DC19, 0x00},
+ { 0x0000DC1A, 0x6A},
+ { 0x0000DC1B, 0xAA},
+ { 0x0000DC1C, 0xAB},
+ { 0x0000DC1D, 0x00},
+ { 0x0000DC1E, 0x16},
+ { 0x0000DC1F, 0xDB},
+ { 0x0000DC20, 0x6D},
+ { 0x0000DE19, 0x00},
+ { 0x0000DE1A, 0x6A},
+ { 0x0000DE1B, 0xAA},
+ { 0x0000DE1C, 0xAB},
+ { 0x0000DE1D, 0x00},
+ { 0x0000DE1E, 0x16},
+ { 0x0000DE1F, 0xDB},
+ { 0x0000DE20, 0x6D},
+ { 0x0000DB32, 0x00},
+ { 0x0000DD32, 0x00},
+ { 0x0000DB33, 0x0A},
+ { 0x0000DD33, 0x0A},
+ { 0x0000DB34, 0x1A},
+ { 0x0000DD34, 0x1A},
+ { 0x0000DB15, 0xEF},
+ { 0x0000DD15, 0xEF},
+ { 0x0000DB17, 0xEF},
+ { 0x0000DD17, 0xEF},
+ { 0x0000DB94, 0x70},
+ { 0x0000DD94, 0x70},
+ { 0x0000DB19, 0x40},
+ { 0x0000DD19, 0x40},
+ { 0x0000DB12, 0xC0},
+ { 0x0000DD12, 0xC0},
+ { 0x0000DB00, 0x4C},
+ { 0x0000DB04, 0x05},
+ { 0x0000DB05, 0x03},
+ { 0x0000DD04, 0x05},
+ { 0x0000DD05, 0x03},
+ { 0x0000DBBB, 0x09},
+ { 0x0000DBBC, 0x30},
+ { 0x0000DBBD, 0xF0},
+ { 0x0000DBBE, 0xF1},
+ { 0x0000DDBB, 0x09},
+ { 0x0000DDBC, 0x30},
+ { 0x0000DDBD, 0xF0},
+ { 0x0000DDBE, 0xF1},
+ { 0x0000DB01, 0x79},
+ { 0x0000DD01, 0x79},
+ { 0x0000DB08, 0x40},
+ { 0x0000DD08, 0x40},
+ { 0x0000DC52, 0xEF},
+ { 0x0000DE52, 0xEF},
+ { 0x0000DB00, 0xCC},
+ { 0x0000CC2C, 0x00},
+ { 0x0000CC2D, 0x2A},
+ { 0x0000CC2E, 0x83},
+ { 0x0000CC2F, 0xA8},
+ { 0x0000CD2C, 0x00},
+ { 0x0000CD2D, 0x2A},
+ { 0x0000CD2E, 0x83},
+ { 0x0000CD2F, 0xA8},
+ { 0x0000CC24, 0x00},
+ { 0x0000CC25, 0x51},
+ { 0x0000CC26, 0xEB},
+ { 0x0000CC27, 0x85},
+ { 0x0000CD24, 0x00},
+ { 0x0000CD25, 0x51},
+ { 0x0000CD26, 0xEB},
+ { 0x0000CD27, 0x85},
+ { 0x0000CC20, 0x00},
+ { 0x0000CC21, 0x00},
+ { 0x0000CC22, 0x43},
+ { 0x0000CD20, 0x00},
+ { 0x0000CD21, 0x00},
+ { 0x0000CD22, 0x43},
+ { 0x0000CC16, 0x0F},
+ { 0x0000CC17, 0x00},
+ { 0x0000CD16, 0x0F},
+ { 0x0000CD17, 0x00},
+ { 0x0000CC29, 0x5D},
+ { 0x0000CC2A, 0xC0},
+ { 0x0000CD29, 0x5D},
+ { 0x0000CD2A, 0xC0},
+ { 0x0000CC31, 0x20},
+ { 0x0000CC32, 0x00},
+ { 0x0000CC33, 0x00},
+ { 0x0000CC34, 0x00},
+ { 0x0000CD31, 0x20},
+ { 0x0000CD32, 0x00},
+ { 0x0000CD33, 0x00},
+ { 0x0000CD34, 0x00},
+ { 0x0000CC36, 0x79},
+ { 0x0000CC37, 0x99},
+ { 0x0000CC38, 0x99},
+ { 0x0000CC39, 0x99},
+ { 0x0000CD36, 0x79},
+ { 0x0000CD37, 0x99},
+ { 0x0000CD38, 0x99},
+ { 0x0000CD39, 0x99},
+ { 0x0000CC09, 0x00},
+ { 0x0000CC0A, 0x07},
+ { 0x0000CC0B, 0x5F},
+ { 0x0000CC0C, 0x6F},
+ { 0x0000CD09, 0x00},
+ { 0x0000CD0A, 0x07},
+ { 0x0000CD0B, 0x5F},
+ { 0x0000CD0C, 0x6F},
+ { 0x0000CC0E, 0x00},
+ { 0x0000CC0F, 0x03},
+ { 0x0000CC10, 0xAF},
+ { 0x0000CC11, 0xB7},
+ { 0x0000CD0E, 0x00},
+ { 0x0000CD0F, 0x03},
+ { 0x0000CD10, 0xAF},
+ { 0x0000CD11, 0xB7},
+ { 0x0000CCD6, 0x00},
+ { 0x0000CCD7, 0x03},
+ { 0x0000CDD6, 0x00},
+ { 0x0000CDD7, 0x03},
+ { 0x0000CCD8, 0x00},
+ { 0x0000CCD9, 0x03},
+ { 0x0000CDD8, 0x00},
+ { 0x0000CDD9, 0x03},
+ { 0x0000CCDA, 0x00},
+ { 0x0000CCDB, 0x03},
+ { 0x0000CDDA, 0x00},
+ { 0x0000CDDB, 0x03},
+ { 0x0000C320, 0x20},
+ { 0x0000C203, 0x9C},
+};
+#define rt1318_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+static const struct reg_default rt1318_reg[] = {
+ { 0xc000, 0x00 },
+ { 0xc001, 0x43 },
+ { 0xc003, 0x22 },
+ { 0xc004, 0x44 },
+ { 0xc005, 0x44 },
+ { 0xc006, 0x33 },
+ { 0xc007, 0x64 },
+ { 0xc008, 0x05 },
+ { 0xc00a, 0xfc },
+ { 0xc00b, 0x0f },
+ { 0xc00c, 0x0e },
+ { 0xc00d, 0xef },
+ { 0xc00e, 0xe5 },
+ { 0xc00f, 0xff },
+ { 0xc120, 0xc0 },
+ { 0xc121, 0x00 },
+ { 0xc122, 0x00 },
+ { 0xc123, 0x14 },
+ { 0xc125, 0x00 },
+ { 0xc130, 0x59 },
+ { 0xc200, 0x00 },
+ { 0xc201, 0x00 },
+ { 0xc202, 0x00 },
+ { 0xc203, 0x04 },
+ { 0xc204, 0x00 },
+ { 0xc205, 0x00 },
+ { 0xc206, 0x68 },
+ { 0xc207, 0x70 },
+ { 0xc208, 0x00 },
+ { 0xc20a, 0x00 },
+ { 0xc20b, 0x01 },
+ { 0xc20c, 0x7f },
+ { 0xc20d, 0x01 },
+ { 0xc20e, 0x7f },
+ { 0xc300, 0x00 },
+ { 0xc301, 0x00 },
+ { 0xc303, 0x80 },
+ { 0xc320, 0x00 },
+ { 0xc321, 0x09 },
+ { 0xc322, 0x02 },
+ { 0xc400, 0x00 },
+ { 0xc401, 0x00 },
+ { 0xc402, 0x00 },
+ { 0xc403, 0x00 },
+ { 0xc404, 0x00 },
+ { 0xc405, 0x00 },
+ { 0xc406, 0x00 },
+ { 0xc407, 0x00 },
+ { 0xc408, 0x00 },
+ { 0xc410, 0x04 },
+ { 0xc430, 0x00 },
+ { 0xc431, 0x00 },
+ { 0xca00, 0x10 },
+ { 0xca01, 0x00 },
+ { 0xca02, 0x0b },
+ { 0xca10, 0x10 },
+ { 0xca11, 0x00 },
+ { 0xca12, 0x0b },
+ { 0xce04, 0x08 },
+ { 0xce05, 0x00 },
+ { 0xce06, 0x00 },
+ { 0xce07, 0x00 },
+ { 0xce60, 0x63 },
+ { 0xcf04, 0x08 },
+ { 0xcf05, 0x00 },
+ { 0xcf06, 0x00 },
+ { 0xcf07, 0x00 },
+ { 0xdb00, 0x00 },
+ { 0xdb08, 0x40 },
+ { 0xdb12, 0x00 },
+ { 0xdb35, 0x00 },
+ { 0xdbb5, 0x00 },
+ { 0xdbb6, 0x40 },
+ { 0xdbb7, 0x00 },
+ { 0xdbb8, 0x00 },
+ { 0xdbc5, 0x00 },
+ { 0xdbc6, 0x00 },
+ { 0xdbc7, 0x00 },
+ { 0xdbc8, 0x00 },
+ { 0xdd08, 0x40 },
+ { 0xdd12, 0x00 },
+ { 0xdd35, 0x00 },
+ { 0xddb5, 0x00 },
+ { 0xddb6, 0x40 },
+ { 0xddb7, 0x00 },
+ { 0xddb8, 0x00 },
+ { 0xddc5, 0x00 },
+ { 0xddc6, 0x00 },
+ { 0xddc7, 0x00 },
+ { 0xddc8, 0x00 },
+ { 0xdd93, 0x00 },
+ { 0xdd94, 0x64 },
+ { 0xdf00, 0x00 },
+ { 0xdf5f, 0x00 },
+ { 0xdf60, 0x00 },
+ { 0xe000, 0x08 },
+ { 0xe300, 0xa0 },
+ { 0xe400, 0x22 },
+ { 0xe706, 0x2f },
+ { 0xe707, 0x2f },
+ { 0xe806, 0x2f },
+ { 0xe807, 0x2f },
+ { 0xea00, 0x43 },
+ { 0xed00, 0x80 },
+ { 0xed01, 0x0f },
+ { 0xed02, 0xff },
+ { 0xed03, 0x00 },
+ { 0xed04, 0x00 },
+ { 0xed05, 0x0f },
+ { 0xed06, 0xff },
+ { 0xf010, 0x10 },
+ { 0xf011, 0xec },
+ { 0xf012, 0x68 },
+ { 0xf013, 0x21 },
+ { 0xf102, 0x00 },
+ { 0xf103, 0x00 },
+ { 0xf104, 0x00 },
+ { 0xf105, 0x00 },
+ { 0xf106, 0x00 },
+ { 0xf107, 0x00 },
+ { 0xf108, 0x00 },
+ { 0xf109, 0x00 },
+ { 0xf10a, 0x03 },
+ { 0xf10b, 0x40 },
+ { 0xf20b, 0x28 },
+ { 0xf20d, 0x00 },
+ { 0xf212, 0x00 },
+ { 0xf21a, 0x00 },
+ { 0xf223, 0x40 },
+ { 0xf224, 0x00 },
+ { 0xf225, 0x00 },
+ { 0xf226, 0x00 },
+ { 0xf227, 0x00 },
+ { 0xf242, 0x0c },
+ { 0xf800, 0x00 },
+ { 0xf801, 0x12 },
+ { 0xf802, 0xe0 },
+ { 0xf803, 0x2f },
+ { 0xf804, 0x00 },
+ { 0xf805, 0x00 },
+ { 0xf806, 0x07 },
+ { 0xf807, 0xff },
+};
+
+static bool rt1318_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000:
+ case 0xc301:
+ case 0xc410:
+ case 0xc430 ... 0xc431:
+ case 0xdb06:
+ case 0xdb12:
+ case 0xdb1d ... 0xdb1f:
+ case 0xdb35:
+ case 0xdb37:
+ case 0xdb8a ... 0xdb92:
+ case 0xdbc5 ... 0xdbc8:
+ case 0xdc2b ... 0xdc49:
+ case 0xdd0b:
+ case 0xdd12:
+ case 0xdd1d ... 0xdd1f:
+ case 0xdd35:
+ case 0xdd8a ... 0xdd92:
+ case 0xddc5 ... 0xddc8:
+ case 0xde2b ... 0xde44:
+ case 0xdf4a ... 0xdf55:
+ case 0xe224 ... 0xe23b:
+ case 0xea01:
+ case 0xebc5:
+ case 0xebc8:
+ case 0xebcb ... 0xebcc:
+ case 0xed03 ... 0xed06:
+ case 0xf010 ... 0xf014:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1318_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000 ... 0xc00f:
+ case 0xc120 ... 0xc130:
+ case 0xc200 ... 0xc20e:
+ case 0xc300 ... 0xc303:
+ case 0xc320 ... 0xc322:
+ case 0xc400 ... 0xc408:
+ case 0xc430 ... 0xc431:
+ case 0xca00 ... 0xca02:
+ case 0xca10 ... 0xca12:
+ case 0xcb00 ... 0xcb0b:
+ case 0xcc00 ... 0xcce5:
+ case 0xcd00 ... 0xcde5:
+ case 0xce00 ... 0xce6a:
+ case 0xcf00 ... 0xcf53:
+ case 0xd000 ... 0xd0cc:
+ case 0xd100 ... 0xd1b9:
+ case 0xdb00 ... 0xdc53:
+ case 0xdd00 ... 0xde53:
+ case 0xdf00 ... 0xdf6b:
+ case 0xe000:
+ case 0xe300:
+ case 0xe400:
+ case 0xe706 ... 0xe707:
+ case 0xe806 ... 0xe807:
+ case 0xea00:
+ case 0xeb00 ... 0xebcc:
+ case 0xec00 ... 0xecb9:
+ case 0xed00 ... 0xed06:
+ case 0xf010 ... 0xf014:
+ case 0xf102 ... 0xf10b:
+ case 0xf20b:
+ case 0xf20d ... 0xf242:
+ case 0xf800 ... 0xf807:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt1318_dac_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);
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1,
+ RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_HIGH);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1,
+ RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_LOW);
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rt1318_dvol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ rt1318->rt1318_dvol = ucontrol->value.integer.value[0];
+
+ if (rt1318->rt1318_dvol <= RT1318_DVOL_STEP && rt1318->rt1318_dvol >= 0) {
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_L_8,
+ rt1318->rt1318_dvol >> 8);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_L_1_7,
+ rt1318->rt1318_dvol & 0xff);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_R_8,
+ rt1318->rt1318_dvol >> 8);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_R_1_7,
+ rt1318->rt1318_dvol & 0xff);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rt1318_dvol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1318->rt1318_dvol;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt1318_snd_controls[] = {
+ SOC_SINGLE_EXT("Amp Playback Volume", SND_SOC_NOPM, 0, 383, 0,
+ rt1318_dvol_get, rt1318_dvol_put),
+};
+
+static const struct snd_soc_dapm_widget rt1318_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ /* DACs */
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
+ rt1318_dac_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("Amp"),
+};
+
+static const struct snd_soc_dapm_route rt1318_dapm_routes[] = {
+ {"DAC", NULL, "AIF1RX"},
+ {"Amp", NULL, "DAC"},
+};
+
+static int rt1318_get_clk_info(int sclk, int rate)
+{
+ int i, pd[] = {1, 2, 4, 8, 16, 24};
+
+ 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 rt1318_clk_ip_info(struct snd_soc_component *component, int lrclk)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ switch (lrclk) {
+ case RT1318_LRCLK_48000:
+ case RT1318_LRCLK_44100:
+ case RT1318_LRCLK_16000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON4);
+ break;
+ case RT1318_LRCLK_96000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON2);
+ break;
+ case RT1318_LRCLK_192000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON1);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported clock rate.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt1318_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 rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ int data_len = 0, ch_len = 0;
+ int pre_div, ret;
+
+ rt1318->lrck = params_rate(params);
+ pre_div = rt1318_get_clk_info(rt1318->sysclk, rt1318->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+ ret = rt1318_clk_ip_info(component, rt1318->lrck);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ data_len = RT1318_I2S_DL_20;
+ ch_len = RT1318_I2S_DL_20;
+ break;
+ case 24:
+ data_len = RT1318_I2S_DL_24;
+ ch_len = RT1318_I2S_DL_24;
+ break;
+ case 32:
+ data_len = RT1318_I2S_DL_32;
+ ch_len = RT1318_I2S_DL_32;
+ break;
+ case 8:
+ data_len = RT1318_I2S_DL_8;
+ ch_len = RT1318_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_CLK2,
+ RT1318_DIV_AP_MASK | RT1318_DIV_DAMOD_MASK,
+ pre_div << RT1318_DIV_AP_SFT |
+ pre_div << RT1318_DIV_DAMOD_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK3,
+ RT1318_AD_STO1_MASK | RT1318_AD_STO2_MASK,
+ pre_div << RT1318_AD_STO1_SFT |
+ pre_div << RT1318_AD_STO2_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK4,
+ RT1318_AD_ANA_STO1_MASK | RT1318_AD_ANA_STO2_MASK,
+ pre_div << RT1318_AD_ANA_STO1_SFT |
+ pre_div << RT1318_AD_ANA_STO2_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK5,
+ RT1318_DIV_FIFO_IN_MASK | RT1318_DIV_FIFO_OUT_MASK,
+ pre_div << RT1318_DIV_FIFO_IN_SFT |
+ pre_div << RT1318_DIV_FIFO_OUT_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK6,
+ RT1318_DIV_NLMS_MASK | RT1318_DIV_AD_MONO_MASK |
+ RT1318_DIV_POST_G_MASK, pre_div << RT1318_DIV_NLMS_SFT |
+ pre_div << RT1318_DIV_AD_MONO_SFT |
+ pre_div << RT1318_DIV_POST_G_SFT);
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2,
+ RT1318_I2S_DL_MASK, data_len << RT1318_I2S_DL_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3,
+ RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK,
+ ch_len << RT1318_I2S_TX_CHL_SFT |
+ ch_len << RT1318_I2S_RX_CHL_SFT);
+
+ return 0;
+}
+
+static int rt1318_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ unsigned int reg_val = 0, reg_val2 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val2 |= RT1318_TDM_BCLK_INV;
+ 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 |= RT1318_FMT_LEFT_J;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1318_FMT_PCM_A_R;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1318_FMT_PCM_B_R;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1,
+ RT1318_I2S_FMT_MASK, reg_val);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1,
+ RT1318_TDM_BCLK_MASK, reg_val2);
+
+ return 0;
+}
+
+static int rt1318_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ int reg_val = 0;
+
+ if (freq == rt1318->sysclk && clk_id == rt1318->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1318_SCLK_S_BCLK:
+ reg_val |= RT1318_SYSCLK_BCLK;
+ break;
+ case RT1318_SCLK_S_SDW:
+ reg_val |= RT1318_SYSCLK_SDW;
+ break;
+ case RT1318_SCLK_S_PLL2F:
+ reg_val |= RT1318_SYSCLK_PLL2F;
+ break;
+ case RT1318_SCLK_S_PLL2B:
+ reg_val |= RT1318_SYSCLK_PLL2B;
+ break;
+ case RT1318_SCLK_S_MCLK:
+ reg_val |= RT1318_SYSCLK_MCLK;
+ break;
+ case RT1318_SCLK_S_RC0:
+ reg_val |= RT1318_SYSCLK_RC1;
+ break;
+ case RT1318_SCLK_S_RC1:
+ reg_val |= RT1318_SYSCLK_RC2;
+ break;
+ case RT1318_SCLK_S_RC2:
+ reg_val |= RT1318_SYSCLK_RC3;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ rt1318->sysclk = freq;
+ rt1318->sysclk_src = clk_id;
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_SYSCLK_SEL_MASK, reg_val);
+
+ return 0;
+}
+
+static const struct pll_calc_map pll_preset_table[] = {
+ {512000, 4096000, 22, 190, 0, true, false},
+ {1024000, 4096000, 22, 94, 0, true, false},
+ {1024000, 16384000, 4, 190, 0, true, false},
+ {1411200, 11289600, 6, 62, 0, true, false},
+ {1536000, 12288000, 6, 62, 0, true, false},
+ {2822400, 11289600, 6, 62, 0, true, false},
+ {2822400, 45158400, 0, 62, 0, true, false},
+ {2822400, 49152000, 0, 62, 0, true, false},
+ {3072000, 12288000, 6, 62, 0, true, false},
+ {3072000, 24576000, 2, 62, 0, true, false},
+ {3072000, 49152000, 0, 62, 0, true, false},
+ {6144000, 24576000, 2, 94, 4, false, false},
+ {6144000, 49152000, 0, 30, 0, true, false},
+ {6144000, 98304000, 0, 94, 4, false, true},
+ {12288000, 49152000, 0, 62, 6, false, false},
+};
+
+static int rt1318_pll_calc(const unsigned int freq_in,
+ const unsigned int freq_out, struct rt1318_pll_code *pll_code)
+{
+ int max_n = RT1318_PLL_N_MAX, max_m = RT1318_PLL_M_MAX;
+ int i, k, red, n_t, pll_out, in_t, out_t;
+ int n = 0, m = 0, m_t = 0;
+ int red_t = abs(freq_out - freq_in);
+ bool m_bypass = false, k_bypass = false;
+
+ if (RT1318_PLL_INP_MAX < freq_in || RT1318_PLL_INP_MIN > freq_in)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) {
+ if (freq_in == pll_preset_table[i].pll_in &&
+ freq_out == pll_preset_table[i].pll_out) {
+ k = pll_preset_table[i].k;
+ m = pll_preset_table[i].m;
+ n = pll_preset_table[i].n;
+ m_bypass = pll_preset_table[i].m_bp;
+ k_bypass = pll_preset_table[i].k_bp;
+ goto code_find;
+ }
+ }
+
+ k = 100000000 / freq_out - 2;
+ if (k > RT1318_PLL_K_MAX)
+ k = RT1318_PLL_K_MAX;
+ if (k < 0) {
+ k = 0;
+ k_bypass = true;
+ }
+ for (n_t = 0; n_t <= max_n; n_t++) {
+ in_t = freq_in / (k_bypass ? 1 : (k + 2));
+ pll_out = freq_out / (n_t + 2);
+ if (in_t < 0)
+ continue;
+ if (in_t == pll_out) {
+ m_bypass = true;
+ n = n_t;
+ goto code_find;
+ }
+ red = abs(in_t - pll_out);
+ if (red < red_t) {
+ m_bypass = true;
+ n = n_t;
+ m = m_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ for (m_t = 0; m_t <= max_m; m_t++) {
+ out_t = in_t / (m_t + 2);
+ red = abs(out_t - pll_out);
+ if (red < red_t) {
+ m_bypass = false;
+ n = n_t;
+ m = m_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ }
+ }
+ pr_debug("Only get approximation about PLL\n");
+
+code_find:
+
+ pll_code->m_bp = m_bypass;
+ pll_code->k_bp = k_bypass;
+ pll_code->m_code = m;
+ pll_code->n_code = n;
+ pll_code->k_code = k;
+ return 0;
+}
+
+static int rt1318_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ struct rt1318_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+ rt1318->pll_in = 0;
+ rt1318->pll_out = 0;
+ return 0;
+ }
+
+ if (source == rt1318->pll_src && freq_in == rt1318->pll_in &&
+ freq_out == rt1318->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT1318_PLL_S_BCLK0:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK0);
+ break;
+ case RT1318_PLL_S_BCLK1:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK1);
+ break;
+ case RT1318_PLL_S_RC:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_RC);
+ break;
+ case RT1318_PLL_S_MCLK:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_MCLK);
+ break;
+ case RT1318_PLL_S_SDW_IN_PLL:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW1);
+ break;
+ case RT1318_PLL_S_SDW_0:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW2);
+ break;
+ case RT1318_PLL_S_SDW_1:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW3);
+ break;
+ case RT1318_PLL_S_SDW_2:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW4);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rt1318_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);
+
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_K,
+ RT1318_K_PLL1_MASK, pll_code.k_code);
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_M,
+ RT1318_M_PLL1_MASK, (pll_code.m_bp ? 0 : pll_code.m_code));
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_8,
+ RT1318_N_8_PLL1_MASK, pll_code.n_code >> 8);
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_7_0,
+ RT1318_N_7_0_PLL1_MASK, pll_code.n_code);
+
+ rt1318->pll_in = freq_in;
+ rt1318->pll_out = freq_out;
+ rt1318->pll_src = source;
+
+ return 0;
+}
+
+static int rt1318_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ unsigned int cn = 0, cl = 0, rx_slotnum;
+ int ret = 0, first_bit;
+
+ switch (slots) {
+ case 4:
+ cn |= RT1318_I2S_CH_TX_4CH;
+ cn |= RT1318_I2S_CH_RX_4CH;
+ break;
+ case 6:
+ cn |= RT1318_I2S_CH_TX_6CH;
+ cn |= RT1318_I2S_CH_RX_6CH;
+ break;
+ case 8:
+ cn |= RT1318_I2S_CH_TX_8CH;
+ cn |= RT1318_I2S_CH_RX_8CH;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ cl |= RT1318_I2S_TX_CHL_20;
+ cl |= RT1318_I2S_RX_CHL_20;
+ break;
+ case 24:
+ cl |= RT1318_I2S_TX_CHL_24;
+ cl |= RT1318_I2S_RX_CHL_24;
+ break;
+ case 32:
+ cl |= RT1318_I2S_TX_CHL_32;
+ cl |= RT1318_I2S_RX_CHL_32;
+ break;
+ case 8:
+ cl |= RT1318_I2S_TX_CHL_8;
+ cl |= RT1318_I2S_RX_CHL_8;
+ break;
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Rx slot configuration */
+ rx_slotnum = hweight_long(rx_mask);
+ if (rx_slotnum != 1) {
+ ret = -EINVAL;
+ dev_err(component->dev, "too many rx slots or zero slot\n");
+ goto _set_tdm_err_;
+ }
+
+ first_bit = __ffs(rx_mask);
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ regmap_update_bits(rt1318->regmap,
+ RT1318_TDM_CTRL9,
+ RT1318_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1318_TDM_I2S_TX_R_DAC1_1_MASK,
+ (first_bit << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) |
+ ((first_bit + 1) << RT1318_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ regmap_update_bits(rt1318->regmap,
+ RT1318_TDM_CTRL9,
+ RT1318_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1318_TDM_I2S_TX_R_DAC1_1_MASK,
+ ((first_bit - 1) << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) |
+ (first_bit << RT1318_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2,
+ RT1318_I2S_CH_TX_MASK | RT1318_I2S_CH_RX_MASK, cn);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3,
+ RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK, cl);
+
+_set_tdm_err_:
+ return ret;
+}
+
+static int rt1318_probe(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ rt1318->component = component;
+
+ schedule_work(&rt1318->cali_work);
+ rt1318->rt1318_dvol = RT1318_DVOL_STEP;
+
+ return 0;
+}
+
+static void rt1318_remove(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ cancel_work_sync(&rt1318->cali_work);
+}
+
+#ifdef CONFIG_PM
+static int rt1318_suspend(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1318->regmap, true);
+ regcache_mark_dirty(rt1318->regmap);
+ return 0;
+}
+
+static int rt1318_resume(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1318->regmap, false);
+ regcache_sync(rt1318->regmap);
+ return 0;
+}
+#else
+#define rt1318_suspend NULL
+#define rt1318_resume NULL
+#endif
+
+#define RT1318_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1318_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt1318_aif_dai_ops = {
+ .hw_params = rt1318_hw_params,
+ .set_fmt = rt1318_set_dai_fmt,
+ .set_sysclk = rt1318_set_dai_sysclk,
+ .set_pll = rt1318_set_dai_pll,
+ .set_tdm_slot = rt1318_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt1318_dai[] = {
+ {
+ .name = "rt1318-aif",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1318_STEREO_RATES,
+ .formats = RT1318_FORMATS,
+ },
+ .ops = &rt1318_aif_dai_ops,
+ }
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1318 = {
+ .probe = rt1318_probe,
+ .remove = rt1318_remove,
+ .suspend = rt1318_suspend,
+ .resume = rt1318_resume,
+ .controls = rt1318_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1318_snd_controls),
+ .dapm_widgets = rt1318_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1318_dapm_widgets),
+ .dapm_routes = rt1318_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1318_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1318_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1318_readable_register,
+ .volatile_reg = rt1318_volatile_register,
+ .max_register = 0x41001888,
+ .reg_defaults = rt1318_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1318_reg),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct i2c_device_id rt1318_i2c_id[] = {
+ { "rt1318", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1318_i2c_id);
+
+static const struct of_device_id rt1318_of_match[] = {
+ { .compatible = "realtek,rt1318", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt1318_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1318_acpi_match[] = {
+ { "10EC1318", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, rt1318_acpi_match);
+#endif
+
+static int rt1318_parse_dt(struct rt1318_priv *rt1318, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,r0_l",
+ &rt1318->pdata.init_r0_l);
+ device_property_read_u32(dev, "realtek,r0_r",
+ &rt1318->pdata.init_r0_r);
+
+ return 0;
+}
+
+static void rt1318_calibration_sequence(struct rt1318_priv *rt1318)
+{
+ regmap_write(rt1318->regmap, RT1318_CLK1, 0x22);
+ regmap_write(rt1318->regmap, RT1318_PLL1_N_7_0, 0x06);
+ regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xCC);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x40);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x40);
+ regmap_write(rt1318->regmap, RT1318_SINE_GEN0, 0x20);
+ regmap_write(rt1318->regmap, RT1318_SPK_VOL_TH, 0x00);
+ regmap_write(rt1318->regmap, RT1318_FEEDBACK_PATH, 0x0B);
+ regmap_write(rt1318->regmap, RT1318_TCON, 0x1C);
+ regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x58);
+ regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x78);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xC2);
+}
+
+static void rt1318_r0_calculate(struct rt1318_priv *rt1318)
+{
+ unsigned int r0_l, r0_l_byte0, r0_l_byte1, r0_l_byte2, r0_l_byte3;
+ unsigned int r0_r, r0_r_byte0, r0_r_byte1, r0_r_byte2, r0_r_byte3;
+ unsigned int r0_l_integer, r0_l_factor, r0_r_integer, r0_r_factor;
+ unsigned int format = 16777216; /* 2^24 */
+
+ regmap_read(rt1318->regmap, RT1318_R0_L_24, &r0_l_byte0);
+ regmap_read(rt1318->regmap, RT1318_R0_L_23_16, &r0_l_byte1);
+ regmap_read(rt1318->regmap, RT1318_R0_L_15_8, &r0_l_byte2);
+ regmap_read(rt1318->regmap, RT1318_R0_L_7_0, &r0_l_byte3);
+ r0_l = r0_l_byte0 << 24 | r0_l_byte1 << 16 | r0_l_byte2 << 8 | r0_l_byte3;
+ r0_l_integer = format / r0_l;
+ r0_l_factor = (format * 10) / r0_l - r0_l_integer * 10;
+
+ regmap_read(rt1318->regmap, RT1318_R0_R_24, &r0_r_byte0);
+ regmap_read(rt1318->regmap, RT1318_R0_R_23_16, &r0_r_byte1);
+ regmap_read(rt1318->regmap, RT1318_R0_R_15_8, &r0_r_byte2);
+ regmap_read(rt1318->regmap, RT1318_R0_R_7_0, &r0_r_byte3);
+ r0_r = r0_r_byte0 << 24 | r0_r_byte1 << 16 | r0_r_byte2 << 8 | r0_r_byte3;
+ r0_r_integer = format / r0_r;
+ r0_r_factor = (format * 10) / r0_r - r0_r_integer * 10;
+
+ dev_dbg(rt1318->component->dev, "r0_l_ch:%d.%d ohm\n", r0_l_integer, r0_l_factor);
+ dev_dbg(rt1318->component->dev, "r0_r_ch:%d.%d ohm\n", r0_r_integer, r0_r_factor);
+}
+
+static void rt1318_r0_restore(struct rt1318_priv *rt1318)
+{
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_24,
+ (rt1318->pdata.init_r0_l >> 24) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_23_16,
+ (rt1318->pdata.init_r0_l >> 16) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_15_8,
+ (rt1318->pdata.init_r0_l >> 8) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_7_0,
+ (rt1318->pdata.init_r0_l >> 0) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_24,
+ (rt1318->pdata.init_r0_r >> 24) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_23_16,
+ (rt1318->pdata.init_r0_r >> 16) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_15_8,
+ (rt1318->pdata.init_r0_r >> 8) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_7_0,
+ (rt1318->pdata.init_r0_r >> 0) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x80);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x80);
+ regmap_write(rt1318->regmap, RT1318_R0_CMP_L_FLAG, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_R0_CMP_R_FLAG, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_R, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xcc);
+ regmap_write(rt1318->regmap, RT1318_TCON, 0x9c);
+}
+
+static int rt1318_calibrate(struct rt1318_priv *rt1318)
+{
+ int chk_cnt = 30, count = 0;
+ int val, val2;
+
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x1);
+ usleep_range(0, 10000);
+ rt1318_calibration_sequence(rt1318);
+
+ while (count < chk_cnt) {
+ msleep(100);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2);
+ val = (val >> 1) & 0x1;
+ val2 = (val2 >> 1) & 0x1;
+ if (val & val2) {
+ dev_dbg(rt1318->component->dev, "Calibration done.\n");
+ break;
+ }
+ count++;
+ if (count == chk_cnt) {
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0);
+ return RT1318_R0_CALIB_NOT_DONE;
+ }
+ }
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2);
+ if ((val & 0x1) & (val2 & 0x1))
+ return RT1318_R0_IN_RANGE;
+ else
+ return RT1318_R0_OUT_OF_RANGE;
+}
+
+static void rt1318_calibration_work(struct work_struct *work)
+{
+ struct rt1318_priv *rt1318 =
+ container_of(work, struct rt1318_priv, cali_work);
+ int ret;
+
+ if (rt1318->pdata.init_r0_l && rt1318->pdata.init_r0_r)
+ rt1318_r0_restore(rt1318);
+ else {
+ ret = rt1318_calibrate(rt1318);
+ if (ret == RT1318_R0_IN_RANGE)
+ rt1318_r0_calculate(rt1318);
+ dev_dbg(rt1318->component->dev, "Calibrate R0 result:%d\n", ret);
+ }
+}
+
+static int rt1318_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1318_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt1318_priv *rt1318;
+ int ret, val, val2, dev_id;
+
+ rt1318 = devm_kzalloc(&i2c->dev, sizeof(struct rt1318_priv),
+ GFP_KERNEL);
+ if (!rt1318)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1318);
+
+ if (pdata)
+ rt1318->pdata = *pdata;
+ else
+ rt1318_parse_dt(rt1318, &i2c->dev);
+
+ rt1318->regmap = devm_regmap_init_i2c(i2c, &rt1318_regmap);
+ if (IS_ERR(rt1318->regmap)) {
+ ret = PTR_ERR(rt1318->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1318->regmap, RT1318_DEV_ID1, &val);
+ regmap_read(rt1318->regmap, RT1318_DEV_ID2, &val2);
+ dev_id = (val << 8) | val2;
+ if (dev_id != 0x6821) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt1318\n",
+ dev_id);
+ return -ENODEV;
+ }
+
+ ret = regmap_register_patch(rt1318->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ INIT_WORK(&rt1318->cali_work, rt1318_calibration_work);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1318, rt1318_dai, ARRAY_SIZE(rt1318_dai));
+}
+
+static struct i2c_driver rt1318_i2c_driver = {
+ .driver = {
+ .name = "rt1318",
+ .of_match_table = of_match_ptr(rt1318_of_match),
+ .acpi_match_table = ACPI_PTR(rt1318_acpi_match),
+ },
+ .probe = rt1318_i2c_probe,
+ .id_table = rt1318_i2c_id,
+};
+module_i2c_driver(rt1318_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1318 driver");
+MODULE_AUTHOR("Jack Yu <jack.yu(a)realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1318.h b/sound/soc/codecs/rt1318.h
new file mode 100644
index 000000000000..cec40b484216
--- /dev/null
+++ b/sound/soc/codecs/rt1318.h
@@ -0,0 +1,342 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1318.h -- Platform data for RT1318
+ *
+ * Copyright 2024 Realtek Semiconductor Corp.
+ */
+#include <sound/rt1318.h>
+
+#ifndef __RT1318_H__
+#define __RT1318_H__
+
+struct rt1318_priv {
+ struct snd_soc_component *component;
+ struct rt1318_platform_data pdata;
+ struct work_struct cali_work;
+ struct regmap *regmap;
+
+ unsigned int r0_l_integer;
+ unsigned int r0_l_factor;
+ unsigned int r0_r_integer;
+ unsigned int r0_r_factor;
+ int rt1318_init;
+ int rt1318_dvol;
+ int sysclk_src;
+ int sysclk;
+ int lrck;
+ int bclk;
+ int master;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+#define RT1318_PLL_INP_MAX 40000000
+#define RT1318_PLL_INP_MIN 256000
+#define RT1318_PLL_N_MAX 0x1ff
+#define RT1318_PLL_K_MAX 0x1f
+#define RT1318_PLL_M_MAX 0x1f
+
+#define RT1318_LRCLK_192000 192000
+#define RT1318_LRCLK_96000 96000
+#define RT1318_LRCLK_48000 48000
+#define RT1318_LRCLK_44100 44100
+#define RT1318_LRCLK_16000 16000
+#define RT1318_DVOL_STEP 383
+
+#define RT1318_CLK1 0xc001
+#define RT1318_CLK2 0xc003
+#define RT1318_CLK3 0xc004
+#define RT1318_CLK4 0xc005
+#define RT1318_CLK5 0xc006
+#define RT1318_CLK6 0xc007
+#define RT1318_CLK7 0xc008
+#define RT1318_PWR_STA1 0xc121
+#define RT1318_SPK_VOL_TH 0xc130
+#define RT1318_TCON 0xc203
+#define RT1318_SRC_TCON 0xc204
+#define RT1318_TCON_RELATE 0xc206
+#define RT1318_DA_VOL_L_8 0xc20b
+#define RT1318_DA_VOL_L_1_7 0xc20c
+#define RT1318_DA_VOL_R_8 0xc20d
+#define RT1318_DA_VOL_R_1_7 0xc20e
+#define RT1318_FEEDBACK_PATH 0xc321
+#define RT1318_STP_TEMP_L 0xdb00
+#define RT1318_STP_SEL_L 0xdb08
+#define RT1318_STP_R0_EN_L 0xdb12
+#define RT1318_R0_CMP_L_FLAG 0xdb35
+#define RT1318_PRE_R0_L_24 0xdbb5
+#define RT1318_PRE_R0_L_23_16 0xdbb6
+#define RT1318_PRE_R0_L_15_8 0xdbb7
+#define RT1318_PRE_R0_L_7_0 0xdbb8
+#define RT1318_R0_L_24 0xdbc5
+#define RT1318_R0_L_23_16 0xdbc6
+#define RT1318_R0_L_15_8 0xdbc7
+#define RT1318_R0_L_7_0 0xdbc8
+#define RT1318_STP_SEL_R 0xdd08
+#define RT1318_STP_R0_EN_R 0xdd12
+#define RT1318_R0_CMP_R_FLAG 0xdd35
+#define RT1318_PRE_R0_R_24 0xddb5
+#define RT1318_PRE_R0_R_23_16 0xddb6
+#define RT1318_PRE_R0_R_15_8 0xddb7
+#define RT1318_PRE_R0_R_7_0 0xddb8
+#define RT1318_R0_R_24 0xddc5
+#define RT1318_R0_R_23_16 0xddc6
+#define RT1318_R0_R_15_8 0xddc7
+#define RT1318_R0_R_7_0 0xddc8
+#define RT1318_DEV_ID1 0xf012
+#define RT1318_DEV_ID2 0xf013
+#define RT1318_PLL1_K 0xf20d
+#define RT1318_PLL1_M 0xf20f
+#define RT1318_PLL1_N_8 0xf211
+#define RT1318_PLL1_N_7_0 0xf212
+#define RT1318_SINE_GEN0 0xf800
+#define RT1318_TDM_CTRL1 0xf900
+#define RT1318_TDM_CTRL2 0xf901
+#define RT1318_TDM_CTRL3 0xf902
+#define RT1318_TDM_CTRL9 0xf908
+
+
+/* Clock-1 (0xC001) */
+#define RT1318_PLLIN_MASK (0x7 << 4)
+#define RT1318_PLLIN_BCLK0 (0x0 << 4)
+#define RT1318_PLLIN_BCLK1 (0x1 << 4)
+#define RT1318_PLLIN_RC (0x2 << 4)
+#define RT1318_PLLIN_MCLK (0x3 << 4)
+#define RT1318_PLLIN_SDW1 (0x4 << 4)
+#define RT1318_PLLIN_SDW2 (0x5 << 4)
+#define RT1318_PLLIN_SDW3 (0x6 << 4)
+#define RT1318_PLLIN_SDW4 (0x7 << 4)
+#define RT1318_SYSCLK_SEL_MASK (0x7 << 0)
+#define RT1318_SYSCLK_BCLK (0x0 << 0)
+#define RT1318_SYSCLK_SDW (0x1 << 0)
+#define RT1318_SYSCLK_PLL2F (0x2 << 0)
+#define RT1318_SYSCLK_PLL2B (0x3 << 0)
+#define RT1318_SYSCLK_MCLK (0x4 << 0)
+#define RT1318_SYSCLK_RC1 (0x5 << 0)
+#define RT1318_SYSCLK_RC2 (0x6 << 0)
+#define RT1318_SYSCLK_RC3 (0x7 << 0)
+/* Clock-2 (0xC003) */
+#define RT1318_DIV_AP_MASK (0x3 << 4)
+#define RT1318_DIV_AP_SFT 4
+#define RT1318_DIV_AP_DIV1 (0x0 << 4)
+#define RT1318_DIV_AP_DIV2 (0x1 << 4)
+#define RT1318_DIV_AP_DIV4 (0x2 << 4)
+#define RT1318_DIV_AP_DIV8 (0x3 << 4)
+#define RT1318_DIV_DAMOD_MASK (0x3 << 0)
+#define RT1318_DIV_DAMOD_SFT 0
+#define RT1318_DIV_DAMOD_DIV1 (0x0 << 0)
+#define RT1318_DIV_DAMOD_DIV2 (0x1 << 0)
+#define RT1318_DIV_DAMOD_DIV4 (0x2 << 0)
+#define RT1318_DIV_DAMOD_DIV8 (0x3 << 0)
+/* Clock-3 (0xC004) */
+#define RT1318_AD_STO1_MASK (0x7 << 4)
+#define RT1318_AD_STO1_SFT 4
+#define RT1318_AD_STO1_DIV1 (0x0 << 4)
+#define RT1318_AD_STO1_DIV2 (0x1 << 4)
+#define RT1318_AD_STO1_DIV4 (0x2 << 4)
+#define RT1318_AD_STO1_DIV8 (0x3 << 4)
+#define RT1318_AD_STO1_DIV16 (0x4 << 4)
+#define RT1318_AD_STO2_MASK (0x7 << 0)
+#define RT1318_AD_STO2_SFT 0
+#define RT1318_AD_STO2_DIV1 (0x0 << 0)
+#define RT1318_AD_STO2_DIV2 (0x1 << 0)
+#define RT1318_AD_STO2_DIV4 (0x2 << 0)
+#define RT1318_AD_STO2_DIV8 (0x3 << 0)
+#define RT1318_AD_STO2_DIV16 (0x4 << 0)
+#define RT1318_AD_STO2_SFT 0
+/* Clock-4 (0xC005) */
+#define RT1318_AD_ANA_STO1_MASK (0x7 << 4)
+#define RT1318_AD_ANA_STO1_SFT 4
+#define RT1318_AD_ANA_STO1_DIV1 (0x0 << 4)
+#define RT1318_AD_ANA_STO1_DIV2 (0x1 << 4)
+#define RT1318_AD_ANA_STO1_DIV4 (0x2 << 4)
+#define RT1318_AD_ANA_STO1_DIV8 (0x3 << 4)
+#define RT1318_AD_ANA_STO1_DIV16 (0x4 << 4)
+#define RT1318_AD_ANA_STO2_MASK (0x7 << 0)
+#define RT1318_AD_ANA_STO2_DIV1 (0x0 << 0)
+#define RT1318_AD_ANA_STO2_DIV2 (0x1 << 0)
+#define RT1318_AD_ANA_STO2_DIV4 (0x2 << 0)
+#define RT1318_AD_ANA_STO2_DIV8 (0x3 << 0)
+#define RT1318_AD_ANA_STO2_DIV16 (0x4 << 0)
+#define RT1318_AD_ANA_STO2_SFT 0
+/* Clock-5 (0xC006) */
+#define RT1318_DIV_FIFO_IN_MASK (0x3 << 4)
+#define RT1318_DIV_FIFO_IN_SFT 4
+#define RT1318_DIV_FIFO_IN_DIV1 (0x0 << 4)
+#define RT1318_DIV_FIFO_IN_DIV2 (0x1 << 4)
+#define RT1318_DIV_FIFO_IN_DIV4 (0x2 << 4)
+#define RT1318_DIV_FIFO_IN_DIV8 (0x3 << 4)
+#define RT1318_DIV_FIFO_OUT_MASK (0x3 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV1 (0x0 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV2 (0x1 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV4 (0x2 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV8 (0x3 << 0)
+#define RT1318_DIV_FIFO_OUT_SFT 0
+/* Clock-6 (0xC007) */
+#define RT1318_DIV_NLMS_MASK (0x3 << 6)
+#define RT1318_DIV_NLMS_SFT 6
+#define RT1318_DIV_NLMS_DIV1 (0x0 << 6)
+#define RT1318_DIV_NLMS_DIV2 (0x1 << 6)
+#define RT1318_DIV_NLMS_DIV4 (0x2 << 6)
+#define RT1318_DIV_NLMS_DIV8 (0x3 << 6)
+#define RT1318_DIV_AD_MONO_MASK (0x7 << 3)
+#define RT1318_DIV_AD_MONO_SFT 3
+#define RT1318_DIV_AD_MONO_DIV1 (0x0 << 3)
+#define RT1318_DIV_AD_MONO_DIV2 (0x1 << 3)
+#define RT1318_DIV_AD_MONO_DIV4 (0x2 << 3)
+#define RT1318_DIV_AD_MONO_DIV8 (0x3 << 3)
+#define RT1318_DIV_AD_MONO_DIV16 (0x4 << 3)
+#define RT1318_DIV_POST_G_MASK (0x7 << 0)
+#define RT1318_DIV_POST_G_SFT 0
+#define RT1318_DIV_POST_G_DIV1 (0x0 << 0)
+#define RT1318_DIV_POST_G_DIV2 (0x1 << 0)
+#define RT1318_DIV_POST_G_DIV4 (0x2 << 0)
+#define RT1318_DIV_POST_G_DIV8 (0x3 << 0)
+#define RT1318_DIV_POST_G_DIV16 (0x4 << 0)
+/* Power Status 1 (0xC121) */
+#define RT1318_PDB_CTRL_MASK (0x1)
+#define RT1318_PDB_CTRL_LOW (0x0)
+#define RT1318_PDB_CTRL_HIGH (0x1)
+#define RT1318_PDB_CTRL_SFT 0
+/* SRC Tcon(0xc204) */
+#define RT1318_SRCIN_IN_SEL_MASK (0x3 << 6)
+#define RT1318_SRCIN_IN_48K (0x0 << 6)
+#define RT1318_SRCIN_IN_44P1 (0x1 << 6)
+#define RT1318_SRCIN_IN_32K (0x2 << 6)
+#define RT1318_SRCIN_IN_16K (0x3 << 6)
+#define RT1318_SRCIN_F12288_MASK (0x3 << 4)
+#define RT1318_SRCIN_TCON1 (0x0 << 4)
+#define RT1318_SRCIN_TCON2 (0x1 << 4)
+#define RT1318_SRCIN_TCON4 (0x2 << 4)
+#define RT1318_SRCIN_TCON8 (0x3 << 4)
+#define RT1318_SRCIN_DACLK_MASK (0x3 << 2)
+#define RT1318_DACLK_TCON1 (0x0 << 2)
+#define RT1318_DACLK_TCON2 (0x1 << 2)
+#define RT1318_DACLK_TCON4 (0x2 << 2)
+#define RT1318_DACLK_TCON8 (0x3 << 2)
+/* R0 Compare Flag (0xDB35) */
+#define RT1318_R0_RANGE_MASK (0x1)
+#define RT1318_R0_OUTOFRANGE (0x0)
+#define RT1318_R0_INRANGE (0x1)
+/* PLL internal setting (0xF20D), K value */
+#define RT1318_K_PLL1_MASK (0x1f << 0)
+/* PLL internal setting (0xF20F), M value */
+#define RT1318_M_PLL1_MASK (0x1f << 0)
+/* PLL internal setting (0xF211), N_8 value */
+#define RT1318_N_8_PLL1_MASK (0x1 << 0)
+/* PLL internal setting (0xF212), N_7_0 value */
+#define RT1318_N_7_0_PLL1_MASK (0xff << 0)
+/* TDM CTRL 1 (0xf900) */
+#define RT1318_TDM_BCLK_MASK (0x1 << 7)
+#define RT1318_TDM_BCLK_NORM (0x0 << 7)
+#define RT1318_TDM_BCLK_INV (0x1 << 7)
+#define RT1318_I2S_FMT_MASK (0x7 << 0)
+#define RT1318_FMT_I2S (0x0 << 0)
+#define RT1318_FMT_LEFT_J (0x1 << 0)
+#define RT1318_FMT_PCM_A_R (0x2 << 0)
+#define RT1318_FMT_PCM_B_R (0x3 << 0)
+#define RT1318_FMT_PCM_A_F (0x6 << 0)
+#define RT1318_FMT_PCM_B_F (0x7 << 0)
+#define RT1318_I2S_FMT_SFT 0
+/* TDM CTRL 2 (0xf901) */
+#define RT1318_I2S_CH_TX_MASK (0x3 << 6)
+#define RT1318_I2S_CH_TX_2CH (0x0 << 6)
+#define RT1318_I2S_CH_TX_4CH (0x1 << 6)
+#define RT1318_I2S_CH_TX_6CH (0x2 << 6)
+#define RT1318_I2S_CH_TX_8CH (0x3 << 6)
+#define RT1318_I2S_CH_RX_MASK (0x3 << 4)
+#define RT1318_I2S_CH_RX_2CH (0x0 << 4)
+#define RT1318_I2S_CH_RX_4CH (0x1 << 4)
+#define RT1318_I2S_CH_RX_6CH (0x2 << 4)
+#define RT1318_I2S_CH_RX_8CH (0x3 << 4)
+#define RT1318_I2S_DL_MASK 0x7
+#define RT1318_I2S_DL_SFT 0
+#define RT1318_I2S_DL_16 0x0
+#define RT1318_I2S_DL_20 0x1
+#define RT1318_I2S_DL_24 0x2
+#define RT1318_I2S_DL_32 0x3
+#define RT1318_I2S_DL_8 0x4
+/* TDM CTRL 3 (0xf902) */
+#define RT1318_I2S_TX_CHL_MASK (0x7 << 4)
+#define RT1318_I2S_TX_CHL_SFT 4
+#define RT1318_I2S_TX_CHL_16 (0x0 << 4)
+#define RT1318_I2S_TX_CHL_20 (0x1 << 4)
+#define RT1318_I2S_TX_CHL_24 (0x2 << 4)
+#define RT1318_I2S_TX_CHL_32 (0x3 << 4)
+#define RT1318_I2S_TX_CHL_8 (0x4 << 4)
+#define RT1318_I2S_RX_CHL_MASK (0x7 << 0)
+#define RT1318_I2S_RX_CHL_SFT 0
+#define RT1318_I2S_RX_CHL_16 (0x0 << 0)
+#define RT1318_I2S_RX_CHL_20 (0x1 << 0)
+#define RT1318_I2S_RX_CHL_24 (0x2 << 0)
+#define RT1318_I2S_RX_CHL_32 (0x3 << 0)
+#define RT1318_I2S_RX_CHL_8 (0x4 << 0)
+/* TDM CTRL 9 (0xf908) */
+#define RT1318_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 4)
+#define RT1318_TDM_I2S_TX_R_DAC1_1_MASK 0x7
+#define RT1318_TDM_I2S_TX_L_DAC1_1_SFT 4
+#define RT1318_TDM_I2S_TX_R_DAC1_1_SFT 0
+
+#define RT1318_REG_DISP_LEN 23
+
+/* System Clock Source */
+enum {
+ RT1318_SCLK_S_BCLK,
+ RT1318_SCLK_S_SDW,
+ RT1318_SCLK_S_PLL2F,
+ RT1318_SCLK_S_PLL2B,
+ RT1318_SCLK_S_MCLK,
+ RT1318_SCLK_S_RC0,
+ RT1318_SCLK_S_RC1,
+ RT1318_SCLK_S_RC2,
+};
+
+/* PLL Source */
+enum {
+ RT1318_PLL_S_BCLK0,
+ RT1318_PLL_S_BCLK1,
+ RT1318_PLL_S_RC,
+ RT1318_PLL_S_MCLK,
+ RT1318_PLL_S_SDW_IN_PLL,
+ RT1318_PLL_S_SDW_0,
+ RT1318_PLL_S_SDW_1,
+ RT1318_PLL_S_SDW_2,
+};
+
+/* TDM channel */
+enum {
+ RT1318_2CH,
+ RT1318_4CH,
+ RT1318_6CH,
+ RT1318_8CH,
+};
+
+/* R0 calibration result */
+enum {
+ RT1318_R0_OUT_OF_RANGE,
+ RT1318_R0_IN_RANGE,
+ RT1318_R0_CALIB_NOT_DONE,
+};
+
+/* PLL pre-defined M/N/K */
+
+struct pll_calc_map {
+ unsigned int pll_in;
+ unsigned int pll_out;
+ int k;
+ int n;
+ int m;
+ bool m_bp;
+ bool k_bp;
+};
+
+struct rt1318_pll_code {
+ bool m_bp; /* Indicates bypass m code or not. */
+ bool k_bp; /* Indicates bypass k code or not. */
+ int m_code;
+ int n_code;
+ int k_code;
+};
+
+#endif /* __RT1318_H__ */
--
2.34.1
3
2

12 Jul '24
Requriment from customer to add new kcontrol to set tas2563 digital
Volume
Signed-off-by: Shenghao Ding <shenghao-ding(a)ti.com>
---
v2:
- Volume controls should end in Volume
- Write this as a switch statement for extension
- Correct return value of tas2563_digital_gain_put
- Submit tas2563 digital Volume as an independant patch
---
include/sound/tas2781-tlv.h | 262 ++++++++++++++++++++++++++++++++-
include/sound/tas2781.h | 1 +
sound/soc/codecs/tas2781-i2c.c | 129 ++++++++++++++--
3 files changed, 382 insertions(+), 10 deletions(-)
diff --git a/include/sound/tas2781-tlv.h b/include/sound/tas2781-tlv.h
index 1dc59005d241..99c41bfc7827 100644
--- a/include/sound/tas2781-tlv.h
+++ b/include/sound/tas2781-tlv.h
@@ -2,7 +2,7 @@
//
// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
//
-// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+// Copyright (C) 2022 - 2024 Texas Instruments Incorporated
// https://www.ti.com
//
// The TAS2781 driver implements a flexible and configurable
@@ -17,5 +17,265 @@
static const __maybe_unused DECLARE_TLV_DB_SCALE(dvc_tlv, -10000, 100, 0);
static const DECLARE_TLV_DB_SCALE(amp_vol_tlv, 1100, 50, 0);
+static const DECLARE_TLV_DB_SCALE(tas2563_dvc_tlv, -12150, 50, 1);
+/* pow(10, db/20) * pow(2,30) */
+static const unsigned char tas2563_dvc_table[][4] = {
+ { 0X00, 0X00, 0X00, 0X00 }, /* -121.5db */
+ { 0X00, 0X00, 0X03, 0XBC }, /* -121.0db */
+ { 0X00, 0X00, 0X03, 0XF5 }, /* -120.5db */
+ { 0X00, 0X00, 0X04, 0X31 }, /* -120.0db */
+ { 0X00, 0X00, 0X04, 0X71 }, /* -119.5db */
+ { 0X00, 0X00, 0X04, 0XB4 }, /* -119.0db */
+ { 0X00, 0X00, 0X04, 0XFC }, /* -118.5db */
+ { 0X00, 0X00, 0X05, 0X47 }, /* -118.0db */
+ { 0X00, 0X00, 0X05, 0X97 }, /* -117.5db */
+ { 0X00, 0X00, 0X05, 0XEC }, /* -117.0db */
+ { 0X00, 0X00, 0X06, 0X46 }, /* -116.5db */
+ { 0X00, 0X00, 0X06, 0XA5 }, /* -116.0db */
+ { 0X00, 0X00, 0X07, 0X0A }, /* -115.5db */
+ { 0X00, 0X00, 0X07, 0X75 }, /* -115.0db */
+ { 0X00, 0X00, 0X07, 0XE6 }, /* -114.5db */
+ { 0X00, 0X00, 0X08, 0X5E }, /* -114.0db */
+ { 0X00, 0X00, 0X08, 0XDD }, /* -113.5db */
+ { 0X00, 0X00, 0X09, 0X63 }, /* -113.0db */
+ { 0X00, 0X00, 0X09, 0XF2 }, /* -112.5db */
+ { 0X00, 0X00, 0X0A, 0X89 }, /* -112.0db */
+ { 0X00, 0X00, 0X0B, 0X28 }, /* -111.5db */
+ { 0X00, 0X00, 0X0B, 0XD2 }, /* -111.0db */
+ { 0X00, 0X00, 0X0C, 0X85 }, /* -110.5db */
+ { 0X00, 0X00, 0X0D, 0X43 }, /* -110.0db */
+ { 0X00, 0X00, 0X0E, 0X0C }, /* -109.5db */
+ { 0X00, 0X00, 0X0E, 0XE1 }, /* -109.0db */
+ { 0X00, 0X00, 0X0F, 0XC3 }, /* -108.5db */
+ { 0X00, 0X00, 0X10, 0XB2 }, /* -108.0db */
+ { 0X00, 0X00, 0X11, 0XAF }, /* -107.5db */
+ { 0X00, 0X00, 0X12, 0XBC }, /* -107.0db */
+ { 0X00, 0X00, 0X13, 0XD8 }, /* -106.5db */
+ { 0X00, 0X00, 0X15, 0X05 }, /* -106.0db */
+ { 0X00, 0X00, 0X16, 0X44 }, /* -105.5db */
+ { 0X00, 0X00, 0X17, 0X96 }, /* -105.0db */
+ { 0X00, 0X00, 0X18, 0XFB }, /* -104.5db */
+ { 0X00, 0X00, 0X1A, 0X76 }, /* -104.0db */
+ { 0X00, 0X00, 0X1C, 0X08 }, /* -103.5db */
+ { 0X00, 0X00, 0X1D, 0XB1 }, /* -103.0db */
+ { 0X00, 0X00, 0X1F, 0X73 }, /* -102.5db */
+ { 0X00, 0X00, 0X21, 0X51 }, /* -102.0db */
+ { 0X00, 0X00, 0X23, 0X4A }, /* -101.5db */
+ { 0X00, 0X00, 0X25, 0X61 }, /* -101.0db */
+ { 0X00, 0X00, 0X27, 0X98 }, /* -100.5db */
+ { 0X00, 0X00, 0X29, 0XF1 }, /* -100.0db */
+ { 0X00, 0X00, 0X2C, 0X6D }, /* -99.5db */
+ { 0X00, 0X00, 0X2F, 0X0F }, /* -99.0db */
+ { 0X00, 0X00, 0X31, 0XD9 }, /* -98.5db */
+ { 0X00, 0X00, 0X34, 0XCD }, /* -98.0db */
+ { 0X00, 0X00, 0X37, 0XEE }, /* -97.5db */
+ { 0X00, 0X00, 0X3B, 0X3F }, /* -97.0db */
+ { 0X00, 0X00, 0X3E, 0XC1 }, /* -96.5db */
+ { 0X00, 0X00, 0X42, 0X79 }, /* -96.0db */
+ { 0X00, 0X00, 0X46, 0X6A }, /* -95.5db */
+ { 0X00, 0X00, 0X4A, 0X96 }, /* -95.0db */
+ { 0X00, 0X00, 0X4F, 0X01 }, /* -94.5db */
+ { 0X00, 0X00, 0X53, 0XAF }, /* -94.0db */
+ { 0X00, 0X00, 0X58, 0XA5 }, /* -93.5db */
+ { 0X00, 0X00, 0X5D, 0XE6 }, /* -93.0db */
+ { 0X00, 0X00, 0X63, 0X76 }, /* -92.5db */
+ { 0X00, 0X00, 0X69, 0X5B }, /* -92.0db */
+ { 0X00, 0X00, 0X6F, 0X99 }, /* -91.5db */
+ { 0X00, 0X00, 0X76, 0X36 }, /* -91.0db */
+ { 0X00, 0X00, 0X7D, 0X37 }, /* -90.5db */
+ { 0X00, 0X00, 0X84, 0XA2 }, /* -90.0db */
+ { 0X00, 0X00, 0X8C, 0X7E }, /* -89.5db */
+ { 0X00, 0X00, 0X94, 0XD1 }, /* -89.0db */
+ { 0X00, 0X00, 0X9D, 0XA3 }, /* -88.5db */
+ { 0X00, 0X00, 0XA6, 0XFA }, /* -88.0db */
+ { 0X00, 0X00, 0XB0, 0XDF }, /* -87.5db */
+ { 0X00, 0X00, 0XBB, 0X5A }, /* -87.0db */
+ { 0X00, 0X00, 0XC6, 0X74 }, /* -86.5db */
+ { 0X00, 0X00, 0XD2, 0X36 }, /* -86.0db */
+ { 0X00, 0X00, 0XDE, 0XAB }, /* -85.5db */
+ { 0X00, 0X00, 0XEB, 0XDC }, /* -85.0db */
+ { 0X00, 0X00, 0XF9, 0XD6 }, /* -84.5db */
+ { 0X00, 0X01, 0X08, 0XA4 }, /* -84.0db */
+ { 0X00, 0X01, 0X18, 0X52 }, /* -83.5db */
+ { 0X00, 0X01, 0X28, 0XEF }, /* -83.0db */
+ { 0X00, 0X01, 0X3A, 0X87 }, /* -82.5db */
+ { 0X00, 0X01, 0X4D, 0X2A }, /* -82.0db */
+ { 0X00, 0X01, 0X60, 0XE8 }, /* -81.5db */
+ { 0X00, 0X01, 0X75, 0XD1 }, /* -81.0db */
+ { 0X00, 0X01, 0X8B, 0XF7 }, /* -80.5db */
+ { 0X00, 0X01, 0XA3, 0X6E }, /* -80.0db */
+ { 0X00, 0X01, 0XBC, 0X48 }, /* -79.5db */
+ { 0X00, 0X01, 0XD6, 0X9B }, /* -79.0db */
+ { 0X00, 0X01, 0XF2, 0X7E }, /* -78.5db */
+ { 0X00, 0X02, 0X10, 0X08 }, /* -78.0db */
+ { 0X00, 0X02, 0X2F, 0X51 }, /* -77.5db */
+ { 0X00, 0X02, 0X50, 0X76 }, /* -77.0db */
+ { 0X00, 0X02, 0X73, 0X91 }, /* -76.5db */
+ { 0X00, 0X02, 0X98, 0XC0 }, /* -76.0db */
+ { 0X00, 0X02, 0XC0, 0X24 }, /* -75.5db */
+ { 0X00, 0X02, 0XE9, 0XDD }, /* -75.0db */
+ { 0X00, 0X03, 0X16, 0X0F }, /* -74.5db */
+ { 0X00, 0X03, 0X44, 0XDF }, /* -74.0db */
+ { 0X00, 0X03, 0X76, 0X76 }, /* -73.5db */
+ { 0X00, 0X03, 0XAA, 0XFC }, /* -73.0db */
+ { 0X00, 0X03, 0XE2, 0XA0 }, /* -72.5db */
+ { 0X00, 0X04, 0X1D, 0X8F }, /* -72.0db */
+ { 0X00, 0X04, 0X5B, 0XFD }, /* -71.5db */
+ { 0X00, 0X04, 0X9E, 0X1D }, /* -71.0db */
+ { 0X00, 0X04, 0XE4, 0X29 }, /* -70.5db */
+ { 0X00, 0X05, 0X2E, 0X5A }, /* -70.0db */
+ { 0X00, 0X05, 0X7C, 0XF2 }, /* -69.5db */
+ { 0X00, 0X05, 0XD0, 0X31 }, /* -69.0db */
+ { 0X00, 0X06, 0X28, 0X60 }, /* -68.5db */
+ { 0X00, 0X06, 0X85, 0XC8 }, /* -68.0db */
+ { 0X00, 0X06, 0XE8, 0XB9 }, /* -67.5db */
+ { 0X00, 0X07, 0X51, 0X86 }, /* -67.0db */
+ { 0X00, 0X07, 0XC0, 0X8A }, /* -66.5db */
+ { 0X00, 0X08, 0X36, 0X21 }, /* -66.0db */
+ { 0X00, 0X08, 0XB2, 0XB0 }, /* -65.5db */
+ { 0X00, 0X09, 0X36, 0XA1 }, /* -65.0db */
+ { 0X00, 0X09, 0XC2, 0X63 }, /* -64.5db */
+ { 0X00, 0X0A, 0X56, 0X6D }, /* -64.0db */
+ { 0X00, 0X0A, 0XF3, 0X3C }, /* -63.5db */
+ { 0X00, 0X0B, 0X99, 0X56 }, /* -63.0db */
+ { 0X00, 0X0C, 0X49, 0X48 }, /* -62.5db */
+ { 0X00, 0X0D, 0X03, 0XA7 }, /* -62.0db */
+ { 0X00, 0X0D, 0XC9, 0X11 }, /* -61.5db */
+ { 0X00, 0X0E, 0X9A, 0X2D }, /* -61.0db */
+ { 0X00, 0X0F, 0X77, 0XAD }, /* -60.5db */
+ { 0X00, 0X10, 0X62, 0X4D }, /* -60.0db */
+ { 0X00, 0X11, 0X5A, 0XD5 }, /* -59.5db */
+ { 0X00, 0X12, 0X62, 0X16 }, /* -59.0db */
+ { 0X00, 0X13, 0X78, 0XF0 }, /* -58.5db */
+ { 0X00, 0X14, 0XA0, 0X50 }, /* -58.0db */
+ { 0X00, 0X15, 0XD9, 0X31 }, /* -57.5db */
+ { 0X00, 0X17, 0X24, 0X9C }, /* -57.0db */
+ { 0X00, 0X18, 0X83, 0XAA }, /* -56.5db */
+ { 0X00, 0X19, 0XF7, 0X86 }, /* -56.0db */
+ { 0X00, 0X1B, 0X81, 0X6A }, /* -55.5db */
+ { 0X00, 0X1D, 0X22, 0XA4 }, /* -55.0db */
+ { 0X00, 0X1E, 0XDC, 0X98 }, /* -54.5db */
+ { 0X00, 0X20, 0XB0, 0XBC }, /* -54.0db */
+ { 0X00, 0X22, 0XA0, 0X9D }, /* -53.5db */
+ { 0X00, 0X24, 0XAD, 0XE0 }, /* -53.0db */
+ { 0X00, 0X26, 0XDA, 0X43 }, /* -52.5db */
+ { 0X00, 0X29, 0X27, 0X9D }, /* -52.0db */
+ { 0X00, 0X2B, 0X97, 0XE3 }, /* -51.5db */
+ { 0X00, 0X2E, 0X2D, 0X27 }, /* -51.0db */
+ { 0X00, 0X30, 0XE9, 0X9A }, /* -50.5db */
+ { 0X00, 0X33, 0XCF, 0X8D }, /* -50.0db */
+ { 0X00, 0X36, 0XE1, 0X78 }, /* -49.5db */
+ { 0X00, 0X3A, 0X21, 0XF3 }, /* -49.0db */
+ { 0X00, 0X3D, 0X93, 0XC3 }, /* -48.5db */
+ { 0X00, 0X41, 0X39, 0XD3 }, /* -48.0db */
+ { 0X00, 0X45, 0X17, 0X3B }, /* -47.5db */
+ { 0X00, 0X49, 0X2F, 0X44 }, /* -47.0db */
+ { 0X00, 0X4D, 0X85, 0X66 }, /* -46.5db */
+ { 0X00, 0X52, 0X1D, 0X50 }, /* -46.0db */
+ { 0X00, 0X56, 0XFA, 0XE8 }, /* -45.5db */
+ { 0X00, 0X5C, 0X22, 0X4E }, /* -45.0db */
+ { 0X00, 0X61, 0X97, 0XE1 }, /* -44.5db */
+ { 0X00, 0X67, 0X60, 0X44 }, /* -44.0db */
+ { 0X00, 0X6D, 0X80, 0X60 }, /* -43.5db */
+ { 0X00, 0X73, 0XFD, 0X65 }, /* -43.0db */
+ { 0X00, 0X7A, 0XDC, 0XD7 }, /* -42.5db */
+ { 0X00, 0X82, 0X24, 0X8A }, /* -42.0db */
+ { 0X00, 0X89, 0XDA, 0XAB }, /* -41.5db */
+ { 0X00, 0X92, 0X05, 0XC6 }, /* -41.0db */
+ { 0X00, 0X9A, 0XAC, 0XC8 }, /* -40.5db */
+ { 0X00, 0XA3, 0XD7, 0X0A }, /* -40.0db */
+ { 0X00, 0XAD, 0X8C, 0X52 }, /* -39.5db */
+ { 0X00, 0XB7, 0XD4, 0XDD }, /* -39.0db */
+ { 0X00, 0XC2, 0XB9, 0X65 }, /* -38.5db */
+ { 0X00, 0XCE, 0X43, 0X28 }, /* -38.0db */
+ { 0X00, 0XDA, 0X7B, 0XF1 }, /* -37.5db */
+ { 0X00, 0XE7, 0X6E, 0X1E }, /* -37.0db */
+ { 0X00, 0XF5, 0X24, 0XAC }, /* -36.5db */
+ { 0X01, 0X03, 0XAB, 0X3D }, /* -36.0db */
+ { 0X01, 0X13, 0X0E, 0X24 }, /* -35.5db */
+ { 0X01, 0X23, 0X5A, 0X71 }, /* -35.0db */
+ { 0X01, 0X34, 0X9D, 0XF8 }, /* -34.5db */
+ { 0X01, 0X46, 0XE7, 0X5D }, /* -34.0db */
+ { 0X01, 0X5A, 0X46, 0X27 }, /* -33.5db */
+ { 0X01, 0X6E, 0XCA, 0XC5 }, /* -33.0db */
+ { 0X01, 0X84, 0X86, 0X9F }, /* -32.5db */
+ { 0X01, 0X9B, 0X8C, 0X27 }, /* -32.0db */
+ { 0X01, 0XB3, 0XEE, 0XE5 }, /* -31.5db */
+ { 0X01, 0XCD, 0XC3, 0X8C }, /* -31.0db */
+ { 0X01, 0XE9, 0X20, 0X05 }, /* -30.5db */
+ { 0X02, 0X06, 0X1B, 0X89 }, /* -30.0db */
+ { 0X02, 0X24, 0XCE, 0XB0 }, /* -29.5db */
+ { 0X02, 0X45, 0X53, 0X85 }, /* -29.0db */
+ { 0X02, 0X67, 0XC5, 0XA2 }, /* -28.5db */
+ { 0X02, 0X8C, 0X42, 0X3F }, /* -28.0db */
+ { 0X02, 0XB2, 0XE8, 0X55 }, /* -27.5db */
+ { 0X02, 0XDB, 0XD8, 0XAD }, /* -27.0db */
+ { 0X03, 0X07, 0X36, 0X05 }, /* -26.5db */
+ { 0X03, 0X35, 0X25, 0X29 }, /* -26.0db */
+ { 0X03, 0X65, 0XCD, 0X13 }, /* -25.5db */
+ { 0X03, 0X99, 0X57, 0X0C }, /* -25.0db */
+ { 0X03, 0XCF, 0XEE, 0XCF }, /* -24.5db */
+ { 0X04, 0X09, 0XC2, 0XB0 }, /* -24.0db */
+ { 0X04, 0X47, 0X03, 0XC1 }, /* -23.5db */
+ { 0X04, 0X87, 0XE5, 0XFB }, /* -23.0db */
+ { 0X04, 0XCC, 0XA0, 0X6D }, /* -22.5db */
+ { 0X05, 0X15, 0X6D, 0X68 }, /* -22.0db */
+ { 0X05, 0X62, 0X8A, 0XB3 }, /* -21.5db */
+ { 0X05, 0XB4, 0X39, 0XBC }, /* -21.0db */
+ { 0X06, 0X0A, 0XBF, 0XD4 }, /* -20.5db */
+ { 0X06, 0X66, 0X66, 0X66 }, /* -20.0db */
+ { 0X06, 0XC7, 0X7B, 0X36 }, /* -19.5db */
+ { 0X07, 0X2E, 0X50, 0XA6 }, /* -19.0db */
+ { 0X07, 0X9B, 0X3D, 0XF6 }, /* -18.5db */
+ { 0X08, 0X0E, 0X9F, 0X96 }, /* -18.0db */
+ { 0X08, 0X88, 0XD7, 0X6D }, /* -17.5db */
+ { 0X09, 0X0A, 0X4D, 0X2F }, /* -17.0db */
+ { 0X09, 0X93, 0X6E, 0XB8 }, /* -16.5db */
+ { 0X0A, 0X24, 0XB0, 0X62 }, /* -16.0db */
+ { 0X0A, 0XBE, 0X8D, 0X70 }, /* -15.5db */
+ { 0X0B, 0X61, 0X88, 0X71 }, /* -15.0db */
+ { 0X0C, 0X0E, 0X2B, 0XB0 }, /* -14.5db */
+ { 0X0C, 0XC5, 0X09, 0XAB }, /* -14.0db */
+ { 0X0D, 0X86, 0XBD, 0X8D }, /* -13.5db */
+ { 0X0E, 0X53, 0XEB, 0XB3 }, /* -13.0db */
+ { 0X0F, 0X2D, 0X42, 0X38 }, /* -12.5db */
+ { 0X10, 0X13, 0X79, 0X87 }, /* -12.0db */
+ { 0X11, 0X07, 0X54, 0XF9 }, /* -11.5db */
+ { 0X12, 0X09, 0XA3, 0X7A }, /* -11.0db */
+ { 0X13, 0X1B, 0X40, 0X39 }, /* -10.5db */
+ { 0X14, 0X3D, 0X13, 0X62 }, /* -10.0db */
+ { 0X15, 0X70, 0X12, 0XE1 }, /* -9.5db */
+ { 0X16, 0XB5, 0X43, 0X37 }, /* -9.0db */
+ { 0X18, 0X0D, 0XB8, 0X54 }, /* -8.5db */
+ { 0X19, 0X7A, 0X96, 0X7F }, /* -8.0db */
+ { 0X1A, 0XFD, 0X13, 0X54 }, /* -7.5db */
+ { 0X1C, 0X96, 0X76, 0XC6 }, /* -7.0db */
+ { 0X1E, 0X48, 0X1C, 0X37 }, /* -6.5db */
+ { 0X20, 0X13, 0X73, 0X9E }, /* -6.0db */
+ { 0X21, 0XFA, 0X02, 0XBF }, /* -5.5db */
+ { 0X23, 0XFD, 0X66, 0X78 }, /* -5.0db */
+ { 0X26, 0X1F, 0X54, 0X1C }, /* -4.5db */
+ { 0X28, 0X61, 0X9A, 0XE9 }, /* -4.0db */
+ { 0X2A, 0XC6, 0X25, 0X91 }, /* -3.5db */
+ { 0X2D, 0X4E, 0XFB, 0XD5 }, /* -3.0db */
+ { 0X2F, 0XFE, 0X44, 0X48 }, /* -2.5db */
+ { 0X32, 0XD6, 0X46, 0X17 }, /* -2.0db */
+ { 0X35, 0XD9, 0X6B, 0X02 }, /* -1.5db */
+ { 0X39, 0X0A, 0X41, 0X5F }, /* -1.0db */
+ { 0X3C, 0X6B, 0X7E, 0X4F }, /* -0.5db */
+ { 0X40, 0X00, 0X00, 0X00 }, /* 0.0db */
+ { 0X43, 0XCA, 0XD0, 0X22 }, /* 0.5db */
+ { 0X47, 0XCF, 0X26, 0X7D }, /* 1.0db */
+ { 0X4C, 0X10, 0X6B, 0XA5 }, /* 1.5db */
+ { 0X50, 0X92, 0X3B, 0XE3 }, /* 2.0db */
+ { 0X55, 0X58, 0X6A, 0X46 }, /* 2.5db */
+ { 0X5A, 0X67, 0X03, 0XDF }, /* 3.0db */
+ { 0X5F, 0XC2, 0X53, 0X32 }, /* 3.5db */
+ { 0X65, 0X6E, 0XE3, 0XDB }, /* 4.0db */
+ { 0X6B, 0X71, 0X86, 0X68 }, /* 4.5db */
+ { 0X71, 0XCF, 0X54, 0X71 }, /* 5.0db */
+ { 0X78, 0X8D, 0XB4, 0XE9 }, /* 5.5db */
+ { 0XFF, 0XFF, 0XFF, 0XFF }, /* 6.0db */
+};
#endif
diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h
index a43ad6dcb7c7..18161d02a96f 100644
--- a/include/sound/tas2781.h
+++ b/include/sound/tas2781.h
@@ -50,6 +50,7 @@
#define TASDEVICE_I2CChecksum TASDEVICE_REG(0x0, 0x0, 0x7E)
/* Volume control */
+#define TAS2563_DVC_LVL TASDEVICE_REG(0x00, 0x02, 0x0C)
#define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1A)
#define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03)
#define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1)
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index d5d95ae57c73..e79d613745b4 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -31,6 +31,7 @@
#include <sound/tas2781.h>
#include <sound/tlv.h>
#include <sound/tas2781-tlv.h>
+#include <asm/unaligned.h>
static const struct i2c_device_id tasdevice_id[] = {
{ "tas2563", TAS2563 },
@@ -140,6 +141,101 @@ static int tasdev_force_fwload_put(struct snd_kcontrol *kcontrol,
return change;
}
+static int tas2563_digital_gain_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ unsigned int l = 0, r = mc->max;
+ unsigned int target, ar_mid, mid, ar_l, ar_r;
+ unsigned int reg = mc->reg;
+ unsigned char data[4];
+ int ret;
+
+ mutex_lock(&tas_dev->codec_lock);
+ /* Read the primary device */
+ ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
+ if (ret) {
+ dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
+ goto out;
+ }
+
+ target = get_unaligned_be32(&data[0]);
+
+ while (r > 1 + l) {
+ mid = (l + r) / 2;
+ ar_mid = get_unaligned_be32(tas2563_dvc_table[mid]);
+ if (target < ar_mid)
+ r = mid;
+ else
+ l = mid;
+ }
+
+ ar_l = get_unaligned_be32(tas2563_dvc_table[l]);
+ ar_r = get_unaligned_be32(tas2563_dvc_table[r]);
+
+ /* find out the member same as or closer to the current volume */
+ ucontrol->value.integer.value[0] =
+ abs(target - ar_l) <= abs(target - ar_r) ? l : r;
+out:
+ mutex_unlock(&tas_dev->codec_lock);
+ return 0;
+}
+
+static int tas2563_digital_gain_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ int vol = ucontrol->value.integer.value[0];
+ int status = 0, max = mc->max, rc = 1;
+ int i, ret;
+ unsigned int reg = mc->reg;
+ unsigned int volrd, volwr;
+ unsigned char data[4];
+
+ vol = clamp(vol, 0, max);
+ mutex_lock(&tas_dev->codec_lock);
+ /* Read the primary device */
+ ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
+ if (ret) {
+ dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
+ rc = -1;
+ goto out;
+ }
+
+ volrd = get_unaligned_be32(&data[0]);
+ volwr = get_unaligned_be32(tas2563_dvc_table[vol]);
+
+ if (volrd == volwr) {
+ rc = 0;
+ goto out;
+ }
+
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_bulk_write(tas_dev, i, reg,
+ (unsigned char *)tas2563_dvc_table[vol], 4);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s, set digital vol error in dev %d\n",
+ __func__, i);
+ status |= BIT(i);
+ }
+ }
+
+ if (status)
+ rc = -1;
+out:
+ mutex_unlock(&tas_dev->codec_lock);
+ return rc;
+}
+
static const struct snd_kcontrol_new tasdevice_snd_controls[] = {
SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
tasdev_force_fwload_get, tasdev_force_fwload_put),
@@ -154,6 +250,13 @@ static const struct snd_kcontrol_new tas2781_snd_controls[] = {
tas2781_digital_putvol, dvc_tlv),
};
+static const struct snd_kcontrol_new tas2563_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2563_DVC_LVL, 0,
+ 0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0,
+ tas2563_digital_gain_get, tas2563_digital_gain_put,
+ tas2563_dvc_tlv),
+};
+
static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -593,17 +696,25 @@ static struct snd_soc_dai_driver tasdevice_dai_driver[] = {
static int tasdevice_codec_probe(struct snd_soc_component *codec)
{
struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+ struct snd_kcontrol_new *p;
+ unsigned int size;
int rc;
- if (tas_priv->chip_id == TAS2781) {
- rc = snd_soc_add_component_controls(codec,
- tas2781_snd_controls,
- ARRAY_SIZE(tas2781_snd_controls));
- if (rc < 0) {
- dev_err(tas_priv->dev, "%s: Add control err rc = %d",
- __func__, rc);
- return rc;
- }
+ switch (tas_priv->chip_id) {
+ case TAS2781:
+ p = (struct snd_kcontrol_new *)tas2781_snd_controls;
+ size = ARRAY_SIZE(tas2781_snd_controls);
+ break;
+ default:
+ p = (struct snd_kcontrol_new *)tas2563_snd_controls;
+ size = ARRAY_SIZE(tas2563_snd_controls);
+ }
+
+ rc = snd_soc_add_component_controls(codec, p, size);
+ if (rc < 0) {
+ dev_err(tas_priv->dev, "%s: Add control err rc = %d",
+ __func__, rc);
+ return rc;
}
tas_priv->name_prefix = codec->name_prefix;
--
2.34.1
2
1

12 Jul '24
Convert the Cirrus Logic CS42448/CS42888 audio CODEC bindings to DT
schema format. Set power supply properties to required only for CS42888.
Cc: Daniel Baluta <daniel.baluta(a)nxp.com>
Signed-off-by: Animesh Agarwal <animeshagarwal28(a)gmail.com>
---
.../bindings/sound/cirrus,cs42xx8.yaml | 81 +++++++++++++++++++
.../devicetree/bindings/sound/cs42xx8.txt | 34 --------
2 files changed, 81 insertions(+), 34 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/cs42xx8.txt
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml
new file mode 100644
index 000000000000..725b47e82062
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/cirrus,cs42xx8.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cirrus Logic CS42448/CS42888 audio CODEC
+
+maintainers:
+ - patches(a)opensource.cirrus.com
+
+properties:
+ compatible:
+ enum:
+ - cirrus,cs42448
+ - cirrus,cs42888
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ minItems: 1
+ maxItems: 2
+
+ clock-names:
+ const: mclk
+
+ VA-supply:
+ description: Analog power supply.
+
+ VD-supply:
+ description: Digital power supply.
+
+ VLC-supply:
+ description: Control port power supply
+
+ VLS-supply:
+ description: Serial port interface power supply.
+
+ reset-gpios:
+ description: This pin is connected to the chip's RESET pin.
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+
+if:
+ properties:
+ compatible:
+ contains:
+ const: cirrus,cs42888
+then:
+ required:
+ - VA-supply
+ - VD-supply
+ - VLC-supply
+ - VLS-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec@48 {
+ compatible = "cirrus,cs42888";
+ reg = <0x48>;
+ clocks = <&codec_mclk 0>;
+ clock-names = "mclk";
+ VA-supply = <®_audio>;
+ VD-supply = <®_audio>;
+ VLS-supply = <®_audio>;
+ VLC-supply = <®_audio>;
+ reset-gpios = <&gpio 1>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/cs42xx8.txt b/Documentation/devicetree/bindings/sound/cs42xx8.txt
deleted file mode 100644
index bbfe39347c20..000000000000
--- a/Documentation/devicetree/bindings/sound/cs42xx8.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-CS42448/CS42888 audio CODEC
-
-Required properties:
-
- - compatible : must contain one of "cirrus,cs42448" and "cirrus,cs42888"
-
- - reg : the I2C address of the device for I2C
-
- - clocks : a list of phandles + clock-specifiers, one for each entry in
- clock-names
-
- - clock-names : must contain "mclk"
-
- - VA-supply, VD-supply, VLS-supply, VLC-supply: power supplies for the device,
- as covered in Documentation/devicetree/bindings/regulator/regulator.txt
-
-Optional properties:
-
- - reset-gpios : a GPIO spec to define which pin is connected to the chip's
- !RESET pin
-
-Example:
-
-cs42888: codec@48 {
- compatible = "cirrus,cs42888";
- reg = <0x48>;
- clocks = <&codec_mclk 0>;
- clock-names = "mclk";
- VA-supply = <®_audio>;
- VD-supply = <®_audio>;
- VLS-supply = <®_audio>;
- VLC-supply = <®_audio>;
- reset-gpios = <&pca9557_b 1 GPIO_ACTIVE_LOW>;
-};
--
2.45.2
1
0

12 Jul '24
The qmc_audio driver supports only audio in interleaved mode.
Non-interleaved mode can be easily supported using several QMC channel
per DAI. In that case, data related to ch0 are sent to (received from)
the first QMC channel, data related to ch1 use the next QMC channel and
so on up to the last channel.
In terms of constraints and settings, the interleaved and
non-interleaved modes are slightly different.
In interleaved mode:
- The sample size should fit in the number of time-slots available for
the QMC channel.
- The number of audio channels should fit in the number of time-slots
(taking into account the sample size) available for the QMC channel.
In non-interleaved mode:
- The number of audio channels is the number of available QMC
channels.
- Each QMC channel should have the same number of time-slots.
- The sample size equals the number of time-slots of one QMC channel.
This series add support for the non-interleaved mode in the qmc_audio
driver and is composed of the following parts:
- Patches 1 and 2: Fix some issues in the qmc_audio
- Patches 3 to 6: Prepare qmc_audio for the non-interleaved mode
- Patches 7 and 8: Extend the QMC driver API
- Patches 9 and 10: The support for non-interleaved mode itself
Compared to the previous iteration, this v2 series mainly improves
qmc_audio_access_is_interleaved().
Best regards,
Hervé
Link to v1: https://lore.kernel.org/lkml/20240620084300.397853-1-herve.codina@bootlin.c…
Changes v1 -> v2
- Patches 1 to 8
No changes
- Patch 9
Add 'Reviewed-by: Rob Herring (Arm) <robh(a)kernel.org>'
- Patch 10
Remove unneeded ';'
Modify qmc_audio_access_is_interleaved()
Herve Codina (10):
ASoC: fsl: fsl_qmc_audio: Check devm_kasprintf() returned value
ASoC: fsl: fsl_qmc_audio: Fix issues detected by checkpatch
ASoC: fsl: fsl_qmc_audio: Split channel buffer and PCM pointer
handling
ASoC: fsl: fsl_qmc_audio: Identify the QMC channel involved in
completion routines
ASoC: fsl: fsl_qmc_audio: Introduce
qmc_audio_pcm_{read,write}_submit()
ASoC: fsl: fsl_qmc_audio: Introduce qmc_dai_constraints_interleaved()
soc: fsl: cpm1: qmc: Introduce functions to get a channel from a
phandle list
soc: fsl: cpm1: qmc: Introduce qmc_chan_count_phandles()
dt-bindings: sound: fsl,qmc-audio: Add support for multiple QMC
channels per DAI
ASoC: fsl: fsl_qmc_audio: Add support for non-interleaved mode.
.../bindings/sound/fsl,qmc-audio.yaml | 41 +-
drivers/soc/fsl/qe/qmc.c | 32 +-
include/soc/fsl/qe/qmc.h | 27 +-
sound/soc/fsl/fsl_qmc_audio.c | 591 +++++++++++++-----
4 files changed, 506 insertions(+), 185 deletions(-)
--
2.45.0
4
14

[PATCH] ASoC: codecs: wsa884x: Implement temperature reading and hwmon
by Krzysztof Kozlowski 12 Jul '24
by Krzysztof Kozlowski 12 Jul '24
12 Jul '24
Read temperature of the speaker and expose it via hwmon interface, which
will be later used during calibration of speaker protection algorithms.
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski(a)linaro.org>
---
sound/soc/codecs/wsa884x.c | 197 +++++++++++++++++++++++++++++++++++++
1 file changed, 197 insertions(+)
diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c
index 7b19df9c1728..5f4acd66f942 100644
--- a/sound/soc/codecs/wsa884x.c
+++ b/sound/soc/codecs/wsa884x.c
@@ -5,11 +5,14 @@
*/
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
+#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -301,8 +304,28 @@
#define WSA884X_PA_FSM_MSK1 (WSA884X_DIG_CTRL0_BASE + 0x3b)
#define WSA884X_PA_FSM_BYP_CTL (WSA884X_DIG_CTRL0_BASE + 0x3c)
#define WSA884X_PA_FSM_BYP0 (WSA884X_DIG_CTRL0_BASE + 0x3d)
+#define WSA884X_PA_FSM_BYP0_DC_CAL_EN_MASK 0x01
+#define WSA884X_PA_FSM_BYP0_DC_CAL_EN_SHIFT 0
+#define WSA884X_PA_FSM_BYP0_CLK_WD_EN_MASK 0x02
+#define WSA884X_PA_FSM_BYP0_CLK_WD_EN_SHIFT 1
+#define WSA884X_PA_FSM_BYP0_BG_EN_MASK 0x04
+#define WSA884X_PA_FSM_BYP0_BG_EN_SHIFT 2
+#define WSA884X_PA_FSM_BYP0_BOOST_EN_MASK 0x08
+#define WSA884X_PA_FSM_BYP0_BOOST_EN_SHIFT 3
+#define WSA884X_PA_FSM_BYP0_PA_EN_MASK 0x10
+#define WSA884X_PA_FSM_BYP0_PA_EN_SHIFT 4
+#define WSA884X_PA_FSM_BYP0_D_UNMUTE_MASK 0x20
+#define WSA884X_PA_FSM_BYP0_D_UNMUTE_SHIFT 5
+#define WSA884X_PA_FSM_BYP0_SPKR_PROT_EN_MASK 0x40
+#define WSA884X_PA_FSM_BYP0_SPKR_PROT_EN_SHIFT 6
+#define WSA884X_PA_FSM_BYP0_TSADC_EN_MASK 0x80
+#define WSA884X_PA_FSM_BYP0_TSADC_EN_SHIFT 7
#define WSA884X_PA_FSM_BYP1 (WSA884X_DIG_CTRL0_BASE + 0x3e)
#define WSA884X_TADC_VALUE_CTL (WSA884X_DIG_CTRL0_BASE + 0x50)
+#define WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK 0x01
+#define WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_SHIFT 0
+#define WSA884X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_MASK 0x02
+#define WSA884X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_SHIFT 1
#define WSA884X_TEMP_DETECT_CTL (WSA884X_DIG_CTRL0_BASE + 0x51)
#define WSA884X_TEMP_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x52)
#define WSA884X_TEMP_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x53)
@@ -691,6 +714,17 @@
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+/* Two-point trimming for temperature calibration */
+#define WSA884X_T1_TEMP -10L
+#define WSA884X_T2_TEMP 150L
+
+/*
+ * Device will report senseless data in many cases, so discard any measurements
+ * outside of valid range.
+ */
+#define WSA884X_LOW_TEMP_THRESHOLD 5
+#define WSA884X_HIGH_TEMP_THRESHOLD 45
+
struct wsa884x_priv {
struct regmap *regmap;
struct device *dev;
@@ -706,6 +740,13 @@ struct wsa884x_priv {
int active_ports;
int dev_mode;
bool hw_init;
+ /*
+ * Protects temperature reading code (related to speaker protection) and
+ * fields: temperature and pa_on.
+ */
+ struct mutex sp_lock;
+ unsigned int temperature;
+ bool pa_on;
};
enum {
@@ -1650,6 +1691,10 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ mutex_lock(&wsa884x->sp_lock);
+ wsa884x->pa_on = true;
+ mutex_unlock(&wsa884x->sp_lock);
+
wsa884x_spkr_post_pmu(component, wsa884x);
snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL,
@@ -1661,6 +1706,10 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w,
snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL,
WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK,
0x0);
+
+ mutex_lock(&wsa884x->sp_lock);
+ wsa884x->pa_on = false;
+ mutex_unlock(&wsa884x->sp_lock);
break;
}
@@ -1800,6 +1849,144 @@ static struct snd_soc_dai_driver wsa884x_dais[] = {
},
};
+static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp)
+{
+ unsigned int d1_msb = 0, d1_lsb = 0, d2_msb = 0, d2_lsb = 0;
+ unsigned int dmeas_msb = 0, dmeas_lsb = 0;
+ int d1, d2, dmeas;
+ unsigned int mask;
+ long val;
+ int ret;
+
+ guard(mutex)(&wsa884x->sp_lock);
+
+ if (wsa884x->pa_on) {
+ /*
+ * Reading temperature is possible only when Power Amplifier is
+ * off. Report last cached data.
+ */
+ *temp = wsa884x->temperature;
+ return 0;
+ }
+
+ ret = pm_runtime_resume_and_get(wsa884x->dev);
+ if (ret < 0)
+ return ret;
+
+ mask = WSA884X_PA_FSM_BYP0_DC_CAL_EN_MASK |
+ WSA884X_PA_FSM_BYP0_CLK_WD_EN_MASK |
+ WSA884X_PA_FSM_BYP0_BG_EN_MASK |
+ WSA884X_PA_FSM_BYP0_D_UNMUTE_MASK |
+ WSA884X_PA_FSM_BYP0_SPKR_PROT_EN_MASK |
+ WSA884X_PA_FSM_BYP0_TSADC_EN_MASK;
+ /*
+ * Here and further do not care about read or update failures.
+ * For example, before turning on Power Amplifier for the first
+ * time, reading WSA884X_TEMP_DIN_MSB will always return 0.
+ * Instead, check if returned value is within reasonable
+ * thresholds.
+ */
+ regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_BYP0, mask, mask);
+
+ regmap_update_bits(wsa884x->regmap, WSA884X_TADC_VALUE_CTL,
+ WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK,
+ FIELD_PREP(WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x0));
+
+ regmap_read(wsa884x->regmap, WSA884X_TEMP_DIN_MSB, &dmeas_msb);
+ regmap_read(wsa884x->regmap, WSA884X_TEMP_DIN_LSB, &dmeas_lsb);
+
+ regmap_update_bits(wsa884x->regmap, WSA884X_TADC_VALUE_CTL,
+ WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK,
+ FIELD_PREP(WSA884X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x1));
+
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_1, &d1_msb);
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_2, &d1_lsb);
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_3, &d2_msb);
+ regmap_read(wsa884x->regmap, WSA884X_OTP_REG_4, &d2_lsb);
+
+ regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_BYP0, mask, 0x0);
+
+ dmeas = (((dmeas_msb & 0xff) << 0x8) | (dmeas_lsb & 0xff)) >> 0x6;
+ d1 = (((d1_msb & 0xff) << 0x8) | (d1_lsb & 0xff)) >> 0x6;
+ d2 = (((d2_msb & 0xff) << 0x8) | (d2_lsb & 0xff)) >> 0x6;
+
+ if (d1 == d2) {
+ /* Incorrect data in OTP? */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ val = WSA884X_T1_TEMP + (((dmeas - d1) * (WSA884X_T2_TEMP - WSA884X_T1_TEMP))/(d2 - d1));
+
+ dev_dbg(wsa884x->dev, "Measured temp %ld (dmeas=%d, d1=%d, d2=%d)\n",
+ val, dmeas, d1, d2);
+
+ if ((val > WSA884X_LOW_TEMP_THRESHOLD) &&
+ (val < WSA884X_HIGH_TEMP_THRESHOLD)) {
+ wsa884x->temperature = val;
+ *temp = val;
+ ret = 0;
+ } else {
+ ret = -EAGAIN;
+ }
+
+out:
+ pm_runtime_mark_last_busy(wsa884x->dev);
+ pm_runtime_put_autosuspend(wsa884x->dev);
+
+ return ret;
+}
+
+static umode_t wsa884x_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int wsa884x_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = wsa884x_get_temp(dev_get_drvdata(dev), temp);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct hwmon_channel_info *const wsa884x_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops wsa884x_hwmon_ops = {
+ .is_visible = wsa884x_hwmon_is_visible,
+ .read = wsa884x_hwmon_read,
+};
+
+static const struct hwmon_chip_info wsa884x_hwmon_chip_info = {
+ .ops = &wsa884x_hwmon_ops,
+ .info = wsa884x_hwmon_info,
+};
+
static void wsa884x_reset_powerdown(void *data)
{
struct wsa884x_priv *wsa884x = data;
@@ -1849,6 +2036,7 @@ static int wsa884x_probe(struct sdw_slave *pdev,
{
struct device *dev = &pdev->dev;
struct wsa884x_priv *wsa884x;
+ struct device *hwmon;
unsigned int i;
int ret;
@@ -1856,6 +2044,8 @@ static int wsa884x_probe(struct sdw_slave *pdev,
if (!wsa884x)
return -ENOMEM;
+ mutex_init(&wsa884x->sp_lock);
+
for (i = 0; i < WSA884X_SUPPLIES_NUM; i++)
wsa884x->supplies[i].supply = wsa884x_supply_name[i];
@@ -1913,6 +2103,13 @@ static int wsa884x_probe(struct sdw_slave *pdev,
regcache_cache_only(wsa884x->regmap, true);
wsa884x->hw_init = true;
+ hwmon = devm_hwmon_device_register_with_info(dev, "wsa884x", wsa884x,
+ &wsa884x_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon))
+ return dev_err_probe(dev, PTR_ERR(hwmon),
+ "Failed to register hwmon sensor\n");
+
pm_runtime_set_autosuspend_delay(dev, 3000);
pm_runtime_use_autosuspend(dev);
pm_runtime_mark_last_busy(dev);
--
2.43.0
1
0

12 Jul '24
The content of this message was lost. It was probably cross-posted to
multiple lists and previously handled on another list.
3
2
The following changes since commit 68f97fe330e01450ace63da0ce5cab676fc97f9a:
ASoC: rt5645: fix issue of random interrupt from push-button (2024-06-26 16:34:48 +0100)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git tags/asoc-fix-v6.10-rc7
for you to fetch changes up to 680e126ec0400f6daecf0510c5bb97a55779ff03:
firmware: cs_dsp: Use strnlen() on name fields in V1 wmfw files (2024-07-08 15:55:11 +0100)
----------------------------------------------------------------
ASoC: Fixes for v6.10
A few fairly small fixes for ASoC, there's a relatively large set of
hardening changes for the cs_dsp firmware file parsing and a couple of
other small device specific fixes.
----------------------------------------------------------------
Bard Liao (1):
ASoC: rt711-sdw: add missing readable registers
Kai Vehmanen (1):
ASoC: SOF: Intel: hda: fix null deref on system suspend entry
Peter Ujfalusi (1):
ASoC: SOF: Intel: hda-pcm: Limit the maximum number of periods by MAX_BDL_ENTRIES
Richard Fitzgerald (5):
firmware: cs_dsp: Fix overflow checking of wmfw header
firmware: cs_dsp: Return error if block header overflows file
firmware: cs_dsp: Validate payload length before processing block
firmware: cs_dsp: Prevent buffer overrun when processing V2 alg headers
firmware: cs_dsp: Use strnlen() on name fields in V1 wmfw files
drivers/firmware/cirrus/cs_dsp.c | 227 ++++++++++++++++++++++++++++-----------
sound/soc/codecs/rt711-sdw.c | 2 +
sound/soc/sof/intel/hda-dai.c | 12 +--
sound/soc/sof/intel/hda-pcm.c | 6 ++
4 files changed, 176 insertions(+), 71 deletions(-)
2
1
syzbot suspects this issue was fixed by commit:
commit fb66df20a7201e60f2b13d7f95d031b31a8831d3
Author: Vladimir Oltean <vladimir.oltean(a)nxp.com>
Date: Mon May 27 15:39:55 2024 +0000
net/sched: taprio: extend minimum interval restriction to entire cycle too
bisection log: https://syzkaller.appspot.com/x/bisect.txt?x=10593441980000
start commit: fe46a7dd189e Merge tag 'sound-6.9-rc1' of git://git.kernel..
git tree: upstream
kernel config: https://syzkaller.appspot.com/x/.config?x=fe78468a74fdc3b7
dashboard link: https://syzkaller.appspot.com/bug?extid=6969434de600a6ba9f07
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=1091a5f6180000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=17a22c13180000
If the result looks correct, please mark the issue as fixed by replying with:
#syz fix: net/sched: taprio: extend minimum interval restriction to entire cycle too
For information about bisection process see: https://goo.gl/tpsmEJ#bisection
1
0
This patch was used to add TAS2781 devices on SPI support in sound/pci/hda.
It use ACPI node descript about parameters of TAS2781 on SPI, it like:
Scope (_SB.PC00.SPI0)
{
Device (GSPK)
{
Name (_HID, "TXNW2781") // _HID: Hardware ID
Method (_CRS, 0, NotSerialized)
{
Name (RBUF, ResourceTemplate ()
{
SpiSerialBusV2 (...)
SpiSerialBusV2 (...)
}
}
}
}
And in platform/x86/serial-multi-instantiate.c, those spi devices will be
added into system as a single SPI device, so TAS2781 SPI driver will
probe twice for every single SPI device. And driver will also parser
mono DSP firmware binary and RCA binary for itself.
The code support Realtek as the primary codec.
Signed-off-by: Baojun Xu <baojun.xu(a)ti.com>
---
v8:
- Move *-y position in sound/pci/hda/Makefile.
- Change + to | for bit operation in macro define.
- Change TASDEVICE_Checksum to TASDEVICE_CHECKSUM.
v7:
- Add new project Gemtree (0x103c8daa) support for tas2781 spi driver.
v6:
- Remove client in tasdevice_priv struct as it was not used.
- Change format for tasdevice_spi_dev_write() in tas2781_hda_spi.c.
- Change update bits function to read & write mode, as tas2781 use last bit
to mask read & write, it cause regmap_update_bits work not as our expected.
- Add element in tas2781_snd_controls, tas2781_prof_ctrl,
tas2781_dsp_conf_ctrl, to support second device.
Previous version add sound card for first index only.
- Change calibration registers address declaire to TASDEVICE_REG() mode.
- Add snd_ctl_new1() for second device.
- Change format for tas2781_hda_unbind() to fit 79 chars in one line.
- Remove client for spi as it was not used.
- Remove unused variables.
- Remove cur_prog clean in runtime_suspend().
- Remove unused variables.
- Add tasdevice_spi_tuning_switch() in runtime_resume.
- Remove cur_prog clean in system_resume().
- Add tuning switch if playback started in system_resume().
- Change TASDEVICE_PAGE_REG to TASDEVICE_REG_ID in tas2781_spi_fwlib.c.
- Remove calibration binary load functions as data will be loaded from EFI.
v5:
- Combined all modification into one patch for pass the compile.
- Remove old hardware id "TIAS2781" as no production on market.
- Change max page size to 256 in tas2781-spi.h.
- Change book reg from 127 to GENMASK(7, 1), as one bit shift is needed.
- Change register address define as one bit shift needed for offset.
- Change block check sum comments to Firmware block from I2C.
- Change define of (page + reg) to TASDEVICE_PAGE_REG from TASDEVICE_PGRG.
- Change to lower case in defined value.
- Add registers default value table in tas2781_hda_spi.c
- Change rang_max to 256 * 256.
- Add zero_flag_mask in regmap_config.
- Change max_register to 256 * 256.
- Add registers default table into structure regmap_config.
- Change parameter from book number to whole register address.
- Remove page 0 switch as SPI driver will do it.
- Add bit(0) for read function as tas2781 will check last bit of address,
to determine operation mode is read (1) or write (0).
- Change switch case to if check in tas2781_hda_playback_hook(),
as it may cause compile problem in clang-19 building.
- Change variable declaration position.
- Change order in if check.
- Remove old hardware id ("TIAS2781") support.
- Remove debug message in runtime suspend and resume.
- Remove regmap.h include in tas2781_spi_fwlib.c.
- Remove MODULE_ in fwlib as this file will work with tas2781_hda_spi.c only.
v4:
- Add old hardware id "TIAS2781" for compatible with old production
- Add 2 devices in struct smi_node tas2781_hda, to compatible with 4 AMPs
v4:
- Add old hardware id "TIAS2781" for compatible with old production
- Add 2 devices in struct smi_node tas2781_hda, to compatible with 4 AMPs
- Move include of sound/tas2781-dsp.h to c source file tas2781_hda_spi.c,
tas2781_spi_fwlib.c, as it's not needed in header file tas2781-spi.h
- Use page and book number define in other micro define
- Change 4000000 to (4 * HZ_PER_MHZ)
- Add define for calibration variable name and size in UEFI.
- Remove structure tasdevice & tasdevice_irqinfo, and move variables in to
structure tasdevice_priv
- Remove some unused variables in sttructure tasdevice_priv
- Remove function declare of tascodec_init(), use tascodec_spi_init()
- Remove function declare of tasdevice_remove()
- Add array_size.h included in tas2781_hda_spi.c
- Add cleanup.h included for change mutex to guard
- Add units.h included
- Include tas2781-dsp.h
- Remove unused variables in tas2781_hda structure
- Change 0xff to GENMASK(7, 0)
- Change 128 to define of max page size
- Change code as all variables was moved to tasdevice_priv structure
- Change comments for error in book write, or page write
- Remove initial of ret, and return 0 directly
- Change comments for wrong typo, add space in front and end
- Add check for bulk read, max size should be a page size
- Change usleep_rang() to fsleep()
- Change mutex_lock to guard(mutex), and remove mutex_unlock()
- Change tasdevice_spi_init() to void from int as no value return required
- Change second parameter to sizeof
- Remove tasdevice_clamp(), use clamp()
- Add compatible with old hardware id "TIAS2781"
- Remove cs_gpio and spi device in tas2781_read_acpi() as use default CS
- Change dev-index check, return -EINVAL if it's <= 0
- Change 0xFF to U8_MAX
- Remove GPIO get for chip-select, use default from SPI device
- Perform RESET in first AMP only as all AMPs share same RESET pin
- return 0 directly in some simple routine to avoid variable initial
- Change comments for function which was used by system
- Change "ON" "OFF" to function str_on_off(...)
- Change format of GUID
- Add temp buffer for first efi.get_variable()
- Free data if it was allocated
- Change format of debug message of calibration date time print
- Remove total_sz = 0, as it's not needed
- Move save_calibration to after tuning_switch to avoid calibration data
overwrite
- Remove dev from structure tasdevice_hda, use dev in tasdevice_priv
- Remove 0 from { "tas2781-hda", 0}, & {"TXNW2781", 0},
- Add old hardware id "TIAS2781" for compatible with old production
- Add 2 devices in struct smi_node tas2781_hda, to compatible with 4 AMPs
v3:
- Move HID up to above /* Non-conforming _HID ... */ in scan.c,
for avoid misunderstanding.
- Move HID up to above /* Non-conforming _HID ... */ in
serial-multi-instantiate.c, for avoid misunderstanding.
- Change objs to y for snd-hda-scodec-tas2781-spi- in Makefile.
- Change format for some define in tas2781-spi.h.
- Add define for TASDEVICE_MAX_BOOK_NUM, TASDEVICE_MAX_PAGE.
- Move declare of "enum calib_data {" to header file from source file.
- Remove "enum device_catlog_id {" as only one platform was supported now.
- Remove "struct calidata", as we will save pure calibration data only.
- Remove "struct calidata", "enum device_catlog_id" in tasdevice_priv.
- Add calibration_data in tasdevice_priv to save pure calibration data.
- Remove declare of tasdevice_save_calibration() and
tasdevice_apply_calibration(), as it will be used within same c file.
- Add below header files included in tas2781_hda_spi.c:
bits.h, mutex.h, property.h, time.h, types.h.
- Move two micro define to header file tas2781-spi.h.
- Change format of some micro define.
- Change format of some structure.
- Remove irq in tas2781_hda, as have it already in tasdevice_priv.
- Remove some local functions declare as not necessarily.
- Return error if regmap_write() failed.
- Change fixed value 2 to sizeof(data).
- Remove all of EXPORT_SYMBOL_GPL,
as all of function was called within same module.
- Remove empty line after last return in some functions.
- Remove some variable initialyzation.
- Remove variable sub, store acpi_subsystem_id directly.
- Remove wrong comments for device index, it's must, can't be NULL.
- Remove some local variables, use elements in structure directly.
- Append commas in last element of an array.
- Change calibration data process, didn't save all of calibration data in EFI,
get in local, and save pure calibration data in tasdevice_priv.
- Call tasdevice_save_calibration() from function pointer in tasdevice_priv;
- Remove subid as one platform was supported only, needn't check.
- Add initialyzation for local variable.
- Move regmap initialyzation to before acpi read.
- Call tasdevice_apply_calibration() from function pointer in tasdevice_priv;
- Remove MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB), as all functions was
performed within same module.
- Update format and variables declare order.
- Change format of multi conditions for if.
- Remove casting which is not needed.
- Change variables type to avoid casting.
- Remove Unneeded parentheses.
- Change if check to avoid goto.
- Add {} for keep same style.
- Remove some local variables, use elements in structure directly.
v2:
- Change depends on SPI to SPI_MASTER in sound/pci/hda/Kconfig
- Add select SND_HDA_SCODEC_COMPONENT in sound/pci/hda/Kconfig
- Change comp_generic_fixup() 5th parameter from "-%s:00" to
"-%s:00-tas2781-hda.%d" in sound/pci/hda/patch_realtek.c
- Change chain_id from ALC285_FIXUP_THINKPAD_HEADSET_JACK to
ALC285_FIXUP_HP_GPIO_LED in sound/pci/hda/patch_realtek.c
- Remove project name "Gemtree"
- Update information in file header of tas2781-spi.h.
- Remove useless micro define.
- Change TASDEVICE_I2CChecksum to TASDEVICE_Checksum
- Remove enum control_bus as current code support SPI only.
- Remove device define as current code support TAS2781 only.
- Remove spi_device **spi_devs in structure tasdevice_priv.
- Remove cal_binaryname in structure tasdevice_priv.
- Remove ndev in structure tasdevice_priv.
- Change isacpi and isspi, replace by control_bus(I2C or SPI).
- Remove void tasdevice_spi_dsp_remove(void *context).
- Add acpi_device and irq in structure tas2781_hda in tas2781_hda_spi.c.
- Remove parameter chn in all of registers access functions.
- Add tascodec_spi_init().
- Add chip select gpio set for SPI device.
- Change tasdevice_tuning_switch() to tasdevice_spi_tuning_switch().
- Change device offset from tas_priv->ndev to tas_priv->index.
- Change tasdevice_dsp_remove() to tasdevice_spi_dsp_remove().
- tasdevice_prmg_load() to tasdevice_spi_prmg_load().
- Change tasdevice_config_info_remove() to
tasdevice_spi_config_info_remove().
- Add one dummy byte shift when read from page 2~127, or not book 0.
- Change tasdevice_spi_change_chn_book() to tasdevice_spi_switch_book()
- Perform put_device(physdev) before error return.
- Change calibration data id to 2781 from 2783 in EFI
- Change calibration data structure name in comments.
- Remove file name in tas2781_spi_fwlib.c.
- Remove #include <linux/i2c.h> as it not needed in SPI mode.
- Change TAB to speace in micro define in tas2781_spi_fwlib.c.
- Change to up-case for DSP.
- Change all of multi-line comments format, empty first line.
- Change all of sizeof(struct xx) to sizeof(*xx).
- Remove Explicit casting for variables compare to avoid possible fault.
- Return directly without goto.
- Change 1 << xx to BIT(xx).
- Remove deviceNumber[] as current SPI device will support one device only.
- Add memory free before return error.
- Change "buf[offset]; offset += 1" to buf[offset++].
- Remove some debug information print.
- Change firmware binary to single device mode.
- Change all bexx_to_cpup() to get_unaligned_bexx().
- Remove ndev process as current SPI device support single device only.
- Remove chn and chnend for single device support only.
- Change all of registers read/write function, remove parameter chn.
- Create new function for single write, burst write, delay, field write
in tasdevice_process_block().
---
drivers/acpi/scan.c | 1 +
.../platform/x86/serial-multi-instantiate.c | 12 +
sound/pci/hda/Kconfig | 14 +
sound/pci/hda/Makefile | 2 +
sound/pci/hda/patch_realtek.c | 14 +
sound/pci/hda/tas2781-spi.h | 148 ++
sound/pci/hda/tas2781_hda_spi.c | 1266 ++++++++++
sound/pci/hda/tas2781_spi_fwlib.c | 2101 +++++++++++++++++
8 files changed, 3558 insertions(+)
create mode 100644 sound/pci/hda/tas2781-spi.h
create mode 100644 sound/pci/hda/tas2781_hda_spi.c
create mode 100644 sound/pci/hda/tas2781_spi_fwlib.c
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 503773707e01..9c08c5886370 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1760,6 +1760,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
{"CSC3557", },
{"INT33FE", },
{"INT3515", },
+ {"TXNW2781", },
/* Non-conforming _HID for Cirrus Logic already released */
{"CLSA0100", },
{"CLSA0101", },
diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c
index 97b9c6392230..23d46063423d 100644
--- a/drivers/platform/x86/serial-multi-instantiate.c
+++ b/drivers/platform/x86/serial-multi-instantiate.c
@@ -368,6 +368,17 @@ static const struct smi_node cs35l57_hda = {
.bus_type = SMI_AUTO_DETECT,
};
+static const struct smi_node tas2781_hda = {
+ .instances = {
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ {}
+ },
+ .bus_type = SMI_AUTO_DETECT,
+};
+
/*
* Note new device-ids must also be added to ignore_serial_bus_ids in
* drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
@@ -380,6 +391,7 @@ static const struct acpi_device_id smi_acpi_ids[] = {
{ "CSC3556", (unsigned long)&cs35l56_hda },
{ "CSC3557", (unsigned long)&cs35l57_hda },
{ "INT3515", (unsigned long)&int3515_data },
+ { "TXNW2781", (unsigned long)&tas2781_hda },
/* Non-conforming _HID for Cirrus Logic already released */
{ "CLSA0100", (unsigned long)&cs35l41_hda },
{ "CLSA0101", (unsigned long)&cs35l41_hda },
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 0da625533afc..113161618788 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -204,6 +204,20 @@ config SND_HDA_SCODEC_TAS2781_I2C
comment "Set to Y if you want auto-loading the side codec driver"
depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m
+config SND_HDA_SCODEC_TAS2781_SPI
+ tristate "Build TAS2781 HD-audio side codec support for SPI Bus"
+ depends on SPI_MASTER
+ depends on ACPI
+ depends on EFI
+ depends on SND_SOC
+ select CRC32_SARWATE
+ help
+ Say Y or M here to include TAS2781 SPI HD-audio side codec support
+ in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+ depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m
+
config SND_HDA_CODEC_REALTEK
tristate "Build Realtek HD-audio codec support"
select SND_HDA_GENERIC
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 058ca0a289e4..39bb30803b24 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -39,6 +39,7 @@ snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o
snd-hda-cs-dsp-ctls-y := hda_cs_dsp_ctl.o
snd-hda-scodec-component-y := hda_component.o
snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o
+snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o tas2781_spi_fwlib.o
# common driver
obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
@@ -70,6 +71,7 @@ obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o
obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o
obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o
obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o
+obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o
# this must be the last entry after codec drivers;
# otherwise the codec patches won't be hooked before the PCI probe
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index aa76d1c88589..38f2cf148477 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -6921,6 +6921,11 @@ static void tas2781_fixup_i2c(struct hda_codec *cdc,
comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1);
}
+static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2);
+}
+
static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc,
const struct hda_fixup *fix, int action)
{
@@ -7498,6 +7503,7 @@ enum {
ALC236_FIXUP_DELL_DUAL_CODECS,
ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
ALC287_FIXUP_TAS2781_I2C,
+ ALC245_FIXUP_TAS2781_SPI_2,
ALC287_FIXUP_YOGA7_14ARB7_I2C,
ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
ALC245_FIXUP_HP_X360_MUTE_LEDS,
@@ -9688,6 +9694,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
},
+ [ALC245_FIXUP_TAS2781_SPI_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_spi,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_LED,
+ },
[ALC287_FIXUP_YOGA7_14ARB7_I2C] = {
.type = HDA_FIXUP_FUNC,
.v.func = yoga7_14arb7_fixup_i2c,
@@ -10156,6 +10168,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8b8d, "HP", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8b8f, "HP", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b93, "HP Varcolac", ALC245_FIXUP_TAS2781_SPI_2),
SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
@@ -10212,6 +10225,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8cdf, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8ce0, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8daa, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2),
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
diff --git a/sound/pci/hda/tas2781-spi.h b/sound/pci/hda/tas2781-spi.h
new file mode 100644
index 000000000000..dd9d620d3bee
--- /dev/null
+++ b/sound/pci/hda/tas2781-spi.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+//
+// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2024 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2781 driver implements a flexible and configurable
+// algo coefficient setting for TAS2781 chips.
+//
+// Author: Baojun Xu <baojun.xu(a)ti.com>
+//
+
+#ifndef __TAS2781_SPI_H__
+#define __TAS2781_SPI_H__
+
+#define TASDEVICE_RATES \
+ (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200)
+
+#define TASDEVICE_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TASDEVICE_MAX_BOOK_NUM 256
+#define TASDEVICE_MAX_PAGE 256
+
+#define TASDEVICE_MAX_SIZE (TASDEVICE_MAX_BOOK_NUM * TASDEVICE_MAX_PAGE)
+
+/* PAGE Control Register (available in page0 of each book) */
+#define TASDEVICE_PAGE_SELECT 0x00
+#define TASDEVICE_BOOKCTL_PAGE 0x00
+#define TASDEVICE_BOOKCTL_REG GENMASK(7, 1)
+#define TASDEVICE_BOOK_ID(reg) ((reg & GENMASK(24, 16)) >> 16)
+#define TASDEVICE_PAGE_ID(reg) ((reg & GENMASK(15, 8)) >> 8)
+#define TASDEVICE_REG_ID(reg) ((reg & GENMASK(7, 1)) >> 1)
+#define TASDEVICE_PAGE_REG(reg) (reg & GENMASK(15, 1))
+#define TASDEVICE_REG(book, page, reg) (((book & GENMASK(7, 0)) << 16) | \
+ ((page & GENMASK(7, 0)) << 8) | \
+ ((reg & GENMASK(6, 0)) << 1))
+
+/* Software Reset */
+#define TAS2781_REG_SWRESET TASDEVICE_REG(0x0, 0x0, 0x01)
+#define TAS2781_REG_SWRESET_RESET BIT(0)
+
+/* Firmware block Checksum */
+#define TASDEVICE_CHECKSUM TASDEVICE_REG(0x0, 0x0, 0x7e)
+
+/* Volume control */
+#define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1a)
+#define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03)
+#define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1)
+
+#define TASDEVICE_CMD_SING_W 0x1
+#define TASDEVICE_CMD_BURST 0x2
+#define TASDEVICE_CMD_DELAY 0x3
+#define TASDEVICE_CMD_FIELD_W 0x4
+
+#define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ)
+
+#define TASDEVICE_CRC8_POLYNOMIAL 0x4d
+#define TASDEVICE_SPEAKER_CALIBRATION_SIZE 20
+
+#define TASDEVICE_CALIBRATION_DATA_NAME L"CALI_DATA"
+#define TASDEVICE_CALIBRATION_DATA_SIZE 256
+
+enum calib_data {
+ R0_VAL = 0,
+ INV_R0,
+ R0LOW,
+ POWER,
+ TLIM,
+ CALIB_MAX
+};
+
+struct tasdevice_priv {
+ struct tasdevice_fw *cali_data_fmw;
+ struct tasdevice_rca rcabin;
+ struct tasdevice_fw *fmw;
+ struct gpio_desc *reset;
+ struct mutex codec_lock;
+ struct regmap *regmap;
+ struct device *dev;
+ struct tm tm;
+
+ const char *acpi_subsystem_id;
+ unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE];
+ unsigned char coef_binaryname[64];
+ unsigned char rca_binaryname[64];
+ unsigned char dev_name[32];
+
+ unsigned int err_code;
+ bool is_loading;
+ bool is_loaderr;
+ int cur_book;
+ int cur_prog;
+ int cur_conf;
+ int fw_state;
+ int index;
+ int irq;
+ void *client;
+ void *codec;
+ bool force_fwload_status;
+ bool playback_started;
+ unsigned int calibration_data[CALIB_MAX];
+
+ int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv,
+ const struct firmware *fmw, int offset);
+ int (*fw_parse_program_data)(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int (*tasdevice_load_block)(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block);
+
+ int (*save_calibration)(struct tasdevice_priv *tas_priv);
+ void (*apply_calibration)(struct tasdevice_priv *tas_priv);
+};
+
+int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
+ unsigned int reg, unsigned int *value);
+int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv,
+ unsigned int reg, unsigned int value);
+int tasdevice_spi_dev_bulk_write(
+ struct tasdevice_priv *tas_priv, unsigned int reg,
+ unsigned char *p_data, unsigned int n_length);
+int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
+ unsigned int reg, unsigned char *p_data, unsigned int n_length);
+int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tasdevice,
+ unsigned int reg, unsigned int mask, unsigned int value);
+
+void tasdevice_spi_select_cfg_blk(void *context, int conf_no,
+ unsigned char block_type);
+void tasdevice_spi_config_info_remove(void *context);
+int tasdevice_spi_dsp_parser(void *context);
+int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw);
+void tasdevice_spi_dsp_remove(void *context);
+void tasdevice_spi_calbin_remove(void *context);
+int tasdevice_spi_select_tuningprm_cfg(void *context, int prm,
+ int cfg_no, int rca_conf_no);
+int tasdevice_spi_prmg_load(void *context, int prm_no);
+int tasdevice_spi_prmg_calibdata_load(void *context, int prm_no);
+void tasdevice_spi_tuning_switch(void *context, int state);
+int tas2781_spi_load_calibration(void *context, char *file_name,
+ unsigned short i);
+#endif /* __TAS2781_SPI_H__ */
diff --git a/sound/pci/hda/tas2781_hda_spi.c b/sound/pci/hda/tas2781_hda_spi.c
new file mode 100644
index 000000000000..e9b77f602c75
--- /dev/null
+++ b/sound/pci/hda/tas2781_hda_spi.c
@@ -0,0 +1,1266 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA SPI driver
+//
+// Copyright 2024 Texas Instruments, Inc.
+//
+// Author: Baojun Xu <baojun.xu(a)ti.com>
+
+#include <linux/acpi.h>
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/firmware.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <sound/hda_codec.h>
+#include <sound/soc.h>
+#include <sound/tas2781-dsp.h>
+#include <sound/tlv.h>
+#include <sound/tas2781-tlv.h>
+
+#include "tas2781-spi.h"
+
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_component.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
+
+/*
+ * No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD
+ * Define two controls, one is Volume control callbacks, the other is
+ * flag setting control callbacks.
+ */
+
+/* Volume control callbacks for tas2781 */
+#define ACARD_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \
+ xhandler_get, xhandler_put, tlv_array) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw_range, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) { \
+ .reg = xreg, .rreg = xreg, \
+ .shift = xshift, .rshift = xshift,\
+ .min = xmin, .max = xmax, .invert = xinvert, \
+ } \
+}
+
+/* Flag control callbacks for tas2781 */
+#define ACARD_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD, \
+ .name = xname, \
+ .info = snd_ctl_boolean_mono_info, \
+ .get = xhandler_get, \
+ .put = xhandler_put, \
+ .private_value = xdata, \
+}
+
+struct tas2781_hda {
+ struct tasdevice_priv *priv;
+ struct acpi_device *dacpi;
+ struct snd_kcontrol *dsp_prog_ctl;
+ struct snd_kcontrol *dsp_conf_ctl;
+ struct snd_kcontrol *snd_ctls[3];
+ struct snd_kcontrol *prof_ctl;
+};
+
+static const struct reg_default tas2781_reg_defaults[] = {
+ /* Default values for ROM mode. Activated. */
+ { 0x04, 0x1a }, /* AMP inactive. */
+ { 0x14, 0x3a }, /* Enable both channels. */
+ { 0xb8, 0x99 }, /* Enable power up on clock. */
+};
+
+static const struct regmap_range_cfg tasdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = TASDEVICE_MAX_SIZE,
+ .selector_reg = TASDEVICE_PAGE_SELECT,
+ .selector_mask = GENMASK(7, 0),
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = TASDEVICE_MAX_PAGE,
+ },
+};
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .zero_flag_mask = true,
+ .cache_type = REGCACHE_NONE,
+ .ranges = tasdevice_ranges,
+ .num_ranges = ARRAY_SIZE(tasdevice_ranges),
+ .max_register = TASDEVICE_MAX_SIZE,
+ .reg_defaults = tas2781_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas2781_reg_defaults),
+};
+
+static int tasdevice_spi_switch_book(struct tasdevice_priv *tas_priv, int reg)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ if (tas_priv->cur_book != TASDEVICE_BOOK_ID(reg)) {
+ ret = regmap_write(map, TASDEVICE_BOOKCTL_REG,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "Switch Book E=%d\n", ret);
+ return ret;
+ }
+ tas_priv->cur_book = TASDEVICE_BOOK_ID(reg);
+ }
+ return 0;
+}
+
+int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv, unsigned int reg,
+ unsigned int *val)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ ret = tasdevice_spi_switch_book(tas_priv, reg);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * In our TAS2781 SPI mode, if read from other book (not book 0),
+ * or read from page number larger than 1 in book 0, one more byte
+ * read is needed, and first byte is a dummy byte, need to be ignored.
+ */
+ if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
+ unsigned char data[2];
+
+ ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1,
+ data, sizeof(data));
+ *val = data[1];
+ } else {
+ ret = regmap_read(map, TASDEVICE_PAGE_REG(reg) | 1, val);
+ }
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv, unsigned int reg,
+ unsigned int value)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ ret = tasdevice_spi_switch_book(tas_priv, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(map, TASDEVICE_PAGE_REG(reg), value);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int tasdevice_spi_dev_bulk_write(struct tasdevice_priv *tas_priv,
+ unsigned int reg, unsigned char *data, unsigned int len)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ ret = tasdevice_spi_switch_book(tas_priv, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_bulk_write(map, TASDEVICE_PAGE_REG(reg), data, len);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
+ unsigned int reg, unsigned char *data, unsigned int len)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ ret = tasdevice_spi_switch_book(tas_priv, reg);
+ if (ret < 0)
+ return ret;
+
+ if (len > TASDEVICE_MAX_PAGE)
+ len = TASDEVICE_MAX_PAGE;
+ /*
+ * In our TAS2781 SPI mode, if read from other book (not book 0),
+ * or read from page number larger than 1 in book 0, one more byte
+ * read is needed, and first byte is a dummy byte, need to be ignored.
+ */
+ if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
+ unsigned char buf[TASDEVICE_MAX_PAGE+1];
+
+ ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1,
+ buf, len + 1);
+ memcpy(data, buf + 1, len);
+ } else {
+ ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1,
+ data, len);
+ }
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tas_priv,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ struct regmap *map = tas_priv->regmap;
+ int ret, val;
+
+ /*
+ * In our TAS2781 SPI mode, read/write was masked in last bit of
+ * address, it cause regmap_update_bits() not work as expected.
+ */
+ ret = tasdevice_spi_dev_read(tas_priv, reg, &val);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ return ret;
+ }
+ ret = regmap_write(map, TASDEVICE_PAGE_REG(reg),
+ (val & ~mask) | (mask & value));
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static void tas2781_spi_reset(struct tasdevice_priv *tas_dev)
+{
+ int ret;
+
+ if (tas_dev->reset) {
+ gpiod_set_value_cansleep(tas_dev->reset, 0);
+ fsleep(800);
+ gpiod_set_value_cansleep(tas_dev->reset, 1);
+ } else {
+ ret = tasdevice_spi_dev_write(tas_dev, TAS2781_REG_SWRESET,
+ TAS2781_REG_SWRESET_RESET);
+ if (ret < 0)
+ dev_err(tas_dev->dev, "dev sw-reset fail, %d\n", ret);
+ }
+ fsleep(1000);
+}
+
+static int tascodec_spi_init(struct tasdevice_priv *tas_priv, void *codec,
+ struct module *module,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ int ret;
+
+ /*
+ * Codec Lock Hold to ensure that codec_probe and firmware parsing and
+ * loading do not simultaneously execute.
+ */
+ guard(mutex)(&tas_priv->codec_lock);
+
+ ret = scnprintf(tas_priv->rca_binaryname,
+ sizeof(tas_priv->rca_binaryname), "%sRCA%d.bin",
+ tas_priv->dev_name, tas_priv->index);
+ if (ret <= 0) {
+ dev_err(tas_priv->dev, "rca name err:0x%08x\n", ret);
+ return ret;
+ }
+ crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
+ tas_priv->codec = codec;
+ ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
+ tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
+ cont);
+ if (ret)
+ dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static void tasdevice_spi_init(struct tasdevice_priv *tas_priv)
+{
+ tas_priv->cur_prog = -1;
+ tas_priv->cur_conf = -1;
+
+ tas_priv->cur_book = -1;
+ tas_priv->cur_prog = -1;
+ tas_priv->cur_conf = -1;
+
+ mutex_init(&tas_priv->codec_lock);
+}
+
+static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask;
+ int max = mc->max;
+ int val, ret;
+
+ mask = rounddown_pow_of_two(max);
+ mask <<= mc->shift;
+ val = clamp(invert ? max - ucontrol->value.integer.value[0] :
+ ucontrol->value.integer.value[0], 0, max);
+ ret = tasdevice_spi_dev_update_bits(tas_priv,
+ mc->reg, mask, (unsigned int)(val << mc->shift));
+ if (ret)
+ dev_err(tas_priv->dev, "set AMP vol error in dev %d\n",
+ tas_priv->index);
+
+ return ret;
+}
+
+static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask = 0;
+ int max = mc->max;
+ int ret, val;
+
+ /* Read the primary device */
+ ret = tasdevice_spi_dev_read(tas_priv, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
+ return ret;
+ }
+
+ mask = rounddown_pow_of_two(max);
+ mask <<= mc->shift;
+ val = (val & mask) >> mc->shift;
+ val = clamp(invert ? max - val : val, 0, max);
+ ucontrol->value.integer.value[0] = val;
+
+ return ret;
+}
+
+static int tasdevice_spi_digital_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int val, ret;
+
+ val = clamp(invert ? max - ucontrol->value.integer.value[0] :
+ ucontrol->value.integer.value[0], 0, max);
+ ret = tasdevice_spi_dev_write(tas_priv, mc->reg, (unsigned int)val);
+ if (ret)
+ dev_err(tas_priv->dev, "set digital vol err in dev %d\n",
+ tas_priv->index);
+
+ return ret;
+
+}
+
+static int tasdevice_spi_digital_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int ret, val;
+
+ /* Read the primary device as the whole */
+ ret = tasdevice_spi_dev_read(tas_priv, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get digital vol err\n", __func__);
+ return ret;
+ }
+
+ val = clamp(invert ? max - val : val, 0, max);
+ ucontrol->value.integer.value[0] = val;
+
+ return ret;
+}
+
+static int tas2781_read_acpi(struct tas2781_hda *tas_hda, const char *hid,
+ int id)
+{
+ struct tasdevice_priv *p = tas_hda->priv;
+ struct acpi_device *adev;
+ struct device *physdev;
+ u32 values[HDA_MAX_COMPONENTS];
+ const char *property;
+ size_t nval;
+ int ret, i;
+
+ adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+ if (!adev) {
+ dev_err(p->dev, "Failed to find ACPI device: %s\n", hid);
+ return -ENODEV;
+ }
+
+ strscpy(p->dev_name, hid, sizeof(p->dev_name));
+ tas_hda->dacpi = adev;
+ physdev = get_device(acpi_get_first_physical_node(adev));
+ acpi_dev_put(adev);
+
+ /* No side-effect to the playback even if subsystem_id is NULL */
+ p->acpi_subsystem_id = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
+
+ property = "ti,dev-index";
+ ret = device_property_count_u32(physdev, property);
+ if ((ret <= 0) || (ret > ARRAY_SIZE(values))) {
+ ret = -EINVAL;
+ goto err;
+ }
+ nval = ret;
+
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+
+ p->index = U8_MAX;
+ for (i = 0; i < nval; i++) {
+ if (values[i] == id) {
+ p->index = i;
+ break;
+ }
+ }
+ if (p->index == U8_MAX) {
+ dev_dbg(p->dev, "No index found in %s\n", property);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (p->index == 0) {
+ /* All of amps share same RESET pin. */
+ p->reset = devm_gpiod_get_index_optional(physdev, "reset",
+ p->index, GPIOD_OUT_LOW);
+ if (IS_ERR(p->reset)) {
+ dev_err_probe(p->dev, ret, "Failed on reset GPIO\n");
+ goto err;
+ }
+ }
+
+ put_device(physdev);
+
+ return 0;
+
+err:
+ dev_err(p->dev, "read acpi error, ret: %d\n", ret);
+ put_device(physdev);
+ acpi_dev_put(adev);
+
+ return ret;
+}
+
+static void tas2781_hda_playback_hook(struct device *dev, int action)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ if ((action == HDA_GEN_PCM_ACT_OPEN) ||
+ (action == HDA_GEN_PCM_ACT_CLOSE)) {
+ if (action == HDA_GEN_PCM_ACT_OPEN)
+ pm_runtime_get_sync(dev);
+ guard(mutex)(&tas_hda->priv->codec_lock);
+ tasdevice_spi_tuning_switch(tas_hda->priv, 0);
+ if (action == HDA_GEN_PCM_ACT_CLOSE) {
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
+ } else {
+ dev_dbg(tas_hda->priv->dev,
+ "Playback action not supported: %d\n", action);
+ }
+}
+
+static int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1;
+
+ return 0;
+}
+
+static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id;
+
+ return 0;
+}
+
+static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ int max = tas_priv->rcabin.ncfgs - 1;
+ int val;
+
+ val = clamp(ucontrol->value.integer.value[0], 0, max);
+ if (tas_priv->rcabin.profile_cfg_id != val) {
+ tas_priv->rcabin.profile_cfg_id = val;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->fmw->nr_programs - 1;
+
+ return 0;
+}
+
+static int tasdevice_info_config(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->fmw->nr_configurations - 1;
+
+ return 0;
+}
+
+static int tasdevice_program_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_prog;
+
+ return 0;
+}
+
+static int tasdevice_program_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ int nr_program = ucontrol->value.integer.value[0];
+ int max = tas_priv->fmw->nr_programs - 1;
+ int val;
+
+ val = clamp(nr_program, 0, max);
+
+ if (tas_priv->cur_prog != val) {
+ tas_priv->cur_prog = val;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int tasdevice_config_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_conf;
+
+ return 0;
+}
+
+static int tasdevice_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ int max = tas_priv->fmw->nr_configurations - 1;
+ int val;
+
+ val = clamp(ucontrol->value.integer.value[0], 0, max);
+
+ if (tas_priv->cur_conf != val) {
+ tas_priv->cur_conf = val;
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * tas2781_digital_getvol - get the volum control
+ * @kcontrol: control pointer
+ * @ucontrol: User data
+ *
+ * Customer Kcontrol for tas2781 is primarily for regmap booking, paging
+ * depends on internal regmap mechanism.
+ * tas2781 contains book and page two-level register map, especially
+ * book switching will set the register BXXP00R7F, after switching to the
+ * correct book, then leverage the mechanism for paging to access the
+ * register.
+ *
+ * Return 0 if succeeded.
+ */
+static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_spi_digital_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ return tasdevice_spi_amp_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ /* The check of the given value is in tasdevice_digital_putvol. */
+ return tasdevice_spi_digital_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ /* The check of the given value is in tasdevice_amp_putvol. */
+ return tasdevice_spi_amp_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status;
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ str_on_off(tas_priv->force_fwload_status));
+
+ return 0;
+}
+
+static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ bool change, val = (bool)ucontrol->value.integer.value[0];
+
+ if (tas_priv->force_fwload_status == val) {
+ change = false;
+ } else {
+ change = true;
+ tas_priv->force_fwload_status = val;
+ }
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ str_on_off(tas_priv->force_fwload_status));
+
+ return change;
+}
+
+static const struct snd_kcontrol_new tas2781_snd_controls[] = {
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain 0", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, amp_vol_tlv),
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain 0", TAS2781_DVC_LVL,
+ 0, 0, 200, 1, tas2781_digital_getvol,
+ tas2781_digital_putvol, dvc_tlv),
+ ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load 0", 0,
+ tas2781_force_fwload_get, tas2781_force_fwload_put),
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain 1", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, amp_vol_tlv),
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain 1", TAS2781_DVC_LVL,
+ 0, 0, 200, 1, tas2781_digital_getvol,
+ tas2781_digital_putvol, dvc_tlv),
+ ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load 1", 0,
+ tas2781_force_fwload_get, tas2781_force_fwload_put),
+};
+
+static const struct snd_kcontrol_new tas2781_prof_ctrl[] = {
+{
+ .name = "Speaker Profile Id - 0",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_profile,
+ .get = tasdevice_get_profile_id,
+ .put = tasdevice_set_profile_id,
+},
+{
+ .name = "Speaker Profile Id - 1",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_profile,
+ .get = tasdevice_get_profile_id,
+ .put = tasdevice_set_profile_id,
+},
+};
+static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl[] = {
+{
+ .name = "Speaker Program Id 0",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_programs,
+ .get = tasdevice_program_get,
+ .put = tasdevice_program_put,
+},
+{
+ .name = "Speaker Program Id 1",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_programs,
+ .get = tasdevice_program_get,
+ .put = tasdevice_program_put,
+},
+};
+
+static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl[] = {
+{
+ .name = "Speaker Config Id 0",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_config,
+ .get = tasdevice_config_get,
+ .put = tasdevice_config_put,
+},
+{
+ .name = "Speaker Config Id 1",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_config,
+ .get = tasdevice_config_get,
+ .put = tasdevice_config_put,
+},
+};
+
+static void tas2781_apply_calib(struct tasdevice_priv *tas_priv)
+{
+ static const unsigned int cali_array[CALIB_MAX] = {
+ TASDEVICE_REG(0, 0x17, 0x74),
+ TASDEVICE_REG(0, 0x18, 0x0c),
+ TASDEVICE_REG(0, 0x18, 0x14),
+ TASDEVICE_REG(0, 0x13, 0x70),
+ TASDEVICE_REG(0, 0x18, 0x7c),
+ };
+ int j, rc;
+
+ /*
+ * If no calibration data exist in tasdevice_priv *tas_priv,
+ * calibration apply will be ignored, and use default values
+ * in firmware binary, which was loaded during firmware download.
+ */
+ if (tas_priv->calibration_data[0] == 0)
+ return;
+ /*
+ * Calibration data was saved in tasdevice_priv *tas_priv as:
+ * unsigned int calibration_data[CALIB_MAX];
+ * and every data (in 4 bytes) will be saved in register which in
+ * book 0, and page number in page_array[], offset was saved in
+ * rgno_array[].
+ */
+ for (j = 0; j < CALIB_MAX; j++) {
+ rc = tasdevice_spi_dev_bulk_write(tas_priv, cali_array[j],
+ (unsigned char *)&tas_priv->calibration_data[j], 4);
+ if (rc < 0)
+ dev_err(tas_priv->dev,
+ "chn %d calib %d bulk_wr err = %d\n",
+ tas_priv->index, j, rc);
+ }
+}
+
+/*
+ * Update the calibration data, including speaker impedance, f0, etc,
+ * into algo. Calibrate data is done by manufacturer in the factory.
+ * These data are used by Algo for calculating the speaker temperature,
+ * speaker membrane excursion and f0 in real time during playback.
+ * Calibration data format in EFI is V2, since 2024.
+ */
+static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
+{
+ /*
+ * GUID was used for data access in BIOS, it was provided by board
+ * manufactory, like HP: "{02f9af02-7734-4233-b43d-93fe5aa35db3}"
+ */
+ efi_guid_t efi_guid =
+ EFI_GUID(0x02f9af02, 0x7734, 0x4233,
+ 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3);
+ static efi_char16_t efi_name[] = TASDEVICE_CALIBRATION_DATA_NAME;
+ unsigned char data[TASDEVICE_CALIBRATION_DATA_SIZE];
+ struct tm *tm = &tas_priv->tm;
+ unsigned long total_sz = 0;
+ unsigned int attr, crc;
+ unsigned int *tmp_val;
+ efi_status_t status;
+
+ tas_priv->calibration_data[0] = 0;
+ status = efi.get_variable(efi_name, &efi_guid, &attr, &total_sz,
+ data);
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ if (total_sz > TASDEVICE_CALIBRATION_DATA_SIZE)
+ return -ENOMEM;
+ /* Get variable contents into buffer */
+ status = efi.get_variable(efi_name, &efi_guid, &attr,
+ &total_sz, data);
+ }
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ tmp_val = (unsigned int *)data;
+ if (tmp_val[0] == 2781) {
+ /*
+ * New features were added in calibrated Data V2:
+ * 1. Added some the fields to store the link_id and
+ * uniqie_id for multi-link solutions
+ * 2. Support flexible number of devices instead of
+ * fixed one in V1.
+ * Layout of calibrated data V2 in UEFI(total 256 bytes):
+ * ChipID (2781, 4 bytes)
+ * Device-Sum (4 bytes)
+ * TimeStamp of Calibration (4 bytes)
+ * for (i = 0; i < Device-Sum; i++) {
+ * Device #i index_info () {
+ * SDW link id (2bytes)
+ * SDW unique_id (2bytes)
+ * }
+ * Calibrated Data of Device #i (20 bytes)
+ * }
+ * CRC (4 bytes)
+ * Reserved (the rest)
+ */
+ crc = crc32(~0, data, 3 + tmp_val[0] * 6) ^ ~0;
+ dev_dbg(tas_priv->dev, "cali crc 0x%08x PK tmp_val 0x%08x\n",
+ crc, tmp_val[3 + tmp_val[0] * 6]);
+
+ if (crc == tmp_val[3 + tmp_val[0] * 6]) {
+ time64_to_tm(tmp_val[2], 0, tm);
+ dev_dbg(tas_priv->dev, "Calibration date %pt.\n", tm);
+ for (int i = 0; i < CALIB_MAX; i++)
+ tas_priv->calibration_data[i] =
+ tmp_val[3 + tas_priv->index * 6 + i];
+ tas_priv->apply_calibration(tas_priv);
+ }
+ } else {
+ /*
+ * Calibration data is in V1 format.
+ * struct cali_data {
+ * char cali_data[20];
+ * }
+ *
+ * struct {
+ * struct cali_data cali_data[4];
+ * int TimeStamp of Calibration (4 bytes)
+ * int CRC (4 bytes)
+ * } ueft;
+ */
+ crc = crc32(~0, data, 84) ^ ~0;
+ dev_dbg(tas_priv->dev, "cali crc 0x%08x PK tmp_val 0x%08x\n",
+ crc, tmp_val[21]);
+
+ if (crc == tmp_val[21]) {
+ time64_to_tm(tmp_val[20], 0, tm);
+ dev_dbg(tas_priv->dev, "Calibration date %pt.\n", tm);
+ for (int i = 0; i < CALIB_MAX; i++)
+ tas_priv->calibration_data[i] =
+ tmp_val[tas_priv->index * 5 + i];
+ tas_priv->apply_calibration(tas_priv);
+ }
+ }
+
+ return 0;
+}
+
+static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda)
+{
+ struct hda_codec *codec = tas_hda->priv->codec;
+
+ if (tas_hda->dsp_prog_ctl)
+ snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl);
+
+ if (tas_hda->dsp_conf_ctl)
+ snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl);
+
+ for (int i = ARRAY_SIZE(tas_hda->snd_ctls) - 1; i >= 0; i--)
+ if (tas_hda->snd_ctls[i])
+ snd_ctl_remove(codec->card, tas_hda->snd_ctls[i]);
+
+ if (tas_hda->prof_ctl)
+ snd_ctl_remove(codec->card, tas_hda->prof_ctl);
+}
+
+static void tasdev_fw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
+ struct hda_codec *codec = tas_priv->codec;
+ int i, j, ret;
+
+ pm_runtime_get_sync(tas_priv->dev);
+ guard(mutex)(&tas_priv->codec_lock);
+
+ ret = tasdevice_spi_rca_parser(tas_priv, fmw);
+ if (ret)
+ goto out;
+
+ /* Add control one time only. */
+ tas_hda->prof_ctl = snd_ctl_new1(&tas2781_prof_ctrl[tas_priv->index],
+ tas_priv);
+ ret = snd_ctl_add(codec->card, tas_hda->prof_ctl);
+ if (ret) {
+ dev_err(tas_priv->dev, "Failed to add KControl %s = %d\n",
+ tas2781_prof_ctrl[tas_priv->index].name, ret);
+ goto out;
+ }
+ j = tas_priv->index * 3;
+ for (i = 0; i < ARRAY_SIZE(tas2781_snd_controls)/2; i++) {
+ tas_hda->snd_ctls[i] =
+ snd_ctl_new1(&tas2781_snd_controls[i + j], tas_priv);
+ ret = snd_ctl_add(codec->card, tas_hda->snd_ctls[i]);
+ if (ret) {
+ dev_err(tas_priv->dev,
+ "Failed to add snd control %s = %d\n",
+ tas2781_snd_controls[i + j].name, ret);
+ goto out;
+ }
+ }
+
+ tasdevice_spi_dsp_remove(tas_priv);
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+ scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%08X-%01d.bin",
+ codec->core.subsystem_id, tas_priv->index);
+ ret = tasdevice_spi_dsp_parser(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "dspfw load %s error\n",
+ tas_priv->coef_binaryname);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+
+ /* Add control one time only. */
+ tas_hda->dsp_prog_ctl =
+ snd_ctl_new1(&tas2781_dsp_prog_ctrl[tas_priv->index],
+ tas_priv);
+ ret = snd_ctl_add(codec->card, tas_hda->dsp_prog_ctl);
+ if (ret) {
+ dev_err(tas_priv->dev,
+ "Failed to add KControl %s = %d\n",
+ tas2781_dsp_prog_ctrl[tas_priv->index].name, ret);
+ goto out;
+ }
+
+ tas_hda->dsp_conf_ctl = snd_ctl_new1(
+ &tas2781_dsp_conf_ctrl[tas_priv->index], tas_priv);
+ ret = snd_ctl_add(codec->card, tas_hda->dsp_conf_ctl);
+ if (ret) {
+ dev_err(tas_priv->dev, "Failed to add KControl %s = %d\n",
+ tas2781_dsp_conf_ctrl[tas_priv->index].name, ret);
+ goto out;
+ }
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ tasdevice_spi_prmg_load(tas_priv, 0);
+ if (tas_priv->fmw->nr_programs > 0)
+ tas_priv->cur_prog = 0;
+ if (tas_priv->fmw->nr_configurations > 0)
+ tas_priv->cur_conf = 0;
+
+ tasdevice_spi_tuning_switch(tas_hda->priv, 0);
+
+ /*
+ * If calibrated data occurs error, dsp will still works with default
+ * calibrated data inside algo.
+ */
+ tas_priv->save_calibration(tas_priv);
+
+out:
+ if (fmw)
+ release_firmware(fmw);
+ pm_runtime_mark_last_busy(tas_hda->priv->dev);
+ pm_runtime_put_autosuspend(tas_hda->priv->dev);
+}
+
+static int tas2781_hda_bind(struct device *dev, struct device *master,
+ void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component *comps = master_data;
+ struct hda_codec *codec;
+ int ret;
+
+ if (tas_hda->priv->index < 0 ||
+ tas_hda->priv->index >= HDA_MAX_COMPONENTS ||
+ (!comps))
+ return -EINVAL;
+
+ comps = &comps[tas_hda->priv->index];
+ if (comps->dev)
+ return -EBUSY;
+
+ codec = comps->codec;
+
+ pm_runtime_get_sync(dev);
+
+ comps->dev = dev;
+
+ strscpy(comps->name, dev_name(dev), sizeof(comps->name));
+
+ ret = tascodec_spi_init(tas_hda->priv, codec, THIS_MODULE,
+ tasdev_fw_ready);
+ if (!ret)
+ comps->playback_hook = tas2781_hda_playback_hook;
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static void tas2781_hda_unbind(struct device *dev, struct device *master,
+ void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component *comps = master_data;
+
+ comps = &comps[tas_hda->priv->index];
+
+ if (comps->dev == dev) {
+ comps->dev = NULL;
+ memset(comps->name, 0, sizeof(comps->name));
+ comps->playback_hook = NULL;
+ }
+
+ tas2781_hda_remove_controls(tas_hda);
+
+ tasdevice_spi_config_info_remove(tas_hda->priv);
+ tasdevice_spi_dsp_remove(tas_hda->priv);
+
+ tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static const struct component_ops tas2781_hda_comp_ops = {
+ .bind = tas2781_hda_bind,
+ .unbind = tas2781_hda_unbind,
+};
+
+static void tas2781_hda_remove(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ pm_runtime_get_sync(tas_hda->priv->dev);
+ pm_runtime_disable(tas_hda->priv->dev);
+
+ component_del(tas_hda->priv->dev, &tas2781_hda_comp_ops);
+
+ pm_runtime_put_noidle(tas_hda->priv->dev);
+
+ mutex_destroy(&tas_hda->priv->codec_lock);
+}
+
+static int tas2781_hda_spi_probe(struct spi_device *spi)
+{
+ struct tasdevice_priv *tas_priv;
+ struct tas2781_hda *tas_hda;
+ const char *device_name;
+ int ret = 0;
+
+ tas_hda = devm_kzalloc(&spi->dev, sizeof(*tas_hda), GFP_KERNEL);
+ if (!tas_hda)
+ return -ENOMEM;
+
+ spi->max_speed_hz = TAS2781_SPI_MAX_FREQ;
+
+ tas_priv = devm_kzalloc(&spi->dev, sizeof(*tas_priv), GFP_KERNEL);
+ if (!tas_priv)
+ goto err;
+ tas_priv->dev = &spi->dev;
+ tas_hda->priv = tas_priv;
+ tas_priv->regmap = devm_regmap_init_spi(spi, &tasdevice_regmap);
+ if (IS_ERR(tas_priv->regmap)) {
+ ret = PTR_ERR(tas_priv->regmap);
+ dev_err(tas_priv->dev, "Failed to allocate regmap: %d\n",
+ ret);
+ goto err;
+ }
+ if (strstr(dev_name(&spi->dev), "TXNW2781")) {
+ device_name = "TXNW2781";
+ tas_priv->save_calibration = tas2781_save_calibration;
+ tas_priv->apply_calibration = tas2781_apply_calib;
+ } else {
+ goto err;
+ }
+
+ tas_priv->irq = spi->irq;
+ dev_set_drvdata(&spi->dev, tas_hda);
+ ret = tas2781_read_acpi(tas_hda, device_name,
+ spi_get_chipselect(spi, 0));
+ if (ret)
+ return dev_err_probe(tas_priv->dev, ret,
+ "Platform not supported\n");
+
+ tasdevice_spi_init(tas_priv);
+
+ pm_runtime_set_autosuspend_delay(tas_priv->dev, 3000);
+ pm_runtime_use_autosuspend(tas_priv->dev);
+ pm_runtime_mark_last_busy(tas_priv->dev);
+ pm_runtime_set_active(tas_priv->dev);
+ pm_runtime_get_noresume(tas_priv->dev);
+ pm_runtime_enable(tas_priv->dev);
+
+ pm_runtime_put_autosuspend(tas_priv->dev);
+
+ tas2781_spi_reset(tas_priv);
+
+ ret = component_add(tas_priv->dev, &tas2781_hda_comp_ops);
+ if (ret) {
+ dev_err(tas_priv->dev, "Register component failed: %d\n", ret);
+ pm_runtime_disable(tas_priv->dev);
+ }
+
+err:
+ if (ret)
+ tas2781_hda_remove(&spi->dev);
+
+ return ret;
+}
+
+static void tas2781_hda_spi_remove(struct spi_device *spi)
+{
+ tas2781_hda_remove(&spi->dev);
+}
+
+static int tas2781_runtime_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ if (tas_hda->priv->playback_started) {
+ tasdevice_spi_tuning_switch(tas_hda->priv, 1);
+ tas_hda->priv->playback_started = false;
+ }
+
+ tas_hda->priv->cur_book = -1;
+ tas_hda->priv->cur_conf = -1;
+
+ return 0;
+}
+
+static int tas2781_runtime_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ tasdevice_spi_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog);
+
+ tasdevice_spi_tuning_switch(tas_hda->priv, 0);
+
+ tas_hda->priv->apply_calibration(tas_hda->priv);
+
+ return 0;
+}
+
+static int tas2781_system_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ /* Shutdown chip before system suspend */
+ tasdevice_spi_tuning_switch(tas_hda->priv, 1);
+
+ /*
+ * Reset GPIO may be shared, so cannot reset here.
+ * However beyond this point, amps may be powered down.
+ */
+ return 0;
+}
+
+static int tas2781_system_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ tas_hda->priv->cur_book = -1;
+ tas_hda->priv->cur_conf = -1;
+
+ tas2781_spi_reset(tas_hda->priv);
+ tasdevice_spi_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog);
+
+ if (tas_hda->priv->playback_started)
+ tasdevice_spi_tuning_switch(tas_hda->priv, 0);
+
+ /*
+ * If calibrated data occurs error, dsp will still work with default
+ * calibrated data inside algo.
+ */
+ tas_hda->priv->apply_calibration(tas_hda->priv);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tas2781_hda_pm_ops = {
+ RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume)
+};
+
+static const struct spi_device_id tas2781_hda_spi_id[] = {
+ { "tas2781-hda", },
+ {}
+};
+
+static const struct acpi_device_id tas2781_acpi_hda_match[] = {
+ {"TXNW2781", },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match);
+
+static struct spi_driver tas2781_hda_spi_driver = {
+ .driver = {
+ .name = "tas2781-hda",
+ .acpi_match_table = tas2781_acpi_hda_match,
+ .pm = &tas2781_hda_pm_ops,
+ },
+ .id_table = tas2781_hda_spi_id,
+ .probe = tas2781_hda_spi_probe,
+ .remove = tas2781_hda_spi_remove,
+};
+module_spi_driver(tas2781_hda_spi_driver);
+
+MODULE_DESCRIPTION("TAS2781 HDA SPI Driver");
+MODULE_AUTHOR("Baojun, Xu, <baojun.xug(a)ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/tas2781_spi_fwlib.c b/sound/pci/hda/tas2781_spi_fwlib.c
new file mode 100644
index 000000000000..6e6a12df56e2
--- /dev/null
+++ b/sound/pci/hda/tas2781_spi_fwlib.c
@@ -0,0 +1,2101 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA SPI driver
+//
+// Copyright 2024 Texas Instruments, Inc.
+//
+// Author: Baojun Xu <baojun.xu(a)ti.com>
+
+#include <asm/unaligned.h>
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tas2781-dsp.h>
+#include <sound/tlv.h>
+
+#include "tas2781-spi.h"
+
+#define OFFSET_ERROR_BIT BIT(31)
+
+#define ERROR_PRAM_CRCCHK 0x0000000
+#define ERROR_YRAM_CRCCHK 0x0000001
+#define PPC_DRIVER_CRCCHK 0x00000200
+
+#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c)
+#define TAS2781_YRAM_BOOK1 140
+#define TAS2781_YRAM1_PAGE 42
+#define TAS2781_YRAM1_START_REG 88
+
+#define TAS2781_YRAM2_START_PAGE 43
+#define TAS2781_YRAM2_END_PAGE 49
+#define TAS2781_YRAM2_START_REG 8
+#define TAS2781_YRAM2_END_REG 127
+
+#define TAS2781_YRAM3_PAGE 50
+#define TAS2781_YRAM3_START_REG 8
+#define TAS2781_YRAM3_END_REG 27
+
+/* should not include B0_P53_R44-R47 */
+#define TAS2781_YRAM_BOOK2 0
+#define TAS2781_YRAM4_START_PAGE 50
+#define TAS2781_YRAM4_END_PAGE 60
+
+#define TAS2781_YRAM5_PAGE 61
+#define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG
+#define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG
+
+#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10
+#define MAIN_ALL_DEVICES_1X 0x01
+#define MAIN_DEVICE_A_1X 0x02
+#define MAIN_DEVICE_B_1X 0x03
+#define MAIN_DEVICE_C_1X 0x04
+#define MAIN_DEVICE_D_1X 0x05
+#define COEFF_DEVICE_A_1X 0x12
+#define COEFF_DEVICE_B_1X 0x13
+#define COEFF_DEVICE_C_1X 0x14
+#define COEFF_DEVICE_D_1X 0x15
+#define PRE_DEVICE_A_1X 0x22
+#define PRE_DEVICE_B_1X 0x23
+#define PRE_DEVICE_C_1X 0x24
+#define PRE_DEVICE_D_1X 0x25
+#define PRE_SOFTWARE_RESET_DEVICE_A 0x41
+#define PRE_SOFTWARE_RESET_DEVICE_B 0x42
+#define PRE_SOFTWARE_RESET_DEVICE_C 0x43
+#define PRE_SOFTWARE_RESET_DEVICE_D 0x44
+#define POST_SOFTWARE_RESET_DEVICE_A 0x45
+#define POST_SOFTWARE_RESET_DEVICE_B 0x46
+#define POST_SOFTWARE_RESET_DEVICE_C 0x47
+#define POST_SOFTWARE_RESET_DEVICE_D 0x48
+
+struct tas_crc {
+ unsigned char offset;
+ unsigned char len;
+};
+
+struct blktyp_devidx_map {
+ unsigned char blktyp;
+ unsigned char dev_idx;
+};
+
+/* fixed m68k compiling issue: mapping table can save code field */
+static const struct blktyp_devidx_map ppc3_tas2781_mapping_table[] = {
+ { MAIN_ALL_DEVICES_1X, 0x80 },
+ { MAIN_DEVICE_A_1X, 0x81 },
+ { COEFF_DEVICE_A_1X, 0xC1 },
+ { PRE_DEVICE_A_1X, 0xC1 },
+ { PRE_SOFTWARE_RESET_DEVICE_A, 0xC1 },
+ { POST_SOFTWARE_RESET_DEVICE_A, 0xC1 },
+ { MAIN_DEVICE_B_1X, 0x82 },
+ { COEFF_DEVICE_B_1X, 0xC2 },
+ { PRE_DEVICE_B_1X, 0xC2 },
+ { PRE_SOFTWARE_RESET_DEVICE_B, 0xC2 },
+ { POST_SOFTWARE_RESET_DEVICE_B, 0xC2 },
+ { MAIN_DEVICE_C_1X, 0x83 },
+ { COEFF_DEVICE_C_1X, 0xC3 },
+ { PRE_DEVICE_C_1X, 0xC3 },
+ { PRE_SOFTWARE_RESET_DEVICE_C, 0xC3 },
+ { POST_SOFTWARE_RESET_DEVICE_C, 0xC3 },
+ { MAIN_DEVICE_D_1X, 0x84 },
+ { COEFF_DEVICE_D_1X, 0xC4 },
+ { PRE_DEVICE_D_1X, 0xC4 },
+ { PRE_SOFTWARE_RESET_DEVICE_D, 0xC4 },
+ { POST_SOFTWARE_RESET_DEVICE_D, 0xC4 },
+};
+
+static const struct blktyp_devidx_map ppc3_mapping_table[] = {
+ { MAIN_ALL_DEVICES_1X, 0x80 },
+ { MAIN_DEVICE_A_1X, 0x81 },
+ { COEFF_DEVICE_A_1X, 0xC1 },
+ { PRE_DEVICE_A_1X, 0xC1 },
+ { MAIN_DEVICE_B_1X, 0x82 },
+ { COEFF_DEVICE_B_1X, 0xC2 },
+ { PRE_DEVICE_B_1X, 0xC2 },
+ { MAIN_DEVICE_C_1X, 0x83 },
+ { COEFF_DEVICE_C_1X, 0xC3 },
+ { PRE_DEVICE_C_1X, 0xC3 },
+ { MAIN_DEVICE_D_1X, 0x84 },
+ { COEFF_DEVICE_D_1X, 0xC4 },
+ { PRE_DEVICE_D_1X, 0xC4 },
+};
+
+static const struct blktyp_devidx_map non_ppc3_mapping_table[] = {
+ { MAIN_ALL_DEVICES, 0x80 },
+ { MAIN_DEVICE_A, 0x81 },
+ { COEFF_DEVICE_A, 0xC1 },
+ { PRE_DEVICE_A, 0xC1 },
+ { MAIN_DEVICE_B, 0x82 },
+ { COEFF_DEVICE_B, 0xC2 },
+ { PRE_DEVICE_B, 0xC2 },
+ { MAIN_DEVICE_C, 0x83 },
+ { COEFF_DEVICE_C, 0xC3 },
+ { PRE_DEVICE_C, 0xC3 },
+ { MAIN_DEVICE_D, 0x84 },
+ { COEFF_DEVICE_D, 0xC4 },
+ { PRE_DEVICE_D, 0xC4 },
+};
+
+static struct tasdevice_config_info *tasdevice_add_config(
+ struct tasdevice_priv *tas_priv, unsigned char *config_data,
+ unsigned int config_size, int *status)
+{
+ struct tasdevice_config_info *cfg_info;
+ struct tasdev_blk_data **bk_da;
+ unsigned int config_offset = 0;
+ unsigned int i;
+
+ /*
+ * In most projects are many audio cases, such as music, handfree,
+ * receiver, games, audio-to-haptics, PMIC record, bypass mode,
+ * portrait, landscape, etc. Even in multiple audios, one or
+ * two of the chips will work for the special case, such as
+ * ultrasonic application. In order to support these variable-numbers
+ * of audio cases, flexible configs have been introduced in the
+ * DSP firmware.
+ */
+ cfg_info = kzalloc(sizeof(*cfg_info), GFP_KERNEL);
+ if (!cfg_info) {
+ *status = -ENOMEM;
+ return NULL;
+ }
+
+ if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) {
+ if ((config_offset + 64) > config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev, "add conf: Out of boundary\n");
+ goto out;
+ }
+ config_offset += 64;
+ }
+
+ if ((config_offset + 4) > config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev, "add config: Out of boundary\n");
+ goto out;
+ }
+
+ /*
+ * convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host
+ */
+ cfg_info->nblocks = get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ /*
+ * Several kinds of dsp/algorithm firmwares can run on tas2781,
+ * the number and size of blk are not fixed and different among
+ * these firmwares.
+ */
+ bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks,
+ sizeof(*bk_da), GFP_KERNEL);
+ if (!bk_da) {
+ *status = -ENOMEM;
+ goto out;
+ }
+ cfg_info->real_nblocks = 0;
+ for (i = 0; i < cfg_info->nblocks; i++) {
+ if (config_offset + 12 > config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev,
+ "%s: Out of boundary: i = %d nblocks = %u!\n",
+ __func__, i, cfg_info->nblocks);
+ goto out1;
+ }
+ bk_da[i] = kzalloc(sizeof(*bk_da[i]), GFP_KERNEL);
+ if (!bk_da[i]) {
+ *status = -ENOMEM;
+ goto out1;
+ }
+
+ bk_da[i]->dev_idx = config_data[config_offset];
+ config_offset++;
+
+ bk_da[i]->block_type = config_data[config_offset];
+ config_offset++;
+
+ bk_da[i]->yram_checksum =
+ get_unaligned_be16(&config_data[config_offset]);
+ config_offset += 2;
+ bk_da[i]->block_size =
+ get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ bk_da[i]->n_subblks =
+ get_unaligned_be32(&config_data[config_offset]);
+
+ config_offset += 4;
+
+ if (config_offset + bk_da[i]->block_size > config_size) {
+ *status = -EINVAL;
+ dev_err(tas_priv->dev,
+ "%s: Out of boundary: i = %d blks = %u!\n",
+ __func__, i, cfg_info->nblocks);
+ goto out1;
+ }
+ /* instead of kzalloc+memcpy */
+ bk_da[i]->regdata = kmemdup(&config_data[config_offset],
+ bk_da[i]->block_size, GFP_KERNEL);
+ if (!bk_da[i]->regdata) {
+ *status = -ENOMEM;
+ i++;
+ goto out1;
+ }
+
+ config_offset += bk_da[i]->block_size;
+ cfg_info->real_nblocks += 1;
+ }
+
+ return cfg_info;
+out1:
+ for (int j = 0; j < i; j++)
+ kfree(bk_da[j]);
+ kfree(bk_da);
+out:
+ kfree(cfg_info);
+ return NULL;
+}
+
+int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_config_info **cfg_info;
+ struct tasdevice_rca_hdr *fw_hdr;
+ struct tasdevice_rca *rca;
+ unsigned int total_config_sz = 0;
+ int offset = 0, ret = 0, i;
+ unsigned char *buf;
+
+ rca = &(tas_priv->rcabin);
+ fw_hdr = &(rca->fw_hdr);
+ if (!fmw || !fmw->data) {
+ dev_err(tas_priv->dev, "Failed to read %s\n",
+ tas_priv->rca_binaryname);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -EINVAL;
+ }
+ buf = (unsigned char *)fmw->data;
+ fw_hdr->img_sz = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ if (fw_hdr->img_sz != fmw->size) {
+ dev_err(tas_priv->dev,
+ "File size not match, %d %u", (int)fmw->size,
+ fw_hdr->img_sz);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -EINVAL;
+ }
+
+ fw_hdr->checksum = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->binary_version_num < 0x103) {
+ dev_err(tas_priv->dev, "File version 0x%04x is too low",
+ fw_hdr->binary_version_num);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -EINVAL;
+ }
+ offset += 4;
+ fw_hdr->drv_fw_version = get_unaligned_be32(&buf[offset]);
+ offset += 8;
+ fw_hdr->plat_type = buf[offset++];
+ fw_hdr->dev_family = buf[offset++];
+ fw_hdr->reserve = buf[offset++];
+ fw_hdr->ndev = buf[offset++];
+ if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) {
+ dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n");
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -EINVAL;
+ }
+
+ for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++)
+ fw_hdr->devs[i] = buf[offset];
+
+ fw_hdr->nconfig = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) {
+ fw_hdr->config_size[i] = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ total_config_sz += fw_hdr->config_size[i];
+ }
+
+ if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) {
+ dev_err(tas_priv->dev, "Bin file err %d - %d != %d!\n",
+ fw_hdr->img_sz, total_config_sz, (int)offset);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -EINVAL;
+ }
+
+ cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL);
+ if (!cfg_info) {
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return -ENOMEM;
+ }
+ rca->cfg_info = cfg_info;
+ rca->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ rca->ncfgs += 1;
+ cfg_info[i] = tasdevice_add_config(tas_priv, &buf[offset],
+ fw_hdr->config_size[i], &ret);
+ if (ret) {
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return ret;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ }
+
+ return ret;
+}
+
+/* fixed m68k compiling issue: mapping table can save code field */
+static unsigned char map_dev_idx(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block)
+{
+ struct blktyp_devidx_map *p =
+ (struct blktyp_devidx_map *)non_ppc3_mapping_table;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+ int i, n = ARRAY_SIZE(non_ppc3_mapping_table);
+ unsigned char dev_idx = 0;
+
+ if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781) {
+ p = (struct blktyp_devidx_map *)ppc3_tas2781_mapping_table;
+ n = ARRAY_SIZE(ppc3_tas2781_mapping_table);
+ } else if (fw_fixed_hdr->ppcver >= PPC3_VERSION) {
+ p = (struct blktyp_devidx_map *)ppc3_mapping_table;
+ n = ARRAY_SIZE(ppc3_mapping_table);
+ }
+
+ for (i = 0; i < n; i++) {
+ if (block->type == p[i].blktyp) {
+ dev_idx = p[i].dev_idx;
+ break;
+ }
+ }
+
+ return dev_idx;
+}
+
+static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+
+ if (offset + 16 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host.
+ */
+ block->type = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ block->is_pchksum_present = data[offset++];
+ block->pchksum = data[offset++];
+ block->is_ychksum_present = data[offset++];
+ block->ychksum = data[offset++];
+ block->blk_size = get_unaligned_be32(&data[offset]);
+ offset += 4;
+ block->nr_subblocks = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ /*
+ * Fixed m68k compiling issue:
+ * 1. mapping table can save code field.
+ * 2. storing the dev_idx as a member of block can reduce unnecessary
+ * time and system resource comsumption of dev_idx mapping every
+ * time the block data writing to the dsp.
+ */
+ block->dev_idx = map_dev_idx(tas_fmw, block);
+
+ if (offset + block->blk_size > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__);
+ return -EINVAL;
+ }
+ /* instead of kzalloc+memcpy */
+ block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL);
+ if (!block->data)
+ return -ENOMEM;
+
+ offset += block->blk_size;
+
+ return offset;
+}
+
+static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = fmw->data;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+ img_data->nr_blk = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ img_data->dev_blks = kcalloc(img_data->nr_blk,
+ sizeof(struct tasdev_blk), GFP_KERNEL);
+ if (!img_data->dev_blks)
+ return -ENOMEM;
+
+ for (i = 0; i < img_data->nr_blk; i++) {
+ blk = &(img_data->dev_blks[i]);
+ offset = fw_parse_block_data_kernel(
+ tas_fmw, blk, fmw, offset);
+ if (offset < 0) {
+ kfree(img_data->dev_blks);
+ return -EINVAL;
+ }
+ }
+
+ return offset;
+}
+
+static int fw_parse_program_data_kernel(
+ struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ struct tasdevice_prog *program;
+ unsigned int i;
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ program = &(tas_fmw->programs[i]);
+ if (offset + 72 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ return -EINVAL;
+ }
+ /* skip 72 unused byts */
+ offset += 72;
+
+ offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ break;
+ }
+
+ return offset;
+}
+
+static int fw_parse_configuration_data_kernel(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+ struct tasdevice_config *config;
+ unsigned int i;
+
+ for (i = 0; i < tas_fmw->nr_configurations; i++) {
+ config = &(tas_fmw->configs[i]);
+ if (offset + 80 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ return -EINVAL;
+ }
+ memcpy(config->name, &data[offset], 64);
+ /* skip extra 16 bytes */
+ offset += 80;
+
+ offset = fw_parse_data_kernel(tas_fmw, &(config->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ break;
+ }
+
+ return offset;
+}
+
+static int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_priv,
+ const struct firmware *fmw, int offset)
+{
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_config *config;
+ struct tasdevice_prog *program;
+ const unsigned char *buf = fmw->data;
+ unsigned short max_confs;
+ unsigned int i;
+
+ if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+ fw_hdr->device_family = get_unaligned_be16(&buf[offset]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_priv->dev, "%s:not TAS device\n", __func__);
+ return -EINVAL;
+ }
+ offset += 2;
+ fw_hdr->device = get_unaligned_be16(&buf[offset]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device);
+ return -EINVAL;
+ }
+ offset += 2;
+
+ tas_fmw->nr_programs = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ if (tas_fmw->nr_programs == 0 ||
+ tas_fmw->nr_programs > TASDEVICE_MAXPROGRAM_NUM_KERNEL) {
+ dev_err(tas_priv->dev, "mnPrograms is invalid\n");
+ return -EINVAL;
+ }
+
+ tas_fmw->programs = kcalloc(tas_fmw->nr_programs,
+ sizeof(*tas_fmw->programs), GFP_KERNEL);
+ if (!tas_fmw->programs)
+ return -ENOMEM;
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ program = &(tas_fmw->programs[i]);
+ program->prog_size = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ }
+
+ /* Skip the unused prog_size */
+ offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs);
+
+ tas_fmw->nr_configurations = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ /*
+ * The max number of config in firmware greater than 4 pieces of
+ * tas2781s is different from the one lower than 4 pieces of
+ * tas2781s.
+ */
+ max_confs = TASDEVICE_MAXCONFIG_NUM_KERNEL;
+ if (tas_fmw->nr_configurations == 0 ||
+ tas_fmw->nr_configurations > max_confs) {
+ dev_err(tas_priv->dev, "%s: Conf is invalid\n", __func__);
+ kfree(tas_fmw->programs);
+ return -EINVAL;
+ }
+
+ if (offset + 4 * max_confs > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__);
+ kfree(tas_fmw->programs);
+ return -EINVAL;
+ }
+
+ tas_fmw->configs = kcalloc(tas_fmw->nr_configurations,
+ sizeof(*tas_fmw->configs), GFP_KERNEL);
+ if (!tas_fmw->configs) {
+ kfree(tas_fmw->programs);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ config = &(tas_fmw->configs[i]);
+ config->cfg_size = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ }
+
+ /* Skip the unused configs */
+ offset += 4 * (max_confs - tas_fmw->nr_programs);
+
+ return offset;
+}
+
+static int tasdevice_single_byte_wr(void *context, unsigned char *data,
+ int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = context;
+ unsigned short len = get_unaligned_be16(&data[2]);
+ int i, subblk_offset, rc;
+
+ subblk_offset = 4;
+ if (subblk_offset + 4 * len > sublocksize) {
+ dev_err(tas_priv->dev, "process_block: Out of boundary\n");
+ return 0;
+ }
+
+ for (i = 0; i < len; i++) {
+ rc = tasdevice_spi_dev_write(tas_priv,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1], data[subblk_offset + 2]),
+ data[subblk_offset + 3]);
+ if (rc < 0) {
+ dev_err(tas_priv->dev,
+ "process_block: single write error\n");
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ subblk_offset += 4;
+ }
+
+ return subblk_offset;
+}
+
+static int tasdevice_burst_wr(void *context, unsigned char *data,
+ int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = context;
+ unsigned short len = get_unaligned_be16(&data[2]);
+ int subblk_offset, rc;
+
+ subblk_offset = 4;
+ if (subblk_offset + 4 + len > sublocksize) {
+ dev_err(tas_priv->dev, "%s: BST Out of boundary\n", __func__);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ if (len % 4) {
+ dev_err(tas_priv->dev, "%s:Bst-len(%u)not div by 4\n",
+ __func__, len);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+
+ rc = tasdevice_spi_dev_bulk_write(tas_priv,
+ TASDEVICE_REG(data[subblk_offset], data[subblk_offset + 1],
+ data[subblk_offset + 2]), &(data[subblk_offset + 4]), len);
+ if (rc < 0) {
+ dev_err(tas_priv->dev, "%s: bulk_write error = %d\n",
+ __func__, rc);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ subblk_offset += (len + 4);
+
+ return subblk_offset;
+}
+
+static int tasdevice_delay(void *context, unsigned char *data,
+ int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = context;
+ unsigned int sleep_time, subblk_offset = 2;
+
+ if (subblk_offset + 2 > sublocksize) {
+ dev_err(tas_priv->dev, "%s: delay Out of boundary\n",
+ __func__);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ sleep_time = get_unaligned_be16(&data[2]) * 1000;
+ fsleep(sleep_time);
+ subblk_offset += 2;
+
+ return subblk_offset;
+}
+
+static int tasdevice_field_wr(void *context, unsigned char *data,
+ int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = context;
+ int rc, subblk_offset = 2;
+
+ if (subblk_offset + 6 > sublocksize) {
+ dev_err(tas_priv->dev, "%s: bit write Out of boundary\n",
+ __func__);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ rc = tasdevice_spi_dev_update_bits(tas_priv,
+ TASDEVICE_REG(data[subblk_offset + 2],
+ data[subblk_offset + 3],
+ data[subblk_offset + 4]),
+ data[subblk_offset + 1], data[subblk_offset + 5]);
+ if (rc < 0) {
+ dev_err(tas_priv->dev, "%s: update_bits error = %d\n",
+ __func__, rc);
+ subblk_offset |= OFFSET_ERROR_BIT;
+ }
+ subblk_offset += 6;
+
+ return subblk_offset;
+}
+
+static int tasdevice_process_block(void *context, unsigned char *data,
+ unsigned char dev_idx, int sublocksize)
+{
+ struct tasdevice_priv *tas_priv = context;
+ int blktyp = dev_idx & 0xC0, subblk_offset;
+ unsigned char subblk_typ = data[1];
+
+ if (tas_priv->is_loading == false)
+ return sublocksize;
+
+ switch (subblk_typ) {
+ case TASDEVICE_CMD_SING_W:
+ subblk_offset = tasdevice_single_byte_wr(tas_priv, data,
+ sublocksize);
+ break;
+ case TASDEVICE_CMD_BURST:
+ subblk_offset = tasdevice_burst_wr(tas_priv, data,
+ sublocksize);
+ break;
+ case TASDEVICE_CMD_DELAY:
+ subblk_offset = tasdevice_delay(tas_priv, data, sublocksize);
+ break;
+ case TASDEVICE_CMD_FIELD_W:
+ subblk_offset = tasdevice_field_wr(tas_priv, data,
+ sublocksize);
+ break;
+ default:
+ subblk_offset = 2;
+ break;
+ }
+ if (((subblk_offset & OFFSET_ERROR_BIT) != 0) && blktyp != 0) {
+ if (blktyp == 0x80) {
+ tas_priv->cur_prog = -1;
+ tas_priv->cur_conf = -1;
+ } else
+ tas_priv->cur_conf = -1;
+ }
+ subblk_offset &= ~OFFSET_ERROR_BIT;
+
+ return subblk_offset;
+}
+
+void tasdevice_spi_select_cfg_blk(void *pContext, int conf_no,
+ unsigned char block_type)
+{
+ struct tasdevice_priv *tas_priv = pContext;
+ struct tasdevice_rca *rca = &(tas_priv->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ struct tasdev_blk_data **blk_data;
+ unsigned int j, k;
+
+ if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) {
+ dev_err(tas_priv->dev, "conf_no should be not more than %u\n",
+ rca->ncfgs);
+ return;
+ }
+ blk_data = cfg_info[conf_no]->blk_data;
+
+ for (j = 0; j < cfg_info[conf_no]->real_nblocks; j++) {
+ unsigned int length = 0, rc = 0;
+
+ if (block_type > 5 || block_type < 2) {
+ dev_err(tas_priv->dev,
+ "block_type should be in range from 2 to 5\n");
+ break;
+ }
+ if (block_type != blk_data[j]->block_type)
+ continue;
+
+ for (k = 0; k < blk_data[j]->n_subblks; k++) {
+ tas_priv->is_loading = true;
+
+ rc = tasdevice_process_block(tas_priv,
+ blk_data[j]->regdata + length,
+ blk_data[j]->dev_idx,
+ blk_data[j]->block_size - length);
+ length += rc;
+ if (blk_data[j]->block_size < length) {
+ dev_err(tas_priv->dev,
+ "%s: %u %u out of boundary\n",
+ __func__, length,
+ blk_data[j]->block_size);
+ break;
+ }
+ }
+ if (length != blk_data[j]->block_size)
+ dev_err(tas_priv->dev, "%s: %u %u size is not same\n",
+ __func__, length, blk_data[j]->block_size);
+ }
+}
+
+static int tasdevice_load_block_kernel(
+ struct tasdevice_priv *tasdevice, struct tasdev_blk *block)
+{
+ const unsigned int blk_size = block->blk_size;
+ unsigned char *data = block->data;
+ unsigned int i, length;
+
+ for (i = 0, length = 0; i < block->nr_subblocks; i++) {
+ int rc = tasdevice_process_block(tasdevice, data + length,
+ block->dev_idx, blk_size - length);
+ if (rc < 0) {
+ dev_err(tasdevice->dev,
+ "%s: %u %u sublock write error\n",
+ __func__, length, blk_size);
+ break;
+ }
+ length += rc;
+ if (blk_size < length) {
+ dev_err(tasdevice->dev, "%s: %u %u out of boundary\n",
+ __func__, length, blk_size);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int fw_parse_variable_hdr(struct tasdevice_priv *tas_priv,
+ struct tasdevice_dspfw_hdr *fw_hdr,
+ const struct firmware *fmw, int offset)
+{
+ const unsigned char *buf = fmw->data;
+ int len = strlen((char *)&buf[offset]);
+
+ len++;
+
+ if (offset + len + 8 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+
+ offset += len;
+
+ fw_hdr->device_family = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_priv->dev, "%s: not TAS device\n", __func__);
+ return -EINVAL;
+ }
+ offset += 4;
+
+ fw_hdr->device = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device);
+ return -EINVAL;
+ }
+ offset += 4;
+ fw_hdr->ndev = 1;
+
+ return offset;
+}
+
+static int fw_parse_variable_header_git(struct tasdevice_priv
+ *tas_priv, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+
+ offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset);
+
+ return offset;
+}
+
+static int fw_parse_block_data(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ int n;
+
+ if (offset + 8 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Type error\n", __func__);
+ return -EINVAL;
+ }
+ block->type = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) {
+ if (offset + 8 > fmw->size) {
+ dev_err(tas_fmw->dev, "PChkSumPresent error\n");
+ return -EINVAL;
+ }
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ block->pchksum = data[offset];
+ offset++;
+
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ block->ychksum = data[offset];
+ offset++;
+ } else {
+ block->is_pchksum_present = 0;
+ block->is_ychksum_present = 0;
+ }
+
+ block->nr_cmds = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ n = block->nr_cmds * 4;
+ if (offset + n > fmw->size) {
+ dev_err(tas_fmw->dev,
+ "%s: File Size(%lu) error offset = %d n = %d\n",
+ __func__, (unsigned long)fmw->size, offset, n);
+ return -EINVAL;
+ }
+ /* instead of kzalloc+memcpy */
+ block->data = kmemdup(&data[offset], n, GFP_KERNEL);
+ if (!block->data)
+ return -ENOMEM;
+
+ offset += n;
+
+ return offset;
+}
+
+/*
+ * When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_data(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = (unsigned char *)fmw->data;
+ struct tasdev_blk *blk;
+ unsigned int i, n;
+
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Name error\n", __func__);
+ return -EINVAL;
+ }
+ memcpy(img_data->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n + 2 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: Description error\n", __func__);
+ return -EINVAL;
+ }
+ offset += n;
+ img_data->nr_blk = get_unaligned_be16(&data[offset]);
+ offset += 2;
+
+ img_data->dev_blks = kcalloc(img_data->nr_blk,
+ sizeof(*img_data->dev_blks), GFP_KERNEL);
+ if (!img_data->dev_blks)
+ return -ENOMEM;
+
+ for (i = 0; i < img_data->nr_blk; i++) {
+ blk = &(img_data->dev_blks[i]);
+ offset = fw_parse_block_data(tas_fmw, blk, fmw, offset);
+ if (offset < 0)
+ return -EINVAL;
+ }
+
+ return offset;
+}
+
+/*
+ * When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_program_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ unsigned char *buf = (unsigned char *)fmw->data;
+ struct tasdevice_prog *program;
+ int i;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+ tas_fmw->nr_programs = get_unaligned_be16(&buf[offset]);
+ offset += 2;
+
+ if (tas_fmw->nr_programs == 0) {
+ /* Not error in calibration Data file, return directly */
+ dev_info(tas_priv->dev, "%s: No Programs data, maybe calbin\n",
+ __func__);
+ return offset;
+ }
+
+ tas_fmw->programs =
+ kcalloc(tas_fmw->nr_programs, sizeof(*tas_fmw->programs),
+ GFP_KERNEL);
+ if (!tas_fmw->programs)
+ return -ENOMEM;
+
+ for (i = 0; i < tas_fmw->nr_programs; i++) {
+ int n = 0;
+
+ program = &(tas_fmw->programs[i]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
+ return -EINVAL;
+ }
+ offset += 64;
+
+ n = strlen((char *)&buf[offset]);
+ /* skip '\0' and 5 unused bytes */
+ n += 6;
+ if (offset + n > fmw->size) {
+ dev_err(tas_priv->dev, "Description err\n");
+ return -EINVAL;
+ }
+
+ offset += n;
+
+ offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw,
+ offset);
+ if (offset < 0)
+ return offset;
+ }
+
+ return offset;
+}
+
+/*
+ * When parsing error occurs, all the memory resource will be released
+ * in the end of tasdevice_rca_ready.
+ */
+static int fw_parse_configuration_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ struct tasdevice_config *config;
+ unsigned int i, n;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+ tas_fmw->nr_configurations = get_unaligned_be16(&data[offset]);
+ offset += 2;
+
+ if (tas_fmw->nr_configurations == 0) {
+ dev_err(tas_priv->dev, "%s: Conf is zero\n", __func__);
+ /* Not error for calibration Data file, return directly */
+ return offset;
+ }
+ tas_fmw->configs = kcalloc(tas_fmw->nr_configurations,
+ sizeof(*tas_fmw->configs), GFP_KERNEL);
+ if (!tas_fmw->configs)
+ return -ENOMEM;
+ for (i = 0; i < tas_fmw->nr_configurations; i++) {
+ config = &(tas_fmw->configs[i]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_priv->dev, "File Size err\n");
+ return -EINVAL;
+ }
+ memcpy(config->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n += 15;
+ if (offset + n > fmw->size) {
+ dev_err(tas_priv->dev, "Description err\n");
+ return -EINVAL;
+ }
+ offset += n;
+ offset = fw_parse_data(tas_fmw, &(config->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ break;
+ }
+
+ return offset;
+}
+
+static bool check_inpage_yram_rg(struct tas_crc *cd,
+ unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (reg <= TAS2781_YRAM5_END_REG &&
+ reg >= TAS2781_YRAM5_START_REG) {
+ if (reg + len > TAS2781_YRAM5_END_REG)
+ cd->len = TAS2781_YRAM5_END_REG - reg + 1;
+ else
+ cd->len = len;
+ cd->offset = reg;
+ in = true;
+ } else if (reg < TAS2781_YRAM5_START_REG) {
+ if (reg + len > TAS2781_YRAM5_START_REG) {
+ cd->offset = TAS2781_YRAM5_START_REG;
+ cd->len = len - TAS2781_YRAM5_START_REG + reg;
+ in = true;
+ }
+ }
+
+ return in;
+}
+
+static bool check_inpage_yram_bk1(struct tas_crc *cd,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (page == TAS2781_YRAM1_PAGE) {
+ if (reg >= TAS2781_YRAM1_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ in = true;
+ } else if (reg + len > TAS2781_YRAM1_START_REG) {
+ cd->offset = TAS2781_YRAM1_START_REG;
+ cd->len = len - TAS2781_YRAM1_START_REG + reg;
+ in = true;
+ }
+ } else if (page == TAS2781_YRAM3_PAGE) {
+ in = check_inpage_yram_rg(cd, reg, len);
+ }
+
+ return in;
+}
+
+/*
+ * Return Code:
+ * true -- the registers are in the inpage yram
+ * false -- the registers are NOT in the inpage yram
+ */
+static bool check_inpage_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (book == TAS2781_YRAM_BOOK1)
+ in = check_inpage_yram_bk1(cd, page, reg, len);
+ else if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE)
+ in = check_inpage_yram_rg(cd, reg, len);
+
+ return in;
+}
+
+static bool check_inblock_yram_bk(struct tas_crc *cd,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if ((page >= TAS2781_YRAM4_START_PAGE &&
+ page <= TAS2781_YRAM4_END_PAGE) ||
+ (page >= TAS2781_YRAM2_START_PAGE &&
+ page <= TAS2781_YRAM2_END_PAGE)) {
+ if (reg <= TAS2781_YRAM2_END_REG &&
+ reg >= TAS2781_YRAM2_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ in = true;
+ } else if (reg < TAS2781_YRAM2_START_REG) {
+ if (reg + len - 1 >= TAS2781_YRAM2_START_REG) {
+ cd->offset = TAS2781_YRAM2_START_REG;
+ cd->len = reg + len - TAS2781_YRAM2_START_REG;
+ in = true;
+ }
+ }
+ }
+
+ return in;
+}
+
+/*
+ * Return Code:
+ * true -- the registers are in the inblock yram
+ * false -- the registers are NOT in the inblock yram
+ */
+static bool check_inblock_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in = false;
+
+ if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2)
+ in = check_inblock_yram_bk(cd, page, reg, len);
+
+ return in;
+}
+
+static bool check_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ bool in;
+
+ in = check_inpage_yram(cd, book, page, reg, len);
+ if (!in)
+ in = check_inblock_yram(cd, book, page, reg, len);
+
+ return in;
+}
+
+static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice,
+ unsigned char book, unsigned char page,
+ unsigned char reg, unsigned int len)
+{
+ struct tas_crc crc_data;
+ unsigned char crc_chksum = 0;
+ unsigned char nBuf1[128];
+ int ret = 0, i;
+ bool in;
+
+ if ((reg + len - 1) > 127) {
+ ret = -EINVAL;
+ dev_err(tasdevice->dev, "firmware error\n");
+ goto end;
+ }
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (reg == TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (len == 4)) {
+ /* DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ in = check_yram(&crc_data, book, page, reg, len);
+ if (!in)
+ goto end;
+
+ if (len == 1) {
+ dev_err(tasdevice->dev, "firmware error\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ret = tasdevice_spi_dev_bulk_read(tasdevice,
+ TASDEVICE_REG(book, page, crc_data.offset),
+ nBuf1, crc_data.len);
+ if (ret < 0)
+ goto end;
+
+ for (i = 0; i < crc_data.len; i++) {
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ ((i + crc_data.offset) >=
+ TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ ((i + crc_data.offset) <=
+ (TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG) + 4)))
+ /* DSP swap command, bypass */
+ continue;
+ else
+ crc_chksum += crc8(tasdevice->crc8_lkp_tbl, &nBuf1[i],
+ 1, 0);
+ }
+
+ ret = crc_chksum;
+
+end:
+ return ret;
+}
+
+static int do_singlereg_checksum(struct tasdevice_priv *tasdevice,
+ unsigned char book, unsigned char page,
+ unsigned char reg, unsigned char val)
+{
+ struct tas_crc crc_data;
+ unsigned int nData1;
+ int ret = 0;
+ bool in;
+
+ /* DSP swap command, pass */
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (reg >= TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) &&
+ (reg <= (TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG) + 4)))
+ return 0;
+
+ in = check_yram(&crc_data, book, page, reg, 1);
+ if (!in)
+ return 0;
+ ret = tasdevice_spi_dev_read(tasdevice,
+ TASDEVICE_REG(book, page, reg), &nData1);
+ if (ret < 0)
+ return ret;
+
+ if (nData1 != val) {
+ dev_err(tasdevice->dev,
+ "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+ book, page, reg, val, nData1);
+ tasdevice->err_code |= ERROR_YRAM_CRCCHK;
+ return -EAGAIN;
+ }
+
+ ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0);
+
+ return ret;
+}
+
+static void set_err_prg_cfg(unsigned int type, struct tasdevice_priv *p)
+{
+ if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A) ||
+ (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C) ||
+ (type == MAIN_DEVICE_D))
+ p->cur_prog = -1;
+ else
+ p->cur_conf = -1;
+}
+
+static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned int len,
+ unsigned char val, unsigned char *crc_chksum)
+{
+ int ret;
+
+ if (len > 1)
+ ret = tasdev_multibytes_chksum(tas_priv, book, page, reg,
+ len);
+ else
+ ret = do_singlereg_checksum(tas_priv, book, page, reg, val);
+
+ if (ret > 0) {
+ *crc_chksum += ret;
+ goto end;
+ }
+
+ if (ret != -EAGAIN)
+ goto end;
+
+ block->nr_retry--;
+ if (block->nr_retry > 0)
+ goto end;
+
+ set_err_prg_cfg(block->type, tas_priv);
+
+end:
+ return ret;
+}
+
+static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char *data,
+ unsigned int len, unsigned int *nr_cmds,
+ unsigned char *crc_chksum)
+{
+ int ret;
+
+ if (len > 1) {
+ ret = tasdevice_spi_dev_bulk_write(tas_priv,
+ TASDEVICE_REG(book, page, reg), data + 3, len);
+ if (ret < 0)
+ return ret;
+ if (block->is_ychksum_present)
+ ret = tasdev_bytes_chksum(tas_priv, block,
+ book, page, reg, len, 0, crc_chksum);
+ } else {
+ ret = tasdevice_spi_dev_write(tas_priv,
+ TASDEVICE_REG(book, page, reg), data[3]);
+ if (ret < 0)
+ return ret;
+ if (block->is_ychksum_present)
+ ret = tasdev_bytes_chksum(tas_priv, block, book,
+ page, reg, 1, data[3], crc_chksum);
+ }
+
+ if (!block->is_ychksum_present || ret >= 0) {
+ *nr_cmds += 1;
+ if (len >= 2)
+ *nr_cmds += ((len - 2) / 4) + 1;
+ }
+
+ return ret;
+}
+
+static int tasdev_block_chksum(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block)
+{
+ unsigned int nr_value;
+ int ret;
+
+ ret = tasdevice_spi_dev_read(tas_priv, TASDEVICE_CHECKSUM, &nr_value);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s: read error %d.\n", __func__, ret);
+ set_err_prg_cfg(block->type, tas_priv);
+ return ret;
+ }
+
+ if ((nr_value & 0xff) != block->pchksum) {
+ dev_err(tas_priv->dev, "%s: PChkSum err %d ", __func__, ret);
+ dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n",
+ block->pchksum, (nr_value & 0xff));
+ tas_priv->err_code |= ERROR_PRAM_CRCCHK;
+ ret = -EAGAIN;
+ block->nr_retry--;
+
+ if (block->nr_retry <= 0)
+ set_err_prg_cfg(block->type, tas_priv);
+ } else {
+ tas_priv->err_code &= ~ERROR_PRAM_CRCCHK;
+ }
+
+ return ret;
+}
+
+static int tasdev_load_blk(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block)
+{
+ unsigned int sleep_time, len, nr_cmds;
+ unsigned char offset, book, page, val;
+ unsigned char *data = block->data;
+ unsigned char crc_chksum = 0;
+ int ret = 0;
+
+ while (block->nr_retry > 0) {
+ if (block->is_pchksum_present) {
+ ret = tasdevice_spi_dev_write(tas_priv,
+ TASDEVICE_CHECKSUM, 0);
+ if (ret < 0)
+ break;
+ }
+
+ if (block->is_ychksum_present)
+ crc_chksum = 0;
+
+ nr_cmds = 0;
+
+ while (nr_cmds < block->nr_cmds) {
+ data = block->data + nr_cmds * 4;
+
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ val = data[3];
+
+ nr_cmds++;
+ /* Single byte write */
+ if (offset <= 0x7F) {
+ ret = tasdevice_spi_dev_write(tas_priv,
+ TASDEVICE_REG(book, page, offset),
+ val);
+ if (ret < 0)
+ break;
+ if (block->is_ychksum_present) {
+ ret = tasdev_bytes_chksum(tas_priv,
+ block, book, page, offset,
+ 1, val, &crc_chksum);
+ if (ret < 0)
+ break;
+ }
+ continue;
+ }
+ /* sleep command */
+ if (offset == 0x81) {
+ /* book -- data[0] page -- data[1] */
+ sleep_time = ((book << 8) + page)*1000;
+ fsleep(sleep_time);
+ continue;
+ }
+ /* Multiple bytes write */
+ if (offset == 0x85) {
+ data += 4;
+ len = (book << 8) + page;
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ ret = tasdev_multibytes_wr(tas_priv,
+ block, book, page, offset, data,
+ len, &nr_cmds, &crc_chksum);
+ if (ret < 0)
+ break;
+ }
+ }
+ if (ret == -EAGAIN) {
+ if (block->nr_retry > 0)
+ continue;
+ } else if (ret < 0) {
+ /* err in current device, skip it */
+ break;
+ }
+
+ if (block->is_pchksum_present) {
+ ret = tasdev_block_chksum(tas_priv, block);
+ if (ret == -EAGAIN) {
+ if (block->nr_retry > 0)
+ continue;
+ } else if (ret < 0) {
+ /* err in current device, skip it */
+ break;
+ }
+ }
+
+ if (block->is_ychksum_present) {
+ /* TBD, open it when FW ready */
+ dev_err(tas_priv->dev,
+ "Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n",
+ block->ychksum, crc_chksum);
+
+ tas_priv->err_code &=
+ ~ERROR_YRAM_CRCCHK;
+ ret = 0;
+ }
+ /* skip current blk */
+ break;
+ }
+
+ return ret;
+}
+
+static int tasdevice_load_block(struct tasdevice_priv *tas_priv,
+ struct tasdev_blk *block)
+{
+ int ret = 0;
+
+ block->nr_retry = 6;
+ if (tas_priv->is_loading == false)
+ return 0;
+ ret = tasdev_load_blk(tas_priv, block);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "Blk (%d) load error\n", block->type);
+
+ return ret;
+}
+
+static int dspfw_default_callback(struct tasdevice_priv *tas_priv,
+ unsigned int drv_ver, unsigned int ppcver)
+{
+ int rc = 0;
+
+ if (drv_ver == 0x100) {
+ if (ppcver >= PPC3_VERSION) {
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ } else {
+ switch (ppcver) {
+ case 0x00:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ dev_err(tas_priv->dev,
+ "%s: PPCVer must be 0x0 or 0x%02x",
+ __func__, PPC3_VERSION);
+ dev_err(tas_priv->dev, " Current:0x%02x\n",
+ ppcver);
+ rc = -EINVAL;
+ break;
+ }
+ }
+ } else {
+ dev_err(tas_priv->dev,
+ "DrvVer must be 0x0, 0x230 or above 0x230 ");
+ dev_err(tas_priv->dev, "current is 0x%02x\n", drv_ver);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int load_calib_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_data *dev_data)
+{
+ struct tasdev_blk *block;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < dev_data->nr_blk; i++) {
+ block = &(dev_data->dev_blks[i]);
+ ret = tasdevice_load_block(tas_priv, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static int fw_parse_header(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+ static const unsigned char magic_number[] = {0x35, 0x35, 0x35, 0x32, };
+ const unsigned char *buf = (unsigned char *)fmw->data;
+
+ if (offset + 92 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ if (memcmp(&buf[offset], magic_number, 4)) {
+ dev_err(tas_priv->dev, "%s: Magic num NOT match\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+
+ /*
+ * Convert data[offset], data[offset + 1], data[offset + 2] and
+ * data[offset + 3] into host
+ */
+ fw_fixed_hdr->fwsize = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ if (fw_fixed_hdr->fwsize != fmw->size) {
+ dev_err(tas_priv->dev, "File size not match, %lu %u",
+ (unsigned long)fmw->size, fw_fixed_hdr->fwsize);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ fw_fixed_hdr->ppcver = get_unaligned_be32(&buf[offset]);
+ offset += 8;
+ fw_fixed_hdr->drv_ver = get_unaligned_be32(&buf[offset]);
+ offset += 72;
+
+out:
+ return offset;
+}
+
+static int tasdevice_dspfw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr;
+ struct tasdevice_fw *tas_fmw;
+ int offset = 0, ret = 0;
+
+ if (!fmw || !fmw->data) {
+ dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n",
+ __func__, tas_priv->coef_binaryname);
+ return -EINVAL;
+ }
+
+ tas_priv->fmw = kzalloc(sizeof(*tas_priv->fmw), GFP_KERNEL);
+ if (!tas_priv->fmw)
+ return -ENOMEM;
+ tas_fmw = tas_priv->fmw;
+ tas_fmw->dev = tas_priv->dev;
+ offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset);
+
+ if (offset == -EINVAL)
+ return -EINVAL;
+
+ fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr);
+ /* Support different versions of firmware */
+ switch (fw_fixed_hdr->drv_ver) {
+ case 0x301:
+ case 0x302:
+ case 0x502:
+ case 0x503:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ break;
+ case 0x202:
+ case 0x400:
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_priv->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ ret = dspfw_default_callback(tas_priv,
+ fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset);
+ if (offset < 0)
+ return offset;
+
+ offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw,
+ offset);
+ if (offset < 0)
+ return offset;
+
+ offset = tas_priv->fw_parse_configuration_data(tas_priv,
+ tas_fmw, fmw, offset);
+ if (offset < 0)
+ ret = offset;
+
+ return ret;
+}
+
+int tasdevice_spi_dsp_parser(void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ const struct firmware *fw_entry;
+ int ret;
+
+ ret = request_firmware(&fw_entry, tas_priv->coef_binaryname,
+ tas_priv->dev);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s: load %s error\n", __func__,
+ tas_priv->coef_binaryname);
+ return ret;
+ }
+
+ ret = tasdevice_dspfw_ready(fw_entry, tas_priv);
+ release_firmware(fw_entry);
+ fw_entry = NULL;
+
+ return ret;
+}
+
+static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog)
+{
+ struct tasdevice_data *tas_dt;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (!prog)
+ return;
+
+ tas_dt = &(prog->dev_data);
+
+ if (!tas_dt->dev_blks)
+ return;
+
+ for (i = 0; i < tas_dt->nr_blk; i++) {
+ blk = &(tas_dt->dev_blks[i]);
+ kfree(blk->data);
+ }
+ kfree(tas_dt->dev_blks);
+}
+
+static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog,
+ unsigned short nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ tasdev_dsp_prog_blk_remove(&prog[i]);
+ kfree(prog);
+}
+
+static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg)
+{
+ struct tasdevice_data *tas_dt;
+ struct tasdev_blk *blk;
+ unsigned int i;
+
+ if (cfg) {
+ tas_dt = &(cfg->dev_data);
+
+ if (!tas_dt->dev_blks)
+ return;
+
+ for (i = 0; i < tas_dt->nr_blk; i++) {
+ blk = &(tas_dt->dev_blks[i]);
+ kfree(blk->data);
+ }
+ kfree(tas_dt->dev_blks);
+ }
+}
+
+static void tasdev_dsp_cfg_remove(struct tasdevice_config *config,
+ unsigned short nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ tasdev_dsp_cfg_blk_remove(&config[i]);
+ kfree(config);
+}
+
+void tasdevice_spi_dsp_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev = context;
+
+ if (!tas_dev->fmw)
+ return;
+
+ if (tas_dev->fmw->programs)
+ tasdev_dsp_prog_remove(tas_dev->fmw->programs,
+ tas_dev->fmw->nr_programs);
+ if (tas_dev->fmw->configs)
+ tasdev_dsp_cfg_remove(tas_dev->fmw->configs,
+ tas_dev->fmw->nr_configurations);
+ kfree(tas_dev->fmw);
+ tas_dev->fmw = NULL;
+}
+
+static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw)
+{
+ struct tasdevice_calibration *calibration;
+ struct tasdev_blk *block;
+ unsigned int blks;
+ int i;
+
+ if (!tas_fmw->calibrations)
+ goto out;
+
+ for (i = 0; i < tas_fmw->nr_calibrations; i++) {
+ calibration = &(tas_fmw->calibrations[i]);
+ if (!calibration)
+ continue;
+
+ if (!calibration->dev_data.dev_blks)
+ continue;
+
+ for (blks = 0; blks < calibration->dev_data.nr_blk; blks++) {
+ block = &(calibration->dev_data.dev_blks[blks]);
+ if (!block)
+ continue;
+ kfree(block->data);
+ }
+ kfree(calibration->dev_data.dev_blks);
+ }
+ kfree(tas_fmw->calibrations);
+out:
+ kfree(tas_fmw);
+}
+
+void tasdevice_spi_calbin_remove(void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+
+ if (tas_priv->cali_data_fmw) {
+ tas2781_clear_calfirmware(tas_priv->cali_data_fmw);
+ tas_priv->cali_data_fmw = NULL;
+ }
+}
+
+void tasdevice_spi_config_info_remove(void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_rca *rca = &(tas_priv->rcabin);
+ struct tasdevice_config_info **ci = rca->cfg_info;
+ unsigned int i, j;
+
+ if (!ci)
+ return;
+ for (i = 0; i < rca->ncfgs; i++) {
+ if (!ci[i])
+ continue;
+ if (ci[i]->blk_data) {
+ for (j = 0; j < ci[i]->real_nblocks; j++) {
+ if (!ci[i]->blk_data[j])
+ continue;
+ kfree(ci[i]->blk_data[j]->regdata);
+ kfree(ci[i]->blk_data[j]);
+ }
+ kfree(ci[i]->blk_data);
+ }
+ kfree(ci[i]);
+ }
+ kfree(ci);
+}
+
+static int tasdevice_load_data(struct tasdevice_priv *tas_priv,
+ struct tasdevice_data *dev_data)
+{
+ struct tasdev_blk *block;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < dev_data->nr_blk; i++) {
+ block = &(dev_data->dev_blks[i]);
+ ret = tas_priv->tasdevice_load_block(tas_priv, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+int tasdevice_spi_select_tuningprm_cfg(void *context, int prm_no,
+ int cfg_no, int rca_conf_no)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_rca *rca = &(tas_priv->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_prog *program;
+ struct tasdevice_config *conf;
+ int prog_status = 0;
+ int status;
+
+ if (!tas_fmw) {
+ dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+ return 0;
+ }
+
+ if (cfg_no >= tas_fmw->nr_configurations) {
+ dev_err(tas_priv->dev,
+ "%s: cfg(%d) is not in range of conf %u\n",
+ __func__, cfg_no, tas_fmw->nr_configurations);
+ return 0;
+ }
+
+ if (prm_no >= tas_fmw->nr_programs) {
+ dev_err(tas_priv->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->nr_programs);
+ return 0;
+ }
+
+ if (rca_conf_no >= rca->ncfgs ||
+ rca_conf_no < 0 ||
+ !cfg_info) {
+ dev_err(tas_priv->dev,
+ "conf_no:%d should be in range from 0 to %u\n",
+ rca_conf_no, rca->ncfgs-1);
+ return 0;
+ }
+
+ if (prm_no >= 0 &&
+ (tas_priv->cur_prog != prm_no ||
+ tas_priv->force_fwload_status)) {
+ tas_priv->cur_conf = -1;
+ tas_priv->is_loading = true;
+ prog_status++;
+ }
+
+ if (prog_status) {
+ program = &(tas_fmw->programs[prm_no]);
+ tasdevice_load_data(tas_priv, &(program->dev_data));
+ if (tas_priv->is_loaderr == false &&
+ tas_priv->is_loading == true) {
+ struct tasdevice_fw *cal_fmw =
+ tas_priv->cali_data_fmw;
+
+ if (cal_fmw) {
+ struct tasdevice_calibration
+ *cal = cal_fmw->calibrations;
+
+ if (cal)
+ load_calib_data(tas_priv,
+ &(cal->dev_data));
+ }
+ tas_priv->cur_prog = prm_no;
+ }
+
+ }
+
+ if (cfg_no &&
+ (cfg_no != tas_priv->cur_conf) &&
+ (tas_priv->is_loaderr == false)) {
+ status++;
+ tas_priv->is_loading = true;
+ } else {
+ tas_priv->is_loading = false;
+ }
+
+ if (status) {
+ conf = &(tas_fmw->configs[cfg_no]);
+ status = 0;
+ tasdevice_load_data(tas_priv, &(conf->dev_data));
+ if (tas_priv->is_loaderr == true) {
+ status |= 1 << 4;
+ } else if (tas_priv->is_loaderr == false &&
+ tas_priv->is_loading == true) {
+ tas_priv->cur_conf = cfg_no;
+ }
+ } else {
+ dev_dbg(tas_priv->dev, "%s: Unneeded loading dsp conf %d\n",
+ __func__, cfg_no);
+ }
+
+ return prog_status;
+}
+
+int tasdevice_spi_prmg_load(void *context, int prm_no)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_prog *program;
+ int prog_status = 0;
+
+ if (!tas_fmw) {
+ dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+ return 0;
+ }
+
+ if (prm_no >= tas_fmw->nr_programs) {
+ dev_err(tas_priv->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->nr_programs);
+ return 0;
+ }
+
+ if (prm_no >= 0 && tas_priv->cur_prog != prm_no) {
+ tas_priv->cur_conf = -1;
+ tas_priv->is_loading = true;
+ prog_status++;
+ }
+ if (prog_status) {
+ program = &(tas_fmw->programs[prm_no]);
+ tasdevice_load_data(tas_priv, &(program->dev_data));
+ if ((tas_priv->is_loaderr == false) &&
+ (tas_priv->is_loading == true))
+ tas_priv->cur_prog = prm_no;
+ }
+
+ return prog_status;
+}
+
+int tasdevice_spi_prmg_calibdata_load(void *context, int prm_no)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct tasdevice_prog *program;
+ int prog_status = 0;
+
+ if (!tas_fmw) {
+ dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__);
+ return 0;
+ }
+
+ if (prm_no >= tas_fmw->nr_programs) {
+ dev_err(tas_priv->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->nr_programs);
+ return 0;
+ }
+
+ if (prm_no >= 0 && tas_priv->cur_prog != prm_no) {
+ tas_priv->cur_conf = -1;
+ tas_priv->is_loading = true;
+ prog_status++;
+ }
+ tas_priv->is_loaderr = false;
+
+ if (prog_status) {
+ program = &(tas_fmw->programs[prm_no]);
+ tasdevice_load_data(tas_priv, &(program->dev_data));
+ if (tas_priv->is_loaderr == false &&
+ tas_priv->is_loading == true) {
+ struct tasdevice_fw *cal_fmw =
+ tas_priv->cali_data_fmw;
+
+ if (cal_fmw) {
+ struct tasdevice_calibration *cal =
+ cal_fmw->calibrations;
+
+ if (cal)
+ load_calib_data(tas_priv,
+ &(cal->dev_data));
+ }
+ tas_priv->cur_prog = prm_no;
+ }
+
+ }
+
+ return prog_status;
+}
+
+void tasdevice_spi_tuning_switch(void *context, int state)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ int profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
+
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) {
+ dev_err(tas_priv->dev, "DSP bin file not loaded\n");
+ return;
+ }
+
+ if (state == 0) {
+ if (tas_priv->cur_prog < tas_fmw->nr_programs) {
+ /* dsp mode or tuning mode */
+ profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
+ tasdevice_spi_select_tuningprm_cfg(tas_priv,
+ tas_priv->cur_prog, tas_priv->cur_conf,
+ profile_cfg_id);
+ }
+
+ tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+ } else {
+ tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+ }
+}
--
2.40.1
6
6

11 Jul '24
Hi,
These patches convert the remaining plain text bindings for Qualcomm
sound drivers to dt schema, so device trees can be validated against
them.
v1: https://lore.kernel.org/all/20240709152808.155405-1-rayyan.ansari@linaro.or…
Thanks,
Rayyan
Rayyan Ansari (2):
ASoC: dt-bindings: qcom,msm8916-wcd-digital-codec: convert to dtschema
ASoC: dt-bindings: qcom,apq8096-sndcard: use dtschema
.../bindings/sound/qcom,apq8096.txt | 128 ------------------
.../sound/qcom,msm8916-wcd-digital-codec.yaml | 55 ++++++++
.../sound/qcom,msm8916-wcd-digital.txt | 20 ---
.../bindings/sound/qcom,sm8250.yaml | 1 +
4 files changed, 56 insertions(+), 148 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/sound/qcom,apq8096.txt
create mode 100644 Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt
--
2.45.2
3
4

11 Jul '24
Commit series that makes some small improvements to code and the
kernel log messages.
---
Changes since V1:
- Replaced commit #3 with one that keeps the wmfw format version
message on its own line instead of merging it into the INFO text
message.
- Commit #4 rebased onto the new #3
Richard Fitzgerald (4):
firmware: cs_dsp: Don't allocate temporary buffer for info text
firmware: cs_dsp: Make wmfw and bin filename arguments const char *
firmware: cs_dsp: Clarify wmfw format version log message
firmware: cs_dsp: Rename fw_ver to wmfw_ver
drivers/firmware/cirrus/cs_dsp.c | 68 +++++++++-----------------
include/linux/firmware/cirrus/cs_dsp.h | 10 ++--
sound/soc/codecs/wm_adsp.c | 2 +-
3 files changed, 29 insertions(+), 51 deletions(-)
--
2.39.2
3
6

11 Jul '24
Convert the Cirrus Logic CS4270 audio CODEC bindings to DT schema. Add
missing va-supply, vd-supply and vlc-supply properties, because they
are already being used in the DTS and the driver for this device.
Cc: Daniel Baluta <daniel.baluta(a)nxp.com>
Signed-off-by: Animesh Agarwal <animeshagarwal28(a)gmail.com>
---
Changes in v2:
- Added proper description for power supply properties.
- Added ref to dai-common.
- Added '#sound-dai-cells'.
- Dropped unused labels in example.
- Replaced example node name to a generic name.
---
.../bindings/sound/cirrus,cs4270.yaml | 59 +++++++++++++++++++
.../devicetree/bindings/sound/cs4270.txt | 21 -------
2 files changed, 59 insertions(+), 21 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/cs4270.txt
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml
new file mode 100644
index 000000000000..336e11773694
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/cirrus,cs4270.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cirrus Logic CS4270 audio CODEC
+
+maintainers:
+ - patches(a)opensource.cirrus.com
+
+description:
+ The CS4270 is a stereo audio codec. The driver for this device currently only
+ supports I2C.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: cirrus,cs4270
+
+ reg:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+ reset-gpios:
+ description:
+ This pin will be deasserted before communication to the codec starts.
+ maxItems: 1
+
+ va-supply:
+ description: Analog power supply.
+
+ vd-supply:
+ description: Digital power supply.
+
+ vlc-supply:
+ description: Serial Control Port power supply.
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec@48 {
+ compatible = "cirrus,cs4270";
+ reg = <0x48>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/cs4270.txt b/Documentation/devicetree/bindings/sound/cs4270.txt
deleted file mode 100644
index c33770ec4c3c..000000000000
--- a/Documentation/devicetree/bindings/sound/cs4270.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-CS4270 audio CODEC
-
-The driver for this device currently only supports I2C.
-
-Required properties:
-
- - compatible : "cirrus,cs4270"
-
- - reg : the I2C address of the device for I2C
-
-Optional properties:
-
- - reset-gpios : a GPIO spec for the reset pin. If specified, it will be
- deasserted before communication to the codec starts.
-
-Example:
-
-codec: cs4270@48 {
- compatible = "cirrus,cs4270";
- reg = <0x48>;
-};
--
2.45.2
1
0

10 Jul '24
Allocate memory, which is being freed at end of the scope, with
scoped/cleanup.h to reduce number of error paths and make code a bit
simpler.
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski(a)linaro.org>
---
sound/soc/codecs/aw88395/aw88395_lib.c | 51 +++++++++-----------------
1 file changed, 17 insertions(+), 34 deletions(-)
diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c
index f25f6e0d4428..769ca32a5c8e 100644
--- a/sound/soc/codecs/aw88395/aw88395_lib.c
+++ b/sound/soc/codecs/aw88395/aw88395_lib.c
@@ -7,6 +7,7 @@
// Author: Bruce zhao <zhaolei(a)awinic.com>
//
+#include <linux/cleanup.h>
#include <linux/crc8.h>
#include <linux/i2c.h>
#include "aw88395_lib.h"
@@ -361,11 +362,11 @@ static int aw_dev_parse_raw_dsp_fw(unsigned char *data, unsigned int data_len,
static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *data,
unsigned int data_len, struct aw_prof_desc *prof_desc)
{
- struct aw_bin *aw_bin;
int ret;
int i;
- aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GFP_KERNEL);
+ struct aw_bin *aw_bin __free(kfree) = kzalloc(data_len + sizeof(struct aw_bin),
+ GFP_KERNEL);
if (!aw_bin)
return -ENOMEM;
@@ -375,7 +376,7 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *
ret = aw_parsing_bin_file(aw_dev, aw_bin);
if (ret < 0) {
dev_err(aw_dev->dev, "parse bin failed");
- goto parse_bin_failed;
+ return ret;
}
for (i = 0; i < aw_bin->all_bin_parse_num; i++) {
@@ -387,10 +388,8 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *
data + aw_bin->header_info[i].valid_data_addr;
break;
case DATA_TYPE_DSP_REG:
- if (aw_bin->header_info[i].valid_data_len & 0x01) {
- ret = -EINVAL;
- goto parse_bin_failed;
- }
+ if (aw_bin->header_info[i].valid_data_len & 0x01)
+ return -EINVAL;
swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr),
aw_bin->header_info[i].valid_data_len >> 1);
@@ -402,10 +401,8 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *
break;
case DATA_TYPE_DSP_FW:
case DATA_TYPE_SOC_APP:
- if (aw_bin->header_info[i].valid_data_len & 0x01) {
- ret = -EINVAL;
- goto parse_bin_failed;
- }
+ if (aw_bin->header_info[i].valid_data_len & 0x01)
+ return -EINVAL;
swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr),
aw_bin->header_info[i].valid_data_len >> 1);
@@ -422,20 +419,17 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *
}
}
prof_desc->prof_st = AW88395_PROFILE_OK;
- ret = 0;
-parse_bin_failed:
- devm_kfree(aw_dev->dev, aw_bin);
- return ret;
+ return 0;
}
static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev,
uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc)
{
- struct aw_bin *aw_bin;
int ret;
- aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(*aw_bin), GFP_KERNEL);
+ struct aw_bin *aw_bin __free(kfree) = kzalloc(data_len + sizeof(*aw_bin),
+ GFP_KERNEL);
if (!aw_bin)
return -ENOMEM;
@@ -445,14 +439,13 @@ static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev,
ret = aw_parsing_bin_file(aw_dev, aw_bin);
if (ret < 0) {
dev_err(aw_dev->dev, "parse bin failed");
- goto parse_bin_failed;
+ return ret;
}
if ((aw_bin->all_bin_parse_num != 1) ||
(aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) {
dev_err(aw_dev->dev, "bin num or type error");
- ret = -EINVAL;
- goto parse_bin_failed;
+ return -EINVAL;
}
prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data =
@@ -461,15 +454,7 @@ static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev,
aw_bin->header_info[0].valid_data_len;
prof_desc->prof_st = AW88395_PROFILE_OK;
- devm_kfree(aw_dev->dev, aw_bin);
- aw_bin = NULL;
-
return 0;
-
-parse_bin_failed:
- devm_kfree(aw_dev->dev, aw_bin);
- aw_bin = NULL;
- return ret;
}
static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr,
@@ -678,21 +663,21 @@ static int aw_dev_cfg_get_multiple_valid_prof(struct aw_device *aw_dev,
static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev,
struct aw_cfg_hdr *prof_hdr)
{
- struct aw_all_prof_info *all_prof_info;
int ret;
- all_prof_info = devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_info), GFP_KERNEL);
+ struct aw_all_prof_info *all_prof_info __free(kfree) = kzalloc(sizeof(*all_prof_info),
+ GFP_KERNEL);
if (!all_prof_info)
return -ENOMEM;
ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info);
if (ret < 0) {
- goto exit;
+ return ret;
} else if (ret == AW88395_DEV_TYPE_NONE) {
dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev");
ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info);
if (ret < 0)
- goto exit;
+ return ret;
}
switch (aw_dev->prof_data_type) {
@@ -710,8 +695,6 @@ static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev,
if (!ret)
aw_dev->prof_info.prof_name_list = profile_name;
-exit:
- devm_kfree(aw_dev->dev, all_prof_info);
return ret;
}
--
2.43.0
2
2
alsa-project/alsa-ucm-conf pull request #430 was edited from aledomu:
I'd like to rename the MIDI ports as well but I don't know how. If you can point me where I can learn about it, I'll be glad to do so and mark this PR as finished.
I guess this fix works the same for all Katana models from all generations, but I don't have the PID available to enable it, nor the devices for testing.
Edit: Didn't post the [`alsa-info.sh` output](https://gist.github.com/aledomu/ac3b2741ca2426bc6fbc057907ad0dab).
Request URL : https://github.com/alsa-project/alsa-ucm-conf/pull/430
Patch URL : https://github.com/alsa-project/alsa-ucm-conf/pull/430.patch
Repository URL: https://github.com/alsa-project/alsa-ucm-conf
1
0

10 Jul '24
Commit series that makes some small improvements to code and the
kernel log messages.
Richard Fitzgerald (4):
firmware: cs_dsp: Don't allocate temporary buffer for info text
firmware: cs_dsp: Make wmfw and bin filename arguments const char *
firmware: cs_dsp: Merge wmfw format log message into INFO message
firmware: cs_dsp: Rename fw_ver to wmfw_ver
drivers/firmware/cirrus/cs_dsp.c | 64 +++++++++-----------------
include/linux/firmware/cirrus/cs_dsp.h | 10 ++--
sound/soc/codecs/wm_adsp.c | 2 +-
3 files changed, 27 insertions(+), 49 deletions(-)
--
2.39.2
2
6

10 Jul '24
Hi,
These patches convert the remaining plain text bindings for Qualcomm
sound drivers to dt schema, so device trees can be validated against
them.
Thanks,
Rayyan
Rayyan Ansari (2):
ASoC: dt-bindings: qcom,msm8916-wcd-digital-codec: convert to dtschema
ASoC: dt-bindings: qcom,apq8096-sndcard: use dtschema
.../bindings/sound/qcom,apq8096.txt | 128 ------------------
.../sound/qcom,msm8916-wcd-digital-codec.yaml | 53 ++++++++
.../sound/qcom,msm8916-wcd-digital.txt | 20 ---
.../bindings/sound/qcom,sm8250.yaml | 1 +
4 files changed, 54 insertions(+), 148 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/sound/qcom,apq8096.txt
create mode 100644 Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt
--
2.45.2
2
4
Allocate the memory with scoped/cleanup.h to reduce error handling
(simpler error paths) and make the code a bit smaller.
Best regards,
Krzysztof
---
Krzysztof Kozlowski (9):
soundwire: amd: simplify return path in hw_params
soundwire: amd: simplify with cleanup.h
soundwire: amd_init: simplify with cleanup.h
soundwire: intel: simplify return path in hw_params
soundwire: intel: simplify with cleanup.h
soundwire: intel_ace2x: simplify return path in hw_params
soundwire: intel_ace2x: simplify with cleanup.h
soundwire: cadence: simplify with cleanup.h
soundwire: debugfs: simplify with cleanup.h
drivers/soundwire/amd_init.c | 7 +++----
drivers/soundwire/amd_manager.c | 13 +++++--------
drivers/soundwire/cadence_master.c | 5 ++---
drivers/soundwire/debugfs.c | 7 ++-----
drivers/soundwire/intel.c | 25 +++++++++----------------
drivers/soundwire/intel_ace2x.c | 22 ++++++++--------------
6 files changed, 29 insertions(+), 50 deletions(-)
---
base-commit: 246cf92bafedeea46cb16964d16478d6b68f8e5a
change-id: 20240703-soundwire-cleanup-h-a29941fb3dd4
Best regards,
--
Krzysztof Kozlowski <krzysztof.kozlowski(a)linaro.org>
2
10

I have the MPG Z690 Edge Wifi DDR4 motherboard, and no sound in front panel or back panel
by GitHub issues - opened 10 Jul '24
by GitHub issues - opened 10 Jul '24
10 Jul '24
alsa-project/alsa-ucm-conf issue #432 was opened from angelbearuk:
I'm using Linux Mint 21, and am very frustrated. Not being able to hear any sound just kills the Linux experience for me. I had no issues with sound on Windows 10. Please help me. I'm a Linux newbie.
Issue URL : https://github.com/alsa-project/alsa-ucm-conf/issues/432
Repository URL: https://github.com/alsa-project/alsa-ucm-conf
1
0

10 Jul '24
Rename the tas2781_reset as tasdevice_reset in case of misunderstanding.
RESET register for both tas2563 and tas2781 is same and the use of reset
pin is also same.
Signed-off-by: Shenghao Ding <shenghao-ding(a)ti.com>
---
v1:
- Rename tas2781_reset as tasdevice_reset
- Changed the copyright year to 2024 in tas2781.h.
- Rename TAS2781_REG_SWRESET_XXX as TASDEVICE_REG_SWRESET_XXX
- Add tasdevice_reset into tasdevice_i2c_probe.
---
include/sound/tas2781.h | 8 ++++----
sound/pci/hda/tas2781_hda_i2c.c | 4 ++--
sound/soc/codecs/tas2781-comlib.c | 8 ++++----
sound/soc/codecs/tas2781-i2c.c | 2 ++
4 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h
index cd8ce522b78e..a43ad6dcb7c7 100644
--- a/include/sound/tas2781.h
+++ b/include/sound/tas2781.h
@@ -2,7 +2,7 @@
//
// ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier
//
-// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+// Copyright (C) 2022 - 2024 Texas Instruments Incorporated
// https://www.ti.com
//
// The TAS2563/TAS2781 driver implements a flexible and configurable
@@ -43,8 +43,8 @@
(page * 128)) + reg)
/*Software Reset */
-#define TAS2781_REG_SWRESET TASDEVICE_REG(0x0, 0X0, 0x01)
-#define TAS2781_REG_SWRESET_RESET BIT(0)
+#define TASDEVICE_REG_SWRESET TASDEVICE_REG(0x0, 0X0, 0x01)
+#define TASDEVICE_REG_SWRESET_RESET BIT(0)
/*I2C Checksum */
#define TASDEVICE_I2CChecksum TASDEVICE_REG(0x0, 0x0, 0x7E)
@@ -140,7 +140,7 @@ struct tasdevice_priv {
void (*apply_calibration)(struct tasdevice_priv *tas_priv);
};
-void tas2781_reset(struct tasdevice_priv *tas_dev);
+void tasdevice_reset(struct tasdevice_priv *tas_dev);
int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
struct module *module,
void (*cont)(const struct firmware *fw, void *context));
diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c
index 75f7674c66ee..10420360b214 100644
--- a/sound/pci/hda/tas2781_hda_i2c.c
+++ b/sound/pci/hda/tas2781_hda_i2c.c
@@ -834,7 +834,7 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt)
pm_runtime_set_active(tas_hda->dev);
pm_runtime_enable(tas_hda->dev);
- tas2781_reset(tas_hda->priv);
+ tasdevice_reset(tas_hda->priv);
ret = component_add(tas_hda->dev, &tas2781_hda_comp_ops);
if (ret) {
@@ -929,7 +929,7 @@ static int tas2781_system_resume(struct device *dev)
tas_hda->priv->tasdevice[i].cur_prog = -1;
tas_hda->priv->tasdevice[i].cur_conf = -1;
}
- tas2781_reset(tas_hda->priv);
+ tasdevice_reset(tas_hda->priv);
tasdevice_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog);
/* If calibrated data occurs error, dsp will still work with default
diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c
index 6db1a260da82..1fbf4560f5cc 100644
--- a/sound/soc/codecs/tas2781-comlib.c
+++ b/sound/soc/codecs/tas2781-comlib.c
@@ -243,7 +243,7 @@ struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c)
}
EXPORT_SYMBOL_GPL(tasdevice_kzalloc);
-void tas2781_reset(struct tasdevice_priv *tas_dev)
+void tasdevice_reset(struct tasdevice_priv *tas_dev)
{
int ret, i;
@@ -254,8 +254,8 @@ void tas2781_reset(struct tasdevice_priv *tas_dev)
} else {
for (i = 0; i < tas_dev->ndev; i++) {
ret = tasdevice_dev_write(tas_dev, i,
- TAS2781_REG_SWRESET,
- TAS2781_REG_SWRESET_RESET);
+ TASDEVICE_REG_SWRESET,
+ TASDEVICE_REG_SWRESET_RESET);
if (ret < 0)
dev_err(tas_dev->dev,
"dev %d swreset fail, %d\n",
@@ -264,7 +264,7 @@ void tas2781_reset(struct tasdevice_priv *tas_dev)
}
usleep_range(1000, 1050);
}
-EXPORT_SYMBOL_GPL(tas2781_reset);
+EXPORT_SYMBOL_GPL(tasdevice_reset);
int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
struct module *module,
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index b412f8f2e5d5..d5d95ae57c73 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -742,6 +742,8 @@ static int tasdevice_i2c_probe(struct i2c_client *i2c)
if (ret)
goto err;
+ tasdevice_reset(tas_priv);
+
ret = devm_snd_soc_register_component(tas_priv->dev,
&soc_codec_driver_tasdevice,
tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver));
--
2.34.1
2
1
Replace a comma between expression statements by a semicolon.
Signed-off-by: Chen Ni <nichen(a)iscas.ac.cn>
---
sound/soc/intel/boards/sof_sdw.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index e94849b84a6b..e5feaef669d1 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -2136,9 +2136,9 @@ static int mc_probe(struct platform_device *pdev)
card = &ctx->card;
card->dev = &pdev->dev;
- card->name = "soundwire",
- card->owner = THIS_MODULE,
- card->late_probe = sof_sdw_card_late_probe,
+ card->name = "soundwire";
+ card->owner = THIS_MODULE;
+ card->late_probe = sof_sdw_card_late_probe;
snd_soc_card_set_drvdata(card, ctx);
--
2.25.1
2
1

09 Jul '24
Convert the Cirrus Logic CS4270 audio CODEC bindings to DT schema. Add
missing va-supply, vd-supply and vlc-supply properties, because they
are already being used in the DTS and the driver for this device.
Cc: Daniel Baluta <daniel.baluta(a)nxp.com>
Signed-off-by: Animesh Agarwal <animeshagarwal28(a)gmail.com>
---
.../bindings/sound/cirrus,cs4270.yaml | 53 +++++++++++++++++++
.../devicetree/bindings/sound/cs4270.txt | 21 --------
2 files changed, 53 insertions(+), 21 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml
delete mode 100644 Documentation/devicetree/bindings/sound/cs4270.txt
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml
new file mode 100644
index 000000000000..824d799ee5ff
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/cirrus,cs4270.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cirrus Logic CS4270 audio CODEC
+
+maintainers:
+ - patches(a)opensource.cirrus.com
+
+description:
+ The CS4270 is a stereo audio codec. The driver for this device currently only
+ supports I2C.
+
+properties:
+ compatible:
+ const: cirrus,cs4270
+
+ reg:
+ maxItems: 1
+
+ reset-gpios:
+ description:
+ This pin will be deasserted before communication to the codec starts.
+ maxItems: 1
+
+ va-supply:
+ description: Voltage regulator phandle for the VA supply.
+
+ vd-supply:
+ description: Voltage regulator phandle for the VD supply.
+
+ vlc-supply:
+ description: Voltage regulator phandle for the VLC supply.
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec: cs4270@48 {
+ compatible = "cirrus,cs4270";
+ reg = <0x48>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/cs4270.txt b/Documentation/devicetree/bindings/sound/cs4270.txt
deleted file mode 100644
index c33770ec4c3c..000000000000
--- a/Documentation/devicetree/bindings/sound/cs4270.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-CS4270 audio CODEC
-
-The driver for this device currently only supports I2C.
-
-Required properties:
-
- - compatible : "cirrus,cs4270"
-
- - reg : the I2C address of the device for I2C
-
-Optional properties:
-
- - reset-gpios : a GPIO spec for the reset pin. If specified, it will be
- deasserted before communication to the codec starts.
-
-Example:
-
-codec: cs4270@48 {
- compatible = "cirrus,cs4270";
- reg = <0x48>;
-};
--
2.45.2
2
2

[PATCH AUTOSEL 4.19 2/7] bytcr_rt5640 : inverse jack detect for Archos 101 cesium
by Sasha Levin 09 Jul '24
by Sasha Levin 09 Jul '24
09 Jul '24
From: Thomas GENTY <tomlohave(a)gmail.com>
[ Upstream commit e3209a1827646daaab744aa6a5767b1f57fb5385 ]
When headphones are plugged in, they appear absent; when they are removed,
they appear present.
Add a specific entry in bytcr_rt5640 for this device
Signed-off-by: Thomas GENTY <tomlohave(a)gmail.com>
Reviewed-by: Hans de Goede <hdegoede(a)redhat.com>
Acked-by: Pierre-Louis Bossart <pierre-louis.bossart(a)linux.intel.com>
Link: https://lore.kernel.org/r/20240608170251.99936-1-tomlohave@gmail.com
Signed-off-by: Mark Brown <broonie(a)kernel.org>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
sound/soc/intel/boards/bytcr_rt5640.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 19f425eb4a40f..16e2ab2903754 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -474,6 +474,17 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_SSP0_AIF1 |
BYT_RT5640_MCLK_EN),
},
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ARCHOS 101 CESIUM"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_JD_NOT_INV |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"),
--
2.43.0
1
0

09 Jul '24
From: Primoz Fiser <primoz.fiser(a)norik.com>
[ Upstream commit 524d3f126362b6033e92cbe107ae2158d7fbff94 ]
Set driver name to "HDMI". This simplifies the code and gets rid of
the following error messages:
ASoC: driver name too long 'HDMI 58040000.encoder' -> 'HDMI_58040000_e'
Signed-off-by: Primoz Fiser <primoz.fiser(a)norik.com>
Acked-by: Peter Ujfalusi <peter.ujfalusi(a)gmail.com>
Link: https://lore.kernel.org/r/20240610125847.773394-1-primoz.fiser@norik.com
Signed-off-by: Mark Brown <broonie(a)kernel.org>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
sound/soc/ti/omap-hdmi.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c
index def2a0ce88863..e4f661c5349ef 100644
--- a/sound/soc/ti/omap-hdmi.c
+++ b/sound/soc/ti/omap-hdmi.c
@@ -353,11 +353,7 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
- card->name = devm_kasprintf(dev, GFP_KERNEL,
- "HDMI %s", dev_name(ad->dssdev));
- if (!card->name)
- return -ENOMEM;
-
+ card->name = "HDMI";
card->owner = THIS_MODULE;
card->dai_link =
devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL);
--
2.43.0
1
0

[PATCH AUTOSEL 5.4 03/11] ASoC: ti: davinci-mcasp: Set min period size using FIFO config
by Sasha Levin 09 Jul '24
by Sasha Levin 09 Jul '24
09 Jul '24
From: Jai Luthra <j-luthra(a)ti.com>
[ Upstream commit c5dcf8ab10606e76c1d8a0ec77f27d84a392e874 ]
The minimum period size was enforced to 64 as older devices integrating
McASP with EDMA used an internal FIFO of 64 samples.
With UDMA based platforms this internal McASP FIFO is optional, as the
DMA engine internally does some buffering which is already accounted for
when registering the platform. So we should read the actual FIFO
configuration (txnumevt/rxnumevt) instead of hardcoding frames.min to
64.
Acked-by: Peter Ujfalusi <peter.ujfalusi(a)gmail.com>
Signed-off-by: Jai Luthra <j-luthra(a)ti.com>
Link: https://lore.kernel.org/r/20240611-asoc_next-v3-2-fcfd84b12164@ti.com
Signed-off-by: Mark Brown <broonie(a)kernel.org>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
sound/soc/ti/davinci-mcasp.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index 0541071f454bd..9a182e4c7d572 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -1419,10 +1419,11 @@ static int davinci_mcasp_hw_rule_min_periodsize(
{
struct snd_interval *period_size = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ u8 numevt = *((u8 *)rule->private);
struct snd_interval frames;
snd_interval_any(&frames);
- frames.min = 64;
+ frames.min = numevt;
frames.integer = 1;
return snd_interval_refine(period_size, &frames);
@@ -1437,6 +1438,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
u32 max_channels = 0;
int i, dir, ret;
int tdm_slots = mcasp->tdm_slots;
+ u8 *numevt;
/* Do not allow more then one stream per direction */
if (mcasp->substreams[substream->stream])
@@ -1536,9 +1538,12 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
return ret;
}
+ numevt = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ &mcasp->txnumevt :
+ &mcasp->rxnumevt;
snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- davinci_mcasp_hw_rule_min_periodsize, NULL,
+ davinci_mcasp_hw_rule_min_periodsize, numevt,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
return 0;
--
2.43.0
1
0