mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-04 01:24:12 +08:00
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:
commit
a6d4b68502
@ -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
|
||||
|
@ -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
|
||||
============ ==================================================
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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");
|
348
drivers/platform/x86/serial-multi-instantiate.c
Normal file
348
drivers/platform/x86/serial-multi-instantiate.c
Normal 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");
|
@ -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));
|
||||
|
||||
|
@ -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 *);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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,12 +1002,10 @@ 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
|
||||
|
||||
if (runtime->oss.trigger) {
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -160,7 +160,7 @@ 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,
|
||||
wait_for_completion_killable_timeout(&acomp->master_bind_complete,
|
||||
msecs_to_jiffies(60 * 1000));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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); */
|
||||
}
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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 },
|
||||
|
@ -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)
|
||||
|
@ -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";
|
||||
|
||||
/*
|
||||
* "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,6 +516,7 @@ static int hda_tegra_probe(struct platform_device *pdev)
|
||||
goto out_free;
|
||||
|
||||
hda->clocks[hda->nclocks++].id = "hda";
|
||||
if (hda->soc->has_hda2hdmi)
|
||||
hda->clocks[hda->nclocks++].id = "hda2hdmi";
|
||||
hda->clocks[hda->nclocks++].id = "hda2codec_2x";
|
||||
|
||||
|
@ -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,16 +3865,29 @@ static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format)
|
||||
else
|
||||
value |= NVIDIA_SCRATCH_VALID;
|
||||
|
||||
if (spec->hdmi_intr_trig_ctrl) {
|
||||
/*
|
||||
* 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.
|
||||
* 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,
|
||||
snd_hda_codec_write(codec, nid, 0,
|
||||
NVIDIA_SET_SCRATCH0_BYTE3, value);
|
||||
}
|
||||
}
|
||||
|
||||
static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
||||
if (!new_fops)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
return err;
|
||||
}
|
||||
if (!file->f_op->open)
|
||||
return -ENODEV;
|
||||
|
||||
return file->f_op->open(inode, file);
|
||||
}
|
||||
|
||||
MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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,7 +219,81 @@ void find_controls(void)
|
||||
snd_config_delete(config);
|
||||
}
|
||||
|
||||
bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val,
|
||||
/*
|
||||
* 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;
|
||||
@ -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,7 +456,7 @@ out:
|
||||
ctl->card->card, ctl->elem);
|
||||
}
|
||||
|
||||
bool show_mismatch(struct ctl_data *ctl, int index,
|
||||
static bool show_mismatch(struct ctl_data *ctl, int index,
|
||||
snd_ctl_elem_value_t *read_val,
|
||||
snd_ctl_elem_value_t *expected_val)
|
||||
{
|
||||
@ -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,
|
||||
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();
|
||||
|
Loading…
Reference in New Issue
Block a user