Merge branch 'for-next' into for-linus

Pull 5.18 development branch

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2022-03-21 16:18:26 +01:00
commit a6d4b68502
38 changed files with 1582 additions and 493 deletions

View File

@ -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

View File

@ -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
============ ==================================================

View File

@ -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 <hdegoede@redhat.com>
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 <sudeep.holla@arm.com>

View File

@ -1734,17 +1734,21 @@ 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", },
{"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

View File

@ -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"
depends on I2C && ACPI
config SERIAL_MULTI_INSTANTIATE
tristate "Serial bus multi instantiate pseudo device driver"
depends on I2C && SPI && 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"

View File

@ -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

View File

@ -1,174 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* I2C multi-instantiate driver, pseudo driver to instantiate multiple
* i2c-clients from a single fwnode.
*
* Copyright 2018 Hans de Goede <hdegoede@redhat.com>
*/
#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/types.h>
#define IRQ_RESOURCE_TYPE GENMASK(1, 0)
#define IRQ_RESOURCE_NONE 0
#define IRQ_RESOURCE_GPIO 1
#define IRQ_RESOURCE_APIC 2
struct i2c_inst_data {
const char *type;
unsigned int flags;
int irq_idx;
};
struct i2c_multi_inst_data {
int num_clients;
struct i2c_client *clients[];
};
static int i2c_multi_inst_probe(struct platform_device *pdev)
{
struct i2c_multi_inst_data *multi;
const struct i2c_inst_data *inst_data;
struct i2c_board_info board_info = {};
struct device *dev = &pdev->dev;
struct acpi_device *adev;
char name[32];
int i, ret;
inst_data = device_get_match_data(dev);
if (!inst_data) {
dev_err(dev, "Error ACPI match data is missing\n");
return -ENODEV;
}
adev = ACPI_COMPANION(dev);
/* Count number of clients to instantiate */
ret = i2c_acpi_client_count(adev);
if (ret < 0)
return ret;
multi = devm_kmalloc(dev, struct_size(multi, clients, ret), GFP_KERNEL);
if (!multi)
return -ENOMEM;
multi->num_clients = ret;
for (i = 0; i < multi->num_clients && inst_data[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);
board_info.dev_name = name;
switch (inst_data[i].flags & IRQ_RESOURCE_TYPE) {
case IRQ_RESOURCE_GPIO:
ret = acpi_dev_gpio_irq_get(adev, inst_data[i].irq_idx);
if (ret < 0) {
dev_err(dev, "Error requesting irq at index %d: %d\n",
inst_data[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);
if (ret < 0) {
dev_dbg(dev, "Error requesting irq at index %d: %d\n",
inst_data[i].irq_idx, ret);
goto error;
}
board_info.irq = ret;
break;
default:
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]),
"Error creating i2c-client, idx %d\n", i);
goto error;
}
}
if (i < multi->num_clients) {
dev_err(dev, "Error finding driver, idx %d\n", i);
ret = -ENODEV;
goto error;
}
platform_set_drvdata(pdev, multi);
return 0;
error:
while (--i >= 0)
i2c_unregister_device(multi->clients[i]);
return ret;
}
static int i2c_multi_inst_remove(struct platform_device *pdev)
{
struct i2c_multi_inst_data *multi = platform_get_drvdata(pdev);
int i;
for (i = 0; i < multi->num_clients; i++)
i2c_unregister_device(multi->clients[i]);
return 0;
}
static const struct i2c_inst_data bsg1160_data[] = {
{ "bmc150_accel", IRQ_RESOURCE_GPIO, 0 },
{ "bmc150_magn" },
{ "bmg160" },
{}
};
static const struct i2c_inst_data 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 i2c_inst_data int3515_data[] = {
{ "tps6598x", IRQ_RESOURCE_APIC, 0 },
{ "tps6598x", IRQ_RESOURCE_APIC, 1 },
{ "tps6598x", IRQ_RESOURCE_APIC, 2 },
{ "tps6598x", IRQ_RESOURCE_APIC, 3 },
{}
};
/*
* Note new device-ids must also be added to i2c_multi_instantiate_ids in
* drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
*/
static const struct acpi_device_id i2c_multi_inst_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);
static struct platform_driver i2c_multi_inst_driver = {
.driver = {
.name = "I2C multi instantiate pseudo device driver",
.acpi_match_table = i2c_multi_inst_acpi_ids,
},
.probe = i2c_multi_inst_probe,
.remove = i2c_multi_inst_remove,
};
module_platform_driver(i2c_multi_inst_driver);
MODULE_DESCRIPTION("I2C multi instantiate pseudo device driver");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,348 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Serial multi-instantiate driver, pseudo driver to instantiate multiple
* client devices from a single fwnode.
*
* Copyright 2018 Hans de Goede <hdegoede@redhat.com>
*/
#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#define IRQ_RESOURCE_TYPE GENMASK(1, 0)
#define IRQ_RESOURCE_NONE 0
#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,
const struct smi_instance *inst)
{
int ret;
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;
}
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]);
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;
}
/**
* 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;
ret = i2c_acpi_client_count(adev);
if (ret < 0)
return ret;
else if (!ret)
return -ENODEV;
count = ret;
smi->i2c_devs = devm_kcalloc(dev, count, sizeof(*smi->i2c_devs), GFP_KERNEL);
if (!smi->i2c_devs)
return -ENOMEM;
for (i = 0; i < count && inst_array[i].type; i++) {
memset(&board_info, 0, sizeof(board_info));
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;
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 (smi->i2c_num < count) {
dev_dbg(dev, "Error finding driver, idx %d\n", i);
ret = -ENODEV;
goto error;
}
dev_info(dev, "Instantiated %d I2C devices.\n", smi->i2c_num);
return 0;
error:
smi_devs_unregister(smi);
return ret;
}
static int smi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct smi_node *node;
struct acpi_device *adev;
struct smi *smi;
adev = ACPI_COMPANION(dev);
if (!adev)
return -ENODEV;
node = device_get_match_data(dev);
if (!node) {
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);
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)
{
struct smi *smi = platform_get_drvdata(pdev);
smi_devs_unregister(smi);
return 0;
}
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_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_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,
};
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().
*/
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);
static struct platform_driver smi_driver = {
.driver = {
.name = "Serial bus multi instantiate pseudo device driver",
.acpi_match_table = smi_acpi_ids,
},
.probe = smi_probe,
.remove = smi_remove,
};
module_platform_driver(smi_driver);
MODULE_DESCRIPTION("Serial multi instantiate pseudo device driver");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_LICENSE("GPL");

View File

@ -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)
{
@ -2318,8 +2320,50 @@ struct acpi_spi_lookup {
int irq;
u8 bits_per_word;
u8 chip_select;
int n;
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)
{
@ -2349,6 +2393,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;
@ -2362,14 +2408,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
@ -2408,8 +2475,25 @@ 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
* @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,
int index)
{
acpi_handle parent_handle = NULL;
struct list_head resource_list;
@ -2417,12 +2501,13 @@ 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;
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,
@ -2431,26 +2516,25 @@ 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)) &&
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);
}
if (!lookup.max_speed_hz)
return AE_OK;
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 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;
@ -2458,6 +2542,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, -1);
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));

View File

@ -16,6 +16,7 @@
#include <linux/gpio/consumer.h>
#include <uapi/linux/spi/spi.h>
#include <linux/acpi.h>
struct dma_chan;
struct software_node;
@ -759,6 +760,13 @@ 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,
int index);
int acpi_spi_count_resources(struct acpi_device *adev);
#endif
/*
* SPI resource management while processing a SPI message
*/
@ -1452,7 +1460,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 *);

View File

@ -306,12 +306,19 @@ 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,
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);
void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec);
/*
* low level functions
@ -490,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

View File

@ -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,

View File

@ -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);

View File

@ -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.

View File

@ -842,6 +842,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)
{
@ -972,12 +983,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) {
@ -986,7 +995,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) {
@ -994,10 +1002,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
@ -1086,6 +1092,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);
@ -2355,13 +2363,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;
}

View File

@ -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;
@ -80,8 +80,7 @@ snd_seq_oss_create_client(void)
system_client = rc;
/* create annoucement receiver port */
memset(port, 0, sizeof(*port));
/* create announcement receiver port */
strcpy(port->name, "Receiver");
port->addr.client = system_client;
port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */

View File

@ -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);

View File

@ -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) {

View File

@ -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;

View File

@ -59,15 +59,15 @@
/* PCI function 0 registers, address = <val> + 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 */

View File

@ -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

View File

@ -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); */
}
}

View File

@ -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)) {

View File

@ -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

View File

@ -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) {
@ -813,7 +817,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 +835,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 +843,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 +865,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;
}
@ -877,36 +894,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,18 +949,21 @@ 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);
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;
@ -951,7 +983,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);
@ -1007,9 +1038,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);
@ -3371,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);
@ -3389,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
@ -3402,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);

View File

@ -2066,14 +2066,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)) {
@ -2081,10 +2083,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;
}
@ -2151,7 +2154,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;
@ -2374,6 +2377,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);
}
@ -2495,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 */
@ -2508,6 +2514,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 },

View File

