Skip to content

Instantly share code, notes, and snippets.

@Saren-Arterius
Last active January 14, 2023 03:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Saren-Arterius/27fd721f50d0b174e20082cee070a454 to your computer and use it in GitHub Desktop.
Save Saren-Arterius/27fd721f50d0b174e20082cee070a454 to your computer and use it in GitHub Desktop.
Lenovo Yoga 7 Gen 7 (14ARB7) Linux fix (for 6.0.x)
diff --git a/Documentation/devicetree/bindings/sound/tas2562.yaml b/Documentation/devicetree/bindings/sound/tas2562.yaml
index 30f6b029ac08..9af81faf4b42 100644
--- a/Documentation/devicetree/bindings/sound/tas2562.yaml
+++ b/Documentation/devicetree/bindings/sound/tas2562.yaml
@@ -54,6 +54,10 @@ properties:
'#sound-dai-cells':
const: 1
+ firmware-name:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: Name of the firmware to be loaded to the DSP. TAS2563 only.
+
required:
- compatible
- reg
diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c
index f9ac12b778e6..46b83f9451be 100644
--- a/drivers/acpi/x86/s2idle.c
+++ b/drivers/acpi/x86/s2idle.c
@@ -17,6 +17,7 @@
#include <linux/acpi.h>
#include <linux/device.h>
+#include <linux/dmi.h>
#include <linux/suspend.h>
#include "../sleep.h"
@@ -27,6 +28,10 @@ static bool sleep_no_lps0 __read_mostly;
module_param(sleep_no_lps0, bool, 0644);
MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface");
+static bool prefer_microsoft_dsm_guid __read_mostly;
+module_param(prefer_microsoft_dsm_guid, bool, 0644);
+MODULE_PARM_DESC(prefer_microsoft_dsm_guid, "Prefer using Microsoft GUID in LPS0 device _DSM evaluation");
+
static const struct acpi_device_id lps0_device_ids[] = {
{"PNP0D80", },
{"", },
@@ -363,40 +368,120 @@ static int validate_dsm(acpi_handle handle, const char *uuid, int rev, guid_t *d
return ret;
}
+
+struct amd_lps0_hid_device_data {
+ const unsigned int rev_id;
+ const bool check_off_by_one;
+ const bool prefer_amd_guid;
+};
+
+static const struct amd_lps0_hid_device_data amd_picasso = {
+ .rev_id = 0,
+ .check_off_by_one = true,
+ .prefer_amd_guid = false,
+};
+
+static const struct amd_lps0_hid_device_data amd_cezanne = {
+ .rev_id = 0,
+ .check_off_by_one = false,
+ .prefer_amd_guid = false,
+};
+
+static const struct amd_lps0_hid_device_data amd_rembrandt = {
+ .rev_id = 2,
+ .check_off_by_one = false,
+ .prefer_amd_guid = true,
+};
+
+static const struct acpi_device_id amd_hid_ids[] = {
+ {"AMD0004", (kernel_ulong_t)&amd_picasso, },
+ {"AMD0005", (kernel_ulong_t)&amd_picasso, },
+ {"AMDI0005", (kernel_ulong_t)&amd_picasso, },
+ {"AMDI0006", (kernel_ulong_t)&amd_cezanne, },
+ {"AMDI0007", (kernel_ulong_t)&amd_rembrandt, },
+ {}
+};
+
+static int lps0_prefer_microsoft(const struct dmi_system_id *id)
+{
+ pr_debug("Preferring Microsoft GUID.\n");
+ prefer_microsoft_dsm_guid = true;
+ return 0;
+}
+
+static const struct dmi_system_id s2idle_dmi_table[] __initconst = {
+ {
+ /*
+ * ASUS TUF Gaming A17 FA707RE
+ * https://bugzilla.kernel.org/show_bug.cgi?id=216101
+ */
+ .callback = lps0_prefer_microsoft,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASUS TUF Gaming A17"),
+ },
+ },
+ {
+ /* ASUS ROG Zephyrus G14 (2022) */
+ .callback = lps0_prefer_microsoft,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ROG Zephyrus G14 GA402"),
+ },
+ },
+ {
+ /* ASUS ROG Zephyrus G14 (2022) */
+ .callback = lps0_prefer_microsoft,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ROG Zephyrus G14 GA402"),
+ },
+ },
+ {
+ /*
+ * Lenovo Yoga Slim 7 Pro X 14ARH7
+ * https://bugzilla.kernel.org/show_bug.cgi?id=216473 : 82V2
+ * https://bugzilla.kernel.org/show_bug.cgi?id=216438 : 82TL
+ */
+ .callback = lps0_prefer_microsoft,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82"),
+ },
+ },
+ {}
+};
+
+
static int lps0_device_attach(struct acpi_device *adev,
const struct acpi_device_id *not_used)
{
if (lps0_device_handle)
return 0;
+ lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle,
+ ACPI_LPS0_DSM_UUID_MICROSOFT, 0,
+ &lps0_dsm_guid_microsoft);
+
if (acpi_s2idle_vendor_amd()) {
- /* AMD0004, AMD0005, AMDI0005:
- * - Should use rev_id 0x0
- * - function mask > 0x3: Should use AMD method, but has off by one bug
- * - function mask = 0x3: Should use Microsoft method
- * AMDI0006:
- * - should use rev_id 0x0
- * - function mask = 0x3: Should use Microsoft method
- * AMDI0007:
- * - Should use rev_id 0x2
- * - Should only use AMD method
- */
- const char *hid = acpi_device_hid(adev);
- rev_id = strcmp(hid, "AMDI0007") ? 0 : 2;
+ static const struct acpi_device_id *dev_id;
+ const struct amd_lps0_hid_device_data *data;
+ for (dev_id = &amd_hid_ids[0]; dev_id->id[0]; dev_id++)
+ if (acpi_dev_hid_uid_match(adev, dev_id->id, NULL))
+ break;
+ if (dev_id)
+ data = (const struct amd_lps0_hid_device_data *) dev_id->driver_data;
+ else
+ data = &amd_rembrandt;
+ rev_id = data->rev_id;
lps0_dsm_func_mask = validate_dsm(adev->handle,
ACPI_LPS0_DSM_UUID_AMD, rev_id, &lps0_dsm_guid);
- lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle,
- ACPI_LPS0_DSM_UUID_MICROSOFT, 0,
- &lps0_dsm_guid_microsoft);
- if (lps0_dsm_func_mask > 0x3 && (!strcmp(hid, "AMD0004") ||
- !strcmp(hid, "AMD0005") ||
- !strcmp(hid, "AMDI0005"))) {
+ if (lps0_dsm_func_mask > 0x3 && data->check_off_by_one) {
lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1;
acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n",
ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask);
- } else if (lps0_dsm_func_mask_microsoft > 0 &&
- (!strcmp(hid, "AMDI0007") ||
- !strcmp(hid, "AMDI0008"))) {
+ } else if (lps0_dsm_func_mask_microsoft > 0 && data->prefer_amd_guid &&
+ !prefer_microsoft_dsm_guid) {
lps0_dsm_func_mask_microsoft = -EINVAL;
acpi_handle_debug(adev->handle, "_DSM Using AMD method\n");
}
@@ -404,7 +489,8 @@ static int lps0_device_attach(struct acpi_device *adev,
rev_id = 1;
lps0_dsm_func_mask = validate_dsm(adev->handle,
ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid);
- lps0_dsm_func_mask_microsoft = -EINVAL;
+ if (!prefer_microsoft_dsm_guid)
+ lps0_dsm_func_mask_microsoft = -EINVAL;
}
if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0)
@@ -533,8 +619,9 @@ static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = {
.end = acpi_s2idle_end,
};
-void acpi_s2idle_setup(void)
+void __init acpi_s2idle_setup(void)
{
+ dmi_check_system(s2idle_dmi_table);
acpi_scan_add_handler(&lps0_handler);
s2idle_set_ops(&acpi_s2idle_ops_lps0);
}
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index e0b24e1daef3..0812a60d271a 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -45,6 +45,13 @@ static struct snd_soc_card acp6x_card = {
};
static const struct dmi_system_id yc_acp_quirk_table[] = {
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82"),
+ }
+ },
{
.driver_data = &acp6x_card,
.matches = {
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 92fd441d426a..685a855aae73 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -346,7 +346,7 @@ snd-soc-max98504-objs := max98504.o
snd-soc-simple-amplifier-objs := simple-amplifier.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
snd-soc-tas2552-objs := tas2552.o
-snd-soc-tas2562-objs := tas2562.o
+snd-soc-tas2562-objs := tas2562.o tas25xx_dsp_loader.o
snd-soc-tas2764-objs := tas2764.o
snd-soc-tas2780-objs := tas2780.o
# Mux
diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c
index dc088a1c6721..9a4945c07d59 100644
--- a/sound/soc/codecs/tas2562.c
+++ b/sound/soc/codecs/tas2562.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/device.h>
+#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -22,6 +23,7 @@
#include <sound/tlv.h>
#include "tas2562.h"
+#include "tas25xx_dsp_loader.h"
#define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FORMAT_S32_LE)
@@ -44,25 +46,6 @@ static const unsigned int float_vol_db_lookup[] = {
0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, 0x40000000
};
-struct tas2562_data {
- struct snd_soc_component *component;
- struct gpio_desc *sdz_gpio;
- struct regmap *regmap;
- struct device *dev;
- struct i2c_client *client;
- int v_sense_slot;
- int i_sense_slot;
- int volume_lvl;
- int model_id;
-};
-
-enum tas256x_model {
- TAS2562,
- TAS2563,
- TAS2564,
- TAS2110,
-};
-
static int tas2562_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
@@ -393,6 +376,17 @@ static int tas2562_mute(struct snd_soc_dai *dai, int mute, int direction)
mute ? TAS2562_MUTE : 0);
}
+static void tas2562_fw_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_component *component = context;
+ struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = tas25xx_init_fw(tas2562, fw);
+ if (ret)
+ dev_err(tas2562->dev, "Firmware failed to initialize\n");
+}
+
static int tas2562_codec_probe(struct snd_soc_component *component)
{
struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
@@ -408,6 +402,12 @@ static int tas2562_codec_probe(struct snd_soc_component *component)
if (ret < 0)
return ret;
+ if (tas2562->load_firmware == 0 && tas2562->model_id == TAS2563)
+ request_firmware_nowait(THIS_MODULE, 1,
+ tas2562->firmware_name, component->dev,
+ GFP_KERNEL, component,
+ tas2562_fw_loaded);
+
return 0;
}
@@ -663,7 +663,7 @@ static struct snd_soc_dai_driver tas2562_dai[] = {
static const struct regmap_range_cfg tas2562_ranges[] = {
{
.range_min = 0,
- .range_max = 5 * 128,
+ .range_max = 255 * 128,
.selector_reg = TAS2562_PAGE_CTRL,
.selector_mask = 0xff,
.selector_shift = 0,
@@ -689,7 +689,7 @@ static const struct regmap_config tas2562_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .max_register = 5 * 128,
+ .max_register = 255 * 128,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = tas2562_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tas2562_reg_defaults),
@@ -749,6 +749,14 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562)
return -EINVAL;
}
+ if (tas2562->model_id != TAS2562) {
+ tas2562->load_firmware = fwnode_property_read_string(dev->fwnode,
+ "firmware-name",
+ &tas2562->firmware_name);
+ if (tas2562->load_firmware)
+ dev_info(dev, "No firmware file to request\n");
+ }
+
return ret;
}
diff --git a/sound/soc/codecs/tas2562.h b/sound/soc/codecs/tas2562.h
index 55b2a1f52ca3..a47c35ab06f4 100644
--- a/sound/soc/codecs/tas2562.h
+++ b/sound/soc/codecs/tas2562.h
@@ -11,6 +11,7 @@
#define __TAS2562_H__
#define TAS2562_PAGE_CTRL 0x00
+#define TAS2562_BOOK_CTRL 0x7f
#define TAS2562_REG(page, reg) ((page * 128) + reg)
@@ -44,6 +45,8 @@
#define TAS2562_DVC_CFG3 TAS2562_REG(2, 0x0e)
#define TAS2562_DVC_CFG4 TAS2562_REG(2, 0x0f)
+#define TAS25XX_DSP_MODE TAS2562_REG(1, 2)
+
#define TAS2562_RESET BIT(0)
#define TAS2562_MODE_MASK GENMASK(1,0)
@@ -87,4 +90,28 @@
#define TAS2562_TDM_CFG6_ISNS_EN BIT(6)
#define TAS2562_TDM_CFG6_ISNS_SLOT_MASK GENMASK(5, 0)
+#define TAS2563_FW_HDR_OFFSET 134
+
+struct tas2562_data {
+ struct snd_soc_component *component;
+ struct gpio_desc *sdz_gpio;
+ struct regmap *regmap;
+ struct device *dev;
+ struct i2c_client *client;
+ struct tas25xx_fw_data *fw_data;
+ const char *firmware_name;
+ int load_firmware;
+ int model_id;
+ int v_sense_slot;
+ int i_sense_slot;
+ int volume_lvl;
+};
+
+enum tas2562_id {
+ TAS2110,
+ TAS2562,
+ TAS2563,
+ TAS2564
+};
+
#endif /* __TAS2562_H__ */

This could work for other zen3+ devices or lenovo amd devices too. Requires a kernel with https://lore.kernel.org/all/CAJZ5v0isLQVX3EqsokFthY5ka=V4Vse9T52s3EGSv41FKM1iGw@mail.gmail.com/ applied (5.19.10+ on archlinux, 5.20.x, 6.0.x) otherwise your keyboard will not work.

Fixes

  1. s2idle issue / not sleeping / keyboard not working after resuming from suspend: https://lore.kernel.org/lkml/20220921204055.22889-1-mario.limonciello@amd.com/
  2. microphone not working: https://wiki.archlinux.org/title/Lenovo_Yoga_Slim_7_Pro_X_(14ARH7)
  3. treble speaker / no bass / bass speaker not enabled / bad speaker quality: https://bugzilla.kernel.org/show_bug.cgi?id=208555#c689

Run after each boot / each resume from suspend:

$ i2cset 3 0x48 0x2 0
@mxmvncnt
Copy link

awesome, will this get pushed to upstream?

@VALDUHA
Copy link

VALDUHA commented Jan 14, 2023

Do you have instructions for installing this patch? I really don't know what to do with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment