From 2b101256fd552bf2e29980d51394ebd88737159c Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 20 Jan 2022 15:16:00 -0600 Subject: [PATCH 01/46] ALSA: usb-audio: scarlett2: Use struct_size() helper in scarlett2_usb() Make use of the struct_size() helper instead of an open-coded version, in order to avoid any potential type mistakes or integer overflows that, in the worst scenario, could lead to heap overflows. Also, address the following sparse warnings: sound/usb/mixer_scarlett_gen2.c:1064:28: warning: using sizeof on a flexible structure sound/usb/mixer_scarlett_gen2.c:1065:29: warning: using sizeof on a flexible structure Link: https://github.com/KSPP/linux/issues/174 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Link: https://lore.kernel.org/r/20220120211600.GA28841@embeddedor Signed-off-by: Takashi Iwai --- sound/usb/mixer_scarlett_gen2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 53ebabf42472..7ff8a4817c67 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1061,9 +1061,9 @@ static int scarlett2_usb( { struct scarlett2_data *private = mixer->private_data; struct usb_device *dev = mixer->chip->dev; - u16 req_buf_size = sizeof(struct scarlett2_usb_packet) + req_size; - u16 resp_buf_size = sizeof(struct scarlett2_usb_packet) + resp_size; struct scarlett2_usb_packet *req, *resp = NULL; + size_t req_buf_size = struct_size(req, data, req_size); + size_t resp_buf_size = struct_size(resp, data, resp_size); int err; req = kmalloc(req_buf_size, GFP_KERNEL); @@ -1111,7 +1111,7 @@ static int scarlett2_usb( usb_audio_err( mixer->chip, "Scarlett Gen 2/3 USB response result cmd %x was %d " - "expected %d\n", + "expected %zu\n", cmd, err, resp_buf_size); err = -EINVAL; goto unlock; From 88b6132248940954b958cd4e6d0432c640a9abd2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 25 Jan 2022 16:28:24 +0000 Subject: [PATCH 02/46] kselftest: alsa: Add test case for writing invalid values Attempt to write various invalid values for control types we know about and check that something sensible happens. The ABI isn't quite as clearly defined as one might like, rather than generating an error when an invalid value is written many devices will silently rewrite the value into one that is valid for the control. The exact value chosen is not predictable so in the case the write succeeds we just check that the value we read back is one that is valid for the control. Reviewed-by: Shuah Khan Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220125162824.3816659-1-broonie@kernel.org Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 222 +++++++++++++++++++++- 1 file changed, 221 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index 17f158d7a767..0e88f4f3d802 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,7 @@ #include "../kselftest.h" -#define TESTS_PER_CONTROL 3 +#define TESTS_PER_CONTROL 4 struct card_data { snd_ctl_t *handle; @@ -679,6 +680,224 @@ void test_ctl_write_valid(struct ctl_data *ctl) ctl->card->card, ctl->elem); } +bool test_ctl_write_invalid_value(struct ctl_data *ctl, + snd_ctl_elem_value_t *val) +{ + int err; + long val_read; + + /* Ideally this will fail... */ + err = snd_ctl_elem_write(ctl->card->handle, val); + if (err < 0) + return false; + + /* ...but some devices will clamp to an in range value */ + err = snd_ctl_elem_read(ctl->card->handle, val); + if (err < 0) { + ksft_print_msg("%s failed to read: %s\n", + ctl->name, snd_strerror(err)); + return true; + } + + return !ctl_value_valid(ctl, val); +} + +bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) +{ + int err, i; + long val_read; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_boolean(val, i, 2); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + } + + return !fail; +} + +bool test_ctl_write_invalid_integer(struct ctl_data *ctl) +{ + int i; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) { + /* Just under range */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer(val, i, + snd_ctl_elem_info_get_min(ctl->info) - 1); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + /* Minimum representable value */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer(val, i, LONG_MIN); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + } + + if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) { + /* Just over range */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer(val, i, + snd_ctl_elem_info_get_max(ctl->info) + 1); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + /* Maximum representable value */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer(val, i, LONG_MAX); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + } + } + + return !fail; +} + +bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) +{ + int i; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) { + /* Just under range */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer64(val, i, + snd_ctl_elem_info_get_min64(ctl->info) - 1); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + /* Minimum representable value */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer64(val, i, LLONG_MIN); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + } + + if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) { + /* Just over range */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer64(val, i, + snd_ctl_elem_info_get_max64(ctl->info) + 1); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + /* Maximum representable value */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_integer64(val, i, LLONG_MAX); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + } + } + + return !fail; +} + +bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) +{ + int err, i; + unsigned int val_read; + bool fail = false; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_alloca(&val); + + snd_ctl_elem_value_set_id(val, ctl->id); + + for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { + /* One beyond maximum */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_enumerated(val, i, + snd_ctl_elem_info_get_items(ctl->info)); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + /* Maximum representable value */ + snd_ctl_elem_value_copy(val, ctl->def_val); + snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX); + + if (test_ctl_write_invalid_value(ctl, val)) + fail = true; + + } + + return !fail; +} + + +void test_ctl_write_invalid(struct ctl_data *ctl) +{ + bool pass; + int err; + + /* If the control is turned off let's be polite */ + if (snd_ctl_elem_info_is_inactive(ctl->info)) { + ksft_print_msg("%s is inactive\n", ctl->name); + ksft_test_result_skip("write_invalid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + if (!snd_ctl_elem_info_is_writable(ctl->info)) { + ksft_print_msg("%s is not writeable\n", ctl->name); + ksft_test_result_skip("write_invalid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + switch (snd_ctl_elem_info_get_type(ctl->info)) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + pass = test_ctl_write_invalid_boolean(ctl); + break; + + case SND_CTL_ELEM_TYPE_INTEGER: + pass = test_ctl_write_invalid_integer(ctl); + break; + + case SND_CTL_ELEM_TYPE_INTEGER64: + pass = test_ctl_write_invalid_integer64(ctl); + break; + + case SND_CTL_ELEM_TYPE_ENUMERATED: + pass = test_ctl_write_invalid_enumerated(ctl); + break; + + default: + /* No tests for this yet */ + ksft_test_result_skip("write_invalid.%d.%d\n", + ctl->card->card, ctl->elem); + return; + } + + /* Restore the default value to minimise disruption */ + err = write_and_verify(ctl, ctl->def_val, NULL); + if (err < 0) + pass = false; + + ksft_test_result(pass, "write_invalid.%d.%d\n", + ctl->card->card, ctl->elem); +} + int main(void) { struct ctl_data *ctl; @@ -697,6 +916,7 @@ int main(void) test_ctl_get_value(ctl); test_ctl_write_default(ctl); test_ctl_write_valid(ctl); + test_ctl_write_invalid(ctl); } ksft_exit_pass(); From e3dc1399506f894110667ee5c66a6a70f06f3348 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 21 Jan 2022 17:24:23 +0000 Subject: [PATCH 03/46] spi: Make spi_alloc_device and spi_add_device public again This functions were previously made private since they were not used. However, these functions will be needed again. Partial revert of commit da21fde0fdb3 ("spi: Make several public functions private to spi.c") Signed-off-by: Stefan Binding Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20220121172431.6876-2-sbinding@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 6 ++++-- include/linux/spi/spi.h | 12 ++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 4599b121d744..1eb84101c4ad 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -532,7 +532,7 @@ static DEFINE_MUTEX(board_lock); * * Return: a pointer to the new device, or NULL. */ -static struct spi_device *spi_alloc_device(struct spi_controller *ctlr) +struct spi_device *spi_alloc_device(struct spi_controller *ctlr) { struct spi_device *spi; @@ -557,6 +557,7 @@ static struct spi_device *spi_alloc_device(struct spi_controller *ctlr) device_initialize(&spi->dev); return spi; } +EXPORT_SYMBOL_GPL(spi_alloc_device); static void spi_dev_set_name(struct spi_device *spi) { @@ -652,7 +653,7 @@ static int __spi_add_device(struct spi_device *spi) * * Return: 0 on success; negative errno on failure */ -static int spi_add_device(struct spi_device *spi) +int spi_add_device(struct spi_device *spi) { struct spi_controller *ctlr = spi->controller; struct device *dev = ctlr->dev.parent; @@ -673,6 +674,7 @@ static int spi_add_device(struct spi_device *spi) mutex_unlock(&ctlr->add_lock); return status; } +EXPORT_SYMBOL_GPL(spi_add_device); static int spi_add_device_locked(struct spi_device *spi) { diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 7ab3fed7b804..0346a3ff27fd 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -1452,7 +1452,19 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) * use spi_new_device() to describe each device. You can also call * spi_unregister_device() to start making that device vanish, but * normally that would be handled by spi_unregister_controller(). + * + * You can also use spi_alloc_device() and spi_add_device() to use a two + * stage registration sequence for each spi_device. This gives the caller + * some more control over the spi_device structure before it is registered, + * but requires that caller to initialize fields that would otherwise + * be defined using the board info. */ +extern struct spi_device * +spi_alloc_device(struct spi_controller *ctlr); + +extern int +spi_add_device(struct spi_device *spi); + extern struct spi_device * spi_new_device(struct spi_controller *, struct spi_board_info *); From 000bee0ed70af79e610444096fb453430220960f Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 21 Jan 2022 17:24:24 +0000 Subject: [PATCH 04/46] spi: Create helper API to lookup ACPI info for spi device This can then be used to find a spi resource inside an ACPI node, and allocate a spi device. Signed-off-by: Stefan Binding Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20220121172431.6876-3-sbinding@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 46 ++++++++++++++++++++++++++++++++--------- include/linux/spi/spi.h | 6 ++++++ 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1eb84101c4ad..13f4701f0694 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2410,8 +2410,18 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) return 1; } -static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, - struct acpi_device *adev) +/** + * acpi_spi_device_alloc - Allocate a spi device, and fill it in with ACPI information + * @ctlr: controller to which the spi device belongs + * @adev: ACPI Device for the spi device + * + * This should be used to allocate a new spi device from and ACPI Node. + * The caller is responsible for calling spi_add_device to register the spi device. + * + * Return: a pointer to the new device, or ERR_PTR on error. + */ +struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, + struct acpi_device *adev) { acpi_handle parent_handle = NULL; struct list_head resource_list; @@ -2419,10 +2429,6 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, struct spi_device *spi; int ret; - if (acpi_bus_get_status(adev) || !adev->status.present || - acpi_device_enumerated(adev)) - return AE_OK; - lookup.ctlr = ctlr; lookup.irq = -1; @@ -2433,7 +2439,7 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, if (ret < 0) /* found SPI in _CRS but it points to another controller */ - return AE_OK; + return ERR_PTR(-ENODEV); if (!lookup.max_speed_hz && ACPI_SUCCESS(acpi_get_parent(adev->handle, &parent_handle)) && @@ -2443,16 +2449,15 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, } if (!lookup.max_speed_hz) - return AE_OK; + return ERR_PTR(-ENODEV); spi = spi_alloc_device(ctlr); if (!spi) { dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n", dev_name(&adev->dev)); - return AE_NO_MEMORY; + return ERR_PTR(-ENOMEM); } - ACPI_COMPANION_SET(&spi->dev, adev); spi->max_speed_hz = lookup.max_speed_hz; spi->mode |= lookup.mode; @@ -2460,6 +2465,27 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, spi->bits_per_word = lookup.bits_per_word; spi->chip_select = lookup.chip_select; + return spi; +} +EXPORT_SYMBOL_GPL(acpi_spi_device_alloc); + +static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, + struct acpi_device *adev) +{ + struct spi_device *spi; + + if (acpi_bus_get_status(adev) || !adev->status.present || + acpi_device_enumerated(adev)) + return AE_OK; + + spi = acpi_spi_device_alloc(ctlr, adev); + if (IS_ERR(spi)) { + if (PTR_ERR(spi) == -ENOMEM) + return AE_NO_MEMORY; + else + return AE_OK; + } + acpi_set_modalias(adev, acpi_device_hid(adev), spi->modalias, sizeof(spi->modalias)); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 0346a3ff27fd..d159cef12f1a 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -16,6 +16,7 @@ #include #include +#include struct dma_chan; struct software_node; @@ -759,6 +760,11 @@ extern int devm_spi_register_controller(struct device *dev, struct spi_controller *ctlr); extern void spi_unregister_controller(struct spi_controller *ctlr); +#if IS_ENABLED(CONFIG_ACPI) +extern struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, + struct acpi_device *adev); +#endif + /* * SPI resource management while processing a SPI message */ From 87e59b36e5e26122efd55d77adb9fac827987db0 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 21 Jan 2022 17:24:25 +0000 Subject: [PATCH 05/46] spi: Support selection of the index of the ACPI Spi Resource before alloc If a node contains more than one SPI resource it may be necessary to use an index to select which one you want to allocate a spi device for. Signed-off-by: Stefan Binding Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20220121172431.6876-4-sbinding@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 51 +++++++++++++++++++++++++++++++++++------ include/linux/spi/spi.h | 3 ++- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 13f4701f0694..06c0a308b38b 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2320,6 +2320,8 @@ struct acpi_spi_lookup { int irq; u8 bits_per_word; u8 chip_select; + int n; + int index; }; static void acpi_spi_parse_apple_properties(struct acpi_device *dev, @@ -2351,6 +2353,8 @@ static void acpi_spi_parse_apple_properties(struct acpi_device *dev, lookup->mode |= SPI_CPHA; } +static struct spi_controller *acpi_spi_find_controller_by_adev(struct acpi_device *adev); + static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) { struct acpi_spi_lookup *lookup = data; @@ -2364,14 +2368,35 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) sb = &ares->data.spi_serial_bus; if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_SPI) { + if (lookup->index != -1 && lookup->n++ != lookup->index) + return 1; + + if (lookup->index == -1 && !ctlr) + return -ENODEV; + status = acpi_get_handle(NULL, sb->resource_source.string_ptr, &parent_handle); - if (ACPI_FAILURE(status) || - ACPI_HANDLE(ctlr->dev.parent) != parent_handle) + if (ACPI_FAILURE(status)) return -ENODEV; + if (ctlr) { + if (ACPI_HANDLE(ctlr->dev.parent) != parent_handle) + return -ENODEV; + } else { + struct acpi_device *adev; + + if (acpi_bus_get_device(parent_handle, &adev)) + return -ENODEV; + + ctlr = acpi_spi_find_controller_by_adev(adev); + if (!ctlr) + return -ENODEV; + + lookup->ctlr = ctlr; + } + /* * ACPI DeviceSelection numbering is handled by the * host controller driver in Windows and can vary @@ -2414,14 +2439,21 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) * acpi_spi_device_alloc - Allocate a spi device, and fill it in with ACPI information * @ctlr: controller to which the spi device belongs * @adev: ACPI Device for the spi device + * @index: Index of the spi resource inside the ACPI Node * * This should be used to allocate a new spi device from and ACPI Node. * The caller is responsible for calling spi_add_device to register the spi device. * + * If ctlr is set to NULL, the Controller for the spi device will be looked up + * using the resource. + * If index is set to -1, index is not used. + * Note: If index is -1, ctlr must be set. + * * Return: a pointer to the new device, or ERR_PTR on error. */ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, - struct acpi_device *adev) + struct acpi_device *adev, + int index) { acpi_handle parent_handle = NULL; struct list_head resource_list; @@ -2429,8 +2461,13 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, struct spi_device *spi; int ret; + if (!ctlr && index == -1) + return ERR_PTR(-EINVAL); + lookup.ctlr = ctlr; lookup.irq = -1; + lookup.index = index; + lookup.n = 0; INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, @@ -2443,7 +2480,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, if (!lookup.max_speed_hz && ACPI_SUCCESS(acpi_get_parent(adev->handle, &parent_handle)) && - ACPI_HANDLE(ctlr->dev.parent) == parent_handle) { + ACPI_HANDLE(lookup.ctlr->dev.parent) == parent_handle) { /* Apple does not use _CRS but nested devices for SPI slaves */ acpi_spi_parse_apple_properties(adev, &lookup); } @@ -2451,9 +2488,9 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, if (!lookup.max_speed_hz) return ERR_PTR(-ENODEV); - spi = spi_alloc_device(ctlr); + spi = spi_alloc_device(lookup.ctlr); if (!spi) { - dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n", + dev_err(&lookup.ctlr->dev, "failed to allocate SPI device for %s\n", dev_name(&adev->dev)); return ERR_PTR(-ENOMEM); } @@ -2478,7 +2515,7 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, acpi_device_enumerated(adev)) return AE_OK; - spi = acpi_spi_device_alloc(ctlr, adev); + spi = acpi_spi_device_alloc(ctlr, adev, -1); if (IS_ERR(spi)) { if (PTR_ERR(spi) == -ENOMEM) return AE_NO_MEMORY; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index d159cef12f1a..e5bbb9cbd3d7 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -762,7 +762,8 @@ extern void spi_unregister_controller(struct spi_controller *ctlr); #if IS_ENABLED(CONFIG_ACPI) extern struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, - struct acpi_device *adev); + struct acpi_device *adev, + int index); #endif /* From e612af7acef2459f1afd885f4107748995a05963 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 21 Jan 2022 17:24:26 +0000 Subject: [PATCH 06/46] spi: Add API to count spi acpi resources Some ACPI nodes may have more than one Spi Resource. To be able to handle these case, its necessary to have a way of counting these resources. Signed-off-by: Stefan Binding Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20220121172431.6876-5-sbinding@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 40 ++++++++++++++++++++++++++++++++++++++++ include/linux/spi/spi.h | 1 + 2 files changed, 41 insertions(+) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 06c0a308b38b..ec9f2ed579e3 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2324,6 +2324,46 @@ struct acpi_spi_lookup { int index; }; +static int acpi_spi_count(struct acpi_resource *ares, void *data) +{ + struct acpi_resource_spi_serialbus *sb; + int *count = data; + + if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return 1; + + sb = &ares->data.spi_serial_bus; + if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_SPI) + return 1; + + *count = *count + 1; + + return 1; +} + +/** + * acpi_spi_count_resources - Count the number of SpiSerialBus resources + * @adev: ACPI device + * + * Returns the number of SpiSerialBus resources in the ACPI-device's + * resource-list; or a negative error code. + */ +int acpi_spi_count_resources(struct acpi_device *adev) +{ + LIST_HEAD(r); + int count = 0; + int ret; + + ret = acpi_dev_get_resources(adev, &r, acpi_spi_count, &count); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&r); + + return count; +} +EXPORT_SYMBOL_GPL(acpi_spi_count_resources); + static void acpi_spi_parse_apple_properties(struct acpi_device *dev, struct acpi_spi_lookup *lookup) { diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index e5bbb9cbd3d7..394b4241d989 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -764,6 +764,7 @@ extern void spi_unregister_controller(struct spi_controller *ctlr); extern struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, struct acpi_device *adev, int index); +int acpi_spi_count_resources(struct acpi_device *adev); #endif /* From 5e63b2ea3dfbab9307e197004a3c5ee4e1442582 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 21 Jan 2022 17:24:27 +0000 Subject: [PATCH 07/46] platform/x86: i2c-multi-instantiate: Rename it for a generic serial driver name Rename I2C multi instantiate driver to serial-multi-instantiate for upcoming addition of SPI support Signed-off-by: Lucas Tanure Signed-off-by: Stefan Binding Link: https://lore.kernel.org/r/20220121172431.6876-6-sbinding@opensource.cirrus.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 4 +- drivers/acpi/scan.c | 13 +-- drivers/platform/x86/Kconfig | 10 +- drivers/platform/x86/Makefile | 2 +- ...stantiate.c => serial-multi-instantiate.c} | 91 +++++++++---------- 5 files changed, 60 insertions(+), 60 deletions(-) rename drivers/platform/x86/{i2c-multi-instantiate.c => serial-multi-instantiate.c} (51%) diff --git a/MAINTAINERS b/MAINTAINERS index ea3e6c914384..f51ff0a45c8c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -388,11 +388,11 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/acpi/arm64 -ACPI I2C MULTI INSTANTIATE DRIVER +ACPI SERIAL MULTI INSTANTIATE DRIVER M: Hans de Goede L: platform-driver-x86@vger.kernel.org S: Maintained -F: drivers/platform/x86/i2c-multi-instantiate.c +F: drivers/platform/x86/serial-multi-instantiate.c ACPI PCC(Platform Communication Channel) MAILBOX DRIVER M: Sudeep Holla diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1331756d4cfc..48db5e80c2dc 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1734,12 +1734,13 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) bool is_serial_bus_slave = false; static const struct acpi_device_id ignore_serial_bus_ids[] = { /* - * These devices have multiple I2cSerialBus resources and an i2c-client - * must be instantiated for each, each with its own i2c_device_id. - * Normally we only instantiate an i2c-client for the first resource, - * using the ACPI HID as id. These special cases are handled by the - * drivers/platform/x86/i2c-multi-instantiate.c driver, which knows - * which i2c_device_id to use for each resource. + * These devices have multiple SerialBus resources and a client + * device must be instantiated for each of them, each with + * its own device id. + * Normally we only instantiate one client device for the first + * resource, using the ACPI HID as id. These special cases are handled + * by the drivers/platform/x86/serial-multi-instantiate.c driver, which + * knows which client device id to use for each resource. */ {"BSG1160", }, {"BSG2150", }, diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 24deeeb29af2..2e656909a866 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -990,16 +990,16 @@ config TOPSTAR_LAPTOP If you have a Topstar laptop, say Y or M here. -config I2C_MULTI_INSTANTIATE - tristate "I2C multi instantiate pseudo device driver" +config SERIAL_MULTI_INSTANTIATE + tristate "Serial bus multi instantiate pseudo device driver" depends on I2C && ACPI help - Some ACPI-based systems list multiple i2c-devices in a single ACPI - firmware-node. This driver will instantiate separate i2c-clients + Some ACPI-based systems list multiple devices in a single ACPI + firmware-node. This driver will instantiate separate clients for each device in the firmware-node. To compile this driver as a module, choose M here: the module - will be called i2c-multi-instantiate. + will be called serial-multi-instantiate. config MLX_PLATFORM tristate "Mellanox Technologies platform support" diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index c12a9b044fd8..9527088bba7f 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -110,7 +110,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o # Platform drivers obj-$(CONFIG_FW_ATTR_CLASS) += firmware_attributes_class.o -obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o +obj-$(CONFIG_SERIAL_MULTI_INSTANTIATE) += serial-multi-instantiate.o obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o diff --git a/drivers/platform/x86/i2c-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c similarity index 51% rename from drivers/platform/x86/i2c-multi-instantiate.c rename to drivers/platform/x86/serial-multi-instantiate.c index 4956a1df5b90..9af5bcd466d1 100644 --- a/drivers/platform/x86/i2c-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * I2C multi-instantiate driver, pseudo driver to instantiate multiple - * i2c-clients from a single fwnode. + * Serial multi-instantiate driver, pseudo driver to instantiate multiple + * client devices from a single fwnode. * * Copyright 2018 Hans de Goede */ @@ -21,29 +21,29 @@ #define IRQ_RESOURCE_GPIO 1 #define IRQ_RESOURCE_APIC 2 -struct i2c_inst_data { +struct smi_instance { const char *type; unsigned int flags; int irq_idx; }; -struct i2c_multi_inst_data { - int num_clients; - struct i2c_client *clients[]; +struct smi { + int i2c_num; + struct i2c_client *i2c_devs[]; }; -static int i2c_multi_inst_probe(struct platform_device *pdev) +static int smi_probe(struct platform_device *pdev) { - struct i2c_multi_inst_data *multi; - const struct i2c_inst_data *inst_data; struct i2c_board_info board_info = {}; + const struct smi_instance *inst; struct device *dev = &pdev->dev; struct acpi_device *adev; + struct smi *smi; char name[32]; int i, ret; - inst_data = device_get_match_data(dev); - if (!inst_data) { + inst = device_get_match_data(dev); + if (!inst) { dev_err(dev, "Error ACPI match data is missing\n"); return -ENODEV; } @@ -55,33 +55,32 @@ static int i2c_multi_inst_probe(struct platform_device *pdev) if (ret < 0) return ret; - multi = devm_kmalloc(dev, struct_size(multi, clients, ret), GFP_KERNEL); - if (!multi) + smi = devm_kmalloc(dev, struct_size(smi, i2c_devs, ret), GFP_KERNEL); + if (!smi) return -ENOMEM; - multi->num_clients = ret; + smi->i2c_num = ret; - for (i = 0; i < multi->num_clients && inst_data[i].type; i++) { + for (i = 0; i < smi->i2c_num && inst[i].type; i++) { memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, inst_data[i].type, I2C_NAME_SIZE); - snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), - inst_data[i].type, i); + strlcpy(board_info.type, inst[i].type, I2C_NAME_SIZE); + snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst[i].type, i); board_info.dev_name = name; - switch (inst_data[i].flags & IRQ_RESOURCE_TYPE) { + switch (inst[i].flags & IRQ_RESOURCE_TYPE) { case IRQ_RESOURCE_GPIO: - ret = acpi_dev_gpio_irq_get(adev, inst_data[i].irq_idx); + ret = acpi_dev_gpio_irq_get(adev, inst[i].irq_idx); if (ret < 0) { dev_err(dev, "Error requesting irq at index %d: %d\n", - inst_data[i].irq_idx, ret); + inst[i].irq_idx, ret); goto error; } board_info.irq = ret; break; case IRQ_RESOURCE_APIC: - ret = platform_get_irq(pdev, inst_data[i].irq_idx); + ret = platform_get_irq(pdev, inst[i].irq_idx); if (ret < 0) { dev_dbg(dev, "Error requesting irq at index %d: %d\n", - inst_data[i].irq_idx, ret); + inst[i].irq_idx, ret); goto error; } board_info.irq = ret; @@ -90,48 +89,48 @@ static int i2c_multi_inst_probe(struct platform_device *pdev) board_info.irq = 0; break; } - multi->clients[i] = i2c_acpi_new_device(dev, i, &board_info); - if (IS_ERR(multi->clients[i])) { - ret = dev_err_probe(dev, PTR_ERR(multi->clients[i]), + smi->i2c_devs[i] = i2c_acpi_new_device(dev, i, &board_info); + if (IS_ERR(smi->i2c_devs[i])) { + ret = dev_err_probe(dev, PTR_ERR(smi->i2c_devs[i]), "Error creating i2c-client, idx %d\n", i); goto error; } } - if (i < multi->num_clients) { + if (i < smi->i2c_num) { dev_err(dev, "Error finding driver, idx %d\n", i); ret = -ENODEV; goto error; } - platform_set_drvdata(pdev, multi); + platform_set_drvdata(pdev, smi); return 0; error: while (--i >= 0) - i2c_unregister_device(multi->clients[i]); + i2c_unregister_device(smi->i2c_devs[i]); return ret; } -static int i2c_multi_inst_remove(struct platform_device *pdev) +static int smi_remove(struct platform_device *pdev) { - struct i2c_multi_inst_data *multi = platform_get_drvdata(pdev); + struct smi *smi = platform_get_drvdata(pdev); int i; - for (i = 0; i < multi->num_clients; i++) - i2c_unregister_device(multi->clients[i]); + for (i = 0; i < smi->i2c_num; i++) + i2c_unregister_device(smi->i2c_devs[i]); return 0; } -static const struct i2c_inst_data bsg1160_data[] = { +static const struct smi_instance bsg1160_data[] = { { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, { "bmc150_magn" }, { "bmg160" }, {} }; -static const struct i2c_inst_data bsg2150_data[] = { +static const struct smi_instance bsg2150_data[] = { { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, { "bmc150_magn" }, /* The resources describe a 3th client, but it is not really there. */ @@ -139,7 +138,7 @@ static const struct i2c_inst_data bsg2150_data[] = { {} }; -static const struct i2c_inst_data int3515_data[] = { +static const struct smi_instance int3515_data[] = { { "tps6598x", IRQ_RESOURCE_APIC, 0 }, { "tps6598x", IRQ_RESOURCE_APIC, 1 }, { "tps6598x", IRQ_RESOURCE_APIC, 2 }, @@ -148,27 +147,27 @@ static const struct i2c_inst_data int3515_data[] = { }; /* - * Note new device-ids must also be added to i2c_multi_instantiate_ids in + * Note new device-ids must also be added to ignore_serial_bus_ids in * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). */ -static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = { +static const struct acpi_device_id smi_acpi_ids[] = { { "BSG1160", (unsigned long)bsg1160_data }, { "BSG2150", (unsigned long)bsg2150_data }, { "INT3515", (unsigned long)int3515_data }, { } }; -MODULE_DEVICE_TABLE(acpi, i2c_multi_inst_acpi_ids); +MODULE_DEVICE_TABLE(acpi, smi_acpi_ids); -static struct platform_driver i2c_multi_inst_driver = { +static struct platform_driver smi_driver = { .driver = { - .name = "I2C multi instantiate pseudo device driver", - .acpi_match_table = i2c_multi_inst_acpi_ids, + .name = "Serial bus multi instantiate pseudo device driver", + .acpi_match_table = smi_acpi_ids, }, - .probe = i2c_multi_inst_probe, - .remove = i2c_multi_inst_remove, + .probe = smi_probe, + .remove = smi_remove, }; -module_platform_driver(i2c_multi_inst_driver); +module_platform_driver(smi_driver); -MODULE_DESCRIPTION("I2C multi instantiate pseudo device driver"); +MODULE_DESCRIPTION("Serial multi instantiate pseudo device driver"); MODULE_AUTHOR("Hans de Goede "); MODULE_LICENSE("GPL"); From 35a36cbb7b1ce7596fc03962f7ce261b2e908d1f Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 21 Jan 2022 17:24:28 +0000 Subject: [PATCH 08/46] platform/x86: serial-multi-instantiate: Reorganize I2C functions Reorganize I2C functions to accommodate SPI support Split the probe and factor out parts of the code that will be used in the SPI support Also switched from strlcpy() to strscpy() Signed-off-by: Lucas Tanure Signed-off-by: Stefan Binding Link: https://lore.kernel.org/r/20220121172431.6876-7-sbinding@opensource.cirrus.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- .../platform/x86/serial-multi-instantiate.c | 144 +++++++++++------- 1 file changed, 90 insertions(+), 54 deletions(-) diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 9af5bcd466d1..1eb29bec405f 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -29,96 +29,132 @@ struct smi_instance { struct smi { int i2c_num; - struct i2c_client *i2c_devs[]; + struct i2c_client **i2c_devs; }; -static int smi_probe(struct platform_device *pdev) +static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev, + const struct smi_instance *inst) { - struct i2c_board_info board_info = {}; - const struct smi_instance *inst; - struct device *dev = &pdev->dev; - struct acpi_device *adev; - struct smi *smi; - char name[32]; - int i, ret; + int ret; - inst = device_get_match_data(dev); - if (!inst) { - dev_err(dev, "Error ACPI match data is missing\n"); - return -ENODEV; + switch (inst->flags & IRQ_RESOURCE_TYPE) { + case IRQ_RESOURCE_GPIO: + ret = acpi_dev_gpio_irq_get(adev, inst->irq_idx); + break; + case IRQ_RESOURCE_APIC: + ret = platform_get_irq(pdev, inst->irq_idx); + break; + default: + return 0; } - adev = ACPI_COMPANION(dev); + if (ret < 0) + dev_err_probe(&pdev->dev, ret, "Error requesting irq at index %d: %d\n", + inst->irq_idx, ret); + + return ret; +} + +static void smi_devs_unregister(struct smi *smi) +{ + while (smi->i2c_num > 0) + i2c_unregister_device(smi->i2c_devs[--smi->i2c_num]); +} + +/** + * smi_i2c_probe - Instantiate multiple I2C devices from inst array + * @pdev: Platform device + * @adev: ACPI device + * @smi: Internal struct for Serial multi instantiate driver + * @inst_array: Array of instances to probe + * + * Returns the number of I2C devices instantiate, Zero if none is found or a negative error code. + */ +static int smi_i2c_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi, + const struct smi_instance *inst_array) +{ + struct i2c_board_info board_info = {}; + struct device *dev = &pdev->dev; + char name[32]; + int i, ret, count; - /* Count number of clients to instantiate */ ret = i2c_acpi_client_count(adev); if (ret < 0) return ret; + else if (!ret) + return -ENODEV; - smi = devm_kmalloc(dev, struct_size(smi, i2c_devs, ret), GFP_KERNEL); - if (!smi) + count = ret; + + smi->i2c_devs = devm_kcalloc(dev, count, sizeof(*smi->i2c_devs), GFP_KERNEL); + if (!smi->i2c_devs) return -ENOMEM; - smi->i2c_num = ret; - - for (i = 0; i < smi->i2c_num && inst[i].type; i++) { + for (i = 0; i < count && inst_array[i].type; i++) { memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, inst[i].type, I2C_NAME_SIZE); - snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst[i].type, i); + strscpy(board_info.type, inst_array[i].type, I2C_NAME_SIZE); + snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst_array[i].type, i); board_info.dev_name = name; - switch (inst[i].flags & IRQ_RESOURCE_TYPE) { - case IRQ_RESOURCE_GPIO: - ret = acpi_dev_gpio_irq_get(adev, inst[i].irq_idx); - if (ret < 0) { - dev_err(dev, "Error requesting irq at index %d: %d\n", - inst[i].irq_idx, ret); - goto error; - } - board_info.irq = ret; - break; - case IRQ_RESOURCE_APIC: - ret = platform_get_irq(pdev, inst[i].irq_idx); - if (ret < 0) { - dev_dbg(dev, "Error requesting irq at index %d: %d\n", - inst[i].irq_idx, ret); - goto error; - } - board_info.irq = ret; - break; - default: - board_info.irq = 0; - break; - } + + ret = smi_get_irq(pdev, adev, &inst_array[i]); + if (ret < 0) + goto error; + board_info.irq = ret; + smi->i2c_devs[i] = i2c_acpi_new_device(dev, i, &board_info); if (IS_ERR(smi->i2c_devs[i])) { ret = dev_err_probe(dev, PTR_ERR(smi->i2c_devs[i]), "Error creating i2c-client, idx %d\n", i); goto error; } + smi->i2c_num++; } - if (i < smi->i2c_num) { - dev_err(dev, "Error finding driver, idx %d\n", i); + if (smi->i2c_num < count) { + dev_dbg(dev, "Error finding driver, idx %d\n", i); ret = -ENODEV; goto error; } - platform_set_drvdata(pdev, smi); - return 0; + dev_info(dev, "Instantiated %d I2C devices.\n", smi->i2c_num); + return 0; error: - while (--i >= 0) - i2c_unregister_device(smi->i2c_devs[i]); + smi_devs_unregister(smi); return ret; } +static int smi_probe(struct platform_device *pdev) +{ + const struct smi_instance *inst_array; + struct device *dev = &pdev->dev; + struct acpi_device *adev; + struct smi *smi; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; + + inst_array = device_get_match_data(dev); + if (!inst_array) { + dev_dbg(dev, "Error ACPI match data is missing\n"); + return -ENODEV; + } + + smi = devm_kzalloc(dev, sizeof(*smi), GFP_KERNEL); + if (!smi) + return -ENOMEM; + + platform_set_drvdata(pdev, smi); + + return smi_i2c_probe(pdev, adev, smi, inst_array); +} + static int smi_remove(struct platform_device *pdev) { struct smi *smi = platform_get_drvdata(pdev); - int i; - for (i = 0; i < smi->i2c_num; i++) - i2c_unregister_device(smi->i2c_devs[i]); + smi_devs_unregister(smi); return 0; } From 68f201f9061c000d7a4a9f359f021b1cd535d62b Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 21 Jan 2022 17:24:29 +0000 Subject: [PATCH 09/46] platform/x86: serial-multi-instantiate: Add SPI support Add support for spi bus in serial-multi-instantiate driver Some peripherals can have either a I2C or a SPI connection to the host (but not both) but use the same HID for both types. So it is not possible to use the HID to determine whether it is I2C or SPI. The driver must check the node to see if it contains I2cSerialBus or SpiSerialBus entries. For backwards-compatibility with the existing nodes I2C is checked first and if such entries are found ONLY I2C devices are created. Since some existing nodes that were already handled by this driver could also contain unrelated SpiSerialBus nodes that were previously ignored, and this preserves that behavior. If there is ever a need to handle a node where both I2C and SPI devices must be instantiated this can be added in future. Signed-off-by: Stefan Binding Link: https://lore.kernel.org/r/20220121172431.6876-8-sbinding@opensource.cirrus.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/Kconfig | 2 +- .../platform/x86/serial-multi-instantiate.c | 173 +++++++++++++++--- 2 files changed, 150 insertions(+), 25 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 2e656909a866..8d1eec208854 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -992,7 +992,7 @@ config TOPSTAR_LAPTOP config SERIAL_MULTI_INSTANTIATE tristate "Serial bus multi instantiate pseudo device driver" - depends on I2C && ACPI + depends on I2C && SPI && ACPI help Some ACPI-based systems list multiple devices in a single ACPI firmware-node. This driver will instantiate separate clients diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 1eb29bec405f..e2fd73905312 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define IRQ_RESOURCE_TYPE GENMASK(1, 0) @@ -21,15 +22,28 @@ #define IRQ_RESOURCE_GPIO 1 #define IRQ_RESOURCE_APIC 2 +enum smi_bus_type { + SMI_I2C, + SMI_SPI, + SMI_AUTO_DETECT, +}; + struct smi_instance { const char *type; unsigned int flags; int irq_idx; }; +struct smi_node { + enum smi_bus_type bus_type; + struct smi_instance instances[]; +}; + struct smi { int i2c_num; + int spi_num; struct i2c_client **i2c_devs; + struct spi_device **spi_devs; }; static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev, @@ -59,6 +73,94 @@ static void smi_devs_unregister(struct smi *smi) { while (smi->i2c_num > 0) i2c_unregister_device(smi->i2c_devs[--smi->i2c_num]); + + while (smi->spi_num > 0) + spi_unregister_device(smi->spi_devs[--smi->spi_num]); +} + +/** + * smi_spi_probe - Instantiate multiple SPI devices from inst array + * @pdev: Platform device + * @adev: ACPI device + * @smi: Internal struct for Serial multi instantiate driver + * @inst_array: Array of instances to probe + * + * Returns the number of SPI devices instantiate, Zero if none is found or a negative error code. + */ +static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi, + const struct smi_instance *inst_array) +{ + struct device *dev = &pdev->dev; + struct spi_controller *ctlr; + struct spi_device *spi_dev; + char name[50]; + int i, ret, count; + + ret = acpi_spi_count_resources(adev); + if (ret < 0) + return ret; + else if (!ret) + return -ENODEV; + + count = ret; + + smi->spi_devs = devm_kcalloc(dev, count, sizeof(*smi->spi_devs), GFP_KERNEL); + if (!smi->spi_devs) + return -ENOMEM; + + for (i = 0; i < count && inst_array[i].type; i++) { + + spi_dev = acpi_spi_device_alloc(NULL, adev, i); + if (IS_ERR(spi_dev)) { + ret = PTR_ERR(spi_dev); + dev_err_probe(dev, ret, "failed to allocate SPI device %s from ACPI: %d\n", + dev_name(&adev->dev), ret); + goto error; + } + + ctlr = spi_dev->controller; + + strscpy(spi_dev->modalias, inst_array[i].type, sizeof(spi_dev->modalias)); + + ret = smi_get_irq(pdev, adev, &inst_array[i]); + if (ret < 0) { + spi_dev_put(spi_dev); + goto error; + } + spi_dev->irq = ret; + + snprintf(name, sizeof(name), "%s-%s-%s.%d", dev_name(&ctlr->dev), dev_name(dev), + inst_array[i].type, i); + spi_dev->dev.init_name = name; + + ret = spi_add_device(spi_dev); + if (ret) { + dev_err_probe(&ctlr->dev, ret, + "failed to add SPI device %s from ACPI: %d\n", + dev_name(&adev->dev), ret); + spi_dev_put(spi_dev); + goto error; + } + + dev_dbg(dev, "SPI device %s using chip select %u", name, spi_dev->chip_select); + + smi->spi_devs[i] = spi_dev; + smi->spi_num++; + } + + if (smi->spi_num < count) { + dev_dbg(dev, "Error finding driver, idx %d\n", i); + ret = -ENODEV; + goto error; + } + + dev_info(dev, "Instantiated %d SPI devices.\n", smi->spi_num); + + return 0; +error: + smi_devs_unregister(smi); + + return ret; } /** @@ -126,8 +228,8 @@ error: static int smi_probe(struct platform_device *pdev) { - const struct smi_instance *inst_array; struct device *dev = &pdev->dev; + const struct smi_node *node; struct acpi_device *adev; struct smi *smi; @@ -135,8 +237,8 @@ static int smi_probe(struct platform_device *pdev) if (!adev) return -ENODEV; - inst_array = device_get_match_data(dev); - if (!inst_array) { + node = device_get_match_data(dev); + if (!node) { dev_dbg(dev, "Error ACPI match data is missing\n"); return -ENODEV; } @@ -147,7 +249,21 @@ static int smi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, smi); - return smi_i2c_probe(pdev, adev, smi, inst_array); + switch (node->bus_type) { + case SMI_I2C: + return smi_i2c_probe(pdev, adev, smi, node->instances); + case SMI_SPI: + return smi_spi_probe(pdev, adev, smi, node->instances); + case SMI_AUTO_DETECT: + if (i2c_acpi_client_count(adev) > 0) + return smi_i2c_probe(pdev, adev, smi, node->instances); + else + return smi_spi_probe(pdev, adev, smi, node->instances); + default: + return -EINVAL; + } + + return 0; /* never reached */ } static int smi_remove(struct platform_device *pdev) @@ -159,27 +275,36 @@ static int smi_remove(struct platform_device *pdev) return 0; } -static const struct smi_instance bsg1160_data[] = { - { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, - { "bmc150_magn" }, - { "bmg160" }, - {} +static const struct smi_node bsg1160_data = { + .instances = { + { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, + { "bmc150_magn" }, + { "bmg160" }, + {} + }, + .bus_type = SMI_I2C, }; -static const struct smi_instance bsg2150_data[] = { - { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, - { "bmc150_magn" }, - /* The resources describe a 3th client, but it is not really there. */ - { "bsg2150_dummy_dev" }, - {} +static const struct smi_node bsg2150_data = { + .instances = { + { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, + { "bmc150_magn" }, + /* The resources describe a 3th client, but it is not really there. */ + { "bsg2150_dummy_dev" }, + {} + }, + .bus_type = SMI_I2C, }; -static const struct smi_instance int3515_data[] = { - { "tps6598x", IRQ_RESOURCE_APIC, 0 }, - { "tps6598x", IRQ_RESOURCE_APIC, 1 }, - { "tps6598x", IRQ_RESOURCE_APIC, 2 }, - { "tps6598x", IRQ_RESOURCE_APIC, 3 }, - {} +static const struct smi_node int3515_data = { + .instances = { + { "tps6598x", IRQ_RESOURCE_APIC, 0 }, + { "tps6598x", IRQ_RESOURCE_APIC, 1 }, + { "tps6598x", IRQ_RESOURCE_APIC, 2 }, + { "tps6598x", IRQ_RESOURCE_APIC, 3 }, + {} + }, + .bus_type = SMI_I2C, }; /* @@ -187,9 +312,9 @@ static const struct smi_instance int3515_data[] = { * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). */ static const struct acpi_device_id smi_acpi_ids[] = { - { "BSG1160", (unsigned long)bsg1160_data }, - { "BSG2150", (unsigned long)bsg2150_data }, - { "INT3515", (unsigned long)int3515_data }, + { "BSG1160", (unsigned long)&bsg1160_data }, + { "BSG2150", (unsigned long)&bsg2150_data }, + { "INT3515", (unsigned long)&int3515_data }, { } }; MODULE_DEVICE_TABLE(acpi, smi_acpi_ids); From 07bcab93946cd9980f8eafe89afe991cd918acb0 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 21 Jan 2022 17:24:30 +0000 Subject: [PATCH 10/46] ALSA: hda/realtek: Add support for HP Laptops Add support for two and four CS35L41 using the component binding method Signed-off-by: Lucas Tanure Signed-off-by: Stefan Binding Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20220121172431.6876-9-sbinding@opensource.cirrus.com Signed-off-by: Hans de Goede --- sound/pci/hda/patch_realtek.c | 43 ++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 668274e52674..db0fce42887c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6611,6 +6611,16 @@ static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2); } +static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action) +{ + cs35l41_generic_fixup(codec, action, "spi0", "CSC3551", 2); +} + +static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action) +{ + cs35l41_generic_fixup(codec, action, "spi0", "CSC3551", 4); +} + static void alc287_legion_16achg6_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, struct snd_pcm_substream *sub, int action) { @@ -6948,6 +6958,9 @@ enum { ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, ALC287_FIXUP_LEGION_16ACHG6, ALC287_FIXUP_CS35L41_I2C_2, + ALC245_FIXUP_CS35L41_SPI_2, + ALC245_FIXUP_CS35L41_SPI_4, + ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED, ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED, }; @@ -8699,6 +8712,20 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_i2c_two, }, + [ALC245_FIXUP_CS35L41_SPI_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_spi_two, + }, + [ALC245_FIXUP_CS35L41_SPI_4] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_spi_four, + }, + [ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_gpio_led, + .chained = true, + .chain_id = ALC245_FIXUP_CS35L41_SPI_4, + }, [ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -8926,7 +8953,21 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89c3, "HP", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x896e, "HP EliteBook x360 830 G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8971, "HP EliteBook 830 G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8972, "HP EliteBook 840 G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8973, "HP EliteBook 860 G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8974, "HP EliteBook 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4), + SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8991, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8992, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8994, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8995, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x89c3, "Zbook Studio G9", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), From d9c01c530cc5e3b6d5bdfeae12c3d0f33fae7498 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Fri, 21 Jan 2022 17:24:31 +0000 Subject: [PATCH 11/46] ACPI / scan: Create platform device for CS35L41 The ACPI device with CSC3551 or CLSA0100 are sound cards with multiple instances of CS35L41 connected by I2C or SPI to the main CPU. We add an ID to the ignore_serial_bus_ids list to enumerate all I2C or SPI devices correctly. The same IDs are also added into serial-multi-instantiate so that the driver can correctly enumerate the ACPI. Signed-off-by: Lucas Tanure Signed-off-by: Stefan Binding Link: https://lore.kernel.org/r/20220121172431.6876-10-sbinding@opensource.cirrus.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/acpi/scan.c | 3 +++ drivers/platform/x86/serial-multi-instantiate.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 48db5e80c2dc..4463c2eda61e 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1744,8 +1744,11 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) */ {"BSG1160", }, {"BSG2150", }, + {"CSC3551", }, {"INT33FE", }, {"INT3515", }, + /* Non-conforming _HID for Cirrus Logic already released */ + {"CLSA0100", }, /* * HIDs of device with an UartSerialBusV2 resource for which userspace * expects a regular tty cdev to be created (instead of the in kernel diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index e2fd73905312..1e8063b7c169 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -307,6 +307,17 @@ static const struct smi_node int3515_data = { .bus_type = SMI_I2C, }; +static const struct smi_node cs35l41_hda = { + .instances = { + { "cs35l41-hda", IRQ_RESOURCE_GPIO, 0 }, + { "cs35l41-hda", IRQ_RESOURCE_GPIO, 0 }, + { "cs35l41-hda", IRQ_RESOURCE_GPIO, 0 }, + { "cs35l41-hda", IRQ_RESOURCE_GPIO, 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(). @@ -315,6 +326,9 @@ static const struct acpi_device_id smi_acpi_ids[] = { { "BSG1160", (unsigned long)&bsg1160_data }, { "BSG2150", (unsigned long)&bsg2150_data }, { "INT3515", (unsigned long)&int3515_data }, + { "CSC3551", (unsigned long)&cs35l41_hda }, + /* Non-conforming _HID for Cirrus Logic already released */ + { "CLSA0100", (unsigned long)&cs35l41_hda }, { } }; MODULE_DEVICE_TABLE(acpi, smi_acpi_ids); From b1446bda56456887f8d439938163164fe916bc0d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Feb 2022 15:09:01 +0000 Subject: [PATCH 12/46] kselftest: alsa: Check for event generation when we write to controls Add some coverage of event generation to mixer-test. Rather than doing a separate set of writes designed to trigger events we add a step to the existing write_and_verify() which checks to see if the value we read back from non-volatile controls matches the value before writing and that an event is or isn't generated as appropriate. The "tests" for events then simply check that no spurious or missing events were detected. This avoids needing further logic to generate appropriate values for each control type and maximises coverage. When checking for events we use a timeout of 0. This relies on the kernel generating any event prior to returning to userspace when setting a control. That is currently the case and it is difficult to see it changing, if it does the test will need to be updated. Using a delay of 0 means that we don't slow things down unduly when checking for no event or when events fail to be generated. We don't check behaviour for volatile controls since we can't tell what the behaviour is supposed to be for any given control. Signed-off-by: Mark Brown Reviewed-by: Shuah Khan Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20220202150902.19563-1-broonie@kernel.org Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 154 +++++++++++++++++++++- 1 file changed, 151 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index 0e88f4f3d802..6edb7dca32af 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -3,7 +3,7 @@ // kselftest for the ALSA mixer API // // Original author: Mark Brown -// Copyright (c) 2021 Arm Limited +// Copyright (c) 2021-2 Arm Limited // This test will iterate over all cards detected in the system, exercising // every mixer control it can find. This may conflict with other system @@ -27,11 +27,12 @@ #include "../kselftest.h" -#define TESTS_PER_CONTROL 4 +#define TESTS_PER_CONTROL 6 struct card_data { snd_ctl_t *handle; int card; + struct pollfd pollfd; int num_ctls; snd_ctl_elem_list_t *ctls; struct card_data *next; @@ -43,6 +44,8 @@ struct ctl_data { snd_ctl_elem_info_t *info; snd_ctl_elem_value_t *def_val; int elem; + int event_missing; + int event_spurious; struct card_data *card; struct ctl_data *next; }; @@ -149,6 +152,7 @@ void find_controls(void) if (!ctl_data) ksft_exit_fail_msg("Out of memory\n"); + memset(ctl_data, 0, sizeof(*ctl_data)); ctl_data->card = card_data; ctl_data->elem = ctl; ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls, @@ -184,6 +188,26 @@ void find_controls(void) ctl_list = ctl_data; } + /* Set up for events */ + err = snd_ctl_subscribe_events(card_data->handle, true); + if (err < 0) { + ksft_exit_fail_msg("snd_ctl_subscribe_events() failed for card %d: %d\n", + card, err); + } + + err = snd_ctl_poll_descriptors_count(card_data->handle); + if (err != 1) { + ksft_exit_fail_msg("Unexpected desciptor count %d for card %d\n", + err, card); + } + + err = snd_ctl_poll_descriptors(card_data->handle, + &card_data->pollfd, 1); + if (err != 1) { + ksft_exit_fail_msg("snd_ctl_poll_descriptors() failed for %d\n", + card, err); + } + next_card: if (snd_card_next(&card) < 0) { ksft_print_msg("snd_card_next"); @@ -194,6 +218,79 @@ void find_controls(void) snd_config_delete(config); } +/* + * Block for up to timeout ms for an event, returns a negative value + * on error, 0 for no event and 1 for an event. + */ +int wait_for_event(struct ctl_data *ctl, int timeout) +{ + unsigned short revents; + snd_ctl_event_t *event; + int count, err; + unsigned int mask = 0; + unsigned int ev_id; + + snd_ctl_event_alloca(&event); + + do { + err = poll(&(ctl->card->pollfd), 1, timeout); + if (err < 0) { + ksft_print_msg("poll() failed for %s: %s (%d)\n", + ctl->name, strerror(errno), errno); + return -1; + } + /* Timeout */ + if (err == 0) + return 0; + + err = snd_ctl_poll_descriptors_revents(ctl->card->handle, + &(ctl->card->pollfd), + 1, &revents); + if (err < 0) { + ksft_print_msg("snd_ctl_poll_desciptors_revents() failed for %s: %d\n", + ctl->name, err); + return err; + } + if (revents & POLLERR) { + ksft_print_msg("snd_ctl_poll_desciptors_revents() reported POLLERR for %s\n", + ctl->name); + return -1; + } + /* No read events */ + if (!(revents & POLLIN)) { + ksft_print_msg("No POLLIN\n"); + continue; + } + + err = snd_ctl_read(ctl->card->handle, event); + if (err < 0) { + ksft_print_msg("snd_ctl_read() failed for %s: %d\n", + ctl->name, err); + return err; + } + + if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM) + continue; + + /* The ID returned from the event is 1 less than numid */ + mask = snd_ctl_event_elem_get_mask(event); + ev_id = snd_ctl_event_elem_get_numid(event); + if (ev_id != snd_ctl_elem_info_get_numid(ctl->info)) { + ksft_print_msg("Event for unexpected ctl %s\n", + snd_ctl_event_elem_get_name(event)); + continue; + } + + if ((mask & SND_CTL_EVENT_MASK_REMOVE) == SND_CTL_EVENT_MASK_REMOVE) { + ksft_print_msg("Removal event for %s\n", + ctl->name); + return -1; + } + } while ((mask & SND_CTL_EVENT_MASK_VALUE) != SND_CTL_EVENT_MASK_VALUE); + + return 1; +} + bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val, int index) { @@ -428,7 +525,8 @@ int write_and_verify(struct ctl_data *ctl, { int err, i; bool error_expected, mismatch_shown; - snd_ctl_elem_value_t *read_val, *w_val; + snd_ctl_elem_value_t *initial_val, *read_val, *w_val; + snd_ctl_elem_value_alloca(&initial_val); snd_ctl_elem_value_alloca(&read_val); snd_ctl_elem_value_alloca(&w_val); @@ -446,6 +544,18 @@ int write_and_verify(struct ctl_data *ctl, snd_ctl_elem_value_copy(expected_val, write_val); } + /* Store the value before we write */ + if (snd_ctl_elem_info_is_readable(ctl->info)) { + snd_ctl_elem_value_set_id(initial_val, ctl->id); + + err = snd_ctl_elem_read(ctl->card->handle, initial_val); + if (err < 0) { + ksft_print_msg("snd_ctl_elem_read() failed: %s\n", + snd_strerror(err)); + return err; + } + } + /* * Do the write, if we have an expected value ignore the error * and carry on to validate the expected value. @@ -470,6 +580,30 @@ int write_and_verify(struct ctl_data *ctl, return err; } + /* + * Check for an event if the value changed, or confirm that + * there was none if it didn't. We rely on the kernel + * generating the notification before it returns from the + * write, this is currently true, should that ever change this + * will most likely break and need updating. + */ + if (!snd_ctl_elem_info_is_volatile(ctl->info)) { + err = wait_for_event(ctl, 0); + if (snd_ctl_elem_value_compare(initial_val, read_val)) { + if (err < 1) { + ksft_print_msg("No event generated for %s\n", + ctl->name); + ctl->event_missing++; + } + } else { + if (err != 0) { + ksft_print_msg("Spurious event generated for %s\n", + ctl->name); + ctl->event_spurious++; + } + } + } + /* * Use the libray to compare values, if there's a mismatch * carry on and try to provide a more useful diagnostic than @@ -898,6 +1032,18 @@ void test_ctl_write_invalid(struct ctl_data *ctl) ctl->card->card, ctl->elem); } +void test_ctl_event_missing(struct ctl_data *ctl) +{ + ksft_test_result(!ctl->event_missing, "event_missing.%d.%d\n", + ctl->card->card, ctl->elem); +} + +void test_ctl_event_spurious(struct ctl_data *ctl) +{ + ksft_test_result(!ctl->event_spurious, "event_spurious.%d.%d\n", + ctl->card->card, ctl->elem); +} + int main(void) { struct ctl_data *ctl; @@ -917,6 +1063,8 @@ int main(void) test_ctl_write_default(ctl); test_ctl_write_valid(ctl); test_ctl_write_invalid(ctl); + test_ctl_event_missing(ctl); + test_ctl_event_spurious(ctl); } ksft_exit_pass(); From 9d73d1928eb861cb90ddad08724c5ceb83c9a13b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Feb 2022 15:09:02 +0000 Subject: [PATCH 13/46] kselftest: alsa: Declare most functions static This program has only one file so most functions can be static. Signed-off-by: Mark Brown Reviewed-by: Shuah Khan Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20220202150902.19563-2-broonie@kernel.org Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 58 ++++++++++++----------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index 6edb7dca32af..d0b788b8d287 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -71,7 +71,8 @@ struct ctl_data *ctl_list = NULL; #endif #ifndef LIB_HAS_LOAD_STRING -int snd_config_load_string(snd_config_t **config, const char *s, size_t size) +static int snd_config_load_string(snd_config_t **config, const char *s, + size_t size) { snd_input_t *input; snd_config_t *dst; @@ -99,7 +100,7 @@ int snd_config_load_string(snd_config_t **config, const char *s, size_t size) } #endif -void find_controls(void) +static void find_controls(void) { char name[32]; int card, ctl, err; @@ -222,7 +223,7 @@ void find_controls(void) * Block for up to timeout ms for an event, returns a negative value * on error, 0 for no event and 1 for an event. */ -int wait_for_event(struct ctl_data *ctl, int timeout) +static int wait_for_event(struct ctl_data *ctl, int timeout) { unsigned short revents; snd_ctl_event_t *event; @@ -291,8 +292,9 @@ int wait_for_event(struct ctl_data *ctl, int timeout) return 1; } -bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val, - int index) +static bool ctl_value_index_valid(struct ctl_data *ctl, + snd_ctl_elem_value_t *val, + int index) { long int_val; long long int64_val; @@ -403,7 +405,7 @@ bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val, * Check that the provided value meets the constraints for the * provided control. */ -bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val) +static bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val) { int i; bool valid = true; @@ -419,7 +421,7 @@ bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val) * Check that we can read the default value and it is valid. Write * tests use the read value to restore the default. */ -void test_ctl_get_value(struct ctl_data *ctl) +static void test_ctl_get_value(struct ctl_data *ctl) { int err; @@ -454,9 +456,9 @@ out: ctl->card->card, ctl->elem); } -bool show_mismatch(struct ctl_data *ctl, int index, - snd_ctl_elem_value_t *read_val, - snd_ctl_elem_value_t *expected_val) +static bool show_mismatch(struct ctl_data *ctl, int index, + snd_ctl_elem_value_t *read_val, + snd_ctl_elem_value_t *expected_val) { long long expected_int, read_int; @@ -519,9 +521,9 @@ bool show_mismatch(struct ctl_data *ctl, int index, * the write to fail, for verifying that invalid writes don't corrupt * anything. */ -int write_and_verify(struct ctl_data *ctl, - snd_ctl_elem_value_t *write_val, - snd_ctl_elem_value_t *expected_val) +static int write_and_verify(struct ctl_data *ctl, + snd_ctl_elem_value_t *write_val, + snd_ctl_elem_value_t *expected_val) { int err, i; bool error_expected, mismatch_shown; @@ -628,7 +630,7 @@ int write_and_verify(struct ctl_data *ctl, * Make sure we can write the default value back to the control, this * should validate that at least some write works. */ -void test_ctl_write_default(struct ctl_data *ctl) +static void test_ctl_write_default(struct ctl_data *ctl) { int err; @@ -661,7 +663,7 @@ void test_ctl_write_default(struct ctl_data *ctl) ctl->card->card, ctl->elem); } -bool test_ctl_write_valid_boolean(struct ctl_data *ctl) +static bool test_ctl_write_valid_boolean(struct ctl_data *ctl) { int err, i, j; bool fail = false; @@ -682,7 +684,7 @@ bool test_ctl_write_valid_boolean(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_valid_integer(struct ctl_data *ctl) +static bool test_ctl_write_valid_integer(struct ctl_data *ctl) { int err; int i; @@ -712,7 +714,7 @@ bool test_ctl_write_valid_integer(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_valid_integer64(struct ctl_data *ctl) +static bool test_ctl_write_valid_integer64(struct ctl_data *ctl) { int err, i; long long j, step; @@ -740,7 +742,7 @@ bool test_ctl_write_valid_integer64(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) +static bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) { int err, i, j; bool fail = false; @@ -761,7 +763,7 @@ bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) return !fail; } -void test_ctl_write_valid(struct ctl_data *ctl) +static void test_ctl_write_valid(struct ctl_data *ctl) { bool pass; int err; @@ -814,8 +816,8 @@ void test_ctl_write_valid(struct ctl_data *ctl) ctl->card->card, ctl->elem); } -bool test_ctl_write_invalid_value(struct ctl_data *ctl, - snd_ctl_elem_value_t *val) +static bool test_ctl_write_invalid_value(struct ctl_data *ctl, + snd_ctl_elem_value_t *val) { int err; long val_read; @@ -836,7 +838,7 @@ bool test_ctl_write_invalid_value(struct ctl_data *ctl, return !ctl_value_valid(ctl, val); } -bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) +static bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) { int err, i; long val_read; @@ -855,7 +857,7 @@ bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_invalid_integer(struct ctl_data *ctl) +static bool test_ctl_write_invalid_integer(struct ctl_data *ctl) { int i; bool fail = false; @@ -901,7 +903,7 @@ bool test_ctl_write_invalid_integer(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) +static bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) { int i; bool fail = false; @@ -947,7 +949,7 @@ bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) return !fail; } -bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) +static bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) { int err, i; unsigned int val_read; @@ -979,7 +981,7 @@ bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) } -void test_ctl_write_invalid(struct ctl_data *ctl) +static void test_ctl_write_invalid(struct ctl_data *ctl) { bool pass; int err; @@ -1032,13 +1034,13 @@ void test_ctl_write_invalid(struct ctl_data *ctl) ctl->card->card, ctl->elem); } -void test_ctl_event_missing(struct ctl_data *ctl) +static void test_ctl_event_missing(struct ctl_data *ctl) { ksft_test_result(!ctl->event_missing, "event_missing.%d.%d\n", ctl->card->card, ctl->elem); } -void test_ctl_event_spurious(struct ctl_data *ctl) +static void test_ctl_event_spurious(struct ctl_data *ctl) { ksft_test_result(!ctl->event_spurious, "event_spurious.%d.%d\n", ctl->card->card, ctl->elem); From 3db3d859441b93cefe715ac5bd90d02873fe65d3 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 7 Feb 2022 14:06:17 +0000 Subject: [PATCH 14/46] ALSA: usb-audio: remove redundant assignment to variable c The variable c is being initialized in an outer for-loop and also re-initialized inside an inner for-loop. The first initialization is redundant and can be removed. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20220207140617.341172-1-colin.i.king@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_s1810c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c index 0255089c9efb..fac4bbc6b275 100644 --- a/sound/usb/mixer_s1810c.c +++ b/sound/usb/mixer_s1810c.c @@ -221,7 +221,7 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip) e = 0xbc; for (n = 0; n < 2; n++) { off = n * 18; - for (b = off, c = 0; b < 18 + off; b++) { + for (b = off; b < 18 + off; b++) { /* This channel to all outputs ? */ for (c = 0; c <= 8; c++) { snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e); From 8f85b4da579e006547c8cf3660220c54b99451da Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 7 Feb 2022 09:22:35 +0000 Subject: [PATCH 15/46] kselftest: alsa: fix spelling mistake "desciptor" -> "descriptor" There are some spelling mistakes in some ksft messages. Fix them. Signed-off-by: Colin Ian King Reviewed-by: Shuah Khan Link: https://lore.kernel.org/r/20220207092235.240284-1-colin.i.king@gmail.com Signed-off-by: Takashi Iwai --- tools/testing/selftests/alsa/mixer-test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index d0b788b8d287..eb2213540fe3 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -198,7 +198,7 @@ static void find_controls(void) err = snd_ctl_poll_descriptors_count(card_data->handle); if (err != 1) { - ksft_exit_fail_msg("Unexpected desciptor count %d for card %d\n", + ksft_exit_fail_msg("Unexpected descriptor count %d for card %d\n", err, card); } @@ -248,12 +248,12 @@ static int wait_for_event(struct ctl_data *ctl, int timeout) &(ctl->card->pollfd), 1, &revents); if (err < 0) { - ksft_print_msg("snd_ctl_poll_desciptors_revents() failed for %s: %d\n", + ksft_print_msg("snd_ctl_poll_descriptors_revents() failed for %s: %d\n", ctl->name, err); return err; } if (revents & POLLERR) { - ksft_print_msg("snd_ctl_poll_desciptors_revents() reported POLLERR for %s\n", + ksft_print_msg("snd_ctl_poll_descriptors_revents() reported POLLERR for %s\n", ctl->name); return -1; } From 69458e2c27800da7697c87ed908b65323ef3f3bd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Feb 2022 09:19:12 +0100 Subject: [PATCH 16/46] ALSA: hda: Fix driver index handling at re-binding HD-audio driver handles the multiple instances and keeps the static index that is incremented at each probe. This becomes a problem when user tries to re-bind the device via sysfs multiple times; as the device index isn't cleared unlike rmmod case, it points to the next element at re-binding, and eventually later you can't probe any more when it reaches to SNDRV_CARDS_MAX (usually 32). This patch is an attempt to improve the handling at rebinding. Instead of a static device index, now we keep a bitmap and assigns to the first zero bit position. At the driver remove, in return, the bitmap slot is cleared again, so that it'll be available for the next probe. Reported-by: Alexander Sergeyev Link: https://lore.kernel.org/r/20220209081912.20687-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4b0338c4c543..a2922233e85f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2064,14 +2064,16 @@ static const struct hda_controller_ops pci_hda_ops = { .position_check = azx_position_check, }; +static DECLARE_BITMAP(probed_devs, SNDRV_CARDS); + static int azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { - static int dev; struct snd_card *card; struct hda_intel *hda; struct azx *chip; bool schedule_probe; + int dev; int err; if (pci_match_id(driver_denylist, pci)) { @@ -2079,10 +2081,11 @@ static int azx_probe(struct pci_dev *pci, return -ENODEV; } + dev = find_first_zero_bit(probed_devs, SNDRV_CARDS); if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { - dev++; + set_bit(dev, probed_devs); return -ENOENT; } @@ -2149,7 +2152,7 @@ static int azx_probe(struct pci_dev *pci, if (schedule_probe) schedule_delayed_work(&hda->probe_work, 0); - dev++; + set_bit(dev, probed_devs); if (chip->disabled) complete_all(&hda->probe_wait); return 0; @@ -2372,6 +2375,7 @@ static void azx_remove(struct pci_dev *pci) cancel_delayed_work_sync(&hda->probe_work); device_lock(&pci->dev); + clear_bit(chip->dev_index, probed_devs); pci_set_drvdata(pci, NULL); snd_card_free(card); } From fdb1e56932a3b5ccba48b34a6f8ba843a2903445 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Feb 2022 13:42:27 +0100 Subject: [PATCH 17/46] ALSA: ca0106: Rename register macro names ca0106 driver code uses too generic names for its register definitions such as PTR, DATA, IPR, etc, which may eventually conflict with other code. This patch renames (some of) those register definitions with CA0106_ prefix to avoid the conflicts. Link: https://lore.kernel.org/r/20220210124227.11272-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/ca0106/ca0106.h | 18 ++++----- sound/pci/ca0106/ca0106_main.c | 70 ++++++++++++++++----------------- sound/pci/ca0106/ca0106_mixer.c | 16 ++++---- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h index f246e6094289..991b1c5d41d5 100644 --- a/sound/pci/ca0106/ca0106.h +++ b/sound/pci/ca0106/ca0106.h @@ -59,15 +59,15 @@ /* PCI function 0 registers, address = + PCIBASE0 */ /************************************************************************************************/ -#define PTR 0x00 /* Indexed register set pointer register */ +#define CA0106_PTR 0x00 /* Indexed register set pointer register */ /* NOTE: The CHANNELNUM and ADDRESS words can */ /* be modified independently of each other. */ /* CNL[1:0], ADDR[27:16] */ -#define DATA 0x04 /* Indexed register set data register */ +#define CA0106_DATA 0x04 /* Indexed register set data register */ /* DATA[31:0] */ -#define IPR 0x08 /* Global interrupt pending register */ +#define CA0106_IPR 0x08 /* Global interrupt pending register */ /* Clear pending interrupts by writing a 1 to */ /* the relevant bits and zero to the other bits */ #define IPR_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */ @@ -88,7 +88,7 @@ #define IPR_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */ #define IPR_PCI 0x00000001 /* PCI Bus error */ -#define INTE 0x0c /* Interrupt enable register */ +#define CA0106_INTE 0x0c /* Interrupt enable register */ #define INTE_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */ #define INTE_MIDI_TX_B 0x00010000 /* MIDI UART-B Transmit buffer empty */ @@ -108,8 +108,8 @@ #define INTE_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */ #define INTE_PCI 0x00000001 /* PCI Bus error */ -#define UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */ -#define HCFG 0x14 /* Hardware config register */ +#define CA0106_UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */ +#define CA0106_HCFG 0x14 /* Hardware config register */ /* 0x1000 causes AC3 to fails. It adds a dither bit. */ #define HCFG_STAC 0x10000000 /* Special mode for STAC9460 Codec. */ @@ -133,7 +133,7 @@ #define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ /* Should be set to 1 when the EMU10K1 is */ /* completely initialized. */ -#define GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */ +#define CA0106_GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */ /* Here pins 0,1,2,3,4,,6 are output. 5,7 are input */ /* For the Audigy LS, pin 0 (or bit 8) controls the SPDIF/Analog jack. */ /* SB Live 24bit: @@ -152,9 +152,9 @@ * GPO [15:8] Default 0x9. (Default to SPDIF jack enabled for SPDIF) * GPO Enable [23:16] Default 0x0f. Setting a bit to 1, causes the pin to be an output pin. */ -#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ +#define CA0106_AC97DATA 0x1c /* AC97 register set data register (16 bit) */ -#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ +#define CA0106_AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ /********************************************************************************************************/ /* CA0106 pointer-offset register set, accessed through the PTR and DATA registers */ diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 36fb150b72fb..8577f9fa5ea6 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -338,8 +338,8 @@ unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu, regptr = (reg << 16) | chn; spin_lock_irqsave(&emu->emu_lock, flags); - outl(regptr, emu->port + PTR); - val = inl(emu->port + DATA); + outl(regptr, emu->port + CA0106_PTR); + val = inl(emu->port + CA0106_DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); return val; } @@ -355,8 +355,8 @@ void snd_ca0106_ptr_write(struct snd_ca0106 *emu, regptr = (reg << 16) | chn; spin_lock_irqsave(&emu->emu_lock, flags); - outl(regptr, emu->port + PTR); - outl(data, emu->port + DATA); + outl(regptr, emu->port + CA0106_PTR); + outl(data, emu->port + CA0106_DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -455,8 +455,8 @@ static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb) unsigned int intr_enable; spin_lock_irqsave(&emu->emu_lock, flags); - intr_enable = inl(emu->port + INTE) | intrenb; - outl(intr_enable, emu->port + INTE); + intr_enable = inl(emu->port + CA0106_INTE) | intrenb; + outl(intr_enable, emu->port + CA0106_INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -466,8 +466,8 @@ static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb unsigned int intr_enable; spin_lock_irqsave(&emu->emu_lock, flags); - intr_enable = inl(emu->port + INTE) & ~intrenb; - outl(intr_enable, emu->port + INTE); + intr_enable = inl(emu->port + CA0106_INTE) & ~intrenb; + outl(intr_enable, emu->port + CA0106_INTE); spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -786,9 +786,9 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream) hcfg_set = 0; break; } - hcfg = inl(emu->port + HCFG) ; + hcfg = inl(emu->port + CA0106_HCFG) ; hcfg = (hcfg & ~hcfg_mask) | hcfg_set; - outl(hcfg, emu->port + HCFG); + outl(hcfg, emu->port + CA0106_HCFG); reg40 = snd_ca0106_ptr_read(emu, 0x40, 0); reg40 = (reg40 & ~reg40_mask) | reg40_set; snd_ca0106_ptr_write(emu, 0x40, 0, reg40); @@ -888,9 +888,9 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream) hcfg_set = 0; break; } - hcfg = inl(emu->port + HCFG) ; + hcfg = inl(emu->port + CA0106_HCFG) ; hcfg = (hcfg & ~hcfg_mask) | hcfg_set; - outl(hcfg, emu->port + HCFG); + outl(hcfg, emu->port + CA0106_HCFG); reg71 = snd_ca0106_ptr_read(emu, 0x71, 0); reg71 = (reg71 & ~reg71_mask) | reg71_set; snd_ca0106_ptr_write(emu, 0x71, 0, reg71); @@ -1142,8 +1142,8 @@ static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97, unsigned short val; spin_lock_irqsave(&emu->emu_lock, flags); - outb(reg, emu->port + AC97ADDRESS); - val = inw(emu->port + AC97DATA); + outb(reg, emu->port + CA0106_AC97ADDRESS); + val = inw(emu->port + CA0106_AC97DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); return val; } @@ -1155,8 +1155,8 @@ static void snd_ca0106_ac97_write(struct snd_ac97 *ac97, unsigned long flags; spin_lock_irqsave(&emu->emu_lock, flags); - outb(reg, emu->port + AC97ADDRESS); - outw(val, emu->port + AC97DATA); + outb(reg, emu->port + CA0106_AC97ADDRESS); + outw(val, emu->port + CA0106_AC97DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -1200,7 +1200,7 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id) unsigned int stat76; struct snd_ca0106_channel *pchannel; - status = inl(chip->port + IPR); + status = inl(chip->port + CA0106_IPR); if (! status) return IRQ_NONE; @@ -1255,7 +1255,7 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id) } // acknowledge the interrupt if necessary - outl(status, chip->port+IPR); + outl(status, chip->port + CA0106_IPR); return IRQ_HANDLED; } @@ -1383,7 +1383,7 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) int ch; unsigned int def_bits; - outl(0, chip->port + INTE); + outl(0, chip->port + CA0106_INTE); /* * Init to 0x02109204 : @@ -1420,8 +1420,8 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000); /* Write 0x8000 to AC97_REC_GAIN to mute it. */ - outb(AC97_REC_GAIN, chip->port + AC97ADDRESS); - outw(0x8000, chip->port + AC97DATA); + outb(AC97_REC_GAIN, chip->port + CA0106_AC97ADDRESS); + outw(0x8000, chip->port + CA0106_AC97DATA); #if 0 /* FIXME: what are these? */ snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006); snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006); @@ -1495,30 +1495,30 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume) /* FIXME: Still need to find out what the other GPIO bits do. * E.g. For digital spdif out. */ - outl(0x0, chip->port+GPIO); - /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */ - outl(0x005f5301, chip->port+GPIO); /* Analog */ + outl(0x0, chip->port + CA0106_GPIO); + /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */ + outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */ } else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */ /* FIXME: Still need to find out what the other GPIO bits do. * E.g. For digital spdif out. */ - outl(0x0, chip->port+GPIO); - /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */ - outl(0x005f5301, chip->port+GPIO); /* Analog */ + outl(0x0, chip->port + CA0106_GPIO); + /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */ + outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */ } else { - outl(0x0, chip->port+GPIO); - outl(0x005f03a3, chip->port+GPIO); /* Analog */ - /* outl(0x005f02a2, chip->port+GPIO); */ /* SPDIF */ + outl(0x0, chip->port + CA0106_GPIO); + outl(0x005f03a3, chip->port + CA0106_GPIO); /* Analog */ + /* outl(0x005f02a2, chip->port + CA0106_GPIO); */ /* SPDIF */ } snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */ /* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */ /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */ - /* outl(0x00001409, chip->port+HCFG); */ - /* outl(0x00000009, chip->port+HCFG); */ + /* outl(0x00001409, chip->port + CA0106_HCFG); */ + /* outl(0x00000009, chip->port + CA0106_HCFG); */ /* AC97 2.0, Enable outputs. */ - outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); + outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port + CA0106_HCFG); if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */ @@ -1560,12 +1560,12 @@ static void ca0106_stop_chip(struct snd_ca0106 *chip) { /* disable interrupts */ snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0); - outl(0, chip->port + INTE); + outl(0, chip->port + CA0106_INTE); snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0); udelay(1000); /* disable audio */ /* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */ - outl(0, chip->port + HCFG); + outl(0, chip->port + CA0106_HCFG); /* FIXME: We need to stop and DMA transfers here. * But as I am not sure how yet, we cannot from the dma pages. * So we can fix: snd-malloc: Memory leak? pages not freed = 8 diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index c852c6a75b91..05f56015ddd8 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -70,8 +70,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu) snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000); val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000; snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); - val = inl(emu->port + GPIO) & ~0x101; - outl(val, emu->port + GPIO); + val = inl(emu->port + CA0106_GPIO) & ~0x101; + outl(val, emu->port + CA0106_GPIO); } else { /* Analog */ @@ -79,8 +79,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu) snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000); val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000; snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); - val = inl(emu->port + GPIO) | 0x101; - outl(val, emu->port + GPIO); + val = inl(emu->port + CA0106_GPIO) | 0x101; + outl(val, emu->port + CA0106_GPIO); } } @@ -119,14 +119,14 @@ static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu) if (emu->capture_mic_line_in) { /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ - tmp = inl(emu->port+GPIO) & ~0x400; + tmp = inl(emu->port + CA0106_GPIO) & ~0x400; tmp = tmp | 0x400; - outl(tmp, emu->port+GPIO); + outl(tmp, emu->port + CA0106_GPIO); /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */ } else { /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ - tmp = inl(emu->port+GPIO) & ~0x400; - outl(tmp, emu->port+GPIO); + tmp = inl(emu->port + CA0106_GPIO) & ~0x400; + outl(tmp, emu->port + CA0106_GPIO); /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */ } } From a531caa5989e0913ffe8c1a224a130ccd8d2c02c Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Thu, 10 Feb 2022 20:54:23 +0200 Subject: [PATCH 18/46] ALSA: hda: Add PCI and HDMI IDs for Intel Raptor Lake Add a set of HD Audio PCI IDs, and the HDMI codec VID, for Intel Raptor Lake. Signed-off-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20220210185423.3671603-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 11 +++++++++++ sound/pci/hda/patch_hdmi.c | 1 + 2 files changed, 12 insertions(+) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index a2922233e85f..a3c4d6e293e1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2510,6 +2510,17 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, { PCI_DEVICE(0x8086, 0x4b58), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* Raptor Lake */ + { PCI_DEVICE(0x8086, 0x7a50), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51ca), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51cb), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51ce), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51cf), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Broxton-P(Apollolake) */ { PCI_DEVICE(0x8086, 0x5a98), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 92df4f243ec6..64fe025fda86 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -4390,6 +4390,7 @@ HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), From bad03efd11dfa6f039fe494207996c482e9364d0 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Wed, 9 Feb 2022 07:01:33 -0800 Subject: [PATCH 19/46] ALSA: cleanup double word in comment Remove the second 'device'. Signed-off-by: Tom Rix Link: https://lore.kernel.org/r/20220209150133.2291856-1-trix@redhat.com Signed-off-by: Takashi Iwai --- include/sound/hda_verbs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sound/hda_verbs.h b/include/sound/hda_verbs.h index e36b77531c5c..006d358acce2 100644 --- a/include/sound/hda_verbs.h +++ b/include/sound/hda_verbs.h @@ -461,7 +461,7 @@ enum { #define AC_DE_ELDV (1<<1) #define AC_DE_IA (1<<2) -/* device device types (0x0-0xf) */ +/* device types (0x0-0xf) */ enum { AC_JACK_LINE_OUT, AC_JACK_SPEAKER, From 15175a4f2bbbcbadb1a40098b6dcff0467300e99 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 16 Feb 2022 19:24:05 +0200 Subject: [PATCH 20/46] ALSA: hda/hdmi: add keep-alive support for ADL-P and DG2 Implement HDA keep alive (KAE) support for Intel display codecs. When no audio stream is active, the display codec will provide a continuous clock and a valid but silent audio stream to any connected HDMI/DP receiver. Without this, upon starting a new playback stream, initial samples may be lost as many receivers require time to initialize for new clock. This is a new feature in Intel AlderLake-P display codec implementation and replaces the Intel i915 silent-stream extension that has been used on older hardware. Main benefit of the new method is that codec no longer needs to be kept in D0 power state. This patch depends on commit 112a87c48e83 ("drm/i915/display: program audio CDCLK-TS for keepalives"). [ a minor coding-style fix by tiwai ] Signed-off-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Jyri Sarha Link: https://lore.kernel.org/r/20220216172405.3994959-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 19 ++--- sound/pci/hda/patch_hdmi.c | 156 +++++++++++++++++++++++++++---------- 2 files changed, 126 insertions(+), 49 deletions(-) diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index febe1c2b7d9a..9f6c99c1d87b 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -285,15 +285,16 @@ config SND_HDA_INTEL_HDMI_SILENT_STREAM bool "Enable Silent Stream always for HDMI" depends on SND_HDA_INTEL help - Intel hardware has a feature called 'silent stream', that - keeps external HDMI receiver's analog circuitry powered on - avoiding 2-3 sec silence during playback start. This mechanism - relies on setting channel_id as 0xf, sending info packet and - preventing codec D3 entry (increasing platform static power - consumption when HDMI receiver is plugged-in). 2-3 sec silence - at the playback start is expected whenever there is format change. - (default is 2 channel format). - Say Y to enable Silent Stream feature. + Say Y to enable HD-Audio Keep Alive (KAE) aka Silent Stream + for HDMI on hardware that supports the feature. + + When enabled, the HDMI/DisplayPort codec will continue to provide + a continuous clock and a valid but silent data stream to + any connected external receiver. This allows to avoid gaps + at start of playback. Many receivers require multiple seconds + to start playing audio after the clock has been stopped. + This feature can impact power consumption as resources + are kept reserved both at transmitter and receiver. endif diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 64fe025fda86..69a912c52ca7 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -120,6 +120,12 @@ struct hdmi_pcm { struct snd_kcontrol *eld_ctl; }; +enum { + SILENT_STREAM_OFF = 0, + SILENT_STREAM_KAE, /* use standard HDA Keep-Alive */ + SILENT_STREAM_I915, /* Intel i915 extension */ +}; + struct hdmi_spec { struct hda_codec *codec; int num_cvts; @@ -179,7 +185,7 @@ struct hdmi_spec { hda_nid_t vendor_nid; const int *port_map; int port_num; - bool send_silent_stream; /* Flag to enable silent stream feature */ + int silent_stream_type; }; #ifdef CONFIG_SND_HDA_COMPONENT @@ -1665,18 +1671,71 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, #define I915_SILENT_FORMAT_BITS 16 #define I915_SILENT_FMT_MASK 0xf +static void silent_stream_enable_i915(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + unsigned int format; + + snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, + per_pin->dev_id, I915_SILENT_RATE); + + /* trigger silent stream generation in hw */ + format = snd_hdac_calc_stream_format(I915_SILENT_RATE, I915_SILENT_CHANNELS, + I915_SILENT_FORMAT, I915_SILENT_FORMAT_BITS, 0); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, + I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); + usleep_range(100, 200); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format); + + per_pin->channels = I915_SILENT_CHANNELS; + hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); +} + +static void silent_stream_set_kae(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool enable) +{ + unsigned int param; + + codec_dbg(codec, "HDMI: KAE %d cvt-NID=0x%x\n", enable, per_pin->cvt_nid); + + param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); + param = (param >> 16) & 0xff; + + if (enable) + param |= AC_DIG3_KAE; + else + param &= ~AC_DIG3_KAE; + + snd_hda_codec_write(codec, per_pin->cvt_nid, 0, AC_VERB_SET_DIGI_CONVERT_3, param); +} + static void silent_stream_enable(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin) { struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_cvt *per_cvt; int cvt_idx, pin_idx, err; - unsigned int format; + int keep_power = 0; + + /* + * Power-up will call hdmi_present_sense, so the PM calls + * have to be done without mutex held. + */ + + err = snd_hda_power_up_pm(codec); + if (err < 0 && err != -EACCES) { + codec_err(codec, + "Failed to power up codec for silent stream enable ret=[%d]\n", err); + snd_hda_power_down_pm(codec); + return; + } mutex_lock(&per_pin->lock); if (per_pin->setup) { codec_dbg(codec, "hdmi: PCM already open, no silent stream\n"); + err = -EBUSY; goto unlock_out; } @@ -1703,22 +1762,23 @@ static void silent_stream_enable(struct hda_codec *codec, /* configure unused pins to choose other converters */ pin_cvt_fixup(codec, per_pin, 0); - snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, - per_pin->dev_id, I915_SILENT_RATE); - - /* trigger silent stream generation in hw */ - format = snd_hdac_calc_stream_format(I915_SILENT_RATE, I915_SILENT_CHANNELS, - I915_SILENT_FORMAT, I915_SILENT_FORMAT_BITS, 0); - snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, - I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); - usleep_range(100, 200); - snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format); - - per_pin->channels = I915_SILENT_CHANNELS; - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + switch (spec->silent_stream_type) { + case SILENT_STREAM_KAE: + silent_stream_set_kae(codec, per_pin, true); + break; + case SILENT_STREAM_I915: + silent_stream_enable_i915(codec, per_pin); + keep_power = 1; + break; + default: + break; + } unlock_out: mutex_unlock(&per_pin->lock); + + if (err || !keep_power) + snd_hda_power_down_pm(codec); } static void silent_stream_disable(struct hda_codec *codec, @@ -1726,7 +1786,16 @@ static void silent_stream_disable(struct hda_codec *codec, { struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_cvt *per_cvt; - int cvt_idx; + int cvt_idx, err; + + err = snd_hda_power_up_pm(codec); + if (err < 0 && err != -EACCES) { + codec_err(codec, + "Failed to power up codec for silent stream disable ret=[%d]\n", + err); + snd_hda_power_down_pm(codec); + return; + } mutex_lock(&per_pin->lock); if (!per_pin->silent_stream) @@ -1741,11 +1810,20 @@ static void silent_stream_disable(struct hda_codec *codec, per_cvt->assigned = 0; } + if (spec->silent_stream_type == SILENT_STREAM_I915) { + /* release ref taken in silent_stream_enable() */ + snd_hda_power_down_pm(codec); + } else if (spec->silent_stream_type == SILENT_STREAM_KAE) { + silent_stream_set_kae(codec, per_pin, false); + } + per_pin->cvt_nid = 0; per_pin->silent_stream = false; unlock_out: mutex_unlock(&per_pin->lock); + + snd_hda_power_down_pm(codec); } /* update ELD and jack state via audio component */ @@ -1767,29 +1845,11 @@ static void sync_eld_via_acomp(struct hda_codec *codec, monitor_next = per_pin->sink_eld.monitor_present; mutex_unlock(&per_pin->lock); - /* - * Power-up will call hdmi_present_sense, so the PM calls - * have to be done without mutex held. - */ - - if (spec->send_silent_stream) { - int pm_ret; - - if (!monitor_prev && monitor_next) { - pm_ret = snd_hda_power_up_pm(codec); - if (pm_ret < 0) - codec_err(codec, - "Monitor plugged-in, Failed to power up codec ret=[%d]\n", - pm_ret); + if (spec->silent_stream_type) { + if (!monitor_prev && monitor_next) silent_stream_enable(codec, per_pin); - } else if (monitor_prev && !monitor_next) { + else if (monitor_prev && !monitor_next) silent_stream_disable(codec, per_pin); - pm_ret = snd_hda_power_down_pm(codec); - if (pm_ret < 0) - codec_err(codec, - "Monitor plugged-out, Failed to power down codec ret=[%d]\n", - pm_ret); - } } } @@ -2982,7 +3042,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, * module param or Kconfig option */ if (send_silent_stream) - spec->send_silent_stream = true; + spec->silent_stream_type = SILENT_STREAM_I915; return parse_intel_hdmi(codec); } @@ -3035,6 +3095,22 @@ static int patch_i915_tgl_hdmi(struct hda_codec *codec) return ret; } +static int patch_i915_adlp_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int res; + + res = patch_i915_tgl_hdmi(codec); + if (!res) { + spec = codec->spec; + + if (spec->silent_stream_type) + spec->silent_stream_type = SILENT_STREAM_KAE; + } + + return res; +} + /* Intel Baytrail and Braswell; with eld notifier */ static int patch_i915_byt_hdmi(struct hda_codec *codec) { @@ -4391,10 +4467,10 @@ HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), From fefee95488412796b293d28c948be6fce63d149b Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 14 Feb 2022 11:14:01 +0100 Subject: [PATCH 21/46] ALSA: hda: Add snd_hdac_ext_bus_link_at() helper This patch exposes a new helper to directly retrieve the link from the codec address, and makes use of this helper when retrieving the link from the codec name. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220214101404.4074026-2-cezary.rojewski@intel.com Signed-off-by: Takashi Iwai --- include/sound/hdaudio_ext.h | 1 + sound/hda/ext/hdac_ext_controller.c | 31 +++++++++++++++++++---------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 77123c3e4095..b0c8e4936168 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -28,6 +28,7 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *chip, bool enable, int index); int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus); +struct hdac_ext_link *snd_hdac_ext_bus_link_at(struct hdac_bus *bus, int addr); struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, const char *codec_name); diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c index b2df7b4f9227..b072392725c7 100644 --- a/sound/hda/ext/hdac_ext_controller.c +++ b/sound/hda/ext/hdac_ext_controller.c @@ -132,6 +132,26 @@ void snd_hdac_link_free_all(struct hdac_bus *bus) } EXPORT_SYMBOL_GPL(snd_hdac_link_free_all); +/** + * snd_hdac_ext_bus_link_at - get link at specified address + * @bus: link's parent bus device + * @addr: codec device address + * + * Returns link object or NULL if matching link is not found. + */ +struct hdac_ext_link *snd_hdac_ext_bus_link_at(struct hdac_bus *bus, int addr) +{ + struct hdac_ext_link *hlink; + int i; + + list_for_each_entry(hlink, &bus->hlink_list, list) + for (i = 0; i < HDA_MAX_CODECS; i++) + if (hlink->lsdiid & (0x1 << addr)) + return hlink; + return NULL; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_at); + /** * snd_hdac_ext_bus_get_link - get link based on codec name * @bus: the pointer to HDAC bus object @@ -140,8 +160,6 @@ EXPORT_SYMBOL_GPL(snd_hdac_link_free_all); struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, const char *codec_name) { - int i; - struct hdac_ext_link *hlink = NULL; int bus_idx, addr; if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2) @@ -151,14 +169,7 @@ struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, if (addr < 0 || addr > 31) return NULL; - list_for_each_entry(hlink, &bus->hlink_list, list) { - for (i = 0; i < HDA_MAX_CODECS; i++) { - if (hlink->lsdiid & (0x1 << addr)) - return hlink; - } - } - - return NULL; + return snd_hdac_ext_bus_link_at(bus, addr); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_link); From 595511a3ab80d9ec6649bbf22a99bee169026fd1 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 14 Feb 2022 11:14:02 +0100 Subject: [PATCH 22/46] ALSA: hda: Update and expose snd_hda_codec_device_init() With few changes, snd_hda_codec_device_init() can be re-used by ASoC drivers. While at it, provide kernel doc for the exposed function. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220214101404.4074026-3-cezary.rojewski@intel.com Signed-off-by: Takashi Iwai --- include/sound/hda_codec.h | 3 +++ sound/pci/hda/hda_codec.c | 45 +++++++++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 82d9daa17851..5e3cbcca42f0 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -306,6 +306,9 @@ struct hda_codec { /* * constructors */ +__printf(3, 4) struct hda_codec * +snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr, + const char *fmt, ...); int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp); int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f552785d301e..b7ac3a10d042 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -877,36 +877,48 @@ static void snd_hda_codec_dev_release(struct device *dev) #define DEV_NAME_LEN 31 -static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card, - unsigned int codec_addr, struct hda_codec **codecp) +/** + * snd_hda_codec_device_init - allocate HDA codec device + * @bus: codec's parent bus + * @codec_addr: the codec address on the parent bus + * @fmt: format string for the device's name + * + * Returns newly allocated codec device or ERR_PTR() on failure. + */ +struct hda_codec * +snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr, + const char *fmt, ...) { + va_list vargs; char name[DEV_NAME_LEN]; struct hda_codec *codec; int err; - dev_dbg(card->dev, "%s: entry\n", __func__); - if (snd_BUG_ON(!bus)) - return -EINVAL; + return ERR_PTR(-EINVAL); if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) - return -EINVAL; + return ERR_PTR(-EINVAL); codec = kzalloc(sizeof(*codec), GFP_KERNEL); if (!codec) - return -ENOMEM; + return ERR_PTR(-ENOMEM); + + va_start(vargs, fmt); + vsprintf(name, fmt, vargs); + va_end(vargs); - sprintf(name, "hdaudioC%dD%d", card->number, codec_addr); err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr); if (err < 0) { kfree(codec); - return err; + return ERR_PTR(err); } + codec->bus = bus; codec->core.type = HDA_DEV_LEGACY; - *codecp = codec; - return err; + return codec; } +EXPORT_SYMBOL_GPL(snd_hda_codec_device_init); /** * snd_hda_codec_new - create a HDA codec @@ -920,11 +932,13 @@ static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card, int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp) { - int ret; + struct hda_codec *codec; - ret = snd_hda_codec_device_init(bus, card, codec_addr, codecp); - if (ret < 0) - return ret; + codec = snd_hda_codec_device_init(bus, codec_addr, "hdaudioC%dD%d", + card->number, codec_addr); + if (IS_ERR(codec)) + return PTR_ERR(codec); + *codecp = codec; return snd_hda_codec_device_new(bus, card, codec_addr, *codecp); } @@ -951,7 +965,6 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, codec->core.dev.release = snd_hda_codec_dev_release; codec->core.exec_verb = codec_exec_verb; - codec->bus = bus; codec->card = card; codec->addr = codec_addr; mutex_init(&codec->spdif_mutex); From 17e0c4cbb748fca764ee184c50b60b3a7b0e0dc7 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 14 Feb 2022 11:14:03 +0100 Subject: [PATCH 23/46] ALSA: hda: Update and expose codec register procedures With few changes, snd_hda_codec_register() and its unregister-counterpart can be re-used by ASoC drivers. While at it, provide kernel doc for the exposed functions. Due to ALSA-device vs ASoC-component organization differences, new 'snddev_managed' argument is specified allowing for better control over codec registration process. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220214101404.4074026-4-cezary.rojewski@intel.com Signed-off-by: Takashi Iwai --- include/sound/hda_codec.h | 5 ++++- sound/pci/hda/hda_codec.c | 35 ++++++++++++++++++++++++++--------- sound/pci/hda/hda_local.h | 1 - sound/soc/codecs/hdac_hda.c | 2 +- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 5e3cbcca42f0..f74abc13414f 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -312,9 +312,12 @@ snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr, int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp); int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, - unsigned int codec_addr, struct hda_codec *codec); + unsigned int codec_addr, struct hda_codec *codec, + bool snddev_managed); int snd_hda_codec_configure(struct hda_codec *codec); int snd_hda_codec_update_widgets(struct hda_codec *codec); +void snd_hda_codec_register(struct hda_codec *codec); +void snd_hda_codec_unregister(struct hda_codec *codec); /* * low level functions diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b7ac3a10d042..f0c74d3b4c65 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -813,7 +813,12 @@ void snd_hda_codec_display_power(struct hda_codec *codec, bool enable) snd_hdac_display_power(&codec->bus->core, codec->addr, enable); } -/* also called from hda_bind.c */ +/** + * snd_hda_codec_register - Finalize codec initialization + * @codec: codec device to register + * + * Also called from hda_bind.c + */ void snd_hda_codec_register(struct hda_codec *codec) { if (codec->registered) @@ -826,6 +831,7 @@ void snd_hda_codec_register(struct hda_codec *codec) codec->registered = 1; } } +EXPORT_SYMBOL_GPL(snd_hda_codec_register); static int snd_hda_codec_dev_register(struct snd_device *device) { @@ -833,10 +839,12 @@ static int snd_hda_codec_dev_register(struct snd_device *device) return 0; } -static int snd_hda_codec_dev_free(struct snd_device *device) +/** + * snd_hda_codec_unregister - Unregister specified codec device + * @codec: codec device to unregister + */ +void snd_hda_codec_unregister(struct hda_codec *codec) { - struct hda_codec *codec = device->device_data; - codec->in_freeing = 1; /* * snd_hda_codec_device_new() is used by legacy HDA and ASoC driver. @@ -853,7 +861,12 @@ static int snd_hda_codec_dev_free(struct snd_device *device) */ if (codec->core.type == HDA_DEV_LEGACY) put_device(hda_codec_dev(codec)); +} +EXPORT_SYMBOL_GPL(snd_hda_codec_unregister); +static int snd_hda_codec_dev_free(struct snd_device *device) +{ + snd_hda_codec_unregister(device->device_data); return 0; } @@ -940,12 +953,13 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, return PTR_ERR(codec); *codecp = codec; - return snd_hda_codec_device_new(bus, card, codec_addr, *codecp); + return snd_hda_codec_device_new(bus, card, codec_addr, *codecp, true); } EXPORT_SYMBOL_GPL(snd_hda_codec_new); int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, - unsigned int codec_addr, struct hda_codec *codec) + unsigned int codec_addr, struct hda_codec *codec, + bool snddev_managed) { char component[31]; hda_nid_t fg; @@ -1020,9 +1034,12 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, codec->core.subsystem_id, codec->core.revision_id); snd_component_add(card, component); - err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops); - if (err < 0) - goto error; + if (snddev_managed) { + /* ASoC features component management instead */ + err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops); + if (err < 0) + goto error; + } /* PM runtime needs to be enabled later after binding codec */ pm_runtime_forbid(&codec->core.dev); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 8621f576446b..4c52dfb615bc 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -135,7 +135,6 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, #define snd_hda_add_vmaster(codec, name, tlv, followers, suffix, access) \ __snd_hda_add_vmaster(codec, name, tlv, followers, suffix, true, access, NULL) int snd_hda_codec_reset(struct hda_codec *codec); -void snd_hda_codec_register(struct hda_codec *codec); void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); void snd_hda_codec_disconnect_pcms(struct hda_codec *codec); diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index de5955db0a5f..667f3df239c7 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -413,7 +413,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) HDA_CODEC_IDX_CONTROLLER, true); ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card, - hdev->addr, hcodec); + hdev->addr, hcodec, true); if (ret < 0) { dev_err(&hdev->dev, "failed to create hda codec %d\n", ret); goto error_no_pm; From bb682f7a91af08f1fdb669d3911978d7166a65d1 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 14 Feb 2022 11:14:04 +0100 Subject: [PATCH 24/46] ALSA: hda: Expose codec cleanup and power-save functions With few changes, snd_hda_codec_set_power_save() and snd_hda_codec_cleanup_for_unbind() can be re-used by ASoC drivers. While at it, provide kernel doc for the exposed functions. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20220214101404.4074026-5-cezary.rojewski@intel.com Signed-off-by: Takashi Iwai --- include/sound/hda_codec.h | 3 +++ sound/pci/hda/hda_codec.c | 14 ++++++++++++-- sound/pci/hda/hda_local.h | 1 - 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index f74abc13414f..77426ff58338 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -318,6 +318,7 @@ int snd_hda_codec_configure(struct hda_codec *codec); int snd_hda_codec_update_widgets(struct hda_codec *codec); void snd_hda_codec_register(struct hda_codec *codec); void snd_hda_codec_unregister(struct hda_codec *codec); +void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); /* * low level functions @@ -496,9 +497,11 @@ int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) #define snd_hda_power_down(codec) snd_hdac_power_down(&(codec)->core) #define snd_hda_power_down_pm(codec) snd_hdac_power_down_pm(&(codec)->core) #ifdef CONFIG_PM +void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay); void snd_hda_set_power_save(struct hda_bus *bus, int delay); void snd_hda_update_power_acct(struct hda_codec *codec); #else +static inline void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay) {} static inline void snd_hda_set_power_save(struct hda_bus *bus, int delay) {} #endif diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f0c74d3b4c65..5cbac315dbe1 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -766,6 +766,10 @@ static void codec_release_pcms(struct hda_codec *codec) } } +/** + * snd_hda_codec_cleanup_for_unbind - Prepare codec for removal + * @codec: codec device to cleanup + */ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) { if (codec->registered) { @@ -3401,7 +3405,12 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls); #ifdef CONFIG_PM -static void codec_set_power_save(struct hda_codec *codec, int delay) +/** + * snd_hda_codec_set_power_save - Configure codec's runtime PM + * @codec: codec device to configure + * @delay: autosuspend delay + */ +void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay) { struct device *dev = hda_codec_dev(codec); @@ -3419,6 +3428,7 @@ static void codec_set_power_save(struct hda_codec *codec, int delay) pm_runtime_forbid(dev); } } +EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_save); /** * snd_hda_set_power_save - reprogram autosuspend for the given delay @@ -3432,7 +3442,7 @@ void snd_hda_set_power_save(struct hda_bus *bus, int delay) struct hda_codec *c; list_for_each_codec(c, bus) - codec_set_power_save(c, delay); + snd_hda_codec_set_power_save(c, delay); } EXPORT_SYMBOL_GPL(snd_hda_set_power_save); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 4c52dfb615bc..aca592651870 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -135,7 +135,6 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, #define snd_hda_add_vmaster(codec, name, tlv, followers, suffix, access) \ __snd_hda_add_vmaster(codec, name, tlv, followers, suffix, true, access, NULL) int snd_hda_codec_reset(struct hda_codec *codec); -void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); void snd_hda_codec_disconnect_pcms(struct hda_codec *codec); #define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core) From f43156a9563f9262d7852ab79770f38f1fae7d76 Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Wed, 16 Feb 2022 14:52:35 +0530 Subject: [PATCH 25/46] ALSA: hda/tegra: Add Tegra234 hda driver support Add hda driver support for the Tegra234 chip. The hdacodec on this chip now supports DP MST feature, HDA block contains azalia controller and one hda-codec instance by supporting 4 independent output streams over DP MST mode. There is no input stream support. Signed-off-by: Mohan Kumar Link: https://lore.kernel.org/r/20220216092240.26464-2-mkumard@nvidia.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_tegra.c | 21 +++++++++++++-- sound/pci/hda/patch_hdmi.c | 54 +++++++++++++++++++++++++++++++++----- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 773f4903550a..95df52b0505b 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -70,6 +70,7 @@ struct hda_tegra_soc { bool has_hda2codec_2x_reset; + bool has_hda2hdmi; }; struct hda_tegra { @@ -435,15 +436,23 @@ static int hda_tegra_create(struct snd_card *card, static const struct hda_tegra_soc tegra30_data = { .has_hda2codec_2x_reset = true, + .has_hda2hdmi = true, }; static const struct hda_tegra_soc tegra194_data = { .has_hda2codec_2x_reset = false, + .has_hda2hdmi = true, +}; + +static const struct hda_tegra_soc tegra234_data = { + .has_hda2codec_2x_reset = true, + .has_hda2hdmi = false, }; static const struct of_device_id hda_tegra_match[] = { { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data }, { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data }, + { .compatible = "nvidia,tegra234-hda", .data = &tegra234_data }, {}, }; MODULE_DEVICE_TABLE(of, hda_tegra_match); @@ -473,7 +482,14 @@ static int hda_tegra_probe(struct platform_device *pdev) } hda->resets[hda->nresets++].id = "hda"; - hda->resets[hda->nresets++].id = "hda2hdmi"; + + /* + * "hda2hdmi" is not applicable for Tegra234. This is because the + * codec is separate IP and not under display SOR partition now. + */ + if (hda->soc->has_hda2hdmi) + hda->resets[hda->nresets++].id = "hda2hdmi"; + /* * "hda2codec_2x" reset is not present on Tegra194. Though DT would * be updated to reflect this, but to have backward compatibility @@ -488,7 +504,8 @@ static int hda_tegra_probe(struct platform_device *pdev) goto out_free; hda->clocks[hda->nclocks++].id = "hda"; - hda->clocks[hda->nclocks++].id = "hda2hdmi"; + if (hda->soc->has_hda2hdmi) + hda->clocks[hda->nclocks++].id = "hda2hdmi"; hda->clocks[hda->nclocks++].id = "hda2codec_2x"; err = devm_clk_bulk_get(&pdev->dev, hda->nclocks, hda->clocks); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 69a912c52ca7..a134d91db5ff 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3927,17 +3927,29 @@ static int tegra_hdmi_build_pcms(struct hda_codec *codec) return 0; } -static int patch_tegra_hdmi(struct hda_codec *codec) +static int tegra_hdmi_init(struct hda_codec *codec) { - struct hdmi_spec *spec; - int err; + struct hdmi_spec *spec = codec->spec; + int i, err; - err = patch_generic_hdmi(codec); - if (err) + err = hdmi_parse_codec(codec); + if (err < 0) { + generic_spec_free(codec); return err; + } + + for (i = 0; i < spec->num_cvts; i++) + snd_hda_codec_write(codec, spec->cvt_nids[i], 0, + AC_VERB_SET_DIGI_CONVERT_1, + AC_DIG1_ENABLE); + + generic_hdmi_init_per_pins(codec); codec->patch_ops.build_pcms = tegra_hdmi_build_pcms; - spec = codec->spec; + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + spec->chmap.ops.chmap_cea_alloc_validate_get_type = nvhdmi_chmap_cea_alloc_validate_get_type; spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; @@ -3945,6 +3957,35 @@ static int patch_tegra_hdmi(struct hda_codec *codec) return 0; } +static int patch_tegra_hdmi(struct hda_codec *codec) +{ + int err; + + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + + return tegra_hdmi_init(codec); +} + +static int patch_tegra234_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + + codec->dp_mst = true; + codec->mst_no_extra_pcms = true; + spec = codec->spec; + spec->dyn_pin_out = true; + spec->dyn_pcm_assign = true; + + return tegra_hdmi_init(codec); +} + /* * ATI/AMD-specific implementations */ @@ -4398,6 +4439,7 @@ HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi), HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0031, "Tegra234 HDMI/DP", patch_tegra234_hdmi), HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi), From b58d511ded88ebfc14381eb9fe6e66080e8ed10f Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Wed, 16 Feb 2022 14:52:36 +0530 Subject: [PATCH 26/46] ALSA: hda/tegra: Hardcode GCAP ISS value on T234 The GCAP register on Tegra234 implies no Input Streams(ISS) supported, but the HW output stream descriptor programming should start with offset 0x20*4 from base stream descriptor address. This will be a problem while calculating the offset for output stream descriptor which will be considering input stream also. So here output stream starts with offset 0 which is wrong as HW register for output stream offset starts with 4. So hardcode the input stream numbers to 4 to avoid the issue in offset calculation. Signed-off-by: Mohan Kumar Link: https://lore.kernel.org/r/20220216092240.26464-3-mkumard@nvidia.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_tegra.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 95df52b0505b..2347d0304f93 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -315,6 +315,18 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) * hardcoded value */ chip->capture_streams = (gcap >> 8) & 0x0f; + + /* The GCAP register on Tegra234 implies no Input Streams(ISS) support, + * but the HW output stream descriptor programming should start with + * offset 0x20*4 from base stream descriptor address. This will be a + * problem while calculating the offset for output stream descriptor + * which will be considering input stream also. So here output stream + * starts with offset 0 which is wrong as HW register for output stream + * offset starts with 4. + */ + if (of_device_is_compatible(np, "nvidia,tegra234-hda")) + chip->capture_streams = 4; + chip->playback_streams = (gcap >> 12) & 0x0f; if (!chip->playback_streams && !chip->capture_streams) { /* gcap didn't give any info, switching to old method */ From 85f29492929bd52dc3b05876c7ae0362608475ce Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Wed, 16 Feb 2022 14:52:37 +0530 Subject: [PATCH 27/46] ALSA: hda/tegra: Update scratch reg. communication Tegra234 chip scratch register communication between audio and hdmi driver differs slightly in the way it triggers the interrupt compared to legacy chips. Interrupt is triggered by writing non-zero values to verb 0xF80 instead of 31st bit of scratch register. DP MST support changed the NID to be used for scratch register read/write from audio function group NID to Converter widget NID. Signed-off-by: Mohan Kumar Link: https://lore.kernel.org/r/20220216092240.26464-4-mkumard@nvidia.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 64 ++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index a134d91db5ff..c85ed7bc121e 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -168,6 +168,8 @@ struct hdmi_spec { bool dyn_pin_out; bool dyn_pcm_assign; bool dyn_pcm_no_legacy; + /* hdmi interrupt trigger control flag for Nvidia codec */ + bool hdmi_intr_trig_ctrl; bool intel_hsw_fixup; /* apply Intel platform-specific fixups */ /* * Non-generic VIA/NVIDIA specific @@ -3797,8 +3799,11 @@ static int patch_nvhdmi_legacy(struct hda_codec *codec) * +-----------------------------------| * * Note that for the trigger bit to take effect it needs to change value - * (i.e. it needs to be toggled). + * (i.e. it needs to be toggled). The trigger bit is not applicable from + * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt + * trigger to hdmi. */ +#define NVIDIA_SET_HOST_INTR 0xf80 #define NVIDIA_GET_SCRATCH0 0xfa6 #define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7 #define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8 @@ -3817,25 +3822,38 @@ static int patch_nvhdmi_legacy(struct hda_codec *codec) * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0, * the format is invalidated so that the HDMI codec can be disabled. */ -static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format) +static void tegra_hdmi_set_format(struct hda_codec *codec, + hda_nid_t cvt_nid, + unsigned int format) { unsigned int value; + unsigned int nid = NVIDIA_AFG_NID; + struct hdmi_spec *spec = codec->spec; + + /* + * Tegra HDA codec design from TEGRA234 chip onwards support DP MST. + * This resulted in moving scratch registers from audio function + * group to converter widget context. So CVT NID should be used for + * scratch register read/write for DP MST supported Tegra HDA codec. + */ + if (codec->dp_mst) + nid = cvt_nid; /* bits [31:30] contain the trigger and valid bits */ - value = snd_hda_codec_read(codec, NVIDIA_AFG_NID, 0, + value = snd_hda_codec_read(codec, nid, 0, NVIDIA_GET_SCRATCH0, 0); value = (value >> 24) & 0xff; /* bits [15:0] are used to store the HDA format */ - snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + snd_hda_codec_write(codec, nid, 0, NVIDIA_SET_SCRATCH0_BYTE0, (format >> 0) & 0xff); - snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + snd_hda_codec_write(codec, nid, 0, NVIDIA_SET_SCRATCH0_BYTE1, (format >> 8) & 0xff); /* bits [16:24] are unused */ - snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + snd_hda_codec_write(codec, nid, 0, NVIDIA_SET_SCRATCH0_BYTE2, 0); /* @@ -3847,15 +3865,28 @@ static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format) else value |= NVIDIA_SCRATCH_VALID; - /* - * Whenever the trigger bit is toggled, an interrupt is raised in the - * HDMI codec. The HDMI driver will use that as trigger to update its - * configuration. - */ - value ^= NVIDIA_SCRATCH_TRIGGER; + if (spec->hdmi_intr_trig_ctrl) { + /* + * For Tegra HDA Codec design from TEGRA234 onwards, the + * Interrupt to hdmi driver is triggered by writing + * non-zero values to verb 0xF80 instead of 31st bit of + * scratch register. + */ + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_HOST_INTR, 0x1); + } else { + /* + * Whenever the 31st trigger bit is toggled, an interrupt is raised + * in the HDMI codec. The HDMI driver will use that as trigger + * to update its configuration. + */ + value ^= NVIDIA_SCRATCH_TRIGGER; - snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, - NVIDIA_SET_SCRATCH0_BYTE3, value); + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); + } } static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -3872,7 +3903,7 @@ static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, return err; /* notify the HDMI codec of the format change */ - tegra_hdmi_set_format(codec, format); + tegra_hdmi_set_format(codec, hinfo->nid, format); return 0; } @@ -3882,7 +3913,7 @@ static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { /* invalidate the format in the HDMI codec */ - tegra_hdmi_set_format(codec, 0); + tegra_hdmi_set_format(codec, hinfo->nid, 0); return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream); } @@ -3982,6 +4013,7 @@ static int patch_tegra234_hdmi(struct hda_codec *codec) spec = codec->spec; spec->dyn_pin_out = true; spec->dyn_pcm_assign = true; + spec->hdmi_intr_trig_ctrl = true; return tegra_hdmi_init(codec); } From d23c49562a887a8875b144d092034e14b0e279ff Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Wed, 16 Feb 2022 14:52:39 +0530 Subject: [PATCH 28/46] dt-bindings: Document Tegra234 HDA support Update binding document for HDA support on Tegra234 chip. Tegra234 has max of 2 clocks and 2 resets which requires to add minItems and maxItems for clocks and resets as Tegra chips can now have minimum of 2 and maximum of 3 clocks and reset support. Signed-off-by: Mohan Kumar Link: https://lore.kernel.org/r/20220216092240.26464-6-mkumard@nvidia.com Signed-off-by: Takashi Iwai --- .../devicetree/bindings/sound/nvidia,tegra30-hda.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml index 2c913aa44fee..12c31b4b99e1 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.yaml @@ -23,6 +23,7 @@ properties: - const: nvidia,tegra30-hda - items: - enum: + - nvidia,tegra234-hda - nvidia,tegra194-hda - nvidia,tegra186-hda - nvidia,tegra210-hda @@ -41,9 +42,11 @@ properties: maxItems: 1 clocks: + minItems: 2 maxItems: 3 clock-names: + minItems: 2 items: - const: hda - const: hda2hdmi From a544684b790f3e9f75173b3b42d7dad1c89dd237 Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Fri, 25 Feb 2022 19:19:29 +0800 Subject: [PATCH 29/46] ALSA: mips: Use platform_get_irq() to get the interrupt platform_get_resource(pdev, IORESOURCE_IRQ, ..) relies on static allocation of IRQ resources in DT core code, this causes an issue when using hierarchical interrupt domains using "interrupts" property in the node as this bypassed the hierarchical setup and messed up the irq chaining. In preparation for removal of static setup of IRQ resource from DT core code use platform_get_irq(). Signed-off-by: Meng Tang Link: https://lore.kernel.org/r/20220225111929.17194-1-tangmeng@uniontech.com Signed-off-by: Takashi Iwai --- sound/mips/snd-n64.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/mips/snd-n64.c b/sound/mips/snd-n64.c index 463a6fe589eb..bff6d85b8fe2 100644 --- a/sound/mips/snd-n64.c +++ b/sound/mips/snd-n64.c @@ -289,8 +289,7 @@ static int __init n64audio_probe(struct platform_device *pdev) struct snd_card *card; struct snd_pcm *pcm; struct n64audio *priv; - struct resource *res; - int err; + int err, irq; err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, @@ -337,12 +336,12 @@ static int __init n64audio_probe(struct platform_device *pdev) strcpy(card->shortname, "N64 Audio"); strcpy(card->longname, "N64 Audio"); - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) { err = -EINVAL; goto fail_dma_alloc; } - if (devm_request_irq(&pdev->dev, res->start, n64audio_isr, + if (devm_request_irq(&pdev->dev, irq, n64audio_isr, IRQF_SHARED, "N64 Audio", priv)) { err = -EBUSY; goto fail_dma_alloc; From ca1697eb09208f0168d94b88b72f57505339cbe5 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Mon, 28 Feb 2022 10:28:39 +0800 Subject: [PATCH 30/46] ALSA: spi: Add check for clk_enable() As the potential failure of the clk_enable(), it should be better to check it and return error if fails. Fixes: 3568459a5113 ("ALSA: at73c213: manage SSC clock") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20220228022839.3547266-1-jiasheng@iscas.ac.cn Signed-off-by: Takashi Iwai --- sound/spi/at73c213.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index 76c0e37a838c..8a2da6b1012e 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -218,7 +218,9 @@ static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream) runtime->hw = snd_at73c213_playback_hw; chip->substream = substream; - clk_enable(chip->ssc->clk); + err = clk_enable(chip->ssc->clk); + if (err) + return err; return 0; } @@ -776,7 +778,9 @@ static int snd_at73c213_chip_init(struct snd_at73c213 *chip) goto out; /* Enable DAC master clock. */ - clk_enable(chip->board->dac_clk); + retval = clk_enable(chip->board->dac_clk); + if (retval) + goto out; /* Initialize at73c213 on SPI bus. */ retval = snd_at73c213_write_reg(chip, DAC_RST, 0x04); @@ -889,7 +893,9 @@ static int snd_at73c213_dev_init(struct snd_card *card, chip->card = card; chip->irq = -1; - clk_enable(chip->ssc->clk); + retval = clk_enable(chip->ssc->clk); + if (retval) + return retval; retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip); if (retval) { @@ -1008,7 +1014,9 @@ static int snd_at73c213_remove(struct spi_device *spi) int retval; /* Stop playback. */ - clk_enable(chip->ssc->clk); + retval = clk_enable(chip->ssc->clk); + if (retval) + goto out; ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); clk_disable(chip->ssc->clk); @@ -1088,9 +1096,16 @@ static int snd_at73c213_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct snd_at73c213 *chip = card->private_data; + int retval; - clk_enable(chip->board->dac_clk); - clk_enable(chip->ssc->clk); + retval = clk_enable(chip->board->dac_clk); + if (retval) + return retval; + retval = clk_enable(chip->ssc->clk); + if (retval) { + clk_disable(chip->board->dac_clk); + return retval; + } ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN)); return 0; From d248b2771f543417dea7f9af8be05190594e0621 Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Mon, 28 Feb 2022 13:02:52 +0800 Subject: [PATCH 31/46] sound: core: remove initialise static variables to 0 Initializing the static variable to 0 causes the following error when exec checkpatch: ERROR: do not initialise statics to 0 FILE: sound/sound_core.c:142: static int preclaim_oss = 0; In addition, considering the following way of writing 139: #ifdef config_sound_oss_core_preclaim 140: Static int preclaim_oss = 1; 141: #ELSE 142: Static int preclaim_oss = 0; 143: #ENDIF We can optimize it by IS_ENABLED(CONFIG_SOUND_OSS_CORE_PRECLAIM), so modified it to static int preclaim_oss = IS_ENABLED(CONFIG_SOUND_OSS_CORE_PRECLAIM); Signed-off-by: Meng Tang Link: https://lore.kernel.org/r/20220228050253.1649-1-tangmeng@uniontech.com Signed-off-by: Takashi Iwai --- sound/sound_core.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sound/sound_core.c b/sound/sound_core.c index 90d118cd9164..aa4a57e488e5 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -136,11 +136,7 @@ struct sound_unit * All these clutters are scheduled to be removed along with * sound-slot/service-* module aliases. */ -#ifdef CONFIG_SOUND_OSS_CORE_PRECLAIM -static int preclaim_oss = 1; -#else -static int preclaim_oss = 0; -#endif +static int preclaim_oss = IS_ENABLED(CONFIG_SOUND_OSS_CORE_PRECLAIM); module_param(preclaim_oss, int, 0444); From e52b78f890675132989aaa298e1e1077b6bf9325 Mon Sep 17 00:00:00 2001 From: Meng Tang Date: Mon, 28 Feb 2022 13:02:53 +0800 Subject: [PATCH 32/46] sound: core: Remove redundant variable and return the last statement Return the result from file->f_op->open() directly instead of taking this in another redundant variable. Make the typical return the last statement, return early and reduce the indentation too. Signed-off-by: Meng Tang Link: https://lore.kernel.org/r/20220228050253.1649-2-tangmeng@uniontech.com Signed-off-by: Takashi Iwai --- sound/sound_core.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/sound_core.c b/sound/sound_core.c index aa4a57e488e5..3332fe321737 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -577,20 +577,20 @@ static int soundcore_open(struct inode *inode, struct file *file) new_fops = fops_get(s->unit_fops); } spin_unlock(&sound_loader_lock); - if (new_fops) { - /* - * We rely upon the fact that we can't be unloaded while the - * subdriver is there. - */ - int err = 0; - replace_fops(file, new_fops); - if (file->f_op->open) - err = file->f_op->open(inode,file); + if (!new_fops) + return -ENODEV; - return err; - } - return -ENODEV; + /* + * We rely upon the fact that we can't be unloaded while the + * subdriver is there. + */ + replace_fops(file, new_fops); + + if (!file->f_op->open) + return -ENODEV; + + return file->f_op->open(inode, file); } MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR); From 76f22f4dcae645ea468811f9d30ec04f9ffaa1ea Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Sun, 27 Feb 2022 22:52:04 +0800 Subject: [PATCH 33/46] Documentation: sound: fix typo in control-names.rst change 'cannel' to 'channel' Signed-off-by: Sunrisepeak Link: https://lore.kernel.org/r/20220227145204.16600-1-speakshen@163.com Signed-off-by: Takashi Iwai --- Documentation/sound/designs/control-names.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/sound/designs/control-names.rst b/Documentation/sound/designs/control-names.rst index 7fedd0f33cd9..765ff9b5b7d9 100644 --- a/Documentation/sound/designs/control-names.rst +++ b/Documentation/sound/designs/control-names.rst @@ -34,7 +34,7 @@ CHANNEL Front front left/right channels Surround rear left/right in 4.0/5.1 surround CLFE C/LFE channels -Center center cannel +Center center channel LFE LFE channel Side side left/right for 7.1 surround ============ ================================================== From 3cffb26fbb5202c6a4bee4b99d6bef5623003fbb Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 2 Mar 2022 17:07:28 +0000 Subject: [PATCH 34/46] ALSA: echoaudio: remove redundant assignment to variable bytes The variable bytes is being assigned a value that is never read, it is being re-assigned inside a following if block. The assignment is redundant and can be removed. Cleans up clang scan build warning: sound/pci/echoaudio/midi.c:211:9: warning: Although the value stored to 'bytes' is used in the enclosing expression, the value is never actually read from 'bytes' [deadcode.DeadStores] Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20220302170728.1094633-1-colin.i.king@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/midi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index cb72d27e809e..7be5c3327b16 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -208,7 +208,7 @@ static void snd_echo_midi_output_write(struct timer_list *t) /* No interrupts are involved: we have to check at regular intervals if the card's output buffer has room for new data. */ - sent = bytes = 0; + sent = 0; spin_lock_irqsave(&chip->lock, flags); chip->midi_full = 0; if (!snd_rawmidi_transmit_empty(chip->midi_out)) { From d7f15befac809ba365742464e1b0bebf07149c58 Mon Sep 17 00:00:00 2001 From: Xiaoke Wang Date: Fri, 4 Mar 2022 16:38:20 +0800 Subject: [PATCH 35/46] ALSA: lola: add a check for the return of vmalloc() vmalloc() is a memory allocation function which can return NULL when some internal memory errors happen. So it is better to check the return of it to catch the error in time. Signed-off-by: Xiaoke Wang Link: https://lore.kernel.org/r/tencent_4221FC4089F6DF01C48F192E5784038BA205@qq.com Signed-off-by: Takashi Iwai --- sound/pci/lola/lola_mixer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c index e2c8f1417001..6b162489cb5f 100644 --- a/sound/pci/lola/lola_mixer.c +++ b/sound/pci/lola/lola_mixer.c @@ -121,6 +121,8 @@ int lola_init_mixer_widget(struct lola *chip, int nid) /* reserve memory to copy mixer data for sleep mode transitions */ chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array)); + if (!chip->mixer.array_saved) + return -ENOMEM; /* mixer matrix sources are physical input data and play streams */ chip->mixer.src_stream_outs = chip->pcm[PLAY].num_streams; From fc4cf4293f0da3985ca66edc2cf067531d933c42 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 5 Mar 2022 09:33:08 +0100 Subject: [PATCH 36/46] ALSA: x86: Use standard mmap helper for Intel HDMI LPE audio Intel HDMI LPE audio driver has its own mmap callback that mimics with the noncached page attributes, but this is rather superfluous and can be replaced with the standard helper, as the device is only for playback and the write-cache should suffice. This patch drops the own code and just uses the standard helper. Link: https://lore.kernel.org/r/20220305083308.15718-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/x86/intel_hdmi_audio.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index 4a3ff6468aa7..b00634663346 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1253,18 +1253,6 @@ static snd_pcm_uframes_t had_pcm_pointer(struct snd_pcm_substream *substream) return len; } -/* - * ALSA PCM mmap callback - */ -static int had_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - return remap_pfn_range(vma, vma->vm_start, - substream->runtime->dma_addr >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot); -} - /* * ALSA PCM ops */ @@ -1276,7 +1264,6 @@ static const struct snd_pcm_ops had_pcm_ops = { .trigger = had_pcm_trigger, .sync_stop = had_pcm_sync_stop, .pointer = had_pcm_pointer, - .mmap = had_pcm_mmap, }; /* process mode change of the running stream; called in mutex */ From 441d1e10476bc9d966baf32745277a6e77c325d5 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 7 Mar 2022 01:51:04 +1030 Subject: [PATCH 37/46] ALSA: scarlett2: Split scarlett2_config_items[] into 3 sections scarlett2_config_items[] contains the parameters for the configuration items. The driver previously had two sets of configurations items; one for devices with no mixer, and one for devices with a mixer. This patch splits the latter into two (one set for Gen 2 devices and one set for Gen 3 devices) in preparation for a new item (standalone) which is present in both but with a different offset. Signed-off-by: Geoffrey D. Bennett Link: https://lore.kernel.org/r/20969f9ea500684e978c87067fbdc7e73de1f6ed.1646578164.git.g@b4.vu Signed-off-by: Takashi Iwai --- sound/usb/mixer_scarlett_gen2.c | 74 +++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 7ff8a4817c67..4b1458e993e7 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -6,7 +6,7 @@ * - 6i6/18i8/18i20 Gen 2 * - Solo/2i2/4i4/8i6/18i8/18i20 Gen 3 * - * Copyright (c) 2018-2021 by Geoffrey D. Bennett + * Copyright (c) 2018-2022 by Geoffrey D. Bennett * Copyright (c) 2020-2021 by Vladimir Sadovnikov * * Based on the Scarlett (Gen 1) Driver for ALSA: @@ -195,6 +195,16 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { /* Maximum number of meters (sum of output port counts) */ #define SCARLETT2_MAX_METERS 65 +/* There are three different sets of configuration parameters across + * the devices + */ +enum { + SCARLETT2_CONFIG_SET_NO_MIXER = 0, + SCARLETT2_CONFIG_SET_GEN_2 = 1, + SCARLETT2_CONFIG_SET_GEN_3 = 2, + SCARLETT2_CONFIG_SET_COUNT = 3 +}; + /* Hardware port types: * - None (no input to mux) * - Analogue I/O @@ -308,10 +318,8 @@ struct scarlett2_device_info { */ u8 has_msd_mode; - /* Gen 3 devices without a mixer have a different - * configuration set - */ - u8 has_mixer; + /* which set of configuration parameters the device uses */ + u8 config_set; /* line out hw volume is sw controlled */ u8 line_out_hw_vol; @@ -426,7 +434,7 @@ struct scarlett2_data { static const struct scarlett2_device_info s6i6_gen2_info = { .usb_id = USB_ID(0x1235, 0x8203), - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_2, .level_input_count = 2, .pad_input_count = 2, @@ -472,7 +480,7 @@ static const struct scarlett2_device_info s6i6_gen2_info = { static const struct scarlett2_device_info s18i8_gen2_info = { .usb_id = USB_ID(0x1235, 0x8204), - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_2, .level_input_count = 2, .pad_input_count = 4, @@ -521,7 +529,7 @@ static const struct scarlett2_device_info s18i8_gen2_info = { static const struct scarlett2_device_info s18i20_gen2_info = { .usb_id = USB_ID(0x1235, 0x8201), - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_2, .line_out_hw_vol = 1, .line_out_descrs = { @@ -576,6 +584,7 @@ static const struct scarlett2_device_info solo_gen3_info = { .usb_id = USB_ID(0x1235, 0x8211), .has_msd_mode = 1, + .config_set = SCARLETT2_CONFIG_SET_NO_MIXER, .level_input_count = 1, .level_input_first = 1, .air_input_count = 1, @@ -588,6 +597,7 @@ static const struct scarlett2_device_info s2i2_gen3_info = { .usb_id = USB_ID(0x1235, 0x8210), .has_msd_mode = 1, + .config_set = SCARLETT2_CONFIG_SET_NO_MIXER, .level_input_count = 2, .air_input_count = 2, .phantom_count = 1, @@ -599,7 +609,7 @@ static const struct scarlett2_device_info s4i4_gen3_info = { .usb_id = USB_ID(0x1235, 0x8212), .has_msd_mode = 1, - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_3, .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, @@ -645,7 +655,7 @@ static const struct scarlett2_device_info s8i6_gen3_info = { .usb_id = USB_ID(0x1235, 0x8213), .has_msd_mode = 1, - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_3, .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, @@ -698,7 +708,7 @@ static const struct scarlett2_device_info s18i8_gen3_info = { .usb_id = USB_ID(0x1235, 0x8214), .has_msd_mode = 1, - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_3, .line_out_hw_vol = 1, .has_speaker_switching = 1, .level_input_count = 2, @@ -768,7 +778,7 @@ static const struct scarlett2_device_info s18i20_gen3_info = { .usb_id = USB_ID(0x1235, 0x8215), .has_msd_mode = 1, - .has_mixer = 1, + .config_set = SCARLETT2_CONFIG_SET_GEN_3, .line_out_hw_vol = 1, .has_speaker_switching = 1, .has_talkback = 1, @@ -944,13 +954,11 @@ struct scarlett2_config { u8 activate; }; -/* scarlett2_config_items[0] is for devices without a mixer - * scarlett2_config_items[1] is for devices with a mixer - */ static const struct scarlett2_config - scarlett2_config_items[2][SCARLETT2_CONFIG_COUNT] = + scarlett2_config_items[SCARLETT2_CONFIG_SET_COUNT] + [SCARLETT2_CONFIG_COUNT] = -/* Devices without a mixer (Solo and 2i2 Gen 3) */ +/* Devices without a mixer (Gen 3 Solo and 2i2) */ { { [SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x04, .size = 8, .activate = 6 }, @@ -970,7 +978,27 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_AIR_SWITCH] = { .offset = 0x09, .size = 1, .activate = 8 }, -/* Devices with a mixer (Gen 2 and all other Gen 3) */ +/* Gen 2 devices: 6i6, 18i8, 18i20 */ +}, { + [SCARLETT2_CONFIG_DIM_MUTE] = { + .offset = 0x31, .size = 8, .activate = 2 }, + + [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { + .offset = 0x34, .size = 16, .activate = 1 }, + + [SCARLETT2_CONFIG_MUTE_SWITCH] = { + .offset = 0x5c, .size = 8, .activate = 1 }, + + [SCARLETT2_CONFIG_SW_HW_SWITCH] = { + .offset = 0x66, .size = 8, .activate = 3 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x7c, .size = 8, .activate = 7 }, + + [SCARLETT2_CONFIG_PAD_SWITCH] = { + .offset = 0x84, .size = 8, .activate = 8 }, + +/* Gen 3 devices: 4i4, 8i6, 18i8, 18i20 */ }, { [SCARLETT2_CONFIG_DIM_MUTE] = { .offset = 0x31, .size = 8, .activate = 2 }, @@ -1175,7 +1203,7 @@ static int scarlett2_usb_get_config( struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; const struct scarlett2_config *config_item = - &scarlett2_config_items[info->has_mixer][config_item_num]; + &scarlett2_config_items[info->config_set][config_item_num]; int size, err, i; u8 *buf_8; u8 value; @@ -1235,7 +1263,7 @@ static int scarlett2_usb_set_config( struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; const struct scarlett2_config *config_item = - &scarlett2_config_items[info->has_mixer][config_item_num]; + &scarlett2_config_items[info->config_set][config_item_num]; struct { __le32 offset; __le32 bytes; @@ -1692,7 +1720,7 @@ static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer) struct scarlett2_data *private = mixer->private_data; /* devices without a mixer also don't support reporting sync status */ - if (!private->info->has_mixer) + if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) return 0; return scarlett2_add_new_ctl(mixer, &scarlett2_sync_ctl, @@ -3399,7 +3427,7 @@ static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer) struct scarlett2_data *private = mixer->private_data; /* devices without a mixer also don't support reporting levels */ - if (!private->info->has_mixer) + if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) return 0; return scarlett2_add_new_ctl(mixer, &scarlett2_meter_ctl, @@ -3632,7 +3660,7 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) return err; /* the rest of the configuration is for devices with a mixer */ - if (!info->has_mixer) + if (info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) return 0; err = scarlett2_update_sync(mixer); From 604b388419d08032c07aff8946ee19c073b59189 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 7 Mar 2022 01:51:39 +1030 Subject: [PATCH 38/46] ALSA: scarlett2: Add support for the internal "standalone" switch The Focusrite Scarlett Gen 2/3 interfaces with internal mixers have a "standalone" mode. When the interface is not connected to a USB host and standalone mode is enabled, the interface will pass audio as previously configured. This patch adds an ALSA control to allow enabling/disabling that mode. Signed-off-by: Geoffrey D. Bennett Link: https://lore.kernel.org/r/cd88871c5e77abd5c23a4758a1f2ec9fd427fd69.1646578164.git.g@b4.vu Signed-off-by: Takashi Iwai --- sound/usb/mixer_scarlett_gen2.c | 97 ++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 4b1458e993e7..69a2cd429ee2 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -60,6 +60,7 @@ * - phantom power, direct monitor, speaker switching, and talkback * controls * - disable/enable MSD mode + * - disable/enable standalone mode * * * /--------------\ 18chn 20chn /--------------\ @@ -411,6 +412,7 @@ struct scarlett2_data { u8 talkback_switch; u8 talkback_map[SCARLETT2_OUTPUT_MIX_MAX]; u8 msd_switch; + u8 standalone_switch; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX]; @@ -936,13 +938,14 @@ enum { SCARLETT2_CONFIG_PAD_SWITCH = 5, SCARLETT2_CONFIG_MSD_SWITCH = 6, SCARLETT2_CONFIG_AIR_SWITCH = 7, - SCARLETT2_CONFIG_PHANTOM_SWITCH = 8, - SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 9, - SCARLETT2_CONFIG_DIRECT_MONITOR = 10, - SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH = 11, - SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE = 12, - SCARLETT2_CONFIG_TALKBACK_MAP = 13, - SCARLETT2_CONFIG_COUNT = 14 + SCARLETT2_CONFIG_STANDALONE_SWITCH = 8, + SCARLETT2_CONFIG_PHANTOM_SWITCH = 9, + SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 10, + SCARLETT2_CONFIG_DIRECT_MONITOR = 11, + SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH = 12, + SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE = 13, + SCARLETT2_CONFIG_TALKBACK_MAP = 14, + SCARLETT2_CONFIG_COUNT = 15 }; /* Location, size, and activation command number for the configuration @@ -998,6 +1001,9 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_PAD_SWITCH] = { .offset = 0x84, .size = 8, .activate = 8 }, + [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { + .offset = 0x8d, .size = 8, .activate = 6 }, + /* Gen 3 devices: 4i4, 8i6, 18i8, 18i20 */ }, { [SCARLETT2_CONFIG_DIM_MUTE] = { @@ -1021,6 +1027,9 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_AIR_SWITCH] = { .offset = 0x8c, .size = 8, .activate = 8 }, + [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { + .offset = 0x95, .size = 8, .activate = 6 }, + [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { .offset = 0x9c, .size = 1, .activate = 8 }, @@ -3502,6 +3511,69 @@ static int scarlett2_add_msd_ctl(struct usb_mixer_interface *mixer) 0, 1, "MSD Mode Switch", NULL); } +/*** Standalone Control ***/ + +static int scarlett2_standalone_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett2_data *private = elem->head.mixer->private_data; + + ucontrol->value.integer.value[0] = private->standalone_switch; + return 0; +} + +static int scarlett2_standalone_ctl_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + oval = private->standalone_switch; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->standalone_switch = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config(mixer, + SCARLETT2_CONFIG_STANDALONE_SWITCH, + 0, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_standalone_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_standalone_ctl_get, + .put = scarlett2_standalone_ctl_put, +}; + +static int scarlett2_add_standalone_ctl(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) + return 0; + + /* Add standalone control */ + return scarlett2_add_new_ctl(mixer, &scarlett2_standalone_ctl, + 0, 1, "Standalone Switch", NULL); +} + /*** Cleanup/Suspend Callbacks ***/ static void scarlett2_private_free(struct usb_mixer_interface *mixer) @@ -3663,6 +3735,12 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) return 0; + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH, + 1, &private->standalone_switch); + if (err < 0) + return err; + err = scarlett2_update_sync(mixer); if (err < 0) return err; @@ -3985,6 +4063,11 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer) if (err < 0) return err; + /* Create the standalone control */ + err = scarlett2_add_standalone_ctl(mixer); + if (err < 0) + return err; + /* Set up the interrupt polling */ err = scarlett2_init_notify(mixer); if (err < 0) From 7cacfa4a7b0dcf8f10dda5327957ee13db1c455f Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Tue, 8 Mar 2022 16:13:22 +0200 Subject: [PATCH 39/46] ALSA: hda: Add AlderLake-PS variant PCI ID Add HD Audio PCI ID for a variant of Intel AlderLake-P. Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20220308141322.880775-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 5af7a1e18013..0a83eb6b88b1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2499,6 +2499,8 @@ static const struct pci_device_id azx_ids[] = { /* Alderlake-P */ { PCI_DEVICE(0x8086, 0x51c8), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51c9), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, { PCI_DEVICE(0x8086, 0x51cd), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Alderlake-M */ From 327e8ba54a212f707a68670c9372747b7a32bb92 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 9 Mar 2022 20:24:39 +0200 Subject: [PATCH 40/46] ALSA: hda/i915 - avoid hung task timeout in i915 wait If kernel is built with hung task detection enabled and CONFIG_DEFAULT_HUNG_TASK_TIMEOUT set to less than 60 seconds, snd_hdac_i915_init() will trigger the hung task timeout in case i915 is not available and taint the kernel. Use wait_for_completion_killable_timeout() for the wait to avoid this problem. Co-developed-by: Ramalingam C Signed-off-by: Ramalingam C Signed-off-by: Kai Vehmanen Acked-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20220309182439.1053856-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/hdac_i915.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 454474ac5716..efe810af28c5 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -160,8 +160,8 @@ int snd_hdac_i915_init(struct hdac_bus *bus) if (!IS_ENABLED(CONFIG_MODULES) || !request_module("i915")) { /* 60s timeout */ - wait_for_completion_timeout(&acomp->master_bind_complete, - msecs_to_jiffies(60 * 1000)); + wait_for_completion_killable_timeout(&acomp->master_bind_complete, + msecs_to_jiffies(60 * 1000)); } } if (!acomp->ops) { From 3baa40d4fd7ff6553325715b8a64cc8b43fd02ef Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 12 Mar 2022 11:27:02 +0100 Subject: [PATCH 41/46] ALSA: seq: oss: use kzalloc Use kzalloc instead of kmalloc + memset. The semantic patch that makes this change is: (https://coccinelle.gitlabpages.inria.fr/website/) // @@ expression res, size, flag; @@ - res = kmalloc(size, flag); + res = kzalloc(size, flag); ... - memset(res, 0, size); // Signed-off-by: Julia Lawall Link: https://lore.kernel.org/r/20220312102705.71413-4-Julia.Lawall@inria.fr Signed-off-by: Takashi Iwai --- sound/core/seq/oss/seq_oss_init.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 0ee4a5081fd6..04a3376a6e66 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -66,7 +66,7 @@ snd_seq_oss_create_client(void) struct snd_seq_port_info *port; struct snd_seq_port_callback port_callback; - port = kmalloc(sizeof(*port), GFP_KERNEL); + port = kzalloc(sizeof(*port), GFP_KERNEL); if (!port) { rc = -ENOMEM; goto __error; @@ -81,7 +81,6 @@ snd_seq_oss_create_client(void) system_client = rc; /* create annoucement receiver port */ - memset(port, 0, sizeof(*port)); strcpy(port->name, "Receiver"); port->addr.client = system_client; port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */ From b62c563f1cb9c216cecadb15ae32d6e571e2bc8a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 13 Mar 2022 09:56:35 +0100 Subject: [PATCH 42/46] ALSA: seq: oss: fix typo Fix typo in "announcement". Reported-by: Joe Perches Signed-off-by: Julia Lawall Link: https://lore.kernel.org/r/20220313085635.102123-1-Julia.Lawall@inria.fr Signed-off-by: Takashi Iwai --- sound/core/seq/oss/seq_oss_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 04a3376a6e66..42d4e7535a82 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -80,7 +80,7 @@ snd_seq_oss_create_client(void) system_client = rc; - /* create annoucement receiver port */ + /* create announcement receiver port */ strcpy(port->name, "Receiver"); port->addr.client = system_client; port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */ From e6194c8d065389b9393290ecb682118fd982fa33 Mon Sep 17 00:00:00 2001 From: Andy Chi Date: Mon, 14 Mar 2022 22:21:19 +0800 Subject: [PATCH 43/46] ALSA: hda/realtek: fix right sounds and mute/micmute LEDs for HP machines * The HP ProBook 440/450 and EliteBook 640/650 are using ALC236 codec which used 0x02 to control mute LED and 0x01 to control micmute LED. Therefore, add a quirk to make it works. Signed-off-by: Andy Chi Link: https://lore.kernel.org/r/20220314142122.71602-1-andy.chi@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7d85989f585b..d7bebe3bdea7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9017,6 +9017,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8992, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8994, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8995, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x89a4, "HP ProBook 440 G9", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89a6, "HP ProBook 450 G9", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89ac, "HP EliteBook 640 G9", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89ae, "HP EliteBook 650 G9", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x89c3, "Zbook Studio G9", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), From 864cb14c0fa22344613ae93d68e155bf9bbbc9fb Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Fri, 18 Mar 2022 06:11:33 +0800 Subject: [PATCH 44/46] ALSA: hda/realtek: Fix LED on Zbook Studio G9 Commit 07bcab93946c ("ALSA: hda/realtek: Add support for HP Laptops") breaks mute and micmute LEDs because it changed the LED quirk from ALC285_FIXUP_HP_GPIO_LED to ALC245_FIXUP_HP_GPIO_LED, so change it back here. Also reorder the chain of quirks to ensure LED quirk is the last one being applied. Fixes: 07bcab93946c ("ALSA: hda/realtek: Add support for HP Laptops") Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20220317221134.566358-1-kai.heng.feng@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d7bebe3bdea7..0a9ad35d34c1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8773,9 +8773,9 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED] = { .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_gpio_led, + .v.func = cs35l41_fixup_spi_four, .chained = true, - .chain_id = ALC245_FIXUP_CS35L41_SPI_4, + .chain_id = ALC285_FIXUP_HP_GPIO_LED, }, [ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = { .type = HDA_FIXUP_VERBS, From a893b7fc7b59cdf3fae01335c86537bee4407edc Mon Sep 17 00:00:00 2001 From: Elijah Harding Date: Thu, 17 Mar 2022 18:52:01 -0700 Subject: [PATCH 45/46] ALSA: core: Fix typo in 'PCM Timer Interface' help Signed-off-by: Elijah Harding Link: https://lore.kernel.org/r/20220318015201.30871-1-eharding830@gmail.com Signed-off-by: Takashi Iwai --- sound/core/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/Kconfig b/sound/core/Kconfig index db2e3c63ff41..dd7b40734723 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -84,7 +84,7 @@ config SND_PCM_TIMER help If you disable this option, pcm timer will be unavailable, so those stubs that use pcm timer (e.g. dmix, dsnoop & co) may work - incorrectlly. + incorrectly. For some embedded devices, we may disable it to reduce memory footprint, about 20KB on x86_64 platform. From 8a580a26760cb14535c160613fe9cd0e4dc6f5c6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Mar 2022 09:21:57 +0100 Subject: [PATCH 46/46] ALSA: oss: Release temporary buffers upon errors When the parameter changes fails, we don't need to keep the old temporary buffers. Release those (and plugin instances) upon errors for reducing dead memory footprint. Since we always call it at the exit of snd_pcm_oss_changes_params_locked(), the explicit calls of snd_pcm_oss_plugin_clear() can be dropped, too. Along with it, unify the buffer-free calls to a single helper and call it from the needed places. Link: https://lore.kernel.org/r/20220318082157.29769-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/oss/pcm_oss.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 3ee9edf85815..b8afa9f02dd6 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -837,6 +837,17 @@ static void unlock_params(struct snd_pcm_runtime *runtime) mutex_unlock(&runtime->oss.params_lock); } +static void snd_pcm_oss_release_buffers(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + kvfree(runtime->oss.buffer); + runtime->oss.buffer = NULL; +#ifdef CONFIG_SND_PCM_OSS_PLUGINS + snd_pcm_oss_plugin_clear(substream); +#endif +} + /* call with params_lock held */ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) { @@ -967,12 +978,10 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) snd_pcm_oss_plugin_clear(substream); if (!direct) { /* add necessary plugins */ - snd_pcm_oss_plugin_clear(substream); err = snd_pcm_plug_format_plugins(substream, params, sparams); if (err < 0) { pcm_dbg(substream->pcm, "snd_pcm_plug_format_plugins failed: %i\n", err); - snd_pcm_oss_plugin_clear(substream); goto failure; } if (runtime->oss.plugin_first) { @@ -981,7 +990,6 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) if (err < 0) { pcm_dbg(substream->pcm, "snd_pcm_plugin_build_io failed: %i\n", err); - snd_pcm_oss_plugin_clear(substream); goto failure; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -989,10 +997,8 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) } else { err = snd_pcm_plugin_insert(plugin); } - if (err < 0) { - snd_pcm_oss_plugin_clear(substream); + if (err < 0) goto failure; - } } } #endif @@ -1082,6 +1088,8 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) err = 0; failure: + if (err) + snd_pcm_oss_release_buffers(substream); kfree(sw_params); kfree(params); kfree(sparams); @@ -2351,13 +2359,7 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream, static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime; - runtime = substream->runtime; - kvfree(runtime->oss.buffer); - runtime->oss.buffer = NULL; -#ifdef CONFIG_SND_PCM_OSS_PLUGINS - snd_pcm_oss_plugin_clear(substream); -#endif + snd_pcm_oss_release_buffers(substream); substream->oss.oss = 0; }