@ -135,8 +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);
#define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core)

View File

@ -70,6 +70,7 @@
struct hda_tegra_soc {
bool has_hda2codec_2x_reset;
bool has_hda2hdmi;
};
struct hda_tegra {
@ -314,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 */
@ -435,15 +448,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 +494,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 +516,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);

View File

@ -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;
@ -162,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
@ -179,7 +187,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 +1673,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 +1764,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 +1788,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 +1812,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 +1847,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 +3044,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 +3097,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)
{
@ -3721,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
@ -3741,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);
/*
@ -3771,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,
@ -3796,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;
}
@ -3806,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);
}
@ -3851,17 +3958,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;
@ -3869,6 +3988,36 @@ 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;
spec->hdmi_intr_trig_ctrl = true;
return tegra_hdmi_init(codec);
}
/*
* ATI/AMD-specific implementations
*/
@ -4322,6 +4471,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),
@ -4390,10 +4540,11 @@ 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(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi),
HDA_CODEC_ENTRY(0x80862818, "Raptorlake 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),

View File

@ -6662,6 +6662,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)
{
@ -6999,6 +7009,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,
};
@ -8750,6 +8763,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 = cs35l41_fixup_spi_four,
.chained = true,
.chain_id = ALC285_FIXUP_HP_GPIO_LED,
},
[ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = {
.type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
@ -8977,7 +9004,25 @@ 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, 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),
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),

View File

@ -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;

View File

@ -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;

View File

@ -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);
@ -581,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);

View File

@ -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;

View File

@ -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);

View File

@ -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 <g at b4.vu>
* Copyright (c) 2018-2022 by Geoffrey D. Bennett <g at b4.vu>
* Copyright (c) 2020-2021 by Vladimir Sadovnikov <sadko4u@gmail.com>
*
* Based on the Scarlett (Gen 1) Driver for ALSA:
@ -60,6 +60,7 @@
* - phantom power, direct monitor, speaker switching, and talkback
* controls
* - disable/enable MSD mode
* - disable/enable standalone mode
*
* <ditaa>
* /--------------\ 18chn 20chn /--------------\
@ -195,6 +196,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 +319,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;
@ -403,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];
@ -426,7 +436,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 +482,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 +531,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 +586,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 +599,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 +611,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 +657,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 +710,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 +780,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,
@ -926,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
@ -944,13 +957,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 +981,30 @@ 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 },
[SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
.offset = 0x8d, .size = 8, .activate = 6 },
/* Gen 3 devices: 4i4, 8i6, 18i8, 18i20 */
}, {
[SCARLETT2_CONFIG_DIM_MUTE] = {
.offset = 0x31, .size = 8, .activate = 2 },
@ -993,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 },
@ -1061,9 +1098,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 +1148,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;
@ -1175,7 +1212,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 +1272,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 +1729,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 +3436,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,
@ -3474,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)
@ -3632,9 +3732,15 @@ 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_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;
@ -3957,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)

View File

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

View File

@ -3,7 +3,7 @@
// kselftest for the ALSA mixer API
//
// Original author: Mark Brown <broonie@kernel.org>
// 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
@ -13,6 +13,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include <string.h>
#include <getopt.h>
#include <stdarg.h>
@ -26,11 +27,12 @@
#include "../kselftest.h"
#define TESTS_PER_CONTROL 3
#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;
@ -42,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;
};
@ -67,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;
@ -95,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;
@ -148,6 +153,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,
@ -183,6 +189,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 descriptor 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");
@ -193,8 +219,82 @@ void find_controls(void)
snd_config_delete(config);
}
bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val,
int index)
/*
* Block for up to timeout ms for an event, returns a negative value
* on error, 0 for no event and 1 for an event.
*/
static 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_descriptors_revents() failed for %s: %d\n",
ctl->name, err);
return err;
}
if (revents & POLLERR) {
ksft_print_msg("snd_ctl_poll_descriptors_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;
}
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;
@ -305,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;
@ -321,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;
@ -356,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;
@ -421,13 +521,14 @@ 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;
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);
@ -445,6 +546,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.
@ -469,6 +582,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
@ -493,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;
@ -526,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;
@ -547,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;
@ -577,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;
@ -605,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;
@ -626,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;
@ -679,6 +816,236 @@ void test_ctl_write_valid(struct ctl_data *ctl)
ctl->card->card, ctl->elem);
}
static 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);
}
static 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;
}
static 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;
}
static 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;
}
static 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;
}
static 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);
}
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);
}
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);
}
int main(void)
{
struct ctl_data *ctl;
@ -697,6 +1064,9 @@ int main(void)
test_ctl_get_value(ctl);
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();